GIS奮闘記

元GISエンジニアの技術紹介ブログ。主にPythonを使用。

スポンサーリンク

ArcGIS API for JavaScript でグラフィックを使ってジオメトリを作図してみよう

さて、本日も ArcGIS API for JavaScript について書いてみようと思います。今回はグラフィックを使ってジオメトリを作図してみようと思います。ただ、単純にグラフィックを使ってジオメトリを作図するだけなので、属性を付与したり、作図したジオメトリを保存したりはしません。一時作図のようなイメージでしょうか。

今回使用するソース

前回のエントリーをベースに今回使用する処理を書き足そうと思います。

www.gis-py.com

完成イメージ

作図ボタンを追加しました。

f:id:sanvarie:20191020104832p:plain

作図ボタンを押すとこのようにセレクトボックスが表示されます。作図するジオメトリの種類を選んで実行ボタンを押すと作図ができるようになります。

f:id:sanvarie:20191020104959p:plain

実行環境

ArcGIS API for JavaScript 4.13
Chrome

ポイントとなる処理

簡単にではありますが、以下の実装について解説していきます。

1.Draw クラスとGraphic クラスを使用
2.実行ボタン押下時の処理
3.ポイントの作図
4.ラインの作図
5.ポリゴンの作図
6.テキストの作図

Draw クラスとGraphic クラスを使用

今回の要件を満たすには Draw クラスとGraphic クラスを使用する必要があります。

1.Draw クラス
グラフィックを作図するためのクラスです。

var draw = new Draw({
  view: view
});

2.Graphic クラス
グラフィックオブジェクトを作成するためのクラスです。

var graphic = new Graphic({
  geometry: point,
  symbol: {
    type: "simple-marker",
    style: "square",
    color: "red",
    size: "16px",
    outline: { 
      color: [255, 255, 0],
      width: 3
    }
  }
});

実行ボタン押下時の処理

セレクトボックスの選択肢によって以下のように分岐するようにしました。

var sketchToggle= document.getElementById("executeSketch"); // 実行ボタン
on(sketchToggle, "click", function() {
  var geometryType = document.getElementById("geometryType").value;
    
  if(geometryType == "ポイント"){
    enableCreatePoint();
  } else if (geometryType == "ライン"){
    enableCreateLine();
  } else if (geometryType == "ポリゴン"){
    enableCreatePolygon();
  } else if (geometryType == "テキスト"){
    enableCreateText();
  }
});

ポイントの作図

draw.create("point") でポイントを作図することができます。そして、最後に view.graphics.add(graphic); で作図したポイントをマップビューに追加します。

function enableCreatePoint() {
  var action = draw.create("point");
  action.on("draw-complete", function (evt) {
    createPointGraphic(evt.coordinates);
  });
}

function createPointGraphic(coordinates){
  var point = {
    type: "point", 
    x: coordinates[0],
    y: coordinates[1],
    spatialReference: view.spatialReference
  };

  var graphic = new Graphic({
    geometry: point,
    symbol: {
      type: "simple-marker",
      style: "square",
      color: "red",
      size: "16px",
      outline: { 
        color: [255, 255, 0],
        width: 3
      }
    }
  });
  view.graphics.add(graphic);
}

ラインの作図

ポイントの作図と基本は同じです。ただ、ラインなのでクリックするごとにラインのセグメントを作成する必要があります。今回は実装上の都合上、ラインはマップ上に一つしか作図できません(二個目を作図しようとすると一個目は削除される)。

function enableCreateLine() {
  var action = draw.create("polyline");
    view.focus();
    action.on(
      [
        "vertex-add", // Fキー押下で頂点追加
        "vertex-remove", // Zキー押下で頂点削除
        "cursor-update",
        "draw-complete"
      ],
      createPolylineGraphic
   );
}

function createPolylineGraphic(event) {
  var vertices = event.vertices;
  view.graphics.removeAll();

  var graphic = new Graphic({
    geometry: {
      type: "polyline",
      paths: vertices,
      spatialReference: view.spatialReference
    },
    symbol: {
      type: "simple-line",
      color: "red",
      width: 4,
      cap: "round",
      join: "round"
    }
  });

  view.graphics.add(graphic);
}

ポリゴンの作図

ラインの作図とほぼ同じです。なので、ここは共通化した方がいいかもしれないですね。こちらも実装上の都合でマップ上に一つのポリゴンのみが作図可能です。

function enableCreatePolygon() {
  var action = draw.create("polygon");
    view.focus();
    action.on(
      [
        "vertex-add", // Fキー押下で頂点追加
        "vertex-remove", // Zキー押下で頂点削除
        "cursor-update",
        "draw-complete"
      ],
      createPolygonGraphic
   );
}

function createPolygonGraphic(event) {
  var vertices = event.vertices;
  view.graphics.removeAll();

  var graphic = new Graphic({
    geometry: {
      type: "polygon",
      rings: vertices,
      spatialReference: view.spatialReference
    },
    symbol: {
      type: "simple-fill", 
      color: "red",
      style: "solid",
      outline: {  
        color: [255, 255, 0],
        width: 3
      }
    }
  });

  view.graphics.add(graphic);
}

テキストの作図

基本はポイントの作図と同じです。今回はテキストの中身は「GIS奮闘記」固定にしましたが、テキストボックスを作ってそこの値をとってくるようにすれば完璧かと思います。

function enableCreateText() {
  var action = draw.create("point");
  action.on("draw-complete", function (evt) {
    createTextGraphic(evt.coordinates);
  });
}

