高崎市にあるヤマダ電機から、車で5分以内、10分以内、20分以内に到達可能なエリア(到達圏)を算出し、統計局の「小地域(町丁・字等別)男女別人口総数及び世帯総数」と組み合わせて、到達圏ごとの男女年代別人口を計算。
このあたりの計算はすべてQGISを計算し、計算結果をGeoJSONとして出力してleaflet.jsを用いて表示している。
<!DOCTYPE html> | |
<html lang='jp'> | |
<head> | |
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.4.0/dist/leaflet.css" | |
integrity="sha512-puBpdR0798OZvTTbP4A8Ix/l+A4dHDD0DGqYW6RQ+9jxkRFclaxxQb/SJAWZfWAkuyeQUytO7+7N4QKrDh+drA==" | |
crossorigin="" /> | |
<script src="https://unpkg.com/leaflet@1.4.0/dist/leaflet.js" | |
integrity="sha512-QVftwZFqvtRNi0ZyCtsznlKSWOStnDORoefr1enyq5mVL4tmKB3S/EnC3rRJcxCPavG10IcrVGSmPh6Qw5lwrg==" | |
crossorigin=""></script> | |
<script src='//unpkg.com/d3@5.0.0/dist/d3.min.js'></script> | |
<style> | |
html, | |
body { | |
width: 100%; | |
height: 100%; | |
padding: 0px; | |
margin: 0px; | |
} | |
#stage { | |
width: 960px; | |
height: 500px; | |
overflow: auto; | |
border:1px solid gray; | |
} | |
#flexbox { | |
display: flex; | |
} | |
#mapid { | |
width: 800px; | |
height: 500px; | |
} | |
#info { | |
padding:10px; | |
font-size: 12px; | |
} | |
#table ,#table tbody { | |
width: 100%; | |
} | |
#table tr:nth-child(even){ | |
background:#F2F2F2; | |
} | |
#table td:nth-child(even){ | |
width: 45px; | |
text-align: right; | |
} | |
#table thead, #table tbody | |
{ | |
display: block; | |
} | |
#table tbody { | |
overflow-y: auto; | |
} | |
#stats { | |
background-color: white; | |
padding: 5px; | |
border-radius: 4px; | |
font-size:12px; | |
} | |
#stats, select { | |
font-size:12px; | |
} | |
#stats td:nth-child(even){ | |
text-align: right; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="stage"> | |
<div id="flexbox"> | |
<div id="mapid"></div> | |
<div id="info"></div> | |
</div> | |
</div> | |
<script> | |
var defaultType = "男20~24歳"; | |
var info = document.getElementById("info"); | |
var format = d3.format(",") | |
var label = { | |
"T000849026": "男20~24歳", | |
"T000849027": "男25~29歳", | |
"T000849028": "男30~34歳", | |
"T000849029": "男35~39歳", | |
"T000849030": "男40~44歳", | |
"T000849031": "男45~49歳", | |
"T000849032": "男50~54歳", | |
"T000849033": "男55~59歳", | |
"T000849034": "男60~64歳", | |
"T000849035": "男65~69歳", | |
"T000849036": "男70~74歳", | |
"T000849046": "女20~24歳", | |
"T000849047": "女25~29歳", | |
"T000849048": "女30~34歳", | |
"T000849049": "女35~39歳", | |
"T000849050": "女40~44歳", | |
"T000849051": "女45~49歳", | |
"T000849052": "女50~54歳", | |
"T000849053": "女55~59歳", | |
"T000849054": "女60~64歳", | |
"T000849055": "女65~69歳", | |
"T000849056": "女70~74歳", | |
} | |
var idLable = { | |
1: "5分以内の到達圏(赤)", | |
2: "10分以内の到達圏(緑)", | |
3: "20分以内の到達圏(青)", | |
} | |
var mymap = L.map('mapid').setView([36.3229373, 139.0125926], 11); | |
L.tileLayer('https://api.maptiler.com/maps/hybrid/{z}/{x}/{y}.jpg?key=K7gU5zPWuSFKb6p1zwsh', { | |
tileSize: 512, | |
zoomOffset: -1, | |
maxZoom: 14, | |
attribution: '<a href="https://maptiler.jp/" target="_blank">© MIERUNE</a> <a href="https://www.maptiler.com/copyright/" target="_blank">© MapTiler</a> <a href="https://www.openstreetmap.org/copyright" target="_blank">© OpenStreetMap contributors</a>', | |
crossOrigin: true | |
}).addTo(mymap); | |
var p1 = d3.json('shouken.geojson'); | |
Promise.all([p1]).then(function (data) { | |
var poligon = data[0]; | |
var marker = L.marker([36.3229373, 139.0125926,]).addTo(mymap); | |
var newpopup = L.popup({ | |
closeOnClick: false, | |
autoClose: false | |
}).setContent("ヤマダ電機"); | |
marker.bindPopup(newpopup).openPopup(); | |
var whenClicked = function(e){ | |
var p = e.target.feature.properties; | |
var tr = Object.keys(p).map(function(key){ | |
if(key.indexOf("T0008")) return ; | |
var keyname = label[key]; | |
if(!keyname) return ; | |
return "<tr><td>"+ keyname+"</td><td>"+ p[key] + "人</td></tr>" | |
}); | |
info.innerHTML = '<table id="table">'+ tr.join("\n") +"</table>"; | |
} | |
var onEachFeature = function (feature, layer) { | |
if (feature.properties) { | |
var p = feature.properties; | |
layer.bindPopup(p.CITY_NAME + p.S_NAME); | |
layer.on({ | |
click: whenClicked | |
}); | |
} | |
} | |
L.geoJSON(poligon, { | |
style: function (feature) { | |
fillColor = "#0000" | |
switch (feature.properties.ID) { | |
case "1": fillColor = "red"; break; | |
case "2": fillColor = "lime"; break; | |
case "3": fillColor = "skyblue"; break; | |
} | |
return { | |
"weight": 0.5, | |
color:"black", | |
fillColor: fillColor, | |
fillOpacity: 0.5 | |
} | |
}, | |
onEachFeature: onEachFeature | |
}).addTo(mymap); | |
var nested = d3.nest() | |
.rollup(function (d) { | |
var values = d.map(function (p) { | |
var p = p.properties; | |
var tmp = {}; | |
Object.keys(p).forEach(function (key) { | |
if (key.indexOf("T0008")) return; | |
tmp[key] = +p[key] | |
}); | |
return tmp | |
}); | |
var sums = {} | |
Object.keys(label).forEach(function (key) { | |
sums[label[key]] = d3.sum(values, function (d) { return d[key] }) | |
}); | |
return sums; | |
}) | |
.key(function (d) { return d.properties.ID }) | |
.map(poligon.features) | |
var renderStats = function (table, type) { | |
var result = nested.entries().map(function (d) { | |
return { id: idLable[d.key], value: d.value[type] } | |
}) | |
table.selectAll("tr").remove(); | |
table | |
.selectAll("tr") | |
.data(result) | |
.enter() | |
.append("tr") | |
.selectAll("td") | |
.data(function (d) { return [d.id, format(d.value) + "人"] }) | |
.enter() | |
.append("td") | |
.text(function (d) { return d }) | |
} | |
L.Control.Watermark = L.Control.extend({ | |
onAdd: function (map) { | |
var div = L.DomUtil.create('div'); | |
var d3div = d3.select(div).attr("id", "stats"); | |
d3div.append("p") | |
.text("到達圏(車)で集計") | |
d3div.append("select") | |
var table = d3div.append("table") | |
var typeSelector = d3div.select("select") | |
typeSelector.selectAll("option") | |
.data(Object.keys(label)) | |
.enter() | |
.append("option") | |
.attr("value", function (key) { return label[key] }) | |
.text(function (key) { return label[key] }) | |
typeSelector.on("change", function () { | |
renderStats(table, this.value) | |
}); | |
renderStats(table, defaultType); | |
return div; | |
} | |
}); | |
L.control.watermark = function (opts) { | |
return new L.Control.Watermark(opts); | |
} | |
L.control.watermark({ position: 'bottomleft' }).addTo(mymap); | |
}); | |
</script> | |
</body> | |
</html> |