Overpass turboで取得したGeoJSONデータをGoogle Photorealistic 3D Tilesに重ねて表示してみる
はじめに
この記事ではOverpass turboの簡単な使い方を解説します。具体的には東京駅周辺の喫茶店を抽出します。また、そのデータをGoogle Photorealistic 3D Tilesに重ねて表示します。
Google Photorealistic 3D TilesにGeoJSONを重ねる方法は以下の記事をご参照ください。
Overpass turboとは
Overpass turboはOpenStreetMapのデータを抽出するツールです。以下のサイトでクエリを書いて条件に合致するデータを取得することができます。また、取得したデータをGeoJSON等でダウンロードすることも可能です。
クエリを書くためにはデータに関する知識が必要です。OpenStreetMapでデータ検索できるのでどのようなデータがOpenStreetMapに存在するのか見てみましょう。右側にあるQuery featuresボタンを選択し、地図上をクリックするとその周辺のフィーチャーを検索します。
今回は喫茶店一覧を探したいので喫茶店の近くでクリックします。いい感じに喫茶店が見つかりました。
クリックすると詳細が確認できます。amenity
キーにcafe
というデータが入っていそうです。
念のためにOpenStreetMapの定義を調べます。amenityはノードやエリアに使用できるキーで住民や観光客にとって重要で有用な施設に使用されます。cafe以外にもbarやrestaurant等が定義されています。cafeは飲み物・軽食を提供する場所で、amenity=cafe
, name=*
でタグ付けできます。
ということで、今回はamenity=cafe
を抽出すると良さそうです。
喫茶店を抽出する
初めてクエリということでWizardを使います。Wizardはnode, way, relationに関して、現在表示されている領域に関するクエリを作成します。
- 東京駅周辺が収まるズームレベルに設定
- Wizardボタンをクリック
- 表示されたモーダル内で
"amenity"="cafe"
と入力
- build and run queryボタンをクリック
- クエリ結果が表示
- Exportボタンをクリック
- GeoJSONのdownloadボタンをクリック
データを加工する
Google Photorealistic 3D Tiles上でPointデータが隠れないように表示するためには少し標高を高めに設定する必要がありました。ここでは以下のようなPythonスクリプトでPointデータの座標に標高1mを追加しました。
import sys
import json
featureCollection = json.loads(sys.stdin.read())
features = []
for feature in featureCollection['features']:
if (feature['geometry'] ['type'] != 'Point'):
features.append(feature)
continue
feature['geometry']['coordinates'].append(1)
features.append(feature)
featureCollection['features'] = features
print(json.dumps(featureCollection, ensure_ascii=False))
使い方は以下のとおりです。
% cat export.geojson| python convert.py > cafe.geojson
cafe
はnode以外にもwayやareaとして定義することも可能で、その場合GeoJSONではPolygon等になります。今回はこれを無視してPointのみを表示していますが、実際にはPolygon等も変換して表示できるようにするのがよいでしょう。
コードを書く
まず、以下のライブラリを読み込みます。
<script src="https://unpkg.com/deck.gl@^8.9.0/dist.min.js"></script>
CSSを設定します。
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
次に地図を表示する場所を作ります。
<div id="map"></div>
次は、JavaScriptのコードです。Gistに置いたcafe.geojson
をfetch
で取得します。そのため、コード全体をasync
で囲んでいます。
(async() => {
const data = await (
await fetch(
"https://gist.githubusercontent.com/OttyLab/f4526ddf444b8f4add296ad337bcc601/raw/b0923efe5b9cec9938bda3e750c2aa0bb23a3ffb/cafe.geojson"
)
).json();
...
})();
中身を見ていきます。まず、deck.jsのTextLayerはデフォルトでは日本語が扱えません。そこで、使用する文字セットを抽出する必要があります。こちらのサイトを参考に以下のコードを記述しました。
const textCharacterSet = new Set(
data.features
.filter((f) => f.properties.name)
.map((f) => Array.from(f.properties.name))
.flat()
);
cafe.geojson
のfeatures
配列の一つ一つの要素が各喫茶店の情報で、その中のproperties
に喫茶店のプロパティが入っています。name
プロパティがない場合はスキップ(.filter(f => f.properties.name)
)し、ある場合はname
プロパティから文字列を取得して位置文字ずつの配列にし(.map(f => Array.from(f.properties.name))
)、更に入れ子になっている配列をフラットにします(.flat()
)。最後にSet
にすることで重複を排除します。これで、name
プロパティで使用されている文字の一覧を作成できました。
最後にdeck.glのコードです。基本的にはこちらの記事と同じ内容です。
const deckgl = new deck.DeckGL({
container: "map",
initialViewState: {
latitude: 35.681054,
longitude: 139.768435,
zoom: 16,
pitch: 0
},
controller: true,
layers: [
new deck.Tile3DLayer({
id: "tile-3d-layer",
data: "https://tile.googleapis.com/v1/3dtiles/root.json",
loadOptions: {
fetch: {
headers: {
"X-GOOG-API-KEY": YOUR_GOOGLE_API_KEY
}
}
},
operation: "terrain+draw"
}),
new deck.GeoJsonLayer({
id: "geojson-layer",
pointType: "circle+text",
data,
getPointRadius: 4,
getFillColor: [255, 165, 0],
getText: (f) => {
return f.properties.name;
},
getTextColor: [255, 165, 0],
getTextBackgroundColor: [64, 64, 64, 200],
textBackground: true,
getTextPixelOffset: [0, -10],
getTextSize: 10,
textCharacterSet,
extensions: [new deck._TerrainExtension()]
})
]
});
結果は以下のとおりです。
初期設定でpitch
を0
以外の値にすると標高が楕円体高として扱われるような挙動になり、結果的にPointがGoogle Photorealistic 3D Tilesの下に隠れてしまいます。そこで今回はpitch
を0
としました。Google Photorealistic 3D Tiles読み込み完了後に手動で変更する(Shiftキーを押下しながらドラッグする)分には問題が発生しないようです。
それにしても東京駅周辺は喫茶店が多いですね!いちど喫茶店巡りをしてみたいものです。
まとめ
Overpass turboで取得したGeoJSONデータをGoogle Photorealistic 3D Tilesに重ねて表示しました。
Discussion