dontHaveName
dontHaveName

Reputation: 1937

Leaflet image mask

Is there a way of displaying images with a geoJson mask in Leaflet/another map library? I took "choropleth map of US States" example and I'd like to display a different background image in each state. How can I achieve it?

Here's my code https://jsfiddle.net/d37zg8f9/, as you can see the images overflow from each state. In a perfect world something like that can be achieved by following HTML:

<svg>
    <mask id="mask">
        <path d=".."></path>
    </mask>
    <image mask="url(#mask)" href=".."></image>
</svg>

But I'm not sure if there's a way of injecting that into Leaflet.

Upvotes: 1

Views: 780

Answers (1)

clickbait
clickbait

Reputation: 2998

https://jsfiddle.net/9rzt2uoe/29

You can use the fill property in CSS to fill SVG paths with SVG patterns.

We need an SVG to hold a bunch of patterns that each contain an image:

<body>

   <div id='map'></div>

   <svg id='patterns' width="0" height="0">
      <defs id='defs'>
      </defs>
   </svg>

</body>

In onEachFeature, instead of adding image overlays on the map, images are added as patterns in defs in the patterns SVG:

let i = 300;

const svgPatternsDefs = document.getElementById('defs');

function onEachFeature(feature, layer) {
   const imageUrl = `https://picsum.photos/${i++}`;
   const imageBounds = layer.getBounds();
   const imageOverlay = L.imageOverlay(imageUrl, imageBounds);
   //imageOverlay.addTo(map);
   const imageId = `picture-${i}`;
   defs.innerHTML += `
      <pattern id='${imageId}' width="1" height="1" viewBox="0 0 100 100" preserveAspectRatio="none">
         <image xlink:href='${imageUrl}' width="100" height="100" preserveAspectRatio="none"></image>
      </pattern>
   `;
   Promise.resolve().then(() => { // defers execution
      layer.getElement().style.fill = `url(#${imageId})`;
   });
}

layer.getElement() returns undefined until the layers get added to the map. Code execution can be deferred with Promise.resolve().then

const geoJson = L.geoJson(statesData, {
   onEachFeature,
   style : {
      "fillOpacity" : 1
   }
}).addTo(map);

fillOpacity is 0.2 by default and should be set to 1 for images to be shown correctly.

Upvotes: 1

Related Questions