Refresh marker clusters after GeoJSON layer has changed - leaflet

I am setting up a GeoJSON layer and on top of it a MarkerCluster layer
this.itemLayer = L.geoJson(items, layerOptions)
this.clusterLayer = L.markerClusterGroup()
this.clusterLayer.addLayer(this.itemLayer)
this.clusterLayer.addTo(this.map)
Upon update I am doing:
this.itemLayer.clearLayers()
this.itemLayer.addData(newItems)
this.clusterLayer.refreshClusters(this.itemLayer)
But the clusters do not appear, nor do the items in the itemLayer
Solution
this.itemLayer.clearLayers()
this.itemLayer.addData(this.props.items)
this.clusterLayer.clearLayers()
this.clusterLayer.addLayer(this.itemLayer)

Leaflet.markercluster does not keep track of Layer Groups (like your this.itemLayer GeoJSON layer group) unfortunately. When passed a group to clusterLayer.addLayer(), MCG will extract all individual (i.e. non-group) layers from that group, and forget any reference to the group.
See also Leaflet.markercluster issue #647.
Therefore when clearing your group with this.itemLayer.clearLayers(), it effectively removes all children from this.itemLayer, but this.clusterLayer is unaffected.
Similarly when adding data to this.itemLayer, that group creates new child layers, but MCG is unaffected.
Then when calling this.clusterLayer.refreshClusters(this.itemLayer), none of the child layers of this.itemLayer are part of this.clusterLayer, so it ends up with unexpected effect (maybe just doing nothing special).
If you want to change the clustered layers, make sure to remove them from MCG (e.g. simply do this.clusterLayer.clearLayers()), then add back the new layers into it. You could also remove the current MCG and build a new one.

Related

Update leaflet baselayer properties with GoogleMutant

I have a google layer (baselayer) GoogleMutant and want to update its options. I try to
map.remove(google.layer)
//update the POI visibility
google.layer.options.styles.forEach(i=>i.stylers[0].visibility = "off")
map.addLayer(google.layer)
this updates the layer options but the Points of interest are still on the map. Is there any way to update the options and apply them to the baselayer?
Thanks
Here is an example with google mutant https://jsfiddle.net/benderlio/2m4c01w6/10/
There is a button "remove POI", and I want to remove all poi from current layer with leaflet API
Leaflet doesn't work changing settings on the fly. You have to remove the whole object and create a substitute.
Like if you need to change a layer, you have to remove it like:
roadMutant.removeFrom(map); // or
map.removeLayer(roadMutant);
And then create and add the new one:
roadMutant.addTo(map);
I created a fiddle to help the change based on yours. It still have bugs and somewhere to grow, but it's a base...

leaflet - manually cluster markers

I need to manually cluster/uncluster few markers on the map (not automatically by zoom)
is there a way to tell Leaflet.markercluster which markers to cluster manually and not automatically by zoom.
I tried manipulating the L.markerClusterGroup layer internal cluster._gridClusters and cluster._gridUnclustered which holds an array of all the zooms and markers/clusters in each.
but changing the objects seems to do nothing and not represented on map.
a solution example would be:
selectedMarkers = [marker1,marker2,marker3];
map.cluster(selectedMarkers);
map.uncluster(selectedMarkers);
please help.

No setBounds function for Leaflet imageOverlay

