日本全国津々浦々寺社仏閣コレクション by OpenStreetMap

立体地形図を手作りしてみようかな、っと(8.5) 兼 六芒星」では 地理院地図WEBとか Google Map でチマチマ調べてプロットしたんだけれども。

OpenStreetMap のネタはそのうちまとまったものを書いてもいいかなとは思っている。というか何年か前に仕事で調べて結構な資料書いたんだけど、一般向けにしつつ、ついでに最新情報を仕込みたい。OpenStreetMap は「良いもの」でもあり「良くないもの」でもある、全体的に「わんぱくな」プロジェクトなんだけれど、そういったことも書けたらいいかなと思うし。とはいえこれは「膨大な」ものであって、まぁそんなにすぐにはやらない。

というかね。「立体地形図を手作りしてみようかな、っと(8.5) 兼 六芒星」で思い出したわけですよ。あぁそういや、狙った獲物だけ情報ダウンロード出来るわ、と。

OpenStreetMap プロジェクト、というのは、一つには「Google Map Alternative」なわけね。なおかつ、「WikiPedia いんすぱいあど」な、「誰でも地図作りに参加出来る」。これがこのプロジェクトの一番大きなミッションなわけな。

このプロジェクトの至上命題はもう2つあって、一つには「誰もが使える(ソフトウェアの)インフラ整備」。Mapnik とか、mod_tile とか。OpenStreetMap との直接の関係は不明だけど、OpenLayers との親和性も高い。OpenLayers は OSGeo のプロジェクト。OpenStreetMap が OSGeo と関係あるのかないのか、よくわかんない。もう一つが、「自由な GIS データ利用」。ダウンロードしてデータだけ使える、ということ。

このね、「ダウンロードしてデータだけ使う」のデータダウンロード手段の一つに、「XAPI」(ざっぴー)があるのです。HTTP GET で、「XPath いんすぱいあど」な API で取得する。無謀なデータ取得方法には「アジア全域」「日本全域」のような全データ取得があるけれど、圧縮した状態でうん十うん百「ギカ」なんてサイズで、そんじょそこらの「ぴぃしぃ」では取り回し出来ないほどの大規模データなので、現実的じゃない。(間違って持ってこれても、osm2pgsql がメモリエラーで落ちたり、落ちなくても丸一日かかっても処理が終わらなかったり。)それに較べると XAPI での「狙い撃ちデータ取得」は、大変現実的。

てな話。

この話、「思い出した、今でも同じこと出来るかな?」程度のことで、今回のエントリではどうデータ活用するかのとこまで行くつもりがない。単に「ダウンロードしとこっかな」でおしまい。

利用に必要な情報は3つ。Xapiと、Map Features、それと、OSM ファイルの構造。OSM ファイルの構造説明は…今回はやめとく。GML と違って簡単な構造で、OSGeo の Simple Feature Model ほどには単純過ぎないもの、で、別に説明が難解なものでもないんだけれど、今回は面倒なんで。簡単に言えば OSM ファイルの構造を意識したクエリを書きます、Xapi では。

Xapi を「XPath 的なもの」とみなすには相当制約ありすぎるけれど、単一条件での絞り込みくらいは出来る。本当の制約は、「bbox queries have a maximum of 10 square degrees」という明示的な制約と、結果セットが大きくなると Bad Request となる暗黙の制約の2つ。ので、「0.5度刻みくらいでチマチマと」取りに行く、というのが正解。

てわけで、「寺社仏閣系と、鉄道の駅くらいは欲しいよねぇ」をまとめて wget するスクリプト:

 1 # -*- coding: utf-8 -*-
 2 
 3 #
 4 # build download list
 5 #
 6 import math
 7 lon_step = 0.5
 8 lat_step = 0.5
 9 
