フリーダムの日記

GIS(地理情報システム)を中心に技術的なことを書いています。

ArcGIS Platform で 1km メッシュ別将来推計人口のベクタータイルを Mapbox GL JS で表示してみる。

f:id:freedom0625:20210523151836p:plain

はじめに

今回は、国土数値情報で公開されている 1km メッシュ別将来推計人口(H30国政局推計)(shape形式版)を使用して、Mapbox GL JS で表示してみたいと思います。1kmメッシュ別将来推計人口(H30国政局推計)のデータを使用するに当たって、ArcGIS Platform にデータを登録して、登録したデータに対してベクタータイルを作成します。最後に作成したベクタータイルを Mapbox GL JS で表示しました。

ArcGIS Platform

1kmメッシュ別将来推計人口(H30国政局推計)データは、全国で提供されており、今回は岐阜県のデータを使用しました。属性情報として、2050 年までの将来推計のデータがあり、2030年の属性データを表示しました。属性情報のデータ項目一覧はこちらで確認できます。

nlftp.mlit.go.jp

ArcGIS Platform は Esri が提供している PaaS(Platform as a Service)のサービスで、開発者アカウントを作成すれば、ある一定枠までは無償で使用することができます。もちろん、今回は無償の範囲で行いました。

Mapbox GL JS で表示したベクタータイル ベースマップ

全体のソースは、GitHub にもアップしました。 github.com

ArcGIS Platform に 1km メッシュ別将来推計人口(H30国政局推計)(shape形式版)を登録

ダウンロードした 1km メッシュ別将来推計人口(H30国政局推計)(shape形式版)を ArcGIS Platform に登録します。

ArcGIS Platform に開発者アカウントを作成すると、ダッシュボードが表示されます。ダッシュボードで Layers をクリックし、Import data をクリックして、1km メッシュ別将来推計人口(H30国政局推計)(shape形式版)を選択して、フィーチャ レイヤーを作成していきます。

ArcGIS Platform

Import data をクリックすると以下のような画面が表示されますので、登録したいデータを選択して、Create layer をクリックして、レイヤーを作成します。

ArcGIS Platform

ArcGIS Platform

データを登録すると、以下のようにフィーチャ レイヤーが作成されます。

ArcGIS Platform

1km メッシュ別将来推計人口(H30国政局推計)で作成したフィーチャ レイヤーに対して、ベクタータイルを作成していくのですが、このフィーチャ レイヤーに対して、以下の画面の例のように属性データに応じて分析などを行うことができます。今回は、2030年の将来推計人口に対して、人口の高い順に応じて可視化してみました。自分が可視化した結果を保存して、その状態でベクタータイルを作成することも可能です。

ArcGIS Platform

1km メッシュ別将来推計人口(H30国政局推計)のベクタータイルの作成

ベクタータイルの作成は簡単です。作成した 1km メッシュ別将来推計人口(H30国政局推計)のフィーチャ レイヤーの Publish as new layer セクションで、Publish vector tile layer をクリックします。

ArcGIS Platform

以下の画面が表示されますので、タイトルや、ベクタータイルで使用する属性情報を選択します。Mapbox GL JS などで属性情報を表示する必要がある場合には、ここで表示したい属性情報や分析に使用する属性情報を含めて作成する必要があります。再作成は何度でも行うことができます。

ArcGIS Platform

ベクタータイルが作成されると、以下の画面で、Style や Source layers などを確認することができます。 ArcGIS Platform

また、作成したベクタータイルは、Style Editor で地図のスタイルを編集したりすることもできます。

ArcGIS Platform

Style Editor によるスタイルの変更例

ArcGIS Platform

今回のような ArcGIS Platform にデータを登録したりなど、データ管理の詳細について以下のガイドを確認していただければと思います。

developers.arcgis.com

また、ベクタータイル レイヤーの作成方法について以下のガイドで確認することができます。

developers.arcgis.com

1km メッシュ別将来推計人口(H30国政局推計)のベクタータイルを Mapbox GL JS で表示

最後に作成したベクタータイルを Mapbox GL JS で表示します。

ここで作成したベクタータイルは ArcGIS API など、OpenLayers や Leaflet などでも表示することができます。以下に、それぞれの API で表示する場合の例を確認することができます。今回は Mapbox GL JS を使用しました。 ArcGIS Platform

Mapbox GL JS で表示するスタイルについては、以下の Mapbox のガイドが参考となります。

docs.mapbox.com

