🗺️

FlatGeobufを使ったサーバレスな逆ジオコーディング

6 min read

この記事ではFlatGeobufという地理空間データ形式を使って、緯度経度などの座標から住所の情報を取得する逆ジオコーディングを実装した例を紹介します。

デモはこちらからどうぞ。

FlatGeobufとは?

https://flatgeobuf.org/

空間情報(道路や空港など位置をもつ情報)のうち、ベクタデータを保存する形式の一つです。特徴はざっくりいうと下記の三点です。

  • HTTPなどを介してストリーミングアクセスが可能。
  • ストリーミングアクセス時に、範囲指定で空間フィルタリングが可能。
  • ジオメトリ(点や線、ポリゴンなどの形状)を簡略化していないため解析処理も可能

FlatGeobufをHTTPからアクセスできるところに置けば、ベクタの生データが空間検索可能な形で配信できるようになります。
しかも、ファイルを置くだけでOKです。(実際にはCORSなどちょこっと設定する箇所がありますが)

これらの特徴から、AWS S3やGoogle Cloud Storageなど、クラウドストレージを使った配信に適したフォーマットと言えると思います。開発された方もクラウドストレージでの利用を念頭に置いているようです。

なお、FlatGeobufの読込・書込にはGDAL3.1以降や、QGIS3.16以降で対応しているため、変換処理のハードルはあまり高くありません。

よく使われるPostGISのようなデータベースや、MapServer・GeoServerに依存せず、クラウドストレージにデータを置くだけで、逆ジオコーディングのような機能が実現できます。

デモアプリ

こちらで実際に逆ジオコーディングが出来るデモアプリが確認できます。

デモアプリ使用時の注意

デモアプリの中では、Cloudflare Workersを使って逆ジオコーディング処理を行っています。
Cloudflare Workersへのリクエストを行う際に、入力された位置情報(緯度・経度)を送信しています。 また、Cloudflare Workersへのアクセスログを取得しています。こちらで個人を識別することはできませんが、自宅など、あまり知られたくない位置情報は送信しないことをお勧めいたします。

やったこと

こんな感じのことをやりました。

  • データの加工・変換
  • FaaS内での逆ジオコーディング
  • 地図を見ながら座標を指定できるフロントエンド部の開発

使用するデータセット

GeoNLPプロジェクトという「地名」の研究プロジェクトの一環で、Geoshapeリポジトリに、国勢調査町丁・字等別境界データセットという町丁・字の境界データ(ポリゴン)が公開されています。このデータはTopoJSON(トポロジを使ってGeoJSONよりも圧縮されてる形式)で公開されています。今回は、こちらをダウンロードさせていただきました。Shapeファイルのように広く流通している形式ではないのですが、WebGISなどでは比較的扱いやすい形式です。また、ベクトルタイルでも提供されているようです。

TopoJSONはQGISなどで読み込むことができるので、こちらを使って手持ちのデータに町丁レベルの住所情報を付加させられたりします。
実際のデータの中身を見てみると、こんな感じです。

「東京都文京区後楽1丁目」のような、町丁・字レベルでの領域が分割されています。画像中では、データ内にあった人口の情報を使って色分けを行っています。黄色いほうが人口が多く、青いほうが人口が少ないという区分になります。

関東圏全体で見てみると、山間部などの人口の少ない地域では町丁・字の面積がかなり広く取られていて、大都市圏では狭小にもかかわらず人口が密集していることがわかります。

話を本題に戻しまして、今回はこちらのTopoJSONデータを結合して巨大なベクタデータを作り、FlatGeobufに変換しました。

逆ジオコーディングをやってみる

データさえ用意してしまえば、内部処理は簡単です。

  1. 求めたい座標を指定して、ピンを刺す
  2. ピンにバッファを持たせて、バウンディングボックスを作る
  3. バウンディングボックスの中にあるデータだけを取得する(FlatGeobufが得意な処理)
  4. ピンとのINTERSECTS条件に一致したデータの属性情報を読み取り、住所を割り出す

という感じです。INTERSECTSの判定はturf.jsを使っています。

