H27国勢調査の500mメッシュ人口データをdeck.glで表示してみました

# はじめに
- H27国勢調査の500mメッシュ人口データを[deck.gl](https://deck.gl/)で表示してみました。
- H27国勢調査の500mメッシュ人口データは、[e-stat](https://www.e-stat.go.jp/)より[統計データ](https://www.e-stat.go.jp/gis/statmap-search?type=1)及び[境界データ](https://www.e-stat.go.jp/gis/statmap-search?type=2)をダウンロードして用いています。
- deck.glでの表示には、[HexagonLayer](https://deck.gl/docs/api-reference/aggregation-layers/hexagon-layer)を用いています。

# アウトプットイメージ
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://t.co/j5x1odcLvT">https://t.co/j5x1odcLvT</a> のサンプルコードを参考（<a href="https://t.co/lLUAmWQ2mR">https://t.co/lLUAmWQ2mR</a>）に、H27国勢調査（500mメッシュ人口）を可視化してみた😇<br><br>グリグリ動かせて、いい感じです🗾<a href="https://t.co/j5x1odcLvT">https://t.co/j5x1odcLvT</a> ごいすー🍵😄 <a href="https://t.co/rSpML7NfHH">pic.twitter.com/rSpML7NfHH</a></p>&mdash; shi_works🌥️ (@syanseto) <a href="https://twitter.com/syanseto/status/1315118634172252160?ref_src=twsrc%5Etfw">October 11, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

![Animation14.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205045/88c14444-6e9c-80a5-bc8c-f86b51fcbbc2.gif)

# 事前準備
- H27国勢調査の500mメッシュ人口データをHexagonLayerで表示するには、500mメッシュのポリゴンデータをポイントデータ（メッシュの重心）に変換する必要があります。
- 500mメッシュのポリゴンデータからポイントデータ（メッシュの重心）の変換にはQGISを用いています。
- QGISでの作業の流れは以下のとおりです（本記事では詳細は割愛します）。
- H27国勢調査の500mメッシュ人口データの境界データと統計データをQGIS上で結合します。
- 結合したデータ（ポリゴンデータ）の重心座標を求め、ポイントデータをcsv形式で保存します。
- 保存した、csv形式のポイントデータは以下のとおりです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205045/adbe7d66-f0b3-2e8a-8e33-3d1f9504d9ac.png)

# 前提条件
- Mapboxのアクセストークンが必要になります。
- Mapboxのアクセストークンを入力してください。
- 27国勢調査の500mメッシュ総人口（ポイントデータ：H27kokusei_4jimesh_point.csv）を読み込んでいます。
- また、[国土数値情報の鉄道データ](https://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-N02-v2_3.html)（ラインデータ：N02-19_RailroadSection.geojson）を読み込んでいます。

# html
```html:H27kokusei_4jimeshjinko.html
<html>
  <head>
    <title>平成27年国勢調査 500mメッシュ総人口</title>
    <meta charset="UTF-8">
    <script src="https://unpkg.com/deck.gl@^7.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>Radius</label>
        <input id="radius" type="range" min="500" max="20000" step="100" value="2000"></input>
        <span id="radius-value"></span>
      </div>
      <div>
        <label>ElevationScale</label>
        <input id="elevationScale" type="range" min="0" max="500" step="50" value="200"></input>
        <span id="elevationScale-value"></span>
      </div>
      <div>
        <label>Coverage</label>
        <input id="coverage" type="range" min="0" max="1" step="0.1" value="1"></input>
        <span id="coverage-value"></span>
      </div>
      <div>
        <label>opacity</label>
        <input id="opacity" type="range" min="0" max="1" step="0.01" value="1"></input>
        <span id="opacity-value"></span>
      </div>
    </div>
    <div id="tooltip"></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;
  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/streets-v9',
  initialViewState: {
    latitude: 35.1801494,
    longitude: 136.9034925,
    zoom: 7,
    maxZoom: 16,
    pitch: 45,
    bearing: 15
  },
  controller: true,
  });

const OPTIONS = ['radius', 'elevationScale', 'coverage', 'opacity'];

const colorRange  = [
  [1, 152, 189],
  [73, 227, 206],
  [216, 254, 181],
  [216, 254, 181],
  [254, 237, 177],
  [254, 237, 177],
  [254, 237, 177],
  [254, 237, 177],
  [254, 173, 84],
  [254, 173, 84],
  [254, 173, 84],
  [254, 173, 84],
  [254, 173, 84],
  [254, 173, 84],
  [254, 173, 84],
  [254, 173, 84],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
  [209, 55, 78],
];

const getValue=(d) =>{
        // console.log(d.jinko)
        return Number(d.jinko);
};

const data = d3.csv('data/H27kokusei_4jimesh_point.csv');
const railway = d3.json('data/N02-19_RailroadSection.geojson');

const renderLayer = () => {
    const options = {};
    OPTIONS.forEach(key => {
      const value = document.getElementById(key).value;
      document.getElementById(key + '-value').innerHTML = value;
      options[key] = Number(value);
    });

    const hexagonLayer = new deck.HexagonLayer({
      id: 'hex',
      data,
      getPosition: d => [Number(d.lng), Number(d.lat)],
      colorRange,
      getColorWeight: getValue,
      getElevationWeight: getValue,
      elevationScale: 200,
      extruded: true,
      pickable: true,
      radius: 1000,
      opacity: 1,
      coverage: 1,
      upperPercentile: 100,
      ...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,
    });
    
    deckgl.setProps({
      layers:[hexagonLayer, geoJsonLayer],
      getTooltip: ({object}) => object && `総人口: ${object.elevationValue}`
    });

}

renderLayer();

OPTIONS.forEach(key => {
  document.getElementById(key).oninput = renderLayer;
});
```
# 参考文献
https://deck.gl/docs/api-reference/aggregation-layers/hexagon-layer

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