10 #http://www.overpass-api.de/api/xapi?*[religion=*][bbox=139.0608,35.6227,139.5608,35.7227]
11 #http://www.overpass-api.de/api/xapi?*[building=train_station][bbox=139.0608,35.6227,139.5608,35.7227]
12 
13 bottom_left = (138.5, 33.5)
14 top_right   = (142.5, 39.5)
15 
16 _BASEURI = "http://www.overpass-api.de/api/xapi?"
17 with open("._downloadlist", "w") as fo:
18     for x in range(int(math.ceil((top_right[0] - bottom_left[0]) / lon_step))):
19         for y in range(int(math.ceil((top_right[1] - bottom_left[1]) / lat_step))):
20             min_long = bottom_left[0] + x * lon_step
21             min_lat = bottom_left[1] + y * lat_step
22             max_long = bottom_left[0] + (x + 1) * lon_step
23             max_lat = bottom_left[1] + (y + 1) * lat_step
24 
25             # use XAPI
26             fo.write(
27                 _BASEURI + "*[religion=*][bbox=%.4f,%.4f,%.4f,%.4f]\n" % (
28                     min_long, min_lat, max_long, max_lat))
29             fo.write(
30                 _BASEURI + "*[building=train_station][bbox=%.4f,%.4f,%.4f,%.4f]\n" % (
31                     min_long, min_lat, max_long, max_lat))
32 
33 #
34 # call wget with created list
35 #
36 import subprocess
37 rc = subprocess.call(" ".join(["wget", "-c", "-i", "._downloadlist"]), shell=True)

ちょいちょいバカだけど、いいんです。はなから真面目にやる気ないんだし。「building=train_station」はわかりやすいでしょう。「religion=*」はね、「religion=shinto」「religion=buddhist」なんてのが入ってますわよ。キリスト教系のもひっかかってくるよ。

religion=* の方はこんな感じ:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <osm version="0.6" generator="Overpass API">
 3 <note>The data included in this document is from www.openstreetmap.org. The data is made available under ODbL.</note>
 4 <meta osm_base="2015-10-10T18:48:02Z"/>
 5 
 6   <node id="442152305" lat="35.4371048" lon="139.3671385">
 7     <tag k="amenity" v="place_of_worship"/>
 8     <tag k="denomination" v="catholic"/>
 9     <tag k="name" v="カトリック厚木教会"/>
10     <tag k="religion" v="christian"/>
11   </node>
12   <node id="448176637" lat="35.4853823" lon="139.3950393">
13     <tag k="amenity" v="place_of_worship"/>
14     <tag k="name" v="水上山 龍源院"/>
15     <tag k="religion" v="buddhist"/>
16   </node>
17   <node id="564634835" lat="35.4613393" lon="139.3985197">
18     <tag k="amenity" v="place_of_worship"/>
19     <tag k="name" v="弥生神社"/>
20     <tag k="religion" v="shinto"/>
21     <tag k="website" v="www11.plala.or.jp/yayoi1/"/>
22   </node>
23   <node id="564637579" lat="35.4679175" lon="139.4020130">
24     <tag k="amenity" v="place_of_worship"/>
25     <tag k="denomination:ja" v="曹洞宗"/>
26     <tag k="name" v="常泉院"/>
27     <tag k="religion" v="buddhist"/>
28   </node>
29   <node id="599912426" lat="35.4463402" lon="139.3755433">
30     <tag k="amenity" v="place_of_worship"/>
31     <tag k="name" v="稲荷山 安養院"/>
32     <tag k="religion" v="shinto"/>
33   </node>
34   <node id="663578611" lat="35.4377708" lon="139.4085008"/>
35   <node id="663578612" lat="35.4377494" lon="139.4086266"/>
36   <node id="663578613" lat="35.4378636" lon="139.4085247"/>
37   <node id="663578614" lat="35.4378421" lon="139.4086505"/>
38   <node id="676016142" lat="35.3933806" lon="139.4620820">
39     <tag k="amenity" v="place_of_worship"/>
40     <tag k="name" v="円行八幡宮"/>
41     <tag k="name:ja_rm" v="Engyō Hachimangū"/>
42     <tag k="religion" v="shinto"/>
43   </node>
44 
45   <!-- ... -->
46   <way id="52061810">
47     <nd ref="663578613"/>
48     <nd ref="663578614"/>
49     <nd ref="663578612"/>
50     <nd ref="663578611"/>
51     <nd ref="663578613"/>
52     <tag k="amenity" v="place_of_worship"/>
53     <tag k="name" v="五社神社"/>
54     <tag k="religion" v="shinto"/>
55     <tag k="wheelchair" v="limited"/>
56   </way>
57   <way id="55910762">
58     <nd ref="702017371"/>
59     <nd ref="702017374"/>
60     <nd ref="702017376"/>
61     <nd ref="702017379"/>
62     <nd ref="702017381"/>
63     <nd ref="702017371"/>
64     <tag k="amenity" v="place_of_worship"/>
65     <tag k="name" v="鈴鹿明神社"/>
66     <tag k="note" v="estimated"/>
67     <tag k="religion" v="shinto"/>
68   </way>
69   <!-- ... -->
70 
71 </osm>

