OSMのライン、ポリゴンの簡単化については記事[1]に述べた。 本ページでは具体例として陸地ポリゴンについて述べる。
OSMでは陸地ポリゴンは森林ポリゴン(natural=wood)、湖などの水域ポリゴン(natural=water)など 他のポリゴンとは少し扱いが異なっている。
wayオブジェクトとしては無数の海岸線データ(natural=coastline) としてマッピングされている。 これは広域森林などと同じである。
違いは、広域森林や巨大な湖などは一つの閉ループ状の wayオブジェクトではなく、分断された境界線の 集合であり、それを繋ぎ合わせる relationオブジェクトが存在する。 つまり、広域森林や湖などは一つの relationオブジェクトであり、wayオブジェクトはそのメンバーである。
陸地ポリゴンについては、島については place=island という relationオブジェクトがあり、 そのメンバーが海岸線wayオブジェクトになっている。
しかし、ヨーロッパ大陸、アジア大陸、あるいはユーラシア大陸といった relationオブジェクトは見当たらない。 誰かが、何らかのタグを付けてそれらに該当する relationオブジェクトを作っているかも知れないが、 標準OSM地図は別の方法で陸地をレンダリングしている。
概念的には大陸 relation によって管理される方がきれいであるが、巨大過ぎるため、実現が難しいのであろう。
relationオブジェクトはなくても、wayオブジェクト(natural=coastline)を繋ぎ合わせれば、 ユーラシア大陸やアフリカ大陸などになる。
標準OSM地図は、この繋ぎ合わせたデータを使って、陸地をレンダリングしている。 エラーが入り込んで、うまく繋がらないときは、陸地ポリゴンデータは更新されない。 時間的には一致しない陸地ポリゴンデータとその他のOSMデータを使って 標準OSM地図はレンダリングされている。したがって、海岸線付近のレンダリングはごくまれには 海の中に建物があるといった矛盾が起きても不思議はない。
本ページでは、OSMデータから、海岸線ライン(natural=coast)オブジェクトを取り出して、 それを繋ぎ合わせて、陸地ポリゴンとすることを試みる。 OSMデータの海岸線データにエラーが多い場合には上手くいかないが、エラーが少なければ大丈夫であろう。それを確かめる。
大陸は巨大なポリゴンとなるため、スマホではメモリ使用量の制約から、描画が難しい。 このため、ポリゴンの分割が必須となる。一方、低ズームの場合、緻密な海岸線データは要らない。 そこで、海岸線(折れ線データ)を構成するノード(頂点)を間引くことにする。
間引きでは、自己交差(ねじれ)が発生することがあり、それにより、陸地ポリゴンの塗りつぶしに 問題が生じることもあるが、塗りつぶしではなく、海岸線の描画では自己交差が起きても問題はないので、 自己交差が起きるかどうかも確認できる。
陸地ポリゴンの簡略化の実行結果を下に示す。 赤字に注目する。
map180=79 は、東経180度のデータの数である。これに対応して緯度は同じで、 西経180度のデータがほぼ同数ある。 対応する2点は同じとして処理しているが、この処置に誤りがあるかも知れない。
ポリゴンの面積が0というのはおかしいが、OSMには無意味なデータが散在しているので、 それほど以上ではない。OSMは先頭と末尾が同じため、ノード数が3では三角形にもならない。
1点だけノードの座標値が得られていないが、沢山あるノードの途中の一つであることを確認済みであり、 これを除いても問題はない。
閉じない海岸線が一つあった。
c:\gisa>java -Dfile.encoding=UTF-8 -Xmx5000m LandPolygon -shrink ノード座標値読み込み完了 num_node=69994719 map180=79 procCoastLine antarctica WayArea == 0, ノード数=64 WayArea == 0, ノード数=4 mapCoastLines エントリ数=0 procCoastLine africa mapCoastLines エントリ数=66 procCoastLine europe mapCoastLines エントリ数=74 procCoastLine asia mapCoastLines エントリ数=4 procCoastLine north-america WayArea == 0, ノード数=3 WayArea == 0, ノード数=3 Error 座標値が得られない Id=7670000000 mapCoastLines エントリ数=8 procCoastLine central-america mapCoastLines エントリ数=6 procCoastLine south-america mapCoastLines エントリ数=4 procCoastLine australia-oceania mapCoastLines エントリ数=2 閉じない海岸線: ノード数=3 (-1800000000 675522267)~(-1800000000 669296325) 実行時間=2.04分
こうして得られたポリゴンの境界線を描画した結果を下に示す。 この地図の範囲では誤まったポリゴンが少なくとも三つある。
間引きは行わず、ポリゴンを分割した場合にも似たような結果となる。 また、間引きによる描画エラーは塗りつぶしには現れるかも知れないが、原理的にライン描画ではエラーは起きない。
拡大してみると、北半球、南半球とも白線は7、8本あることが分かった。 やはり、東経180度と西経180度の処理を間違えている可能性が高い。
Overpass APIで世界中の natural=coastlineデータをダウンロードした際には、 ひとつのエラーもなく閉ループが構成された。
ただし、Overpass APIでは世界規模となると、nodeの座標情報まで一緒には入手できないため、描画は確認できていない。
東経180度、西経180度というノードは Geofabrik が europe.osm.pbf、asia.osm.pbf というように分割する都合で生まれたものかも知れない。
東経180度と西経180度の点は同じものとせず、異なるものとして扱ってみる。
結果は下に示すように閉じない海岸線が多数生まれた。
閉じない海岸線: ノード数=5014 (1800000000 715317037)~(1800000000 709758500) 閉じない海岸線: ノード数=185 (1800000000 -164823229)~(1800000000 -165276519) 閉じない海岸線: ノード数=848 (1800000000 -167934111)~(1800000000 -169667229) 閉じない海岸線: ノード数=3 (-1800000000 675522267)~(-1800000000 669296325) 閉じない海岸線: ノード数=593 (1800000000 689771899)~(1800000000 689756800) 閉じない海岸線: ノード数=6776500 (1800000000 689698101)~(1800000000 650388028) 閉じない海岸線: ノード数=58753 (-1800000000 650388028)~(-1800000000 689771899) 閉じない海岸線: ノード数=20564 (-1800000000 709758500)~(-1800000000 715317037) 閉じない海岸線: ノード数=9 (1800000000 -167688122)~(1800000000 -167699960) 閉じない海岸線: ノード数=8 (1800000000 -161393939)~(1800000000 -161404285) 閉じない海岸線: ノード数=5 (-1800000000 -161404285)~(-1800000000 -161415979) 閉じない海岸線: ノード数=1312 (-1800000000 -161685081)~(-1800000000 -161393939) 閉じない海岸線: ノード数=799 (-1800000000 -165276519)~(-1800000000 -164823229) 閉じない海岸線: ノード数=1111 (-1800000000 -169667229)~(-1800000000 -167934111) 閉じない海岸線: ノード数=27 (-1800000000 -167699960)~(-1800000000 -167688122) 閉じない海岸線: ノード数=8 (-1800000000 689756800)~(-1800000000 689698101) 閉じない海岸線: ノード数=36307 (1800000000 -161415979)~(1800000000 -161685081)
これらを無視して海岸線を描画すると下のようになった。 地球を横断する水平な線は全て消えたが、ユーラシア大陸やイングランドが描画されない。
恐らく、上記の閉じない海岸線に適切な補助線を補い閉ループを完成させるのが正解であろう。
最初の図は次のように、東経180度のノードの緯度をキー、ノードId を値として HashMap map180 に登録しておき、
西経180度で同じ緯度をもつノードがあったら、東経180度のノードとみなしていた。 しかし、これでは、いけないようだ。
if (lon == 1800000000) { //System.out.printf("id=%d: lon=%d, lat=%d\n", nid, lon, lat); map180.put(lat, nid); } int lon = lons[ix]; int lat = lats[ix]; if (lon == -1800000000 && map180.containsKey(lat)) { listNodes.add(map180.get(lat)); } else { listNodes.add(nid); }
念のため、上記のプログラムで 180度 と -180度を入れ替えてみた。
map180 に登録される数は変わったが、描画結果は同じであった。
ノード座標値読み込み完了 num_node=69994719 map180=55 procCoastLine antarctica WayArea == 0, ノード数=64 WayArea == 0, ノード数=4 mapCoastLines エントリ数=0 procCoastLine africa mapCoastLines エントリ数=66 procCoastLine europe mapCoastLines エントリ数=74 procCoastLine asia mapCoastLines エントリ数=4 procCoastLine north-america WayArea == 0, ノード数=3 WayArea == 0, ノード数=3 Error 座標値が得られない Id=7670000000 mapCoastLines エントリ数=8 procCoastLine central-america mapCoastLines エントリ数=6 procCoastLine south-america mapCoastLines エントリ数=4 procCoastLine australia-oceania mapCoastLines エントリ数=2 閉じない海岸線: ノード数=3 (-1800000000 675522267)~(-1800000000 669296325)
平面的な地図に描画する都合上、西経180度(東経180度)をまたぐポリゴンは 西経180度線で分割しなければならないのかも知れない。
もしそうだとすれば、西Aと東A と対になる 西Bと東B を見つけて、 垂直線分西A-西B および 垂直線分東A-東B を補い、二つの閉ループとしなければならない。
西Aと東A、西Bと東B、を同一視して一つの閉ループとするのは誤まりである。 二つのノードに分かれているのであるから、同一視すべきでないのかも知れない。
上のデータでは対にならないデータが一つだけあるが、取りあえず、始点と終点を結び全て閉ループとしてみる。
結果を以下に示す。こうするのが正解のようだ。陸地ポリゴンの境界線(下図)を見る限り不審は見当たらない。
LandPolygon の出力ファイルが 62.9MB に対して、分割したファイルの合計が 24.9MB となった。 分割ファイルは座標値は可能な場合、差分コードを使う。全てのレコードで差分コード化が使われても、 サイズ的には 1/2強のはずであるが、1/2以下になっている。
描画結果に異常が見つかれば原因を探るが当面はこのままとする。
境界線の描画から陸地ポリゴンの塗りつぶしに戻す。よく見るとカスピ海が消えたことがわかる。 カスピ海の海岸線はユーラシア大陸ポリゴンに包含されている。 このような場合、OSMではマルチポリゴンと読んでいるが、空洞のある穴あきマルチポリゴンとすれば、 正しくレンダリングされる。ユーラシア大陸を outer polygon、カスピ海を inner polygon とすれば、 正しく描画される。
いずれ世界地図については、国境、国名、広域森林、水域(広大な湖など)、砂漠、草原などをレンダリングする 予定であり、その時、カスピ海は水域(natural=water)としてレンダリングするため、陸地描画ではこの状態でもよい。
一方、何故黒海が描画されているかと言うと、地図を拡大してみれば分かるが(この図では無理)、 黒海はユーラシア大陸に包含されているわけではなく、地中海、更には海へと川で繋がっている。 つまり、一筆書きのユーラシア大陸の海岸になっている。このため、マルチポリゴンを使わなくても、 黒海が描画される。
europe.osm.pbfや asia.osm.pbf などは巨大なため、前処理には時間がかかるが、 海岸線データを繋ぎ合わせて、陸地ポリゴンを作り出したり、低ズーム用にノードを間引いて ファイルサイズを小さくする処理には時間がかからない。
デバッグに多少日数がかかったが、Geofabrikのサイトからダウンロードしたデータで陸地を描画できるようになった。
低ズームについては、ポリゴンの分割は要らないが、 高ズームについては精度を保つために、間引きは行わないが、 そのままでは一つのポリゴンデータのサイズがスマホには大きすぎることとレンダリングに時間がかかりすぎるため、 ポリゴンの分割が必要である。これについては、ページを改める。