文系プログラマによるTIPSブログ

文系プログラマ脳の私が開発現場で学んだ事やプログラミングのTIPSをまとめています。

GoogleMapでMarkerとGeoJSONのプロット速度をゆる〜く比較する

うーむ、まあ、そうですよね・・・
f:id:treeapps:20170111000706p:plain

現在新tree-mapsの開発中なのですが、折角なのでGoogleMap以外を使ってみようとか、プロットの方式を変えてみようとか、色々試行錯誤しています。

※ この記事はゆる〜く計測するものです。完全で間違いの無い計測を目指したものではありません。

地図の選択肢

まず最初にGoogleMapとOpenStreetmapでマーカー生成速度を比較してみたのですが・・・・・OpenStreetMapのマーカー生成処理速度、おっそ・・・・これは使えません。信じられない程の遅さです。

OpenStreetMapが遅いのでBingやYahooの地図も試したいところですが、今回は割愛します。

GoogleMap一択になったので、まずは普通にMarker APIでマーカーを生成するのと、GeoJSON APIで一気に座標を読み込んでマーカーを生成するのは、どれくらいの速度が出るのか、比較してみました。

計測で使うGeoJSONファイル

こんな感じです。

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "Point",
        "coordinates": [
          139.0274727702,
          35.720855461
        ]
      }
    },
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "Point",
        "coordinates": [
          139.0456545884,
          35.720855461
        ]
      }
    },
・・・以下略・・・

Marker APIによるマーカー生成

ソースコード

こんなクソコードでいい筈ありませんが、何となくの速度計測はできます。碁盤のように縦横整列した状態でピンが綺麗に生成されます。

縦200個、横300個、合計6万マーカーの生成を行います。

<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<style>
html {height: 100%;}
body {height: 100%;}
#map {height: 97%; border: 1px solid gray;}
</style>
</head>
<body>
<input type="button" value="マーカーを追加" id="add-marker"/>
<input type="button" value="マーカーを削除" id="delete-marker"/>
 マーカー数=<span id="marker-num"></span>件 処理時間=<span id="result">0</span><div id="map"></div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script type="text/javascript" src="https://maps.google.com/maps/api/js?key=${APIキー}"></script>
<script type="text/javascript">
var map = null;
var markers = new Array();
var ROW_COUNT = 200;
var COL_COUNT = 300;

$('#marker-num').text(ROW_COUNT * COL_COUNT);
google.maps.event.addDomListener(window, 'load', function () {
    var mapOptions = {
        zoom: 8,
        center: new google.maps.LatLng(36.72085546100, 141.8274727702),
        mapTypeId: google.maps.MapTypeId.ROADMAP,
        scaleControl: true
    };
    map = new google.maps.Map(document.getElementById('map'), mapOptions);
});

$('#add-marker').on('click', function () {
    var startTime = new Date();
    for (var row = 0; row < ROW_COUNT; row++) {
        for (var col = 0; col < COL_COUNT; col++) {
            var lat = 35.72085546100 + (row / 100);
            var lng = 139.0274727702 + (col / 55);
            var latlng = new google.maps.LatLng(lat, lng);
            markers.push(new google.maps.Marker({position: latlng, map: map}));
        }
    }
    $('#result').text((new Date() - startTime) / 1000);
});

$('#delete-marker').on('click', function () {
    jQuery.each(markers, function () {this.setMap(null);});
});
</script>
</body>
</html>

実行結果

処理時間の計測完了からマーカー表示が遅いですが、これはGoogleMapApiの処理は完了済みだが、マシンが頑張って大量のマーカーをレンダリング(表示)する処理に時間がかかっています。つまり処理自体は高速ですが、マーカーの描画の時間がかかっているわけです。

f:id:treeapps:20170110232304g:plain

GeoJSON APIによるマーカー生成

GeoJSON

geoJsonという汎用空間データフォーマットがあり、ここ最近GoogleMapはこのGeoJSONに対応しました。

データレイヤ  |  Google Maps JavaScript API  |  Google Developers

ということで早速どれくらいの速度なのか、計測してみましょう。

ソースコード