function createTextGraphic(coordinates){
  var point = {
    type: "point", 
    x: coordinates[0],
    y: coordinates[1],
    spatialReference: view.spatialReference
  };

  var textSymbol = {
    type: "text", 
    color: "red",
    haloColor: "black",
    haloSize: "1px",
    text: "GIS奮闘記",
    xoffset: 3,
    yoffset: 3,
    font: { 
      size: 20,
      weight: "bold"
    }
  };

  var graphic = new Graphic({
    geometry: point,
    symbol: textSymbol
  });

  view.graphics.add(graphic);
}

サンプルコード

以下に全コードを記載します。

  
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
  <title>レイヤ一覧表示</title>
  <link rel="stylesheet" href="https://js.arcgis.com/4.12/esri/css/main.css">
  <script src="https://js.arcgis.com/4.13/"></script>
  <style>
    html,
    body,
    #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }
    #layerToggle,#sketchToggle{
      display: none;
      top: 80px;
      right: 20px;
      position: absolute;
      z-index: 99;
      background-color: white;
      border-radius: 8px;
      padding: 10px;
      opacity: 0.75;
    }
    #menu{
      padding: 0;
      margin: 0;
      height:42px; 
      background-color:#eeeeee;
      font-weight:bold;
    }
    #menu li {
      height:42px;
      margin-right: 2px;
      display: inline-block;
    }
    #button,#buttonElevation,#buttonSketch,#geometryType {
      left:0px;
      width:164px; 
      height:42px;
    }
    #executeSketch {
      left:0px;
      width:82px; 
      height:42px;
    }

  </style>
  <script>
    require([
        "esri/Map",
        "esri/views/MapView",
        "esri/layers/VectorTileLayer",
        "esri/layers/FeatureLayer",
        "esri/layers/MapImageLayer",
        "esri/Graphic",
        "esri/views/draw/Draw",
        "dojo/on",
        "dojo/domReady!"
      ],
      function(
        Map, 
        MapView, 
        VectorTileLayer,
        FeatureLayer, 
        MapImageLayer,
        Graphic,
        Draw,
        on
      ) {
        
        var map = new Map({
            basemap: "gray-vector"
        });

        var vectorTileLayer = new VectorTileLayer({
          url:
            "https://www.arcgis.com/sharing/rest/content/items/92c551c9f07b4147846aae273e822714/resources/styles/root.json",
            id:"vectorTile"
        });
        
        var mapImageLayer = new MapImageLayer({
            url: "https://content.esrij.com/arcgis/rest/services/Dosyasaigai/Dosyasaigai_Tile/MapServer",
            id:"mapImage"
        });

        var boundary = new FeatureLayer({
          url:"https://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/JPN_Boundaries_ECM/FeatureServer",
          id:"boundary",
          opacity:0.5,
          minScale:1500000,
          maxScale:50000
        });

        map.add(vectorTileLayer);
        map.add(mapImageLayer);
        map.add(boundary);

        var view = new MapView({  
          container: "viewDiv",
          map: map,
          center: [139.740286, 35.678601],
          zoom: 15
        });

        var vectorTileToggle = document.getElementById("vectorTileLayer");
        var mapImageToggle = document.getElementById("mapImageLayer");
        var boundaryToggle = document.getElementById("boundary");

        on(vectorTileToggle, "change", function() {
          vectorTileLayer.visible = vectorTileToggle.checked;
        });
        on(mapImageToggle, "change", function() {
            mapImageLayer.visible = mapImageToggle.checked;
        });
        on(boundaryToggle, "change", function() {
          boundary.visible = boundaryToggle.checked;
        });

        view.on("click", function (event) {  
          if(elevation==1){ 

            var data = { 'lon': event.mapPoint.longitude , 'lat': event.mapPoint.latitude  };
            var querystring = encodeQueryData(data);            
            var url = "http://cyberjapandata2.gsi.go.jp/general/dem/scripts/getelevation.php?" + querystring + "&outtype=JSON"

            // リクエストなげる
            var request = new XMLHttpRequest();
            request.open('GET', url);
            request.onreadystatechange = function () {
                var result = JSON.parse(request.responseText);
                view.popup.open({
                  title: result.elevation,
                  content : result.elevation,
                  location: event.mapPoint 
                });
            };
            request.send(null);
          }
        }); 

        function encodeQueryData(data) {
          let ret = [];
          for (let d in data)
            ret.push(encodeURIComponent(d) + '=' + encodeURIComponent(data[d]));
          return ret.join('&');
        }

        var sketchToggle= document.getElementById("executeSketch");
        on(sketchToggle, "click", function() {
          var geometryType = document.getElementById("geometryType").value;
            
          if(geometryType == "ポイント"){
            enableCreatePoint();
          } else if (geometryType == "ライン"){
            enableCreateLine();
          } else if (geometryType == "ポリゴン"){
            enableCreatePolygon();
          } else if (geometryType == "テキスト"){
            enableCreateText();
          }
        });

        var draw = new Draw({
          view: view
        });

        // ポイント作図
        function enableCreatePoint() {
          var action = draw.create("point");
          action.on("draw-complete", function (evt) {
            createPointGraphic(evt.coordinates);
          });
        }

        function createPointGraphic(coordinates){
          var point = {
            type: "point", 
            x: coordinates[0],
            y: coordinates[1],
            spatialReference: view.spatialReference
          };

          var graphic = new Graphic({
            geometry: point,
            symbol: {
              type: "simple-marker",
              style: "square",
              color: "red",
              size: "16px",
              outline: { 
                color: [255, 255, 0],
                width: 3
              }
            }
          });
          view.graphics.add(graphic);
        }

        // ライン作図
        function enableCreateLine() {
          var action = draw.create("polyline");
            view.focus();
            action.on(
              [
                "vertex-add", // Fキー押下で頂点追加
                "vertex-remove", // Zキー押下で頂点削除
                "cursor-update",
                "draw-complete"
              ],
              createPolylineGraphic
           );
        }

        function createPolylineGraphic(event) {
          var vertices = event.vertices;
          view.graphics.removeAll();

          var graphic = new Graphic({
            geometry: {
              type: "polyline",
              paths: vertices,
              spatialReference: view.spatialReference
            },
            symbol: {
              type: "simple-line",
              color: "red",
              width: 4,
              cap: "round",
              join: "round"
            }
          });

          view.graphics.add(graphic);
        }

        // ポリゴン作図
        function enableCreatePolygon() {
          var action = draw.create("polygon");
            view.focus();
            action.on(
              [
                "vertex-add", // Fキー押下で頂点追加
                "vertex-remove", // Zキー押下で頂点削除
                "cursor-update",
                "draw-complete"
              ],
              createPolygonGraphic
           );
        }

        function createPolygonGraphic(event) {
          var vertices = event.vertices;
          view.graphics.removeAll();

          var graphic = new Graphic({
            geometry: {
              type: "polygon",
              rings: vertices,
              spatialReference: view.spatialReference
            },
            symbol: {
              type: "simple-fill", 
              color: "red",
              style: "solid",
              outline: {  
                color: [255, 255, 0],
                width: 3
              }
            }
          });

          view.graphics.add(graphic);
        }

        // テキスト作図
        function enableCreateText() {
          var action = draw.create("point");
          action.on("draw-complete", function (evt) {
            createTextGraphic(evt.coordinates);
          });
        }

        function createTextGraphic(coordinates){
          var point = {
            type: "point", 
            x: coordinates[0],
            y: coordinates[1],
            spatialReference: view.spatialReference
          };

          var textSymbol = {
            type: "text", 
            color: "red",
            haloColor: "black",
            haloSize: "1px",
            text: "GIS奮闘記",
            xoffset: 3,
            yoffset: 3,
            font: { 
              size: 20,
              weight: "bold"
            }
          };

          var graphic = new Graphic({
            geometry: point,
            symbol: textSymbol
          });

          view.graphics.add(graphic);
        }

      }); 

      function displayLayerList(){
        var toggle = document.getElementById("layerToggle");
        if(toggle.style.display=="block"){
          // 非表示
          toggle.style.display ="none";
        }else{
          // 表示
          toggle.style.display ="block";
        }
      }

      // 標高ボタンを押して標高を取得できるモードを切り替える
      var elevation = 0;
      function getElevation(){
        if(elevation==1){
          elevation = 0;
        }else{
          elevation = 1;
        }
      }
      
      // 作図
      function edit(){
        var toggle = document.getElementById("sketchToggle");
        if(toggle.style.display=="block"){
          // 非表示
          toggle.style.display ="none";
        }else{
          // 表示
          toggle.style.display ="block";
        }
      }  
  </script>
