LoginSignup
3
6

More than 3 years have passed since last update.

静岡県の点群データDBをVue+Leafletで表示してみた

Last updated at Posted at 2020-01-15

今回作ったもの

静岡県点群データマップ
GitHub - shizokaPCDB
スクリーンショット 2020-01-16 1.35.33.png

注意

この記事はLIDARデータをLeafletに表示する記事ではありません。
点群データベースの情報(データの取得地や工事日など)を表示するものです。

はじめに

スクリーンショット 2020-01-16 1.49.59.png
Rockな静岡県庁さんは、自前のウェブサイトShizuoka Point Cloud DB(以下「静岡PCDB」)で工事成果品のLIDARデータをオープンデータとして多数公開しています。興味本位でソースを眺めてみると、外部からでもデータを取得出来そうだなとわかりました(htmlにJavaScriptが直書きだったため)。ただこのサイト、上記画像を見るとわかるとおり①マーカーの数が多くパフォーマンスに影響が出ている事と、②地図領域が変わるたびにその領域に含まれるデータをサーバーから取得しておりさらにパフォーマンスが低下している事、という2つの問題があると思いました。データが外部からでも取得出来るなら改善の余地があるかもと思い、開発に至った次第です。

そんな訳でデータベースへのアクセス・整形とフロントでの表示の二本立てでお送りします。

使用技術

  • Flask
  • Vue.js
  • Vue2-leaflet

データベースへのアクセス・整形

アクセス

静岡PCDBのソースを見ると、サーバーへのアクセス・Leafletでの表示は100行程度で実装されていました。
サーバーからデータを取得する部分を以下に示します。


