Search code examples
javascriptleafletleaflet.markercluster

How to add and remove marker clusters on a leaflet map?


I am using leaflet market cluster and I am setting markers as circle markers.

Leaflet version: [email protected]

Leaflet cluster version: [email protected]

NOTE

$.fn.almDone... and $.fn.almEmpty... are just some functions I use for my ajax callbacks

I am drawing some markers on the map if we have results, and clear the markers we've drawn if on a second callback we have zero results. Or simply tell the user we've got zero results and therefore zero markers.

We have 2 arrays with values where I get the coordinates:

var longitude = [];
var latitude = [];
var count = [];

We set the var to stopAjax true when we start:

var stopAjax = true;

On click we start the search and we set stopAjax to false

$(".alm-filters--button").on("click", function(){
  stopAjax = false;
});

And that is the basic logic, now we define two functions:

// This is run when we finish the call and we have results
// So on the callback we run the function to draw the markers

$.fn.almDone = function(alm){
  drawMarkers();
};

// Let's draw the markers

function drawMarkers() {

  // This is the logic to read latitude and longitude arrays
  // and push to a new array the two values as pair of coords
  // eg. 4.66, 4,5555 

  var i;
  for (i = 0; i < longitude.length; ++i) {
    pair=[ parseFloat( latitude[i] ) , parseFloat(  longitude[i]  ) ]
    count.push(  pair );
    $("#searchNations").removeAttr("disabled");
    $(this).attr("disabled", "disabled");
    var myYears = $('#years').val();
    $("#ajax-load-more ul").attr("data-meta-value", myYears);
  };

  // We check if we said to run ajax
  // and if so draw the markers
  // for myself I had also to retrieve those coords
  // and place them in individual inputs for the form

  if(stopAjax == false) {
    L.MarkerCluster.include({
      spiderfy: function(e) {
        var childMarkers = this.getAllChildMarkers();
        this._group._unspiderfy();
        this._group._spiderfied = this;

        // Fill the markersList.

        markersList.innerHTML = childMarkers
        .map((marker, index) => `<li>Marker ${index + 1}: ${marker.getLatLng()}</li>`)
        .join('');

        // If there are any childMarkers
        // draw the cluster and run a form

        if(childMarkers.length > 0) {
          // Match the lat and lng numbers from the string returned by getLatLng()
          const [lat, lng] = `${ childMarkers[0].getLatLng() }`.match(/(-?\d+.\d*)/gi);  

          // Construct the required string value from the extracted numbers
          const requiredString = `${ lat }, ${ lng }`;

          // Use requiredString to populate the value attribute of the input field in OP

          // grab the coords in individual inputs
          // that's just for my specific case

          $("#longiTude").attr("value",lat);
          $("#latiTude").attr("value", lng); 

          // run a form to see results

          submitSearchForm();
        }
      },
      unspiderfy: function() {
        this._group._spiderfied = null;
      }
    });

    // this bit is for single marker
    // we want to add a click to run the form 
    // also for the single marker and grab the coords 
    // and place them in inputs for the form

    var mcg = L.markerClusterGroup().addTo(map);

    circles = new L.MarkerClusterGroup();
    for (var i = 0; i < count.length; i++) {
      var a = count[i];
      var circle = new L.CircleMarker([a[0], a[1]]);
      circles.addLayer(circle);
      circle.on('click', function (e) {
        var curPos = e.target.getLatLng();
        $("#longiTude").val(curPos.lat);
        $("#latiTude").val(curPos.lng);
        submitSearchForm();
      });
    }

    // we add the markers to the map
    map.addLayer(circles);

    // we empty the arrays for the future calls

    count = [];
    longitude = [];

    // we set again stopAjax var to true to reset
    stopAjax = true;   
  }
}

Then the zero results function

//This is run when we have zero results

$.fn.almEmpty = function(alm) {
 stopAjax = true;
  clearMarkers();
};

// We empty the arrays and we
// clear the previously drawn markers

function clearMarkers(stopAjax) {
  if(stopAjax == true) {
    count = [];
    longitude = [];
    map.removeLayer(circles);
  }
  // if zero results, we launch a modal to advise user
  $('#zeroResults').modal('show');
}

The above works if We first have results and then we do another search and we have zero results, but in case we've made a search at first and we've got zero results, then we will have an error:

Uncaught ReferenceError: circles is not defined

And that's correct because since we get into the empty function we try to clear the markers which we haven't ever defined as we never got into the draw markers function where we define circles.

I am getting very confused on how to draw the markers and make the var circles available in both cases.

p.s. Happy for anyone to improve the logic regardless of the question issue


Solution

  • I would consider putting circles as a variable outside the scope of either function via var circles = undefined;. Note that the problem isn't that circles is undefined but that it is not defined, i.e not recognised as a variable.

    Then set it as you do in drawMarkers.

    On clearMarkers before the call to removeLayer check if (circles) to check that it is defined. Then set it to undefined again after calling removeLayer.