PostGISとGeoDjangoを使ってLeafletでGeoJSON Tile Layerを表示してみる(4) – GeoDjangoでGeoJSONを出力する –

前回で管理画面までできました。ようやくメインとなるブラウザ側と連携するところを実装したいと思います。ところでDjangoでGeoJSONってどうやるんだろうか。そんなところから今回は始まります。

GeoJSONとは

そもそもGeoJSONってなんなのよという話ですが、Wikipediaさんによると…

GeoJSONはJavaScript Object Notationを用いて空間データをエンコードし非空間属性を関連付けるファイル形式である。 属性にはポイント(住所や座標)、ライン(各種道路や境界線)、 ポリゴン(国や地域)などが含まれる。 他のGISファイル形式との違いとして、Open Geospatial Consortiumではなく世界各地の開発者達が開発し管理している点で異なる。

要は空間データを扱えるようにしたJSONですね、詳細は以下にフォーマットが定義されています。

The GeoJSON Format Specification

GeoDjangoでGeoJSONを出力する

GeoDjangoの機能を試す

GeoDjangoのドキュメントを調べてみるとそれっぽいメソッドがあったので試してみました。

$ python manage.py shell
Python 2.7.6 (default, Mar 22 2014, 22:59:56)
Type "copyright", "credits" or "license" for more information.

IPython 2.3.1 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: from demo.models import Border

In [2]: print(Border.objects.filter(id=73274).geojson()[0].geojson)
{"type":"Polygon","coordinates":[[[123.75450033,24.06346143],[123.75468433,24.06348943],[123.75469133,24.06332543],[123.75459233,24.06329143],[123.75450033,24.06346143]]]}

In [3]:

あれ? 属性(properties)が出力されませんね。どうも開発版には新しいAPIがあるみたいなんですけど、今はコレしか出来ないみたいですね。ポリゴン情報はコレを使って後は自前でどうにかするしかないのかなと考えていたんですが、念の為プラグインないかなと探してみたらdjango-geojsonというのがあるじゃないですか。

django-geojson

django-geojsonのインストール

pipでインストール。

$ sudo pip install  django-geojson
Downloading/unpacking django-geojson
  Downloading django-geojson-2.6.0.zip
  Running setup.py (path:/tmp/pip_build_root/django-geojson/setup.py) egg_info for package django-geojson

Requirement already satisfied (use --upgrade to upgrade): Django in /usr/local/lib/python2.7/dist-packages (from django-geojson)
Requirement already satisfied (use --upgrade to upgrade): six in /usr/lib/python2.7/dist-packages (from django-geojson)
Installing collected packages: django-geojson
  Running setup.py install for django-geojson

Successfully installed django-geojson
Cleaning up...

django-geojsonの有効化

プロジェクト設定を編集してプラグインを追加します。

$ vi geodjango/settings.py

以下の一行をINSTALLED_APPSの末尾に追加します。

INSTALLED_APPS = (
    'django.contrib.gis', 
    'demo',               
    'leaflet', 
    'djgeojson', # add
)

では、試してみましょう。

$ python manage.py shell
Python 2.7.6 (default, Mar 22 2014, 22:59:56)
Type "copyright", "credits" or "license" for more information.

IPython 2.3.1 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: from demo.models import Border                                          
In [2]: from djgeojson.serializers import Serializer as GeoJSONSerializer       
In [3]: print(GeoJSONSerializer().serialize(Border.objects.filter(id=73274)))
{"crs": {"type": "link", "properties": {"href": "http://spatialreference.org/ref/epsg/4326/", "type": "proj4"}}, "type": "FeatureCollection", "features": [{"geometry": {"type": "Polygon", "coordinates": [[[123.75450032600007, 24.063461431000064], [123.75468432500007, 24.06348943100005], [123.75469132500008, 24.063325431000067], [123.75459232600008, 24.063291431000035], [123.75450032600007, 24.063461431000064]]]}, "type": "Feature", "properties": {"n03_007": "47381", "n03_004": "\u7af9\u5bcc\u753a", "n03_003": "\u516b\u91cd\u5c71\u90e1", "n03_002": "", "n03_001": "\u6c96\u7e04\u770c", "model": "demo.border"}, "id": 73274}]}

In [4]:

キタ━(゚∀゚)━! 日本語が文字化けしてるけど構造は問題なさそうなのでコレを使いましょう。

GeoDjangoでGeoJSONを出力するWebAPIを作る

それではさっそく、ブラウザ側にGeoJSONを返すWebAPIを作成してみましょう。

アプリ作成

demoのままでもいいんですが、気持ちを改めて「api」という名前でアプリ作成します。するとアプリ名のディレクトリが作成され、作成したディレクトリ配下に次のようなファイルが自動生成されます。

