今すぐできる！ArcGIS Living Atlasで始める GIS アプリケーション開発

こちらは、[ArcGIS活用法アドベントカレンダー](https://qiita.com/advent-calendar/2024/arcgis)2024の15日目の記事です。他の記事もぜひご覧ください。

今回は、下図のような地図にデータをプロットするだけのGISアプリケーションを作成してみます。

![スクリーンショット 2024-12-14 2.16.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3942828/f7e3c742-5e59-f0b0-d756-aba37e6ea87c.png)


# GISを活用するハードル
 GISを使った分析には当然ながらデータが欠かせません。
 幸い日本においては国土交通省の国土数値情報を始めとした位置情報を有したオープンデータが商用、非商用を問わずに利用できる地盤が整っています。
 しかし、それらをGISで使うにはデータをダウンロードして、ダウンロードしたデータを自身で管理する必要があります。また、どのようにして使うかにもよりますが、例えばWebアプリケーションで利用するとなればデータを自身が管理するサーバーに配置するなどしてデータを参照する基盤を構築する必要があります。また、目的のデータを探すのにもそれなりに時間を擁します。そのためGISを活用したアプリケーション開発のハードルとなるかと思います。
 
# ArcGIS Living Atlas of the World とは
> ArcGIS Living Atlasは、 ArcGIS Online コンテンツから、Esri、Esri パートナー、および GIS ユーザー コミュニティからの、信頼できかつ最良のコンテンツを精選したコレクションです。
> Living Atlas のコンテンツの特徴は、Esri の各分野のエキスパートにより検証および監修され ArcGIS システム全体の一般的なワークフローに統合されていることです。たとえば、Map Viewer や ArcGIS Pro でマップを作成する際に、Living Atlas からベースマップやコンテンツを検索して、目的のマップを構築できます。<br>
 > 引用:[ArcGISブログ](https://blog.esrij.com/2023/01/12/post-47263/)

上記の通り、ArcGIS Living Atlasには、日本のオープンデータもいくつかあります。国土数値情報で配布されている各種データやアメダスの気象データ、警視庁が公開するデータなどが代表例です。詳しくはLiving Atlasのページで[日本から公開されたデータに絞って検索](https://livingatlas.arcgis.com/en/browse/#d=2&rgnCode=JP)することで確認できます。
ここから探したデータを使って簡単なGISアプリケーションを作ってみましょう。

# オープンデータを使って簡単な GIS アプリケーションを作ってみよう
今回は、国土数値情報の「[小学校区データ](https://www.arcgis.com/home/item.html?id=96f82dd5f09546adb5036326c0dcbf57)」と警察庁の「[交通事故箇所(2023年データ)](https://www.arcgis.com/home/item.html?id=2fecd435295147e8a945334bac444648)」を使って、小学校区ごとの交通事故発生数の可視化を行なっていきます。

## 無償から使える ArcGIS Location Platform 

ArcGISのアカウントをお持ちでない方は[開発者アカウントの作成]()を参考に無償から使えるArcGIS Location Platformのアカウントで始めましょう。
今回はAPIキーなしでログインして使ってみましょう。

## 使用する SDK
  ArcGIS Maps SDK for JavaScript 4.31

## 地図を表示してみる

地図を表示するだけならこれくらいのシンプルなコードでできます。
実際に表示する際にはログインもしくはAPIキーが必要です。

``` HTML
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>初めてのGISアプリケーション</title>
    <!-- ArcGIS JS をCDNで参照して利用 -->
    <link rel="stylesheet" type="text/css" href="https://js.arcgis.com/4.31/esri/themes/dark/main.css"/>
    <script src="https://js.arcgis.com/4.31/"></script>
    <style>
    html,
body,
#view {
  margin: 0;
  height: 100%;
  width: 100%;
}
    </style>

</head>
<body>
    <div id="view"></div>
    <script>
        require([ "esri/Map",
                  "esri/views/MapView"], 
                  (Map, MapView)  =>{ 
            const maps = new Map({
              basemap: "arcgis/streets" // 今回は一番無難そうなものを
            });
          
            const view = new MapView({
              container: "view", // divのidを入力
              map: maps,
              zoom: 10,
              // 東京タワーの中心座標
              center: [139.69171, 35.6895]
            });
        });
    </script>
</body>
</html>
```

![スクリーンショット 2024-12-14 2.46.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3942828/1a338016-b774-8c56-c320-be76887a69d9.png)


初期表示位置が微妙ですが、ひとまず地図が表示されることを確認できました。
選べる地図もさまざまありますが、ここでは日本語ラベルのマップを参照しています。

## 使いたいレイヤーを表示する

次に「小学校区」と「交通事故箇所」のデータを表示してみます。
下記に掲載のコードは、上記のhtmlのscriptタグ内に追加するようなかたちでお試しください。
なお、どちらも縮尺によっては表示できないのですが、一旦そのままで表示してみます。

``` JavaScript
require([ "esri/Map",
          "esri/views/MapView",
          "esri/layers/FeatureLayer",
          "esri/widgets/Legend"], 
          (Map, MapView,FeatureLayer,Legend)  =>{ 
            const primary_school = new FeatureLayer({
                portalItem: {
                    id: "96f82dd5f09546adb5036326c0dcbf57"
                  },
            });

            const traffic_accident2023 = new FeatureLayer({
                portalItem: {
                    id: "2fecd435295147e8a945334bac444648"
                  }
              });
        
            const maps = new Map({
              basemap: "arcgis/streets",// 今回は一番無難そうなものを
              layers: [primary_school,traffic_accident2023] 
            });
          
            const view = new MapView({
              container: "view", // divのidを入力
              map: maps,
              zoom: 10,
              // 東京タワーの中心座標
              center: [139.69171, 35.6895]
            });
            // 凡例を表示
            const legend = new Legend({
                view: view,
                layerInfos: [
                  {
                    layer: traffic_accident2023,
                    title: "交通事故箇所(2023)"
                  }
                ]
              });
            view.ui.add(legend, "bottom-right");
    });
```

上記の記述によって下図のような地図ができました。
少しズームして見てみると大通り沿いに目立ちますね。

![スクリーンショット 2024-12-14 3.05.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3942828/2b25a1d1-521e-a03f-41db-f5ba26c98273.png)


もし、縮尺による制限を取っ払いたい場合は、minscaleプロパティを追加すれば表示されます。
ここまででも十分可視化ができているとは言えますが、少し煩雑でどの辺りでどの種類の事故が起きているかわかりづらいですね。これを少し良くしていきます。

## 交通事故箇所をパイチャートで表現する

Esri Developer のサイトでは、さまざまなサンプルが用意されています。
今回はその中でもクラスターの表現を使ったパイチャートのできる[サンプル](https://developers.arcgis.com/javascript/latest/sample-code/featurereduction-cluster-pie-charts/)をそのまま持ってきて交通事故のレイヤーに割り当ててみましょう。

``` JavaScript
require([ "esri/Map", "esri/views/MapView" , "esri/layers/FeatureLayer","esri/widgets/Legend",
  "esri/widgets/Expand",
  "esri/widgets/Home",
  "esri/smartMapping/labels/clusters",
  "esri/smartMapping/renderers/pieChart"], function (Map, MapView, FeatureLayer,Legend,
    Expand,
    Home,
    clusterLabelCreator,
    pieChartRendererCreator)  {(async ()=>{
    const primary_school = new FeatureLayer({
        portalItem: {
            id: "96f82dd5f09546adb5036326c0dcbf57"
          },
    });

    const traffic_accident2023 = new FeatureLayer({
        portalItem: {
            id: "2fecd435295147e8a945334bac444648"
          },
          minScale: 0 // 制限を取っ払って見てます
      });

    const maps = new Map({
      basemap: "arcgis/streets",
        layers: [primary_school,traffic_accident2023]
    });
  
    const view = new MapView({
      container: "view", // divのidを入力
      map: maps,
      zoom: 12,
      // 東京タワーの中心座標
      center: [139.69171, 35.6895]

    });
  
    await view.when();
    const layer = view.map.layers.getItemAt(1);
    layer.effect = "drop-shadow(2px, 2px, 2px, gray)";


    layer.when()
      .then(generateClusterConfig)
      .then((featureReduction) => {

        layer.featureReduction = featureReduction;

      }).catch((error) => {
        console.error(error);
      });


    async function generateClusterConfig(layer){
      // generates default labelingInfo
      const { labelingInfo, clusterMinSize } = await clusterLabelCreator
        .getLabelSchemes({ layer, view })
        .then(labelSchemes => labelSchemes.primaryScheme );

      const labelSymbol = labelingInfo[0].symbol;
      labelSymbol.color = labelSymbol.haloColor.clone();
      labelSymbol.haloColor = [255,255,255,0.3];
      labelSymbol.font.size = 10;


      const { renderer, fields } = await pieChartRendererCreator
        .createRendererForClustering({
          layer,
          shape: "donut"
        });

      renderer.holePercentage = 0.66;

      const fieldInfos = fields.map( field => {
        return {
          fieldName: field.name,
          label: field.alias,
          format: {
            places: 0,
            digitSeparator: true
          }
        }
      });

      // maps the field names for the popup chart
      const fieldNames = fieldInfos.map(field => {
        return field.fieldName
      });

      const popupTemplate = {
        content: [{
          type: "text",
          text: "This cluster represents <b>{cluster_count}</b> features."
        },
        {
          type: "media",
          mediaInfos: [{
            title: "311 Reports",
            type: "pie-chart",
            value: {
              fields: fieldNames
            }
          }]
        },
        {
          type: "fields"
        }],
        fieldInfos
      };

      return {
        type: "cluster",
        popupTemplate,
        labelingInfo,
        clusterMinSize,

        fields,
        renderer

      };
    }


    view.ui.add(
      new Home({
        view
      }),
      "top-left"
    );

    view.ui.add(
      new Expand({
        view: view,
        content: new Legend({
          view
        }),
        expandIcon: "list-bullet",
        expanded: true
      }),
      "top-left"
    );



  })();
});

```


上記のように記述したことで冒頭の図になりますが、なんとなくまとまって見えるようになりました。

![スクリーンショット 2024-12-14 2.16.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3942828/f7e3c742-5e59-f0b0-d756-aba37e6ea87c.png)

# 最後に
本当は、小学校区内でのチャートも出力してみたかったですが、ここで時間切れでした。追記で対応するかもしれませんがご容赦ください。
さて、実際にWebサービスとして運用する際に利用するかは別として、最初の一歩としてはかなり簡単に利用することができるかなと思います。ぜひ、初めてのGISアプリケーション開発の際に参考としていただければと思います。