I've been working through a problem with others on 1) grabbing id's from a GeoJSON, 2) placing those id's into a dropdown menu on a Leaflet map, 3) and enabling functionality to allow the user to choose an id, after which the map will filter and zoom to the chosen location.
We've got the control,
L.FeatureSelect = L.Control.extend({
options: {
position: 'topright',
title: 'Name, Clear',
lookupProperty: 'name',
lookupInFeatureProperties: true
},
initialize: function (features, options) {
this.featureCollection = features;
L.Util.setOptions(this, options);
},
onAdd: function(map) {
this.div = L.DomUtil.create('div', 'leaflet-featureselect-container');
this.select = L.DomUtil.create('select', 'leaflet-featureselect', this.div);
var content = '';
if (this.options.title.length > 0 ) {
content += '';
}
if (this.options.lookupInFeatureProperties) {
for (var i = 0; i < this.featureCollection.features.length; i++) {
content += '';
}
}
else {
for (var i = 0; i < this.featureCollection.features.length; i++) {
content += '';
}
};
this.select.innerHTML = content;
this.select.onmousedown = L.DomEvent.stopPropagation;
return this.div;
},
on: function(type,handler) {
if (type == 'change'){
this.onChange = handler;
L.DomEvent.addListener(this.select, 'change', this._onChange, this);
} else {
console.log('FeatureSelect - cannot handle ' + type + ' events.')
}
},
_onChange: function(e) {
var selectedItemKey = this.select.options[this.select.selectedIndex].value;
if (this.options.lookupInFeatureProperties) {
for (var i = 0; i < this.featureCollection.features.length; i++) {
if (this.featureCollection.features[i].properties[this.options.lookupProperty] == selectedItemKey) {
e.feature = this.featureCollection.features[i];
break;
}
}
}
else {
for (var i = 0; i < this.featureCollection.features.length; i++) {
if (this.featureCollection.features[i][this.options.lookupProperty] == selectedItemKey) {
e.feature = this.featureCollection.features[i];
break;
}
}
}
this.onChange(e);
}
});
L.featureSelect = function(features, options) {
return new L.FeatureSelect(features, options);
};
And the map, which clears content when choosing the title (Name, Clear) using data from this location.
var baseLayer = L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png',{attribution: 'Tiles © CartoDB'});
var map = L.map("map",{layers: [baseLayer], center: [-23.88, -62.75], zoom: 4});
$.getJSON( "world.geo.json")
.done(function(data) {
var select = L.featureSelect(data);
select.addTo(map);
select.on('change', function(e) {
if (e.feature === undefined) {
return;
}
var feature = L.geoJson(e.feature);
if (this.previousFeature != null) {
map.removeLayer(this.previousFeature);
}
this.previousFeature = feature;
map.addLayer(feature);
map.fitBounds(feature.getBounds());
});
});
The problem is that I'd like to feed the script a geojson with many non unique lookupProperty
values. i.e. a polygon layer with many Brazil or many Canada values. Currently, the script will return all values under the "lookupProperty", meaning many duplicates end up in the dropdown menu.
How may I grab and place all non unique id's into dropdown menu array?
From my understanding, if the dropdown menu has all non unique id's, when a user selects the unique id, all features with that property value will be filtered and displayed on the map, which is what we want.
Answer
First it has to be noted that control mentioned in the question is modification of Leaflet.CountrySelect plugin, written by Anika S. Halota.
To achieve unique lookup keys in menu, the following changes were made to control:
- At the load time (
onAdd
method) all lookup keys are stored into internal array where they are sorted and made unigue. - Internal method
_sortUnique
was added to get sorted and unique lookup keys. - When features are selected to be displayed (
_onChange
method), loop goes through all the features to get possible multiple features that correspond to selected lookup value.
L.FeatureSelect = L.Control.extend({
options: {
position: 'topright',
title: 'Name, Clear',
lookupProperty: 'id',
lookupInFeatureProperties: false
},
initialize: function (features, options) {
this.featureCollection = features;
L.Util.setOptions(this, options);
},
onAdd: function(map) {
this.div = L.DomUtil.create('div', 'leaflet-featureselect-container');
this.select = L.DomUtil.create('select', 'leaflet-featureselect', this.div);
var content = '';
this.lookupArray = [];
if (this.options.title.length > 0 ) {
content += '';
}
if (this.options.lookupInFeatureProperties) {
for (var i = 0; i < this.featureCollection.features.length; i++) {
this.lookupArray.push(this.featureCollection.features[i].properties[this.options.lookupProperty]);
}
}
else {
for (var i = 0; i < this.featureCollection.features.length; i++) {
this.lookupArray.push(this.featureCollection.features[i][this.options.lookupProperty]);
}
};
this.lookupArray = this._sortUnique(this.lookupArray);
for (var i = 0; i < this.lookupArray.length; i++) {
content += '';
}
this.select.innerHTML = content;
this.select.onmousedown = L.DomEvent.stopPropagation;
return this.div;
},
on: function(type, handler) {
if (type == 'change'){
this.onChange = handler;
L.DomEvent.addListener(this.select, 'change', this._onChange, this);
} else {
console.log('FeatureSelect - cannot handle ' + type + ' events.')
}
},
_onChange: function(e) {
e.features = [];
var selectedItemKey = this.select.options[this.select.selectedIndex].value;
if (this.options.lookupInFeatureProperties) {
for (var i = 0; i < this.featureCollection.features.length; i++) {
if (this.featureCollection.features[i].properties[this.options.lookupProperty] == selectedItemKey) {
e.features.push(this.featureCollection.features[i]);
}
}
}
else {
for (var i = 0; i < this.featureCollection.features.length; i++) {
if (this.featureCollection.features[i][this.options.lookupProperty] == selectedItemKey) {
e.features.push(this.featureCollection.features[i]);
}
}
}
this.onChange(e);
},
_sortUnique: function(array) {
array.sort();
var last_i;
for (var i = 0; i < array.length; i++)
if ((last_i = array.lastIndexOf(array[i])) !== i) {
array.splice(i + 1, last_i - i);
}
return array;
}
});
L.featureSelect = function(features, options) {
return new L.FeatureSelect(features, options);
};
Since multiple selected features are now possible, adding and removing is done through L.featureGroup
:
$.getJSON( "world.geo.json")
.done(function(data) {
var select = L.featureSelect(data);
select.addTo(map);
select.on('change', function(e) {
if (this.previousFeatures != null) {
map.removeLayer(this.previousFeatures);
}
if (e.features === undefined) {
return;
}
this.previousFeatures = L.featureGroup();
for (var i = 0; i < e.features.length; i++) {
this.previousFeatures.addLayer(L.geoJson(e.features[i]));
}
map.addLayer(this.previousFeatures);
map.fitBounds(this.previousFeatures.getBounds());
});
});
No comments:
Post a Comment