I have an app that updates a Bootstrap select input dropdown menu when you click a polygon on the map in ngx-leaflet based on this example. I want to be able to also select a polygon name from the select input and have the same functionality as the click event--in this case, using fitBounds
to pan and zoom to the selected polygon.
I added event binding to my HTML template, which detects each new selection from the dropdown menu. I passed a new function, onChange()
to that event. But I'm stumped where to go from here. In the click event, I am able to use e.target
to access the bounds of the selected polygon. But inside onChange()
, all I have access to is the name of the selected polygon, but I don't actually have the geometry associated with that polygon. So how am I able to use the dropdown select input to select a polygon name and have the map update the polygon associated with that name? (Please note that I am hoping for a flexible response, as I would like to do more than just fitBounds()
in my actual app outside of this example.)
Here is my example code:
polygons.geojson (in assets
folder)
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"stroke": "#555555",
"stroke-width": 2,
"stroke-opacity": 1,
"fill": "#555555",
"fill-opacity": 0.5,
"name": "poly1"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-121.95098876953125,
46.82966386051541
],
[
-121.78482055664061,
46.82966386051541
],
[
-121.78482055664061,
46.91368905872705
],
[
-121.95098876953125,
46.91368905872705
],
[
-121.95098876953125,
46.82966386051541
]
]
]
}
},
{
"type": "Feature",
"properties": {
"stroke": "#555555",
"stroke-width": 2,
"stroke-opacity": 1,
"fill": "#555555",
"fill-opacity": 0.5,
"name": "poly2"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-121.77726745605469,
46.83107318799318
],
[
-121.62963867187499,
46.83107318799318
],
[
-121.62963867187499,
46.913220009605624
],
[
-121.77726745605469,
46.913220009605624
],
[
-121.77726745605469,
46.83107318799318
]
]
]
}
}
]
}
app.component.html
<div class="map"
leaflet
[leafletLayers]="layers"
[leafletFitBounds]="fitBounds"></div>
<div class="form-group">
<select [(ngModel)]="selected" class="form-control" id="selectRegion" [value]="clicked" (change)="onChange()">
<option *ngFor="let region of regions">{{ region }}</option>
</select>
</div>
app.component.ts
import {Component, NgZone, OnInit} from '@angular/core';
import { HttpClient } from '@angular/common/http';
import * as L from 'leaflet';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
layers: L.Layer[];
fitBounds = [[46.67, -122.25], [47.01, -121.302]];
regions = [];
clicked = '';
selected = '';
constructor(private http: HttpClient, private zone: NgZone) { }
ngOnInit() {
// read in geojson of poly
this.http.get<any>('/assets/polygons.geojson')
.subscribe(poly => {
const tileLayer = L.tileLayer('https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_nolabels/{z}/{x}/{y}.png', {
subdomains: 'abcd',
maxZoom: 19
});
const polyLayer = L.geoJSON(poly, {
onEachFeature: this.onEachFeature.bind(this)
});
this.layers = [ tileLayer, polyLayer ];
});
}
// loop through each feature of polyLayer
onEachFeature(feature, layer) {
this.zone.run(() => {
// push polygon names to regions array
this.regions.push(feature.properties.name);
layer.on('click', <LeafletMouseEvent> (e) => {
this.zone.run(() => {
this.fitBounds = e.target.getBounds();
this.clicked = e.target.feature.properties.name;
});
});
});
}
onChange() {
console.log(this.selected);
}
}
I was able to solve this by first initializing polyLayer
as a null attribute at the top of my App Component class before constructor()
. So I updated instances of polyLayer
to this.polyLayer
throughout the rest of the code. With this, I am now able to access the polygons inside onChange()
, filter with eachLayer()
, and fit the bounds of the map:
app.component.ts update
import {Component, NgZone, OnInit} from '@angular/core';
import { HttpClient } from '@angular/common/http';
import * as L from 'leaflet';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
layers: L.Layer[];
fitBounds = [[46.67, -122.25], [47.01, -121.302]];
regions = [];
clicked = '';
selected = '';
polyLayer = null;
constructor(private http: HttpClient, private zone: NgZone) { }
ngOnInit() {
// read in geojson of poly
this.http.get<any>('/assets/polygons.geojson')
.subscribe(poly => {
const tileLayer = L.tileLayer('https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_nolabels/{z}/{x}/{y}.png', {
subdomains: 'abcd',
maxZoom: 19
});
this.polyLayer = L.geoJSON(poly, {
onEachFeature: this.onEachFeature.bind(this)
});
this.layers = [ tileLayer, this.polyLayer ];
});
}
// loop through each feature of polyLayer
onEachFeature(feature, layer) {
this.zone.run(() => {
// push polygon names to regions array
this.regions.push(feature.properties.name);
layer.on('click', <LeafletMouseEvent> (e) => {
this.zone.run(() => {
this.fitBounds = e.target.getBounds();
this.clicked = e.target.feature.properties.name;
});
});
});
}
onChange() {
let that = this;
// console.log(this.polyLayer._layers);
this.polyLayer.eachLayer(function(layer){
if(layer.feature.properties.name === that.selected){
that.fitBounds = layer.getBounds();
}
});
}
}