Sort order of layers in Leaflet layer control - javascript

I have created a leaflet map to display some geoJSON data in separate vertical layers.
It works fine, except that the layer control will list the options out of order, i.e. "layer 0, layer 2, layer 1" as opposed to "layer 0, layer 1, layer 2". The data is loaded through AJAX calls, and so the layers are added to the control in the order which the calls complete, i.e. basically random.
According to the documentation for the layer control [ https://leafletjs.com/reference-1.3.0.html#control-layers ], if sortLayers is set to true, the default is to sort the layers by name, but that seems to not be happening.
So, I have tried passing in a sort function explicitly, but the doc does not give an example of what one should look like.
When my code looks like this:
var layerControlOptions = {
hideSingleBase : true,
autoZIndex : true,
sortLayers: true}
var layerControl = L.control.layers(null, null, layerControlOptions).addTo(map);
The control looks like this:
layer control with sortLayers: true and no sortFunction being defined
My best guess at writing my own sorting function:
var layerControlOptions = {
hideSingleBase : true,
autoZIndex : true,
sortLayers: true,
sortFunction: function(layerA, layerB, nameA, nameB){return
[nameA,nameB].sort();}}
var layerControl = L.control.layers(null, null,
layerControlOptions).addTo(map);
With this, the layer names appear in the same order as when no function is given. Not sure if I am writing it incorrectly, or it is simply equivalent to the default, which is returning the wrong order for some reason.
Seems like the same issue here: Leaflet Layer Control: Ordering
which has no accepted answer and makes no mention of the layer control options. I did try the solution which is proposed here, but it did not fix the problem either.
Any help on this would be greatly appreciated, thank you.

You may look at this solution to order layers in the controls (it does not order layers). I've mainly borrowed code from Leaflet specs code;
If you have not problem to sort things in the control but you have issue related to loading JSON/GeoJSON (hence layer too) in a particular order, you may want to combine fetch with Promise.all but it's not anymore about Leaflet but about JavaScript knowledge (look at this page to try to grasp fetch + Promise.all)

You can add the layers as you normally would and then sort them with a function that removes labels and re-add them as sorted.
function sortLabels() {
var controlLayers = {}
layerControl._layers.forEach(function(x) {
if (x.overlay) {
controlLayers[x.name] = x.layer
}
});
names = Object.keys(controlLayers).sort()
//remove and add sorted layernames
names.forEach(x => layerControl.removeLayer(controlLayers[x]))
names.forEach(x => layerControl.addOverlay(controlLayers[x], x))
}
var baseMaps = { "OpenStreetMap": tile_layer_osm, "ESRI World Imagery" : esri_satelite };
var overlayMaps = {nameC: layerC, nameA: layerA, nameB: layerB}
var layerControl = L.control.layers(baseMaps, overlayMaps, null, {collapsed: false}).addTo(map);
sortLabels()

Related

Structure of MarkerList array in Wikitude Javascript SDK

WikiTude Augmented reality JavaScript SDK has an array of markers named markerList. I want to know what is the structure of the array (It is no in the documentation). For example, I want to know how to access the enabled property of a geoObject in markerlist.
Example, can it be like,
World.markerList[i].enabled=false
What the the structure and sub-structures of the markerlist array?
Can I do like the following. Please help. I am really stuck here. I don't want to reload and recreate POIS every time I change my custom slider. I want the GeoOjects to activate and deactivate, so that it would prevent flickering.
marker.js
// Labels and properties described here.
this.markerObject = new AR.GeoObject(markerLocation, {
drawables: {
cam: [this.markerDrawable_idle,this.markerDrawable_selected,this.titleLabel,this.distanceUpdate,this.descriptionLabel],
enabled : true,
indicator: this.directionIndicatorDrawable,
radar: this.radardrawables
}
});
this.markerObject.changeVisibilitysetter= function(markerObject) {
this.markerObject.enabled = false;
}
and call the function from fromwebservice like
World.changeVisibilitysetter(marerList[i].markerObject);
The elements in the array are of type 'Marker'. Such a Marker does not have a enabled property. You need to extend the class with such a setter and in the implementation, access the underlying AR.GeoObject and change it's enabled property.

Leaflet control Joomla issue

I've created a Joomla Component and i have a Leaflet map in the component window.
I've used Leaflet with Omnivore plugin to add GPX and KML to my map and I used the Leaflet controls to allow to add and remove the layers.
I've tested the controls in a clean joomla development installation and the controls are OK, as seen in the first image
enter image description here
When I use the component in my Joomla site che controls are not OK, there are some dirty entry as seen in the second figures
enter image description here
I think this is because of the templates and some script that interfere with Leaflet but I can't fix it.
The joomla versions are the same, the template no, the joomla site use gantry.
This is the function I used to populate the map:
function showRouteTracks(tracce, baseURI, popup=false, enableLayers=true, enableElevation=false){
var layerControl = new Array();
for (var i = 0; i < tracce.length; i++) {
var customLayer = L.geoJson(null, {
style: getStyle(i)
});
if(tracce[i][3]=='GPX'){
var layer = omnivore.gpx(baseURI+tracce[i][2], null, customLayer).on('ready', function() {
elevation(enableElevation,layer);
});
if(popup){
link=''+tracce[i][5]+''
layer.bindPopup(tracce[i][0]+"➝"+tracce[i][1]+"<br/>"+link);
}
lvrtMap.addLayer(layer);
layerControl[tracce[i][0]+"➝"+tracce[i][1]]=layer;
}
if(tracce[i][3]=='KML'){
var layer = omnivore.kml(baseURI+tracce[i][2], null, customLayer).on('ready', function() {
elevation(enableElevation,layer);
});
if(popup){
link=''+tracce[i][5]+''
layer.bindPopup(tracce[i][0]+"➝"+tracce[i][1]+"<br/>"+link);
}
lvrtMap.addLayer(layer);
layerControl[tracce[i][0]+"➝"+tracce[i][1]]=layer;
}
}
if(!enableLayers)
layerControl=null;
if(enableElevation)
L.control.layers(lvrtMapLayers,layerControl,{'position':'bottomright'}).addTo(lvrtMap);
else
L.control.layers(lvrtMapLayers,layerControl).addTo(lvrtMap);
}
Currently you're creating an array to store the title/layer items:
var layerControl = new Array();
But L.Control.Layers takes object literals as baselayer/overlay parameters:
var baseLayers = {
"Mapbox": mapbox,
"OpenStreetMap": osm
};
var overlays = {
"Marker": marker,
"Roads": roadsLayer
};
L.control.layers(baseLayers, overlays).addTo(map);
So you should use an object literal:
var layerControl = {};
You can add items the same way as you did before:
layerControl['MyTitle'] = myLayerInstance;
I'll bet you'll have no problem then. What happening now is that your trying to assign string keys to items in an array, which isn't supported (even supposed to work but that aside). A javascript array can only have numeric keys and nothing else.
That it works for you with a clean install and not in your production setup is that probably in production you have a javascript library/framework loaded which adds methods/properties to javascript's native array prototype and they are enumerable. Thus when the layercontrol instance iterates the array it also finds these extra methods/properties and tries to add them to the layercontrol.
Just use a object literal: {} not an Array, you'll be fine, good luck.
EDIT: Got curious and did some digging. As it turns out this is caused by Mootools and then i ran into this question: Looping through empty javascript array returns array object functions which gives some explanation and some other solutions but it's best if you just use a object literal because at the moment you're kind of abusing Array.

Fire Event to Open CartoDB/Leaflet Marker

I've got a pretty specific question that I'm not really expecting a direct answer to, but any guidance will be helpful.
Simple and plain, I want to programmatically fire a click event on a marker positioned on a Leaflet map, powered by CartoDB. I have a single layer on the map that contains markers, and each marker has click events associated with them. So, essentially, I just need to find the marker and fire the click event on it.
For context, Mapbox actually does exactly this, but unfortunately I can not use Mapbox for this particular implementation:
https://www.mapbox.com/mapbox.js/example/v1.0.0/open-popup-externally/
I'm open to suggestions, but preferably, I'd like to do something similar to the code in the link above -- interrogate either Leaflet or CartoDB via javascript to find and access the marker via custom properties/lat-lng/??. I figure it would be simple enough to go from there.
Another way could be to hook an event when the markers are created, store them in a hash, then access that storage when I need to do my manual click. However, I don't know of any such event that exists, and I can't locate documentation that lists supported events.
Since I'm not creating the markers myself, and can not (for reasons), storing them as I add them to the map is not an option here.
I assume since Mapbox is doing it, there must be some hook, but I can't find any valuable documentation to point me in the right direction.
Any suggestions or pointers?
To anyone who stumbles upon this, I've got a workable solution for my particular case. This call will do it for you:
layer.trigger 'featureClick', event, latlng, pos, data, layer_count
Essentially, you'll want to grab the layer in question and trigger the click, passing the appropriate data:
event: this can be null, since there is no event
latlng: the lat/long position
data: an object like { cartodb_id: 123 }. The cartodb_id is required.
layer_count: the index of your layer (probably 0)
To grab the latlng and the cartodb_id here, you'll probably need to do what I did -- query for it:
function openMarker(layer, my_object_id) {
vars = { database_id: my_object_id };
opts = { format: 'geojson' };
query = "SELECT * FROM my_table WHERE my_object_id = {{ my_object_id }}"
sql = new cartodb.SQL({user: my_user_id});
sql.execute(query, vars, opts).done(function(data) {
if (data.features != undefined && data.features.length == 1)
row = data.features[0];
latlng = [ row.geometry.coordinates[1], row.geometry.coordinates[0] ];
layer.trigger('featureClick', null, latlng, null, { cartodb_id: row.properties.cartodb_id }, 0);
});
}
Then, you can just call open_marker, passing the layer you wish to open the marker on and your object identifier. As long as that's in your cartodb database, you can grab it. Of course, the query can be adjusted to suit your means.
For my particular implementation, I had to use a setTimeout call to get the flow of control right, as I do my calls on page load. But if you're calling after the page has loaded, should be no problem.
So, simple enough way to open a marker given a local identifier. Hope this helps someone else!
Kudos for the inspiration for this solution go to:
https://gist.github.com/javisantana/7b817fda1e7511c451c7#file-index-html-L39

Styling a geoJSON Feature Layer in MapBox

I just started playing with MapBox and am running into a confusing issue. I'm creating a map with a geoJSON layer using this code:
var map = L.mapbox.map('map', '<MapBoxID>');
var zipLayer = L.mapbox.featureLayer('data/blah.json');
zipLayer.addTo(map);
zipLayer.setStyle({color: 'red'});
The map appears and shows the geoJSON, but it ignores the styling. When I copy that last line into the JS console in my browser, it works fine, though.
What am I missing here? Also, I've tried at least a dozen different ways of including the style in the options directly in the featureLayer() call, but nothing has worked. How do I specify the style when creating the feature layer?
I'm guessing a bit here, since I don't know the Mapbox JS very well, but it sounds a lot like an async error. Strangely, I don't see anything in the Mapbox or Leaflet APIs about a callback for this function. But, you can pass straight GeoJSON to featureLayer(), so I'd suggest using jQuery (or your XHR library of choice) to grab the data:
var map = L.mapbox.map('map', '<MapBoxID>');
var zipLayer;
$.getJSON('data/blah.json', function(data) {
zipLayer = L.mapbox.featureLayer(data);
zipLayer.addTo(map);
zipLayer.setStyle({color: 'red'});
});
Hopefully that'll do the trick.
I would go the route of using the built-in featureLayer function, then listening for it to be ready. This should help get you pointed in the right direction:
var featureLayer = L.mapbox.featureLayer()
.loadURL('/example-single.geojson')
.on('ready', function(layer) {
this.eachLayer(function(marker) {
// See the following for styling hints:
// https://help.github.com/articles/mapping-geojson-files-on-github#styling-features
marker.setIcon(L.mapbox.marker.icon({
'marker-color': '#CC0000'
}));
});
})
.addTo(map);
Have you tried adding the zipLayer after setting the style?

set the data to dojo data grid

I am working with Dojo, and I have a small requirement. I am using a store to create a Memory. I have a hardcoded list so that I can set this list to a Dojo data grid store and thereby perform some rowclick functions. My issue is that I am not able to set the store, and I am getting an error,
this.headerContentNode.firstChild is null. Please can anybody help me out?
the code snippet....
postCreate : function () {
//publist, sublist etc
this._handleLogDetails();
},
//methods...
_handleLogDetails : function(){
alert("hello...h..");
var theGreatestTeamOfAllTime = [ {
"jobName":"12",
"jobId":"Jim Kelly",
"status":"QB",
"timeStamp":"0"
},
{
"jobName":"1",
"jobId":" Kelly",
"status":"B",
"timeStamp":"10"
}
];
var clientJobStore = this.jobModel.getLogStore();
clientJobStore.setData(theGreatestTeamOfAllTime);
var thisData = new ObjectStore({objectStore: clientJobStore});
this.dapJobStatusGrid1.setStore(thisData); // attach point of dojo data grid, getting an error at this point...
Hard to say exactly what the issue is here without seeing how the grid is configured, but this is likely caused by one of the following:
You didn't call startup() on your grid.
When constructing your grid, you did not provide a DOM node for the grid.
For example, perhaps try something like the following, where the second argument is the DOM node the grid should be attached to.
var options = {// some options};
var foo = new DataGrid(options, someContainer.domNode);
foo.startup();

Categories

Resources