[PYTHON] [Django] Affichage de la carte Google des données SIG et représentation graphique des paramètres

1.Tout d'abord

J'ai créé cet article parce que je n'avais pas de code simple pour télécharger des données csv, les représenter graphiquement, les afficher sur une carte et les afficher. L'image terminée est présentée ci-dessous.

amchart_demo.png Fig.1 Page Web terminée

La moitié supérieure de la figure 1 a été créée à l'aide du graphique linéaire de amchart.js. La plage d'affichage peut être librement sélectionnée pour l'axe x et l'axe y. L'axe des x est l'axe du temps. La moitié inférieure de la figure 2 utilise GoogleMapAPI pour saisir et afficher les données de latitude / longitude.

2. Services et cadres utilisés

・ API Google Map ・ Django ・ Amchart.js

3. Mise en œuvre

3-1. Environnement de construction

L'environnement et la version sont les suivants.

macOS Catalina version 10.15.4
Python 3.7.0

3-2. Structure du répertoire

─root─app─templates─app─base.html
     │   |             └import.html
     │   ├migrations-・ ・ ・
     │   ├forms.py
     │   ├views.py
│ ・ ・ ・ ・
     |
     |
     ├project─settings.py
     |       ├urls.py
     |・ ・ ・
     |
     ├static─js─mychart.js
     |      |  └googlemap.js
     |      └media-temp.csv (<-Un fichier qui complète temporairement le fichier csv importé)
     └manage.py

Il suit la structure de répertoire de base de Django.

3-3. Code

app/views.py


import csv
from django.http import HttpResponse
from django.shortcuts import redirect
from django.urls import reverse_lazy
from django.views import generic
from .forms import CSVUploadForm
from .models import Post


class PostIndex(generic.ListView):
    model = Post


class PostImport(generic.FormView):
    template_name = 'app/import.html'
    success_url = reverse_lazy('app:index')
    form_class = CSVUploadForm

    def form_valid(self, form):
        form.save()
        return redirect('app:index')


def post_export(request):
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="posts.csv"'
    #L'objet HttpResponse est un objet de type fichier, donc csv.Vous pouvez le transmettre à l'écrivain tel quel.
    writer = csv.writer(response)
    for post in Post.objects.all():
        writer.writerow([post.pk, post.title])
    return response

forms.py est le code source le plus conçu.

app/forms.py


import csv
import io
from django import forms
from django.core.validators import FileExtensionValidator
from .models import Post
import codecs

class CSVUploadForm(forms.Form):
    file = forms.FileField(
        label='Fichier CSV',
        help_text='* Veuillez télécharger le fichier avec l'extension csv.',
        validators=[FileExtensionValidator(allowed_extensions=['csv'])]
    )

    def clean_file(self):
        file = self.cleaned_data['file']

        print("file:",file)

        # csv.Convertir en fichier en mode texte avec TextIOWrapper pour passer au lecteur
        csv_file = io.TextIOWrapper(file, encoding='utf-8')
        print("csv_file",csv_file)
        reader = csv.reader(csv_file)
        print("reader",reader)
        
        self.csv_file = reader
        
        for row in reader:
            #print("reader row", row)
            #print("line_num", reader.line_num)
            if reader.line_num == 1:
                with open("static/media/temp.csv", mode="w", encoding="utf-8") as f:
                    print("row in x before", row)
                    row = ",".join(row) 
                    row = row + "\n" 
                    print("row in x after", row)
                    f.write(row)
                print("option=x")

            else:
                with open("static/media/temp.csv", mode="a", encoding="utf-8") as f:
                    #print("row in a before", row)
                    #print(type(row))
                    row = ",".join(row) 
                    row = row + "\n" 
                    #print("row in a after", row)
                    #print(type(row))
                    
                    f.write(row)
                #print("option=a")


        #Liste pour stocker l'instance de modèle non enregistrée créée à partir de chaque ligne

        self._instances = []
        
        try:
            for row in reader:
                post = Post(pk=row[0], title=row[1])
                self._instances.append(post)

        except UnicodeDecodeError:
            raise forms.ValidationError('Vérifiez le codage du fichier et le fichier CSV correct.')

        return file

    def save(self):
        Post.objects.bulk_create(self._instances, ignore_conflicts=True)
        Post.objects.bulk_update(self._instances, fields=['title'])
        print("save content in save func", Post)

