jawj / OverlappingMarkerSpiderfier

Deals with overlapping markers in Google Maps JS API v3, Google Earth-style
http://blog.mackerron.com
836 stars 238 forks source link

h1. Overlapping Marker Spiderfier for Google Maps API v3

Ever noticed how, in "Google Earth":http://earth.google.com, marker pins that overlap each other spring apart gracefully when you click them, so you can pick the one you wanted?

Ever noticed how, when using the "Google Maps API":http://code.google.com/apis/maps/documentation/javascript/, the same thing doesn't happen?

This library makes map markers in the Google Maps API (version 3) behave in that Google Earth way (minus the animation). Small numbers of markers (yes, up to 8) spiderfy into a circle. Larger numbers fan out into a more space-efficient spiral.

The compiled code has no dependencies beyond Google Maps. Compiled out of "CoffeeScript":http://jashkenas.github.com/coffee-script/, minified with Google's "Closure Compiler":http://code.google.com/closure/compiler/ and gzipped, it's under 4KB.

I originally wrote it as part of "Mappiness":http://www.mappiness.org.uk/maps/. There is also "a port for the Leaflet maps API":https://github.com/jawj/OverlappingMarkerSpiderfier-Leaflet, which has fewer features.

h3. Doesn't clustering solve this problem?

You may have seen the "marker clustering library":https://github.com/googlemaps/v3-utility-library/tree/master/markerclusterer, which also helps deal with markers that are close together.

That might be what you want. However, it probably isn't what you want (or isn't the only thing you want) if you have markers that could be in the exact same location, or close enough to overlap even at maximum zoom. In that case, clustering won't help your users see and/or click on the marker they're looking for.

OverlappingMarkerSpiderfier plays nice with clustering, and you can use them together. Once you get down to a zoom level where individual markers are shown, these markers then spiderfy happily. But you may need to set the @maxZoom@ parameter on the clusterer to ensure that it doesn't cluster identical points all the way to the map's maximum zoom level (@14@ or @15@ have been "suggested":https://stackoverflow.com/questions/9726920/integrating-spiderfier-js-into-markerclusterer-v3-to-explode-multi-markers-with as sensible values).

h2. What's new?

h3. 1.0

Version 1.0 brings three key enhancements:

Also, a few potentially breaking changes:

If you've only been using @addMarker()@ and @removeMarker()@, and you always add or remove your markers from the map at the same time as the spiderfier, you won't need to do anything new.

h3. 0.3

Breaking changes:

h2. Demo

There are three demo maps, showing increasing levels of functionality and complexity. Studying the source of these may well be the best way to understand how to use this library.

In all cases, the data is randomised: reload the map to reposition the markers.

h2. Download

Download "the compiled, minified JS source":http://jawj.github.com/OverlappingMarkerSpiderfier/bin/oms.min.js.

Or use it straight from "cdnjs":https://cdnjs.com/libraries/OverlappingMarkerSpiderfier: @@.

h2. How to use

See the source of the demo maps, or follow along here for a slightly simpler usage with commentary.

h3. Simplest integration

Create a map and an InfoWindow as per usual:

bc. var mapElement = document.getElementById('map_element'); var map = new google.maps.Map(mapElement, { center: new google.maps.LatLng(50, 0), zoom: 6 }); var iw = new google.maps.InfoWindow();

Now create an @OverlappingMarkerSpiderfier@ instance associated with the map (the three options set here are not required, but will save some memory and CPU in simple use cases like this one):

bc. var oms = new OverlappingMarkerSpiderfier(map, { markersWontMove: true, markersWontHide: true, basicFormatEvents: true });

As you create your markers, instead of attaching @click@ listeners, attach @spider_click@ listeners.

And, instead of adding them to the map with @marker.setMap(map)@, add them to your @OverlappingMarkerSpiderfier@ instance (and the map too) with @oms.addMarker(marker)@.

bc. for (var i = 0, len = window.mapData.length; i < len; i ++) { (function() { // make a closure over the marker and marker data var markerData = window.mapData[i]; // e.g. { lat: 50.123, lng: 0.123, text: 'XYZ' } var marker = new google.maps.Marker({ position: markerData }); // markerData works here as a LatLngLiteral google.maps.event.addListener(marker, 'spider_click', function(e) { // 'spider_click', not plain 'click' iw.setContent(markerData.text); iw.open(map, marker); }); oms.addMarker(marker); // adds the marker to the spiderfier and the map })(); }

h3. Marker formatting

New in version 1.0, you can add marker formatting listeners to differentiate between markers that will and won't spiderfy (and that are and aren't spiderfied).

You can either add a @format@ listener to the spiderfier instance (simplest if all your markers look the same, aside from their spiderfying status), or a @spider_format@ listener to each individual marker (useful if you independently have different marker styles).

The first of these options, as seen in the "standard demo source":https://github.com/jawj/OverlappingMarkerSpiderfier/blob/gh-pages/demo-2.html, looks something like this:

bc. oms.addListener('format', function(marker, status) { var iconURL = status == OverlappingMarkerSpiderfier.markerStatus.SPIDERFIED ? 'marker-highlight.svg' : status == OverlappingMarkerSpiderfier.markerStatus.SPIDERFIABLE ? 'marker-plus.svg' : status == OverlappingMarkerSpiderfier.markerStatus.UNSPIDERFIABLE ? 'marker.svg' : null; marker.setIcon({ url: iconURL, scaledSize: new google.maps.Size(23, 32); // makes SVG icons work in IE }); });

For an example of the second, per-marker option, see the "fancy demo source":https://github.com/jawj/OverlappingMarkerSpiderfier/blob/gh-pages/demo-3.html.

Again, thanks to "Graphileon":http://www.graphileon.com/ for sponsoring this feature.

h2. Docs

h3. Loading

The Google Maps API code changes frequently. Some earlier versions had broken support for z-indices, and the 'frozen' versions appear not to be as frozen as you'd like. At this moment, the 'stable' version 3.27 seems to work well, but do test with whatever version you fix on. Sometimes, glitches can be fixed by setting the @optimized: false@ on your markers.

To enable async/deferred loading, as used by the Google Maps library itself, you can either provide a top-level function named @spiderfier_callback@, or specify a @spiderfier_callback@ parameter that names some other top-level function in the script @src@ attribute (i.e. @ Githubissues.

  • Githubissues is a development platform for aggregating issues.