レイヤーを拡張して「Tile Map Service」のサービスを表示

Document created by shinji_katayaesrij-esridist Employee on Oct 29, 2019Last modified by shinji_katayaesrij-esridist Employee on Oct 30, 2019
Version 2Show Document
  • View in full screen mode

はじめに

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:3785) and EPSG:4326 (WGS84):

        TMS , WTMS , Quadkey での座標を表示できるWebアプリあり。また、それを計算しているPythonのコードも閲覧可能。

Attachments

    Outcomes