Leaflet maps marker power

Picking the right markers for your maps will squeeze maximum visualisation power out of your data and delight your audience.

Leaflet is the go-to totally free, open-sourced, lightweight, lightning fast solution for JavaScript maps that operate as smooth as native apps.
This article gives a few pointers for those who want to take standard Leaflet maps and markers to the next level.

Maps are strong visual elements and map markers can convey a lot of information. You can do engaging things with moving and rotating markers and markers with added indicators reflecting the state of a location.

Every example in this article comes with a link to the self-contained source code, as a GitHub gist file, for you to study. Alternatively, download the whole set from this GitHub repository. When downloaded, a double-click on any of the .html files will open the interactive map corresponding to the article in your browser.

1 The bare basics
See if you can get this first super-simple Leaflet map from the set to open on your laptop (gist 1). It should look like this:

Leaflet basic map with default marker and balloon
Leaflet basic map with default marker and balloon (gist 1).

In terms of the JavaScript to achieve the above, you create a map object with L.map, center and zoom to the desired level (e.g. 9) with setView, add a marker with L.marker and select the tile set you wish to use with addLayer. In this example we’ve gone for the Wikimedia tile layer, which has a clean uncluttered feel about it:

 const markerOptions = { draggable: true } // allow mouse-dragging const myMap = L.map('map-placeholder').setView([-37.75, 145], 9) const myMarker = L.marker([-37.8, 145], markerOptions).addTo(myMap) myMap.addLayer(L.tileLayer(
{ attribution: '© OpenStreetMap contr.' }

The above code relies on the accompanying HTML to feature a div element with id “map-placeholder” where the map will be inserted. It is important that this div has its height set, either via CSS or in the HTML. Like so, for instance:

 <div id="map-placeholder" style=”height: 400px”></div>

The width of the map auto-extends to the container the map is in.

Leaflet’s default marker is the blue pin shown above. You can replace the standard blue pin by something more eye-catching, using an alternative image, supplied by a .png or .svg file (with transparency), a font-icon, an emoji or an inline SVG.

