Tips for implementing AJAX in PruneCluster

Utilizing ajax to constantly update the markers' location every 10 seconds using PruneCluster.


(function update() {
    $.ajax({
        url: 'https://wanderdrone.appspot.com/',
        dataType: 'json',
        success: function (data) {

            var leafletView = new PruneClusterForLeaflet();
            leafletView.BuildLeafletClusterIcon = function (cluster) {
                var e = new L.Icon.MarkerCluster();
                e.stats = cluster.stats;
                e.population = cluster.population;
                cluster.ENABLE_MARKERS_LIST = true;
                return e;
            };

            var markers = [];
            var myServerData = data;
            console.log(data);
            map.setView(new L.LatLng((myServerData.geometry.coordinates[1]), (myServerData.geometry.coordinates[0])), 12);
            var marker = new PruneCluster.Marker((myServerData.geometry.coordinates[1]), (myServerData.geometry.coordinates[0]));
            markers.push(marker);
            leafletView.RegisterMarker(marker);
            leafletView.ProcessView();
            map.addLayer(leafletView);

        }
    })
        .then(function () {
            setTimeout(update, 10000);
        });
})();

Although I successfully update the locations of new markers, they keep overlaying the previous ones without replacing them. The new markers and clusters simply accumulate on top of the old ones. How can I remove the previous markers' layer before adding the new one?

Answer №1

Your code has been updated with a few changes.

It appears that on each ajax request, a new array and marker are being created. This results in the cluster marker not updating, but rather new markers being plotted over the previous ones. To address this issue, consider initializing your array as a global variable. Before creating a new marker from the ajax request, check if an instance of that marker already exists in the array (by assigning an id to the marker using the marker.data object). If it exists, update it; if not, create a new one.

const map = L.map("map", {
  attributionControl: false,
  zoomControl: false
}).setView(new L.LatLng(59.911111, 10.752778), 4);

L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
  detectRetina: true,
  maxNativeZoom: 17
}).addTo(map);

let markers = [];
const leafletView = new PruneClusterForLeaflet();

leafletView.BuildLeafletClusterIcon = function(cluster) {
  var e = new L.Icon.MarkerCluster();
  e.stats = cluster.stats;
  e.population = cluster.population;
  cluster.ENABLE_MARKERS_LIST = true
  return e;
};

const update = () => {
  $.ajax({
      url: 'https://wanderdrone.appspot.com/',
      dataType: 'json',
      success: function(data) {

        const myServerData = data;
        console.log(data);
        map.setView(new L.LatLng((myServerData.geometry.coordinates[1]), (myServerData.geometry.coordinates[0])), 12)

        /** Check if Marker Already Exists On Map */
        const markerAlreadyExists = checkIfMarkerExists(myServerData.id);

        switch (markerAlreadyExists) {
          case true:
            const existingMarker = getExistingMaker(myServerData.id);
            existingMarker.position.lat = myServerData.geometry.coordinates[1];
            existingMarker.position.lng = myServerData.geometry.coordinates[0];
            leafletView.ProcessView();
            break;
          case false:
            const marker = new PruneCluster.Marker((myServerData.geometry.coordinates[1]), (myServerData.geometry.coordinates[0]));
            markers.push(marker);
            leafletView.RegisterMarker(marker);
            leafletView.ProcessView();
            break;
        }

      }

    })
    .then(function() { // upon completion, restart
      setTimeout(update, 10000); 
    });
};
update(); 

map.addLayer(leafletView);


/**
 * FUNCTION TO CHECK IF A MARKER EXISTS IN MARKER ARRAY
 */
const checkIfMarkerExists = id => {
  let exists = false;
  for (const existingMarker of markers) {
    if (existingMarker.data.id === id) {
      exists = true;
    }
  }
  return exists;
};


/**
 * FUNCTION TO RETRIEVE AN EXISTING MARKER FROM MARKER ARRAY
 */
const getExistingMaker = id => {
  let markerObject = new Object();
  for (const existingMarker of markers) {
    if (existingMarker.data.id === id) {
      markerObject = existingMarker;
    }
  }
  return markerObject;
}
html,
body,
#map {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  font-family: sans-serif;
}

div#size,
a#delete {
  position: absolute;
  right: 1em;
  top: 1em;
  background: white;
  color: black;
  padding: 0.4em;
  border-radius: 4px;
  z-index: 500;
}
<!DOCTYPE html>

<html lang="en">

<head>
  <meta charset="utf-8" />
  <title>PruneCluster - AjAX</title>

  <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, target-densitydpi=device-dpi" />

  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.0-beta.2.rc.2/leaflet.css" />
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.0-beta.2.rc.2/leaflet.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/prunecluster/2.1.0/PruneCluster.js"></script>
</head>

<body>
  <div id="map"></div>
</body>

</html>

Check out a simple Spring Boot project on my GitHub where these concepts have been implemented.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

MongoError: Transaction could not be initiated

