UPDATE May 20, 2011: Google made an API update in version 3.4 that noticeably improves the standard method for loading markers. The method shown in this tutorial still loads faster.
If you have ever tried cramming more than 100 markers onto a Google map you know how poor the performance becomes. The default Google marker functions are slow if you need to deal with a large number of markers. This tutorial shows you how to add 1,000 markers to a Google map very fast. The load time difference on my computer went from around 20 seconds down to right under 2 seconds.
This is not another marker cluster approach. Gabriel Svennerberg has an excllent article comparing various clustering methods. Nor is it a pre-rendered image layer. This tutorial is for situations where you need to show many markers (more than 100) on various zoom levels without it crashing your browser.
The principal here is making use of DOM DocumentFragments. I first learned about DocumentFragments from John Resig. John's little discovery has majorly increased the speed of our JavaScript applications at Sabramedia.
The problem is DOM calls are very expensive. Every time a marker is added to the map, a function searches the DOM looking for the point to manipulate (add the marker).
Before we get into the code, I will give you two links below to test the difference in speed. Both links open in new windows. Note: I am using a custom marker icon (Sabramedia fish) for the fast loading example.
- Slow (API V3.3): 1,000 markers loading on a Google map (Beware it takes a while.)
- Slow (API V3.4): 1,000 markers loading on a Google map
- Fast (API V3.4): 1,000 markers loading on a Google map
UPDATED: Using Firebug in FF 4.0.1 to profile the number of calls compares like this:
- Slow render (API V3.3) - 2,087,604 calls
- Slow render (API V3.4) - 589,323 calls
- Fast render (API V3.4) - 133,363 calls (about same in V3.3)
Download the full code for fast marker loading here.
Code Explained
Google Maps API V3 is used. Google has officially deprecated V2. A knowledge of how Google's Overlay object works will benefit your understanding of this tutorial. Let's begin.
The following code sets the boundary box that the 1,000 markers are randomly added to. The South West corner (latitude, longitude) is Los Angeles, California and the North East corner is New Your, NY.
Now we get into the heart of the code. We will create an overlay class (function) here that will be called after the Google map object is initialized later. We will prototype Google's OverlayView methods in order to create our marker layer.
The onAdd() method is called when the map is initialized. Here the new marker layer we created previously is appended to Google map's pane 4. The marker layer is cleaned up in onRemove() when the map is destroyed or removed.
The draw() method is where the marker layer is initialized, then re-drawn on every zoom. This is where the performance happens. Let's go through it.
Lines 3-5. Line 3 is absolutely critical to understand. This is the current projection view. This will be used later to convert latitude and longitude coordinates into pixel left and top position. Line 4 identifies the current zoom level. Line 5 is where we create the node holder with DocumentFragment before we push it to the DOM view.
Line 7. Remember that this.markerLayer is a jQuery object set in the onAdd() method. Because draw() is called on state changes such as zoom it will be important to do some clean up each time. That is where we use the jQuery empty() to clean up the marker layer, otherwise we would have element buildup in that layer. Not the desired effect.
Lines 9-14. The for loop begins, we arbitrarily set a thousand loops. Line 11 sets a random latitude/longitude object via Google's LatLng( lat, lng ) method. This could be any data set you want to pass into the loop of course.
Line 16. This is where the projection object methods come in handy. The method fromLatLngToDivPixel(object:LatLng) takes a Google LatLng object and converts it into the x/y pixel coordinates of the current projection view.
Line 17-33. I like jQuery's chaining of methods. They look neat and clean when adding attributes and style to a DOM element such as a DIV tag; However, they are slow and noticeably so when working with many elements. Skip the chaining and simply create the element by appending strings in order to improve the speed. It saved me about 28,000 calls in this tutorial example.
Line 35-51. This code block is optional. I added to illustrate the flexibility with this approach. For zoom level 8 or closer a tool tip is shown above the marker icon.
Line 54. Here we append the HTML for the marker point to the fragment.
Set the default latitude/longitude position. I used the location of our office in Jasper, Indiana in this case. Initialize the map with desired options. Lastly apply the custom OverlayView that you just created.
After this code I added a simple jQuery UI dialog to the map. In this example the dialog just opens in the center. Bringing the marker to the map center and adjusting for the dialog is possible, but beyond the scope of this tutorial. Email me if you would like the code for this.
Angel
December 19, 2010Awesome tutorial, this helped me out with a project I'm currently working on.
To take it one step further though, if you wanted to dynamically add new markers to the map through some sort of UI modal how would you set this functionality up? Would you just push the newly created marker onto the array of markers and redraw the map overlay? Or could you add a function to just append a new 'map-point' div to the DOM?
Thanks again for the walkthrough.
Nick Johnson
December 28, 2010@Angel. Two things you need to add a marker dynamically are, 1) The current projection and 2) The custom marker layer in the DOM.
In this tutorial you can access the custom marker layer via the OverlayMap.markerLayer. The projection can be set either in a global variable for the current state, or you can set it in this.projection so it can be accessed via OverlayMap.projection.
Keep in mind that this tutorial randomly sets the lat/lng of the points in the draw() method. You would need to keep an external data set of the lat/lng's, including any markers dynamically added after initialization, to refer to every time the draw() method is called e.g. on zoom. I hope this helps.
Mark
April 20, 2011I have same prolems here, What can i do?
Jeremy
May 17, 2011This is a very cool method of drawing the markers. However, I noticed that the "slow" page isn't really much slower than the fast page. I would guess that something changed in the google code to accelerate this.
Would your technique still be useful in a case where there are 1000 markers all based on different images?
Craig
May 20, 2011Nice tutorial. I noticed the same as Jeremy. Both are pretty fast.
Nick Johnson
May 20, 2011@Jeremy, @Craig
Thanks guys for bringing this to my attention. It appears that Google did make a significant improvement in version 3.4. I updated the tutorial to make mention of this.
@Jeremy
Regarding the images, this method would be better than the standard method, but this is an entirely separate issue. This tutorial shows an improved method for rendering the actual code markup. If you are loading 1,000 different images, then you might want use a clustering or MarkerManager approach. You can see these at the Svennerberg link in the article above or checkout this Google article.
dungnguyen
July 31, 2011I benmarked 2 examples : yours and one with google suggestion (create markers normally by call their contructors) and the result is that google is 20 times faster! (i'm using firefox 5)
Robert
August 24, 2011Panning the map around w/ your custom marker is noticeably slower and choppy compared with the Slow V3.4 method in Chrome and FF.
Vaishakh
September 1, 2011I have around 20,000 markers how do I go about , should I server side clustering or client side ?
Bowral Boy
October 25, 2011Great tutorial! The fragment method looks like an interesting approach to minimising marker display overheads. Excited to give it a try!
Aldo Lavin
February 29, 2012How can I implement some different map layers witch depend of checkboxes?
I would like to activate/deactivate layers independiently.
Thanks.
Aldo Lavin
February 29, 2012How can I implement some different map layers witch depend of checkboxes?
I would like to activate/deactivate layers independiently.
Thanks.
Fernando Ott
May 10, 2012Great Tutorial Nick!
Really solve my problems with markers.
But one problem that I detected was the map.fitBounds(...) method.
This method just didn't work anymore with this approach. You know what could be?
Thanks
seti
July 20, 2012Hi Nick Johnson, thanks so much for this great artical.
Can you please send the jqurey UI dialog code to me?
seti
seti
July 20, 2012My eamil addr:
greatseti@hotmail.com
Zachary
October 19, 2012This is fantastic! I'd love to see your code.
sam
March 20, 2013This is very nice post.
Jaime Yule Jacobson
May 20, 2013awesome post!
Abhay Yadav
June 10, 2013Nice tutorial.
Tommy Brask
June 11, 2013Find "How To Quickly Add Many Markers Without Killing The Map" and try to convert it for my application because I need a fast system to show information big amount of markers.
I'm relatively new at JQuery and trying to learn more and now I have problem how to write texts in the box who appear when clicking a marker, in the example only ID is showing. I find your tutorial very helpful and informative.
Laurence Blake
June 19, 2013Maybe i'm doing something wrong, but the other two approaches run much quicker for me than your approach.
Mahen
July 1, 2013Nice post. Really helpful to play with G Maps for novice Developers. Cool !
Few Useful comments to run through.
Recent V3 examples by Google are helpful : https://developers.google.com/maps/documentation/javascript/overlays#AddingOverlays
Ricky
July 30, 2013Pls speak English
H5bak
January 20, 2014OMG!!!
This is a very awesome post.
Thanks.
Rohit Verma
July 2, 2014hi nick,
this is great tutorial , this helped me lot
I just need to know how to attach events on markers ?
Hi I want to load markers in after map loaded and button is clicked event how can i do that? plz help
Joao Conceicao
September 16, 2014Hi, nice approach. Can you send me the code?
Thanks!
anil
April 7, 2015How top add different icon for 20000 locations
Mengyu
April 23, 2015nice post! I've learnt a lot from this tuto. But when i opened these three links in the beginning of article, the approach presented is the slowest one. Another reader Laurence Blake has the same result. Maybe I do something wrong? Anybody else test with these three examples?
Ricardo Santana
June 10, 2015First I would like to congratulate for your article has been of great ajusda for the project I'm imprementando in the company in which work.
However I am with the following section difficulty, I am unable to update the markers in the map of events "dragend" and Zoom!
You podeira guide me about it?
graciously
Ricardo Santana
ooo
Stefan Maier
November 21, 2015Hi, first of all: thanks a lot for your effort! I also tried all 3 links. But apparently the same issue happens as Mengyu described. Actually the first link (fast one) made my firefox unusable (froze) for at least 30 seconds. Guess it's related to some change on the google side, cause this article was written in 2012. Do you have any information on that?
Cheers!
Pankaj S.
March 23, 2016Firstly thanks for making this example. But this is not working with dynamic location and run time loading xml. I am creating xml for marker location as per google doc. Its working but taking too much time. And when using this example. XML is not loaded. How to fix this ?
Basically i am creating map dynamically on click. Every time different location marker.
Brilliant. I've been trying to find a way to do this for days. Thanks
Anson
January 17, 2018This example saved me lots of time. Thanks you so much!
Lee
April 20, 2020This is nice. How would I implement clustering with zoom in on clicking a cluster?
Lee
April 24, 2020I checked out the clustering link you posted in the comments above. That brings me back to the original problem though because running "new google.maps.Marker..." thousands of times was the bottle neck. Is there a better approach? Also, I also noticed as someone else commented that fitbounds no longer works on the map. How would I do that.