🦁

Overpass turboで取得したGeoJSONデータをGoogle Photorealistic 3D Tilesに重ねて表示してみる

2023/05/24に公開

はじめに

この記事ではOverpass turboの簡単な使い方を解説します。具体的には東京駅周辺の喫茶店を抽出します。また、そのデータをGoogle Photorealistic 3D Tilesに重ねて表示します。

Google Photorealistic 3D TilesにGeoJSONを重ねる方法は以下の記事をご参照ください。

Overpass turboとは

Overpass turboはOpenStreetMapのデータを抽出するツールです。以下のサイトでクエリを書いて条件に合致するデータを取得することができます。また、取得したデータをGeoJSON等でダウンロードすることも可能です。

クエリを書くためにはデータに関する知識が必要です。OpenStreetMapでデータ検索できるのでどのようなデータがOpenStreetMapに存在するのか見てみましょう。右側にあるQuery featuresボタンを選択し、地図上をクリックするとその周辺のフィーチャーを検索します。

overpass query features

今回は喫茶店一覧を探したいので喫茶店の近くでクリックします。いい感じに喫茶店が見つかりました。

overpass cafe

クリックすると詳細が確認できます。amenityキーにcafeというデータが入っていそうです。

overpass starbucks

念のためにOpenStreetMapの定義を調べます。amenityはノードやエリアに使用できるキーで住民や観光客にとって重要で有用な施設に使用されます。cafe以外にもbarやrestaurant等が定義されています。cafeは飲み物・軽食を提供する場所で、amenity=cafe, name=*でタグ付けできます。

ということで、今回はamenity=cafeを抽出すると良さそうです。

喫茶店を抽出する

初めてクエリということでWizardを使います。Wizardはnode, way, relationに関して、現在表示されている領域に関するクエリを作成します。

  1. 東京駅周辺が収まるズームレベルに設定
  2. Wizardボタンをクリック

overpass wizard button

  1. 表示されたモーダル内で"amenity"="cafe"と入力

overpass query wizard

  1. build and run queryボタンをクリック
  2. クエリ結果が表示

overpass result

  1. Exportボタンをクリック
  2. 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.geojsonfetchで取得します。そのため、コード全体を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.geojsonfeatures配列の一つ一つの要素が各喫茶店の情報で、その中の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()]
      })
    ]
  });

結果は以下のとおりです。

初期設定でpitch0以外の値にすると標高が楕円体高として扱われるような挙動になり、結果的にPointがGoogle Photorealistic 3D Tilesの下に隠れてしまいます。そこで今回はpitch0としました。Google Photorealistic 3D Tiles読み込み完了後に手動で変更する(Shiftキーを押下しながらドラッグする)分には問題が発生しないようです。

それにしても東京駅周辺は喫茶店が多いですね!いちど喫茶店巡りをしてみたいものです。

まとめ

Overpass turboで取得したGeoJSONデータをGoogle Photorealistic 3D Tilesに重ねて表示しました。

GitHubで編集を提案
マップボックス・ジャパン合同会社

Discussion