GithubHelp home page GithubHelp logo

jawj / overlappingmarkerspiderfier Goto Github PK

View Code? Open in Web Editor NEW
835.0 48.0 238.0 257 KB

Deals with overlapping markers in Google Maps JS API v3, Google Earth-style

Home Page: http://blog.mackerron.com

Shell 0.26% JavaScript 87.85% CoffeeScript 11.89%

overlappingmarkerspiderfier's Introduction

Overlapping Marker Spiderfier for Google Maps API v3

Ever noticed how, in Google Earth, 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, 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, minified with Google’s Closure Compiler and gzipped, it’s under 4KB.

I originally wrote it as part of Mappiness. There is also a port for the Leaflet maps API, which has fewer features.

Doesn’t clustering solve this problem?

You may have seen the marker clustering library, 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 as sensible values).

What’s new?

1.0

Version 1.0 brings three key enhancements:

  • Easy differential formatting of markers that will and won’t spiderfy on click (via a new event listener). Thanks go to Graphileon for sponsoring this much-requested feature.
  • Simplified integration, via a per-marker spider_click listener that’s a direct replacement for the standard click listener.
  • Support for async/deferred loading in parallel with Google Maps. We no longer require the Google Maps API to be loaded first.

Also, a few potentially breaking changes:

  • The methods addMarker(), removeMarker() and clearMarkers() have been renamed to trackMarker(), forgetMarker() and forgetAllMarkers(). This better reflects what they do. At the same time, new shortcut methods addMarker(), removeMarker() and removeAllMarkers() have been added — these call trackMarker(), forgetMarker() or forgetAllMarkers() respectively and also add or remove the relevant marker(s) from the Google Map itself.

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.

0.3

Breaking changes:

  • The willSpiderfy(marker) and markersThatWillAndWontSpiderfy() methods were replaced by the (similar, but different) markersNearMarker(marker) and markersNearAnyOtherMarker() methods. This should only worry advanced users.

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.

Download

Download the compiled, minified JS source.

Or use it straight from cdnjs: <script src="https://cdnjs.cloudflare.com/ajax/libs/OverlappingMarkerSpiderfier/1.0.3/oms.min.js"></script>.

How to use

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

Simplest integration

Create a map and an InfoWindow as per usual:

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):

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).

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
  })();
}

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, looks something like this:

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.

Again, thanks to Graphileon for sponsoring this feature.

Docs

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. <script src="/path/to/oms.min.js?spiderfier_callback=myCallbackFunction"><script>).

Construction

new OverlappingMarkerSpiderfier(map, options)

Creates an instance associated with map (a google.maps.Map).

The options argument is an optional Object specifying any options you want changed from their defaults. The available options are:

markersWontMove (default: false)
markersWontHide (default: false)

By default, change events for each added marker’s position and visibility are observed (so that, if a spiderfied marker is moved or hidden, all spiderfied markers are unspiderfied, and the new position is respected where applicable).

However, if you know that you won’t be moving and/or hiding any of the markers you add to this instance, you can save memory (a closure per marker in each case) by setting the options named markersWontMove and/or markersWontHide to true.

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

basicFormatEvents (default: false)

By default, marker status is recalculated for all markers on any relevant change, triggering any 'spider_format' marker listeners and 'format' instance listeners, with one of the following status values:

OverlappingMarkerSpiderfier.markerStatus.SPIDERFIED
OverlappingMarkerSpiderfier.markerStatus.SPIDERFIABLE
OverlappingMarkerSpiderfier.markerStatus.UNSPIDERFIABLE

This recalculation can be quite CPU intensive for large numbers of markers, so if you don’t intend to format markers differently depending on whether they will spiderfy when clicked, you should opt out of this behaviour by setting basicFormatEvents to true.

Then the 'spider_format' and 'format' listeners will receive only these status values instead:

OverlappingMarkerSpiderfier.markerStatus.SPIDERFIED
OverlappingMarkerSpiderfier.markerStatus.UNSPIDERFIED

keepSpiderfied (default: false)

By default, the OverlappingMarkerSpiderfier works like Google Earth, in that when you click a spiderfied marker, the markers unspiderfy before any other action takes place.

Since this can make it tricky for the user to work through a set of markers one by one, you can override this behaviour by setting the keepSpiderfied option to true. Note that the markers will still be unspiderfied if any other marker than those in the currently spiderfied set are clicked.

ignoreMapClick (default: false)

By default, clicking an empty spot on the map causes spiderfied markers to unspiderfy. Setting this option to true suppresses that behaviour.

nearbyDistance (default: 20).

This is the pixel radius within which a marker is considered to be overlapping a clicked marker.

circleSpiralSwitchover (default: 9)

