29

I like to create a map with Google Maps that can handle large amounts of markers (over 10.000). To not slow down the map I've created a XML-file that only outputs the markers that are inside the current viewport.

First, I use initialize() to setup the map options:

function initialize() {
    var myLatlng = new google.maps.LatLng(51.25503952021694,3.27392578125);
    var myOptions = {
        zoom: 8,
        center: myLatlng,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    }
    var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);

    google.maps.event.addListener(map, 'tilesloaded', function () {
    loadMapFromCurrentBounds(map);
    }); 
}

When the event 'tilesloaded' is finished, I use loadMapFromCurrentBounds(), this functions will get the current bounds and sends a request to the XML-file to show the markers that are inside the current viewport:

function loadMapFromCurrentBounds(map) {

    // First, determine the map bounds
    var bounds = map.getBounds();

    // Then the points
    var swPoint = bounds.getSouthWest();
    var nePoint = bounds.getNorthEast();

    // Now, each individual coordinate
    var swLat = swPoint.lat();
    var swLng = swPoint.lng();
    var neLat = nePoint.lat();
    var neLng = nePoint.lng();

    downloadUrl("mapsxml.php?swLat="+swLat+"&swLng="+swLng+"&neLat="+neLat+"&neLng="+neLng+"", function(data) {
        var xml = parseXml(data);
        var markers = xml.documentElement.getElementsByTagName("marker");
        var infoWindow = new google.maps.InfoWindow; 

        for (var i = 0; i < markers.length; i++) {
            var address = markers[i].getAttribute("address");
            var type = markers[i].getAttribute("type");
            var name = markers[i].getAttribute("name");

            var point = new google.maps.LatLng( 
            parseFloat(markers[i].getAttribute("lat")),
            parseFloat(markers[i].getAttribute("lng"))
            );

            var html = "<b>" + name + "</b> <br/>" + address;
            var icon = customIcons[type] || {};

            var marker = new google.maps.Marker({
            map: map,
            position: point,
            icon: icon.icon,
            shadow: icon.shadow});

            bindInfoWindow(marker, map, infoWindow, html);
        }
    })
}

This is working great, however, the current code doesn't offload markers that aren't in de viewport anymore. Besides that, it loads markers again who are already loaded, that slows down the map really fast when moving the map a view times in the same area.

So when the viewport changes, I like to clear the whole map first before loading new markers. What is the best way to do this?

jamesmortensen
  • 33,636
  • 11
  • 99
  • 120
Thijs
  • 299
  • 1
  • 3
  • 4
  • Hey @jeff, thx for the edits! I just wanted to let you know that you can actually add syntax highlighting to *all* the answers on a question just by adding the `JavaScript` tag, using the "edit tags" link that appears to the right of the tags. Good luck! :) – jamesmortensen Aug 07 '12 at 03:03
  • The [Marker Clusterer](https://code.google.com/p/google-maps-utility-library-v3/wiki/Libraries) might be useful when dealing with so many markers. – Blazemonger May 10 '13 at 14:09
  • 1
    you can save a few lines of code above by doing `map.getBounds().toUrlValue().split(',')` and have a nice array for your corners. – tim Jun 18 '13 at 23:30

6 Answers6

17

You need to add another Event Listener to the map:

google.maps.event.addListener(map,'bounds_changed', removeMarkers);

See here for more on removing all markers from a google map - unfortunately I dont think it can be done with one call. So you will have to write the removeMarkers or something similar which will have to iterate through all the markers on the map removing them individually like so:

 markersArray[i].setMap(null);

I don't know whether it's quicker to check if the marker is in the viewport before removing by using:

 map.getBounds();

Read more about Google Map API v3 events

Community
  • 1
  • 1
  • Thanks for your reply! Why do I have to use 'bounds_changed'? The Event Listener I'm using now ('Tilesloaded') seems to do the same, every time the viewport changes the functions loadMapFromCurrentBounds() is called. I've tried markersArray[i].setMap(null); but it doesn't work. Besides that, I like the solutions where I only clear the markers that aren't in the viewport anymore... I'll try to upload an example later this evening! – Thijs Jun 01 '10 at 14:13
  • 2
    also, tilesloaded may not fire if you're moving the map just a little bit, i.e. if your movement does not cause a new tile to be loaded into the viewport – tim Jun 18 '13 at 18:31
7

Due to the following explanation using 'tilesloaded' or 'bounds_changed' would be very wrong and cause unwilling continuous firings. Instead you would want to use 'idle' event which will fire once the user has stopped panning/zooming.

google.maps.event.addListener(map, 'idle', loadMapFromCurrentBounds);

https://developers.google.com/maps/articles/toomanymarkers#viewportmarkermanagement

6

You may want to check out this thread. Daniel answered this quite nicely.

What's the most efficient way to create routes on google maps from gps files?

Also, bounds_changed is the first opportunity to call your function. tilesloaded, will be called constantly. The viewport may contain more than one tile to fill the viewport.

Alternatively, you can also do a setVisible(false).

In order to remove the marker, you may need to remove the listeners.

google.maps.event.clearInstanceListeners(marker);
marker.setMap(null);
markers.remove(marker);
delete marker;
Community
  • 1
  • 1
CrazyEnigma
  • 2,824
  • 1
  • 17
  • 17
5

This article goes through it pretty nicely: Dynamically loading thousands of markers in Google Maps

  • dynamically load markers until we reach a threshold
  • keep a hashtable of markers that have already been added
  • after the threshold has been reached, remove markers that aren’t currently within the viewport
  • remove all markers from the map when the user has zoomed out, and don’t load any markers until the user zooms back to a reasonable level
webjunkie
  • 6,891
  • 7
  • 46
  • 43
  • 3
    Try this link http://xyzzyb.tumblr.com/post/10317033064/dynamically-loading-thousands-of-markers-in-google-maps – webjunkie Aug 14 '12 at 16:05
3

Your original function seems like a lot of code. I'd do something like this:

if( map.getBounds().contains(markers[i].getPosition()) ) {
   myMarkerDisplayFunction(markers[i]);
}
jeff
  • 8,300
  • 2
  • 31
  • 43
tim
  • 3,823
  • 5
  • 34
  • 39
0

You might want to check out this documentation from Google. It explains what you need:

With the new list of markers you can remove the current markers 
(marker.setMap(null)) that are on the map and 
add the new ones (marker.setMap(map)).
hellomello
  • 8,219
  • 39
  • 151
  • 297