はじめに
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 のサービス
Tile Map Service でサービスを配信している主なものは、国内では次のようなものが挙げられます。
サンプルとしての実装方法を紹介する記事なのでどのサービスを使ってもよいのですが、最近、できるネットの「この土地には何があった? 古地図と現在の地図を比較できる「今昔マップ on the web」【水害・地震への備えに】」でも取りあげられているように、古い土地の歴史を確認するのに有用な「今昔マップ on the Web」のサービスを例にします。
また、本記事で作成するサンプルアプリケーションには、以下の機能を持たせ、そこに必要となる3つのポイントを解説します。
1) Tile Map Service のサービスをレイヤーとして表示/非表示の切替機能
2) ArcGIS Online の背景地図とスワイプできる機能
なお、本記事のようにAPIでサービス利用をする場合においても、必ずサービス元の利用ポリシーを確認の上、利用ポリシーに従ってご利用ください。

それではポイントを見ていきましょう。
ポイント1:カスタムレイヤー
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);
}
});
ポイント2:独自のプロキシ サービスをホスティングし設定
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>
ポイント3:ウィジェットを活用
ウィジェット を使うのは必須ではありませんが、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>
関連リンク(ArcGIS 関連)
ArcGIS API for JavaScript 4.x のWeb ヘルプ
・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 でのカスタムレイヤーとレイヤービューのテクニカルセッション資料
・独自のWeb サーバーにホストするプロキシサービス:
Proxy files for DotNet, Java and PHP の日本語解説ページ
・ ArcGIS for Developers 開発者アカウント作成方法:
ArcGIS for Developers のアカウントを作成する日本語解説ページ
関連リンク(その他)
・Tiled web map:
OpenLayers でサポートしているOGC標準のタイルサービスについての説明。
・Web Map Tile Service:
TMSより新しいOGC標準のタイルサービスについての説明。
・OpenStreetMap wiki:
OpenStreetMapの仕様(WMTS)等について記載されているサイト。
・Slippy map tilenames - OpenStreetMap Wiki:
OpenStreetMapのファイル命名規則、各種言語( Python, Ruby, JS, C# など)での、緯度、経度、ズームレベルのタイル座標の相互の計算式等が多数記載あり。
・tmcw/xyz_vs_tms.md:
TMSからWTMS のタイルY座標を計算をする式が記載されてmdファイル
・Tiles à la Google Maps: Coordinates, Tile Bounds and Projection - conversion to EPSG:900913 (EPSG:37...
TMS , WTMS , Quadkey での座標を表示できるWebアプリあり。また、それを計算しているPythonのコードも閲覧可能。