Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update just one specific marker #563

Closed
Quentin22 opened this issue Oct 1, 2015 · 12 comments
Closed

update just one specific marker #563

Quentin22 opened this issue Oct 1, 2015 · 12 comments

Comments

@Quentin22
Copy link

Hello,
Context :
I have a nodejs server to serve tiles with .mbtiles file and I use MarkerClusterGroup on my client to display markers with a .geojson file which represent a state of communication. Markers are .png red or blue according to their state of communication. I try to have an interactive map which display markers and show if the communication is OK (blue.png) or if the communication is down (red.png).

Issue :
I want to be able to get one specific marker by his geographic coordinates or by his name (in .geojson file and on popupText) and change the color of it by changing the .png (red or blue). On cascade, I wish to change the color of cluster parent of this marker in red for example.

Is it possible to do that without removing layer with all markers and add again this layer with function "removeLayer" and "addLayer" ? I just wand to change one specific marker.

Thank you !

Quentin

@ghybs
Copy link
Contributor

ghybs commented Oct 1, 2015

Hi,

It looks like many people start using MCG for real-time status data! :-) (referring to #561, #555 and #535)

Obviously the marker should reflect the new status, even if the latter changed while the marker was clustered.

Furthermore, in many cases you would like a cluster to reflect somehow the status of its children markers, so you need a way to re-draw its icon (as also referred to in #498).

Definitely +1 for me as well.

@ghybs
Copy link
Contributor

ghybs commented Oct 1, 2015

All,

Seems to be quite simple to implement.
Please try to add the following method to your Marker Cluster Group:

/**
 * Changes the class of all supplied markers and updates the clusters.
 * @param aMarkers array|L.LayerGroup|L.MarkerClusterGroup of markers to be modified.
 * @param sNewClassOrFn string|function new class to be applied on markers,
 * either as a string or as a function that receives the old class as argument.
 */
updateMarkersClass: function (aMarkers, sNewClassOrFn) {
    if (aMarkers instanceof L.MarkerClusterGroup) {
        aMarkers = aMarkers.getAllChildMarkers();
    } else if (aMarkers instanceof L.LayerGroup) {
        var array = [];
        for (var j in aMarkers._layers) {
            array.push(aMarkers._layers[j]);
        }
        aMarkers = array;
    } else if (!(aMarkers.length)) {
        aMarkers = [aMarkers];
    }
    // Make a function that returns always the same string if needed.
    if (typeof sNewClassOrFn !== "function") {
        sNewClassOrFn = function () {
            return sNewClassOrFn;
        }
    }

    var marker,
        i = 0,
        icon,
        parent;

    for(; i < aMarkers.length; i += 1) {
        marker = aMarkers[i];
        icon = marker.options.icon;

        // Update icon class.
        icon.options.className = sNewClassOrFn(icon.options.className);

        // Force Marker redraw.
        marker.setIcon(icon);

        // Set parent clusters' icon dirty, all the way up.
        parent = marker.__parent;
        while (parent) {
            parent._iconNeedsUpdate = true;
            parent = parent.__parent;
        }
    }

    // Refresh visible markers.
    this._featureGroup.eachLayer(function (c) {
        if (c instanceof L.MarkerCluster && c._iconNeedsUpdate) {
            c._updateIcon();
        }
    });
}

If looks good I will try to make a PR when I have time.

@IvanSanchez
Copy link
Member

@ghybs Do not copy-paste patches into comments, do pull requests instead. Right now it's nearly impossible to see the diff against the old code.

@Quentin22
Copy link
Author

Thanks for your responses. I don't understand what this method do. I try to add this code to my MarkerClusterGroup but getAllChildMarkers() is unknown.

You will find below my code to display the map and markers :

var map,
    BlueIcon,
    RedIcon,
    markers;

function init() {
  var mapBounds = new L.LatLngBounds(
    new L.LatLng(-85.0511, -180),
    new L.LatLng(85.0511, 180));
  map = L.map('map').setView([30,0], 3);

  L.tileLayer('{z}/{x}/{y}.png', {
    minZoom: mapMinZoom, 
    maxZoom: mapMaxZoom,
    bounds: mapBounds,
    opacity: 0.70,
  }).addTo(map);

  BlueIcon = new L.icon({
    iconUrl: '../../images/marker-icon-blue.png',
    ...
  });

  RedIcon = new L.icon({
    iconUrl: '../../images/marker-icon-red.png',
    ...
  });

  function onEachFeature(feature, layer) {
    var popupText = 'Label : ' + feature.properties.name;
    if(feature.geometry.type == "Point"){
      layer.bindPopup(popupText);
      var markerIcon = new L.icon({
        iconUrl: '../../images/marker-icon-' + feature.properties.color + '.png',
        iconAnchor: [12, 42],          
        popupAnchor: [0, -41]
      });
      layer.setIcon(markerIcon);
    }
  }

  var geo = L.geoJson(geojson, {
    onEachFeature: onEachFeature
  });

  markers = new L.MarkerClusterGroup();
  markers.addLayer(geo);
  map.addLayer(markers);
}

function clickBtn() {
 //Simulate an event
 //Change .png of one specific marker and color of marker cluster parent
}

This is a part of cluster :
cluster
This is 3 markers under a part of cluster :
marker

On my map I have got 22 markers under some clusters.
I try to change just one specific marker under one cluster and change color of this cluster.

Do you think it's possible to do that ?

Thank you !

@ghybs
Copy link
Contributor

ghybs commented Oct 1, 2015

Hi,

@IvanSanchez thanks for your comment, I certainly understand your point.
The fact is this does not change any previous code, it is just an extra method. I should probably add it through L.MarkerClusterGroup.include({updateMarkersClass}) by the way, as it is more like a comfort feature, not a core one…

I will certainly make a PR when I have time.

@Quentin22, your need is certainly doable, you should be able to modify my code to change the iconUrl of your standard L.Icon, instead of my code modifying the class of an L.DivIcon.

Hope this helps.

@ghybs
Copy link
Contributor

ghybs commented Oct 1, 2015

My bad, line 3 after comments:

aMarkers = aMarkers.getAllChildMarkers();

should have been:

aMarkers = aMarkers._topClusterLevel.getAllChildMarkers();

I tried to tidy up and split the code in PR #564. Hopefully it should be easier to understand and use.
See: https://github.com/Leaflet/Leaflet.markercluster/pull/564/files

@Quentin22
Copy link
Author

Thank you for you code ! But I don't how to use it, sorry...

For now I create a geographic coordinates of one marker present on my map and change his color with an event by removing layer and adding layer again with juste one marker updated :

var lat = 10.574222078332806,
    lng = -68.62060546875;

function clickBtn() {
  map.removeLayer(markers);

  var geo = L.geoJson(geojson, {
    onEachFeature: function (feature, layer) {
      var popupText = 'Label : ' + feature.properties.name;
      layer.bindPopup(popupText);
      if(feature.geometry.type == "Point"){
        if(feature.geometry.coordinates[0] == lng && feature.geometry.coordinates[1] == lat) {
          layer.setIcon(RedIcon);
        }
        else {
          layer.setIcon(BlueIcon);
        }
      }
    }
  markers = new L.MarkerClusterGroup();
  markers.addLayer(geo);
  map.addLayer(markers);
}

But I still don't know how to change color of cluster which contains this updated marker.

@ghybs
Copy link
Contributor

ghybs commented Oct 5, 2015

Hi,

All right, in fact you seem to need first basic instructions on how to update a marker in a layerGroup, independently from MarkerClusterGroup, or even from L.GeoJSON.

  1. Use the following method to loop through the "layers" (in fact, your markers) in your layerGroup (in your case: MCG markers):
    http://leafletjs.com/reference.html#layergroup-eachlayer
    …instead of re-creating your entire MCG.

  2. Next, what you actually want is to customize the style of clusters.
    See the option iconCreateFunction on how to do that:
    https://github.com/Leaflet/Leaflet.markercluster/tree/master#customising-the-clustered-markers
    Within your function, use cluster.getAllChildMarkers() to get the array of contained markers, loop through them to look for possibly updated markers and style accordingly.

  3. Only once you have done the 2 above steps, you will be interested in the above code. Copy the code from this file into your script:
    https://github.com/Leaflet/Leaflet.markercluster/pull/564/files
    Then use the method markers.refreshClustersOf(layers) with layers being the array of updated markers, or your entire MCG itself to keep it simple: markers.refreshClustersOf(markers).

If you need further help, I recommend you using Stack Overflow. You would probably get help quicker for what you need.

@Quentin22
Copy link
Author

Thank you so much for your response. I'm going to focus on that and I will let you know if I can do something, or not =).

@Quentin22
Copy link
Author

It's ok for the first step, I can get all markers and test it to change or not the marker with setIcon function in red or blue depending of their state.

But for the second step I try to change only one cluster (only change the color) If one or more childMarker is in red. However if I use iconCreateFunction I will customize all cluster because I create only one MarkerClusterGroup.

I don't understand why you have to refresh markers when you use setIcon, I used it and I didn't have to refresh anything.

For now, I just only have to customize the color of clusters which have some child markers in red. And change again in green if all child markers are in correct state.

Like this example, one cluster with 2 markers in correct state and another cluster with some markers with an incorrect state :
first
Zoom in :
second
Zoom in :
third
Zoom in :
four

This is exactly what I try to do.

@ghybs
Copy link
Contributor

ghybs commented Oct 6, 2015

Hi Quentin,

I just realized there is a forum for Leaflet: https://groups.google.com/forum/#!forum/leaflet-js
That would probably be the most appropriate place for you to get help, even better than Stack Overflow.

@ghybs
Copy link
Contributor

ghybs commented Oct 9, 2015

Hi @Quentin22, please can you kindly close this issue, now that you seem to have succeeded in making it work?
https://groups.google.com/d/msg/leaflet-js/Vropa26ykPA/7hDXcqfIEgAJ
Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants