この記事は「はじめてのWeb マッピングアプリケーション開発」のシリーズ記事として、はじめて地図アプリ開発を行う開発者の方に向けて、ArcGIS API for JavaScript を使用した Web アプリ開発の流れをシリーズで連載しています。
第 2 回目となる今回は、地図にいくつかのレイヤーを追加し、表示 / 非表示を切り替えをするところを中心に紹介してみたいと思います。
前回の記事と同様にソースコードは ESRIジャパンの GitHub で公開しています(※2021年12月追記:”ファイル名.html” が2018年時のサンプル、”ファイル名_2021.html” が2021年に書き換えしたサンプルです)ので、同時に参照いただきながら、本記事を読んでいただければ、より理解がすすむかと思います。
Web アプリ開発の実行環境をお持ちでない方は、JSBin を使用して、Web ブラウザー上でコードの入力・編集、アプリの実行を試すこともできます。
今回の記事の最終的なアプリケーションは、次のようなイメージになりますが、記事は手順を追って進められるよう、大きく 3 つの構成に分けて記載しています。
まずは基本的な手順を示してステップ 1. ~ 4. をご理解いただき、余裕がある方は、ステップ 5. 以降へ読み進めてください。
【完成版イメージ】
レイヤー(Layer)は最も基本的な地図(Map)の構成要素で、位置情報を表現するグラフィックやイメージのデータの集まりです。
そのうち、FeatureLayer や GraphicsLayer などの ベクトルレイヤー は、それぞれの フィーチャ にジオメトリと属性を持っているのが特徴で、Graphic オブジェクトとして View 内で描画されます。これらのベクトルレイヤーに対してデータをクエリしたり、解析を行ったりする GIS 的な操作を、今後の記事の中でも取りあげていきます!
また、背景地図 としても利用できるレイヤーとして、 TileLayer、WebTileLayer などのクラスもあり、これらも同様に View 内で描画されます。WebTileLayer は ArcGIS 以外にホストされている OGC(Open Geospatial Consortium)の仕様に準拠した WMTS(Web Tile Map Service)のキャッシュ イメージ タイル、例えば国土地理院などのタイルを扱うことができるクラスです。
その他にも、ArcGIS API for JavaScript で 扱うことが可能なレイヤーはまだまだありますので、興味がある方は API リファレンス の esri/layers/Layer クラス をご参照ください。
それでは、まず上記で簡単にご紹介した FeatureLayer と WebTileLayer の 2 種類のレイヤーを表示するまでを行ってみましょう。
地図を表示するまでのステップは、前回の記事のステップ 1. ~ 6. の通りのためここでは省略します。前回の記事ではなくコードを参照したい方は、ESRI ジャパンのGitHub の前回の記事のコード 、(※2021年12月追記)もしくは2021年12月に最新化したコード をご参照ください。
ステップ1でのコードにWebTileLayer と FeatureLayer のインスタンスを作成するコードを追加します。そのためには、esri/layers/WebTileLayer クラス と esri/layers/FeatureLayer クラス の require を定義します。
また、今回のサンプルで WebTileLayer として 国土地理院が提供している地理院タイル を利用しますが、urlTemplate プロパティには、元データでは url に /{z}/{x}/{y}.png として定義されているものを読み換えし、/{level}/{col}/{row}.png のように指定します。
FeatureLayer の url プロパティは、ArcGIS Server、Portal for ArcGIS、ArcGIS Online のいずれかでホストされている url を指定します。
require([ "esri/Map", "esri/views/MapView", "esri/layers/WebTileLayer", "esri/layers/FeatureLayer", "esri/geometry/support/webMercatorUtils", "dojo/on", "dojo/domReady!" ], function( Map, MapView, WebTileLayer, FeatureLayer, webMercatorUtils, on ) { /********************************************************************************** * 省略 **********************************************************************************/ // WebTileLayer:地理院タイル(淡色地図) // 地理院タイルの形式から WebTileLayer への読み換え // 【読み換え前】https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png // 【読み換え後】https://cyberjapandata.gsi.go.jp/xyz/pale/{level}/{col}/{row}.png const gsipaleLyr = new WebTileLayer({ urlTemplate:"https://cyberjapandata.gsi.go.jp/xyz/pale/{level}/{col}/{row}.png", }); // FeatureLayer:愛知県 地価公示:オープンデータ const chikakojiLyr = new FeatureLayer({ url:"http://services3.arcgis.com/iH4Iz7CEdh5xTJYb/arcgis/rest/services/Aichi_ken_Chikakoji_H25/FeatureServer", }); // FeatureLayer:全国市区町村界データ(簡易版): Living Atlas const cityareaLyr = new FeatureLayer({ url:"http://services.arcgis.com/wlVTGRSYTzAbjjiC/arcgis/rest/services/all_Japan_shikuchoson/FeatureServer", }); });
※2021年12月追記:2021年12月以降に試す場合は以下のコードを追加してください。
require([
"esri/Map",
"esri/views/MapView",
"esri/layers/WebTileLayer",
"esri/layers/FeatureLayer"
],
(
Map,
MapView,
WebTileLayer,
FeatureLayer
) => {
/**********************************************************************************
* 省略
**********************************************************************************/
// WebTileLayer:地理院タイル(淡色地図)
// 地理院タイルの形式から WebTileLayer への読み換え
// 【読み換え前】https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png
// 【読み換え後】https://cyberjapandata.gsi.go.jp/xyz/pale/{level}/{col}/{row}.png
const gsipaleLyr = new WebTileLayer({
urlTemplate:"https://cyberjapandata.gsi.go.jp/xyz/pale/{level}/{col}/{row}.png",
});
// FeatureLayer:公示地価(国土数値情報 調査時点:令和3年1月1日): Living Atlas
const chikakojiLyr = new FeatureLayer({
url: "https://services.arcgis.com/wlVTGRSYTzAbjjiC/arcgis/rest/services/LandPrice/FeatureServer/0",
});
// FeatureLayer:全国市区町村界データ 2021: Living Atlas
const cityareaLyr = new FeatureLayer({
url: "https://services.arcgis.com/wlVTGRSYTzAbjjiC/arcgis/rest/services/municipalityboundaries2021/FeatureServer",
});
});
レイヤーに追加のプロパティを設定します。
追加可能なプロパティとしては、id、minScale、maxScale、opacity や visible などがありますが、今回のサンプルでは、WebTileLayer には id、opacity、copyright、visible、FeatureLayer には id、visible、opacity、minScale、maxScale などを設定しています。
// WebTileLayer:地理院タイル(淡色地図) // 地理院タイルの形式から WebTileLayer への読み換え // 【読み換え前】https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png // 【読み換え後】https://cyberjapandata.gsi.go.jp/xyz/pale/{level}/{col}/{row}.png const gsipaleLyr = new WebTileLayer({ urlTemplate:"https://cyberjapandata.gsi.go.jp/xyz/pale/{level}/{col}/{row}.png", id:"gsipale", opacity:0.9, copyright:"地理院タイル(淡色地図)", visible: false }); // FeatureLayer:愛知県 地価公示:オープンデータ const chikakojiLyr = new FeatureLayer({ url:"http://services3.arcgis.com/iH4Iz7CEdh5xTJYb/arcgis/rest/services/Aichi_ken_Chikakoji_H25/FeatureServer", id:"chikakoji" }); // FeatureLayer:全国市区町村界データ(簡易版): Living Atlas const cityareaLyr = new FeatureLayer({ url:"http://services.arcgis.com/wlVTGRSYTzAbjjiC/arcgis/rest/services/all_Japan_shikuchoson/FeatureServer", id:"cityarea", opacity:0.5, minScale:1500000, maxScale:50000 });
※2021年12月追記:2021年12月以降に試す場合は以下のコードを追加してください。
// WebTileLayer:地理院タイル(淡色地図)
// 地理院タイルの形式から WebTileLayer への読み換え
// 【読み換え前】https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png
// 【読み換え後】https://cyberjapandata.gsi.go.jp/xyz/pale/{level}/{col}/{row}.png
const gsipaleLyr = new WebTileLayer({
urlTemplate:"https://cyberjapandata.gsi.go.jp/xyz/pale/{level}/{col}/{row}.png",
id:"gsipale",
opacity:0.9,
copyright:"地理院タイル(淡色地図)",
visible: false
});
// FeatureLayer:公示地価(国土数値情報 調査時点:令和3年1月1日): Living Atlas
const chikakojiLyr = new FeatureLayer({
url: "https://services.arcgis.com/wlVTGRSYTzAbjjiC/arcgis/rest/services/LandPrice/FeatureServer/0",
id: "chikakoji",
// 2021にデータを変更したことに伴いレイヤーが愛知県のみとなるようフィルタ定義を追加
definitionExpression: "L01_021 LIKE '23%'"
});
// FeatureLayer:全国市区町村界データ 2021: Living Atlas
const cityareaLyr = new FeatureLayer({
url: "https://services.arcgis.com/wlVTGRSYTzAbjjiC/arcgis/rest/services/municipalityboundaries2021/FeatureServer",
id: "cityarea",
opacity: 0.5,
minScale: 5000000,//1500000, パフォーマンスが向上しているのでスケール変更
maxScale: 50000,
// 上記と同様にこちらも愛知県のみになるようフィルタ定義を追加
definitionExpression: "JCODE LIKE '23%'"
});
id :レイヤーの一意識別子で、アプリケーションの別の部分で参照できるようにするために設定します。開発者が直接指定しない場合は、レイヤーのインスタンスが作成されたときに自動的に生成されます。
minScale と maxScale : さまざまな縮尺でのレイヤーの表示状態をコントロールするために設定し、これを設定することにより、あるスケールでのアプリのパフォーマンスを改善することができます。
opacity : レイヤーの透過状態のプロパティで、他のレイヤーと重ね合わせたときに透過の方が見やすい場合などに設定します。
visible : レイヤーの表示状態を表し、デフォルトでは true が設定されます。今回のサンプルでは、初期表示で非表示にしたい WebTileLayer の visible を false に設定しています。
definitionExpression: (2021年12月追記) 利用データの変更に伴い、以前のサンプルと同じような挙動になるよう、愛知県のみとなるようフィルタ定義を追加しています。
map のインスタンスにレイヤーを追加する方法はいくつかあります。
API リファレンスの Map.layers に詳細な解説がありますが、今回のサンプルでは2つの異なる方法で追加しています。
1つ目の方法として、Map のコンストラクターの中で、レイヤーを追加する方法です。
const map = new Map({ basemap:"streets", layers:[gsipaleLyr] });
もう一つの方法は、map.add() を使って追加する方法です。
map.add(chikakojiLyr); map.add(cityareaLyr);
上記の2つの方法を通じて、gsipaleLyr は非表示状態、chikakojiLyr と cityareaLyr のレイヤーは表示状態で、View に追加されます。
※2021年12月追記:2021年12月以降に試す場合は以下のコードを追加してください。
const map = new Map({
basemap: "streets",
layers:[chikakojiLyr]
});
map.add(cityareaLyr, 0);
map.add(gsipaleLyr, 0);
ここまでのステップの段階で、地図を表示してみると次のようになります。
【途中段階イメージ】
まだ、世界地図の中に小さくレイヤーが表示されるだけなので、さらに完成版を目指す方は次に進んでください。
1. で定義してある "viewDiv" の <div> 要素の下に、HTML の <input> タグで type 属性を "checkbox" としたものを 3 つ追加します。
3 つのうち 1 つはチェックオフ状態、2 つはチェックオンの状態です。この要素の説明は後で説明します。
<body> <div id="viewDiv"></div> <span id="layerToggle"> <input type="checkbox" id="gsipaleLyr" />地理院タイル-淡色地図(WebTileLayer)<br> <input type="checkbox" id="chikakojiLyr" checked />愛知県の公示地価(FeatureLayer) <input type="checkbox" id="cityareaLyr" checked />全国市区町村界(FeatureLayer) </span> </body>
レイヤーの表示状態の制御を行う最初の段階として、<input>タグの "checkbox" での change event をリッスンするため、dojo/on モジュール の require が必要となります(※2021年12月追記:バージョン4.16 以降、API はDojo に依存しなくなったので、addEventListener でchange event をリッスンするようサンプルを更新しています)。
document.getElementById で取得した要素に on で操作するコードを追加します。
今回は、チェックボックスをオンしたときにレイヤーが表示状態になり、オフにしたときに非表示状態になるコードを記載しています。
require([ "esri/Map", "esri/views/MapView", "esri/layers/WebTileLayer", "esri/layers/FeatureLayer", "esri/geometry/support/webMercatorUtils", "dojo/on", "dojo/domReady!" ], function( Map, MapView, WebTileLayer, FeatureLayer, webMercatorUtils, on ) { /***************************************************************** * ~省略~ *******************************************************************/ const gsipaleToggle = document.getElementById("gsipaleLyr"); const chikakojiToggle = document.getElementById("chikakojiLyr"); const cityareaToggle = document.getElementById("cityareaLyr"); /********************************************************************************** * レイヤーの visible プロパティで view 内でのレイヤーの表示/非表示を切り替えを行う * なお、レイヤーが非表示状態であっても、そのレイヤーのプロパティにアクセスしたり、 * そのレイヤーに対して解析を実行することは可能 **********************************************************************************/ on(gsipaleToggle, "change", function() { gsipaleLyr.visible = gsipaleToggle.checked; }); on(chikakojiToggle, "change", function() { chikakojiLyr.visible = chikakojiToggle.checked; }); on(cityareaToggle, "change", function() { cityareaLyr.visible = cityareaToggle.checked; }); });
※2021年12月追記:2021年12月以降に試す場合は以下のコードを追加してください。
/*****************************************************************
* ~省略~
*******************************************************************/
const gsipaleToggle = document.getElementById("gsipaleLyr");
const chikakojiToggle = document.getElementById("chikakojiLyr");
const cityareaToggle = document.getElementById("cityareaLyr");
/**********************************************************************************
* レイヤーの visible プロパティで view 内でのレイヤーの表示/非表示を切り替えを行う
* なお、レイヤーが非表示状態であっても、そのレイヤーのプロパティにアクセスしたり、
* そのレイヤーに対して解析を実行することは可能
**********************************************************************************/
gsipaleToggle.addEventListener("change", () => {
gsipaleLyr.visible = gsipaleToggle.checked;
});
chikakojiToggle.addEventListener("change", () => {
chikakojiLyr.visible = chikakojiToggle.checked;
});
cityareaToggle.addEventListener("change", () => {
cityareaLyr.visible = cityareaToggle.checked;
});
なお、レイヤーは view 内で非表示状態にあっても、map の一部として存在しています。そのため、レイヤーのすべてのプロパティにアクセスすることは可能であり、レイヤーで表示状態に関わらず解析に利用することができます。
レイヤー オブジェクトは、サービスとして公開された位置と属性データを管理しますが、View 内の描画の処理は行いません。描画の処理を行うのが LayerView の役割です。
LayerView は view 内で描画される直前に作成されます。FeatureLayer を操作する場合、対応する FeatureLayerView は、そのレイヤーの機能に関連する view 内で描画されるグラフィックスへのアクセスを開発者に提供します。
このステップでは、view の ”layerview-create” イベントをリッスンし、コンソール内でそれらのプロパティを確認できるよう print 文で出力しています。必要なレイヤーを取得するために、ステップ 3 で作成したレイヤーの id を利用しています。
require([ "esri/Map", "esri/views/MapView", "esri/layers/WebTileLayer", "esri/layers/FeatureLayer", "esri/geometry/support/webMercatorUtils", "dojo/on", "dojo/domReady!" ], function( Map, MapView, WebTileLayer, FeatureLayer, webMercatorUtils, on ) { /***************************************************************** * ~省略~ *******************************************************************/ /********************************************************************************** * view の ”layerview-create” イベントで * コンソール内でそれらのプロパティを確認できるよう print 文で出力 **********************************************************************************/ view.on("layerview-create", function(event) { if (event.layer.id === "gsipale") { // gsipaleLyr view のプロパティを確認できる console.log("地理院タイル(淡色地図)の LayerView が作成されました!", event.layerView); } if (event.layer.id === "chikakoji"){ // chikakojiLyr view のプロパティを確認できる console.log("地価公示 の LayerView が作成されました!", event.layerView); } if (event.layer.id === "cityarea"){ // cityLyr view のプロパティを確認できる console.log("全国市区町村界データ の LayerView が作成されました!", event.layerView); } }); });
レイヤーはロードされたとき、またはすべてのプロパティが開発者に利用可能になったとき、layer.when( 'callback' ) で指定されているコールバック関数が実行されます。
このサンプルでは、MapView の初期表示で地図を表示した後、chikakojiLyr の fullExtent へズームしてアニメーション表示で移動するようなコードが記載されています。
ここで、layer.when() を用いているのは、レイヤーのロードが完了するまでレイヤーの fullExtent を取得することはできないため、layer.when( ’callback’ ) のコールバック関数内で表示範囲を移動する view.goTo() を記載しています。
また、このサンプルで利用している FeatureLayer は緯度経度座標系のため、初期表示の Extent の範囲を背景地図座標系で使われている Web メルカトル図法に投影したのち、表示範囲を移動しています。JavaScript API では Web メルカトル図法への投影変換を行うための esri/geometry/support/webMercatorUtils がJavaScript のクラスとして追加されています。
なお、地理座標系と投影座標系の違いなど、座標系に関する基礎的な内容は、弊社ホームページの GIS 基礎解説をご参照ください。
require([ "esri/Map", "esri/views/MapView", "esri/layers/WebTileLayer", "esri/layers/FeatureLayer", "esri/geometry/support/webMercatorUtils", "dojo/on", "dojo/domReady!" ], function( Map, MapView, WebTileLayer, FeatureLayer, webMercatorUtils, on ) { /***************************************************************** * ~省略~ *******************************************************************/ /********************************************************************************** * ロードされた時 もしくは すべてのプロパティにアクセスできるようになったとき * layer.when('callback') で指定されるコールバック関数が実行される * * 地価公示レイヤーがロードされると、view が初期表示範囲として * レイヤーの表示範囲にズームしながら移動していく処理 **********************************************************************************/ view.when(function() { chikakojiLyr.when(function() { // 今回の FeauterLayer は緯度経度座標なので WebMercatorUtil を使って Webメルカトル座標へ変換 // またデータの範囲が狭いので expand で Extent を拡張している const ext = webMercatorUtils.geographicToWebMercator(chikakojiLyr.fullExtent.expand(2.0)); view.goTo(ext); }); }); });
※2021年12月追記:2021年12月以降は、公示地価のFeatureLayer を変更したため、はdefinitionExpression プロパティで愛知県をフィルタ定義("L01_021 LIKE '23%'" )して、レイヤーを読み込みしています。 そのため、chikakojiLyr.queryExtent() の結果を、view.goTo を使って移動するように書き換えしています。
/**********************************************************************************
* ロードされた時 もしくは すべてのプロパティにアクセスできるようになったとき
* layer.when('callback') で指定されるコールバック関数が実行される
*
* 地価公示レイヤーがロードされると、view が初期表示範囲として
* レイヤーの表示範囲にズームしながら移動していく処理
**********************************************************************************/
view.when(() => {
chikakojiLyr.when(() => {
return chikakojiLyr.queryExtent();
}).then((response) => {
// goTo のアニメーションの動きを調整するため、GoToOptions2D のduration にデフォルトの10倍の値を設定
// https://developers.arcgis.com/javascript/latest/api-reference/esri-views-MapView.html#methods-summary
view.goTo(response.extent, {"duration":2000});
});
});
Layer オブジェクトには、ここで取り上げていない多くのプロパティがあります。特定のレイヤーに関連するその他のプロパティの詳細については、APIドキュメントおよびサンプルを参照してください。
今回作成したサンプルは以下で動作を確認できます。
※2021年12月追記:2021年12月以降は、以下で動作を確認できます。
次回はさまざまなレンダリングで、レイヤーを表現する方法をご紹介します。
今回の記事では、Esri が提供しているオリジナルの 「Intro to layers」のサンプル において、宣言として "var" を利用していた部分を "const" を使って書き換えています。また、今回、ステップ 2.で記載している esri/layers/FeatureLayer クラス の API リファレンス内でも、次のように宣言として "const" が使われています。
require(["esri/layers/FeatureLayer"], function(FeatureLayer){ // points to the states layer in a service storing U.S. census data const fl = new FeatureLayer({ url: "https://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/3" }); map.add(fl); // adds the layer to the map });
ES2015(ES6)では "let"、"const" が宣言文として追加され、現段階ではほとんどの最新ブラウザーは対応しており、Quiita 等でもたくさん書かれている方がいらっしゃいます(例:「var/let/constの使い分け」)。
ArcGIS API for JavsScript の リファレンスでも、現段階では混在した状態ですので、ご利用いただく際にはご注意ください。