I'm reading an imageOverlay URL from an ArcGIS webserver that uses the leaflet getBound() coordinates as part of the URL (we have large maps that are filtered for the current window 'extent'). Apologies for not including the actual path (I'm working with sensitive client data). Eg:
http://myarcgiswebserver.com/MapServer/export/dpi=96&format=png32&bbox=27.119750976562504%2C-31.194007509998823%2C32.39044189453126%2C-29.692824739380754&size=1719%2C434
[bbox] = current imageBounds
When dragging my map the imageOverlay url is updated correctly but my leaflet window is no longer aligned to the imageBound values that were set when first adding the imageOverlay which results in a skewed output (this is my assumption):
The only workaround is to remove the existing imageOverlay and add a new one (which ruins the user experience as the map disappears then reappears each time the window is dragged or zoomed).
Am i approaching this problem incorrectly or would the introduction of a function to update the current imageBounds resolve this? Perhaps not a new function but the expansion of setUrl with additional parameters...?
Many thanks for any feedback...
As #ghybs pointed out, your use case might be better served by using the WMS
interface of your ArcGIS server.
Anyway, you say
The only workaround is to remove the existing imageOverlay and add a new one (which ruins the user experience as the map disappears then reappears each time the window is dragged or zoomed).
Well, that glitch is due to you probably doing something like:
Remove old overlay
Add new overlay
Wait until the image is received from the network
Wait one frame so the new overlay is shown
and instead you should be doing something like:
Add new overlay
Wait until the image is received from the network
Remove old overlay
Wait one frame so the new overlay is shown
The problem is just the async wait and the possible race conditions there, but should be easy to hack together, e.g.:
var activeOverlay = null;
var overlayInRequest = null;
map.on('moveend zoomend', {
// If we are already requesting a new overlay, ignore it.
// This might need some additional debouncing logic to prevent
// lots of concurrent requests
if (overlayInRequest) {
overlayInRequest.off('load', showOverlay);
}
overlayInRequest = L.imageOverlay( computeUrl( map.getBounds() ), myOverlayOptions );
overlayInRequest.on('load', showOverlay);
});
function showOverlay(ev) {
activeOverlay.remove();
activeOverlay = overlayInRequest;
activeOverlay.addTo(map);
overlayInRequest = undefined;
}
If you use an ImageOverlay but change its url dynamically, with a new image that reflects a new bounding box, then indeed that is the reason for the behaviour you describe: you display an image that has been generated using a new bbox, but positioned in the initial bbox, since the image overlay remains at the same geographical position on the map.
Instead, it sounds to me that you should use a TileLayer.WMS.
It would automatically manage the bounding box update for you. You may need to find the correct options to fit your service provider required URL syntax, though.
Example: http://playground-leaflet.rhcloud.com/yel/1/edit?html,output

Keep node selection from spreading to PropertySheet

I'm developing an application based on the netbeans platform.
The problem I'm having is this; I have a TopComponent that contains two panels, A and B, each with a ExplorerManager. I have two BeanTreeViews in each panel with different sets of nodes. When I change selection in the A panel, the nodes in the B panel will be created. Now, I'd like to be able to select a node in the B panel and see it's properties in the default property view. But I still have a selected node in the A panel, and therefore the property view only says 'Multiple objects.' Is there any way to keep the selection in the A panel from spreading to the PropertyView?
I'd like to be able to use NodeActions on both sets of nodes, and therefore I've added
associateLookup(new ProxyLookup(
ExplorerUtils.createLookup(PanelA.getExlporerManager(), map),
ExplorerUtils.createLookup(PanelB.getExplorerManager(), map)
));
to the TopComponent if i only associate PanelA's ExplorerManager then this isn't a problem.
Ok some after testing around I found a solution that works for me. Perhaps I was a bit quick to post this question. Anyway;
In the TopComponent I did
associateLookup(new ProxyLookup(
ExplorerUtils.createLookup(PanelA.getExlporerManager(), map)
));
Then only the Nodes in PanelA will be spread to the PropertyView. And then to get the NodeActions to work in PanelB, I made it implement Lookup.Provider, and created it's Lookup with
lookup = ExplorerUtils.createLookup(explorerManager, map);

Two ICEfaces panel positioned interacting

How do you constrain the elements of a PanelPositioned so they can only be dragged within the current panel? I have two vertical lists of different data types, one above the other. Both use a PanelPositioned to allow drag & drop reordering, but the elements can be dragged up and down to the other panel, generating an error
<ice:panelPositioned id="dragPanel1" var="dataType1var"
value="#{displayBean.dataType1List}" constraint="vertical">
<ice:panelGroup style="cursor:move;">
</ice:panelGroup>
</ice:panelPositioned>
<ice:panelPositioned id="dragPanel2" var="dataType2var"
value="#{displayBean.dataType2List}" constraint="vertical">
<ice:panelGroup style="cursor:move;">
</ice:panelGroup>
</ice:panelPositioned>
For icefaces 3.0, add a beforeChangeListener to the ice:panelPositioned element. This listener should then call event.cancel() if the event.getType() != to PanelPositionedEvent.TYPE_MOVE and the event.getIndex() and event.getOldIndex() are both greater than 1. If it is a move, then it is within panel. add/delete are moving between panels.
For icefaces 1.8.2 (neve used 2.0), you would need to add a listener to the ice:panelPositioned element. There is no beforeChangeListener. you will need a before list and current list (two copies). Both initialize to same elements when initializing backing bean. The listener, if a move - set before list to current list otherwise set current list to before list. Must be a copy, not a reference.