前回でようやく、PostGISに登録されているデータをGeoDjango経由でGeoJSONに出力してブラウザ上でLeafletを使って地図上に表示されるところまでできました。ところがどっこいデータ量が多いとモッサリしすぎて使いものにならないときたもんだ。ああ困ってしまった。ということで「それ、Tiled GeoJSONにすればいいじゃん?」という最終回です。
GeoDjangoでTiledなGeoJSONを出力する
実はですね、django-geojsonにはTiledGeoJSONLayerViewがいるんですよ。え? 知ってた? そんなキミの都合は知らんがな、これ使うようにするのにどんだけ苦労したと思ってんだ。いや、あんまりしてないけどね。
URLマッピングの定義変更
Tile用のURLマッピングを追加します。ちなみにこのワイルドカードでの指定、公式のサンプルの形式だと動きません。
vi api/urls.py
# -*- coding: utf-8 -*- from django.conf.urls import patterns, url from djgeojson.views import GeoJSONLayerView from demo.models import Border from api import views urlpatterns = patterns('', url(r'^v1/borders.geojson$',GeoJSONLayerView.as_view(model=Border), name='borders'), url(r'^v2/borders/(\d+)/(\d+)/(\d+).geojson$', views.BorderTiledLayer.as_view(model=Border), name='tiledborders'), )
続いてTiledGeoJSONLayerViewを継承したBorderTiledLayerクラスを定義します。
vi api/view.py
# -*- coding: utf-8 -*- from django.http import HttpResponse from demo.models import Border from djgeojson.views import TiledGeoJSONLayerView class BorderTiledLayer(TiledGeoJSONLayerView): # Options srid=4612 trim_to_boundary=False properties=('n03_001','n03_003','n03_004','n03_007')
直接呼んでもいいのですがオプションが多いので継承した方がシンプルです。因みにオプションの「trim_to_boundary」をFalseにしないと動かなかったのです、これも罠です。
さて、今度は、クライアント側のTile対応です。
Leaflet GeoJSON Tile Layerのダウンロード
Djangoだけでなく、Leafletもプラグインが存在しています。そして今回は「Leaflet GeoJSON Tile Layer」というその名の通りのプラグインを使います。
上記サイトから「TileLayer.GeoJSON.js」を取得して以下のように配置します(何度も言いますがお好みでよいのですが)。
$ tree -c leaflet-demo/ leaflet-demo/ ├── js │ ├── jquery-2.1.1.min.js │ ├── TileLayer.GeoJSON.js │ ├── leaflet.js │ └── leaflet-src.js ├── index.html ├── css │ └── leaflet.css └── images ├── layers-2x.png ├── layers.png ├── marker-icon-2x.png ├── marker-icon.png └── marker-shadow.png
index.htmlをこんな内容で改造(「TileLayer.GeoJSON.js」のREADME.mdのまま)
<!DOCTYPE html> <html> <head lang="ja"> <meta charset="UTF-8"> <title>Leafletテスト</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="css/leaflet.css" /> </head> <body> <div id="map" style="width: 960px; height: 600px"></div> <script src="js/leaflet.js"></script> <script src="js/jquery-2.1.1.min.js"></script> <script src="js/TileLayer.GeoJSON.js"></script> <script> var map = L.map('map'); L.tileLayer('https://{s}.tiles.mapbox.com/v3/{id}/{z}/{x}/{y}.png', { maxZoom: 30, attribution: 'Map data © ' + '<a href="http://openstreetmap.org">OpenStreetMap</a> contributors, ' + '<a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, ' + 'Imagery © <a href="http://mapbox.com">Mapbox</a>', id: 'examples.map-i875mjb7' }).addTo(map); map.setView([35.85556, 139.65117], 15); var style = { "clickable": true, "color": "#00D", "fillColor": "#00D", "weight": 1.0, "opacity": 0.3, "fillOpacity": 0.2 }; var hoverStyle = { "fillOpacity": 0.5 }; var geojsonURL = 'http://192.168.1.15:8000/api/v2/borders/{z}/{x}/{y}.geojson'; var geojsonTileLayer = new L.TileLayer.GeoJSON(geojsonURL, { clipTiles: true, unique: function (feature) { return feature.id; } }, { style: style, onEachFeature: function (feature, layer) { if (feature.properties) { var popupString = '<div class="popup">'; for (var k in feature.properties) { var v = feature.properties[k]; popupString += k + ': ' + v + '<br />'; } popupString += '</div>'; layer.bindPopup(popupString); } if (!(layer instanceof L.Point)) { layer.on('mouseover', function () { layer.setStyle(hoverStyle); }); layer.on('mouseout', function () { layer.setStyle(style); }); } } } ); map.addLayer(geojsonTileLayer); </script> </body> </html> </body> </html>
それではブラウザで開いてみましょう。
はいキタ━(゚∀゚)━! もうサックサクじゃないですかー。ついでにマウスオーバースタイルとかポップアップまでできちゃってますよー。すごい。もう感動しますわ。マジで。
とはいえ、流石にズームを引いていくと取得するTile数が増えるのでサーバ側が悲鳴を上げると思いますし、クライアント側の描画も辛くなっていくと思います。その辺りはまだまだ改善の余地があります。また、GeoDjangoは当然空間検索ができるのでそういうった機能と組み合わせていくと楽しいかなと思います。それではみなさんよいクリスマスをお過ごしください。