もしGISのオープンソース を使用することになったら押さえておくこと(OpenLayersを例にする)

# はじめに
GISのオープンソースに携わって最初結構苦労した。
色々記事はあるが、その知識を使えるまでに、初心者用にこの辺クローズアップされて整理されていれば、もっとスムーズに進んでいたのにと思う所を整理してみる。

# GISのオープンソースに取り組んで必要だったこと
・オープンソースを使用した地図操作方法
・バンドル(webpack等)の理解
・地図の理解
上記の動きがどんなものかを早く掴むことが理解のスピードにつながる。
webpackは地図のオープンソースを使用する場合においてよく使用され、知らないと足かせになる。ただそんなにボリュームが大きい箇所ではなく、早急に理解をする必要はある。
地図の理解も必要だが、最初はそれに振り回されるかもしれない。

# 本件のテーマ
本件はオープンソースを使用した地図操作方法で例をあげる。
本件記載していることは他の記事等で記載されていることであるが、あまりにも簡潔に記載されているため、中々理解まで落ちなかった。そういった箇所をソースコードで例にあげ、Openlayersというオープンソースを使用する。なおバンドルツールは使用しない。

# 地図操作方法例
本テーマでは、Openlayersというオープンソースを使用した例とする。
地図表示から表示したいデータを地図に表示し、そのデータ箇所をクリックイベントを起こし何かをする所までを例にあげる。

最初はわかりにくいが簡潔に記載すると

1. map表示
    1. ol.Mapを呼び出す。
1. データを表示
    1. データ表示用のmapオブジェクトのプロパティ(オプション)のlayer(レイヤー)に対し、地図表示とは別のデータ表示用のレイヤーを作る。
    1. そのレイヤーのsouceオプションに対し、featureオブジェクトをセットする。
    1. featureオブジェクトに、データをセットする。
1. そのデータをクリックして表示
    1. mapオブジェクトのクリックイベントを起こしクリックした場合
    1. クリックした時、forEachFeatureAtPixelというmapメソッドを使用し、featureオブジェクトを検知するメソッドを起動
    1. fetureがあれば、そのfeatureから欲しいデータを取得し、overlayオプション等を使用し、プロットを表示する

**押さえておくことは以上の１から３ができること、特に2のデータセットの動きです。**

# 1.まず地図を表示することから行う。
![スクリーンショット 2021-05-01 15.50.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/0b262c0e-4289-07aa-37d6-6f18bb3c7948.png)

```javascript
<!doctype html>
<html lang="en">
  <head>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.5.0/css/ol.css" type="text/css">
    <style>
      .map {
        height: 400px;
        width: 100%;
      }
    </style>
    <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.5.0/build/ol.js"></script>
    <title>OpenLayers example</title>
  </head>
  <body>
    <h2>My Map</h2>
    <div id="map" class="map"></div>
    <script type="text/javascript">
      var map = new ol.Map({
        target: 'map',
        layers: [
          new ol.layer.Tile({
            source: new ol.source.OSM()
          })
        ],
        view: new ol.View({
          center: ol.proj.fromLonLat([139.745433, 35.658581]),
          zoom: 16
        })
      });
    </script>
  </body>
</html>
```
OpenLayersMapオブジェクトを作成

```javascript
<div id="map" class="map"></div>
```

OpenLayersの地図を表示する。レイヤー(layers)オプションで、表示したい地図レイヤーを記述する。

```javascript
    layers: [
      new ol.layer.Tile({
        source: new ol.source.OSM()
      })
    ]
```

viewオプションで、マップの中心、解像度、および回転を指定。

```javascript
   view: new ol.View({
      center: ol.proj.fromLonLat([37.41, 8.82]),
      zoom: 4
    })
```
以上がまず最初のステップである。
この辺りまではそういうものなんだとわかると思う。
問題は次のステップである。

# 2.地図に提供された緯度を地図に出す処理を行う
![スクリーンショット 2021-05-01 16.02.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/759ba2f4-3255-b964-4807-8583799ba0e7.png)

黄色のプロットが、指定したデータの経度、緯度に基づき表示したものである。