「駅」のほうも似たようなもん:

  1 <?xml version="1.0" encoding="UTF-8"?>
  2 <osm version="0.6" generator="Overpass API">
  3 <note>The data included in this document is from www.openstreetmap.org. The data is made available under ODbL.</note>
  4 <meta osm_base="2015-10-10T18:50:01Z"/>
  5 
  6   <node id="257362961" lat="35.9401590" lon="139.6097084"/>
  7   <node id="257362962" lat="35.9401166" lon="139.6093643"/>
  8   <node id="257362963" lat="35.9403205" lon="139.6093125"/>
  9   <node id="257362964" lat="35.9403665" lon="139.6096631"/>
 10   <node id="264813350" lat="35.6446804" lon="139.5014334"/>
 11   <node id="428413445" lat="35.6256778" lon="139.7243784"/>
 12   <!-- ... -->
 13   <node id="1730435566" lat="35.6786902" lon="139.6934965">
 14     <tag k="amenity" v="vending_machine"/>
 15     <tag k="vending" v="public_transport_tickets"/>
 16   </node>
 17   <node id="1730435569" lat="35.6786924" lon="139.6936946"/>
 18   <node id="1730435575" lat="35.6787152" lon="139.6935148">
 19     <tag k="access" v="yes"/>
 20     <tag k="entrance" v="main"/>
 21   </node>
 22   <node id="1730435578" lat="35.6789679" lon="139.6938729"/>
 23   <!-- ... -->
 24   <node id="2217952046" lat="35.6616200" lon="139.6671654">
 25     <tag k="access" v="yes"/>
 26     <tag k="entrance" v="main"/>
 27     <tag k="name" v="下北沢駅入口"/>
 28     <tag k="source" v="image,2013-03-23;Bing"/>
 29   </node>
 30   <node id="2237786589" lat="35.7060898" lon="139.6837092"/>
 31   <!-- ... -->
 32   <way id="23753050">
 33     <nd ref="257362961"/>
 34     <nd ref="257362962"/>
 35     <nd ref="1400371223"/>
 36     <nd ref="257362963"/>
 37     <nd ref="257362964"/>
 38     <nd ref="257362961"/>
 39     <tag k="building" v="train_station"/>
 40     <tag k="name:ja" v="宮原駅"/>
 41     <tag k="note" v="estimated"/>
 42     <tag k="source" v="Bing,2007-04"/>
 43   </way>
 44   <!-- ... -->
 45   <relation id="3067866">
 46     <member type="way" ref="155588179" role="outer"/>
 47     <member type="way" ref="228930110" role="inner"/>
 48     <member type="way" ref="228930111" role="inner"/>
 49     <member type="way" ref="228930112" role="inner"/>
 50     <tag k="building" v="train_station"/>
 51     <tag k="type" v="multipolygon"/>
 52   </relation>
 53   <relation id="3808609">
 54     <member type="way" ref="98964842" role="outer"/>
 55     <member type="way" ref="286088576" role="inner"/>
 56     <member type="way" ref="27223744" role="inner"/>
 57     <tag k="building" v="train_station"/>
 58     <tag k="building:levels" v="3"/>
 59     <tag k="name" v="大宮駅"/>
 60     <tag k="type" v="multipolygon"/>
 61   </relation>
 62   <!-- ... -->
 63   <relation id="4856156">
 64     <member type="way" ref="342276644" role="outer"/>
 65     <member type="way" ref="342276673" role="outer"/>
 66     <member type="way" ref="342276629" role="outer"/>
 67     <member type="way" ref="342276670" role="outer"/>
 68     <member type="way" ref="342276622" role="outer"/>
 69     <member type="way" ref="342276645" role="outer"/>
 70     <member type="way" ref="342276648" role="outer"/>
 71     <member type="way" ref="342276672" role="outer"/>
 72     <member type="way" ref="342276633" role="outer"/>
 73     <member type="way" ref="342276639" role="outer"/>
 74     <member type="way" ref="342276618" role="outer"/>
 75     <member type="way" ref="342276667" role="outer"/>
 76     <member type="way" ref="342276630" role="outer"/>
 77     <member type="way" ref="342276657" role="outer"/>
 78     <member type="way" ref="342276678" role="outer"/>
 79     <member type="way" ref="342276631" role="outer"/>
 80     <member type="way" ref="342276666" role="outer"/>
 81     <member type="way" ref="342276638" role="outer"/>
 82     <member type="way" ref="342276647" role="outer"/>
 83     <member type="way" ref="342276609" role="outer"/>
 84     <member type="way" ref="342276607" role="outer"/>
 85     <member type="way" ref="342276660" role="outer"/>
 86     <member type="way" ref="342276612" role="outer"/>
 87     <member type="way" ref="342276650" role="outer"/>
 88     <member type="way" ref="342276621" role="outer"/>
 89     <member type="way" ref="342276668" role="outer"/>
 90     <member type="way" ref="342276643" role="outer"/>
 91     <member type="way" ref="342276654" role="outer"/>
 92     <member type="way" ref="342276626" role="outer"/>
 93     <member type="way" ref="145407027" role="outer"/>
 94     <member type="way" ref="342276625" role="outer"/>
 95     <member type="way" ref="342276653" role="outer"/>
 96     <member type="way" ref="342276642" role="outer"/>
 97     <member type="way" ref="342276655" role="outer"/>
 98     <member type="way" ref="342276632" role="outer"/>
 99     <tag k="addr:block_number" v="9"/>