https://turfjs.org/

実際のコードはこんな感じです。

import { geojson } from "flatgeobuf"
import booleanIntersects from "@turf/boolean-intersects"
import { polygon, point } from "@turf/helpers"

const getTownName = (x, y) => {
  //対象地点
  const target = point([x, y])
  //バウンディングボックスのバッファ
  const buffer = 0.0000001
  //検索対象にするバウンディングボックスの作成
  const rect = { minX: x - buffer, maxX: x + buffer, minY: y - buffer, maxY: y + buffer }


  //空間フィルタリングをかけながらデータ取得
  for await (const feature of geojson.deserialize(fgbURL, rect)) {
    const prop = feature.properties
    const pref = prop["PREF_NAME"] ? prop["PREF_NAME"] : ""
    const city = prop["CITY_NAME"] ? prop["CITY_NAME"] : ""
    const town = prop["S_NAME"] ? prop["S_NAME"] : ""
    const townName = pref + city + town
    //turfのポリゴンへ
    const geom = polygon(feature.geometry.coordinates)
    //ここでINTERSECTSを判定してさらに絞り込む
    const intersects = booleanIntersects(target, geom)
    if (intersects) return townName
  }
  return "not found."
}

今回、FlatGeobufはGoogle Cloud Storage上に配置しています。リクエストヘッダでRangeを指定出来るようになっていれば、S3などの環境でも動作すると思います。
ヘッダにRangeを含むため、単純リクエストにはならないことに少し注意が必要です。

また、CDNを使う場合には、CORSによって発生するプリフライトをキャッシュできるようにしたほうが良いそうです。

Popular CDNs, like Cloudfront, support Range Requests, but don’t cache the requisite preflight OPTIONS requests by default. Consider enabling OPTIONS request caching . Without this, the preflight authorization request could be much slower than necessary.

https://flatgeobuf.org/

今回は、Cloudflare WorkersというFaaSで一連の解析(地名を返すところまで)を実装していますが、クライアントサイドで直接解析を行うことも可能です。ただし、現在のFlatGeobufのJavaScript実装は、古めのSafariでは動かないことがあります。(BigInt関連が未実装の場合あり)

一方でFlatGeobufは、属性を検索するような用途(例:全国の「〇〇村」を抽出」)には向いていません。あくまで、空間インデックスによる空間検索に向いたフォーマットです。通常のジオコーディングのような場合には、各種DBを使ったほうが相性がよいと思います。

デモアプリの操作説明

こちらのデモアプリの簡単な操作説明を記載します。

地図をクリックしたりタップするとマーカーが設置されます。
マーカーを設置した後に、「マーカーの住所を検索」ボタンを押すと、住所が表示されます。

Cloudflare Workers内でのFlatgeobuf取得が遅いのが速度面での課題なので、R2が開始されたら、乗り換えてみたいとおもいます。

一般的な逆ジオコーディングAPI

よく見かける逆ジオコーディングのAPIを紹介します。

国土地理院のAPIはたぶん元データが同じなので、似たような情報を返しています。Nominatimはおそらく一番近いPOIの住所を返しているようです。

最後に

アプリの中でちょっと空間情報の生データを使って何かやりたいが、PostGISやMapServerなどを立てるのはしんどい…というケースで、FlatGeobufは有用な特性を持っていると思います。

HTTP経由の配信に最適化された生の空間データフォーマットを策定する流れは、ここ最近アツいように思います。ベクタ(2D)のほうはFlatGeobuf、ラスタのほうはCloud Optimized GeoTIFFなどがあります。
そのまま置けちゃうのに、必要な分だけとってこれる生データは利便性が高いので、自治体のオープンデータなどもこういう形式で配信されれば、利用者目線ではWeb経由で使いやすくなるかもしれません。

データ出典

国勢調査町丁・字等別境界データセット | Geoshapeリポジトリ
『国勢調査町丁・字等別境界データセット』(CODH作成) 「平成27年国勢調査町丁・字等別境界データ」(NICT加工)

上記データを加工して利用しています。

Discussion

ログインするとコメントできます