Mapbox-gl-js: Allow external icon-image

Created on 10 Nov 2014  路  34Comments  路  Source: mapbox/mapbox-gl-js

Right now the only option for symbol.icon-image is a string in reference to a precompiled sprite sheet that's undocumented and very difficult for any normal user to be able to update. If the icon-image property would also take a URL to an image and render that, it would be fantastic. This would also mean great support for custom markers, which is a huge missing piece from mapbox gl right now. Bonus points if icon-image would accept svgs!

Most helpful comment

To those of you arriving here from a very frustrating Google search, external/generated icons _can_ be passed to icon-image if you use map.loadImage() and map.addImage() first.

Examples:
Add an icon to the map
Add a generated icon to the map

All 34 comments

@jenius check out our style spec-- under sprite, you can put in the URL to your own custom-compiled sprite sheet. Just make sure you format the address it correctly (.json and .png will the appended automatically, as will @2x for retina screens). To apply the icon for your symbol, reference your symbol's name (as defined in your sprite.json) under icon-image.

Let me know if you have any questions on making either the images or accompanying JSONs.

Hey @peterqliu -- thanks for the response! So this solution would require pre-compiling out images into a sprite sheet. There are a few problems here:

  1. They have to be raster. Would love to just add a svg and have it be vector. This would work much better with the vector map anyway.
  2. It would not work at runtime. So for example if you hit an API and get back people's names, photos, and locations to plot on the map, you wouldn't be able to do this because you'd have to have run a build process beforehand to make those images available.
  3. The documentation for generating your own sprite sheet is severely lacking. Like, not actually present at all. What is a sprite.json? What goes in it? How should the folder of images be formatted? What tasks should be run to link them up? This is such an involved process that it really needs a guide of specific steps you can take to make it happen.

@jenius Great points. Sorry about the lack of docs. We're planning on some major changes to how icon sprites work, so the lack of documentation is somewhat a reflection of that. When things solidify we plan on having better docs and much better tooling support (e.g. an editor that will create the sprite for you automatically). We'll also keep the dynamic icon use case in mind.

Would be awesome. I'm working on an application right now that is banking on dynamic icon support, and will have to switch back to the non-gl version very sadly if that's not somewhat close on the roadmap, which I'm afraid it probably isn't.

Either way, would be happy to help out with this stuff if I can, and would appreciate any updates or rough timeframes on any of it!

@jenius shucks that we can't help with dynamic support yet, but if you decide to use static sprites in the future, here's a crash course (writing this partly for you, and partly as a jumpstart for future docs).

_Folder setup_

My folder when making maps looks like this:

screen shot 2014-11-11 at 6 14 00 pm

index.html is the page itself, style.json is my stylesheet. For sprites, I make a sprite.png with all my raster images compiled, and sprite.json tells the renderer where in the PNG to fetch each sprite (more on this below). I make a second version of each for retina screens, hence the @2x files.

To link up my stylesheet and sprite assets, I store the assets' filenames in the stylesheet's sprite property (since my assets here are just called sprite, it would read "sprite": "sprite" )

_Defining the sprites_

Let's take a typical sprite PNG image-- looks something like this:

If we check out the corresponding sprite.json, the first object is:

marker-24: { x: 0, y: 0, width: 24, height: 24, pixelRatio: 1, sdf: false }

This tells the renderer that a sprite called marker-24 is 24px wide, 24px high, and its upper-left hand corner is at x=0 y=0. Looking at the PNG, that's the very first sprite above, next to the giraffe. We make one of these objects for each sprite.

_Putting it together_

Let's say we're trying to use marker-24 in a map, on a retina screen.

1) In style.json, we reference marker-24 in a paint property, like icon-image.

2) In style.json, the renderer will grab the sprite assets' filename ( sprite ) and figure out the type of screen we're on (retina), to assemble the expected JSON filename ( sprite + @2x + .json ).

3) Once it finds this file, it will look for a marker-24 object there, and fetch the x/y/width/height values therein.

4) It will then seek the corresponding PNG file to the JSON ( [email protected] ), and there find the portion of the image as defined by the four values above. This is the portion that is applied to your map.

Awesome, thank you! Exactly what I was after. Looking forward to how the changes to sprites work out, this should get me through it for now :grinning:

Remaining issues here are covered by #358.

Thanks for the explanation @peterqliu. Did anyone manage to use custom markers and would share a working example? For some reason I can't get them to show up linking to either Mapbox or custom style and sprites. See this question on SO. Thanks.

+1 @fallenartist

Hello from 2016 all of the above 2014/15 comments :)

So, we're switching from Google Maps to Mapbox GL, and I've just come across this. The necessity of a sprite sheet is an absolute deal breaker for us. Has this been addressed at all? If not, we'll need to switch to Mapbox.js (leaflet)

@lukerollans what's your use case? Mapbox Studio now makes the sprite sheet for you, though it still means you prepare it in the map style ahead of time.

@peterqliu we are a travel companion application, in which our subscribers add their businesses so travelers can find them easily. Within the app is a map page and a "featured image" is used as the map marker. So, they're completely user defined and quite variable.

What does sdf mean in sprite.json? You have it set to false and so do all the definitions in bright.json.