This is the lowest number of markers that will be fanned out into a spiral instead of a circle. Set this to 0 to always get spirals, or Infinity for all circles.

circleFootSeparation (default: 23)
circleStartAngle (default: pi / 6)

Parameters that determine the positioning of markers when spiderfied out into a circle. The defaults work pretty well for a standard Google Maps marker icon, but you may want to change them for icons that are larger/smaller/differently shaped.

spiralFootSeparation (default: 26)
spiralLengthStart (default: 11)
spiralLengthFactor (default: 4)

Parameters determining the positioning of markers when spiderfied out into a spiral. The defaults work pretty well for a standard Google Maps marker icon, but you may want to change them for icons that are larger/smaller/differently shaped. If you want to know exactly how they work — read the code! But to get the arrangement you’re looking for, you probably just need to experiment.

legWeight (default: 1.5)

This determines the thickness of the lines joining spiderfied markers to their original locations.

Instance methods: managing markers

Note: methods that have no obvious return value return the OverlappingMarkerSpiderfier instance they were called on, in case you want to chain method calls.

trackMarker(marker, listener)

Starts tracking marker (a google.maps.Marker), but does not add it to the map. If listener is specified, it is attached to the marker as a spider_click listener.

addMarker(marker, listener)

Starts tracking marker (a google.maps.Marker) and adds it to the map. If listener is specified, it is attached to the marker as a spider_click listener.

forgetMarker(marker)

Stops marker being tracked, but does not remove it from the map (to remove a marker from the map you must call setMap(null) on it, as per usual, or call removeMarker(marker) instead).

removeMarker(marker)

Stops marker being tracked and removes it from the map.

forgetAllMarkers()

Stops every marker being tracked. Much quicker than calling forgetMarker(marker) in a loop, since that has to search the markers array every time.

This does not remove the markers from the map (to remove the markers from the map you must call setMap(null) on each of them, as per usual, or call removeAllMarkers() instead).

removeAllMarkers()

Stops every marker being tracked, and removes them all from the map. Much quicker than calling removeMarker(marker) in a loop, since that has to search the markers array every time.

getMarkers()

Returns an array of all the markers that are currently being tracked. This is a copy of the one used internally, so you can do what you like with it.

Marker events

New from version 1.0, two new events are supported on your markers.

'spider_click'

The 'spider_click' event is triggered on a marker when it (a) has no markers nearby and is clicked, or (b) has been spiderfied along with nearby markers, and is then clicked. In general, you’ll want to replace your 'click' listeners with 'spider_click' listeners when integrating the OverlappingMarkerSpiderfier library.

'spider_format'

The 'spider_format' event is triggered when the spiderfying status of a marker could have changed. That could be because this marker began to be tracked, because other markers were added, removed or hidden, or because the zoom level changed.

You can use a listener on this event to make a visual distinction between markers that are a) unspiderfied and will not spiderfy when clicked, b) unspiderfied and will spiderfy when clicked, or c) spiderfied.

The listener function receives one argument, which is a status value (one of OverlappingMarkerSpiderfier.markerStatus.SPIDERFIED, OverlappingMarkerSpiderfier.markerStatus.SPIDERFIABLE or OverlappingMarkerSpiderfier.markerStatus.UNSPIDERFIABLE for standard formatting events; or one of OverlappingMarkerSpiderfier.markerStatus.SPIDERFIED or OverlappingMarkerSpiderfier.markerStatus.UNSPIDERFIED if formatting events have been restricted, for efficiency, via the basicFormatEvents option).

Instance methods: managing listeners

addListener(event, listenerFunc)

Adds a listener to react to one of four events.

event may be 'click', 'format', 'spiderfy' or 'unspiderfy'.

For 'click' events, listenerFunc receives one argument: the clicked marker object. You’ll probably want to use this listener to do something like show a google.maps.InfoWindow. Note that this is the traditional method of responding to a marker click, but you may well now find it easier to add a separate 'spider_click' event to each marker instead.

For 'format' events, listenerFunc receives two arguments: a marker object, and a status value (one of OverlappingMarkerSpiderfier.markerStatus.SPIDERFIED, OverlappingMarkerSpiderfier.markerStatus.SPIDERFIABLE or OverlappingMarkerSpiderfier.markerStatus.UNSPIDERFIABLE for standard formatting events; or one of OverlappingMarkerSpiderfier.markerStatus.SPIDERFIED or OverlappingMarkerSpiderfier.markerStatus.UNSPIDERFIED if only basic formatting events have been requested, for efficiency, via the basicFormatEvents option).

For 'spiderfy' or 'unspiderfy' events, listenerFunc receives two arguments: first, an array of the markers that were spiderfied or unspiderfied; second, an array of the markers that were not. Traditionally, one use for these listeners was to make some distinction between spiderfied and non-spiderfied markers when some markers are spiderfied — e.g. highlighting those that are spiderfied, or dimming out those that aren’t. However, the newer 'format' event is now a better and more flexible way to do this.