例によって、こんなクソコードでいいのかは棚上げし、こんな感じで準備しました。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<style>
html, body, #map-canvas {height: 100%;margin: 0px;padding: 0px}
</style>
</head>
<body>
<input type="button" value="マーカーを追加" id="add-marker" />
<input type="button" value="マーカーを削除" id="delete-marker" />
 処理時間=<span id="result">0</span><div id="map-canvas"></div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?v=3.exp&key=${APIキー}"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.min.js"></script>
<script>
var map;
google.maps.event.addDomListener(window, 'load', function () {
    map = new google.maps.Map(document.getElementById('map-canvas'), {
        center: {lat: 36.72085546100, lng: 141.8274727702}, zoom: 8
    });
});

$('#add-marker').on('click', function() {
    var startTime = new Date();
    map.data.loadGeoJson('/assets/html/coordinates.geojson', null, function(features) {
        $('#result').text((new Date() - startTime) / 1000);
    });
})
</script>
</body>
</html>

実行結果

f:id:treeapps:20170110233758g:plain

あれ、遅い。。。

GeoJSONファイルのパース処理の時間は追加される筈ですが、パース後は何か特別なマーカー生成をしてくれると期待して、Marker APIより高速になるかも!?なんて思ったらこれです。

件数が少ないのが悪いのかと思って、試しに縦400、横500の計20万件で試したのですが、結果はやはりGeoJSONの方が遅いというか、Marker APIはちゃんとプロットできましたが、GeoJSON APIは500エラーで取り込めず。

GeoJSONはポリゴンとか、マーカー以外の方が強そうなので、マーカー生成は素直にMarker APIでゴリゴリした方がいいのかもしれませんね。

おまけ

OpenStreetMapにMarker APIで2500件プロットすると、どれくらい時間がかかるでしょうか。

ソースコード

leaflet.jsを使っています。

<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.2/dist/leaflet.css"/>
<style>
html {height: 95%;}
body {height: 100%;}
#map {height: 97%;border: 1px solid gray;}
</style>
</head>
<body>
<input type="button" value="マーカーを追加" id="add-marker"/>
<input type="button" value="マーカーを削除" id="delete-marker"/>
マーカー数=<span id="marker-num"></span>件 処理時間=<span id="result">0</span><div id="map"></div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://unpkg.com/leaflet@1.0.2/dist/leaflet.js"></script>
<script type="text/javascript">
    var ROW_COUNT = 50;
    var COL_COUNT = 50;
    var ZOOM = 10;

    $('#marker-num').text(ROW_COUNT * COL_COUNT);
    var map = L.map('map').setView([36.02085546100, 140.5274727702], ZOOM);
    L.tileLayer('http://tile.openstreetmap.jp/{z}/{x}/{y}.png', {
            id: 'osmmap',
            attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
        }
    ).addTo(map);

    var markers = new Array();
    $('#add-marker').on('click', function () {
        var startTime = new Date();
        for (var row = 0; row < ROW_COUNT; row++) {
            for (var col = 0; col < COL_COUNT; col++) {
                var lat = 35.72085546100 + (row / 95);
                var lng = 139.9274727702 + (col / 45);
                var marker = L.marker([lat, lng]).addTo(map);
                markers.push(marker);
            }
        }
        $('#result').text((new Date() - startTime) / 1000);
    });

    $('#delete-marker').on('click', function () {
        jQuery.each(marker, function () {map.removeLayer(marker)});
    });
</script>
</body>
</html>

実行結果

f:id:treeapps:20170110235251g:plain

うーん、たった2500件なんですけどね。もしかしてleaflet.jsの処理速度が遅いとかですかね?それとも実装の仕方が間違っているとか。

雑感

何か根本的に私は何かを間違えているような気がして仕方ありません。

ちなみにGeoJSONファイルは、一旦緯度・経度のcsvファイルを用意し、以下のnode_modulesを使ってcsv -> GeoJSONに変換しました。
github.com

とりあえず普通にMarker APIでプロットした方がよさそうという事が解ったので、新tree-mapsの大量プロット機能はまたGoogleMapにしようと思います。