</head>
<body>
    <ul id="menu">
      <li id="menu">
        <button id="button" type="button" onClick="displayLayerList()">レイヤー一覧</button>
      </li>
      <li>
        <button id="buttonElevation" type="button" onClick="getElevation()">標高</button>
      </li>
      <li>
        <button id="buttonSketch" type="button" onClick="edit()">作図</button>
      </li>
    </ul>
  <div id="viewDiv"></div>
  <div id="layerToggle">
    <input type="checkbox" id="vectorTileLayer" checked/>背景<br>
    <input type="checkbox" id="mapImageLayer" checked/>国土数値情報 災害情報<br>
    <input type="checkbox" id="boundary" checked />平成 27 年国勢調査 都道府県界<br>
  </div>
  <div id="sketchToggle">
    <select id="geometryType">
        <option value=""></option>
        <option value="ポイント">ポイント</option>
        <option value="ライン">ライン</option>
        <option value="ポリゴン">ポリゴン</option>
        <option value="テキスト">テキスト</option>
      </select>
    <button id="executeSketch" type="button">実行</button>
  </div>
</body>
</html>

それでは作図してみようと思います。

ポイント

f:id:sanvarie:20191020172615p:plain

ライン

f:id:sanvarie:20191020172720p:plain

ポリゴン

f:id:sanvarie:20191020172818p:plain

テキスト

f:id:sanvarie:20191020172910p:plain

ばっちりですね!ソースはそんなに複雑ではないのに ArcGIS API for JavaScript を使えば本当に色々なことができますね。これからは Web GIS が更に普及していくかと思いますので、もしまだ ArcGIS API for JavaScript を使ったことがないという方は使っておいて損はないかと思います。今回は Draw と Graphic クラスの基本的な使い方を紹介しましたが、今後、機会があればもう一歩踏み込んだ使い方を紹介できればと思います。本日は以上です。

ArcGIS API for JavaScript で標高 API を使ってみよう

さて、本日も ArcGIS API for JavaScript について書いてみようと思います。標高 API は以下のエントリーで紹介しているのですが、Python と C# での呼び出しについてでした。今回は JavaScript を使って処理を書いてみようと思います。

www.gis-py.com

www.gis-py.com

www.gis-py.com

今回やろうとしていること

ArcGIS API for JavaScript を使用して、マップをクリックしたら標高を表示する、というような処理を書いてみようと思います。

今回使用するソース

前回のエントリーをベースに今回使用する処理を書き足そうと思います。

www.gis-py.com

前回の完成イメージです。

f:id:sanvarie:20191012150629p:plain

今回は以下のように「レイヤー一覧」ボタンの隣に「標高」ボタンを追加しようと思います。

f:id:sanvarie:20191019134913p:plain

環境

ArcGIS API for JavaScript 4.12
Chrome

マップクリック時に何か処理を走らせたい場合

View クラスの on() メソッドを使用します。

view.on("click", function(event){
  console.log(event.mapPoint);
});

標高API を使用してみる

以下のように実装してみました。リクエストを投げて受け取ったJSONをパースするだけなので、すごく簡単にできますね。

view.on("click", function (event) {  
  if(elevation==1){ 

    var data = { 'lon': event.mapPoint.longitude , 'lat': event.mapPoint.latitude  };
    var querystring = encodeQueryData(data);            
    var url = "http://cyberjapandata2.gsi.go.jp/general/dem/scripts/getelevation.php?" + querystring + "&outtype=JSON"

    // リクエストなげる
    var request = new XMLHttpRequest();
    request.open('GET', url);
    request.onreadystatechange = function () {
        var result = JSON.parse(request.responseText);
        view.popup.open({
          title: result.elevation,
          content : result.elevation,
          location: event.mapPoint 
        });
    };
    request.send(null);
  }
}); 

function encodeQueryData(data) {
  let ret = [];
  for (let d in data)
    ret.push(encodeURIComponent(d) + '=' + encodeURIComponent(data[d]));
  return ret.join('&');
}

if(elevation==1) の部分ですが、これは「標高」ボタンを押したら標高 API を使用するモードを切り替える処理を以下のように入れているためです。

// 標高ボタンを押して標高を取得できるモードを切り替える
var elevation = 0;
function getElevation(){
  if(elevation==1){
    elevation = 0;
  }else{
    elevation = 1;
  }
}    

HTMLの部分を除いてですが、今回追加した処理はこれだけです。

サンプルコード

全コードを以下に記載します。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
  <title>レイヤ一覧表示</title>
  <link rel="stylesheet" href="https://js.arcgis.com/4.12/esri/css/main.css">
  <script src="https://js.arcgis.com/4.12/"></script>
  <style>
    html,
    body,
    #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }
    #layerToggle {
      display: none;
      top: 80px;
      right: 20px;
      position: absolute;
      z-index: 99;
      background-color: white;
      border-radius: 8px;
      padding: 10px;
      opacity: 0.75;
    }
    #menu{
      padding: 0;
      margin: 0;
      height:42px; 
      background-color:#eeeeee;
      font-weight:bold;
    }
    #menu li {
      height:42px;
      margin-right: 2px;
      display: inline-block;
    }
    #button,#buttonElevation {
      left:0px;
      width:164px; 
      height:42px;
    }

  </style>
  <script>
    require([
        "esri/Map",
        "esri/views/MapView",
        "esri/layers/VectorTileLayer",
        "esri/layers/FeatureLayer",
        "esri/layers/MapImageLayer",
        "dojo/on",
        "dojo/domReady!"
      ],
      function(
        Map, 
        MapView, 
        VectorTileLayer,
        FeatureLayer, 
        MapImageLayer,
        on
      ) {
        
        var map = new Map({
            basemap: "gray-vector"
        });

        var vectorTileLayer = new VectorTileLayer({
          url:
            "https://www.arcgis.com/sharing/rest/content/items/92c551c9f07b4147846aae273e822714/resources/styles/root.json",
            id:"vectorTile"
        });
        
        var mapImageLayer = new MapImageLayer({
            url: "https://content.esrij.com/arcgis/rest/services/Dosyasaigai/Dosyasaigai_Tile/MapServer",
            id:"mapImage"
        });

        var boundary = new FeatureLayer({
          url:"https://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/JPN_Boundaries_ECM/FeatureServer",
          id:"boundary",
          opacity:0.5,
          minScale:1500000,
          maxScale:50000
        });
          
        map.add(vectorTileLayer);
        map.add(mapImageLayer);
        map.add(boundary);

        var view = new MapView({  
          container: "viewDiv",
          map: map,
          center: [139.740286, 35.678601],
          zoom: 15
        });

        var vectorTileToggle = document.getElementById("vectorTileLayer");
        var mapImageToggle = document.getElementById("mapImageLayer");
        var boundaryToggle = document.getElementById("boundary");

        on(vectorTileToggle, "change", function() {
          vectorTileLayer.visible = vectorTileToggle.checked;
        });
        on(mapImageToggle, "change", function() {
            mapImageLayer.visible = mapImageToggle.checked;
        });
        on(boundaryToggle, "change", function() {
          boundary.visible = boundaryToggle.checked;
        });

        view.on("click", function (event) {  
          if(elevation==1){ 

            var data = { 'lon': event.mapPoint.longitude , 'lat': event.mapPoint.latitude  };
            var querystring = encodeQueryData(data);            
            var url = "http://cyberjapandata2.gsi.go.jp/general/dem/scripts/getelevation.php?" + querystring + "&outtype=JSON"

            // リクエストなげる
            var request = new XMLHttpRequest();
            request.open('GET', url);
            request.onreadystatechange = function () {
                var result = JSON.parse(request.responseText);
                view.popup.open({
                  title: result.elevation,
                  content : result.elevation,
                  location: event.mapPoint 
                });
            };
            request.send(null);
          }
        }); 

        function encodeQueryData(data) {
          let ret = [];
          for (let d in data)
            ret.push(encodeURIComponent(d) + '=' + encodeURIComponent(data[d]));
          return ret.join('&');
        }
      }); 

      function displayLayerList(){
          var toggle = document.getElementById("layerToggle");
          if(toggle.style.display=="block"){
            // 非表示
            toggle.style.display ="none";
          }else{
            // 表示
            toggle.style.display ="block";
          }
        }

        // 標高ボタンを押して標高を取得できるモードを切り替える
        var elevation = 0;
        function getElevation(){
          if(elevation==1){
            elevation = 0;
          }else{
            elevation = 1;
          }
        }    
  </script>
