フリーダムの日記

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

Mapbox GL JS を使用して到達圏解析を実装してみた。

はじめに

今回は、Mapbox GL JS と ArcGIS Platform で提供されている到達圏解析のサービスを使用して、到達圏解析が実行できる簡単なアプリを作成しました。

Mapbox GL JS 到達圏解析

到達圏解析サービスなどのロケーションサービスについては、前回のブログでも紹介していますので、ご参照ください。

freedom-tech.hatenablog.com

作成したサンプルアプリは以下になります。 地図をクリックした地点から 250m、500m、750m、1000m のエリアを表示しています。

Mapbox GL JS 到達圏解析 アプリは GitHub に公開しております。また、サンプルアプリのソースコードは、GitHub にも公開していますのでご参照ください。

到達圏解析は、ArcGIS Platform で提供されている Service Areas を使用しました。 developers.arcgis.com

Service Areas(到達圏)とは

Service Area(到達圏)は、isochrone とも呼ばれ、道路ネットワーク上を車や徒歩で移動したときに到達できる距離をポリゴンで表現することができます。この分析では、不動産の検索や学校、企業、その他の施設への運転時間を判断する際によく用いられます。例えば、都市の中心部から任意の方向に 20分で移動できる距離を運転時間のポリゴンで作成することができます。

developers.arcgis.com

Service Area(到達圏)を利用して、以下のようなアプリケーションを構築することができます。

  • 何らかのサービスを提供している場所へのアクセシビリティを可視化して測定します。例えば、食料品店の周囲にドライブ時間 3分のポリゴンを設定し、どの住民が 3分以内に店に到着することができ、その結果、その店で買い物をする可能性が高いのかを判断することができます。

  • 1つまたは複数の場所の周囲に複数の Service Area(到達圏)を生成することで、移動時間や移動距離の増加に伴ってアクセシビリティの変化を示すことができます。例えば、学校から車で 5分、10分、15分以内のところにどれだけの病院があるかを調べることができます。

  • 移動時間に基づいてサービスエリアを作成する場合、交通データを活用することで、時間帯によって到達可能なエリアに影響を与える可能性があります。

Mapbox GL JS による Service Areas(到達圏)の実装

下記のチュートリを参考にして実装しています。ここでは、Mapbox GL JS で使用している ArcGIS REST JS について説明します。

developers.arcgis.com

Mapbox GL JS で Service Areas(到達圏)のサービスを利用するには、ArcGIS REST JS を使用することで簡単に実装することができるようになっています。Mapbox GL JS から ArcGIS REST JS のクラスやメソッドを使用することができます。 Service Areas(到達圏)のポリゴンの作成など基本的なアプリの機能は、Mapbox GL JS で実装しています。

esri.github.io

Mapbox GL JS から ArcGIS REST JS を使用するためにヘッダー部で以下のように設定します。

  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
     .......
    // ArcGIS REST JS
    <script src="https://unpkg.com/@esri/arcgis-rest-request@3.0.0/dist/umd/request.umd.js"></script>
    <script src="https://unpkg.com/@esri/arcgis-rest-routing@3.0.0/dist/umd/routing.umd.js"></script>
    <script src="https://unpkg.com/@esri/arcgis-rest-auth@3.0.0/dist/umd/auth.umd.js"></script>
    .......

Service Areas(到達圏)サービスの使用は、ArcGIS REST の serviceArea メソッドを使用します。 serviceArea のリクエストパラメータでは、authentication には API キー、facilities には、Service Areas(到達圏)が生成される 1 つまたは複数の場所を指定します。

defaultBreaks には、各施設に生成する Service Areas(到達圏)のサイズと数を指定します。今回はサイズとして 250m、500m、750m、1000m の4つ指定しました。

impedanceAttributeName ではインピーダンスを指定します。インピーダンスの初期値は TravelTime で、その他の値として、TravelTime|TruckTravelTime|WalkTime|Miles|Kilometers を指定することができます。

Service Areas(到達圏)サービスには多くの便利な機能があります。詳細については、以下をご参照ください。

developers.arcgis.com

        // クリック地点のポイント 
        const coordinates = e.lngLat.toArray();
        const point = {
          type: "Point",
          coordinates
        };
        map.getSource("start").setData(point);

        // API キーの設定
        const authentication = new arcgisRest.ApiKey({
           key: apiKey
        });

        // パラメータの設定
        const addParams = { defaultBreaks: [0.25, 0.5, 0.75, 1], // km
                            impedanceAttributeName: "Kilometers",
                          }; 

        arcgisRest
          .serviceArea({
            authentication,
            facilities: [coordinates],
            params: addParams
          })

          .then((response) => {

            map.getSource("servicearea").setData(response.saPolygons.geoJson);
    
            //
              .......
              .......
            //
    
          });

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

