I am trying to render 5 sets of data on a map, one at a time, selectable by the user.
I'm using layers to store each set and I can switch between them - works well.
I want to be able to filter each set based on user input. I was using L.layerGroup but tried reformatting my data at load time to GeoJSON so I could use the built in filter feature. That only seems to work if I remove the layers and re-add each marker.
Example here: http://jsfiddle.net/callum/5sunB/
For 1000 points, on my system it takes about 200ms which is too slow.
Is there a way to only show/hide the markers already in a layer based on a filter function?
Thanks.
Not really. You can save your markers and then it's faster to just add them to the map than it is to re-create them and then add them.
For example,
var markers = new Array();
...
// create a marker using some unique id and save it to the markers array
var i = feature.properties.id;
markers[i] = L.circleMarker(latlng, {
....
});
return markers[i];
Then when you filter (on click or other event), clear the marker layer and re-add the (saved) markers that match:
if (map.hasLayer(marker_layer)) {
map.removeLayer(marker_layer);
}
marker_layer = new L.featureGroup();
// filter criteria here
for (var i = 0; i < src_data.features.length; i++) {
var feature = src_data.features[i];
if (feature.properties.hits >= n1 && feature.properties.hits <= n2) {
marker_layer.addLayer(markers[i]);
}
}
marker_layer.addTo(map);
I updated your JSFiddle here: http://jsfiddle.net/5sunB/5/ so you can compare re-creating the filtered markers vs just re-adding them.
Related
I want to display multiple marker infoWindows to mark different routes. However, only the last created one is being displayed.
My (relevant) marker creation code is:-
if (routes.size() < 3) {
Polyline roadOverlay = new Polyline();
roadOverlay.setColor(polyClr.get(routes.size()));
roadOverlay.setWidth(5f);
roadOverlay.setPoints(waypoints);
// Add Route Marker
Marker m = new Marker(map);
double d = roadOverlay.getDistance()*5/8000;
GeoPoint midpt = waypoints.get((int)(waypoints.size()/2));
m.setTitle(rteDesc.get(routes.size())+" - "+String.format("%.2f miles",d));
m.setSnippet("Tap to Save");
m.setIcon(getResources().getDrawable(R.drawable.transparent));
m.setPosition(midpt);
m.showInfoWindow();
rtemkrs.add(m);
routes.add(roadOverlay);
}
and the display code is:-
for (int j = rtemkrs.size()-1; j>=0; j--) {
map.getOverlays().add(rtemkrs.get(j));
}
map.invalidate();
I'm using osmdroid v 6.1.0 and osmbonuspack v 6.6.0
How can I display multiple marker infoWindows?
By default, all markers use one shared view as their InfoWindow. Therefore only the one view can be displayed.
But it is possible to change the behaviour:
You'll need to create a MarkerInfoWindow instance for each marker, for example this his how the default one is created: new MarkerInfoWindow(R.layout.bonuspack_bubble, mMapView);
You'll have to pass the view to the marker by calling marker.setInfoWindow(...) (see method's javadoc) for each marker
Using Leaflet javascript. I would like my "clear button" to do two things...
1) uncheck all the L.Control layers
2) remove the current overlay from the map
I can do the first easily enough using this code:
var checks = document.querySelectorAll('[type = "checkbox"]'), i;
function uncheckBoxes() {
for (i = 0; i < checks.length; ++i) {
checks[i].checked = false;
}
}
The next is a little more tricky. I have tried using the removeLayer() and clearLayers() function but they do not work. I don't see a way in the leaflet documentation to remove an L.control overlayMap layer from the map unless you physically uncheck it yourself.
Any insight into this would be greatly appreciated.
Not exactly sure what is your difficulty in programmatically removing some layers / overlays from your map.
It is normally trivial (map.removeLayer(layer)), and the Layers Control automatically reflects what is happening on the map (in that case, if layer is one of the overlays, its associated checkbox will become unticked).
As for removing all your overlays from map, you just need to keep a reference to those overlays, loop through them and remove them from the map:
var overlays = {
'Name 1': someLayer,
'Name 2': someOtherLayer
};
L.control.layers(null, overlays).addTo(map);
// Whenever you want to remove all overlays:
for (var name in overlays) {
map.removeLayer(overlays[name]);
}
Demo: https://jsfiddle.net/3v7hd2vx/357/
Another developer created our original map but I'm tasked with making some changes. One of these is making sure the activated marker is brought to the front when clicked on (where it is partially overlapped by other markers).
The developers have used mapbox 2.2.2.
I have looked at leafletjs's docs, have followed some instructions on other posted solutions (e.g. solution one and solution two). Neither of these makes any difference.
Examining the marker in Chrome's console I can see the value of options.zIndexOffset is being set (10000 in my test case). I've even set _zIndex to an artificially high value and can see that reflected in the marker's data structure. But visually nothing is changing.
This is how the map is set up initially. All features are from a single geojson feed:
L.mapbox.accessToken = '<access token here>';
var map = L.mapbox.map('map', 'map.id', {
}).setView([37.8, -96], 3);
var jsonFeed, jsonFeedURL;
var featureLayer = L.mapbox.featureLayer()
.addTo(map)
.setFilter(function (f) {
return false;
});
$.getJSON(jsonFeedURL, function (json) {
jsonFeed = json;
jsonFeedOld = json;
// Load all the map features from our json file
featureLayer.setGeoJSON(jsonFeed);
}).done(function(e) {
// Once the json feed has loaded via AJAX, check to see if
// we should show a default view
mapControl.activateInitialItem();
});
Below is a snippet of how I had tried setting values to change the z-index. When a visual marker on the featureLayer is clicked, 'activateMarker' is called:
featureLayer.on('click', function (e) {
mapControl.activateMarker(e);
});
The GEOjson feed has urls for the icons to show, and the active marker icon is switched to an alternative version (which is also larger). When the active feature is a single Point I've tried to set values for the marker (lines commented out, some of the various things I've tried!)
activateMarker: function (e) {
var marker = e.layer;
var feature = e.layer.feature;
this.resetMarkers();
if (feature.properties.hasOwnProperty('icon')) {
feature.properties.icon['oldIcon'] = feature.properties.icon['iconUrl'];
feature.properties.icon['iconUrl'] = feature.properties.icon['iconActive'];
feature.properties.icon['oldIconSize'] = feature.properties.icon['iconSize'];
feature.properties.icon['iconSize'] = feature.properties.icon['iconSizeActive'];
}
if (feature.geometry.type == 'Point') {
marker.setZIndexOffset(10001);
marker.addTo(featureLayer);
}
//featureLayer.setGeoJSON(jsonFeed);
}
Any advice would be greatly appreciated! I'm at the point where I don't know what else to try (and that's saying something).
What probably happens is that you just flush your markers with the last call to .setGeoJSON():
If the layer already has features, they are replaced with the new features.
You correctly adjust the GeoJSON data related to your icon, so that when re-created, your featureLayer can use the new values to show a new icon (depending on how you configured featureLayer).
But anything you changed directly on the marker is lost, as the marker is removed and replaced by a new one, re-built from the GeoJSON data.
The "cleanest" way would probably be to avoid re-creating all features at every click.
Another way could be to also change something else in your GeoJSON data that tells featureLayer to build your new marker (through the pointToLayer option) with a different zIndexOffset option.
I'm building an online version of a boardgame using Leaflet as the viewport.
My source: https://github.com/edenLOL/gotta-chug-em-all/tree/master
What I'm trying to achieve, is a way to spiderfy/spread out markers that land on the same square as each other, including when they move past each other.
I'm using https://github.com/jawj/OverlappingMarkerSpiderfier-Leaflet to Spiderfy the markers, which appears to work when clicking the markers.
However, I need these markers to be spiderfied always, without requiring a click event. So I set keepSpiderfied: true for the spiderfier object (oms).
var options = { //**spiderfier
keepSpiderfied: true,
nearbyDistance: 120
};
oms = new OverlappingMarkerSpiderfier(map, options); //**spiderfier
But this doesn't work. I can't seem to load the markers in a spiderfied state, it always requires a click to spiderfy them.
For context, markers are generated dynamically during a prompt before the user sees the map.
('#playerIcons > table > tbody > tr > td').on('click', function(){
pokemonSelected = $(this).attr('class');
if ( !$(this).hasClass('pokemonSelected') ){
playerArray[pokeCounter].pokemon = ''+ pokemonSelected +'';
playerArray[pokeCounter].marker = L.marker([playerArray[pokeCounter].coords[0], playerArray[pokeCounter].coords[1]], {
icon: window[pokemonSelected]
}).addTo(map);
oms.addMarker(playerArray[pokeCounter].marker) //**spiderfier
pokeCounter += 1;
} else {
return false;
};
});
I've also tried using Leaflet/Leaflet.markercluster. This works to an extent, and spiderfies the Markers at defined zoom levels, however the markers don't move, and stay stacked on top of each other.
What am I missing here that could be causing these issues? I don't mind using either Spiderfier or MarkerCluster, as both should be able to provide the solution I'm looking for once either issue is fixed.
Spiderfier: Markers need to spiderfy automatically
MarkerCluster: Markers don't physically move when spiderfying out of cluster
Note: If you decide to open index.html, be aware the Pokemon theme song will autoplay in the background on page load (until the map is drawn), so check your volume :)
I solve the problem like this, using fire('click') аfrom leaflet.
scope.spiderMarker - the marker, i want to spidrefy. In your case i think its playerArray[pokeCounter].marker
try{
scope.spiderMarker.fire('click')}
catch (e) {
console.log(e);
};
After dojo drag and drop, once the page is submitted, I have to save the position of every item that has been placed into "targetZone". How can we save the position?
Eugen answered it here :
Dojo Drag and drop: how to retrieve order of items?
That would be the right way. If you look at the link above, you can save the resulting "orderedDataItems" object as a JSON ...
Look at the following function. It saves our DND "Lightbox" (dojo.dnd.source) to a JSON.
_it is the current raw dnd item
_it.data.item contains all your stuff you need to keep
in our case _it.data.item.label keeps the customized nodes (pictures, video, docs) as a string, we can use later to dojo.place it
it is the dnd item you want to save without dom nodes
E.g. if you drop items from a dijit tree to a arbitrary dojo dnd source / target:
_RAM or _S in our data.item we made before needs to be overwritten.
LBtoJson: function(){
var that = this;
var orderedLBitems = this.dndSource.getAllNodes().map(function(node){
var _it = that.dndSource.getItem(node.id);
var it = { data:{ item:{} }, label:'', type:'' };
if((_it.data.item._RAM)){_it.data.item._RAM={}}
if((_it.data.item._S)){_it.data.item._S={}}
it.data.item = dojo.clone(_it.data.item);
it.label = it.data.item.label[0]||it.data.item.label;
it.type = _it.type;
console.log( it );
return it;
});
var LBjson = dojo.toJson(orderedLBitems);
return LBjson;
}
By calling getAllNodes(), you'll receive a list of nodes in the order they are shown. So if you wanted to save a list in a specific order, you could do something similar to this:
var data;
var nodes = dndSrc.getAllNodes();
for(var i; i < nodes.length; i++)
{
data.push({id: nodes[i].id, order: i});
}
For more information about Dojo DnD regarding data submission, check out this article about DnD and Form Submission: http://www.chrisweldon.net/2009/05/09/dojo-drag-n-drop-and-form-submission