</head>
<body>
    <ul id="menu">
      <li id="menu">
        <button id="button" type="button" onClick="displayLayerList()">レイヤー一覧</button>
      </li>
      <li>
        <button id="buttonElevation" type="button" onClick="getElevation()">標高</button>
      </li>
    </ul>
  <div id="viewDiv"></div>
  <div id="layerToggle">
    <input type="checkbox" id="vectorTileLayer" checked/>背景<br>
    <input type="checkbox" id="mapImageLayer" checked/>国土数値情報 災害情報<br>
    <input type="checkbox" id="boundary" checked />平成 27 年国勢調査 都道府県界<br>
  </div>
</body>
</html>

このようにマップをクリックすると標高を取得することができました。皇居周辺は大体標高が20数メートルということがわかりました!

f:id:sanvarie:20191019134517p:plain

今回は簡単にポップアップで標高を表示してみましたが、グラフィックか何かで標高を表示しても面白そうですね。その辺の実装も機会があれば紹介できればと思います。今回は以上です。

ArcGIS API for JavaScript でレイヤーの表示/非表示を行う方法

さて、本日も ArcGIS API for JavaScript について書いてみようと思います。前回は以下エントリーでウィジェットについて紹介したのですが、今回はレイヤー表示の切り替えについて書いてみようと思います。

www.gis-py.com

実行環境

ArcGIS API for JavaScript 4.12
Chrome

今回のポイントとなる処理

  1. レイヤーの表示
  2. レイヤーのスケールフィルタの設定
  3. レイヤーの透明度の設定
  4. 初期表示の設定
  5. レイヤーの表示/非表示の制御
  6. レイヤー一覧ボタンの配置

ざっとこんなところでしょうか。色々あるのですが、どれも難しい処理ではありません。

完成イメージ

初期表示は以下のような感じです。
f:id:sanvarie:20191012120012p:plain

「レイヤー一覧」ボタンを押すとレイヤー一覧が表示されます。
f:id:sanvarie:20191012120037p:plain

レイヤをすべて非表示にした状態です。ベースマップのみが表示されている状態になります。
f:id:sanvarie:20191012120058p:plain

各処理について

各処理のポイントを以下で解説します。

レイヤ-の表示

以下のようにすればレイヤーの表示を行うことができます。var map = new Map({basemap: "gray-vector"}); でマップのオブジェクトを作成します。その後に各レイヤのオブジェクトを作成し、それぞれ map.add(); します。そして、作成したマップをパラメーターにして var view = new MapView({ }); でマップビューのオブジェクトを作成します。

var map = new Map({
    basemap: "gray-vector"
});

var vectorTileLayer = new VectorTileLayer({
    url:
    "https://www.arcgis.com/sharing/rest/content/items/92c551c9f07b4147846aae273e822714/resources/styles/root.json",
    id:"vectorTile"
});

var mapImageLayer = new MapImageLayer({
    url: "https://content.esrij.com/arcgis/rest/services/Dosyasaigai/Dosyasaigai_Tile/MapServer",
    id:"mapImage"
});

var boundary = new FeatureLayer({
    url:"https://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/JPN_Boundaries_ECM/FeatureServer",
    id:"boundary",
    opacity:0.5,
    minScale:1500000,
    maxScale:50000
});

map.add(vectorTileLayer);
map.add(mapImageLayer);
map.add(boundary);

var view = new MapView({  
    container: "viewDiv",
    map: map,
    center: [139.740286, 35.678601],
    zoom: 15
});

レイヤーのスケールフィルタの設定

minScale:1500000,
maxScale:50000

レイヤーの透明度の設定

opacity:0.5

初期表示の設定

center: [139.740286, 35.678601],
zoom: 15

レイヤの表示/非表示の制御

dojo/on モジュールを使用します。

on(vectorTileToggle, "change", function() {
    vectorTileLayer.visible = vectorTileToggle.checked;
});
on(mapImageToggle, "change", function() {
    mapImageLayer.visible = mapImageToggle.checked;
});
on(boundaryToggle, "change", function() {
    boundary.visible = boundaryToggle.checked;
});

レイヤー一覧ボタンの配置

当該ボタンを押すと layerToggle の表示/非表示が切り替わります。

function displayLayerList(){
    var toggle = document.getElementById("layerToggle");
    if(toggle.style.display=="block"){
      // 非表示
      toggle.style.display ="none";
    }else{
      // 表示
      toggle.style.display ="block";
    }
}      

