フロントエンド開発で好きなマップを作る

# 「みんなでテイクアウトMAP」を例に説明
* 「テイクアウト」とあるけど、別に「トイレ」でも「酒屋」でもOK
* データソースはOpenStreetMap。オープンデータで登録不要のAPIがあるので便利
    * 自前でデータベースと中に入れるデータを整備するよりも遥かに効率的
    * 不足したデータはみんなで登録することで、他プロジェクトの役にも立つ
    * OpenStreetMapは、プロジェクト間のデータ共有プラットフォームが本質
* WebサーバーはGitHubをお借りする（Pages利用）ので無料
* OpenStreetMapのデータはOverPass APIを利用するので登録不要＆無料
* 後はHTMLとJavaScriptでコードを書くだけでテイクアウトマップが作れる 

# OverPass APIについて
* 大雑把に書くと、JavaScriptからOpenStreetMapの「データ」を取得するAPIのこと
    * 「データ」とは緯度経度付きの地物（施設、建物、道路、川、森、石碑など）
* ビジネスなど大量アクセスしない限り、登録不要・無料で使えるAPIサーバーがある
* クエリをAPIを呼ぶことで、欲しいデータだけを抜き出せる（例えば酒屋だけ抜き出す）
* 簡単な使い方は、OpenStreetMap Advent Calendar 2019[「OverPass APIのススメ」](https://qiita.com/K-Sakanoshita/items/0c610d76bba2e7245ea5)参照

# Overpass APIでテイクアウト店舗を取得する
* システム構成図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216230/d27ac4e6-23b7-59d3-78cd-853fd73980df.png)

* GitHub:  [みんなでテイクアウトMAP <開発版>](https://github.com/K-Sakanoshita/takeaway) 
* 動作画面 / [デモサイト](https://k-sakanoshita.github.io/takeaway/)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216230/c2a1b1cc-3d8d-305e-697b-5c72c433fdf8.png)
* 動作画面のポイント
    * 画面左側
        * 背景地図は何でも良い。例えばGoogle Maps（開発が必要だけど）でもOK
            * もちろん、OpenStreetMap標準タイルサーバーを利用してもOK
            * 今回は日本でもサービスが始まった [Maptiler](https://maptiler.jp/) を利用してみた
        * OverPass APIで取得した店舗に合ったアイコンを表示させている
        * 背景地図は画像だけ利用し、APIで取得したデータを上に表示させている
    * 画面右側
        * OverPass APIで取得した店舗をリスト（ソート順は中心位置からの近さ）
        * GeoJsonで取得できるので、サーバーを別に用意する必要は無い

# ソースコードの説明
## OverPass APIの利用
* OvPassCntクロージャのgetでデータを取得している
* getの引数(targets)の内容は ["takeaway","delivery"] といった単なる配列
* コード後半の targets.forEach でループさせて Conf.target[key].ovpass でOverPass APIに投げるクエリを取得させている

```javascript:takeawayLib.js
var OvPassCnt = (function () {
    var Cache = { "geojson": [], "targets": [] };   // Cache variable
    var LLc = { "NW": { "lat": 0, "lng": 0 }, "SE": { "lat": 0, "lng": 0 } }; // latlng cache area

    return {
        get: function (targets) {
            return new Promise((resolve, reject) => {
                // 中略 // 
                targets.forEach(key => {
                    for (let ovpass in Conf.target[key].ovpass) { query += Conf.target[key].ovpass[ovpass] + maparea };
                });
                let url = OvServer + '?data=[out:json][timeout:30];(' + query + ');(._;>;);out meta qt;';
                console.log("GET: " + url);
                jqXHRs.push($.get(url, () => { DisplayStatus.progress(100) }));
```
* 最終的にOverPass APIには以下のような内容で投げている（サンプルはシンプルにしてます）
* 実際にクエリを投げてみると[Jsonで返ってくる](https://overpass-api.de/api/interpreter?data=[out:json][timeout:30];(node[%22takeaway%22](34.689042740830814,135.44517159461978,34.715592149311874,135.48583388328555););(._;%3E;);out%20meta%20qt;)ことが分かる
 
```
GET: https://overpass-api.de/api/interpreter?data=[out:json][timeout:30];(node["takeaway"](34.689042740830814,135.44517159461978,34.715592149311874,135.48583388328555););(._;>;);out meta qt;
```

## APIの返答結果をマーカー表示
* OverPass APIから取得したデータを変数に格納して、それをマーカー表示させる部分
* マーカー表示には [leaflet.js](https://leafletjs.com/) のDivIconを利用している（背景地図もleaflet.js）

```javascript:takeawayLib.js
var Marker = (function () {
    var markers = [];

    return {
        set: function (target) {
            markers[target] = [];
            let pois = PoiCont.get_target(target);
            pois.geojson.forEach(function (node, idx) {
                let tags = node.properties;
                let name = tags.name == undefined || Conf.local.TextViewZoom > map.getZoom() ? "" : tags.name;
                let keyn = tags.amenity !== undefined ? "amenity" : "shop";
                let icon = Conf.icon[keyn][tags[keyn]];
                icon = "./image/" + (icon !== undefined ? icon : Conf.icon.default);
                let html = '<div class="d-flex align-items-center"><img class="icon" src="' + icon + '"><span class="icon">' + name + '</span></div>'
                icon = L.divIcon({ "className": 'icon', "iconAnchor": [8, 8], "html": html });
                markers[target].push(L.marker(new L.LatLng(pois.latlng[idx].lat, pois.latlng[idx].lng), { icon: icon, draggable: false }));
                markers[target][markers[target].length - 1].addTo(map).on('click', e => Takeaway.view(e.target.takeaway_id));
            });
        },
```

## 詳しくはソースで！
* 紹介した「みんなでテイクアウトMAP」はオープンソース（MITライセンス）で公開中
* OverPass APIに渡すクエリと周辺を書き換えると、例えば「トイレマップ」なんかも作れる
* GitHub:  [みんなでテイクアウトMAP <開発版>](https://github.com/K-Sakanoshita/takeaway) 
* (参考) OpenStreetMap Wiki: [JA:Overpass API](https://wiki.openstreetmap.org/wiki/JA:Overpass_API)

もし良ければ、「みんなでテイクアウトMAP」を[開発した理由](https://k-sakanoshita.medium.com/%E3%81%BF%E3%82%93%E3%81%AA%E3%81%A7%E3%83%86%E3%82%A4%E3%82%AF%E3%82%A2%E3%82%A6%E3%83%88map%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6-b0739ec217c9)も読んで頂けるとありがたいです。