1
votes

I have a project built with Angular 8 and OpenLayers 3. I want to be able to draw and also delete Polygons from a map. I tried to port this working example into my project.

These are the interacting components.

  • sidebar.html: contains the button to be clicked
  • sidebar.component: calls the next() of the observable.
  • sidebar.service: contains the implementation of an observable.
  • map.component: in which we subscribe() to the observable and add a (temporary) vector layer.

With the following code I am able to draw polygons on the map, but i can't delete them.

In sidebar.html a button is set to call addInteraction().

sidebar.html

  ...
  <mat-button-toggle value="Polygon" (change)="addInteraction()">Draw Polygon
    <mat-icon>crop_original</mat-icon></mat-button-toggle>
  <br><br/>
  ...

In the sidebar component we call next()

sidebar.component

  ...
  addInteraction() {
    this._sidebarService.nextDrawAOI('Polygon');
  }
  ...

In the sidebar service is were next() is implemented.

sidebar.service

  private drawAOI = new Subject<string>();

  ...
  getDrawAOI() {
    return this.drawAOI.asObservable();
  }
   ...
  nextDrawAOI(x) {
    this.drawAOI.next(x);
  }

In map component, subscribe() is called and . .

  1. The Vector layer is created.
  2. Draw, Snap and Modify interactions are added.
  3. The map is added to the vector layer with setMap(this.map). The layer won't get handled by the map but it will be temporarily added to it.

map.component

    ...
private polygonLayer: VectorLayer;
private polygonSource: VectorSource;
private modify: Modify;
private draw: Draw;
private snap: Snap;
    ...
ngAfterViewInit() {

..

// Layers
let source = new OlXYZ({
  ..
});

let layer = new OlTileLayer({
  ..
});

// View
let view = new OlView({
  ..
});

const mousePositionControl = new MousePosition({
   ...
});

// Map
this._map = new OlMap({
  controls: defaultControls().extend([
    scaleLineControl, mousePositionControl
  ]),
  interactions: OlXYZ.interactions,
  target: 'map',
  layers: [layer],
  view: view
});


this._sidebarService.getDrawAOI().pipe(map((x) => {
  this.polygonSource = new VectorSource({
    wrapX: false,
    dataProjection: 'EPSG:4326',
    featureProjection: 'EPSG:3857'
  });
  this.polygonLayer = new VectorLayer({
    source: this.polygonSource
  });

  this.modify = new Modify({source: this.polygonSource});
  this._map.addInteraction(this.modify);

  this._map.removeInteraction(this.draw);
  this._map.removeInteraction(this.snap);
  this.polygonLayer.setMap(null);

  this.draw = new Draw({
    source: this.polygonSource,
    type: x
  });
  this._map.addInteraction(this.draw);
  this.snap = new Snap({source: this.polygonSource});
  this._map.addInteraction(this.snap);

  this.polygonLayer.setMap(this._map);
  return x;
})).subscribe((x) => {
  console.log(x);
});


this._map.on('click', function(evt) {

In the next line when an event is fired this.polygonLayer is undefined !

  var features = this.polygonLayer.getSource().getFeatures();
  features.forEach((feature) => {
    this.polygonLayer.getSource().removeFeature(feature);
  });
});

}

The problem lies here.

In the map.component i have placed the handler of an event. When an event is fired, the vector layer (aka. polygonLayer) is undefined. It seems like the vector will not bind to this. Is this a binding problem? And if it is, why does this occur?

1

1 Answers

0
votes

Yes, it is a binding (scope) problem with your this. Change your function to an arrow function:

this._map.on('click', (evt) => {
  var features = this.polygonLayer.getSource().getFeatures();
  features.forEach((feature) => {
    this.polygonLayer.getSource().removeFeature(feature);
  });
});

Since:

An arrow function does not have its own this. The this value of the enclosing lexical scope is used; arrow functions follow the normal variable lookup rules. So while searching for this which is not present in current scope, an arrow function ends up finding the this from its enclosing scope.

Then your this has the right scope (your component).