2030年の将来推計人口に対して、人口の高い順に応じて表示するようにスタイルを適用しました。以下がそのコード部分です。

      
let PT0_2030_1 = ["<", ["get", "PT0_2030"], 100];
let PT0_2030_2 = ["all", [">=", ["get", "PT0_2030"], 100], ["<", ["get", "PT0_2030"], 300]];
let PT0_2030_3 = ["all", [">=", ["get", "PT0_2030"], 300], ["<", ["get", "PT0_2030"], 500]];
let PT0_2030_4 = ["all", [">=", ["get", "PT0_2030"], 500], ["<", ["get", "PT0_2030"], 800]];
let PT0_2030_5 = ["all", [">=", ["get", "PT0_2030"], 800], ["<", ["get", "PT0_2030"], 1300]];
let PT0_2030_6 = ["all", [">=", ["get", "PT0_2030"], 1300], ["<", ["get", "PT0_2030"], 1800]];
let PT0_2030_7 = ["all", [">=", ["get", "PT0_2030"], 1800], ["<", ["get", "PT0_2030"], 2500]];
let PT0_2030_8 = ["all", [">=", ["get", "PT0_2030"], 2500], ["<", ["get", "PT0_2030"], 3300]];
let PT0_2030_9 = ["all", [">=", ["get", "PT0_2030"], 3300], ["<", ["get", "PT0_2030"], 4500]];
let PT0_2030_10 = ["all", [">=", ["get", "PT0_2030"], 4500], ["<", ["get", "PT0_2030"], 7600]];

let colors = ['#ffffcc', '#ffeda0', '#fed976', '#feb24c', '#fd8d3c', '#fc4e2a', '#e31a1c', '#bd0026', '#940025', '#67001f'] 

map.on("load", () => {
    
    map.addSource("layer-id", {
        type: "vector",
        tiles: ["https://vectortileservices5.arcgis.com/HzGpeRqGvs5TMkVr/arcgis/rest/services/1km_mesh_suikei_2018/VectorTileServer/tile/{z}/{y}/{x}.pbf"]
    });
    
    map.addLayer({
        id: "layer-fill",
        type: "fill",
        source: "layer-id",
        "source-layer": "1km_mesh_suikei_2018",
        paint: {
            "fill-color":
            ["case",
            PT0_2050_1, colors[0],
            PT0_2050_2, colors[1],
            PT0_2050_3, colors[2],
            PT0_2050_4, colors[3], 
            PT0_2050_5, colors[4], 
            PT0_2050_6, colors[5], 
            PT0_2050_7, colors[6], 
            PT0_2050_8, colors[7], 
            PT0_2050_9, colors[8], 
            PT0_2050_10, colors[9], 
            colors[9]
            ],
            "fill-outline-color": "white",
            "fill-opacity": ["case",
                ["boolean", ["feature-state", "hover"], false],
                1,
                0.5
            ]
        }
    });

});

全体のコードは以下になります。

