121

Is there a way to randomly change marker-colors in native Leaflet? I'm using svg elements which could be styled. I know that it is possible with mapbox.js

To clarify what I intend to do: If you add markers to the map via a doubleclick or something it should have random colors. To achieve this I wanted to use svg icons for the markers to style them.

This is my code:

myIcon = L.icon({
  iconUrl: "icon_33997.svg",
  iconAnchor: pinAnchor
});

newMarker = L.marker(lat, long], {
  icon: myIcon
});
1
  • 1
    I am not sure what do you mean by "using SVG element which could be styled". Are you using custom marker with DivIcon and inserting <svg> onto map this way? Or your marker just refers to SVG file with Icon? Can you share JSFiddle with your setup? Also what do you mean by "randomly change"? Different, colors for marker randomly chosen when added? Or randomly changing in time or other event? Commented May 10, 2014 at 19:53

20 Answers 20

108

So this is one of the top hits in Google for styling Leaflet Icon, but it didn't have a solution that worked without third parties, and I was having this problem in React as we needed dynamic colours for our routes and icons.

The solution I came up with was to use Leaflet.DivIcon html attribute, it allows you to pass a string which is evaluated into a DOM element.

So for example I created a marker style follows:

const myCustomColour = '#583470'

const markerHtmlStyles = `
  background-color: ${myCustomColour};
  width: 3rem;
  height: 3rem;
  display: block;
  left: -1.5rem;
  top: -1.5rem;
  position: relative;
  border-radius: 3rem 3rem 0;
  transform: rotate(45deg);
  border: 1px solid #FFFFFF`

const icon = Leaflet.divIcon({
  className: "my-custom-pin",
  iconAnchor: [0, 24],
  labelAnchor: [-6, 0],
  popupAnchor: [0, -36],
  html: `<span style="${markerHtmlStyles}" />`
})

Change background-color in markerHtmlStyles to your custom colour and you're good to go.

Map with multiple coloured Markers

3
  • 2
    I had to explicitly set className of the divIcon options to '' to prevent the whitesquares from showing.
    – Timo
    Commented Dec 21, 2017 at 20:35
  • Hi, how do you add the connection line between the markers? Commented Mar 31, 2020 at 6:35
  • 1
    Hi @SittiMunirahAbdulRazak from memory I used a GeoJSON feature collection, because we were using an OSRM server to generate the routes. The react-leaflet library has a Geojson component you can pass this data directly to, you can do the same with Leaflet too without React, here's a plugin they promote on their site liedman.net/leaflet-routing-machine
    – tutts
    Commented Mar 31, 2020 at 12:15
92

Bind in the icons from this site!

https://github.com/pointhi/leaflet-color-markers

Detailed how-to information included on the website.

Use the code below to add a marker icon and just replace the link to the one with the color of your choice. (i.e. marker-icon-2x-green.png to some other image)

var greenIcon = new L.Icon({
  iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-green.png',
  shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
  iconSize: [25, 41],
  iconAnchor: [12, 41],
  popupAnchor: [1, -34],
  shadowSize: [41, 41]
});

L.marker([51.5, -0.09], {icon: greenIcon}).addTo(map);
0
74

A cheap way to change the Leaflet marker colour is to use the CSS filter property. Give the icon an extra class and then change its colour in the stylesheet:

<style>
img.huechange { filter: hue-rotate(120deg); }
</style>
<script>
var marker = L.marker([y, x]).addTo(map);
marker._icon.classList.add("huechange");
</script>

and this will produce a red marker: alter the value given to hue-rotate to alter the colour.

enter image description here

4
  • 5
    This works and is by far the easiest way without creating custom graphics or depending on a library or CDN. Thanks for sharing! Commented Mar 9, 2022 at 23:29
  • 4
    You can also directly change the style (without using a class) if you need more flexibility: marker._icon.style.filter = "hue-rotate(120deg)"
    – Simon
    Commented Dec 6, 2022 at 10:21
  • 2
    any way to do this but with an arbitrary color? Commented Dec 19, 2022 at 19:49
  • With Angular the style needs to be in the root styles.css and not in the component css file
    – Caustix
    Commented Nov 23, 2023 at 16:24