```javascript
<!doctype html>
<html lang="en">
  <head>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.5.0/css/ol.css" type="text/css">
    <style>
      .map {
        height: 600px;
        width: 100%;
      }
    </style>
    <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.5.0/build/ol.js"></script>
    <title>OpenLayers example</title>
  </head>
  <body>
    <h2>My Map</h2>
    <div id="map" class="map"></div>
    <script type="text/javascript">


      var map = new ol.Map({
        target: 'map',
        layers: [
          new ol.layer.Tile({
            source: new ol.source.OSM()
          })
        ],
        view: new ol.View({
          center: ol.proj.fromLonLat([139.745433, 35.658581]),
          zoom: 16
        })
      });


// 浜松町〜東京タワーの道のりを表す座標をピックアップした配列
var coordinates = [
  {
    name:'住所1',
    coordinate:[139.75655240134424, 35.6553463380788],
  },
  {
    name:'住所2',
    coordinate:[139.75573020071425, 35.654585477741634],
  },
  {
    name:'住所3',
    coordinate:[139.75390308820317, 35.65482672692599],
  },
  {
    name:'住所4',
    coordinate:[139.74578081849876, 35.659007609306684],
  }
];
// 上記配列をのせるためのレイヤーを作成する
var markerLayer = new ol.layer.Vector({
  source: new ol.source.Vector()
});
map.addLayer(markerLayer);

// 点を出す
drawDot();

function drawDot(){
    // 点をレイヤーにプロットする
    for (var i in coordinates) {
      var info = coordinates[i];
      // 地物オブジェクトを作成
      var feature = new ol.Feature({
          geometry: new ol.geom.Point(ol.proj.fromLonLat(info.coordinate))
      });
      feature.information = info;

      // 点のスタイルを設定
      var style = new ol.style.Style({
          image: new ol.style.Icon({
              src: 'img/icon.png',
              anchor: [0.5, 1.0],
              scale: 0.8
          })
      });
      feature.setStyle(style);

      // 地物を追加する
      markerLayer.getSource().addFeature(feature);
  }
}


    </script>
  </body>
</html>
```
経度、緯度の配列をどうやって、地図に表示しているかに注目して頂きたい。
点を出すためのレイヤーを定義し、現状の地図のレイヤーに追加している。

```javascript
// 上記配列をのせるためのレイヤーを作成する
var markerLayer = new ol.layer.Vector({
  source: new ol.source.Vector()
});
map.addLayer(markerLayer);
```

そして、追加したレイヤーのsouceオプションにfeatureを追加している所である。

```javascript
      // 地物を追加する
      markerLayer.getSource().addFeature(feature);
```
今回はレイヤーからsouceオプションを取得し、featureを追加する形をとっている。
そしてfeatureはというと、

```
      // 地物オブジェクトを作成
      var feature = new ol.Feature({
          geometry: new ol.geom.Point(ol.proj.fromLonLat(info.coordinate))
      });
```
上記のように、経度、緯度をFeatureをnewして、geometoryオプションに、点を表示するPointオブジェクトを呼んで、経度、緯度を追加している。ちなみに今回は割愛するがfromLonLatは、経度、緯度表示をopenlayersのメルカトル記法に合わせて変換している。

以上が経度、緯度のデータを表示している手順である。
ここが、何をしているのかわかりにくくさせている所ではないかと思う。

# 3.その点をクリックして、その情報を表示する所を行う。
![スクリーンショット 2021-05-01 16.12.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/24e56222-c949-77de-9a7f-8ab647c4dff0.png)