<html>

  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
    <link href="https://api.tiles.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.css" rel="stylesheet" />
    <script src="https://api.tiles.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.js"></script>

    <style>
        body {
            margin: 0;
            padding: 0;
        }
        #map {
            position: absolute;
            top: 0;
            bottom: 0;
            width: 100%;
        }
        .legend {
            background-color: #fff;
            border-radius: 3px;
            bottom: 30px;
            box-shadow: 0 1px 2px rgba(0,0,0,0.10);
            font: 15px/20px 'Helvetica Neue', Arial, Helvetica, sans-serif;
            padding: 10px;
            position: absolute;
            right: 10px;
            z-index: 1;
            line-height: 25px;
            height: 280px;
        }
        .legend h4 {
            margin: 0 0 10px;
        }
        .legend div span {
            border-radius: 50%;
            display: inline-block;
            height: 15px;
            margin-right: 10px;
            width: 15px;
            color:orangered
        }
    </style>
  </head>

  <body>
    <div id="map"></div>
    <div id='county-legend' class='legend'>
        <h4>1kmメッシュ別将来推計人口 2030年</h4>
        <div><span style='background-color: #ffffcc'></span>0 - 100</div>
        <div><span style='background-color: #ffeda0'></span>100 - 300</div>
        <div><span style='background-color: #fed976'></span>300 - 500</div>
        <div><span style='background-color: #feb24c'></span>500 - 800</div>
        <div><span style='background-color: #fd8d3c'></span>800 - 1300</div>
        <div><span style='background-color: #fc4e2a'></span>1,300 - 1,800</div>
        <div><span style='background-color: #e31a1c'></span>1,800 - 2,500</div>
        <div><span style='background-color: #bd0026'></span>2,500 - 3,300</div>
        <div><span style='background-color: #940025'></span>3,300 - 4,500</div>
        <div><span style='background-color: #67001f'></span>4,500 - 7,600</div>
   </div>    
    <script>

      const apiKey = "AAPK07c4048cb3bb48d2a98ee544297631b918Z5mh7gBlBL6owpfjx6YzXq2gnRQzTZhhFiahyfSYojOmvFgnbBV-aaZT9S3nCb";
      const basemapEnum = "ArcGIS:Topographic";

      const map = new mapboxgl.Map({
        container: "map", // the id of the div element
        style: `https://basemaps-api.arcgis.com/arcgis/rest/services/styles/${basemapEnum}?type=style&apiKey=${apiKey}`,
        zoom: 14, // starting zoom
        center: [136.72222, 35.39111],
        pitch: 45,
        hash: true
      });

      map.addControl(new mapboxgl.NavigationControl());

      let scale = new mapboxgl.ScaleControl({
        maxWidth: 250,
        unit: 'metric'
      });

      map.addControl(scale);

      // Create a popup, but don't add it to the map yet.
      let popup = new mapboxgl.Popup({
        closeButton: false,
        closeOnClick: false
      });

      let PT0_2030_1 = ["<", ["get", "PT0_2030"], 100];
      let PT0_2030_2 = ["all", [">=", ["get", "PT0_2030"], 100], ["<", ["get", "PT0_2030"], 300]];
      let PT0_2030_3 = ["all", [">=", ["get", "PT0_2030"], 300], ["<", ["get", "PT0_2030"], 500]];
      let PT0_2030_4 = ["all", [">=", ["get", "PT0_2030"], 500], ["<", ["get", "PT0_2030"], 800]];
      let PT0_2030_5 = ["all", [">=", ["get", "PT0_2030"], 800], ["<", ["get", "PT0_2030"], 1300]];
      let PT0_2030_6 = ["all", [">=", ["get", "PT0_2030"], 1300], ["<", ["get", "PT0_2030"], 1800]];
      let PT0_2030_7 = ["all", [">=", ["get", "PT0_2030"], 1800], ["<", ["get", "PT0_2030"], 2500]];
      let PT0_2030_8 = ["all", [">=", ["get", "PT0_2030"], 2500], ["<", ["get", "PT0_2030"], 3300]];
      let PT0_2030_9 = ["all", [">=", ["get", "PT0_2030"], 3300], ["<", ["get", "PT0_2030"], 4500]];
      let PT0_2030_10 = ["all", [">=", ["get", "PT0_2030"], 4500], ["<", ["get", "PT0_2030"], 7600]];

      let colors = ['#ffffcc', '#ffeda0',
                    '#fed976', '#feb24c',
                    '#fd8d3c', '#fc4e2a',
                    '#e31a1c', '#bd0026',
                    '#940025', '#67001f'] 

      var hoveredStateId = null;

      map.on("load", () => {
        
        map.addSource("layer-id", {
            type: "vector",
            tiles: ["https://vectortileservices5.arcgis.com/HzGpeRqGvs5TMkVr/arcgis/rest/services/1km_mesh_suikei_2018/VectorTileServer/tile/{z}/{y}/{x}.pbf"]
        });
        
        map.addLayer({
            id: "layer-fill",
            type: "fill",
            source: "layer-id",
            "source-layer": "1km_mesh_suikei_2018",
            paint: {
                "fill-color":
                ["case",
                 PT0_2050_1, colors[0],
                 PT0_2050_2, colors[1],
                 PT0_2050_3, colors[2],
                 PT0_2050_4, colors[3], 
                 PT0_2050_5, colors[4], 
                 PT0_2050_6, colors[5], 
                 PT0_2050_7, colors[6], 
                 PT0_2050_8, colors[7], 
                 PT0_2050_9, colors[8], 
                 PT0_2050_10, colors[9], 
                 colors[9]
                ],
                "fill-outline-color": "white",
                "fill-opacity": ["case",
                    ["boolean", ["feature-state", "hover"], false],
                     1,
                     0.5
                ]
            }
        });

      });

      map.on("mousemove", "layer-fill", function(e) {

        map.getCanvas().style.cursor = 'pointer';

        if (e.features.length > 0) {
            if (hoveredStateId !== null) {
                map.setFeatureState(
                    { source: 'layer-id', sourceLayer: '1km_mesh_suikei_2018', id: hoveredStateId },
                    { hover: false }
                );
            }
            hoveredStateId = e.features[0].properties.MESH_ID;
            map.setFeatureState(
                { source: 'layer-id', sourceLayer: '1km_mesh_suikei_2018', id: hoveredStateId },
                { hover: true }
            );
        }

        popup.setLngLat(e.lngLat)
            .setHTML(
            "<div><b>市区町村コード &nbsp;</b>" + e.features[0].properties.SHICODE + "</div>" + 
            "<div><b>将来推計人口 2030年 (男女計)</b></div>" + 
            "<div>" + Math.round(e.features[0].properties.PT0_2030) + " 人</div>")
            .addTo(map);

      });
        
      // When the mouse leaves the state-fill layer, update the feature state of the
      // previously hovered feature.
      map.on("mouseleave", "layer-fill", function() {
        map.getCanvas().style.cursor = '';
        if (hoveredStateId !== null) {
            map.setFeatureState(
                { source: 'layer-id', sourceLayer: '1km_mesh_suikei_2018', id: hoveredStateId },
                { hover: false }
            );
        }
        hoveredStateId = null;
        popup.remove();
      });

    </script>
  </body>
</html>

さいごに

ArcGIS Platform を使用すれば簡単にベクタータイルを作成してサービスとして公開することができました。そして、それを Mapbox GL JS で自由に扱うことができるので、とても便利だなと思います。次回は、Mapbox 以外にも OpenLayers や Leaflet についても触れてみたいと思います。