Search code examples
javascriptmapboxmapbox-gl-jsglyphicons

How can I create SDF-Icon's (used in Mapbox) from PNG?


My task is to change the icon-color of an icon-image in Mapbox. The only way mapbox allow to do this is by using sdf-icons(https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#paint-symbol-icon-color).

By Hour's of searching i couldn't found the easiest way to achieve this. There is an npm module I found is https://www.npmjs.com/package/image-sdf but after using its command on a png to convert it into sdf and then rendering on a map is not giving me finest results.

The command I am using

image-sdf cycle-initial.png --spread 5 --downscale 1 --color black > cycle.png

cycle-initial.png(INPUT) is below:

enter image description here

cycle.png(OUTPUT) is below:

enter image description here

But while using the cycle.png as an Image src is not giving the finest results.

enter image description here

Code snippet:

const img = new Image();
img.addEventListener('load', () => {
            this.mapInstance.addImage('circle-icon', img, { sdf: true });
        }, false);
img.src = cycle;

I request if anyone, please help me if I am doing anything wrong here, or is there any correct way to create sdf-icon to render correctly.


Solution

  • After days of research, I think i found the solution. Also, I want to sum-up creation steps of SDF icon's.

    First, thing first what is SDF in simple words: SDF is a raster format that is optimised for displaying resized, recoloured, and rotated raster images. They are single color.

    Usage: This is my first interaction with SDF in MAPBOX.

    Reason is we cannot change the color of icon(if svg or normal raster png) in symbol layer, you have to provide SDF(specialised png) format in order to achieve it.(https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#paint-symbol-icon-color)

    How to create?

    1. Npm module: https://www.npmjs.com/package/image-sdf Command:
    image-sdf input.png --spread 10 --downscale 1 --color black > output.png
    
    1. Imagemagick: https://imagemagick.org/script/download.php . ImageMagick allows you to do this with one command:
    convert in.png -filter Jinc -resize 400% -threshold 30% \( +clone -negate -morphology Distance Euclidean -level 50%,-50% \) -morphology Distance Euclidean -compose Plus -composite -level 45%,55% -resize 25% out.png
    

    UPDATE

    1. There is also third way of achieving this by using older tag of Maki Library, Where there is only one JS file sdf-render.js can convert your source svg to sdf icon. But please note that this Tag(v0.5.0) is using Older version of JSDOM which is not supported now, To execute this file you have to convert the code of this file using svgdom and @svgdotjs/svg.js modules.

    More Detail's: Box is appearing around Icons in Mapbox on sdf = true

    What i was doing wrong?

    Answer: Before executing the image-sdf command on a png(Please note: it has to be black & white raster path). Resize(Increase) it to around 40-50%.

    Like in my case, I resized the normal raster png and the result's were amazing. Have a look. (Please note: I didn't change a word in code and used the same image-sdf command)

    enter image description here