• Stars
    star
    247
  • Rank 164,117 (Top 4 %)
  • Language
    CoffeeScript
  • Created over 12 years ago
  • Updated over 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Deals with overlapping markers in the Leaflet maps API, Google Earth-style

Overlapping Marker Spiderfier for Leaflet

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 meant?

And ever noticed how, when using the Leaflet API, the same thing doesn’t happen?

This code makes Leaflet map markers 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 Leaflet. And it’s under 3K when compiled out of CoffeeScript, minified with Google’s Closure Compiler and gzipped.

It’s a port of my original library for the Google Maps API. (Since the Leaflet API doesn’t let us observe all the event types that the Google one does, the main difference between the original and the port is this: you must first call unspiderfy if and when you want to move a marker in the Leaflet version).

Doesn’t clustering solve this problem?

You may have seen the marker clustering libraries, which also help 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 the maximum zoom level. In that case, clustering won’t help your users see and/or click on the marker they’re looking for.

Demo

See the demo map (the data is random: 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-Leaflet/0.2.6/oms.min.js"></script>.

How to use

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

Create your map like normal (using the beautiful Stamen watercolour OSM map):

var map = new L.Map('map_canvas', {center: new L.LatLng(51.505, -0.09), zoom: 13});
var layer = new L.StamenTileLayer('watercolor');
map.addLayer(layer);

Create an OverlappingMarkerSpiderfier instance:

var oms = new OverlappingMarkerSpiderfier(map);

Instead of adding click listeners to your markers directly via marker.addEventListener or marker.on, add a global listener on the OverlappingMarkerSpiderfier instance instead. The listener will be passed the clicked marker as its first argument.

var popup = new L.Popup();
oms.addListener('click', function(marker) {
  popup.setContent(marker.desc);
  popup.setLatLng(marker.getLatLng());
  map.openPopup(popup);
});

You can also add listeners on the spiderfy and unspiderfy events, which will be passed an array of the markers affected. In this example, we observe only the spiderfy event, using it to close any open InfoWindow:

oms.addListener('spiderfy', function(markers) {
  map.closePopup();
});

Finally, tell the OverlappingMarkerSpiderfier instance about each marker as you add it, using the addMarker method:

for (var i = 0; i < window.mapData.length; i ++) {
  var datum = window.mapData[i];
  var loc = new L.LatLng(datum.lat, datum.lon);
  var marker = new L.Marker(loc);
  marker.desc = datum.d;
  map.addLayer(marker);
  oms.addMarker(marker);  // <-- here
}

Docs

Loading

The Leaflet L object must be available when this code runs — i.e. put the Leaflet API <script> tag before this one. The code has been tested with the 0.4 API version.

Construction

new OverlappingMarkerSpiderfier(map, options)

Creates an instance associated with map (an L.Map).

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

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.

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.

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.

addMarker(marker)

Adds marker (an L.Marker) to be tracked.

removeMarker(marker)

Removes marker from those being tracked.

clearMarkers()

Removes every marker from being tracked. Much quicker than calling removeMarker 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.

Instance methods: managing listeners

addListener(event, listenerFunc)

Adds a listener to react to one of three events.

event may be 'click', '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 an L.Popup.

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. One use for these listeners is 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.

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. Be sure to call this before you call setLatLng on any tracked marker.

Properties

You can set the following properties on an OverlappingMarkerSpiderfier instance:

legColors.usual and legColors.highlighted

These determine the usual and highlighted colours of the lines.

You can also get and set any of the options noted in the constructor function documentation above as properties on an OverlappingMarkerSpiderfier instance.

Licence

This software is released under the MIT licence.

More Repositories

1

IKEv2-setup

Set up Ubuntu Server 20.04 (or 18.04) as an IKEv2 VPN server
Shell
1,248
star
2

zapatos

Zero-abstraction Postgres for TypeScript: a non-ORM database library
TypeScript
1,057
star
3

OverlappingMarkerSpiderfier

Deals with overlapping markers in Google Maps JS API v3, Google Earth-style
JavaScript
819
star
4

subtls

A proof-of-concept TypeScript TLS 1.3 client
JavaScript
333
star
5

mostly-ormless

Ergonomic Postgres from TypeScript
TypeScript
202
star
6

websocket-kinect

Streams Kinect data over binary WebSockets to web clients, which visualise using Three.js/WebGL (currently Chrome and Firefox 11+ only)
JavaScript
172
star
7

iphoto-flickr

Ruby + Applescript to incrementally back up my iPhoto library to Flickr (abandoned due to the advent of Photos.app)
Ruby
105
star
8

github-widget

Simple script to display own GitHub projects on a webpage, ordered by number of watchers
CoffeeScript
81
star
9

web-scraping-for-researchers

Press Cmd + Alt + I
JavaScript
33
star
10

js-xre

A small, focused, forward-looking library for extended Regular Expressions in JavaScript, using ES2015+ tagged template literals
JavaScript
24
star
11

Zot2Bib

A Zotero extension that helps you combine the no-typing-required bibliographic magic of Zotero with the TeX-compatibility and Mac-like goodness of BibDesk
JavaScript
23
star
12

pigeonsim

Fly! Courtesy of this small project linking Kinect -> OpenNI -> Processing -> WebSockets -> CoffeeScript -> Google Earth API
CoffeeScript
19
star
13

MapMarkerAwesome

FontAwesome SVG map markers
JavaScript
16
star
14

OSTN02C

C implementation of Ordnance Survey OSTN02 transformation
C
9
star
15

NSMutableAttributedString-InlineStyles

Lightweight inline formatting (*bold*, /italic/, etc.) for NSAttributedString
Objective-C
8
star
16

linode-to-hetzner-xen

7
star
17

PasscodeViewController

A class to set/challenge for a passcode modelled closely on Apple's own, with some tasteful enhancements (iPhone only)
Objective-C
4
star
18

neon-cf-example

JavaScript
3
star
19

cubicsplines

Three kinds of cubic spline calculation CoffeeScript: natural, clamped and monotonic
CoffeeScript
3
star
20

snow-mo

Contribution to the CASA Xmas decoration contest, and a first play with Three.js (the snowflakes are, with very high probability, unique)
HTML
2
star
21

zapatos-docs

Documentation for the Zapatos Postgres/TypeScript library
PLpgSQL
2
star
22

json-custom-numbers

JSON parsing library that enables custom number parsing
JavaScript
1
star
23

spindlytext

A library that lets you write in the sky with the Google Earth API using KML LineStrings
CoffeeScript
1
star
24

hackdoc

CoffeeScript
1
star
25

mtwist

Concise, tested Mersenne Twister in CoffeeScript/JavaScript
C
1
star
26

zapatos-demo

Bits and pieces I use to check and exercise various bits of Zapatos.
TypeScript
1
star
27

bear-test

JavaScript
1
star
28

robinremote

CoffeeScript
1
star
29

jawj.github.com

1
star
30

perfcompare

JavaScript
1
star
31

GJMLessCrashySKView

An SKView stand-in that doesn't crash your app when audio is playing or freeze up when temporarily disappeared
Objective-C
1
star