removeListener(event, listenerFunc)

Removes the specified listener on the specified event.

clearListeners(event)

Removes all listeners on the specified event.

unspiderfy()

Returns any spiderfied markers to their original positions, and triggers any listeners you may have set for this event. Unless no markers are spiderfied, in which case it does nothing.

Instance methods: advanced use only!

These methods were previously provided mainly to enable differential marker formatting according to whether a marker would spiderfy when clicked; since this is now supported explicitly, you’re quite unlikely to need them.

markersNearMarker(marker, firstOnly)

Returns an array of markers within nearbyDistance pixels of marker — i.e. those that will be spiderfied when marker is clicked. If you pass true as the second argument, the search will stop when a single marker has been found. This is more efficient if all you want to know is whether there are any nearby markers.

Don’t call this method in a loop over all your markers, since this can take a very long time.

The return value of this method may change any time the zoom level changes, and when any marker is added, moved, hidden or removed. Hence you’ll very likely want call it (and take appropriate action) every time the map’s zoom_changed event fires and any time you add, move, hide or remove a marker.

Note also that this method relies on the map’s Projection object being available, and thus cannot be called until the map’s first idle event fires.

markersNearAnyOtherMarker()

Returns an array of all markers that are near one or more other markers — i.e. those will be spiderfied when clicked.

This method is several orders of magnitude faster than looping over all markers calling markersNearMarker (primarily because it only does the expensive business of converting lat/lons to pixel coordinates once per marker).

The return value of this method may change any time the zoom level changes, and when any marker is added, moved, hidden or removed. Hence you’ll very likely want call it (and take appropriate action) every time the map’s zoom_changed event fires and any time you add, move, hide or remove a marker.

Note also that this method relies on the map’s Projection object being available, and thus cannot be called until the map’s first idle event fires.

Properties

You can set the following properties on an OverlappingMarkerSpiderfier instance:

legColors.usual[mapType] and legColors.highlighted[mapType]

These determine the usual and highlighted colours of the lines, where mapType is one of the google.maps.MapTypeId constants (or a custom map type ID).

The defaults are as follows:

var mti = google.maps.MapTypeId;
legColors.usual[mti.HYBRID] = legColors.usual[mti.SATELLITE] = '#fff';
legColors.usual[mti.TERRAIN] = legColors.usual[mti.ROADMAP] = '#444';
legColors.highlighted[mti.HYBRID] = legColors.highlighted[mti.SATELLITE] =
  legColors.highlighted[mti.TERRAIN] = legColors.highlighted[mti.ROADMAP] = '#f00';

You can also get and set any of the options noted in the constructor function documentation above as properties on an OverlappingMarkerSpiderfier instance. However, for some of these options (e.g. markersWontMove) modifications won’t be applied retroactively.

Licence

This software is released under the MIT licence.

overlappingmarkerspiderfier's People

Contributors

dtryse avatar ericjustusson avatar jawj avatar jimvanderveen avatar skarnl avatar strykaizer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

overlappingmarkerspiderfier's Issues

Auto-Spiderfy at a certain zoom level

I like OMS, but... is it possible to auto-spiderfy grouped markers at a certain zoom level?

I use markerclusterer.js for Zoom level 1-14, which is very nice and try to make those markers that are at the exact same location to spiderfy automatically at level 15. is there a way to tell all groups to spiderfy at a certain zoom level? oms.spiderfyAll(); or at last oms.spiderfly(marker); or something?

Show count of markers spiderfied

Hello
We are using your script on our site and now we are wondering, if it is possible to add a flag, when initializing the OMS to show the count of markers spiderfied on a certain point, like marker clusterer does. We have a lot of markers, who are directly above eachother and one does not know, if they are spiderfied or not. Would be a nice to have :-D

Unable to remove markers

I can't seem to remove any markers from the map. I'm keeping things as simple as possible, and created a hyperlink to call a function that calls the clearMarkers function and then prints out the number of Markers returned by the getMarkers method. The Markers are getting cleared internally, because after calling the clear method the getMarker method returns 0. Unfortunately the markers are still visible on the map. Any thoughts on what is happening?

function clear_everything() {
window.oms.clearMarkers();
alert(window.oms.getMarkers().size());
}

Thanks,
Robbie

Using mouseover instead of click

I tried using mouseover instead of click event to open markers, but it doesn't seem to work:

From the demo:

      oms.addListener('mouseover', function(marker) {
        iw.setContent(marker.desc);
        iw.open(map, marker);
     });

Possible to change legLength

Hello,