I can't seem to apply colours to the maki icon sprite sheet .. do I need to make my own for each set of colours I want?

@aidanlister Maki does not support the symbol-color property because it is not an SDF icon set. You must make your own for each set of colors you want. You may find Maki Chef useful.

For anyone who wants to generate their own sprite sheets for Mapbox from SVG icons using gulp, I've created a gulp plugin that wraps Mapbox's spritezero package.

You can find the gulp-spritezero package here.

The only thing it doesn't do right now is generate SDF images鈥擨 haven't figured that bit out yet. So if anyone knows how to do this, please point me in the right direction or create a PR.

There hasn't been much communication about this yet but we are seriously considering dropping SDF support because it is undocumented and unused. See https://github.com/mapbox/mapbox-gl-style-spec/issues/444 for more.

why not use svg? Whats your ideas for the future?

https://github.com/rev22/svgl ?

what is the purpose of generating sdf/png files when dealing with vector gfx?
Is there a method to use svg in opengl on your maps?

Why can i just add my Image url to layout

map.addLayer({
    "id": "uck1",
    "type": "symbol",
    "source": "uck1",
    "layout": {
        "icon-image": "http://x.x.x.x/an.png"
    }
});

@koolkarni Icons are supplied via a sprite sheet, not via a single file per (potential) feature.
@lucaswoj What is the current stance on SDF icons? We are considering to open source our sprite sheet generator. A simple cli tool to generate a (sdf) sprite sheet from a directory of SVG files.

No official stance on SDF icons or per-feature icons at the moment. I'm personally in favor of using an established standard + icon-colorize rather than our undocumented SDF standard.

To those of you arriving here from a very frustrating Google search, external/generated icons _can_ be passed to icon-image if you use map.loadImage() and map.addImage() first.

Examples:
Add an icon to the map
Add a generated icon to the map

@mgd722 there's a limitation of 500 icons per sprite right? And only one sprite per map can be loaded, map.loadImage() add the new image to the sprite, as far as I know, our situation is that we are using the symbol layer to implement custom markers and we are reaching the limit of 500 images with the images of the markers and the svgs used by our custom mapbox maps (our layers). Any ideas on how can we sort this issue, other alternatives?

@santinogue There is a complimentary map.removeImage method: https://www.mapbox.com/mapbox-gl-js/api/#map#removeimage

We are using this successfully to switch out icon which are not being displayed right now. If you want to display a lot more icon simultaneously you are going to run into problems.

@Scarysize thanks for your answer, but removeImage wont help us. For example, we have copied the street layer from mapbox and made some modifications, we load the sprite containing the svgs for that map and other custom ones, so there are like 400 (or more) icons on that sprite, that leves 100 icons or less to use with the symbol layers (our custom markers), and we want to display them at the same time.

Okay, but be aware that the limit of icons is not fixed to 500. The limit is the maximum size of the internal sprite sheet, which is limited to 1024x1024px (2048x2048 on retina). So you might squeeze some more icons into your style by using smaller images.

Ok, the 500 icons restriction is specified in the API documentation https://www.mapbox.com/api-documentation/#sprites, anyway, if the limit is 500 or 1000, if we keep adding styles or new icons, we will reach the limit eventually, is this something that want's to be supported?

@santinogue @Scarysize Are you sure these are hard limits now? After the internal changes to the SpriteAtlas, I believe there might be only hardware or internal memory that would limit the amount of icons you could add using addImage().

@dagjomar I'm not sure, but the documentation led me to think that. Maybe someone can clarify if this limits are still there, and the docs should be updated if not the case.

The limits documented at https://www.mapbox.com/api-documentation/#sprites apply to the sprites web API (/styles/v1/{username}/{style_id}/sprite/...). There is no explicit limit to the number of images that can be added via the Mapbox GL JS addImage API -- only the effective (and device specific) limits imposed by available RAM and maximum WebGL texture size.

I used map.loadImage( ) and map.addImage( ). It works fine when the image is "http://.........". I have created my custom icons which lay on my server where my index.html is also there (in the same folder). But it seems to pick the icons mentioned as "http://......" but not the icons in my folder. It works fine on my local machine. Is there a way around to this?

@shrutikar It sounds like you're asking about loading icons over protocols other than http://, such as via the local file system (file://). Protocols other than http:// may work to some extent but cannot be relied upon. You'll save yourself some trouble if you always use the http:// protocol, including using a local http development server on your computer.

StackOverflow is a better place for questions like this in the future. We try to reserve GitHub issues for feature requests and bug reports.

If the icon-image property would also take a URL to an image and render that, it would be fantastic.

map.addLayer({
"id": "uck1",
"type": "symbol",
"source": "uck1",
"layout": {
"icon-image": "http://x.x.x.x/an.png"
}
});

Out of interest, what is the technical reason for this not being possible? Is there some fundamental issue here, or is this just not a development priority?

It's possible to create images by creating a DOM element and attaching that to the map, though this is presumably rather inefficient.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

stevage picture stevage  路  3Comments

rigoneri picture rigoneri  路  3Comments

aendrew picture aendrew  路  3Comments

stevage picture stevage  路  3Comments

iamdenny picture iamdenny  路  3Comments