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 クラスの基本的な使い方を紹介しましたが、今後、機会があればもう一歩踏み込んだ使い方を紹介できればと思います。本日は以上です。