app/urls.py


from django.urls import path
from . import views

app_name = 'app'

urlpatterns = [
    path('', views.PostIndex.as_view(), name='index'),
    path('import/', views.PostImport.as_view(), name='import'),
    path('export/', views.post_export, name='export'),
]

Parmi les sources suivantes, [API-KEY] insère la clé API. Veuillez émettre Google_API-Key par vous-même.

app/templates/app/base.html


<!doctype html>
{% load static %}
<html lang="ja">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

    <style>
      #chartdiv {
        width: 100%;
        height: 500px;
      }
      #map {
        width: 100%;
        height: 400px;
      }
      </style>
      
      <!-- Resources -->
      <script src="https://www.amcharts.com/lib/4/core.js"></script>
      <script src="https://www.amcharts.com/lib/4/charts.js"></script>
      <script src="https://www.amcharts.com/lib/4/themes/animated.js"></script>
      
      <!-- Chart code -->
      <script src="{% static 'js/mychart.js' %}"></script>
    <title>Visualisation du capteur Rover</title>
  </head>

  <body>
    <ul class="nav justify-content-center">
      <li class="nav-item">
        <a class="nav-link" href="{% url 'app:index' %}">Index</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="{% url 'app:import' %}">Lecture CSV</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="{% url 'app:export' %}">Sortie CSV</a>
      </li>
    </ul>
  <div class="container">
    {% block content %}{% endblock %}
      
  
    <div id="map"></div>
      
    <script  src="{% static 'js/googlemap.js' %}"></script>
    <script src="https://maps.googleapis.com/maps/api/js?key=[API-KEY]"
async defer></script>

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
  </div>

  </body>
</html>

app/templates/app/import.html


{% extends 'app/base.html' %}

{% block content %}
<form action="" method="POST" enctype="multipart/form-data">
  {{ form.as_ul }}
  {% csrf_token %}
  <button type="submit">Envoyer</button>
</form>
{% endblock %}

app/templates/app/post_list.html


{% extends 'app/base.html' %}

{% block content %}
<div id="chartdiv"></div>
{% endblock %}

project/static/js/mychart.js


