はじめに
- H27国勢調査のODデータ(従業・通学市区町村,常住市区町村,男女別自宅外就業者数及び通学者数)をdeck.glで表示してみました
- H27国勢調査のODデータ(従業・通学市区町村,常住市区町村,男女別自宅外就業者数及び通学者数)は、e-statより統計データをダウンロードして用いています。
- deck.glの表示には、ArcLayer及びTextLayerを用いています。
アウトプットイメージ
H27国勢調査の自宅外就業者数及び通学者数(自市区町村での従業・通学は含まず)をhttps://t.co/j5x1odcLvTのArcLayerで可視化してみた🙋
— shi_works🌥️ (@syanseto) November 3, 2020
ODの位置が同じ場合はどうしようかと思ったけど、getTiltで円弧を横に傾けることができる😇https://t.co/j5x1odcLvTごいすー🙆♂️https://t.co/IrQgCnYaEV pic.twitter.com/Bzplsa21iu
H27国勢調査の就業者数・通学者数のhttps://t.co/j5x1odcLvTのArcLayerに、
— shi_works🌥️ (@syanseto) November 7, 2020
・市区町村名(ローマ字表記)
・ツールチップで就業者数・通学者数の表示
を追加してみた😇 pic.twitter.com/hsgjJduJro
事前準備
- H27国勢調査のODデータをArcLayerで表示するためには、市区町村(重心)の座標データが必要です。
- 市区町村(重心)の座標データは、e-statより小地域の境界データをダウンロードして、QGISで小地域のポリゴンデータを市区町村単位でマージを行うとともに、市区町村(重心)の座標データを作成して、ODデータに市区町村(重心)の座標を紐づけています。
- 作成した、ODデータ(座標付き)は以下のとおりです。
- ヘッダ名にfromが付いているものが常住市区町村の市区町村コード(5桁)、市区町村名及び座標(lng及びlat)、toが付いているものが従業・通学市区町村の市区町村コード(5桁)、市区町村名及び座標(lng及びlat)になります。
- jinkoが自宅外就業者数及び通学者数になります。
- 市区町村名をTextLayerで表示するため(deck.glのTextLayerは日本語表記には未対応のためローマ字表記になる)に、市区町村のポイントデータを以下のとおり作成して用いています。
前提条件
- Mapboxのアクセストークンが必要になります。
- Mapboxのアクセストークンを入力してください。
- ODデータ(座標付き)(H27ka01_Commute_v2.csv)を読み込んでいます。
- 国土数値情報の鉄道データ(ラインデータ:N02-19_RailroadSection.geojson)を読み込んでいます。
- 市区町村のポイントデータ(h27ka01_47_city_center_v2.csv)を読み込んでいます。
html
H27kokusei_cityjyugyojinko.html
<html>
<head>
<title>平成27年国勢調査 常住市区町村 従業・通学市区町村 自宅外就業者数及び通学者数</title>
<meta charset="UTF-8">
<!--<script src="https://unpkg.com/deck.gl@^7.0.0/dist.min.js"></script>-->
<script src="https://unpkg.com/deck.gl@^8.0.0/dist.min.js"></script>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://api.tiles.mapbox.com/mapbox-gl-js/v0.50.0/mapbox-gl.js"></script>
<link rel="stylesheet" href="style.css"/>
</head>
<body>
<div id="control-panel">
<div>
<label>opacity</label>
<input id="opacity" type="range" min="0" max="1" step="0.01" value="0.3"></input>
<span id="opacity-value"></span>
</div>
<div>
<label>getHeight</label>
<input id="getHeight" type="range" min="0" max="2" step="0.1" value="1"></input>
<span id="getHeight-value"></span>
</div>
<div>
<label>getTilt</label>
<input id="getTilt" type="range" min="0" max="30" step="1" value="5"></input>
<span id="getTilt-value"></span>
</div>
<div>
<label>Font Size</label>
<input id="getSize" type="range" min="0" max="100" step="1" value="0"></input>
<span id="getSize-value"></span>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
CSS
style.css
html,body {
font-family: Helvetica, Arial, sans-serif;
width: 100vw;
height: 100vh;
margin: 0;
}
.deck-tooltip {
font-family: Helvetica, Arial, sans-serif;
padding: 6px !important;
margin: 8px;
color: #ffffff;
background-color: #4682b4;
max-width: 300px;
font-size: 20px;
}
#control-panel {
position: absolute;
top: 0;
left: 0;
margin: 12px;
padding: 20px;
font-size: 12px;
line-height: 1.5;
z-index: 1;
background: #fff;
font-family: Helvetica, Arial, sans-serif;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.15);
}
label {
display: inline-block;
width: 140px;
}
JavaScript
script.js
const deckgl = new deck.DeckGL({
mapboxApiAccessToken: 'Mapboxのアクセストークンを入力してください',
mapStyle: 'mapbox://styles/mapbox/dark-v9',
initialViewState: {
latitude: 35.1801494,
longitude: 136.9034925,
zoom: 7,
maxZoom: 16,
pitch: 45,
bearing: 15
},
controller: true,
});
const OPTIONS = ['opacity','getHeight','getTilt','getSize'];
const getValue=(d) =>{
return Number(d.jinko);
};
const getValue2=(d) =>{
return d.city_name;
};
const getValue3=(d) =>{
return Number(d.jinko/1000);
};
const jinko = d3.csv('data/H27ka01_Commute_v2.csv');
const railway = d3.json('data/N02-19_RailroadSection.geojson');
const city = d3.csv('data/h27ka01_47_city_center_v2.csv');
const renderLayer = () => {
const options = {};
OPTIONS.forEach(key => {
const value = document.getElementById(key).value;
document.getElementById(key + '-value').innerHTML = value;
options[key] = Number(value);
});
const arcLayer = new deck.ArcLayer({
id: 'arc',
data: jinko,
opacity: 0.3,
pickable: true,
getSourcePosition: d => [Number(d.from_lng), Number(d.from_lat)],
getTargetPosition: d => [Number(d.to_lng), Number(d.to_lat)],
getSourceColor: [204,255,204],
getTargetColor: [0,203,0],
getHeight: 1,
getTilt: 5,
getWidth: getValue3,
...options
});
const geoJsonLayer = new deck.GeoJsonLayer({
id: 'geojson',
data: railway,
stroked: false,
filled: false,
lineWidthMinPixels: 2,
parameters: {
depthTest: false
},
opacity: 0.5,
getLineColor: [240,240,240],
getLineWidth: 2,
pickable: false,
});
const textLayer = new deck.TextLayer({
id: 'text',
data: city,
pickable: true,
getPosition: d => [Number(d.lng), Number(d.lat)],
getText: d => d.city_name_en,
getSize: 0,
getAngle: 0,
getColor: [255,255,255],
getTextAnchor: 'middle',
getAlignmentBaseline: 'center',
...options
});
deckgl.setProps({
layers:[arcLayer, geoJsonLayer, textLayer],
getTooltip: ({object}) => object && `${object.from_cityname} ⇒ ${object.to_cityname} 通勤・通学人口: ${object.jinko}`
});
}
renderLayer();
OPTIONS.forEach(key => {
document.getElementById(key).oninput = renderLayer;
});
参考文献