OverpassAPI を {Java,Type}Script から使ってみよう

# Overpass API とは

OpenStreetMap には全世界のマッパーたちが登録した道路や建物、さらにはそれらについての詳細なタグ情報が登録されています。
そのような膨大なデータ群を検索可能にし、簡単にアクセスできるような API を公開してくれているプロジェクトが Overpass API です。

Overpass API は**Overpass QL**という言語で書かれたクエリを送ると、検索結果を JSON,CSV,XML などの形式で送り返してくれます。下の例では京都の市街地にある郵便局の一覧を得ることができます。

```rakuchu-postoffice.ql
[out:json]; // JSONファイルで出力
node
	// 四条通から北大路通、西大路通から河原町通で囲まれる四角形から検索
	(35.0, 135.73, 35.04, 135.77)
	// nameタグに郵便局で終わる文字列が含まれるものを抽出
	['name' ~ '郵便局$'];
out;
```

Overpass API の使い方は[OpenStreetMap の Wiki](https://wiki.openstreetmap.org/wiki/JA:Overpass_API)に詳しい解説があります。

# Overpass API をJavaScript/TypeScriptから使ってみよう

Overpass API には[Overpass turbo](https://overpass-turbo.eu/)という素晴らしいフロントエンドがあって、クエリの実行から結果の表示、地図へのマッピングまで行なってくれる優れものです。
ただ、プログラマチックにデータを取得したい場合や、自分が開発しているアプリにこの API の機能を組み込みたい場合は、 Overpass API のサーバーに直接問い合わせる必要があります。
JavaScript から Overpass API にアクセスする方法は、[@t-kurasawa](https://qiita.com/t-kurasawa)さんによる
[OpenStreetMap Advent Calendar 2021 の 4 日目の記事](https://qiita.com/t-kurasawa/items/03e5bc9c9a07d8ff99b7#openstreetmap-%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E6%89%B1%E3%81%86%E3%81%9F%E3%82%81%E3%81%AE-api)にも紹介されている通り、難しいものではないのですがライブラリがあると便利です。  
ただ、数年前で開発が止まっているものや、型定義ファイルがなかったりして TypeScript から呼び出すにはひと手間かける必要があるもの、コールバックで結果を受け取る設計のものが少なくなく、僕のユースケースを満たすものが見つかりませんでした。

僕がほしいライブラリはこんな感じです ↓

- 結果が GeoJSON で出力される
  - そのまま Leaflet とか MapLibre とかに持ち込んで表示できる
- TypeScript で開発されている
  - JavaScript から呼び出す場合も型定義があれば補完が効いて便利
- async/await を使って書きたい
  - コールバック読みにくい...ですよね?
  - イマドキな書き方をしてみたい！

ざっとこの要件を満たすラッパーを作ってみたので、今回はそれを紹介させていただけたらと思います。

# 自作ライブラリ osmQL の紹介

osmQL というライブラリを作成しました。

- GitHub
  - [https://github.com/YUUKIToriyama/osmQL](https://github.com/YUUKIToriyama/osmQL)
- npm
  - [https://www.npmjs.com/package/@toriyama/osmql](https://www.npmjs.com/package/@toriyama/osmql)

急いで作ったので Node.js でしか動きませんが、ゆくゆくはブラウザ上でも動作するように改良していこうと考えています。

## osmQL でできること

osmQL では主に 2 つのことができます。

1. 手書きの Overpass QL を読み込み実行
2. 付属のユーティリティを使ってクエリを作成して実行

## Overpass QL のファイルを読み込んで実行

ひとつめはクエリ文字列や、クエリを書いたファイルを読み込んでそれを実行する機能です。
たとえば、先程の

```rakuchu-postoffice.ql
[out:json]; // JSONファイルで出力
node
	// 四条通から北大路通、西大路通から河原町通で囲まれる四角形から検索
	(35.0, 135.73, 35.04, 135.77)
	// nameタグに郵便局で終わる文字列が含まれるものを抽出
	['name' ~ '郵便局$'];
out;
```

というファイルがあったとしてこれを読み込んで Overpass API に問い合わせるコードは次のように書くことができます。

```javascript:getPostoffice.mjs
import { OSMQuery } from "@toriyama/osmql";
(async () => {
	const osmQuery = new OSMQuery();
	const query = osmQuery.fromQLFile("./sample.ql");
	const result = await query.execute();
})();
```

さらに、`result`には`toGeoJSON()`というメソッドがあって、これを実行すると Overpass API からの結果を GeoJSON 形式に変換することができます。

```javascript
console.log(result.toGeoJSON());
```

```javascript:result
{
  type: 'FeatureCollection',
  features: [
    {
      type: 'Feature',
      id: 'node/796933156',
      properties: [Object],
      geometry: [Object]
    },
    {
      type: 'Feature',
      id: 'node/1422960082',
      properties: [Object],
      geometry: [Object]
    },
    ...
  ]
}
```

## ユーティリティを用いてクエリを作成し実行

このライブラリの一番の目玉がクエリを書かなくても付属の`QueryBuilder`を使えば、誰でもかんたんに Overpass API にアクセスできる機能です。

たとえば先ほどの京都市街地の郵便局を検索するクエリを`QueryBuilder`を使って作成・実行してみましょう。

```rakuchu-postoffice.ql
[out:json]; // JSONファイルで出力
node
	// 四条通から北大路通、西大路通から河原町通で囲まれる四角形から検索
	(35.0, 135.73, 35.04, 135.77)
	// nameタグに郵便局で終わる文字列が含まれるものを抽出
	['name' ~ '郵便局$'];
out;
```

```javascript:use-querybuilder.mjs
import fs from "fs/promises";
import { OSMQuery } from "@toriyama/osmql";

const query = OSMQuery.queryBuilder({
	category: "node",
});
query.setBounding({
	south: 35.0,
	west: 135.73,
	north: 35.04,
	east: 135.77,
});
query.filterBy("name")(/郵便局$/);
OSMQuery.get(query).then((result) => {
	const geojson = result.toGeoJSON();
	fs.writeFile("./postoffices.geojson", JSON.stringify(geojson, "", "\t"));
});
```

するとカレントディレクトリに`postoffices.geojson`というファイルが出力されると思います。
これを[geojson.io](https://geojson.io/)などに読み込ませて表示させてみると、こんな感じになると思います！

![geojson-io.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274026/73dfcdd7-bd48-7f70-370d-068a2766b0fa.jpeg)


`QueryBuilder`は以下のようにしても読み込むことができます。

```javascript
import { QueryBuilder } from "@toriyama/osmql";
```

`QueryBuilder`には検索する領域を指定する`setBounding(bounding)`メソッドだけでなく、

- 指定したタグに一致するものがあるか検索する`filterBy(tagName)(value)`メソッド
- マッパー ID やユーザーネームを入れてその人の成果を検索できる`editedBy(user)`メソッド

などが用意されています。Overpass QL を書くほどでないかんたんな検索の実行に便利であるほか、このライブラリを使えばクエリ言語にあまり親しみがないユーザーでもかんたんに Overpass API に触れることができます。

# おわりに――一緒に地図を書きませんか


OpenStreetMap は日々全世界のユーザーが絶えずコミットし続けている巨大なプロジェクトです。
しかも、その蓄積された成果はオープンデータとして公開され自由につかうことができます^[1]。

[^1]: https://www.openstreetmap.org/copyright

OpenStreetMap を金銀財宝の眠る山と喩えるならば、Overpass API はそれを掘り当てるためのつるはしでしょうか。お宝探しの旅に出るときは拙作のライブラリもぜひお供に連れて行ってくれると嬉しいです。では Happy Mapping!🗺 & Mining!⛏