東京都23区の外国人人口データのCSVファイルを読み込み、Google Mapで可視化してみる

# 東京都23区の外国人人口データを地図で可視化してみます
私自身が中国出身の故、この記事で使用する外国人人口データから中国を取り上げました。東京都23区の中国人人口をGoogle Mapに表示した画面はこのようになります。
![Animation2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2916958/f86f1267-2ed2-4548-e3e6-1eb2438e266d.gif)


人口数が高い区がより赤い色で表示するように色分けしています。そして人口の情報をGoogle MapのInfoWindow機能で地図クリック時に表示しています。
結果を見てみると、中国人人口が一番多いエリアは江東区19259人、一番低いエリアは千代田区2172人になります。

# 東京都23区のポリゴンについて
東京都23区（市区町村レベル）のポリゴン表示は以下の記事をベースにしました。

https://qiita.com/yan_x/items/c069c8fb7c5d2648fe50

# 東京都23区の外国人人口データについて
この記事で使用している外国人人口データはサイト「東京都の統計」が提供してる外国人人口令和6年7月「区市町村別国籍・地域別外国人人口(上位10か国・地域)」のデータです。このデータは住民基本台帳上の人口によるものだそうです。

https://www.toukei.metro.tokyo.lg.jp/gaikoku/2024/ga24010000.htm

## コードについて
Maps JavaScript APIを使用します。

### CSVファイルの読み込み
CSVファイルの読み込みはfetchとPapaParseというライブラリを使用しています。
```html
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.3.0/papaparse.min.js"></script>
  </head>
```
```js
fetch('sample.csv')
  .then(response => response.text())
  .then(csvData => {
    parsedData = Papa.parse(csvData.trim(), {
      header: true,
      skipEmptyLines: true
    }).data;

    initMap();
  })
  .catch(error => {
    console.error('Error fetching CSV:', error);
  });
```

### 人口で色塗り分けするコード
5000刻みで色塗り分けしています。
```js
  // ポリゴンに人口データを紐付け
  map.data.setStyle((feature) => {
    const regionName = feature.getProperty("points")[0][0];
    const populationData = parsedData.find((row) => row["地域"] === regionName);
    const population = populationData ? parseInt(populationData["中国人人口"], 10) : 0;

    // 人口に基づいて色を決定
    let fillColor;
    if (population <= 5000) {
      fillColor = '#5b97ee'; // 濃い青
    } else if (population <= 10000) {
      fillColor = '#92c7ff'; // 薄い青
    } else if (population <= 15000) {
      fillColor = '#ffd976'; // 薄い黄色
    } else if (population <= 20000) {
      fillColor = '#ffa746'; // 濃い黄色
    } else {
      fillColor = '#d7352b'; // 赤い
    } 

    return {
      fillColor: fillColor,
      strokeWeight: 1,
      fillOpacity: 0.5,
    };
  });
```
### JavaScript全体のサンプルコード
<details><summary>index.js</summary>

```js
let parsedData = [];

// CSVファイルをfetchで取得
fetch('sample.csv')
  .then(response => response.text())
  .then(csvData => {
    parsedData = Papa.parse(csvData.trim(), {
      header: true,
      skipEmptyLines: true
    }).data;

    initMap();
  })
  .catch(error => {
    console.error('Error fetching CSV:', error);
  });

let map;

async function initMap() {
  const { Map } = await google.maps.importLibrary("maps");
  const centerLocation = { lat: 35.6812362, lng: 139.7671248 };
  map = new Map(document.getElementById("map"), {
      zoom: 12,
      center: centerLocation,
    });
  // TerraMap APIからデータを取得する部分はJSONファイルを読み込んで疑似的に再現
  fetch('area.json')
  .then(response => response.json())
  .then(geoJsonData => {
      map.data.addGeoJson(geoJsonData);
  })
  .catch(error => console.error('Error fetching GeoJSON data:', error));

  // ポリゴンに人口データを紐付け
  map.data.setStyle((feature) => {
    const regionName = feature.getProperty("points")[0][0];
    const populationData = parsedData.find((row) => row["地域"] === regionName);
    const population = populationData ? parseInt(populationData["中国人人口"], 10) : 0;

    // 人口に基づいて色を決定
    let fillColor;
    if (population <= 5000) {
      fillColor = '#5b97ee'; // 濃い青
    } else if (population <= 10000) {
      fillColor = '#92c7ff'; // 薄い青
    } else if (population <= 15000) {
      fillColor = '#ffd976'; // 薄い黄色
    } else if (population <= 20000) {
      fillColor = '#ffa746'; // 濃い黄色
    } else {
      fillColor = '#d7352b'; // 赤い
    } 

    return {
      fillColor: fillColor,
      strokeWeight: 1,
      fillOpacity: 0.5,
    };
  });

  // ポリゴンをクリックしたときに情報ウィンドウを表示
  const infoWindow = new google.maps.InfoWindow();
  map.data.addListener("click", (event) => {
    const regionName = event.feature.getProperty("points")[0][0];
    const populationData = parsedData.find((row) => row["地域"] === regionName);
    const population = populationData ? populationData["中国人人口"] : "データなし";
    infoWindow.setContent(`<strong>${regionName}</strong><br>中国人人口: ${population}`);
    infoWindow.setPosition(event.latLng);
    infoWindow.open(map);
  });
}

window.initMap = initMap;

initMap();
```

</details>

index.jsコードで使用したCSVファイルの抜粋は以下になります。

<details><summary>sample.csv</summary>

```csv
地域,中国人人口
千代田区,2172
中央区,6080
港区,6685
新宿区,17522
```

</details>

index.jsコードで使用したJSONファイルの抜粋は以下になります。

<details><summary>area.json</summary>

```json
{
    "type": "FeatureCollection",
    "features": [
        {
            "type": "Feature",
            "properties": {
                "area": {
                    "area": 11.397688758847336,
                    "ratio_area": [
                        11.397688758847336
                    ],
                    "ratio": [
                        1.0
                    ]
                },
                "data": [
                    {
                        "is_authorized": true,
                        "ratio_value": [
                            "33993"
                        ],
                        "stat_item_id": 17955,
                        "stat_id": "007002000",
                        "value": "33993"
                    },
                    {
                        "is_authorized": true,
                        "ratio_value": [
                            "27271300"
                        ],
                        "stat_item_id": 17969,
                        "stat_id": "007002000",
                        "value": "27271300"
                    }
                ],
                "point_coordinates": [
                    139.75019,
                    35.68687
                ],
                "geocode": "13101",
                "points": [
                    [
                        "千代田区"
                    ]
                ]
            },
            "geometry": {
                "type": "MultiPolygon",
                "coordinates": [
                    [
                        [
                            [
                                139.748974398,
                                35.670568286
                            ],


```

</details>