Leafletで地図に円を描き、人口データの属性を持つ町丁目ポリゴンを取得して表示してみる

## はじめに
今回は、Leafletを用いて地図上に円を描き、円に交差する町丁目ポリゴンとその属性である人口総数データを取得して表示する方法を試してみました。主な技術要素・使用しているものは以下になります。
- [Leaflet](https://leafletjs.com/)
- JavaScript
- TerraMap API（区画ポリゴンや統計データ等を提供するWeb API）

※今回使用している人口総数データは、令和2年国勢調査（2020年国勢調査）の結果に基づいております。

https://www.mapmarketing.co.jp/terramap-api?grid=qiita&utm_source=qiita.com&utm_medium=referral&utm_campaign=qiita_leaflet_circle_area_cb96b832fffa5f3b30cc_01

弊社の記事では、Leaflet以外でも円・ポリゴン・座標を指定してサーバーへのリクエストに利用する方法をご案内してきました。今回と同じ目的で、Google マップでの実現にご興味をお持ちでしたら、以下の記事をご参照下さい。

https://qiita.com/yan_x/items/0cf06675739ddcfa3576

## 全体フロー
全体のフロー図は以下になります。

![LeafletToApiFlow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2893989/6c4e6875-b8f1-162f-cef4-f9ccdb1653ec.png)

## HTML
```index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <title>
      Leafletで地図に円を描き、人口データの属性を持つ町丁目ポリゴンを表示する
    </title>
    <link
      rel="stylesheet"
      href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
      integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
      crossorigin=""
    />
    <script
      src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
      integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
      crossorigin=""
    ></script>
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.css"
    />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script>
  </head>
  <body>
    <div id="map" style="width: 100%; height: 95vh"></div>
    <script src="./index.js"></script>
  </body>
</html>
```
円を描画するためには、Leaflet Draw を利用します。

参考情報
https://leafletjs.com/examples/quick-start/
https://leaflet.github.io/Leaflet.draw/docs/leaflet-draw-latest.html

## JavaScript
JavaScriptのコードは以下のようになりました。各箇所はコメントで補足しております。
```index.js
const map = L.map("map").setView([35.6727, 139.662], 14);

// 背景地図はOpenStreetMap
const tiles = L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
  maxZoom: 19,
  attribution:
    '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
}).addTo(map);

// 描画コントロールの設定（円描画に限定）
map.addControl(
  new L.Control.Draw({
    edit: false,
    draw: {
      polyline: false,
      polygon: false,
      rectangle: false,
      marker: false,
      circlemarker: false,
      circle: {
        shapeOptions: {
          fill: false,
        },
      },
    },
  })
);
// 描画レイヤーの初期化
const drawnItems = L.featureGroup().addTo(map);

// GeoJSON用のペインを作成し、円の下に配置
map.createPane("paneGeoJson").style.zIndex = 300;
// GeoJSONのスタイル
const geoJsonStyle = {
  color: "#7a7a7a",
  fillColor: "#9a9a9a",
  fillOpacity: 0.5,
};

// onEachFeatureオプションを利用して属性データをPopup表示できるようにする
function onEachFeature(feature, layer) {
  // 属性データはfeature.propertiesの構成に従って参照する必要があります
  const prop = `住所：${feature.properties.points[0].join("")}<br>人口：${
    feature.properties.data[0].value
  }`;
  layer.bindPopup(prop);
}

// 円描画終了時の処理
map.on(L.Draw.Event.CREATED, (event) => {
  const circle = event.layer;
  // 描画レイヤーに円を追加
  drawnItems.addLayer(circle);

  // サーバーサイドプログラムには円の中心位置と半径(m)を渡します
  const latitude = circle.getLatLng().lat;
  const longitude = circle.getLatLng().lng;
  const circleRadius = circle.getRadius();

  // パラメータはJSON形式へ
  const params = {
    lat: latitude,
    lng: longitude,
    radius: circleRadius,
  };
  const queryParams = new URLSearchParams(params);

  // サーバーサイドプログラムへGeoJSONをリクエスト
  fetch(`http://localhost:8000/?${queryParams}`, {
    method: "GET",
  })
    .then((response) => response.json())
    .then((data) => {
      // GeoJSONのレイヤー
      L.geoJSON(data, {
        style: geoJsonStyle,
        pane: "paneGeoJson",  // ペイン設定
        onEachFeature: onEachFeature,  // 上記関数とリンク
      }).addTo(map);
    })
    .catch((error) => {
      console.log(error);
    });
});
```
:::note
**詳細①**　GeoJSONの町丁目ポリゴンは透過しているので見た目はほとんど変わりませんが、[ペインの設定](https://leafletjs.com/reference.html#map-pane)を用いて円の下に町丁目ポリゴンを配置させています。
| レイヤー | zIndex |
|:-|-:|
| 円 | 400 |
| 町丁目ポリゴン (ペイン設定後) | 300 |
| 背景地図 | 200 |
:::
:::note
**詳細②**　GeoJSONレイヤーのonEachFeatureオプションを利用すると、レイヤーに追加する前に各フィーチャで呼び出される関数を指定できるようです。

参考情報
https://leafletjs.com/examples/geojson/
:::
## サーバーサイド
サーバーサイドのプログラムではTerraMap APIへのリクエストを実行しますが、既存記事のコードが共通して使えるので、詳細は割愛させていただきます。以下をご参照下さい。

[PHPでcURL関数を使ってTerraMap APIから町丁目ポリゴン、統計データ、住所を取得する](https://qiita.com/yan_x/items/0cf06675739ddcfa3576#php%E3%81%A7curl%E9%96%A2%E6%95%B0%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6terramap-api%E3%81%8B%E3%82%89%E7%94%BA%E4%B8%81%E7%9B%AE%E3%83%9D%E3%83%AA%E3%82%B4%E3%83%B3%E7%B5%B1%E8%A8%88%E3%83%87%E3%83%BC%E3%82%BF%E4%BD%8F%E6%89%80%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B)

## 動作確認
全体が上手く行くと、以下のようなことができるようになります。

![leaflet_circle_area.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2893989/2104d9c0-0ef0-13b2-ba7d-b98acdf345d3.gif)

最後まで読んでいただきまして、ありがとうございました。