I recently delved into using mongoose transactions for the first time. Following the guidelines in the documentation and some related articles, I managed to get it up and running with the help of run-rs for local replicas. However, I encountered a couple o ...

Modifying a leave transition in Vue.js dynamically following the completion of an enter transition

I am dealing with a dynamic transition for a slider element that moves left or right. Vue only allows me to have one transition, so I am using a dynamic transition property like this: <transition v-bind:name="'slider-' + slideDirection"> ...

Troubleshooting: encountering a 404 error with AJAX redirect in webpack-dev-server

Currently, I am in the process of integrating the OAuth2 flow into a ReactJS application that utilizes both webpack-dev-server and react-router BrowserRouter. I have encountered issues with implementing two different flows. The first flow functions correc ...

Expansion of border in Bootstrap carousel with each slide progression

I am working on creating a bootstrap5 carousel that includes a video with fixed dimensions and a box-shadow applied to it. However, I am facing an issue where the box-shadow expands during each slide transition, making it appear as if the video element&ap ...

Creating secure paths with ReactJS

I'm currently working on implementing protected routes in my React application. I have a Node.js backend with an endpoint called /isLoggedIn that checks if the user is logged in. The backend functionality is working fine, but I'm encountering mul ...

Is there a more efficient way to streamline this function?

Hey there, I'm currently working with this function: const [shopItems,setShopItems] = useState("");_ useEffect(()=> { commerce.products.list().then((product) => { setShopItems(product) }); }, []) function categori ...

Troubleshooting issue: Jquery keypress function not functioning as expected in conjunction

I'm completely lost. The code below is just my latest attempt at solving this issue: <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{title}}</title> <link rel="stylesheet" hre ...

What is the process for activating my redux function within a component?

I'm working on a form that should create a new user when submitted. In my handleCreate function, I want to use Redux to trigger the addUser action and update the state to add the new user. However, it seems like I'm having trouble calling the act ...

The debate between importing images and including them inline in NextJS is a hot

When using NextJS, what sets apart importing an image from directly including it with next/image? import logo from './logo.png'; <Image src={logo} /> /* versus */ <Image src={'/logo.png'} /> (This is pseudocode meant for ...

JavaScript communication between clients and servers

I am looking to develop two scripts, one for the client-side and one for the server-side. I came across a snippet that allows for asynchronous calling of JavaScript: <html> <head> </head> <body> <script> (function() { ...

The inclusion of a content editable feature within a carousel is leading to unexpected event propagation

I am dynamically creating an editable div using the code snippet below. <div class='reflection-field' contenteditable="true" data-number="${index}"></div> Expected Outcome: When I click on the generated div, I anticipate that the c ...

Invoke the ngrx component store's Effect in a synchronous manner

In the ComponentStore of ngrx, we have two effects. readonly startAndUpdateProgress = this.effect<void>( (trigger$) => trigger$.pipe( exhaustMap(() => this.numbersObservable.pipe( tapResponse({ next: (p ...

Extract data from an RSS feed and showcase it on an HTML webpage

Having trouble parsing a Yahoo Finance RSS news feed and displaying the information on a webpage. I've tried different methods but can't seem to get it right. Hoping someone out there can assist me with this. I came across a tutorial on how to p ...

Achieving object rotation in three.js by synchronizing with the camera

I'm currently working on making an object follow the camera to create a rifle viewfinder effect. I am using OrbitControl, but I've run into an issue with the camera rotation range. The camera.rotation.y property only goes from -PI/2 to PI/2, even ...

When a webpage loaded through ajax, an error may occur indicating that Javascript functions are not

The functions have been defined and are functioning properly. However, when the same page is loaded via ajax, an error stating callA is not defined appears in the firebug console. I'm puzzled as to why this works in one scenario but not the other. Am ...

Troubleshooting error 1045: User 'root'@'localhost' denied access

When trying to connect a MySQL database to my NODEJS application, I keep encountering an error every time I start the server. The error message reads "Access denied for user 'root'@'localhost' using password YES, error number 1045." De ...

Exploring the concept of self-referencing models in Django using django-ajax-related-fields

I'm currently experimenting with django-ajax-related-fields, and I am encountering difficulty creating a foreign key relationship to the model itself. I have attempted 'self', Node, and even self (without quotes), but unfortunately, the outc ...

AngularJS Constants in TypeScript using CommonJS modules

Let's talk about a scenario where I need to select a filter object to build. The filters are stored in an array: app.constant("filters", () => <IFilterList>[ (value, label) => <IFilterObject>{ value: value, label: label } ]); i ...

Obtain output from a callback function

Is there a way to set a variable using the code var myVar = myFunction(); when myFunction contains a callback function that prevents it from returning a value directly? I am unsure of how to modify this code in order to allow for a return value from the ...

Verify that the length of all input fields is exactly 7 characters

My task involves checking the length of multiple input fields that share a common class. The goal is to verify that all fields have a length of 7. After attempting a solution, I encountered an issue where even if the length of all fields is indeed 7, the ...