First off thank you for the amazing code, it is JUST what I needed. I'd also like to re-confirm that yes, it works just as expected (for me anyway) in conjunction with the MarkerClusterer code.

My question is: Is it possible to change the LENGTH of the legs as opposed to just the weight? My Markers are slightly larger than normal and you can't see the legs at all because the Markers are so big / close together.

Any ideas?

Thanks again for your great work on this, it's really useful!

Show # markers

How how about having an option to show a label over the pin with the # of markers at that position? That way you can tell the difference between a normal marker and a cluster. Also, it'd mean that we can just use oms and not have to use the google maps clustering library anymore.

mouseover options?

Hello,

Sorry if I'm posting this in the wrong section but I've got a couple of questions:

  1. How do I change a marker's color on mouseover.
  2. How do I change a marker's color on click.
  3. After clicking and unclicking on a spiderfied region of multiple markers, the markers change colors to yellow, how do I keep them in the original color (and only change the color of those that i specifically clicked on after spiderfying them (before unclicking).

If this isn't clear, please let me know and I will re-phrase. Thanks!

get an error

Hi! In console I get this error just loading the script:

TypeError: (intermediate value)(...) is not a function

Can't understand

Only works in Google Chrome!?

I tested the script and mainly used the example code. So nothing special was added.

I'm on Chrome 25 and everything's as expected.
It's not working on ie10, opera 12.14, firefox 16
The map is shown and I don't get any error.
But all the markers are missing.

clearMarkers issue

sylon104 commented on 49657fd

Hi, for the life of me i'm trying to clear markers of the oms object from an outside function. I declared the oms variable as a global one. and try to call oms.clearMarkers() within another function to clear those markers but it does not work. Please advise.

keepSpiderfied never observed

In the function p.spiderListener = function(marker) { ...} around line # 261, 'markerSpiderfied is always null and therefore satisfies the condition, which then always fire off unspiderfy.

Maybe the simple solutions is to just say:
if (!this['keepSpiderfied']){
this'unspiderfy';
}

After Panorama Load SpiderFier stop wokring

I am plotting markers on map, and for same location I am using this Library. What I want is to when each marker clicked the marker Panorama should be display below the map in separate container.

After once Panorama load then SpiderFier stop working and then markers can not Spiderfy again

What could be the issue ?

Thanks in Advance.

Don't Spiderfy in StreetView

When in Street View, clicks on close-together markers cause them to spread out as normal. However, from the Street View POV this just moves the markers away from your position, off into the distance!

Colored Lines Do Not Work on Styled Google Maps

If you use Google's styled maps option (http://code.google.com/apis/maps/documentation/javascript/styling.html) and try to assign a leg color (legColors.usual[mti.ROADMAP] = '#444';), the colors don't work.

When you specify a style, you override a current Google style, like ROADMAP:

var mapOptions = {
mapTypeControlOptions: { mapTypeIds: [google.maps.MapTypeId.ROADMAP, 'custom_style_name'] }
};

So there should be some way to access this new style, but ROADMAP is not the way. Here is a test link. Click the orange marker to the right of the center marker.

http://www.yourmapper.com/mapV3.php?id=174&num=25&lat=38.23469126&lon=-85.7198380

The spiderfied lines should be blue with an orange highlight (as you can see in the Terrain view), but they are the default black with no highlight.

PHP Mysql Example (HELP)

Would it be possible for someone to post a PHP, MYSQL example for using this? I have a database of points I was using for a non Spiderfier map and would like to convert it. Contact me if you would like to take this on as a paid job also. Thanks

markersWontMove and markersWontHide

If markersWontMove and markersWontHide is set to false and the markers are spiderfied and then moved or hidden, I get an error "this.H() is not a function".

Can I get the original position of a marker?

I implemented the plugin in a project and it works really fine.
But there is one thing I really miss: I use the markers position to calculate the distance between my location and the marker. When a marker is spiderfied it changes its position. Due to this my calculations are not correct. In most cases it's just a few meters, but sometimes several hundred meter if I have a large amount of markers and therefor a big spiral.

Long story short: Is there any way to get the markers original position without unspiderfying them?

Uncaught TypeError when only one marker

I've built a search interface enabling the user to search through a list of locations and I'm using OMS on the map that is displayed on the results page.

This works as expected where the results return more than one item (either at the same or separate locations).

However when the results generate only a single item, the map_canvas element displays as a blank grey box without tiles or marker being displayed. Looking in the console, OMS seems to be throwing an Uncaught TypeError:

Uncaught TypeError: Cannot call method 'contains' of undefined
(anonymous function)
jg.(anonymous function).forEach main.js:26
hB
(anonymous function)
O.trigger main.js:19
(anonymous function)
O.trigger main.js:19
$f main.js:25
F.set main.js:24
Zt.(anonymous function).b
(anonymous function)

Possibility to leave markers spiderfied when clicked

Hey there,

first of all: thanks for this great script, really helped me a lot! :-)