100     <tag k="addr:city" v="千代田区"/>
101     <tag k="addr:country" v="JP"/>
102     <tag k="addr:full" v="東京都千代田区丸の内1-9-1"/>
103     <tag k="addr:housenumber" v="1"/>
104     <tag k="addr:neighbourhood" v="1丁目"/>
105     <tag k="addr:postcode" v="100-0005"/>
106     <tag k="addr:province" v="東京都"/>
107     <tag k="addr:quarter" v="丸の内"/>
108     <tag k="alt_name:en" v="Tokyo Station (Marunouchi)"/>
109     <tag k="alt_name:ja" v="東京駅 (丸の内)"/>
110     <tag k="building" v="train_station"/>
111     <tag k="name" v="東京駅"/>
112     <tag k="name:en" v="Tokyo Station"/>
113     <tag k="name:ja" v="東京駅"/>
114     <tag k="type" v="multipolygon"/>
115   </relation>
116 
117 </osm>

ほとんど OSM の構造、わかったでしょ? node, way, relation。node で「点」、way や relation でポリゴンを作ってるというわけ。建造物の細かな構造もこうやって管理されてるわけ。

さーて、持ってきたはいいけど、どう使おうかなぁ…。Postgis に突っ込んでしまうのが一番「王道」ではあるんだけれど、素のままもしくは独自管理の何か(libspatialindex とか使って)という手もないではないしな。とはいっても、そこそこ複雑な構造ではあるから、独自するなら「ちゃんとプログラムしないといけない」しな。だいたいにして、ワタシの今の目的だと、「何か=中心点」で十分なんだけど、そう使うにはポリゴンやらからわざわざ抽出せねばならん。

うーん、ゆっくり考えよっと。とりあえずはテキストエディタ(わたしの場合は emacs)で開いて検索する、というだけでも、「地理院地図WEBとか Google Map でチマチマ調べ」るよりはずっと快適なんだよね。だってさ、「地図上の場所でプロットされているの図」が欲しいんじゃなくて、緯度経度だけ知りたいんだから。GUI は全くいらんのです。というかその地図 WEB GUI が激重だからこそ、なかなかにストレスなわけで。テキストエディタでの検索ならさ、ネットワーク不調でも問題ないわけね。しばらくはそういう使い方だわな、ワタシの場合。













ところで、「WikiPedia いんすぱいあど」には厳重に注意ね。ライセンス問題は、「皆が行儀良ければ問題がない」ことになっているけれど、WikiPedia がそうではないのは誰でも知っていること(なにせ精神病的なパペットが暗躍する場所だ)。OpenStreetMap のデータにそんなものが紛れ込んでいるはずがない、なんて考えるのはお人好し過ぎです。

あぁ、あとねぇ、今のうちにいっておくと、「bbox が重複しないようにデータ取得」しても、結果的にデータ重複します。そりゃそうなのだ。bbox とのインターセクションで持ってくるんだから、ポリゴンは両方の bbox でヒットしてしまうのは当たり前。でな、これが osm2pgsql で問題起こします。というか、検証した何年か前(多分2年前)は問題起こしてた。そのときは手作業で(自作スクリプトで)重複を取り除く加工をしてから osm2pgsql にかける、という手間のかかることをしてた。ま、こんな情報でも誰かの役に立つかもしれんから、一応。