Google Mapに多角形を描き、人口データの属性を持つ町丁目ポリゴンを取得し、表示する

## はじめに
今回は、Google Mapにフリーハンドの多角形を描きサーバーと連携、多角形内の町丁目ポリゴンを取得・表示し、町丁目ポリゴンが持つ人口データを出力してみました。
この記事は、下記のGoogle Mapや町丁目ポリゴンをテーマとした記事の**第3弾**になります。合わせて読んでいただけると理解しやすいかと思います。是非こちらもご覧下さい。

- [Maps JavaScript APIを使ってGoogle Mapに町丁目ポリゴンを表示してみる](https://qiita.com/yan_x/items/c069c8fb7c5d2648fe50)
- [地図(Google Map)の任意地点に円を描き、人口データの属性を持つ町丁目ポリゴンを取得し、表示する](https://qiita.com/yan_x/items/0cf06675739ddcfa3576)

## 使用したもの
- Google Map
- Maps JavaScript API

https://developers.google.com/maps/documentation/javascript?hl=ja

- JavaScript
- PHP
- 町丁目ポリゴン、令和2年国勢調査結果/人口総数
今回使用した町丁目ポリゴンおよび人口総数データは、TerraMap API からレスポンスされたものです。

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

## 完成イメージ
全体の完成イメージは、以下の動画のようになります。今回は TerraMap API から町丁目ポリゴンや人口データを取得するために、サーバーへのリクエストも必要になります。

**多角形の描画　→　サーバーへのリクエスト　
　→　町丁目ポリゴンの取得・表示　→　クリックイベントでの住所・人口出力**
![gmap_freehand_polygon.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2893989/9a6cb725-bb73-3e58-4677-3d130abf645e.gif)

:::note info
**商圏分析における多角形エリア指定について**
多角形を描き、分析するエリアを指定できるメリットは、大きな道路や河川などの地理的な分断要因に対応できる点と言えます。
:::

## 多角形の描画
多角形を描画するためには、Maps JavaScript APIのDrawingManagerライブラリを利用するので、リクエストパラメータ **libraries=drawing** を地図のURLに加えます。
```html:index.html
<!DOCTYPE html>
<html>
  <head>
    <title>
      Google Mapに多角形を描き、人口データの属性を持つ町丁目ポリゴンを表示する
    </title>

    <script type="module" src="./index.js"></script>
  </head>
  <body>
    <!--The div element for the map -->
    <div id="map" style="width: 100%; height: 600px"></div>
    <script
      src="https://maps.googleapis.com/maps/api/js?key=YOUR_GOOGLE_MAP_KEY&libraries=drawing&callback=initMap&v=weekly"
      defer
    ></script>
  </body>
</html>
```
JavaScriptでもDrawingManagerの設定を行います。
```javascript:index.js
function initMap() {
  // 中心位置
  const centerLocation = { lat: 35.699, lng: 139.9155 };

  const map = new google.maps.Map(document.getElementById("map"), {
    zoom: 15,
    center: centerLocation,
  });

  // DrawingManagerの設定
  const drawingManager = new google.maps.drawing.DrawingManager({
    drawingControl: true,
    drawingControlOptions: {
      position: google.maps.ControlPosition.TOP_CENTER,
      drawingModes: [google.maps.drawing.OverlayType.POLYGON],
    },
    // 描画する多角形のスタイル設定
    polygonOptions: {
      fillColor: "#FFD700",
      fillOpacity: 0.2,
      strokeColor: "#FFD700",
      strokeOpacity: 1,
      strokeWeight: 4,
      clickable: false,
      editable: false,
      zIndex: 1,
    },
  });

  drawingManager.setMap(map);
}

window.initMap = initMap;
```

## サーバーへのリクエスト、町丁目ポリゴンの取得・表示
次に、多角形の描画を終えたときに、座標情報をサーバーへPOST送信させます。レスポンスされた町丁目ポリゴンのGeoJSONは、指定されたスタイルで地図に表示させます。
以下のコードを initMap() 内に追記しました。
```javascript:index.js
  google.maps.event.addListener(
    drawingManager,
    "polygoncomplete",
    (polygon) => {
      // 多角形描画後の処理
      const singlePolygon = [];

      // サーバーサイドプログラムに渡す座標情報を用意
      for (const point of polygon.getPath().getArray()) {
        singlePolygon.push([point.lng(), point.lat()]);
      }
      // 最初の座標を加え多角形は閉じておく
      singlePolygon.push(singlePolygon[0]);

      const requestBody = {
        polygon: singlePolygon,
      };

      // サーバーサイドプログラムからTerraMap APIへのリクエスト例はページ下部に説明があります
      fetch(`https://YOUR_SERVER_DOMAIN`, {
        method: "POST",
        body: JSON.stringify(requestBody),
      })
        .then((response) => response.json())
        .then((data) => {
          map.data.addGeoJson(data);
        })
        .catch((error) => {
          console.log(error);
        });
    }
  );

  // レスポンスされて表示される町丁目ポリゴンのスタイル設定
  const polygonStyleOptions = {
    strokeColor: "#000080",
    strokeOpacity: 1.0,
    strokeWeight: 2.0,
    fillColor: "#000080",
    fillOpacity: 0.5,
  };

  map.data.setStyle((feature) => {
    if (feature.getGeometry().getType() === "MultiPolygon") {
      return polygonStyleOptions;
    }
    return null;
  });