<body>
    <ul id="menu">
      <li id="menu">
        <button id="button" type="button" onClick="displayLayerList()">レイヤー一覧</button>
      </li>
    </ul>
  <div id="viewDiv"></div>
  <div id="layerToggle">
    <input type="checkbox" id="vectorTileLayer" checked/>背景<br>
    <input type="checkbox" id="mapImageLayer" checked/>国土数値情報 災害情報<br>
    <input type="checkbox" id="boundary" checked />平成 27 年国勢調査 都道府県界<br>
  </div>
</body>

サンプルコード

全コードを以下に記載します。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
  <title>レイヤ一覧表示</title>
  <link rel="stylesheet" href="https://js.arcgis.com/4.12/esri/css/main.css">
  <script src="https://js.arcgis.com/4.12/"></script>
  <style>
    html,
    body,
    #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }
    #layerToggle {
      display: none;
      top: 80px;
      right: 20px;
      position: absolute;
      z-index: 99;
      background-color: white;
      border-radius: 8px;
      padding: 10px;
      opacity: 0.75;
    }
    #menu{
      padding: 0;
      margin: 0;
	  height:42px; 
      background-color:#eeeeee;
      font-weight:bold;
    }
    #menu li {
      height:42px;
      margin-right: 2px;
      display: inline-block;
    }
    #button {
      left:0px;
      width:164px; 
      height:42px;
    }

  </style>
  <script>
    require([
        "esri/Map",
        "esri/views/MapView",
        "esri/layers/VectorTileLayer",
        "esri/layers/FeatureLayer",
        "esri/layers/MapImageLayer",
        "dojo/on",
        "dojo/domReady!"
      ],
      function(
        Map, 
        MapView, 
        VectorTileLayer,
        FeatureLayer, 
        MapImageLayer,
        on
      ) {
        
        var map = new Map({
            basemap: "gray-vector"
        });

        var vectorTileLayer = new VectorTileLayer({
          url:
            "https://www.arcgis.com/sharing/rest/content/items/92c551c9f07b4147846aae273e822714/resources/styles/root.json",
            id:"vectorTile"
        });
        
        var mapImageLayer = new MapImageLayer({
            url: "https://content.esrij.com/arcgis/rest/services/Dosyasaigai/Dosyasaigai_Tile/MapServer",
            id:"mapImage"
        });

        var boundary = new FeatureLayer({
          url:"https://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/JPN_Boundaries_ECM/FeatureServer",
          id:"boundary",
          opacity:0.5,
          minScale:1500000,
          maxScale:50000
        });
          
        map.add(vectorTileLayer);
        map.add(mapImageLayer);
        map.add(boundary);

        var view = new MapView({  
          container: "viewDiv",
          map: map,
          center: [139.740286, 35.678601],
          zoom: 15
        });

        var vectorTileToggle = document.getElementById("vectorTileLayer");
        var mapImageToggle = document.getElementById("mapImageLayer");
        var boundaryToggle = document.getElementById("boundary");

        on(vectorTileToggle, "change", function() {
          vectorTileLayer.visible = vectorTileToggle.checked;
        });
        on(mapImageToggle, "change", function() {
            mapImageLayer.visible = mapImageToggle.checked;
        });
        on(boundaryToggle, "change", function() {
          boundary.visible = boundaryToggle.checked;
        });
      });

      function displayLayerList(){
        var toggle = document.getElementById("layerToggle");
        if(toggle.style.display=="block"){
          // 非表示
          toggle.style.display ="none";
        }else{
          // 表示
          toggle.style.display ="block";
        }
      }      
  </script>
</head>
<body>
    <ul id="menu">
      <li id="menu">
        <button id="button" type="button" onClick="displayLayerList()">レイヤー一覧</button>
      </li>
    </ul>
  <div id="viewDiv"></div>
  <div id="layerToggle">
    <input type="checkbox" id="vectorTileLayer" checked/>背景<br>
    <input type="checkbox" id="mapImageLayer" checked/>国土数値情報 災害情報<br>
    <input type="checkbox" id="boundary" checked />平成 27 年国勢調査 都道府県界<br>
  </div>
</body>
</html>


少し動かしてみます。以下のようにスケールを変更すると「平成 27 年国勢調査 都道府県界」レイヤーが表示されます。

f:id:sanvarie:20191012122548p:plain

少し見づらいという場合は当該レイヤーを非表示にすれば大丈夫です。「国土数値情報 災害情報」レイヤーがはっきり見えるようになりました。かっこいいですね!

f:id:sanvarie:20191012122719p:plain

ソースは少し複雑そうに見えるのですが、実際は大したことはしていません。単純に地図を表示するだけでも大丈夫なのですが、今回のようにボタンを追加したりすると、そこから色々な処理を展開することができます。例えば、マップとの対話的な操作などでしょうか。次回はこのソースをベースにもう少し機能を追加してみようと思います。本日は以上です。

ArcGIS API for JavaScript のウィジェットを使ってみよう

さて、本日は ArcGIS API for JavaScript について書いてみようと思います。

ArcGIS API for JavaScript とは

ArcGIS API for JavaScript はWeb ブラウザー向けのアプリケーションや Web サイトに GIS(地図)機能を組み込むための API です。これを使用することにより以下のようなことが実現可能になります。

  • 地図表示・操作
  • デバイス連携(GPS・カメラ等)
  • 図形・属性情報編集
  • 距離・面積計測
  • 空間・属性検索
  • 各種解析機能
  • リアルタイム データ表示
  • 3D(※ バージョン 4.0 以降)

