[PYTHON] [Django] Google-Kartenanzeige von GIS-Daten und grafische Darstellung von Parametern

1. Zuallererst

Ich habe diesen Artikel erstellt, weil ich keinen einfachen Code hatte, um CSV-Daten hochzuladen, grafisch darzustellen, auf einer Karte anzuzeigen und anzuzeigen. Das fertige Bild wird unten gezeigt.

amchart_demo.png Abb.1 Abgeschlossene Webseite

Die obere Hälfte von Fig. 1 wurde unter Verwendung des Liniendiagramms von amchart.js erstellt. Der Anzeigebereich kann sowohl für die x-Achse als auch für die y-Achse frei gewählt werden. Die x-Achse ist die Zeitachse. Die untere Hälfte von Abb. 2 verwendet GoogleMapAPI zur Eingabe und Anzeige von Breiten- / Längengraddaten.

2. Verwendete Dienste und Frameworks

・ Google Map API · Django ・ Amchart.js

3. Implementierung

3-1. Bauumgebung

Die Umgebung und Version sind wie folgt.

macOS Catalina version 10.15.4
Python 3.7.0

3-2. Verzeichnisstruktur

─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 (<-Eine Datei, die die importierte CSV-Datei vorübergehend ergänzt)
     └manage.py

Es folgt der grundlegenden Verzeichnisstruktur von 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"'
    #Das HttpResponse-Objekt ist ein dateiähnliches Objekt, also csv.Sie können es so wie es ist an den Autor weitergeben.
    writer = csv.writer(response)
    for post in Post.objects.all():
        writer.writerow([post.pk, post.title])
    return response

forms.py ist der am meisten entwickelte Quellcode.

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='CSV-Datei',
        help_text='* Bitte laden Sie die Datei mit der Erweiterung csv hoch.',
        validators=[FileExtensionValidator(allowed_extensions=['csv'])]
    )

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

        print("file:",file)

        # csv.Konvertieren Sie mit TextIOWrapper in eine Textmodusdatei, um sie an den Reader zu übergeben
        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 zum Speichern der nicht gespeicherten Modellinstanz, die aus jeder Zeile erstellt wurde

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

        except UnicodeDecodeError:
            raise forms.ValidationError('Überprüfen Sie die Codierung der Datei und die richtige CSV-Datei.')

        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'),
]

Von den folgenden Quellen fügt [API-KEY] den API-Key ein. Bitte geben Sie den Google_API-Key selbst aus.

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>Visualisierung des Rover-Sensors</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' %}">CSV-Lesung</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="{% url 'app:export' %}">CSV-Ausgabe</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">Senden</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)Konvertieren Sie von CSV in 2D-Array
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.Bereiten Sie ein Array für js Dataset vor
    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.Zeichne mit 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); //hinzufügen
      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)Laden Sie die CSV-Datei mit Ajax
    var req = new XMLHttpRequest();
    var filePath = file_name//'acc_gyro.csv';
    req.open("GET", filePath, true);
    req.onload = function() {
      // 2)Rufen Sie die CSV-Datenkonvertierung auf
      data = csv2Array(req.responseText);
      // 3) amChart.js Datenaufbereitung, 4) amChart.js Zeichnungsaufruf
      makeLineChart(data);
    }
    req.send(null);
  }

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

static/js/googlemap.js


// 2)Konvertieren Sie von CSV in 2D-Array
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;
  }

//Eigenschaften und Eigenschaften in einem zweidimensionalen Array
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);  
    //Anfangseinstellungen des Markers markieren
    var markerOpts = {
    position: {lat: Number(location_line.lat), lng: Number(location_line.lng)},
    map: map,
    title: "mark"
    };
    //Erstellen Sie einen Marker mit den zuvor erstellten Markeroptionen
    var marker = new google.maps.Marker(markerOpts);
  }
  
}

function main(file_name) {
    // 1)Laden Sie die CSV-Datei mit Ajax
    var req = new XMLHttpRequest();
    var filePath = file_name;//'static/js/acc_gyro.csv';
    req.open("GET", filePath, true);
    req.onload = function() {
      // 2)Rufen Sie die CSV-Datenkonvertierung auf
      data = csv2Array(req.responseText);
      // 3)Vorbereitung der Google-Kartendaten, 4)Google Map Drawing Call
      initMap(data);
    }
    req.send(null);
  }

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

project/setting.py


(Unterlassung)
# 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. Eindrücke

・ Es ist die erste mit Django erstellte Webanwendung. Ich bin tief bewegt.

5. Referenzen

So implementieren Sie amchart: https://www.suzu6.net/posts/56-amcharts-samples/ Implementierung der CSV-Upload-Funktion: https://qiita.com/t-iguchi/items/d2862e7ef7ec7f1b07e5

Recommended Posts

[Django] Google-Kartenanzeige von GIS-Daten und grafische Darstellung von Parametern
Anfänger der Google Maps API und der Twitter API haben "Tweet Map" erstellt.
Versuchen Sie, Google Map und Geography Map mit Python anzuzeigen
[Ruby on Rails] Anzeigen und Fixieren von GoolgeMAP mithilfe der Google-API
[Rails] So zeigen Sie Google Map an
Zeigen Sie mehrere Markierungen auf Google Map an
[Rails] google maps api So posten und anzeigen Sie einschließlich Karteninformationen
[Android] Zeigen Sie Bilder im Web im infoWindow von Google Map an
Grafische Anzeige von AIX- und Linux-Nmon-Daten ohne Verwendung von MS Excel
Bedeutung von Deep-Learning-Modellen und -Parametern
Trennung von Design und Daten in matplotlib
Ramen-Kartenerstellung mit Scrapy und Django
[Python] Von der morphologischen Analyse von CSV-Daten bis zur CSV-Ausgabe und Diagrammanzeige [GiNZA]
Verwendung früherer meteorologischer Daten 3 (Zeitreihen-Wärmekartenanzeige des Niederschlags bei starkem Regen)