4

I am looking for a way to have a fuzzy/blur/gradient outline of a leaflet polygon.

This should help make country outlines more simple (currently, when you zoom in to a svg representing a country, it gets ugly/inaccurate).

I was thinking about attaching CSS attributes to the svg similiar to this: http://www.w3schools.com/svg/svg_fegaussianblur.asp But apparently the svg subelement <g> (used for the leaflet polygon) does not accept this.

I also had a look at <defs> of svg (see here: http://geoexamples.blogspot.be/2014/01/d3-map-styling-tutorial-ii-giving-style.html) but have no clue in applying this to leaflet.

http://leafletjs.com/examples/quick-start-example.html

example illustration

nauti
  • 1,396
  • 3
  • 16
  • 37

1 Answers1

4

You would first need to put the actual filter element into the svg element of the map, otherwise assigning a filter to a path or g won't work because the filter will be undefined. So you're going to need to do this in Javascript. But assigning a filter by classname in CSS is as far as i can see impossible because it will only work with the url() function of CSS. That won't fly with the dynamic SVG embedded in Leaflet's overlaypane. You can however assign it with Javascript:

// Get the SVG element from the overlayPane
var svg = map.getPanes().overlayPane.firstChild,
    // Create filter element
    svgFilter = document.createElementNS('http://www.w3.org/2000/svg', 'filter'),
    // Create blur element
    svgBlur = document.createElementNS('http://www.w3.org/2000/svg', 'feGaussianBlur');

// Set ID attribute of filter
svgFilter.setAttribute('id', 'blur');

// Give room to blur to prevent clipping
svgFilter.setAttribute('x', '-100%');
svgFilter.setAttribute('y', '-100%');
svgFilter.setAttribute('width', '500%');
svgFilter.setAttribute('height', '500%');

// Set deviation attribute of blur
svgBlur.setAttribute('stdDeviation', 3);

// Append blur element to filter element 
svgFilter.appendChild(svgBlur);
// Append filter element to SVG element
svg.appendChild(svgFilter);

After that you can use the filter on polygons, linestrings, etc:

// Creating a polygon and adding to the map
var polygon = L.polygon([[10, 10],[-10,10], [-10,-10],[10,-10]]).addTo(map);

// Set filter attribute on the polygon
polygon._path.setAttribute('filter', 'url(#blur)');

That's it, here's a working example on Plunker: http://plnkr.co/edit/JTNgeXuiBaL8LIbmkVjz?p=preview

iH8
  • 27,722
  • 4
  • 67
  • 76
  • Thanks! That's very close to what I want to achieve. I modified your plunker a bit: http://plnkr.co/edit/xmjgwxSAo4TJx7ZfgAhr?p=preview Only one problem: when you scroll out, the edges of the ` become very sharp. Any idea to make it "overflow" the edges of the `? – nauti Jan 30 '15 at 14:12
  • No thanks, you're welcome, just accept the answer if it's the solution to your problem. I see what you mean. I've set the blur on the actual `path` element not on the `g` element, but i've tried setting it on the `g` element: `polygon._container.setAttribute('filter', 'url(#blur)');` (container is the actual `g` element) and that renders exacty the same when zooming. When i have some time i'll do some more experimenting but i'm guessing that's just the way it is or would need to do something expensive like removing and add the polygons on zoom if that will work and is an option performancewise – iH8 Jan 30 '15 at 14:21
  • Yes I guess ` have no actual width or style, but is only the container for the svg children, here a path. So the overflow (if possible) would have to be applied on the path element. But adding overflow="visible" does not seem to do anything. – nauti Jan 30 '15 at 14:24
  • After some research, i know now that overflow won't work: "The overflow attribute applies to elements that establish new viewports (see below), elements and elements. For all other elements, the property has no effect." https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/overflow I think the solution lies in `filterUnits`, see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion I've had some luck with setting `width` and `height` of the filter but i think the key is the `x` an `y` coordinates. See update plunk: http://plnkr.co/edit/JTNgeXuiBaL8LIbmkVjz?p=preview – iH8 Jan 30 '15 at 15:16
  • Sorry forgot to freeze :) As you can see, it works correctly on the right and bottom now which leads me to think that moving the actual filter effect region by using the `x` and `y` coordinates will do it. Haven't had any luck with that so far, because i guess i need the correct coordinates for the path and i haven't got much time at the moment to test/play around now. – iH8 Jan 30 '15 at 15:36
  • That's it! http://plnkr.co/edit/tis1feSfRnt3VZ8g9qOf?p=preview Adding the two lines svgFilter.setAttribute('x', '-100%'); svgFilter.setAttribute('y', '-100%'); did the trick – nauti Jan 30 '15 at 15:57
  • Way to go :) I'll edit that into the answer, for when other users with the same question find it. Glad it's working now, gave an upvote, nice question! – iH8 Jan 30 '15 at 16:01