なぜ ArcGIS API for JavaScript を取り上げるのか

GIS は昔と違いどんどん Web 化の方向に進んでいます。なので、本ブログでも Web GIS についてもっと取り上げていこうと思い、ArcGIS API for JavaScript をチョイスしてみました。ただ、私も初心者に毛が生えたようなものなので、勉強しながら API にまつわる色々なことを紹介していければと思います。

ArcGIS API for JavaScript は無償で利用可能

開発リソースは ArcGIS for Developers にて開発者アカウントを作成すれば、一部の機能を除き無償で利用可能です。素晴らしいですね!

今回やってみること

まずは簡単なところから始めてみようと思います。色々なことができる API なのですが、私の一押しはウィジェットです。Web アプリなどを開発する際、一から部品を作るのは時間がかかりますし、不具合が発生するリスクもあるかと思います。

ArcGIS API for JavaScript で使用できるウィジェットを使えば、誰でも簡単に便利な部品を使うことができます。今回はその中のほんの一部になりますが、以下を紹介しようと思います。

  • BasemapGallery widget

    • 使用できるベースマップの一覧を表示するウィジェットです。ウィジェット内に表示されているベースマップをクリックすると、マップのベースマップが切り替わります。
  • Locate button

    • 現在位置に移動できるボタンです。モバイルで使用する際に非常に便利な機能ですね。

これらの機能ですが、自分で一から作ろうとすると非常に大変だと思いますが、ArcGIS API for JavaScript を使えば、簡単に実装することができます。

実行環境

ArcGIS API for JavaScript 4.12
Chrome

サンプル

BasemapGallery widget と Locate button を使用したサンプルです。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="initial-scale=1, maximum-scale=1,user-scalable=no"
    />
    <title>sample of Locate button and BasemapGallery widget - 4.12</title>
    <link
      rel="stylesheet"
      href="https://js.arcgis.com/4.12/esri/themes/light/main.css"
    />
    <style>
      html,
      body,
      #viewDiv {
        padding: 0;
        margin: 0;
        height: 100%;
        width: 100%;
      }
    </style>
    <script src="https://js.arcgis.com/4.12/"></script>
    <script>
      require([
        "esri/Map",
        "esri/views/MapView",
        "esri/widgets/Locate",
        "esri/widgets/BasemapGallery"
      ], function(Map, MapView, Locate, BasemapGallery) {
        var map = new Map({
          basemap: "streets"
        });

        var view = new MapView({
          container: "viewDiv",
          map: map,
          center: [139.740286, 35.678601],
          zoom: 18
        });

        var locateBtn = new Locate({
          view: view
        });
        
        var basemapGallery = new BasemapGallery({
          view: view
        });

        // Add the locate widget to the top left corner of the view
        view.ui.add(locateBtn, {
          position: "top-left"
        });
        
        view.ui.add(basemapGallery, {
          position: "top-right"
        });
      });
    </script>
  </head>
  <body>
    <div id="viewDiv"></div>
  </body>
</html>

ポイント

  • <script src="https://js.arcgis.com/4.12/"></script> で API の読み込みをしています。
  • その下の <script>~</script> でマップの表示とウィジェットの読み込みをしています。

これを html として保存して開いてみると、このような感じになります。

f:id:sanvarie:20190930160707p:plain

場所は center: [139.740286, 35.678601] で設定しています。今回は永田町を中心点にしてみました。

マイナスボタンの下にあるのが Locate button で右側にあるのが BasemapGallery widget です。

Locate button を押すと、以下のように現在の場所に移動します。

f:id:sanvarie:20190930161050p:plain

BasemapGallery widget で衛星画像を選択するとこのようにベースマップが切り替わります。素晴らしいですね。

f:id:sanvarie:20190930161355p:plain

このようにウィジェットを使用すると、便利な機能を簡単に実装することができます。今後も他のウィジェットや別の機能などを色々紹介していこうと思うので、興味がある方はぜひ読んでみてください。

参考サイト

ArcGIS API for JavaScript を使用して開発をするために必要となるサンプルコードなどが色々ありますので、ぜひ活用してみてください。

https://www.esrij.com/products/arcgis-api-for-javascript/documents/

Python でリストを CSV 出力する方法

さて、今回は Python でリストを CSV 出力する方法を紹介しようと思います。単純に一次元のリストだけではなく、多次元のリストにも対応したソースを紹介しますので、興味のある方はぜひ読んでみてください。

環境

Windows 10
Python 3.6.5

出力するデータ

すごく単純なデータですが、[1,2,3]と [[1,2,3],[4,5,6],[7,8,9]]というデータを CSV 出力してみようと思います。

使用するライブラリ

csv というPython の標準ライブラリを使用します。

サンプル

一次元と多次元のリストを CSV 出力するサンプルです。

# -*- coding: utf-8 -*-
import csv

def export_list_csv(export_list, csv_dir):

    with open(csv_dir, "w") as f:
        writer = csv.writer(f, lineterminator='\n')

        if isinstance(export_list[0], list): #多次元の場合
            writer.writerows(export_list)

        else:
            writer.writerow(export_list)

def make_list():
    one_dimensional_list = [1,2,3]
    two_dimensional_list = [[1,2,3],[4,5,6],[7,8,9]]

    export_list_csv(one_dimensional_list, r"D:\data\one_dimensional_list.csv")
    export_list_csv(two_dimensional_list, r"D:\data\one_dimensional_list.csv")

