OpenLayersで図形を描画する①~基本

JavaScript

目次

OpenLayersでは地図内にお絵かきができます。
以下のようなお絵かきが可能です。

  • マーカーの配置
  • 線を引く
  • 円で囲む
  • 三角形や四角形などの「多角形」で囲む
  • テキストを書き込む
  • 矢印で強調する
  • 固定画像の配置
  • フリーハンドで線を書き込む

今回は「マーカーの配置」、「線を引く」、「円で囲む」、「多角形で囲む」をやってみます。

実際にやってみた

触れます↓↓↓↓

地図左側に配置した各ボタンを押下すると描画モードになります。

ソースコードはこちらになります。

    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/4.6.5/ol.css" integrity="sha256-rQq4Fxpq3LlPQ8yP11i6Z2lAo82b6ACDgd35CKyNEBw=" crossorigin="anonymous">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/4.6.5/ol.js" integrity="sha256-77IKwU93jwIX7zmgEBfYGHcmeO0Fx2MoWB/ooh9QkBA=" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script>
  
  
    <div id="map" style="height:300px"></div>
    <script>
    
      // 描画された図形に適用されるスタイル
      var styleFunction = function(feature) {
        var geometry = feature.getGeometry();
        var styles = [];
        if (geometry.getType() == 'Point') {
          // 点の場合、マーカー配置
          styles.push(new ol.style.Style({
            image: new ol.style.Icon ({
                anchor: [0.5, 46],
                anchorXUnits: 'fraction',
                anchorYUnits: 'pixels',
                opacity: 0.95,
                src: 'https://openlayers.org/en/latest/examples/data/icon.png'
            })
          }));
        } else if (geometry.getType() == 'LineString'){
          styles.push(new ol.style.Style({
            stroke: new ol.style.Stroke({
              color: '#ff0000',
              width: 2
            })
          }));
        } else {
          styles.push(new ol.style.Style({
            stroke: new ol.style.Stroke({
              color: '#ff0000',
              width: 2
            }),
            fill: new ol.style.Fill({
              color: 'rgba(255,0,0,0.3)',
            })
          }));
        }

        return styles;
      };
      
      // 図形を描画するレイヤーを定義する
      var vectorSource = new ol.source.Vector();
      var vectorLayer = new ol.layer.Vector({
        source: vectorSource,
        style: styleFunction
      });
      
      // 描画用ボタンの配置
      // マーカーボタン
      var MarkerControl = (function (Control) {
        function MarkerControl(opt_options) {
          var options = opt_options || {};

          var button = document.createElement('button');
          button.innerHTML = '※';
          button.name = 'Point';
          button.classList.add('map-button');

          var element = document.createElement('div');
          element.className = 'ol-unselectable ol-control';
          element.style.top = '100px';
          element.style.left = '5px';
          element.appendChild(button);

          Control.call(this, {
            element: element,
            target: options.target
          });

          button.addEventListener('click', this.drawMarker.bind(this), false);
        }

        if ( Control ) MarkerControl.__proto__ = Control;
        MarkerControl.prototype = Object.create( Control && Control.prototype );
        MarkerControl.prototype.constructor = MarkerControl;

        MarkerControl.prototype.drawMarker = function drawMarker () {
          addInteractions('Point');
        };

        return MarkerControl;
      }(ol.control.Control));
      
      // Linstringボタン
      var LineStringControl = (function (Control) {
        function LineStringControl(opt_options) {
          var options = opt_options || {};

          var button = document.createElement('button');
          button.innerHTML = '<';
          button.name = 'LineString';
          button.classList.add('map-button');

          var element = document.createElement('div');
          element.className = 'ol-unselectable ol-control';
          element.style.top = '140px';
          element.style.left = '5px';
          element.appendChild(button);

          Control.call(this, {
            element: element,
            target: options.target
          });

          button.addEventListener('click', this.drawLineString.bind(this), false);
        }

        if ( Control ) LineStringControl.__proto__ = Control;
        LineStringControl.prototype = Object.create( Control && Control.prototype );
        LineStringControl.prototype.constructor = LineStringControl;

        LineStringControl.prototype.drawLineString = function drawLineString () {
          addInteractions('LineString');
        };

        return LineStringControl;
      }(ol.control.Control));
      
      // 円ボタン
      var CircleControl = (function (Control) {
        function CircleControl(opt_options) {
          var options = opt_options || {};

          var button = document.createElement('button');
          button.innerHTML = 'О';
          button.name = 'Circle';
          button.classList.add('map-button');

          var element = document.createElement('div');
          element.className = 'ol-unselectable ol-control';
          element.style.top = '180px';
          element.style.left = '5px';
          element.appendChild(button);

          Control.call(this, {
            element: element,
            target: options.target
          });

          button.addEventListener('click', this.drawCircle.bind(this), false);
        }

        if ( Control ) CircleControl.__proto__ = Control;
        CircleControl.prototype = Object.create( Control && Control.prototype );
        CircleControl.prototype.constructor = CircleControl;

        CircleControl.prototype.drawCircle = function drawCircle () {
          addInteractions('Circle');
        };

        return CircleControl;
      }(ol.control.Control));
      
      // 多角形ボタン
      var PolygonControl = (function (Control) {
        function PolygonControl(opt_options) {
          var options = opt_options || {};

          var button = document.createElement('button');
          button.innerHTML = '△';
          button.name = 'Polygon';
          button.classList.add('map-button');

          var element = document.createElement('div');
          element.className = 'ol-unselectable ol-control';
          element.style.top = '220px';
          element.style.left = '5px';
          element.appendChild(button);

          Control.call(this, {
            element: element,
            target: options.target
          });

          button.addEventListener('click', this.drawPolygon.bind(this), false);
        }

        if ( Control ) PolygonControl.__proto__ = Control;
        PolygonControl.prototype = Object.create( Control && Control.prototype );
        PolygonControl.prototype.constructor = PolygonControl;

        PolygonControl.prototype.drawPolygon = function drawPolygon () {
          addInteractions('Polygon');
        };

        return PolygonControl;
      }(ol.control.Control));

      // 地図の初期化
      var map = new ol.Map({
        controls: ol.control.defaults().extend([
          // マーカーボタン
          new MarkerControl(),
          // LineStringボタン
          new LineStringControl(),
          // 円ボタン
          new CircleControl(),
          // 多角形ボタン
          new PolygonControl
        ]),
        target: 'map',
        layers: [
          new ol.layer.Tile({
            preload: 4,
            source: new ol.source.OSM()
          }), vectorLayer
        ],
        loadTilesWhileAnimating: true,
        view: new ol.View({
            center: ol.proj.fromLonLat([134.227352, 35.539909]),
            zoom: 13
        })
      });

      // 図形描画処理
      var draw, snap;
      function addInteractions(type) {
        // 描画モードの切り替え
        if (draw) {
          map.removeInteraction(draw);
          draw = null;
        }
        draw = new ol.interaction.Draw({
          source: vectorSource,
          type: type
        });
        map.addInteraction(draw);
        
        // マウスカーソルが図形の座標に近づいたときにカーソルを合わせる
        if (!snap) {
          snap = new ol.interaction.Snap({source: vectorSource});
          map.addInteraction(snap);
        }
        
        // 現在の描画モードのボタンを色付けする
        $(".map-button").css('color', 'white');
        $(".map-button[name='" + type + "']").css('color', 'red');
      }
    </script>

以下のように図形を描画することができます。

  • 「※」ボタン……マーカー配置モードに
    地図上をクリックすることでマーカーを配置できます。
  • 「<」ボタン……線描画モード
    地図上で線を引きたい2点をクリックすることで描画できます。
    ダブルクリックで描画を終了します。
  • 「○」ボタン……円描画モード
    中心点をクリックで指定し、その後円の大きさを指定します。
  • 「△」ボタン……多角形描画モード
    地図上に頂点となる座標をクリックで指定することで、多角形を描画できます。

解説

14~48行目で図形に適用するスタイルを定義しています。OpenLayersで描画する図形には以下のようなスタイルを適用することができます。

  • 線の幅(stroke width)
  • 線の色(stroke color)
  • (円、多角形の)塗りつぶしの色(fill color)
  • マーカー画像の配置もスタイルの一種

51~55行目では図形を描画するためのレイヤーを定義しています。図形を描画するためには、地図が表示されている画像レイヤーとは別のレイヤー(Vector Layer)を用意する必要があります。

59~196行目では地図左側のボタンを定義しています。詳細は前回の記事を参考にしてください。87、122、157、192行目で押下時の処理を記載しています。

199~222行目では地図の初期化を行っています。上の処理で定義したボタンの定義やVector Layerの定義はここで使用します。

225~247行目が今回の本題である描画処理です。ol.interaction.Drawという機能を使用します。
※interactionとは地図上で行う直感的な操作のことです。標準定義されているものに以下のようなものがあります。

  • 地図上でドラッグすることで移動する
  • 地図上をダブルクリックすることでズームインする
  • Shiftを押下しながらドラッグ&ドロップ操作で範囲拡大
  • Shift + Alt + マウスドラッグで地図を回転する

mapオブジェクトに対してDrawをaddInteractionすることで、地図を「描画モード」にすることができます。

描画モードを定義する際、「どのような図形を描画するか」という指定が必要です。以下の種類の描画モードを指定することができます。

  • 点(Point)
  • 線(LineString)
  • 円(Circle)
  • 多角形(Polygon)

また今回は、どの描画モードか明示的にするために、現在の描画モードのボタンの文字を赤色で表示するようにしています。(245~246行目)

まとめ

今回はボリューミーな内容になりました。

しかし、これはOpenLayersが高機能たる所以です。
本来、このような描画処理を実現するためには、自作でゴリゴリJavaScriptを記述する必要があります。
OpenLayersでは図形を描画する機能が標準で備わっていますので、比較的簡単なソースで「おえかき」が可能になっています。

Drawにはまだまだ面白い機能が備わっているので次回以降お伝えしてまいります。

OpenLayersについての別記事もぜひご覧ください!

①図形を描画する(マーカー、線、円、多角形)
②テキストを書き込む(矩形)
③フリーハンドで書き込む
④矢印で強調する
⑤固定画像を配置する
⑥図形を選択する
⑦図形を移動させる
⑧固定画像の移動
⑨図形を変形させる
⑩長方形を変形させる
⑪図形を複数選択する
⑫図形をJSON形式で保存&読込
⑬図形を削除する
⑭線のスタイルを設定する
⑮図形の色を設定する

【入門】地図を表示させる方法
ボタンを配置する方法
ミニマップを表示する方法

コメント

タイトルとURLをコピーしました