Mapbox GL JSを使って地図に多角形を描き、多角形内の人口データを持つポリゴンを取得し、表示する

# 概要説明
今回は、[Mapbox GL JS](https://docs.mapbox.com/jp/mapbox-gl-js/overview/)を使って地図にフリーハンドの多角形を描きサーバーと連携、多角形内の町丁目ポリゴンを取得・表示し、町丁目ポリゴンが持つ人口データを出力してみました。
まず、この記事の関連記事を紹介します。
Mapbox GL JSを使用して地図の表示や町丁目ポリゴンの表示に興味ある方はこちら、

https://qiita.com/Book-Ma/items/2cfc5bc68606bd6e2366

本記事と同じ内容ですが、Google Mapで実現してみたい方はこちらの記事を読んでいただけると理解しやすいかと思います。

https://qiita.com/y-mto/items/5c9e1a18b9442d434375

# ソース構成
最終的なソース構成は以下になります。
```
.
└─サーバサイド
   └─index.php
.
└─フロントサイド
   ├── index.html
   └── index.js
```

# 完成イメージ
ブラウザでの完成イメージは、以下のようになります。
**多角形の描画**　→　**サーバーサイドへのリクエスト**　
→　**TerraMap APIから町丁目ポリゴンの取得・表示**　→　**ポリゴンクリックでの住所・人口の表示**
![polygon.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2916958/9e07214f-dda9-f324-febe-736cf1ebd345.gif)

:::note info
フリーハンドの多角形を描き、分析地点（調査物件や店舗など）を中心に手動で商圏を作成できます。お好きな形の商圏を作成できるので、鉄道及び河川などの地形条件を考慮することが可能です。
:::

# 多角形の描画
HTMLとJavaScriptを用意してMapbox地図をブラウザで表示し、MapboxDrawでポリゴンを描きます。
```html:index.html
<!DOCTYPE html>
<html>
  <head>
    <title>
      Mapboxに多角形を描き、人口データの属性を持つ町丁目ポリゴンを表示する
    </title>
    <script src="https://api.mapbox.com/mapbox-gl-js/v2.6.1/mapbox-gl.js"></script>
    <link
      href="https://api.mapbox.com/mapbox-gl-js/v2.6.1/mapbox-gl.css"
      rel="stylesheet"
    />
  </head>
  <body>
    <script src="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.2.2/mapbox-gl-draw.js"></script>
    <link
      rel="stylesheet"
      href="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.2.2/mapbox-gl-draw.css"
      type="text/css"
    />
    <div id="map" style="width: 100%; height: 700px"></div>
    <script src="./index.js"></script>
  </body>
</html>
```
```javascript:index.js
mapboxgl.accessToken = "YOUR_MAPBOX_TOKEN";

const map = new mapboxgl.Map({
  container: "map",
  style: "mapbox://styles/mapbox/streets-v11",
  center: [139.662, 35.6727],
  zoom: 14
});

const draw = new MapboxDraw({
  displayControlsDefault: false,
  controls: {
    polygon: true,
    trash: true
  },
});
map.addControl(draw);
```
>コード参考情報
[描画ポリゴン領域を表示
](https://docs.mapbox.com/jp/mapbox-gl-js/example/mapbox-gl-draw/)


# サーバーへのリクエスト、町丁目ポリゴンの取得・表示
次に、多角形の描画を終えたときに、座標情報をサーバーへPOST送信させます。レスポンスされた町丁目ポリゴンのGeoJSONは、指定されたスタイルで地図に表示させます。
index.jsに下記のコードを追加します。
```javascript:index.js
let polyNum = 1;

function getPolygonArea(e) {
  const singlePolygon = e.features[0].geometry.coordinates[0];

  const requestBody = {
    polygon: singlePolygon
  };

  // サーバーサイドプログラムからTerraMap APIへのリクエスト例はページ下部に説明があります
  fetch(`https://YOUR_SERVER_DOMAIN`, {
    method: "POST",
    body: JSON.stringify(requestBody),
  })
    .then((response) => response.json())
    .then((poly) => {
      // GeoJSONのソースを追加
      map.addSource(`cho_polygon${polyNum}`, {
        type: "geojson",
        data: poly
      });

      // 町丁目ポリゴンの背景色を設定します
      map.addLayer(
        {
          id: `polygon${polyNum}`,
          type: "fill",
          source: `cho_polygon${polyNum}`,
          layout: {},
          paint: {
            "fill-color": "#1c1c1c",
            "fill-opacity": 0.5
          },
        },
        "gl-draw-polygon-fill-inactive.cold" // 描画レイヤーを指定して、その下にレイヤーを追加
      );

      // 町丁目ポリゴンの枠線を設定します
      map.addLayer(
        {
          id: `outline${polyNum}`,
          type: "line",
          source: `cho_polygon${polyNum}`,
          layout: {},
          paint: {
            "line-color": "#1c1c1c",
            "line-width": 2
          },
        },
        "gl-draw-polygon-fill-inactive.cold" // 描画レイヤーを指定して、その下にレイヤーを追加
      );

      // ポリゴン番号を加算
      polyNum += 1;
    })
    .catch((error) => {
      console.log(error);
    });
}