<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>

    <script src="https://unpkg.com/@esri/arcgis-rest-request@3.0.0/dist/umd/request.umd.js"></script>
    <script src="https://unpkg.com/@esri/arcgis-rest-routing@3.0.0/dist/umd/routing.umd.js"></script>
    <script src="https://unpkg.com/@esri/arcgis-rest-auth@3.0.0/dist/umd/auth.umd.js"></script>

    <style>
        body {
            margin: 0;
            padding: 0;
        }
        #map {
            position: absolute;
            top: 0;
            bottom: 0;
            width: 100%;
        }
        #infoDiv {
          background-color: white;
          position: relative;
          color: black;
          font-size: 12pt;
          margin: 6px;
          width: 350px;
        }
    </style>
  </head>

  <body>
    <div id="map"></div>
    <div id="infoDiv">
        Mapbox GL JS と Esri の <a href="https://developers.arcgis.com/documentation/mapping-apis-and-location-services/routing/service-areas/
" target="_blank">到達圏解析 (Service areas)</a> を使用した簡単なデモです。<br/><br/>
        地図をクリックした地点から 250m、500m、750m、1000m のエリアを表示しています。<br/>
    </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: [139.767125, 35.681236],
        pitch: 45,
        hash: true
      });

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

      let scale = new mapboxgl.ScaleControl({
        maxWidth: 200,
        unit: 'metric'
      });
      map.addControl(scale, "bottom-right");

      function addServiceAreaLayer() {

        map.addSource("servicearea", {
          type: "geojson",
          data: {
            type: "FeatureCollection",
            features: []
          }
        });

        map.addLayer({
          id: "servicearea-fill",
          type: "fill",
          source: "servicearea",
          paint: {
            "fill-color": [
              "match",
              ["get", "ObjectID"],
              1,
              "hsl(210, 80%, 40%)",
              2,
              "hsl(210, 80%, 60%)",
              3,
              "hsl(210, 80%, 80%)",
              4,
              "hsl(210, 80%, 100%)",
              "transparent"
            ],
            "fill-outline-color": "black",
            "fill-opacity": 0.5
          }
        });

      }
    
      function addServiceAreaLabelLayer() {

        map.addSource("servicearealabel", {
          type: "geojson",
          data: {
            type: "FeatureCollection",
            features: []
          }
        });
        
        map.addLayer({
          id: "servicearea-label",
          type: "symbol",
          source: "servicearealabel",
          layout: {
            "icon-allow-overlap": true,
            'text-field': ['get', 'ToBreak'],
            'text-font': ["Arial Italic"],
            'text-size': 20,
            'text-anchor': 'top'
          },
          'paint': {
            'text-color': 'rgba(0,0,0,0.5)',
            "text-halo-color": "#fff",
            "text-halo-width": 2
           }
        });

      }

      function addStartingPointLayer() {

        map.addSource("start", {
          type: "geojson",
          data: {
            type: "FeatureCollection",
            features: []
          }

        });
        map.addLayer({
          id: "start-circle",
          type: "circle",
          source: "start",

          paint: {
            "circle-radius": 6,
            "circle-color": "white",
            "circle-stroke-color": "black",
            "circle-stroke-width": 2
          }
        });

      }

      map.on("load", () => {
        addServiceAreaLayer();
        addServiceAreaLabelLayer();
        addStartingPointLayer();
      });

      map.on("click", (e) => {

        const coordinates = e.lngLat.toArray();
        const point = {
          type: "Point",
          coordinates
        };
        map.getSource("start").setData(point);

        const authentication = new arcgisRest.ApiKey({
          key: apiKey
        });

        const addParams = { defaultBreaks: [0.25, 0.5, 0.75, 1], // km
                            impedanceAttributeName: "Kilometers",
                          }; 
        
        arcgisRest
          .serviceArea({
            authentication,
            facilities: [coordinates],
            params: addParams
          })

          .then((response) => {

            map.getSource("servicearea").setData(response.saPolygons.geoJson);

            const features = [];
            for (const value of response.saPolygons.features) {
                //console.log(value.attributes.ToBreak);
                let feature = {
                    "type": "Feature",
                    "geometry": {
                        "type": "Point",
                        "coordinates": value.geometry.rings[0][0]
                    },
                    "properties": {
                        "ToBreak": value.attributes.ToBreak * 1000 + " m"
                    }
                }
                features.push(feature);
            }
            const geojson = {type: "FeatureCollection", features: features}
            map.getSource("servicearealabel").setData(geojson);
            //console.log(features)
          });

      });

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

さいごに

今回は、Mapbox GL JS で ArcGIS Platform で提供されている 到達圏サービスを使用しました。Mapbox GL JS では Mapbox のサービスも使用できますが、ArcGIS Platform のサービスも利用できるのは、オープンソースの醍醐味ですね。今後もオープンソースを使用して紹介できればと思います。