// 2)Conversion de CSV en tableau 2D
function csv2Array(str) {
    var csvData = [];
    var lines = str.split("\n");
    for (var i = 0; i < lines.length; ++i) {
      var cells = lines[i].split(",");
      csvData.push(cells);
    }
    return csvData;
  }
  
  function drawBarChart(data) {
    // 3)chart.Préparer un tableau pour l'ensemble de données js
    var tmpLabels = [], tmpData1 = [], tmpData2 = [];
    for (var row in data) {
      tmpLabels.push(data[row][0])
      tmpData1.push(data[row][1])
      tmpData2.push(data[row][2])
    };
  
    // 4)chart.Dessiner avec js
    var ctx = document.getElementById("myChart").getContext("2d");
    var myChart = new Chart(ctx, {
      type: 'bar',
      data: {
        labels: tmpLabels,
        datasets: [
          { label: "Tokyo", data: tmpData1, backgroundColor: "red" },
          { label: "Osaka", data: tmpData2, backgroundColor: "blue" }
        ]
      }
    });
  }
  
  function makeLineChart(data) {
    am4core.ready(function() {
            
      // Themes begin
      am4core.useTheme(am4themes_animated);
      // Themes end
      
      // Create chart instance
      var chart = am4core.create("chartdiv", am4charts.XYChart);
      
      // Add data
      console.log(data);
      console.log(typeof data);
      console.log(data[1][1]);
      chart.data = generateChartData(data); //ajouter à
      console.log(chart.data);
      
      // Create axes
      var xAxis = chart.xAxes.push(new am4charts.ValueAxis());
  
      //xAxis.renderer.minGridDistance = 150;
      xAxis.title.text = "Time (sec)";
      
      var yAxis = chart.yAxes.push(new am4charts.ValueAxis());
      yAxis.title.text = "ax";
      
      // Create series
      var series = chart.series.push(new am4charts.LineSeries());
      series.dataFields.valueX = "timestamp";
      series.dataFields.valueY ="ax";
      series.name = "ax";
    
      series.strokeWidth = 2;
      series.minBulletDistance = 10;
      series.tooltipText = "{valueY}";
      series.tooltip.pointerOrientation = "vertical";
      series.tooltip.background.cornerRadius = 20;
      series.tooltip.background.fillOpacity = 0.5;
      series.tooltip.label.padding(12,12,12,12)
  
      var series2 = chart.series.push(new am4charts.LineSeries());
      series2.dataFields.valueX = "timestamp";
      series2.dataFields.valueY = "ay";
      series2.name = "ay";
  
  
      var series3 = chart.series.push(new am4charts.LineSeries());
      series3.dataFields.valueX = "timestamp";
      series3.dataFields.valueY = "az";
      series3.name = "az";
  
      // Create y axis range
      var range = yAxis.axisRanges.create();
      range.label.disabled = false;
      range.label.rotation = 270;
  
      // Create x axis range
      var range_x = xAxis.axisRanges.create();
      range_x.label.disabled = false;
      range_x.label.rotation = 0;
  
  
      // Add scrollbar
      chart.scrollbarX = new am4charts.XYChartScrollbar();
      chart.scrollbarX.series.push(series);
      chart.scrollbarX.series.push(series2);
      chart.scrollbarX.series.push(series3);
      chart.scrollbarY = new am4charts.XYChartScrollbar();
      chart.scrollbarY.series.push(series);
      chart.scrollbarY.series.push(series2);
      chart.scrollbarY.series.push(series3);
      
      // Add cursor
      chart.cursor = new am4charts.XYCursor();
      chart.cursor.xAxis = xAxis;
      chart.cursor.yAxis = yAxis;
      chart.cursor.snapToSeries = series;
      chart.cursor.snapToSeries = series2;
      chart.cursor.snapToSeries = series3;
      
      // Add legend
      chart.legend = new am4charts.Legend();
  
      function generateChartData(data) {
          var chartData = [];
          var index_length = Number(data.length);
          const initial_time = data[0][0]
          console.log(index_length);
          for (var i = 0; i < (index_length-1); i++) {
      
              var data_line = data[i];
  
              chartData.push({
                timestamp: (data_line[0]-initial_time)/1000,
                ax: data_line[1],
                ay: data_line[2],
                az: data_line[3],
                wx: data_line[4],
                wy: data_line[5],
                wz: data_line[6],
                mx: data_line[7],
                my: data_line[8],
                mz: data_line[9],
                lat: data_line[10],
                lng: data_line[11],
                yaw: data_line[12]              
            });
          }
  
          return chartData;
      }
      
      });
  }
  
  function main(file_name) {
    // 1)Charger le fichier CSV avec ajax
    var req = new XMLHttpRequest();
    var filePath = file_name//'acc_gyro.csv';
    req.open("GET", filePath, true);
    req.onload = function() {
      // 2)Appelez la conversion de données CSV
      data = csv2Array(req.responseText);
      // 3) amChart.js préparation des données, 4) amChart.appel de dessin js
      makeLineChart(data);
    }
    req.send(null);
  }

  main("/static/media/temp.csv");

static/js/googlemap.js


// 2)Conversion de CSV en tableau 2D
function csv2Array(str) {
    var csvData = [];
    var lines = str.split("\n");
    for (var i = 0; i < lines.length; ++i) {
      var cells = lines[i].split(",");
      csvData.push(cells);
    }
    return csvData;
  }

