-
-
Notifications
You must be signed in to change notification settings - Fork 719
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Supported method for retrieving sprite images #2162
Comments
Can you elaborate on the relevant use cases? |
Sure. In OSM Americana we generate custom highway shields using raster sprites as a base "blank" graphic, which is then modified in several ways in order to create the final graphic that's shown on the map. For example, the US National Highway System uses a graphical shield that looks something like this: It is infeasible to generate a graphic for every possible US highway route, of which there are hundreds. Instead, we start with a blank, which might look something like this: To generate the finalized graphic above, the styleimagemissing is hooked when it gets a request for a US 40 graphic. That code then retrieves the blank above, which is stored in the sprite sheet, draws the "40" on top of it, and then invokes Our sprite sheet presently looks like this. Look at all those pretty shield blanks! You'll notice that there are not "basic" shapes, e.g. circles, rectangles, pentagons, etc. These are all drawn in canvas code and inserted on the fly in There are additional variants that we need to support as well for the full proliferation of shields. There are several examples of base shields which take on different colors in different cases. For example, historic US Highways use the same US highway shield, but are drawn in brown instead of black. Rather than add an additional shield blank, we can simply retrieve the existing shield blank, re-color it brown, and then insert it back into the sprite list with But wait, there's more. We also have bannered routes, in which 1-3 banners need to be displayed as part of the shield graphic: The most absurd of these examples is the US 30 Alternate-Truck-Business route, which has a triple-stack of these banners: Alternate approach that doesn't workThe naive approach to shield rendering (the only possibility before #716 was merged) was to say "okay, this road is a certain network, use the shield blank as an icon and then draw text on top in the style". The style would specify a wide blank for a 3-digit route number and a narrow blank for a 2-digit route. The reason this doesn't work is that you can only have one icon, and in the real world, multiple routes can share the same stretch of road, which is called a "concurrency". Additionally, North American cartography expects concurrencies to be displayed as a "snake" of shields that are drawn along the path of the road, as shown here: In order to achieve this, multiple attributes are present in the tile data which lists the routes that are concurrent for a stretch of road. There is no way to say to maplibre "render this list of graphics and draw them in a repeating snake pattern with each graphic upright". However, there are formatted expressions, which accept images, which allows this to happen. Because this methodology accepts images, they must be constructed using runtime styling as I described above. In addition, runtime styling (retrieving blanks, modifying them, inserting the final image back in the map) allows the style author to have pixel-perfect full control over the appearance of the rendered graphic. See OSM American's shieldtest demo for a demonstration of runtime styling in action. In summary, the historical approach to shield drawing (single image representing "most important highway network in a concurrency") is insufficient for the cartographic need to draw a snake of shields for concurrent routes. |
Thanks for this long explanation. It doesn't answer my question. Also the last sentence that you wrote feels like putting even more effort to it while it doesn't solve the problem seems like a waste of everyone's time - if we implement things and they are not adopted by the people that pushed them, it's a very alarming sign... I might have interpret what you wrote wrongly though... |
Let’s take a step back for a moment. The ability to get an item is one of the four basic elements of a CRUD interface. The others are already implemented as I agree with @ZeLonewolf that Setting aside shield rendering and other |
I think there’s been a misunderstanding. @ZeLonewolf isn’t saying we wouldn’t be using this API – we already are! 😅 Rather, he’s highlighting the fact that many OSM-based or GL JS–based maps have historically settled for a basic design for marking routes that falls short of longstanding cartographic conventions. Don’t take our word for it: a 2015 conference of cartographers across North America overwhelmingly favored grouping shields along a line (option F) over the approach taken by popular Mapnik-based styles such as openstreetmap-carto (option A). Laypeople also notice the difference between concurrency-capable maps (e.g., Apple Maps, Organic Maps) and those that render a generic image or only one shield at a time without any layout capabilities (openstreetmap-carto, Mapbox Streets, and many other GL JS styles out there). This is because print maps have been laying out shields in this manner since at least the 1920s. What may appear like an obsession is actually a desire for parity with print maps and high-quality digital maps, especially those geared towards audiences in the many countries where concurrent route shields are part of everyday life for drivers, cyclists, and hikers. Yet we know that not everyone has this experience in every country, so we take every opportunity to explain it to those who are unfamiliar. Please excuse the information overload. |
The only valid technical argument I got in this thread is related to CRUD, which is ok, but a bit weak, but I still don't understand what this |
A clearer articulation of this project’s overarching goals would help contributors make arguments that are less weak in your estimation. For example, one of the original goals of gl-js/gl-native was platform parity. If that goal remains – as #2064 (comment) seemed to imply – then the method in question needs to be publicly documented, or it needs to be exposed by another publicly documented method in GL JS, for parity with the other platforms. It may not be such an exciting enhancement, but the small things matter too. You’re absolutely right that the return value would need to be well documented along with the method. Currently, // image
Object { data: {…}, pixelRatio: 2, sdf: undefined, stretchX: undefined, stretchY: undefined, content: undefined }
// data
Object { width: 70, height: 70, data: Uint8Array(19600) } However, if the image happens to have been added dynamically, it looks like this: Object { data: {…}, pixelRatio: 2, stretchX: undefined, stretchY: undefined, content: undefined, sdf: false, version: 0, userImage: {…} }
// userImage
Object { width: 40, height: 42, data: Uint8ClampedArray(6720) } If I’m not mistaken, this corresponds to the following type: maplibre-gl-js/src/style/style_image.ts Lines 13 to 29 in c8615fa
part of which is already publicly documented as part of both the API reference and an example: maplibre-gl-js/src/style/style_image.ts Lines 31 to 41 in c8615fa
I’ve found this format to be reasonably usable, but the v3.0 version bump does present an opportunity to make ergonomic changes or lock down any parts you think would be unsustainable. For comparison, on each of the native platforms, the return value is actually the platform’s standard type for an image data container ( As to how Americana is using this method, I invite you to open Americana and click the Legend button at the bottom. Then zoom in to where you can see POI icons or route shields and click the Legend button again. The icons come directly from feature querying results plus |
I have yet to receive a short motivation for this. |
Thanks for clarifying, I wasn't aware that there was an expectation for user stories in issue tickets. I've opened #2166 to document this explicitly, which should help mitigate future misunderstandings. |
I would write a user story for this as follows: As a style author, I would like to access loaded images so that I can make modified variants of them at runtime without synchronous HTTP fetches. |
Great! This helps a lot! |
I do use The problem is that the required contents of the sprite is not known until runtime. Why should I re-fetch the base artwork when it's already loaded and rasterized in maplibre's sprite sheet? It makes more sense to retrieve this work that's already been done, make the needed modifications, and then insert the result back in with I disagree that "retrieving a graphic from cache, drawing a number and label on it, and inserting a copy back in the cache" is "complicated internal processing". |
I see. |
Update is already implemented: |
It may feel like a hack, but it's explicitly documented with the purpose of "dynamically generate a missing icon at runtime and add it to the map", which is the purpose that we use it for also. It's also separate from this discussion, which is about whether it's acceptable to use the sprite sheet as a repository for graphics storage. To be fair - an alternate approach would have us preload and store all the graphic templates outside of maplibre, and otherwise follow the same pattern we're following now, to insert them as needed. However, even if we did that, as you've suggested in a few spots, we would still need to use the styleimagemissing hook to prompt us to create the dynamic graphic, regardless of where all the graphics are stored. The question of whether maplibre users should be allowed access to maplibre's graphics storage repository for general purposes is more of a philosophical discussion, but I do appreciate your position on it. |
In case this user story was overlooked earlier in #2162 (comment):
Rephrased:
|
Motivation
Currently, there is no documented method for retrieving a sprite image from a mapLibre map. This has caused style authors such as myself to hunt around in the codebase looking for a way to access sprite data, such as I did in #2159 where I accessed internal (but nonetheless exposed) variables. Retrieving sprite images is needed for runtime icon manipulation, such as we do in OSM Americana. There is presently a method
map.style.getImage(id)
in the code, however this is not publicly documented.Design Alternatives
This functionality is currently accessible via
map.style.getImage(id)
, however, this option is undocumented.Design
The recommended implementation is to implement
Map.getImage()
to delegate back to map.style.getImage(id) as is used in other parts of map.ts.Implementation
Additional work would be needed to implement this across the maplibre-gl variants. However, the naming convention of
getImage()
follows the same naming conventions elsewhere in this file.The text was updated successfully, but these errors were encountered: