はじめに
前の記事では、東京都23区の年収データをGeoJSON形式で取得し、平均年収に応じて色分け表示しました。
今回は、新たな年収データを用いて各区の平均年収表示を行い、加えて各区の年収ヒストグラムを作成してみました。
完成画面
画面上半分には地図が表示され、各区の平均年収で色分けしています。
画面下半分には、クリックした区の年収ヒストグラムが表示されます。グラフのx軸は年収範囲、y軸は世帯数となっており、各区の年収分布状況を視覚的に把握できます。
新年収データについて
新年収データ(年収別世帯数推計データ)住宅・土地統計および賃金関係データを最新年度に更新しています。
コードについて
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
],