H27国勢調査のODデータ（自宅外就業者数及び通学者数）をdeck.glで表示してみました

# はじめに
- H27国勢調査のODデータ（従業・通学市区町村，常住市区町村，男女別自宅外就業者数及び通学者数）を[deck.gl](https://deck.gl/)で表示してみました
- H27国勢調査のODデータ（従業・通学市区町村，常住市区町村，男女別自宅外就業者数及び通学者数）は、[e-stat](https://www.e-stat.go.jp/)より[統計データ](https://www.e-stat.go.jp/stat-search/files?page=1&layout=datalist&toukei=00200521&tstat=000001080615&cycle=0&tclass1=000001101935&tclass2=000001101936&tclass3val=0)をダウンロードして用いています。
- deck.glの表示には、[ArcLayer](https://deck.gl/docs/api-reference/layers/arc-layer)及び[TextLayer](https://deck.gl/docs/api-reference/layers/text-layer)を用いています。

# アウトプットイメージ
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">H27国勢調査の自宅外就業者数及び通学者数（自市区町村での従業・通学は含まず）を<a href="https://t.co/j5x1odcLvT">https://t.co/j5x1odcLvT</a>のArcLayerで可視化してみた🙋<br><br>ODの位置が同じ場合はどうしようかと思ったけど、getTiltで円弧を横に傾けることができる😇<a href="https://t.co/j5x1odcLvT">https://t.co/j5x1odcLvT</a>ごいすー🙆‍♂️<a href="https://t.co/IrQgCnYaEV">https://t.co/IrQgCnYaEV</a> <a href="https://t.co/Bzplsa21iu">pic.twitter.com/Bzplsa21iu</a></p>&mdash; shi_works🌥️ (@syanseto) <a href="https://twitter.com/syanseto/status/1323501923023036417?ref_src=twsrc%5Etfw">November 3, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">H27国勢調査の就業者数・通学者数の<a href="https://t.co/j5x1odcLvT">https://t.co/j5x1odcLvT</a>のArcLayerに、<br>・市区町村名（ローマ字表記）<br>・ツールチップで就業者数・通学者数の表示<br>を追加してみた😇 <a href="https://t.co/hsgjJduJro">pic.twitter.com/hsgjJduJro</a></p>&mdash; shi_works🌥️ (@syanseto) <a href="https://twitter.com/syanseto/status/1324896230741209088?ref_src=twsrc%5Etfw">November 7, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# 事前準備
- H27国勢調査のODデータを[ArcLayer](https://deck.gl/docs/api-reference/layers/arc-layer)で表示するためには、市区町村（重心）の座標データが必要です。
- 市区町村（重心）の座標データは、[e-stat](https://www.e-stat.go.jp/)より[小地域の境界データ](https://www.e-stat.go.jp/gis/statmap-search?page=1&type=2&aggregateUnitForBoundary=A&toukeiCode=00200521)をダウンロードして、QGISで小地域のポリゴンデータを市区町村単位でマージを行うとともに、市区町村（重心）の座標データを作成して、ODデータに市区町村（重心）の座標を紐づけています。
- 作成した、ODデータ（座標付き）は以下のとおりです。
- ヘッダ名にfromが付いているものが常住市区町村の市区町村コード（5桁）、市区町村名及び座標（lng及びlat）、toが付いているものが従業・通学市区町村の市区町村コード（5桁）、市区町村名及び座標（lng及びlat）になります。
- jinkoが自宅外就業者数及び通学者数になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205045/b89cd7c7-c68b-80bf-ad0c-088d01e224f9.png)
- 市区町村名をTextLayerで表示するため（deck.glのTextLayerは日本語表記には未対応のためローマ字表記になる）に、市区町村のポイントデータを以下のとおり作成して用いています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205045/c18fdb6c-8237-4b4b-bbb3-39b2e9ab129f.png)

# 前提条件
- Mapboxのアクセストークンが必要になります。
- Mapboxのアクセストークンを入力してください。
- ODデータ（座標付き）（H27ka01_Commute_v2.csv）を読み込んでいます。
- [国土数値情報の鉄道データ](https://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-N02-v2_3.html)（ラインデータ：N02-19_RailroadSection.geojson）を読み込んでいます。
- 市区町村のポイントデータ（h27ka01_47_city_center_v2.csv）を読み込んでいます。

# html
```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
```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
```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;
});
```
# 参考文献
https://deck.gl/docs/api-reference/layers/arc-layer

https://deck.gl/examples/arc-layer/

https://deck.gl/docs/api-reference/layers/text-layer

https://deck.gl/examples/text-layer