Friday 31 March 2017

javascript - Adding feature property values to filter drop down option in Leaflet, instead of keys?



How would I convert this function to store and utilize (feature.property.id) json values instead of keys in the dropdown menu within a leaflet map?



















with the corresponding JavaScript,


L.CountrySelect = {};

L.CountrySelect.countries = {"Afghanistan":{"type":"Feature","id":"AFG","properties":{"name":"Afghanistan"},"geometry":{"type":"Polygon","coordinates":[[[61.210817,35.650072],[62.230651,35.270664],[62.984662,35.404041],[63.193538,35.857166],[63.982896,36.007957],[64.546479,36.312073],[64.746105,37.111818],[65.588948,37.305217],[65.745631,37.661164],[66.217385,37.39379],[66.518607,37.362784],[67.075782,37.356144],[67.83,37.144994],[68.135562,37.023115],[68.859446,37.344336],[69.196273,37.151144],[69.518785,37.608997],[70.116578,37.588223],[70.270574,37.735165],[70.376304,38.138396],[70.806821,38.486282],[71.348131,38.258905],[71.239404,37.953265],[71.541918,37.905774],[71.448693,37.065645],[71.844638,36.738171],[72.193041,36.948288],[72.63689,37.047558],[73.260056,37.495257],[73.948696,37.421566],[74.980002,37.41999],[75.158028,37.133031],[74.575893,37.020841],[74.067552,36.836176],[72.920025,36.720007],[71.846292,36.509942],[71.262348,36.074388],[71.498768,35.650563],[71.613076,35.153203],[71.115019,34.733126],[71.156773,34.348911],[70.881803,33.988856],[69.930543,34.02012],[70.323594,33.358533],[69.687147,33.105499],[69.262522,32.501944],[69.317764,31.901412],[68.926677,31.620189],[68.556932,31.71331],[67.792689,31.58293],[67.683394,31.303154],[66.938891,31.304911],[66.381458,30.738899],[66.346473,29.887943],[65.046862,29.472181],[64.350419,29.560031],[64.148002,29.340819],[63.550261,29.468331],[62.549857,29.318572],[60.874248,29.829239],[61.781222,30.73585],[61.699314,31.379506],[60.941945,31.548075],[60.863655,32.18292],[60.536078,32.981269],[60.9637,33.528832],[60.52843,33.676446],[60.803193,34.404102],[61.210817,35.650072]]]}},"Angola":{"type":"Feature","id":"AGO","properties":{"name":"Angola"},"geometry":{"type":"MultiPolygon","coordinates":[[[[16.326528,-5.87747],[16.57318,-6.622645],[16.860191,-7.222298],[17.089996,-7.545689],[17.47297,-8.068551],[18.134222,-7.987678],[18.464176,-7.847014],[19.016752,-7.988246],[19.166613,-7.738184],[19.417502,-7.155429],[20.037723,-7.116361],[20.091622,-6.94309],[20.601823,-6.939318],[20.514748,-7.299606],[21.728111,-7.290872],[21.746456,-7.920085],[21.949131,-8.305901],[21.801801,-8.908707],[21.875182,-9.523708],[22.208753,-9.894796],[22.155268,-11.084801],[22.402798,-10.993075],[22.837345,-11.017622],[23.456791,-10.867863],[23.912215,-10.926826],[24.017894,-11.237298],[23.904154,-11.722282],[24.079905,-12.191297],[23.930922,-12.565848],[24.016137,-12.911046],[21.933886,-12.898437],[21.887843,-16.08031],[22.562478,-16.898451],[23.215048,-17.523116],[21.377176,-17.930636],[18.956187,-17.789095],[18.263309,-17.309951],[14.209707,-17.353101],[14.058501,-17.423381],[13.462362,-16.971212],[12.814081,-16.941343],[12.215461,-17.111668],[11.734199,-17.301889],[11.640096,-16.673142],[11.778537,-15.793816],[12.123581,-14.878316],[12.175619,-14.449144],[12.500095,-13.5477],[12.738479,-13.137906],[13.312914,-12.48363],[13.633721,-12.038645],[13.738728,-11.297863],[13.686379,-10.731076],[13.387328,-10.373578],[13.120988,-9.766897],[12.87537,-9.166934],[12.929061,-8.959091],[13.236433,-8.562629],[12.93304,-7.596539],[12.728298,-6.927122],[12.227347,-6.294448],[12.322432,-6.100092],[12.735171,-5.965682],[13.024869,-5.984389],[13.375597,-5.864241],[16.326528,-5.87747]]],[[[12.436688,-5.684304],[12.182337,-5.789931],[11.914963,-5.037987],[12.318608,-4.60623],[12.62076,-4.438023],[12.995517,-4.781103],[12.631612,-4.991271],[12.468004,-5.248362],[12.436688,-5.684304]]]]}},"Albania":{"type":"Feature","id":"ALB","properties":{"name":"Albania"},"geometry":{"type":"Polygon","coordinates":[[[20.590247,41.855404],[20.463175,41.515089],[20.605182,41.086226],[21.02004,40.842727],[20.99999,40.580004],[20.674997,40.435],[20.615,40.110007],[20.150016,39.624998],[19.98,39.694993],[19.960002,39.915006],[19.406082,40.250773],[19.319059,40.72723],[19.40355,41.409566],[19.540027,41.719986],[19.371769,41.877548],[19.304486,42.195745],[19.738051,42.688247],[19.801613,42.500093],[20.0707,42.58863],[20.283755,42.32026],[20.52295,42.21787],[20.590247,41.855404]]]}}

L.CountrySelect = L.Control.extend({
options: {
position: 'topright',
title: 'Country',
exclude: [],
include: [],
countries: L.CountrySelect.countries,
},

onAdd: function(map) {
this.div = L.DomUtil.create('div','leaflet-countryselect-container');
this.select = L.DomUtil.create('select','leaflet-countryselect',this.div);
var content = '';

if (this.options.title.length > 0 ){
content += '';
}

var countries = (Array.isArray(this.options.include) && this.options.include.length > 0) ? this.options.include : this.options.countries;


var countryKeys = Object.keys(countries).sort();
for (i in countryKeys){
if (this.options.exclude.indexOf(countryKeys[i]) == -1){
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 if (type == 'click'){ //don't need this here probably, but for convenience?
this.onClick = handler;

L.DomEvent.addListener(this.select,'click',this.onClick,this);
} else {
console.log('CountrySelect - cannot handle '+type+' events.')
}
},
_onChange: function(e) {
var selectedCountry = this.select.options[this.select.selectedIndex].value;
e.feature = this.options.countries[selectedCountry];
this.onChange(e);
}

});

L.countrySelect = function(id,options){
return new L.CountrySelect(id,options);
};

Mainly this function is pulling the super key county name, but this doesn't work well with a general JSON/GeoJSONs (please correct me if I'm wrong). I'd like to add feature.propoerty.id values to the dropdown list and have that filter/zoom to associated county based on that value instead of the super key.


Here's a demo: http://ahalota.github.io/Leaflet.CountrySelect/demo.html


I've been scratching my head for a couple hours on this and I'm pretty new to JavaScript.


More information on this function can be found here -> https://github.com/ahalota/Leaflet.CountrySelect




Answer



First it has to be noted that solution below is modification of Leaflet.CountrySelect plugin, written by Anika S. Halota.


Requirement that control would work with any JSON file is to broad, since file must have defined structure that plugin can work with. Since file has to include geometries, the obvious candidate is then GeoJSON feature collection, where each feature has geometry that can be displayed and some id by which it can be identified.


The following changes were made to control:



  1. Control was renamed to L.featureSelect

  2. Input parameter features was added to control. This is input GeoJSON object.

  3. New option lookupProperty was added. It specifies feature property name to be used for lookup. Deafult value is 'id'.

  4. New option lookupInFeatureProperties was added. If set to true, feature property specified with lookupProperty will be searched for in properties feature property.

  5. Options exclude, include and countries were removed, sonce thay are not needed any more.


  6. Lookup of selected feature is done by looping through the features in feature collection until the feature with desired lookup value in specified property is found.


So here is how new control looks like:


L.FeatureSelect = L.Control.extend({ 
options: {
position: 'topright',
title: 'Feature ID',
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 = '';
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);
};

Below is example of using the new control with original countries file world.geo.json, available at https://github.com/johan/world.geo.json (jQuery is used to load GeoJSON file from local server):



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());
});
});


In the above example feature property id would be used to select countries by id. Since each feature (country) has also country name stored properties property under name property, it could also be used to select countries by name by defining control like this:


var select = L.featureSelect(data, {
lookupProperty: 'name',
lookupInFeatureProperties: true,
title: 'Country'
});

No comments:

Post a Comment

arcpy - Changing output name when exporting data driven pages to JPG?

Is there a way to save the output JPG, changing the output file name to the page name, instead of page number? I mean changing the script fo...