// 描画終了時に関数を実行
map.on("draw.create", getPolygonArea);
```

<details><summary>GeoJSONレスポンスの抜粋</summary>

```json:resonse.geojson
{
    "type": "Feature",
    "properties": {
        "area": {
            "area": 0.20722762578111142
        },
        "data": [
            {
                "is_authorized": true,
                "stat_item_id": 15776,
                "stat_id": "001012000",
                "value": "2232"
            }
        ],
        "geocode": "13104009403",
        "points": [
            [
                "東京都",
                "新宿区",
                "西新宿３丁目"
            ]
        ]
    },
    "geometry": {
        "type": "MultiPolygon",
        "coordinates": [
            [
                [
                    [
                        139.69563390759,
                        35.6870800042744
                    ],
                    [
                        139.695360956055,
                        35.6869514546403
                    ],
```
</details>


# クリックしたポリゴンの住所・人口表示
そして、ポリゴンをクリックすると、その場所の住所と人口データを含むポップアップが表示されるようにしてみます。
fetch()メソッドの後処理、```polyNum += 1;```の前に以下のコードを追加します。
```javascript:index.js
      map.on("click", `polygon${polyNum}`, (e) => {
        const objPoints = JSON.parse(e.features[0].properties.points);
        const objData = JSON.parse(e.features[0].properties.data);

        const html =
          `住所：${objPoints[0]}` + "<br>" + `人口：${objData[0].value}`;

        new mapboxgl.Popup().setLngLat(e.lngLat).setHTML(html).addTo(map);
      });
```

>コード参考情報
[クリック時にポリゴン情報を表示
](https://docs.mapbox.com/jp/mapbox-gl-js/example/polygon-popup-on-click/)

# サーバーサイド (PHP)
最後はサーバーサイドのプログラムです。PHPでの実装例を挙げておきます。
サーバーサイドでは、TerraMap APIのインターフェース仕様に従いますが、Mapbox地図から多角形の座標情報を受け取り、住所と2020年国勢調査の人口総数の属性を持つ町丁目ポリゴンを返すように作りました。
```index.php
<?php
header('Content-Type: application/json');

// URLとAPIキーはダミーです
$url = 'https://tmapi.example.jp/api/area';
$apiKey = 'YOUR_TERRAMAP_API_KEY';

$json = file_get_contents("php://input");
$params = json_decode($json, true);

// パラメータをJSON形式でセット
$requestBody  = json_encode([
    "layer_id" => "00104",          // レイヤーは町丁目を指定
    "area_type" => "polygon",       // エリアタイプはポリゴンを指定
    "polygons" => array(
        "type" => "FeatureCollection",
        "features" => [
            array(
                "type" => "Feature",
                "geometry" => array(
                    "type" => "Polygon",
                    "coordinates" => [
                        $params['polygon']    // パラメータの座標配列をセット
                    ],
                ),
            ),
        ]
    ),
    "output" => "polygon,point,data",       // ポリゴン、住所、データを出力する
    "stats" => [
        array(
            "stat_id" => "001012000",       // 令和2年国勢調査（2020年国勢調査）
            "stat_item_id" => [15776]       // 人口総数
        )
    ],
]);

$ch = curl_init();
// Content-TypeとAPIキーをセット
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'X-API-KEY: ' . $apiKey]);
// POSTメソッドを指定
curl_setopt($ch, CURLOPT_POST, true);
// パラメータをセット
curl_setopt($ch, CURLOPT_POSTFIELDS, $requestBody);
// URLをセット
curl_setopt($ch, CURLOPT_URL, $url);

// レスポンスをブラウザに渡す
curl_exec($ch);

curl_close($ch);
```

# おわりに
今回はMapboxに多角形を描き、人口データの属性を持つ町丁目ポリゴンを取得し、表示する方法をご紹介しました。
TerraMap APIを利用することによって町丁目ポリゴンと人口データを簡単に表示することができます。
TerraMap APIは人口総数以外にも様々な統計データを取得することができ、地図を使った統計データの見える化やエリアマーケティングに特化した分析が実現できます。

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