```javascript
<!doctype html>
<html lang="en">
  <head>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.5.0/css/ol.css" type="text/css">
    <style>
      .map {
        height: 600px;
        width: 100%;
      }
      #popup {
          position: relative;
          display: inline-block;
          margin: 1.5em 0;
          padding: 7px 10px;
          min-width: 120px;
          max-width: 100%;
          color: #555;
          font-size: 16px;
          background: #FFF;
          border: solid 3px #555;
          box-sizing: border-box;
      }
  
      #popup:before {
          content: "";
          position: absolute;
          bottom: -24px;
          left: 50%;
          margin-left: -15px;
          border: 12px solid transparent;
          border-top: 12px solid #FFF;
          z-index: 2;
      }
  
      #popup:after {
          content: "";
          position: absolute;
          bottom: -30px;
          left: 50%;
          margin-left: -17px;
          border: 14px solid transparent;
          border-top: 14px solid #555;
          z-index: 1;
      }
  
      .popup p {
          margin: 0;
          padding: 0;
      }
    </style>
    <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.5.0/build/ol.js"></script>
    <title>OpenLayers example</title>
  </head>
  <body>
    <h2>My Map</h2>
    <div id="map" class="map"></div>
    <div id='popup'></div>
    <script type="text/javascript">


      var map = new ol.Map({
        target: 'map',
        layers: [
          new ol.layer.Tile({
            source: new ol.source.OSM()
          })
        ],
        view: new ol.View({
          center: ol.proj.fromLonLat([139.745433, 35.658581]),
          zoom: 16
        })
      });


// 浜松町〜東京タワーの道のりを表す座標をピックアップした配列
var coordinates = [
  {
    name:'住所1',
    coordinate:[139.75655240134424, 35.6553463380788],
  },
  {
    name:'住所2',
    coordinate:[139.75573020071425, 35.654585477741634],
  },
  {
    name:'住所3',
    coordinate:[139.75390308820317, 35.65482672692599],
  },
  {
    name:'住所4',
    coordinate:[139.74578081849876, 35.659007609306684],
  }
];
// 上記配列をのせるためのレイヤーを作成する
var markerLayer = new ol.layer.Vector({
  source: new ol.source.Vector()
});
map.addLayer(markerLayer);

// 点を出す
drawDot();

// ポップアップを表示するためのオーバーレイを作成する
 var overlay = new ol.Overlay({
  element: document.getElementById('popup'),
  positioning: 'bottom-center'
});

function drawDot(){
    // 点をレイヤーにプロットする
    for (var i in coordinates) {
      var info = coordinates[i];
      // 地物オブジェクトを作成
      var feature = new ol.Feature({
          geometry: new ol.geom.Point(ol.proj.fromLonLat(info.coordinate))
      });
      feature.information = info;

      // 点のスタイルを設定
      var style = new ol.style.Style({
          image: new ol.style.Icon({
              src: 'img/icon.png',
              anchor: [0.5, 1.0],
              scale: 0.8
          })
      });
      feature.setStyle(style);

      // 地物を追加する
      markerLayer.getSource().addFeature(feature);
  }
}


    // 地図のクリックイベントを設定
    map.on('click', function (evt) {
      var feature = map.forEachFeatureAtPixel(evt.pixel,
          function (feature) {
              return feature;
          });
      if (feature) {
          var coordinates = feature.getGeometry().getCoordinates();
          var info = feature.information;
          var element = overlay.getElement();
          console.log(element);
          var descriptionHTML = "<div>" + info.name + "</div>";
          element.innerHTML = descriptionHTML;
          overlay.setPosition(coordinates);
          map.addOverlay(overlay);
      } else {
          map.removeOverlay(overlay);
      }
  });


    </script>
  </body>
</html>
```
地図をクリックして、点からプロットをどうやって表示している箇所は下記になる。

```javascript
// ポップアップを表示するためのオーバーレイを作成する
 var overlay = new ol.Overlay({
  element: document.getElementById('popup'),
  positioning: 'bottom-center'
});

    // 地図のクリックイベントを設定
    map.on('click', function (evt) {
      var feature = map.forEachFeatureAtPixel(evt.pixel,
          function (feature) {
              return feature;
          });
      if (feature) {
          var coordinates = feature.getGeometry().getCoordinates();
          var info = feature.information;
          var element = overlay.getElement();
          console.log(element);
          var descriptionHTML = "<div>" + info.name + "</div>";
          element.innerHTML = descriptionHTML;
          overlay.setPosition(coordinates);
          map.addOverlay(overlay);
      } else {
          map.removeOverlay(overlay);
      }
  });

```
地図をクリックすると、featureを検知し、そのfeatureからクリックしたfeatureのデータを取得している。
その値を、mapオブジェクトのoverlayオプション(プロパティ)に追加して、プロットという形で表示している。
地図をクリックした動作は、こういうのがあるということを知っておくべきではないかと思う。

# まとめ
GISのオープンソースを使用し、戸惑った箇所をわかりやすくするつもりで記載した(そうなってないと思われるが)。
最初この辺がわからず説明を受けても、理解できない状況で、事前にこういう理解があったらと思う所である。
地図のオープンソースとして、google mapsが一般的に使用されると思うが、大きな違いは、データセットがgoogl mapsでは充実し、簡単に使用できる所だと思うが、そのデータそのものを利用できない。オープンソースではデータを自由に読み込み、データをセットできるが、その箇所が初心者に情報が少なく戸惑いを感じさせる所でもあると思う。

# 参考にした本
下記を参考にしました。案件に入る前に、下記に取り組んでいればもっと理解が早かったと思います。

[JavaScriptではじめるWebマップアプリケーション (PDF版)](https://booth.pm/ja/items/1314906)

[OpenLayers4で遊ぼう　無料の地図データをWebに表示！ (技術の泉シリーズ（NextPublishing）)](https://www.amazon.co.jp/OpenLayers4%E3%81%A7%E9%81%8A%E3%81%BC%E3%81%86-%E7%84%A1%E6%96%99%E3%81%AE%E5%9C%B0%E5%9B%B3%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92Web%E3%81%AB%E8%A1%A8%E7%A4%BA%EF%BC%81-%E6%8A%80%E8%A1%93%E6%9B%B8%E5%85%B8%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA%EF%BC%88NextPublishing%EF%BC%89-%E4%BD%90%E8%97%A4-%E5%A5%88%E3%80%85%E5%AD%90-ebook/dp/B07CH95DYB/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&dchild=1&keywords=openlayers&qid=1619855075&s=digital-text&sr=1-1)
今回記載しているソースは本参考書を参考にさせて頂いております。