if __name__ == '__main__':
    make_list()
  • ポイント
    if isinstance(export_list[0], list): で多次元かどうかを判別しています。意外と便利な isinstance() ですね。

結果

EXCEL で出力した CSV を開いてみました。想定通りの結果が得られました。

f:id:sanvarie:20190928121345p:plain

f:id:sanvarie:20190928121401p:plain

今後は こういった Python の小ネタ集も作っていきたいと思いますので、ぜひ読んでみてください。

ArcGIS Pro で ポイントをアノテーションに変換する方法

さて、本日は ArcGIS Pro で ポイントをアノテーションに変換する方法について紹介したいと思います。

使用するデータ

位置参照情報のデータを使用したいと思います。位置参照情報については以下エントリーでも紹介していますので、興味のある方はぜひ読んでみてください。

www.gis-py.com

www.gis-py.com

データの詳細

今回は大字・町丁目レベルの横須賀市のデータを使用したいと思います。形式は CSV になっています。

f:id:sanvarie:20190927201811p:plain

アノテーション化する属性

「大字町丁目名」という属性をアノテーション化します。

手順

1.「XYテーブル→ポイント」ジオプロセシングツールで CSV をポイントにします。

「XYテーブル→ポイント」で以下のようにパラメーターを設定して「実行」ボタンをクリックしてください。
f:id:sanvarie:20190927202325p:plain

CSV のデータがポイントとして登録されました。 f:id:sanvarie:20190927202354p:plain

2.「大字町丁目名」をラベルとして設定します。

コンテンツウインドウで登録されたポイントレイヤーを右クリックし、「ラベル プロパティ」をクリックしてください。
f:id:sanvarie:20190927202608p:plain

ラベルクラス ウインドウが表示されるので、条件式に「$feature.大字町丁目名」と入力して「適用」ボタンをクリックしてください。
f:id:sanvarie:20190927202702p:plain

再度、コンテンツウインドウで登録されたポイントレイヤーを右クリックし、「ラベル」 をクリックしてください。
f:id:sanvarie:20190927202504p:plain

「大字町丁目名」がラベルとして表示されました。
f:id:sanvarie:20190927203055p:plain

3.「ラベルをアノテーションに変換」ジオプロセシングツールでラベルをアノテーションに変換します。

出力先を入力し、必要であればその他の設定を行い、「実行」ボタンをクリックしてください。
f:id:sanvarie:20190927203831p:plain

「大字町丁目名」ラベルがアノテーションに変換されました。
f:id:sanvarie:20190927203951p:plain

すごく簡単にポイントをアノテーションに変換することができました。文字表記はラベルで十分という方もいらっしゃるかと思いますが、GIS を使う場合、アノテーションを使用するケースは少なくないかと思います。緯度経度と属性さえあれば、このように簡単に CSV などのデータをアノテーション化できますので、ぜひ試してみてください。本日は以上です。

ArcGIS Pro で解析ツールギャラリーにお気に入りのジオプロセシングツールを登録しよう

さて、本日も ArcGIS Pro について書いてみようと思います。皆さんは ArcGIS Pro を使っていますか?ArcMap やほかのGIS デスクトップアプリで十分という場合もあるかもしれませんが、ArcGIS Pro を使えば、作業をより効率的に行うことができます。また、ArcGIS Pro はかゆい所に手が届く機能を数多く搭載しているので、まだ使ったことがないという方はぜひ使ってみてください。

ArcGIS Pro について、本ブログで色々紹介していますので、興味がある方はぜひ以下のエントリーも読んでみてください。
www.gis-py.com

www.gis-py.com

www.gis-py.com

ジオプロセシングツールとは

ArcGIS ユーザーの方はご存知かと思いますが、ESRIジャパンさんのサイトに詳しく載っていますので、そちらをご参照ください。

www.esrij.com

ジオプロセシングツールをもっと効率的に使うには

特定のジオプロセシングツールを毎日のように使用する場合、毎回、対象のジオプロセシングツールを検索して実行するのは少し面倒だと思います。そこで解析ツールギャラリーですね。解析ツールギャラリーにお気に入りのジオプロセシングツールを登録することができますので、ジオプロセシングツールを毎回検索するという面倒な作業を省くことができます。

解析ツールギャラリーとは

「解析タブ」にあります。以下赤枠の部分のことです。

f:id:sanvarie:20190927193625p:plain

解析ツールギャラリーにジオプロセシングツールを登録する方法

赤枠のところをクリックすると
f:id:sanvarie:20190927193709p:plain

このように展開するので、「カスタマイズ」をクリックします。
f:id:sanvarie:20190927193819p:plain

以下画面が起動します。「+」ボタンをクリックします。
f:id:sanvarie:20190927193945p:plain

グループ名を入力し、「+」ボタンをクリックします。
f:id:sanvarie:20190927194136p:plain

登録したいジオプロセシングツールを検索し、クリックします。
f:id:sanvarie:20190927194230p:plain

登録し終えたら最後にOKをクリックします。
f:id:sanvarie:20190927194344p:plain

解析ツールギャラリーに登録したジオプロセシングツールが表示されました。
f:id:sanvarie:20190927194506p:plain

あっさりできましたね。このように ArcGIS Pro には作業を効率化するための便利な機能がたくさん搭載されています。今後も少しずつになってしまうと思いますが、便利機能を紹介していきますので、ぜひ次回も読んでいただければと思います。