0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

新年収データを使用して東京都23区の年収ヒストグラムを表示してみました

Last updated at Posted at 2025-04-08

はじめに

前の記事では、東京都23区の年収データをGeoJSON形式で取得し、平均年収に応じて色分け表示しました。

今回は、新たな年収データを用いて各区の平均年収表示を行い、加えて各区の年収ヒストグラムを作成してみました。

完成画面

画面上半分には地図が表示され、各区の平均年収で色分けしています。
画面下半分には、クリックした区の年収ヒストグラムが表示されます。グラフのx軸は年収範囲、y軸は世帯数となっており、各区の年収分布状況を視覚的に把握できます。
年収23区ヒストグラム.gif

新年収データについて

新年収データ(年収別世帯数推計データ)住宅・土地統計および賃金関係データを最新年度に更新しています。

コードについて

JavaScript全体のサンプルコード

ヒストグラムの作成はChart.jsを使用しています。

let map;
let histogramChart = null;
let globalInfoWindow;

const labelMapping = {
    24532: "年収200万円未満",
    24533: "年収200~300万円",
    24534: "年収300~400万円",
    24535: "年収400~500万円",
    24536: "年収500~600万円",
    24537: "年収600~700万円",
    24538: "年収700~800万円",
    24539: "年収800~900万円",
    24540: "年収900~1000万円",
    24541: "年収1000~1250万円",
    24542: "年収1250~1500万円",
    24543: "年収1500~2000万円",
    24544: "年収2000万円以上"
};

async function initMap() {
  const { Map, InfoWindow } = await window.google.maps.importLibrary("maps");
  map = new Map(document.getElementById("map"), {
    center: { lat: 35.68437, lng: 139.75247 },
    zoom: 10,
  });
  
  globalInfoWindow = new InfoWindow();

  // TerraMap APIからデータを取得する部分はJSONファイルを読み込んで疑似的に再現
  fetch('area.json')
    .then(response => response.json())
    .then(geoJsonData => {
      drawPolygons(geoJsonData);
    })
    .catch(error => console.error('Error fetching GeoJSON data:', error));
}

// 平均年収に基づいて色を決定する関数
function getColor(value) {
  if (value <= 450) {
    return '#5b97ee';
  } else if (value <= 550) {
    return '#92c7ff';
  } else if (value <= 650) {
    return '#ffd976';
  } else if (value <= 750) {
    return '#ffa746';
  } else {
    return '#d7352b';
  }
}

function drawPolygons(geoJsonData) {
  geoJsonData.features.forEach(feature => {
    const polygons = feature.geometry.coordinates;
    
    // 年収総額データと世帯合計データ
    const nenshuSum = feature.properties.data.find(item => item.stat_item_id === 24545)?.value;
    const householdNumber = feature.properties.data.find(item => item.stat_item_id === 24531)?.value;
    const calculatedValue = (nenshuSum && householdNumber) ? (nenshuSum / householdNumber).toFixed(2) : "N/A";
    
    const wardName = feature.properties.points[0].join("");
    
    polygons.forEach(polygon => {
      const paths = polygon[0].map(coord => ({ lat: coord[1], lng: coord[0] }));
      
      const poly = new google.maps.Polygon({
        paths: paths,
        strokeColor: '#666',
        strokeOpacity: 0.8,
        strokeWeight: 2,
        fillColor: getColor(calculatedValue),
        fillOpacity: 0.6,
        map: map
      });
      
      // クリック時にInfoWindow下部のヒストグラムを更新
      poly.addListener('click', (event) => {
        globalInfoWindow.close();
        
        // ヒストグラム用のstat_item_id一覧
        const statIds = [24532, 24533, 24534, 24535, 24536, 24537, 24538, 24539, 24540, 24541, 24542, 24543, 24544];
        const histogramData = statIds.map(id => {
          const item = feature.properties.data.find(item => item.stat_item_id === id);
          return item ? item.value : 0;
        });
        
        // InfoWindowに平均年収を表示
        const infoContent = `
          <div class="custom-info-window">
            <h3>${wardName}</h3>
            <p>平均年収(万円): ${calculatedValue}</p>
          </div>
        `;
        globalInfoWindow.setContent(infoContent);
        globalInfoWindow.setPosition(event.latLng);
        globalInfoWindow.open(map);
        
        // ヒストグラムキャンバスを更新
        const canvas = document.getElementById('histogramChart');
        if (histogramChart) {
          histogramChart.destroy();
        }
        histogramChart = new Chart(canvas.getContext('2d'), {
          type: 'bar',
          data: {
            labels: statIds.map(id => labelMapping[id]),
            datasets: [{
              label: wardName,
              data: histogramData,
              backgroundColor: 'rgba(75, 192, 192, 0.2)',
              borderColor: 'rgba(75, 192, 192, 1)',
              borderWidth: 1
            }]
          },
          options: {
            responsive: true,
            maintainAspectRatio: false,
            plugins: {
                tooltip: {
                  enabled: false
                }
            },            
            scales: {
              y: {
                beginAtZero: true
              }
            }
          }
        });
      });
    });
  });
}

