ArcGIS API for JavaScript では、たくさんの定義済みのレイヤーを提供しています。これらのレイヤーは、サーバーから画像またはデータを取得し、ビューに表示するために利用します。
上記でサポートされていないレイヤーをArcGIS API for JavaScript で扱う方法がヘルプの Introduction to layer extensibility に記載されおり、カスタムレイヤーを作成して対応することが可能です。
今回は、ArcGIS API for JavaScript では現在サポートしていない OGCの Tile Map Service(TMS)を対象に、カスタムレイヤーを作成して対応する方法を紹介したいと思います。
Tips !
弊社製品に詳しい方は、WebTileLayer クラスで対応できるのでは?と思った方がいらっしゃるかもしれませんが、WebTileLayer は原点が左上(北西)、Tile Map Service は原点が左下(南西)というところが大きく異なっています。
Tile Map Service でサービスを配信している主なものは、国内では次のようなものが挙げられます。
防災科学技術研究所 - 水害地形分類図デジタルアーカイブ(故大矢雅彦早稲田大学名誉教授)
[水害地形分類図デジタルアーカイブ] > [データ利用方法] > [画像データおよびAPIの利用に関する技術情報]
今昔マップ on the Web
農研機構のタイル地図キャッシュサービス
[Finds.jp] > [タイル地図キャッシュサービス] > [レイヤー一覧]
サンプルとしての実装方法を紹介する記事なのでどのサービスを使ってもよいのですが、最近、できるネットの「この土地には何があった? 古地図と現在の地図を比較できる「今昔マップ on the web」【水害・地震への備えに】」でも取りあげられているように、古い土地の歴史を確認するのに有用な「今昔マップ on the Web」のサービスを例にします。
また、本記事で作成するサンプルアプリケーションには、以下の機能を持たせ、そこに必要となる3つのポイントを解説します。
1) Tile Map Service のサービスをレイヤーとして表示/非表示の切替機能
2) ArcGIS Online の背景地図とスワイプできる機能
なお、本記事のようにAPIでサービス利用をする場合においても、必ずサービス元の利用ポリシーを確認の上、利用ポリシーに従ってご利用ください。
それではポイントを見ていきましょう。
Introduction to layer extensibility の Request tiles as they are predefined from a data source のコードを参考に、カスタムレイヤー(今回はTMSLayerとした)を WebTileLayer.createSubclass で定義し、getTileUrl() メソッドをオーバーライドして、y座標を入れ替えします。
// ******************************************************* // ArcGIS API for JavaScript 4.x での TMSLayer の実装例 // TMSLayer を WebTileLayer のサブクラスとして実装し、 // getTileUrl() メソッドをオーバーライド // ******************************************************* var TMSLayer = WebTileLayer.createSubclass({ getTileUrl: function(level, row, col){ //var y = Math.pow(2, level) - row - 1; var y = (1 << level) - row - 1;; // シフト演算の方を採用 return this.urlTemplate.replace("{level}", level).replace("{col}", col).replace("{row}", y); } });
Tips !
maps.nyc.gov に同様の実装例があります。
// ******************************************************* // 参考: maps.nyc.gov でのTMSLayer の実装例 // https://maps.nyc.gov/tiles/#arcgis4-xyz // ******************************************************* var TMSLayer = WebTileLayer.createSubclass({ getTileUrl: function(level, row, col){ var supermethod = this.getInherited(arguments); var y = Math.pow(2, level) - row - 1; return supermethod.call(this, level, y, col); } });
ArcGIS API for JavaScript 4.9 から、CORS(Cross-Origin Resource Sharing) をサポートしていることをデフォルトとして扱うようになったため、CORS の設定をしていない今回のようなサーバーを使う場合は、独自のプロキシ サービスをIIS 等のWeb サーバーに設定しておく必要があります。
独自のプロキシ サービスの設定方法は、ArcGIS for Devlopers 開発リソース集の、独自の Web サーバーにホストするプロキシ サービス のページをご参照ください。
// ******************************************************* // JavaScript API 4.9 から CORS をサポートしているのをデフォルトとして扱うようになったので // CORS の設定をしていないサーバーを使う場合は、独自のプロキシ サービスを自分で設定しておく // 参考:独自の Web サーバーにホストするプロキシ サービス // http://esrijapan.github.io/arcgis-dev-resources/proxy-services/ // ******************************************************* // 今回、設置した独自のプロキシ サービスは.NET 版 // サーバー側でのTLS 1.2 が未対応だったため、「独自の Web サーバーにホストするプロキシ サービス」に記載の回避コードを追記済み urlUtils.addProxyRule({ urlPrefix: "sv53.wadax.ne.jp", //ktgis.net,www.finds.jp proxyUrl: "https://yourhostserver/DotNet/proxy.ashx" });
なお、プロキシ サービス 設定用の proxy.config のXML ファイルには次のように設定します。
<?xml version="1.0" encoding="utf-8" ?> <ProxyConfig allowedReferers="*" mustMatch="true"> <serverUrls> <!-- <serverUrl url="http://services.arcgisonline.com" matchAll="true"/>--> <!--今昔マップ--> <serverUrl url="https://sv53.wadax.ne.jp" matchAll="true"/> </serverUrls> </ProxyConfig>
ウィジェット を使うのは必須ではありませんが、LayerList (API 4.2から), Expand(API 4.3から), Swipe(API 4.13から) のwidget を上手く活用すると、たった数行のコードで
1) Tile Map Service のサービスのレイヤーの表示/非表示の切替機能
2) ArcGIS Online の背景地図とスワイプできる機能
を実装することができます。
// ウィジェット活用1: // 新規のLayerList widget を作成 var layerList = new LayerList({ view: view, }); // 新規のExpand widget を作成 var llExpand = new Expand({ view: view, content: layerList, expanded: false }); // widget を ui の右上へ追加 view.ui.add(llExpand, "top-right"); // ウィジェット活用2: // 新規の Swipe widget を作成 var swipe = new Swipe({ //leadingLayers: [layername],// 今回は未使用 trailingLayers: [tmsLayer,tmsLayer1,tmsLayer2,tmsLayer3,tmsLayer4,tmsLayer5,tmsLayer6,tmsLayer7,tmsLayer8], position: 50, // widget の位置を50%に設定 view: view }); // widget を ui に追加 view.ui.add(swipe);
最後に完成版のコードは次のようになりますが、いつものようにGitHub上でも公開しておきます。
ArcGIS for Developers のアカウント※をお持ちであれば、独自のプロキシ サービスをIIS 等のWeb サーバーにホスティングし、そのホスティング環境の設定を完成版のコードへ適用することで、ご自身の環境で試すことが出来ると思いますので、お試しいただければと思います。
※ 今回のコードを試す用途であれば、ArcGIS Developer Subscription の無償のEssentials でも可能
なお、上記に記載したように、APIでサービス利用をする場合においても、必ずサービス元の利用ポリシーを確認の上、利用ポリシーに従ってご利用ください。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no"> <title>Custom TileLayer - 4.13</title> <link rel="stylesheet" href="https://js.arcgis.com/4.13/esri/themes/dark/main.css" /> <script src="https://js.arcgis.com/4.13/"></script> <style> html, body, #viewDiv { padding: 0; margin: 0; height: 100%; width: 100%; } </style> <script> require([ "esri/Map", "esri/config", "esri/request", "esri/Color", "esri/views/MapView", "esri/widgets/LayerList", "esri/widgets/Swipe", "esri/widgets/Expand", "esri/layers/WebTileLayer", "esri/core/urlUtils", "dojo/domReady!" ], function( Map, esriConfig, esriRequest, Color, MapView, LayerList, Swipe, Expand, WebTileLayer, urlUtils ) { // ******************************************************* // ArcGIS API for JavaScript 4.x での TMSLayer の実装例 // TMSLayer を WebTileLayer のサブクラスとして実装し、 // getTileUrl() メソッドをオーバーライド // ******************************************************* var TMSLayer = WebTileLayer.createSubclass({ getTileUrl: function(level, row, col){ //var y = Math.pow(2, level) - row - 1; var y = (1 << level) - row - 1; // シフト演算の方を採用 return this.urlTemplate.replace("{level}", level).replace("{col}", col).replace("{row}", y); } }); // ******************************************************* // 参考: maps.nyc.gov でのTMSLayer の実装例 // https://maps.nyc.gov/tiles/#arcgis4-xyz // ******************************************************* /* var TMSLayer = WebTileLayer.createSubclass({ getTileUrl: function(level, row, col){ var supermethod = this.getInherited(arguments); var y = Math.pow(2, level) - row - 1; return supermethod.call(this, level, y, col); } }); */ // ******************************************************* // JavaScript API 4.9 から CORS をサポートしているのをデフォルトとして扱うようになったので // CORS の設定をしていないサーバーを使う場合は、独自のプロキシ サービスを自分で設定しておく // 参考:独自の Web サーバーにホストするプロキシ サービス // http://esrijapan.github.io/arcgis-dev-resources/proxy-services/ // ******************************************************* // 今回、設置した独自のプロキシ サービスは.NET 版 // サーバー側でのTLS 1.2 が未対応だったため、「独自の Web サーバーにホストするプロキシ サービス」に記載の回避コードを追記済み urlUtils.addProxyRule({ urlPrefix: "sv53.wadax.ne.jp", //ktgis.net,www.finds.jp proxyUrl: "https://yourhostserver/DotNet/proxy.ashx" }); // ******************************************************* // JavaScript application の開始 // ******************************************************* // TMSLayer のインスタンスを作成し、プロパティを設定 var tmsLayer = new TMSLayer({ urlTemplate: "https://sv53.wadax.ne.jp/~ktgis-net/kjmapw/kjtilemap/tokyo50/2man/{level}/{col}/{row}.png", opacity: 0.6, title: "今昔マップ-首都圏編(1896-1909年)" }); var tmsLayer1 = new TMSLayer({ urlTemplate: "https://sv53.wadax.ne.jp/~ktgis-net/kjmapw/kjtilemap/tokyo50/00/{level}/{col}/{row}.png", opacity: 0.6, title: "今昔マップ-首都圏編(1917-1924年)", visible: false }); var tmsLayer2 = new TMSLayer({ urlTemplate: "https://sv53.wadax.ne.jp/~ktgis-net/kjmapw/kjtilemap/tokyo50/01/{level}/{col}/{row}.png", opacity: 0.6, title: "今昔マップ-首都圏編(1927-1939年)", visible: false }); var tmsLayer3 = new TMSLayer({ urlTemplate: "https://sv53.wadax.ne.jp/~ktgis-net/kjmapw/kjtilemap/tokyo50/02/{level}/{col}/{row}.png", opacity: 0.6, title: "今昔マップ-首都圏編(1944-1954年)", visible: false }); var tmsLayer4 = new TMSLayer({ urlTemplate: "https://sv53.wadax.ne.jp/~ktgis-net/kjmapw/kjtilemap/tokyo50/03/{level}/{col}/{row}.png", opacity: 0.6, title: "今昔マップ-首都圏編(1965-1968年)", visible: false }); var tmsLayer5 = new TMSLayer({ urlTemplate: "https://sv53.wadax.ne.jp/~ktgis-net/kjmapw/kjtilemap/tokyo50/04/{level}/{col}/{row}.png", opacity: 0.6, title: "今昔マップ-首都圏編(1975-1978年)", visible: false }); var tmsLayer6 = new TMSLayer({ urlTemplate: "https://sv53.wadax.ne.jp/~ktgis-net/kjmapw/kjtilemap/tokyo50/05/{level}/{col}/{row}.png", opacity: 0.6, title: "今昔マップ-首都圏編(1983-1987年)", visible: false }); var tmsLayer7 = new TMSLayer({ urlTemplate: "https://sv53.wadax.ne.jp/~ktgis-net/kjmapw/kjtilemap/tokyo50/06/{level}/{col}/{row}.png", opacity: 0.6, title: "今昔マップ-首都圏編(1992-1995年)", visible: false }); var tmsLayer8 = new TMSLayer({ urlTemplate: "https://sv53.wadax.ne.jp/~ktgis-net/kjmapw/kjtilemap/tokyo50/07/{level}/{col}/{row}.png", opacity: 0.6, title: "今昔マップ-首都圏編(1998-2005年)", visible: false }); // TMSLayer のインスタンスをマップに追加 var map = new Map({ layers:[tmsLayer,tmsLayer1,tmsLayer2,tmsLayer3,tmsLayer4,tmsLayer5,tmsLayer6,tmsLayer7,tmsLayer8], basemap: "topo" }); // map を MapView に追加 var view = new MapView({ container: "viewDiv", map: map, center: [139.715512, 35.678257], zoom: 15 }); // 新規のLayerList widget を作成 var layerList = new LayerList({ view: view, }); // 新規のExpand widget を作成 var llExpand = new Expand({ view: view, content: layerList, expanded: false }); // widget を ui の右上へ追加 view.ui.add(llExpand, "top-right"); // 新規の Swipe widget を作成 var swipe = new Swipe({ //leadingLayers: [layername],// 今回は未使用 trailingLayers: [tmsLayer,tmsLayer1,tmsLayer2,tmsLayer3,tmsLayer4,tmsLayer5,tmsLayer6,tmsLayer7,tmsLayer8], position: 50, // widget の位置を50%に設定 view: view }); // widget を ui に追加 view.ui.add(swipe); }); </script> </head> <body> <div id="viewDiv"></div> </body> </html>
・Introduction to layer extensibility:
ArcGIS API for JavaScript 4.x でのカスタムレイヤーについての説明
・ArcGIS API for Javascript: Creating Custom Layers and Layer Views(pdf):
Esri Developer Summit 2019 でのカスタムレイヤーとレイヤービューのテクニカルセッション資料
Proxy files for DotNet, Java and PHP の日本語解説ページ
・ ArcGIS for Developers 開発者アカウント作成方法:
ArcGIS for Developers のアカウントを作成する日本語解説ページ
OpenLayers でサポートしているOGC標準のタイルサービスについての説明。
TMSより新しいOGC標準のタイルサービスについての説明。
OpenStreetMapの仕様(WMTS)等について記載されているサイト。
・Slippy map tilenames - OpenStreetMap Wiki:
OpenStreetMapのファイル命名規則、各種言語( Python, Ruby, JS, C# など)での、緯度、経度、ズームレベルのタイル座標の相互の計算式等が多数記載あり。
TMSからWTMS のタイルY座標を計算をする式が記載されてmdファイル
TMS , WTMS , Quadkey での座標を表示できるWebアプリあり。また、それを計算しているPythonのコードも閲覧可能。