var data = { request : "MarkerSet",
                Xmax : map.getBounds().getNorthEast().lng,
                Xmin : map.getBounds().getSouthWest().lng ,
                Ymax : map.getBounds().getNorthEast().lat,
                Ymin : map.getBounds().getSouthWest().lat };
    $.ajax("ankenmapsrc",{
        type: "GET",
        data: data,
        success: function(data, dataType){
            var myIcon = L.icon({
                iconUrl: 'http://cdn.leafletjs.com/leaflet-0.5/images/marker-icon@2x.png',
                iconSize: [15,25],
                iconAnchor: [5, 5],
            });
//以下処理が続きます

dataというクエリでankenmapsrcにアクセスすれば、データが得られそうです。
ここでdataはリクエスト名"MarkerSet"と取得する領域を示すXmax,Xmin,Ymax,Yminを持ちます。
領域の4つの変数には、Leafletで表示している領域が格納されます。
この処理はLeafletの表示領域が更新されるたびに呼ばれます。つまりその都度サーバーからデータを取得している訳です。

さてこのリクエストの返り値を見てみましょう。

30XXX01010001:平成30年度韮山反射炉計測業務:138.96214537214:35.03962001009?
28XXX00030007:白糸の滝滝見橋周辺整備事業 その7:138.58870495572:35.312506370532?
28XXX00030008:白糸の滝滝見橋周辺整備事業 その8:138.58881502806:35.312596432406?
28XXX00030009:白糸の滝滝見橋周辺整備事業 その9:138.58892510063:35.312686494178?
29C2001011361:平成29年度[第29-C2001-01号] 伊豆半島の屋外広告物の実態調査業務委託(函南町道_1-2号線):138.93794860595:35.083520492945
...

本来は改行はありませんが、説明のため追加しています。
このデータを見ると、工事ごとの区切り文字は"?"で、データ要素ごとの区切りは":"である事がわかります。

整形

Flaskで静岡PCDBにアクセスし、整形したデータをレスポンスするAPIサーバをつくります。

import urllib.request, urllib.parse
import json
@app.route('/markers')
def getMarkers():
    #全件取得するために、静岡県全域が含まれる緯度経度を整数値で設定
    xMax = 140
    xMin = 137
    yMax = 36
    yMin = 33

    params = {
        'request':'MarkerSet',
        'Xmax':xMax,
        'Xmin':xMin,
        'Ymax':yMax,
        'Ymin':yMin
    }
    p = urllib.parse.urlencode(params)
    url = "https://pointcloud.pref.shizuoka.jp/lasmap/ankenmapsrc?" + p

    #上記で生成したURLパラメータでSIZUOKA POINT CLOUD DBにリクエストし案件一覧文字列を取得
    allAnkenStr = ""
    with urllib.request.urlopen(url) as res:
        allAnkenStr = res.read().decode()

    #returnするjsonを作成
    ankensObj = {
        "ankenList":[]
    }

    ankenList = allAnkenStr.split('?')
    for anken in ankenList:
        ankenInfo = anken.split(':')
        #不適切なデータがあった場合、スキップする
        if len(ankenInfo) != 4:
            continue

        #和暦を西暦に変換
        yy = int(ankenInfo[0][:2])
        #令和
        if yy < 24:
            yyyy = 2018 + yy
        else:
            yyyy = 1988 + yy

        ankenObj = {
            "no":ankenInfo[0],
            "name":ankenInfo[1],
            "lon":ankenInfo[2],
            "lat":ankenInfo[3],
            "year":yyyy
        }
        ankensObj['ankenList'].append(ankenObj)
    return jsonify(ankensObj)

冒頭で説明した問題点②地図領域が変わるたびにその領域に含まれるデータをサーバーから取得しておりさらにパフォーマンスが低下している事については、静岡県の領域を全て含むXmax,Xmin,Ymax,Yminとする事で解決しました。というのも、全件でも1400件程度であり、実際に全件取得してみても遅くはなかったため、最初に全部取得しておけば良いという結論になりました。

取得したankenデータをパースして、工事番号、工事名、経度・緯度、工事年度をjsonとしてreturnする事としました。

フロントでの表示

残る問題点①マーカーの数が多くパフォーマンスに影響が出ている事ですが、動作の早いMapbox GL JSで表示すれば解決すると考えていました。しかしながら実装してみたところ、マーカーの表示はLeafletより遅くなってしまいました。という事で、LeafletのMarkerClusterを採用する事でこの問題を解決しました。

LeafletとVueの環境構築についてはTry #027 – Vue.jsでLeafletとMapbox GL JSの開発環境を構築してみたが詳しいので割愛します。
くわえて、vue2-leaflet-markerclusterをインストールします。

npm install vue2-leaflet-markercluster

VueでAPIサーバに非同期通信

FetchAPIを使います。
App.vueでデータを取得しMapPane.vueへ渡します。

App.vue
<MapPane :ankens="ankens"/>
App.vue
  data() {
    return {
      ankens:[],
    }
  },
  created() {
    let vm = this
    fetch("/markers")
    .then(response => {
        return response.json()
    })
    .then(data => {
        //sortしてからdataを渡しています
        vm.ankens = data.ankenList.sort(function (a, b) {
            if (a.no < b.no) {
                return 1
            }
            if (a.no > b.no) {
                return -1
            }
            return 0 
        }).sort(function (a, b) {
            if (a.year < b.year) {
                return 1
            }
            if (a.year > b.year) {
                return -1
            }
            return 0 
        })
    })
    .catch(error => {
        console.log(error)
        alert("エラーが発生しました。")
    });
  }
MapPane.vue
<template>
    <div class="mapPane">
        <l-map
            :zoom="zoom"
            :center="center"
            :preferCanvas="true"
        >
            <l-control-scale
                position="bottomleft"
                :imperial="false"
                :metric="true"
            ></l-control-scale>

            <l-tile-layer
                :name="tileProvider.name"
                :visible="tileProvider.visible"
                :url="tileProvider.url"
                :attribution="tileProvider.attribution"
            ></l-tile-layer>

            <Vue2LeafletMarkerCluster :options="clusterOptions" >
                <LMarker v-for="anken in ankens" :key="anken.no" :lat-lng="makeLatLng(anken)" @click="onMarkerClick(anken.no)">
                    <LPopup :content="makeMarkerContent(anken)" ></LPopup>
                </LMarker>
            </Vue2LeafletMarkerCluster>
        </l-map>
    </div>
</template>
MapPane.vue

props: {
    ankens:Array
},
MapPane.vue
<style scoped>
    @import "~leaflet.markercluster/dist/MarkerCluster.css";
    @import "~leaflet.markercluster/dist/MarkerCluster.Default.css";

</style>

LMarkerをVue2LeafletMarkerClusterで包めば良きにはからってくれます。
以上により、密接したマーカーはクラスター(束)となり、拡大するまでまとめてひとつの地物として表示されます。結果問題点①も解決しました。

おわりに

今後はデータの一覧表示をソート出来たり検索出来たり、ローディング中のアニメーションを表示したいなと思っています。LIDARデータがアツいらしく調べていると静岡PCDBに辿り着きましたが、思わぬ大脱線となりました。これほど多数の貴重なデータをオープンデータとする静岡県さんはマジでRockであり、最近では兵庫県さんもRockerの仲間入りをしたようです。オープンデータ活用の本流とは若干逸れている気がする今回の案件ですが、少なくとも自己研鑽にはなったかなと思います。これからもオープンデータ界隈にはアンテナを張っておきたいと思います。

3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6