$ python manage.py startapp api

URLマッピングを定義

vi geodjango/urls.py
from django.conf.urls import patterns, include, url
from django.contrib.gis import admin
admin.autodiscover()

urlpatterns = patterns('',
    url(r'^admin/', include(admin.site.urls)),
    url(r'^api/', include('api.urls', namespace='api')), # add
)
vi api/urls.py
# -*- coding: utf-8 -*-
from django.conf.urls import patterns, url
from djgeojson.views import GeoJSONLayerView
from demo.models import Border

urlpatterns = patterns('',
    url(r'^v1/borders.geojson$',GeoJSONLayerView.as_view(model=Border), name='borders'),
)

なお、通常はapi/views.pyに関数定義するのが一般的なようですが、今回はdjango-geojsonのページのサンプルに従いapi/urls.pyでGeoJSONLayerViewを直接呼んでいます。さて、これで「/api/v1/borders.geojson」へのアクセスで全登録データがGeoJSONで出力されるようになりました。

DjangoでCORSを無効化する

Web-APIのテストでいつもハマるのがCORS(Cross-Origin Resource Sharing)ですね。Django側で全部配置しておけば解決するんでしょうが、最初のうちは分けて試しがちです。とりあえず今回はプラグインで回避しましょう。

django-cors-headersのインストール

pipでインストール。

$ sudo pip install django-cors-headers
Downloading/unpacking django-cors-headers
  Downloading django-cors-headers-0.13.tar.gz
  Running setup.py (path:/tmp/pip_build_root/django-cors-headers/setup.py) egg_info for package django-cors-headers

Installing collected packages: django-cors-headers
  Running setup.py install for django-cors-headers

Successfully installed django-cors-headers
Cleaning up...

django-cors-headersの有効化

プロジェクト設定を編集してプラグインを追加します。

$ vi geodjango/settings.py
INSTALLED_APPS = (
    'django.contrib.gis', 
    'demo',               
    'leaflet', 
    'djgeojson',
    'corsheaders',# add
)
MIDDLEWARE_CLASSES = (
    'corsheaders.middleware.CorsMiddleware',     # add
    'django.middleware.common.CommonMiddleware', # add
)
CORS_ORIGIN_ALLOW_ALL = True # add

さて、いよいよ、クライアント側の作成です。Django側でやるのはわからないので今回は別に作ります。

LeafletとjQueryのダウンロード

以下、サイトからLeafletとjQueryの最新版をダウンロードして任意の場所に展開します。

Leaflet
jQuery

初めてのLeaflet

ダウンロードしたファイルをこんな形で配置します(お好みですが)。

$ tree -c leaflet-demo/
leaflet-demo/
├── index.html
├── js
│   ├── jquery-2.1.1.min.js
│   ├── leaflet.js
│   └── leaflet-src.js
├── css
│   └── leaflet.css
└── images
    ├── layers-2x.png
    ├── layers.png
    ├── marker-icon-2x.png
    ├── marker-icon.png
    └── marker-shadow.png

index.htmlをこんな内容で書きましょう

<!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>

    var map = L.map('map');
    L.tileLayer('https://{s}.tiles.mapbox.com/v3/{id}/{z}/{x}/{y}.png', {
        maxZoom: 30,
        attribution: 'Map data &copy; ' +
        '<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);
</script>
</body>
</html>

それではブラウザで開いてみましょう。

Leaflet_1

はい、地図が見えますね。

ベースはこれでOKですのでいよいよ、GeoJSONでデータを取得してみましょう。なお、下記コード中の取得先URLのIPは各自の環境で変更してください。

<!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>

    var map = L.map('map');
    L.tileLayer('https://{s}.tiles.mapbox.com/v3/{id}/{z}/{x}/{y}.png', {
        maxZoom: 30,
        attribution: 'Map data &copy; ' +
        '<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);

   /* GeoJSONレイヤーを追加します */
   var layer = L.geoJson();
   map.addLayer(layer);
   $.getJSON("http://x.x.x.x:8000/api/v1/borders.geojson", function (data) {
       layer.addData(data);
   });
</script>
</body>
</html>

それではブラウザで開いてみましょう。え、さっきと変わらないですって?暫くお待ち下さい。そうですね、大体5分位ですかね。マシンが悲鳴をあげなければ表示されます。

Leaflet_2

はいキタ━(゚∀゚)━!とうとう、ここまで来ましたよ。いやー長かった。もう泣きたい。え? なんだか重い? それは貴方のPCの性能が悪いんじゃないですかね? 性能がいいのにモッサリしている? それはしょうがないじゃないですか、全件データ返すんですから。だって7万件ありますから。
え? そんなの実用的じゃないですって? じゃあTile Layerにしてみちゃいますか? (いよいよ最終回)

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*