Maybe you can help me: Is there a way I could leave the markers spiderfied once I click one of them? Because in your version the user has to go back each time and click on the first marker to see all of the markers again...
Do you know what I mean?

Paul

event bubbling

Hi there,

nice job, thumbs up.
the addListener('click', function(marker){....
is bubbling on my application, how do I prevent from this?
Using this listener to load content with ajax in separate div. The listener is calling the ajax-function four times.

warm regards

Can't drag markers that are spidified

I can't seem to drag markers to a new location once they are in the spidifier. Markers that don't overlap are draggable just fine. Do you have a suggestion. I thought about finding all the markers in the current spider and then telling them to not spidify but that was getting weird and high inefficient because then you can't find the marker you just found. Do you have a good idea for a way to handle this use case.

Problem with 'markersThatWillAndWontSpiderfy()'

Every time I try to call markersThatWillAndWontSpiderfy() or willSpiderfy(marker) I get the following error

Uncaught TypeError: Cannot call method 'fromLatLngToDivPixel' of undefined oms.min.js:20
OverlappingMarkerSpiderfier.d.c oms.min.js:20
OverlappingMarkerSpiderfier.d.markersThatWillAndWontSpiderfy

Leave unspiderfied

Normal behaviour is that when I click to some group of markers, then are displaced (unspiderfied). When is clicked marker from other group then is previous group placed back to theirs positions.
Is there any way to leave markers unspiderfied? If isn't, would be good to have this feature as configurable option.

Marker-Icons

Hello,

it is possible to change the MarkerIcon when markers are spiderfied?

best regrads
MAH

Move marker to center

I often have 50 or more markers that spdify in my application. Is there a way to move a certain marker to the the center of the spidify so its always in the 1st item in the spdified array.

click event trigger problem

When I click on an a spidered marker, it triggers the click X times where X is the number of markers I've loaded into oms. Using 0.3.3

var markers = [];
map = new google.maps.Map(document.getElementById("map"), mapOptions);
oms = new OverlappingMarkerSpiderfier(map);

if (businesses) {
    bounds = new google.maps.LatLngBounds();
    for (var i = 0; i < businesses.length; i++) {
        var business = businesses[i];
        var latlng = new google.maps.LatLng(business.lat, business.lng);
        var icon = icons[business.type].default;
        bounds.extend(latlng);
        markers[i] = new google.maps.Marker({
            id: i,
            position: latlng,
            map: map,
            title: business.name,
            icon: icon,
            userdata: business
        });
        markerLookup[business.id] = i;

        /* google.maps.event.addListener(markers[i], 'click', function() {
            setCurrentMarker(this.id);
            openInfowindow(this.id);
        }); */

        oms.addListener('click', function(marker, event) {
            setCurrentMarker(marker.id);
            openInfowindow(marker.id);
        });

        oms.addMarker(markers[i]);
    }
    map.fitBounds(bounds);
}

Possibility to initialize markers spiderflied?

Thank you for this great work!

I use your Lib in a small map with a few single and some overlapping points. It is perfect to get a proper overview. But is it maybe possible to initialize the markers in the spiderflied mode?

Thank You

Marc

IE9 SCRIPT445: Object does not support this action ERROR

Hello,

The OverlappingMarkerSpiderfier code fails when using Internet Explorer 9 Update Version 9.0.6.

I unmimified the javascript code and when I run it I receive the following error in the IE Developers Tools console:
SCRIPT445: Object doesn't support this action
For line:
n.c.prototype = new o.OverlayView;

Thanks, Wendy

Make it easy to indicate that a marker will spiderfy when clicked

This can currently be achieved using markersNearAnyOtherMarker(), but it's a bit fiddly. I have in mind to support a marker-formatting callback function, which will receive arguments to indicate whether a marker is (a) spiderfied, (b) unspiderfied and unspiderfiable, or (c) unspiderfied but spiderfiable, and be called with appropriate markers at appropriate times.

Cannot run willSpiderfy() immediately after placing marker

My coffee is as follows:

    for job in jobs
      do (job) ->
        latlng = new google.maps.LatLng(job.lat,job.long)
        mk = new google.maps.Marker
          position: latlng
          icon: pin
          title: "#{job.id}"
          optimized: false
          map: map.map
        oms.addMarker(mk)
        console.log oms.willSpiderfy(mk)

However I get a TypeError on willSpiderfy when it is called here

Uncaught TypeError: Cannot call method 'fromLatLngToDivPixel' of undefined

How else can I check which markers will spiderfy either during or after this loop?

Colored Lines Do Not Work on Styled Google Maps

If you use Google's styled maps option (http://code.google.com/apis/maps/documentation/javascript/styling.html) and try to assign a leg color (legColors.usual[mti.ROADMAP] = '#444';), the colors don't work.

When you specify a style, you override a current Google style, like ROADMAP:

var mapOptions = {
mapTypeControlOptions: { mapTypeIds: [google.maps.MapTypeId.ROADMAP, 'custom_style_name'] }
};

So there should be some way to access this new style, but ROADMAP is not the way. Here is a test link. Click the orange marker to the right of the center marker.

http://www.yourmapper.com/mapV3.php?id=174&num=25&lat=38.23469126&lon=-85.7198380

The spiderfied lines should be blue with an orange highlight (as you can see in the Terrain view), but they are the default black with no highlight.

Reference Error:OverlappingMarkerSpiderfier is not defined

Hey,
I am naive to this .I am actually editing old code with marker clusterer After creations of markers if there are more than markers with same location then am creating a OverlappingMarkerSpiderfier instead of clusterer
But every-time i create new OverlappingMarkerSpiderfier (map); code it gives me the error of OverlappingMarkerSpiderfier is not defined.

Please help me what must be the error.

Is it any problem with synchronous call in an asynchronous function.

Please do help me..

var markerCluster;
var map;
var infowindow;
var oms ;
var usualColor = 'eebb22';
var spiderfiedColor = 'ffee22';
var shadow;
var iconWithColor;

function initialize() {
var mapOptions = {
center : new google.maps.LatLng(24.700677, 46.728287),
zoom : 5,
mapTypeId : google.maps.MapTypeId.ROADMAP
};

map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);



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

infowindow = new google.maps.InfoWindow({
    maxWidth : 10
});
 iconWithColor = function(color, number) {
    return 'http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=' + number + '|' + color + '|000000';
};



//createOWS();

}

function loadScript() {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "http://maps.googleapis.com/maps/api/js?libraries=geometry&sensor=false&language=ar&region=SA&callback=initialize";
document.body.appendChild(script);
}

function updateMapData(xhr, status, args) {

if (!args || !args.listingsData)
    return;
var docs = JSON.parse(args.listingsData);
var markers = [];

if(docs)
    $('#results-count').text(docs.length);
else $('#results-count').text(0);

for ( var i = 0; i < docs.length; i++) {
    val = docs[i];
    var latlngAr = val.location_coords.split(",");
    var latLng = new google.maps.LatLng(latlngAr[0], latlngAr[1]);
    markers.push(createMarker(latLng, val));
}
;
var isSame=verifyMarkers(markers);

alert(isSame);
if(isSame==true){
    markerCluster.clearMarkers();
    addMarkerstoOWS(markers);
}
else{
if (markerCluster) {
    markerCluster.clearMarkers();
    markerCluster.addMarkers(markers);
} else
    markerCluster = new MarkerClusterer(map, markers);


}

}

function createMarker(latLng, val) {
var marker = new google.maps.Marker({
position : latLng,
icon: iconBase + 'mapMarker.png',
});

google.maps.event.addListener(marker, "click", function() {

    var cardTemplate = $("#infoWindowTemplate").html();
    var template = cardTemplate.format(val.id, val.neighborhood+", "+val.city, val.type, val.num_of_rooms, val.num_of_lavatories, val.price, val.street, val.agent_org_name);
    infowindow.setContent(template);
    infowindow.open(map, marker);
});

return marker;

}

function fetchDataForCurrentViewport() {
var bounds = map.getBounds();
var center = map.getCenter();
var d = google.maps.geometry.spherical.computeDistanceBetween(bounds
.getNorthEast(), center) / 1000;

fetchListingsData([ {
    name : 'pt',
    value : center.toUrlValue()
}, {
    name : 'd',
    value : d
} ]);

}

function fitBounds(sw, ne) {
var sw_latlng = new google.maps.LatLng(sw.split(',')[0], sw.split(',')[1]);
var ne_latlng = new google.maps.LatLng(ne.split(',')[0], ne.split(',')[1]);
var bounds = new google.maps.LatLngBounds(sw_latlng, ne_latlng);
map.fitBounds(bounds);
fetchDataForCurrentViewport();

}

function verifyMarkers(markers){
//alert("inside verify method");
//alert(markers[0].position.toString().split(',')[0] +"and "+ markers[0].position.toString().split(',')[1]);
var temp=markers[0].position.toString().split(',');
var latBM=temp[0];
var lngBM=temp[1];
var isSame=true;
for(var i=1;i<markers.length;i++){

    if(    (latBM==(markers[i].position.toString().split(',')[0]))   &&   (lngBM==(markers[i].position.toString().split(',')[1]))   ){
        isSame=true;
    }
    else{
        isSame=false;
    }
     }
return isSame;
}


function addMarkerstoOWS(markers){

    alert("inside add OWs method");
    try{

         shadow = new google.maps.MarkerImage(
                'https://www.google.com/intl/en_ALL/mapfiles/shadow50.png',
                new google.maps.Size(37, 34), // size - for sprite clipping
                new google.maps.Point(0, 0), // origin - ditto
                new google.maps.Point(10, 34) // anchor - where to meet map location
              );
     oms = new OverlappingMarkerSpiderfier(map);

    //var iw =  new google.maps.InfoWindow();

        oms.addListener('click', function(marker) {
       // iw.setContent(marker.desc);
            infowindow.setContent("sample");
            infowindow.open(map, marker);
      });
      oms.addListener('spiderfy', function(markers) {
        for(var i = 0; i < markers.length; i ++) {
          markers[i].setIcon(iconWithColor(spiderfiedColor));
          markers[i].setShadow(null);
        }
        infowindow.close();
      });
      oms.addListener('unspiderfy', function(markers) {
        for(var i = 0; i < markers.length; i ++) {
          markers[i].setIcon(iconWithColor(usualColor));
          markers[i].setShadow(shadow);
          markers[i].setShadow(null);
        }
      });




    }catch(err){
        alert("error in creating OverlappingMarkerSpiderfier " + err);
    }

    for(var i=1;i<markers.length;i++){
        oms.addMarker(markers[i]);
    }
}
function createOWS(){

}

String.prototype.format = function() {
var args = arguments;
return this.replace(/{(\d+)}/g, function(match, number) {
return typeof args[number] != 'undefined'
? args[number]
: match
;
});
};

Overlapped marker display

I notice in your demo that some overlapped markers are actually drawn as 2 or more markers slightly offset verticlally. Other overlapped markers are drawn as one marker only (in your website it says these markers have a darker shadow, but I don't notice that.) Anyway, if there was an option to always displaly 2 offfset markers, or else to specify a different image to use for overlapped markers, I think it would be clearer to users that there are overlapping markers. Thank you for a great enhancement.

Spiderfy All?

Hey there, thanks a lot of developing this it's a great plugin.

I'm not sure if you'd thought of this use case, but I'm hoping to use this in a printing scenario, whereby the print view of a map would be much better suited for all markers to be spiderfied rather than having lots of them overlapping.

Is there an easy way I can 'spiderfy all'? is it worth me trying to hack this together or is the underlying code such that this wont be possible without a lot of work? My JS skills are average and the source is a little scary :)

thanks again.
George

Retrieving overlapping markers

Hi,

Thanks for the library, it works really well.
I was wondering if there is a (non-hackish) way to be able to grab all of the overlapping markers for a given marker. Something like

overlapping_markers = oms.getOverlapping(marker);

Which is a zero size array if no overlapping markers, otherwise it contains all of the markers that overlap the given marker -- i.e. exactly those that would be spiderfied.

Thanks!

Wrong marker with wrong info

I have this problem. the script works by opening the markers but when i click to get info it jumps to the wrong marker and opens up its infowindow

var markers = [
    ["<h3>Cape Town International Airpor</h3>", -33.97136111, 18.60427778],
    ['Coogee Beach', -33.97136111, 18.60427778],
    ['Cronulla Beach', -34.028249, 151.157507],
    ['Manly Beach', -33.80010128657071, 151.28747820854187],
    ['Maroubra Beach', -33.950198, 151.259302]
];

function initializeMaps() {
    var myOptions = {
         center: new google.maps.LatLng(-28.696825, 24.695511),
      zoom: 5,
        mapTypeId: google.maps.MapTypeId.ROADMAP

    };


    var map = new google.maps.Map(document.getElementById("map_canvas"),myOptions);
    var infowindow = new google.maps.InfoWindow(); 
    var marker, i;
    var bounds = new google.maps.LatLngBounds();

    var oms = new OverlappingMarkerSpiderfier(map);

    for (i = 0; i < markers.length; i++) { 


        var pos = new google.maps.LatLng(markers[i][1], markers[i][2]);
        bounds.extend(pos);
        marker = new google.maps.Marker({
            position: pos,
            map: map,
        });
         oms.addMarker(marker);

        oms.addListener( 'click', (function(marker, i) {
            return function() {

                infowindow.setContent(this.markers[i][0]);
                infowindow.open(map, marker);

            }

        })(marker, i));
    }
    //map.fitBounds(bounds);

}

google.maps.event.addDomListener(window, 'load', initializeMaps);

Hope you can help me

PHP/MySQL help with code examples

Hi, I'm trying to use spiderfier with PHP/MySQL. Without spiderfier, my code works, but has the Too Many Markers issue. When adding the code for spiderfier, my existing For Loop and marker variables seemed to conflict with what I got from your example. Here is my code for that portion without the spiderfier:

  downloadUrl("ABA_genxml3.php", 
    function(data) {
        var xml = data.responseXML;
        var markers = xml.documentElement.getElementsByTagName("marker");
        for (var i = 0; i < markers.length; i++) {
          var date = markers[i].getAttribute("Date");
          var time = markers[i].getAttribute("Time");
          var species = markers[i].getAttribute("Species");
          var rarity = markers[i].getAttribute("Rarity");
          var location = markers[i].getAttribute("Location");
          var buffer = markers[i].getAttribute("Buffer");
          var submitter = markers[i].getAttribute("Submitter");
          var description = markers[i].getAttribute("Description");
          var point = new google.maps.LatLng(
              parseFloat(markers[i].getAttribute("Lat")),
              parseFloat(markers[i].getAttribute("Lng")));
          var html = "Date: " + date + "<br/>" + "Time: " + time + "<br/>" + "Location: " + location + "<br/>" + "Species: " + species + "<br/>" + "Rarity Code: " + rarity + "<br/>" + "Radius of Uncertainty: " + buffer + " miles" + "<br/>" + "Submitter: " + submitter + "<br/>" + "Additional info: " + description ;
          var icon = customIcons[rarity] || {};
          var marker = new google.maps.Marker({
            map: map,
            position: point,
            icon: icon.icon,
          });
        bindInfoWindow(marker, map, infoWindow, html);
        }   
    });

I couldn't tell if when you use "marker" and "marker.desc" if that refers back to var marker, like I already had defined here, or if it just from the js source. I tried changing the name of var marker, but it seems like a bigger issue.

Also, will my For Loop conflict with the spiderfier one? Should I combine them or use 2 separate ones?

Any help you can give I would greatly appreciate!

feature request: icon offset

Since icons often are offset from the actual point (using iconAnchor: [15, 30] for instance) it makes sense to take this into account (it doesn't seem to do so at the moment) . After all, goal is to clarify which icon is clicked / are present.

Would this make sense?

Best,
Geert-Jan

P.s: and of course thanks for this great plugin!

z-indexing and leg/image locations

I have 2 small issues and can't seem to resolve them.

z-index construct overriding seems to be being ignored.
I'm overriding the z's on construct using the following bit of code.

var oms = new OverlappingMarkerSpiderfier(map,{
markersWontMove: true,
markersWontHide: true,
nearbyDistance: 20,
legWeight: 3,
circleFootSeparation: 35,
spiralFootSeparation: 35,
usualLegZIndex: 100001,
spiderfiedZIndex: 100000,
highlightedLegZIndex: 100002
});

The 2nd issue is I'm using custom icons and sizes different than the standard google ones. This results in the images being out of line with the leg endings. I can't seem to reposition the image in relation to the leg ending in any way. Am I missing something?

Both issues can be seen in this little screenshot
http://i.imgur.com/gCkZS.png

big delay to show infowindow with large ammount of markers (+100)

With more than 100 markers (markerWithLabel) I wait 30 seconds to view an infowindow aflter click, while the processor is at 50% (eight cores).

Maps API v3.13

function PlacemarksTraslados(jsonTraslados) {
JsonData = eval(jsonTraslados);
if (!map) {
startMap();
}
oms = new OverlappingMarkerSpiderfier(map);

var infowindow = new google.maps.InfoWindow({
    content: ""
});

// JsonData.length is about 120 items

for (var i = 0; i < JsonData.length; i++) {

    var myLatlng = new google.maps.LatLng(JsonData[i].Latitud / 1000000, JsonData[i].Longitud /1000000);

    var dirIcon = {
        icon: JsonData[i].PathIcono
    };

    var marker = new MarkerWithLabel({
        id: JsonData[i].NumPedido,
        position: myLatlng,
        labelContent: JsonData[i].NumPedido + '\n' + JsonData[i].NomPrestador,
        title: JsonData[i].NumPedido,
        icon: JsonData[i].PathIcono,
        labelAnchor: new google.maps.Point(36, 0),
        labelClass: "mapMarkerText"
    });

    bindInfoWindow(marker, map, infowindow, "<iframe src=detalleTraslado.aspx?numpedido=" + JsonData[i].NumPedido + "></iframe>");

    marker.setMap(map);
}

}

function bindInfoWindow(marker, map, infowindow, strDescription) {
oms.addListener('click', function (marker, event) {
infowindow.setContent(strDescription);
infowindow.open(map, marker);
});

oms.addListener('spiderfy', function (markers) {
    infowindow.close();
});

oms.addMarker(marker);

}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.