KMLをGeoJSONに変換する方法と、私が「KMLは爆発しろ」と思う理由
「ケイエムエルみな殺すべし……ハイクを詠め。カイシャクしてやる」
先日、上記のような発表があって驚愕していたのですが、愚痴っても仕方がないので各自治体から公開される地理データがKMLになっても困らないようにと調べたGeoJSONへの変換方法を記載しておきます。
ちなみに今回はGoogle「マイマップ」で公開されている群馬県が誇る「焼まんじゅうマップ」を例題として使用させていただきました。
メニューにある下記の何と呼んでいいのか分からないボタンをクリックするとKMLをダウンロードできます。
なお、KMLが嫌いな理由は最後の方に書いてあります。
(GMLは、また別の機会に)
ogr2ogr web client
手っ取り早くKMLをGeoJSONに変換するなら「ogr2ogr web client」を使うのが簡単です。KML/KMZファイルを選択して「Convert to GeoJSON」ボタンを押すだけです。
他にも、「2GeoJSON」というサービスもあります。
ogr2ogr
Webサービスは手軽に使えて便利ですが「データの更新があった際に毎回手動でコンバートをするのはメンドウ! 自働化したい!」と思われる方もいらっしゃるでしょう。ogr2ogrコマンドを利用するとコンバート作業をバッチ処理に組み込めます。
参考:OGR のコマンド解説と使い方レシピ
1 |
ogr2ogr -f GeoJSON 焼きまんじゅうマップ.geojson 焼きまんじゅうマップ.kml |
一点問題があって、KMLの場合は問題ないのですがKMZ(KMLをZIP圧縮したもの)の場合、一度KMLにunzipしてからogr2ogrでコンバート処理を行う必要があるみたいです。(ogr2ogrにunzipオプションのようなものがあるかな?と思ったのですが見つかりませんでした)
togeojsonライブラリを使う
togeojsonライブラリはMapbox社製のGeoJSONコンバータです。node.jsを実行環境としていてサーバーサイドでコンバート処理が行えるのはもちろん、npmでインストールする際にグローバルオプションをつければshellコマンドとしても利用できます。また、通常のjsライブラリとしてscriptタグで読み込めばフロントエンドでも動くという汎用性の高いライブラリです。
shellコマンドとして実行する
1 |
togeojson 焼きまんじゅうマップ.kml > 焼きまんじゅうマップ.geojson |
サーバーサイドで実行
1 2 3 4 5 6 7 8 9 10 |
var tj = require('togeojson') var fs = require('fs'), var jsdom = require('jsdom').jsdom; var kml = jsdom(fs.readFileSync('焼きまんじゅうマップ.kml', 'utf8')); var converted = tj.kml(kml); console.log(converted); |
※jsdomがver4.xになってからio.jsに移行したらしくnode.jsで動かそうとすると「jsdom 4.x onward only works on io.js, not Node.js」とエラーが表示されます。
xmlをパースしているだけらしいので、他の方法が見つかったらここに追記します。
フロントエンドで実行する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> <style> html, body { width: 100%; height: 100%; } textarea { display: inline; width: 48%; height: 100%; } </style> </head> <body> <textarea id="kml"></textarea> <textarea id="geojson"></textarea> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.min.js"></script> <script src="togeojson.js"></script> <script> d3.xml('焼きまんじゅうマップ.kml', function(kml){ var kml_area = document.querySelector("#kml"); var geojson_area = document.querySelector("#geojson"); var geojson = toGeoJSON.kml(kml); //kml を geojsonに変換 kml_area["innerText" in kml_area ? "innerText" : "textContent" ] = new XMLSerializer().serializeToString(kml); geojson_area["innerText" in geojson_area ? "innerText" : "textContent" ] = JSON.stringify(geojson, null, "t"); }); </script> </body> </html> |
description問題
さて、このようにKMLをGeoJSONに変換するのは比較的簡単です……変換するだけなら。
コンバートした「焼きまんじゅうマップ(GeoJSON)」の一部を見てみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{ "type": "Feature", "geometry": { "type": "Point", "coordinates": [ 139.085541, 36.383978, 0 ] }, "properties": { "name": "日赤前たなかや(田中屋日赤前店)", "description": "<img src="http://pds.exblog.jp/imgc/i=http%253A%252F%252Fpds.exblog.jp%252Fpds%252F1%252F201204%252F07%252F20%252Fa0243720_1730475.jpg,small=800,quality=75,type=jpg" height="200" width="auto" /><br><br>★日赤前たなかや (http://mesousa2.exblog.jp/14998494/)※クリックでブログ記事<br>◆群馬県前橋市朝日町3-12-7<br>◆9:00~18:30 ◆月曜休み ◆駐車場6台<br>◆焼まんじゅう(¥170)、あん入り焼まんじゅう(¥250)、<br>黒ごまあん入り焼まんじゅう(¥250)、ミックス(¥210)、<br>ひとくち焼まんじゅう(¥70)、タレのミニボトル(小~中~大)", "gx_media_links": "http://pds.exblog.jp/imgc/i=http%253A%252F%252Fpds.exblog.jp%252Fpds%252F1%252F201204%252F07%252F20%252Fa0243720_1730475.jpg,small=800,quality=75,type=jpg" } }, |
問題はpropertiesの中の「description」の項目です。
Googleが策定している基本的なKMLドキュメントの構造は以下です。
- XML ヘッダー。すべての KML ファイルの 1 行目です。この行の前にスペースや他の文字を入力することはできません。
- KML 名前空間宣言。すべての KML 2.2 ファイルの 2 行目です。
- 次の要素を含む目印オブジェクト:
- name: 目印のラベルとして使用される名前
- description: 目印の「バルーン」に表示される説明
- Point: 地表での目印の位置を指定する(longitude、latitude、オプションの altitude)
「description」は地図上でマーカーをクリックした際にバルーン(info Window)の内容が書きこまれる場所となっています。KMLでは属性データの取り扱いが厳密に決められていないのか、結果、descriptionの一項目の中に全ての情報が登録されていることが非常に多いです。しかもHTMLで。
改めてマイマップで公開されていた「焼きまんじゅうマップ」データを見てみましょう。
「description」の中に、画像(imgタグ)、住所、営業時間、定休日、詳細へのリンクがひとまとめされて書きこまれています。
このデータから各店の焼きまんじゅう画像を取り出して表示したい場合はどうしたらいいでしょうか?
descriptionの値を正規表現等を使って独自にパースしなければなりません。
このデータを住所を元にカテゴライズしたい場合はどうしたらいいでしょうか?
descriptionの値を正規表現等を使って独自にパースしなければなりません。
定休日ごとにマーカーの色を変えたい場合は?
descriptionの値を正規表現等を使って独自にパースしなければなりません。
ブログ記事へのURL一覧を取得したい場合は?
descriptionの値を正規表現等を使って独自にパースしなければなりません。
アイエエエエ! ケイエムエル!? ケイエムエルナンデ!?
スゲーめんどうです。
マイマップで公開されているデータとか、Fusion Tablesからエクスポートしたデータとか全部こういう仕様なので、KMLってメンドウなデータ形式という印象しかありません。
【追記】
同じようにdescriptionの中身に苦しんでいたのか、NSA(アメリカ国家安全保障局)がKMLのdescriptionをパースするQGISプラグインを作成していました。
ありがたい。