Angular9でOpenLayersを使う(3)

##マップ上でクリックした位置にマーカーを置く
この記事は「[Angular9でOpenLayersを使う(2)](https://qiita.com/ayato077/items/81a3402cba687474a53c)」の続きです。

前回はマップ上にマーカーを表示する方法について解説しました。今回はそれを応用して、マップ上でクリックした場所にマーカーを表示させます。

##使用ライブラリ等
- Angular9: https://angular.io/
- OpenLayers v6.3.1: https://openlayers.org/
- CodeSandbox: Online IDE: https://codesandbox.io/

##動的にマーカーを設置する方法
クリックした点の座標を読み取り、その座標に新しい要素(Feature)を追加します。今回はマップ上に表示するマーカーの数を１つとして作成します。次の関数をapp.component.tsに追加していきましょう。

```
//app.component.ts
addSinglePin(horizontal: number, vertical: number): void {
    //Delete previous setted marker
    this.iconVectorSource.refresh();

    this.iconFeature = new Feature({
      geometry: new Point([horizontal, vertical])
    });

    this.iconFeature.setStyle(this.iconPinStyle);

    this.iconVectorSource.addFeature(this.iconFeature);
  }
```
シンプルな内容ですね。続いてngOnInit()にイベント発生時にこの関数を呼び出すように設定してあげましょう。singleclickで発生するようにします。次のコードをngOnInit()に追加します。

```
//app.component.ts
//This is for the scope-access from callback function
    let self = this;

    //event.coordinate is array [horizontal, vertical]
    this.map.on("singleclick", function(event) {
      self.addSinglePin(event.coordinate[0], event.coordinate[1]);
    });
```

コールバック関数からthis.addSinglePinと記述してしまうと、スコープが異なるので参照できません。ngOnInit()内でself=thisとして参照しておきましょう。map.onはイベントリスナーを追加するための関数です。(map.unでリスナーを削除できます)
シングルクリックした際のeventから座標をaddSinglePinに渡して呼びます。

これで自由にクリックした位置にマーカーが表示されれば完成です。ちなみにaddSinglePinのthis.iconVectorSource.refresh()の行を消すと、クリックごとにマーカーが追加されます。
最後にapp.component.tsのコードを載せておきます。

```
//app.component.ts
import { Component, OnInit } from "@angular/core";
import "ol/ol.css";
import { Map, View, Feature } from "ol";
import { OSM } from "ol/source";
import { fromLonLat } from "ol/proj";
import { Tile } from "ol/layer";
import VectorLayer from "ol/layer/Vector";
import VectorSource from "ol/source/Vector";
import Icon from "ol/style/Icon";
import Style from "ol/style/Style";
import { Point } from "ol/geom";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
  title = "OpenLayers";

  skytree = [139.81083333, 35.71000138];

  map: Map;

  iconFeature = new Feature({
    geometry: new Point(fromLonLat(this.skytree))
  });

  iconVectorSource = new VectorSource({
    features: []
  });

  iconVectorLayer = new VectorLayer({
    source: this.iconVectorSource
  });

  iconPinStyle = new Style({
    image: new Icon({
      anchor: [0.5, 1],
      src: "../assets/pin.svg",
      color: "black",
      scale: 0.4
    })
  });

  ngOnInit() {
    this.map = new Map({
      target: "map",
      layers: [
        new Tile({
          source: new OSM()
        })
      ],
      view: new View({
        center: fromLonLat(this.skytree),
        zoom: 15
      })
    });

    this.map.addLayer(this.iconVectorLayer);

    this.iconFeature.setStyle(this.iconPinStyle);

    this.iconVectorSource.addFeature(this.iconFeature);

    //This is for the scope-access from callback function
    let self = this;

    //event.coordinate is array [horizontal, vertical]
    this.map.on("singleclick", function(event) {
      self.addSinglePin(event.coordinate[0], event.coordinate[1]);
    });
  }

  addSinglePin(horizontal: number, vertical: number): void {
    //Delete previous setted marker
    this.iconVectorSource.refresh();

    this.iconFeature = new Feature({
      geometry: new Point([horizontal, vertical])
    });

    this.iconFeature.setStyle(this.iconPinStyle);

    this.iconVectorSource.addFeature(this.iconFeature);
  }
}

```