//Propriétés et propriétés dans un tableau à deux dimensions
function mapData(data) {
    var mapData = [];
    const initial_time = data[0][0]
    var index_length = Number(data.length);
    for (var i = 0; i < (index_length-1); i++) {

        var data_line = data[i];

        mapData.push({
          timestamp: (data_line[0]-initial_time)/1000,
          ax: data_line[1],
          ay: data_line[2],
          az: data_line[3],
          wx: data_line[4],
          wy: data_line[5],
          wz: data_line[6],
          mx: data_line[7],
          my: data_line[8],
          mz: data_line[9],
          lat: data_line[10],
          lng: data_line[11],
          yaw: data_line[12]              
      });
    }
    return mapData;
}


function initMap(data) {
  var map;
  mapdata = mapData(data);
  var index_length = Number(data.length);
  console.log(mapdata);
  console.log(index_length);
  initial_loc = mapdata[0];

  map = new google.maps.Map(document.getElementById('map'), {
    center: {lat: Number(initial_loc.lat), lng: Number(initial_loc.lng)},
    zoom: 15
  });
  
 for(var i = 1; i < (index_length-1); i++) {
    location_line = mapdata[i];
    //console.log(i); 
    //console.log(location_line);  
    //Paramètres initiaux du marqueur
    var markerOpts = {
    position: {lat: Number(location_line.lat), lng: Number(location_line.lng)},
    map: map,
    title: "mark"
    };
    //Créez un marqueur à l'aide des options de marqueur créées juste avant
    var marker = new google.maps.Marker(markerOpts);
  }
  
}

function main(file_name) {
    // 1)Charger le fichier CSV avec ajax
    var req = new XMLHttpRequest();
    var filePath = file_name;//'static/js/acc_gyro.csv';
    req.open("GET", filePath, true);
    req.onload = function() {
      // 2)Appelez la conversion de données CSV
      data = csv2Array(req.responseText);
      // 3)préparation des données de google map, 4)appel de dessin de google map
      initMap(data);
    }
    req.send(null);
  }

main("/static/media/temp.csv");

project/setting.py


(Omission)
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/

STATIC_URL = '/static/'
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, "static"),
)

4. Impressions

・ Ce sera la première application Web créée avec Django. Je suis profondément ému. -Comme il n'y avait pas de description dans le tutoriel sur la façon de définir le répertoire statique, j'ai beaucoup appris. ・ Ensuite, je voudrais rapporter les résultats du déploiement d'AWS sur les instances EC2.

5. Références

Comment implémenter amchart: https://www.suzu6.net/posts/56-amcharts-samples/ Implémentation de la fonction de téléchargement csv: https://qiita.com/t-iguchi/items/d2862e7ef7ec7f1b07e5

Recommended Posts

[Django] Affichage de la carte Google des données SIG et représentation graphique des paramètres
Les débutants de l'API Google Maps et de l'API Twitter ont créé "Tweet Map"
Essayez d'afficher la carte google et la carte géographique avec python
[Ruby on Rails] Affichage et épinglage de GoolgeMAP à l'aide de l'API Google
[Rails] Comment afficher Google Map
Afficher plusieurs marqueurs sur Google Map
[Rails] google maps api Comment publier et afficher des informations cartographiques
[Android] Afficher des images sur le Web dans la fenêtre info de Google Map
Affichage graphique des données AIX et Linux nmon sans utiliser MS Excel
Signification des modèles et paramètres d'apprentissage en profondeur
Séparation de la conception et des données dans matplotlib
Création de carte Ramen avec Scrapy et Django
[Python] De l'analyse morphologique des données CSV à la sortie CSV et à l'affichage graphique [GiNZA]
Utilisation des données météorologiques passées 3 (affichage de cartes thermiques chronologiques des précipitations lors de fortes pluies)