2 Markers created from emojis, font-icons or inline SVGs (gists 2a, 2b, 2c)
A huge advantage of using either emojis, font-icons or inline SVGs is that they infinitely scale. These images are super-sharp at any render size. In addition they don’t require HTTP requests for every image/icon. Although font-icons do require a one-off request to load the entire set they’re part of.
There are pros and cons with all three options, in particular with respect to accessibility. Check the Internet for details.
For now though, to use any of the above in Leaflet, we take advantage of the icon attribute of the L.marker. See gist 2a.

 const markerOptions = {
icon: L.divIcon(iconOptions)

Using the above code the marker HTML will be a <div>, rather than an <img> element. Through the iconOptions you can specify the dimensions of the div, as well as its inner HTML. This is where we can insert something more attractive than the blue pin, like an emoji, for instance.
There are already thousands of emojis to choose from and more are added every year. Emojis require no additional files and are ultra-low bandwidth, as an emoji takes up as little as 3 bytes (yes bytes, not kilobytes) of memory. A few more bytes are required when you apply “modifiers”, like skin tone and gender, or when you join base emojis together, e.g. to create a single image of a family consisting of mum, dad and daughter.

 const iconOptions = {
iconSize : [size, size],
iconAnchor: [size/2, size + 9],
className : 'mymarker',
//A runner emoji, medium skin tone, Zero-Width-Joiner, female:
html: '🏃🏽‍♀' // or: '&#x1f3c3;&#x1f3fd;&#x200d;&#x2640;'

In order for the above iconOptions to work correctly you want to set the size of the div equal to the size of the font.
That’s why the JavaScript code has const size = 50 and the HTML <head> has <style>.mymarker { font-size: 50px; }</style>

Leaflet map with emoji marker
Leaflet map with emoji marker (gist 2a).

If you can’t find an emoji to suit your needs, a font icon may do the job for you (gist 2b).
Building further on the emoji example file, you need to add only two lines of code.
First import the font-icon set you wish to use. Bootstrap 3 for example can be loaded like this (note: Bootstrap 4 has no built-in font-icons but there are plenty of other providers you can use):

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"/>

and then specify the icon you wish to use for the iconOptions attribute, html:

 html: '<span class="glyphicon glyphicon-home"></span>'
Leaflet map with font-icon marker
Leaflet map with font-icon marker (gist 2b).

Font-icons are mono-chromatic. But they don’t have to be black. You can set the colour and size of your font-icon like so:

.mymarker {
color: purple;
font-size: 50px;

One thing to remember is that while font-icons are similar to emojis in the sense that they only take up a couple of bytes, they usually come as part of a larger set that your page has to download in its entirety, even if you only use a couple of icons from the set.
For instance the above Bootstrap3 CSS file, itself 20k in size, under the wraps pulls in a glyphicons file at another 18k.
Looked at it this way, that single marker has a foot print of 38k, which is probably a little bigger than your average marker image file (the default Leaflet marker pin is a .png of 2.5k in size).

That’s why inline SVG’s (gist 2c) can be a great alternative for font-icons. With the inline SVG embedded into your code in the same way that an emoji is, and no necessity for another network request to obtain further files, an inline SVG is lean and loads quickly. Simple SVG icons without a lot of detail can add as little as 1k to your html file size. The campground SVG icon below was taken from Line Awesome. It adds 1550 bytes to the example file, compared to an emoji.

Leaflet map with inline SVG marker
Leaflet map with inline SVG marker (gist 2c).

No need to set the font-size in this case, because an SVG is not a font. You can set the colour of the inline SVG like so:

.mymarker svg path { 
fill: limegreen;

3 Markers with indicators
To add information about locations to your markers without taking up the space that a marker balloon does, you can easily add indicator images and numbers. The numbers may represent things like the price for an overnight stay, water temperature at a swimming beach, the speed of a moving vehicle etc.
Indicators act like dashboard alerts, saying something about the state of the location, e.g. “currently closed”, “toilets available” etc.

Leaflet map with emoji marker and emoji indicators (gist 3).

Gist 3 has the complete code. The essential change to the iconOptions is its html attribute:

 html: '🏛️<div class="indicator">🍽️ 🍷 🔓</div>'

While the CSS to cater for the positioning and size of the indicators looks like this:

 .mymarker { 
font-size: 50px;
.mymarker .indicator {
font-size: 28px;
position: relative;
left: -26px;
bottom: 15px;
white-space: nowrap;

4 Rotated markers and indicators
Markers are great to convey direction too. For instance when your map shows GPS-tracked vehicles, an arrow marker is great to show in which direction the car travels, perhaps with an additional number indicating velocity. Or if your map shows weather conditions, a rotating arrow can indicate both wind direction and wind speed (size of the arrow).

After adding 20 lines of JavaScript (details in gist 4a), you can specify the rotation to be applied to a marker like so:

const markerOptions = {
icon: L.divIcon(iconOptions),
rotation: 70
Leaflet rotated marker, showing direction a vehicle is moving in.
Rotated marker, showing direction a vehicle is moving in.

Naturally when the marker represents a tracked object, both marker location (lat, lon) and rotation angle will change continuously, as the object moves across the map.

This rotation approach works for images (.png, .svg), emoji, font-icons and inline SVG.

In the earlier indicator example the indicators were “baked-in” with the marker, like so:

'🏛️ <div class="indicator">🍽️ 🍷 🔓</div>'

This means that when the marker rotates, the indicators rotate with it. The image below (gist 4a) shows what’s wrong with that. This image depicts the positions of a couple of boats and the compass heading they’re sailing in. One marker indicator is looking great, the other… not so great.

Rotated map markers depicting boats with compass heading.
With rotated markers you normally don’t want your indicators to rotate as well (gist 4a).

In most use-cases when a marker rotates, you’d like the associated indicators to not rotate, and remain horizontal in the same position relative to the marker, for instance a little above and to the right of it.
To prevent the indicators from rotating, we need to reorganise our marker implementation so that the indicators act more like “side-plates”. Rotating the main plate (the marker) should not affect the orientation of the side-plates (the indicators).
However, with marker and indicators “decoupled” we will then have to make sure that when the main plate moves (translates), the side-plate follows. Similarly we’ll also have to capture map zoom events and respond accordingly.

Rotated marker with eye balls indicator as used in the wemapshare app.

An additional ten lines of JavaScript implement the desired behaviour (gist 4b).
Gist 4b is an interactive demo: by clicking around the marker you can rotate it and see how the compass heading updates.

In our wemapshare app (wemapshare.com) we also use indicators in addition to rotating vehicle markers. For instance a pair of eye balls is shown to indicate to a driver that they’re currently being viewed by the follower who they map-shared their trip with.

* * *

Software developer, entrepreneur, happy mapper.