```
レスポンスされる町丁目ポリゴン（一部抜粋）

```json:response.geojson
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "area": {
          "area": 0.43989642093133924
        },
        "data": [
          {
            "is_authorized": true,
            "stat_item_id": 15776,
            "stat_id": "001012000",
            "value": "3747"
          }
        ],
        "geocode": "13123002903",
        "points": [
          [
            "東京都",
            "江戸川区",
            "篠崎町３丁目"
          ]
        ]
      },
      "geometry": {
        "type": "MultiPolygon",
        "coordinates": [
          [
            [
              [
                139.908293846655,
                35.7052046878798
              ],
              [
                139.908724965996,
                35.7053861225666
              ],
```

## クリックイベントでの住所・人口出力
次に、町丁目ポリゴンのクリックイベントを利用してその場所の住所と人口を出力させます。出力にはMaps JavaScript APIのInfoWindowクラスを利用します。
この処理も、initMap() 内に以下を追記しました。
```javascript:index.js
  const infowindow = new google.maps.InfoWindow();

  // 町丁目ポリゴンのクリックイベントで住所と人口を出力
  map.data.addListener("click", (event) => {
    const html =
      `住所：${event.feature.getProperty("points")[0]}` +
      "<br>" +
      `人口：${event.feature.getProperty("data")[0].value}`;

    infowindow.setContent(html);
    infowindow.setPosition(event.latLng);
    infowindow.open(map);
  });
```

## サーバーサイド (PHP)
最後はサーバーサイドのプログラムです。PHPでの実装例を挙げておきます。
サーバーサイドでは、TerraMap APIのインターフェース仕様に従いますが、Google Mapから多角形の座標情報を受け取り、住所と国勢調査2020の人口総数の属性を持つ町丁目ポリゴンを返すように作りました。

```php: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(
        // 5個までのポリゴンをGeoJSON形式で指定できるが、今回はGoogle Mapから単一のポリゴンを受け付ける
        "type" => "FeatureCollection",
        "features" => [
            array(
                "type" => "Feature",
                "geometry" => array(
                    "type" => "Polygon",
                    "coordinates" => [
                        $params['polygon'],     // パラメータの座標配列をセット
                    ],
                ),
            ),
        ]
    ),
    "output" => "polygon,point,data",       // ポリゴン、住所、データを出力する
    "stats" => [
        array(
            "stat_id" => "001012000",       // 国勢調査2020を指定
            "stat_item_id" => [15776]       // 人口総数20を指定
        )
    ],
]);

$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);
```
https://www.mapmarketing.co.jp/terramap-api?grid=qiita&utm_source=qiita.com&utm_medium=referral&utm_campaign=qiita_gmap_freehand_polygon_02

## 参考情報
[Drawing Tools](https://developers.google.com/maps/documentation/javascript/examples/drawing-tools)
[描画レイヤ（ライブラリ）](https://developers.google.com/maps/documentation/javascript/drawinglayer)
[イベント](https://developers.google.com/maps/documentation/javascript/events)
[情報ウィンドウ](https://developers.google.com/maps/documentation/javascript/infowindows)