initMap();

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

area.json
{
    "type": "FeatureCollection",
    "features": [
        {
            "type": "Feature",
            "properties": {
                "area": {
                    "area": 11.39768875876782,
                    "ratio_area": [
                        11.39768875876782
                    ],
                    "ratio": [
                        1.0
                    ]
                },
                "data": [
                    {
                        "is_authorized": true,
                        "ratio_value": [
                            "33993"
                        ],
                        "stat_item_id": 24531,
                        "stat_id": "007002300",
                        "value": "33993"
                    },
                    {
                        "is_authorized": true,
                        "ratio_value": [
                            "2148"
                        ],
                        "stat_item_id": 24532,
                        "stat_id": "007002300",
                        "value": "2148"
                    },
                    {
                        "is_authorized": true,
                        "ratio_value": [
                            "2483"
                        ],
                        "stat_item_id": 24533,
                        "stat_id": "007002300",
                        "value": "2483"
                    },
                    {
                        "is_authorized": true,
                        "ratio_value": [
                            "2667"
                        ],
                        "stat_item_id": 24534,
                        "stat_id": "007002300",
                        "value": "2667"
                    },
                    {
                        "is_authorized": true,
                        "ratio_value": [
                            "2280"
                        ],
                        "stat_item_id": 24535,
                        "stat_id": "007002300",
                        "value": "2280"
                    },
                    {
                        "is_authorized": true,
                        "ratio_value": [
                            "2536"
                        ],
                        "stat_item_id": 24536,
                        "stat_id": "007002300",
                        "value": "2536"
                    },
                    {
                        "is_authorized": true,
                        "ratio_value": [
                            "1978"
                        ],
                        "stat_item_id": 24537,
                        "stat_id": "007002300",
                        "value": "1978"
                    },
                    {
                        "is_authorized": true,
                        "ratio_value": [
                            "2483"
                        ],
                        "stat_item_id": 24538,
                        "stat_id": "007002300",
                        "value": "2483"
                    },
                    {
                        "is_authorized": true,
                        "ratio_value": [
                            "2416"
                        ],
                        "stat_item_id": 24539,
                        "stat_id": "007002300",
                        "value": "2416"
                    },
                    {
                        "is_authorized": true,
                        "ratio_value": [
                            "1945"
                        ],
                        "stat_item_id": 24540,
                        "stat_id": "007002300",
                        "value": "1945"
                    },
                    {
                        "is_authorized": true,
                        "ratio_value": [
                            "4735"
                        ],
                        "stat_item_id": 24541,
                        "stat_id": "007002300",
                        "value": "4735"
                    },
                    {
                        "is_authorized": true,
                        "ratio_value": [
                            "2353"
                        ],
                        "stat_item_id": 24542,
                        "stat_id": "007002300",
                        "value": "2353"
                    },
                    {
                        "is_authorized": true,
                        "ratio_value": [
                            "3430"
                        ],
                        "stat_item_id": 24543,
                        "stat_id": "007002300",
                        "value": "3430"
                    },
                    {
                        "is_authorized": true,
                        "ratio_value": [
                            "2539"
                        ],
                        "stat_item_id": 24544,
                        "stat_id": "007002300",
                        "value": "2539"
                    },
                    {
                        "is_authorized": true,
                        "ratio_value": [
                            "31624000"
                        ],
                        "stat_item_id": 24545,
                        "stat_id": "007002300",
                        "value": "31624000"
                    }
                ],
                "point_coordinates": [
                    139.75019,
                    35.68687
                ],
                "geocode": "13101",
                "points": [
                    [
                        "東京都",
                        "千代田区"
                    ]
                ]
            },
            "geometry": {
                "type": "MultiPolygon",
                "coordinates": [
                    [
                        [
                            [
                                139.748974398,
                                35.670568286
                            ],
                            [
                                139.748956569,
                                35.670558464
                            ],
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?