28

Leaflet Markers are stored as files unlike other objects (like Polylines, etc.)

If you want your own markers, you can find The official Leaflet Tutorial that explains how to do it.

After reading this conversation with the main developer I searched for the marker SVG and here it is.

With this you should be able to color the marker the way you want and randomly set their color.

You can also use MakiMarkers to set the color of a marker and use this extension to make some random stuffs. (It's simple and well explained)

2
  • 1
    I know but I want to style them with random color.
    – Chromos
    Commented May 20, 2014 at 20:57
  • Did you try MakiMarkers, as suggested in the edit? Did they work? if so, please accept. If not, can you say why? Any feedback would help others who read this question in the future, just as you were helped.
    – Mawg
    Commented Jan 17, 2021 at 21:23
20

Since you are working with svg elements you could feed your leaflet marker's icon with L.divIcon's html property instead of using iconUrl from L.icon to link your image.

L.marker(latlng, {
    icon: L.divIcon({
        className: 'ship-div-icon',
        html: '<svg>...</svg>'
    })
}).addTo(map);

Then use CSS fill property to color your svg shape. It could be useful to add a class\es to svg's path\s to have precise control

<svg ... >
    <g>
        <path class="ship-icon" ... />
    </g>
    ...
</svg>

At the end, as I also needed to randomly change the marker color I changed directly the path's fill property at the time of creating the marker

var pathFillColor = '#'+(Math.random()*0xFFFFFF<<0).toString(16);

L.marker(latlng, {
    icon: L.divIcon({
        className: 'ship-div-icon',
        html: '<svg ... ><g><path fill="'+pathFillColor+'" ... /> </g></svg>'
    })
}).addTo(map);
1
  • I found this was also the easiest way if you need to style your svg in your react code. An alternative to writing out the svg in your code is the put it in a separate file and load it into your file as a react component. I posted this method also as a reaction, because I can't paste code in the comments.
    – Emmy
    Commented Dec 12, 2019 at 15:46
17

Try the Leaflet.awesome-markers lib -- it allows you to set colors and other styles.

2
  • 4
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From Review
    – loki
    Commented Jan 29, 2018 at 12:55
  • @loki ok, that makes sense. I thought it was best to link the general documentation for the library because maybe their API could change and make my example obsolete? But is it just best practice here to leave an example of using the library?
    – Matt
    Commented Feb 1, 2018 at 2:47
10

You can also use the Google Charts API to get icons (just change 'abcdef' with the hexadecimal color you want:

Examples:

0
8

Ahh an event listener and change the icon through setIcon() method:

createdMarker.on("dblclick", function(evt) {

        var myIcon = L.icon({
            iconUrl: 'res/marker-icon-red.png',
            shadowUrl: 'res/marker-shadow.png'
        });
        this.setIcon(myIcon);
});
6

I found the SVG marker/icon to be best one yet. It is very flexible and allows any color you like. You can customize the entire icon without much of a hassle:

function createIcon(markerColor) {
  /* ...Code ommitted ... */

  return new L.DivIcon.SVGIcon({
            color: markerColor,
            iconSize: [15,30],
            circleRatio: 0.35
  });
}
6

Here is the SVG of the icon.

<svg width="28" height="41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
 <defs>
  <linearGradient id="b">
   <stop stop-color="#2e6c97" offset="0"/>
   <stop stop-color="#3883b7" offset="1"/>
  </linearGradient>
  <linearGradient id="a">
   <stop stop-color="#126fc6" offset="0"/>
   <stop stop-color="#4c9cd1" offset="1"/>
  </linearGradient>
  <linearGradient y2="-0.004651" x2="0.498125" y1="0.971494" x1="0.498125" id="c" xlink:href="#a"/>
  <linearGradient y2="-0.004651" x2="0.415917" y1="0.490437" x1="0.415917" id="d" xlink:href="#b"/>
 </defs>
 <g>
  <title>Layer 1</title>
  <rect id="svg_1" fill="#fff" width="12.625" height="14.5" x="411.279" y="508.575"/>
  <path stroke="url(#d)" id="svg_2" stroke-linecap="round" stroke-width="1.1" fill="url(#c)" d="m14.095833,1.55c-6.846875,0 -12.545833,5.691 -12.545833,11.866c0,2.778 1.629167,6.308 2.80625,8.746l9.69375,17.872l9.647916,-17.872c1.177083,-2.438 2.852083,-5.791 2.852083,-8.746c0,-6.175 -5.607291,-11.866 -12.454166,-11.866zm0,7.155c2.691667,0.017 4.873958,2.122 4.873958,4.71s-2.182292,4.663 -4.873958,4.679c-2.691667,-0.017 -4.873958,-2.09 -4.873958,-4.679c0,-2.588 2.182292,-4.693 4.873958,-4.71z"/>
  <path id="svg_3" fill="none" stroke-opacity="0.122" stroke-linecap="round" stroke-width="1.1" stroke="#fff" d="m347.488007,453.719c-5.944,0 -10.938,5.219 -10.938,10.75c0,2.359 1.443,5.832 2.563,8.25l0.031,0.031l8.313,15.969l8.25,-15.969l0.031,-0.031c1.135,-2.448 2.625,-5.706 2.625,-8.25c0,-5.538 -4.931,-10.75 -10.875,-10.75zm0,4.969c3.168,0.021 5.781,2.601 5.781,5.781c0,3.18 -2.613,5.761 -5.781,5.781c-3.168,-0.02 -5.75,-2.61 -5.75,-5.781c0,-3.172 2.582,-5.761 5.75,-5.781z"/>
 </g>
</svg>
4

Html code for adding map

<div id="map" style="height:480px; width:360px;"></div>

css for loading map

.leaflet-div-icon
{
    background-image: url('http://cloudmade.com/images/layout/cm-logo.png');
}

Logic for changing marker colour


var map = L.map('map').setView([51.5, -0.09], 13);
        
L.marker([51.49, -0.09]).addTo(map)
    .bindPopup('Demo CSS3 popup. <br> Easily customizable.');
        
const myCustomColour = '#5f93ed'
        
const markerHtmlStyles = `
    background-color: ${myCustomColour};
    width: 15px;
    height: 15px;
    font-size:15px;
    text-align:center;
    display: block; 
`
        
function thing(ct) {
    return L.divIcon({
        className: "box",
        iconAnchor: [12, 25],
        labelAnchor: [-6, 0],
        popupAnchor: [0, -15],
        html: `<span style="${markerHtmlStyles}" >M</span>`
    })
}
       
L.marker([51.51, -0.09], {
    icon: thing("hello")
}).addTo(map)
    .bindPopup('divIcon CSS3 popup. <br> Supposed to be easily stylable.');
2

In R, use the addAwesomeMarkers() function. Sample code producing red marker:

leaflet() %>%
addTiles() %>%
addAwesomeMarkers(lng = -77.03654, lat = 38.8973, icon = awesomeIcons(icon = 'ion-ionic', library = 'ion', markerColor = 'red'))

Link for ion icons: http://ionicons.com/

2

In addition to the way @guillermogfer describe, this is the way to do it if you don't want to paste your whole svg code into your react component. You can just place your svg code in a separate file and load it into your code file as a React Component like this:

import { ReactComponent as SatelliteIcon } from "../assets/icons/satellite.svg";

After you do this you can just use your svg as if it is a react component, so like this:

<SatelliteIcon className="svg-icon light-blue" />

What we ended up doing is define some standard svg colors that are used in our application so we can pass them as a class in order to change the color of the svg. If you don't know the color beforehand or don't have a defined set of colors you can dynamically overwrite the properties in your svg file by using the style attribute, something like this:

<SatelliteIcon style={{ stroke: "black" }}/>

In order for this to work it is important that the attributes you are trying to overwrite are set on the element in your svg and not on the individual paths.

In order to get this svg icon to display in leaflet you can use the divIcon as described by @guillermogfer. However, because the html attribute of the divIcon doesn't understand jsx we need to convert it to string first:

L.divIcon({
    className: 'div-icon',
    html: ReactDOMServer.renderToString(
        <SatelliteIcon className="svg-icon light-blue" />
    )
})
2

I liked the simplicity of sil's answer, but I wanted to at least be able to approximate a given colour so my markers could reflect the HTML colours used in my key.

Arbitrary colour markers in Leaflet

I had the colours in CSS format (for example "#cc00ff") so I drew on Wayne's code to split the string into component RGB parts and Kamil Kiełczewski's excellent snippet to convert RGB to HSV as well as the hue-rotate, saturate, and brightness CSS filters to convert from the original blue colour for the marker (which I captured using the colour picker tool) to something close to the colour in the key.

It doesn't do white, that grey is as bright as it gets, and it doesn't do dark colours brilliantly because the white circle disappears so I limited the brightness to no less than 30%.

var c = parseInt(colour.substring(1), 16);
// Extract RGB values as floats between 0 and 1
c = {
    hex: c.toString(16),
    r: ((c & 0xff0000) >> 16) / 255,
    g: ((c & 0x00ff00) >> 8) / 255,
    b: (c & 0x0000ff) / 255
};
// Convert to H between 0 and 360 and SV between 0 and 1
c.v = Math.max(c.r, c.g, c.b);
c.c = c.v - Math.min(c.r, c.g, c.b);
c.h = c.c &&
    ((c.v == c.r) ? (c.g - c.b) / c.c
    : ((c.v == c.g) ? 2 + (c.b - c.r) / c.c
    : 4 + (c.r - c.g) / c.c)); 
c.h = 60 * (c.h < 0 ? c.h + 6 : c.h);
c.s = c.v && c.c / c.v;
delete c.c;
// Colour of the main blue body in the original icon
// although V is taken from the brighter outline colour
var o = {h: 205.9, s: 0.719, v: 0.839}
// Add CSS filters to convert to an approximation of the
// specified colour.
marker._icon.style.filter =
    "hue-rotate(" + (c.h - o.h) + "deg)"
    + " saturate(" + (c.s / o.s) + ")"
    + " brightness(" + Math.max(c.v / o.v, 0.3) + ")";
1

A pretty easy way is to use hue-rotate filter:

https://developer.mozilla.org/fr/docs/Web/CSS/filter-function/hue-rotate()

With this, you can call

var random_value = Math.floor(Math.random() * 360);
marker._icon.style.webkitFilter = "hue-rotate(" + parseString(random_value) + "deg)"; 

where random_value is a number between 0 and 360.

It will apply a color depending on this value.

2
  • 1
    Is this answer better than the existing stackoverflow.com/a/61982880/3124333?
    – SiKing
    Commented Jul 21, 2021 at 22:26
  • I did not see that someone had a similar answer. But in Sil's answer you have to create class for each color you need and as asked in the question, we want to be able to change color randomly. With my answer, you are avec to generate a number randomly in order to change color. Commented Jul 22, 2021 at 9:40
0

Write a function which, given a color (or any other characteristics), returns an SVG representation of the desired icon. Then, when creating the marker, reference this function with the appropriate parameter(s).

0

adding to @tutts excelent answer, I modified it to this:

... includes a caption - where you can use FontAwesome icons or alike ...

var myCustomColour = '#334455d0', // d0 -> alpha value
    lat = 5.5,
    lon = 5.5;

var caption = '', // '<i class="fa fa-eye" />' or 'abc' or ...
    size = 10,    // size of the marker
    border = 2;   // border thickness

var markerHtmlStyles = ' \
    background-color: ' + myCustomColour + '; \
    width: '+ (size * 3) +'px; \
    height: '+ (size * 3) +'px; \
    display: block; \
    left: '+ (size * -1.5) +'px; \
    top: '+ (size * -1.5) +'px; \
    position: relative; \
    border-radius: '+ (size * 3) +'px '+ (size * 3) +'px 0; \
    transform: rotate(45deg); \
    border: '+border+'px solid #FFFFFF;\
    ';

var captionStyles = '\
    transform: rotate(-45deg); \
    display:block; \
    width: '+ (size * 3) +'px; \
    text-align: center; \
    line-height: '+ (size * 3) +'px; \
    ';

var icon = L.divIcon({
    className: "color-pin-" + myCustomColour.replace('#', ''),

    // on another project this is needed: [0, size*2 + border/2]
    iconAnchor: [border, size*2 + border*2], 

    labelAnchor: [-(size/2), 0],
    popupAnchor: [0, -(size*3 + border)],
    html: '<span style="' + markerHtmlStyles + '"><span style="'+captionStyles+'">'+ caption + '</span></span>'
});

var marker = L.marker([lat, lon], {icon: icon})
.addTo(mymap);

and the ES6 version (like @tutts) .. I am using it with vue-leaflet

// caption could be: '<i class="fa fa-eye" />',
function makeMarkerIcon(color, caption) {
	let myCustomColour = color + 'd0';

	let size = 10,    // size of the marker
		border = 2;   // border thickness

	let markerHtmlStyles = `
		background-color: ${myCustomColour};
		width: ${size * 3}px;
		height: ${size * 3}px;
		display: block;
		left: ${size * -1.5}px;
		top: ${size * -1.5}px;
		position: relative;
		border-radius: ${size * 3}px ${size * 3}px 0;
		transform: rotate(45deg);
		border: ${border}px solid #FFFFFF;
		`;

	let captionStyles = `
		transform: rotate(-45deg);
		display:block;
		width: ${size * 3}px;
		text-align: center;
		line-height: ${size * 3}px;
		`;

	let icon = L.divIcon({
		className: 'color-pin-' + myCustomColour.replace('#', ''),
		iconAnchor: [border, size*2 + border*2],
		labelAnchor: [-(size/2), 0],
		popupAnchor: [0, -(size*3 + border)],

		html: `<span style="${markerHtmlStyles}"><span style="${captionStyles}">${caption || ''}</span></span>`
	});

	return icon;
}

var marker = L.marker([lat, lon], {icon: makeMarkerIcon('#123456d0', '?')})
.addTo(mymap);

0

You can adjust the hue in css

img.leaflet-marker-icon {
   filter: hue-rotate(244deg);
}
0

Here is an answer based on tutts answer using CSS variable in order to separate CSS from JavaScript:

:root {
  --marker-color: #424874; /* default color */
}

.marker {
  background-color: var(--marker-color);
  width: 1.5rem;
  height: 1.5rem;
  display: block;
  left: -0.75rem;
  top: -0.75rem;
  position: relative;
  border-radius: 3rem 3rem 0;
  transform: rotate(45deg);
  border: 1px solid #FFFFFF;
}
// assuming data is your GeoJSON content
const poiLayer = L.geoJSON(data, {
    pointToLayer: (feature, point) => { 
        // assuming getColor is a function returning a hex code
        const color = getColor(feature.properties['Category']);
        return L.marker(point, {
            icon: L.divIcon({
                className: "marker-container",
                iconAnchor: [0, 24],
                labelAnchor: [-6, 0],
                popupAnchor: [0, -36],
                html: `
                    <span class="marker" style="--marker-color: ${color};" />
                `
            })
        }); 
    },
});
-2

1) Add the marker 2) find the backgroundcolor attribute for the css and change it. Here it is:
JS

var myIcon = L.divIcon({
      className: 'my-div-icon',
      iconSize: [5, 5]
    });
var marker = L.marker([50,-20], {icon: myIcon}).addTo(map);
    marker.valueOf()._icon.style.backgroundColor = 'green'; //or any color
2
  • 1
    For me with a L.divIcon marker, marker._icon.style.color works.
    – K3---rnc
    Commented Aug 23, 2016 at 15:33
  • 1
    style is not exposed on _icon, assuming _ denotes a private variable that shouldn't be used for public consumption, probably changed in versions.
    – tutts
    Commented Nov 29, 2016 at 16:13

Not the answer you're looking for? Browse other questions tagged or ask your own question.