Skip to content

Conversation

@remogeissbuehler
Copy link
Contributor

Overview

This PR adds support for multiple sprite sources. In the most recent spec used by MapLibre, the sprite field in the style.json can either be a simple string (as is assumed to be the case now) or an array of multiple sprites, like the following:

"sprite": [
  {
    "id": "roadsigns",
    "url": "https://example.com/myroadsigns"
  },
  {
    "id": "default",
    "url": "https://example2.com/anotherurl"
  }
]

This PR adds support for this kind of definition.

Motivation

Note that the multiple definitions kind is used by versatiles, which currently does not work with ol-mapbox-style:

"sprite": [
  {
    "id": "basics",
    "url": "https://tiles.versatiles.org/assets/sprites/basics/sprites"
  }
],

source

With this change, the map is shown correctly, including the sprites:
Screenshot 2025-03-02 at 21 18 28

and they still do for the existing maps, like the sdf-sprites example:
Screenshot 2025-03-02 at 21 19 50

Implementation Details

To support this, I added a new function to utils which normalizes all sprite definitions to the array-kind, to make it easier to work with.
In apply.js, I replaced the code that fetches the spriteData. It now loops over all the different sprite sources and assigns the different sprites to spriteData, prefixed by the sprite source id, as is specified by MapLibre.

This should be fully backwards-compatible, as the "old" way of just having a url is still supported.

Copy link
Member

@ahocevar ahocevar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately this will not work if more than one sprite is specified, because you're overwriting the spriteImageUrl, and the style function still can handle only one spriteImageUrl.

To do this properly, you could add the respective spriteImageUrl to the spriteData of each icon, and pass undefined as spriteImageUrl to the stylefunction().

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new example should be added to the index file, with an appropriate short description.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in a27f39f

src/mapbox.js Outdated
Comment on lines 41 to 52
if (!Array.isArray(sprite)) {
throw new Error(`unexpected sprites type: ${typeof sprite}`);
}

const hasRequiredAttrs = sprite.every(function (spriteObj) {
const attrs = Object.keys(spriteObj);
return attrs.includes('id') && attrs.includes('url');
});

if (!hasRequiredAttrs) {
throw new Error('Some sprite definitions lack url or id');
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We assume validated style objects, so these checks are not necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for pointing that out, done in f2aaa7b

src/mapbox.js Outdated
Comment on lines 54 to 60
return sprite.map(function (spriteObj) {
return {
'id': spriteObj.id,
'url': normalizeSpriteUrl(spriteObj.url, token, styleUrl),
};
});
}
Copy link
Member

@ahocevar ahocevar Mar 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When normalizeSpriteUrl() is done in place, you can simply

Suggested change
return sprite.map(function (spriteObj) {
return {
'id': spriteObj.id,
'url': normalizeSpriteUrl(spriteObj.url, token, styleUrl),
};
});
}
return sprite;

here without any new allocation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, also done in f2aaa7b

src/apply.js Outdated
sizeFactor +
'.json' +
sprite.search;
for (const sprite of sprites) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sprites have to be fetched in parallel, with Promise.all, not separately in a loop. Otherwise we don't know when we're done.

@remogeissbuehler remogeissbuehler force-pushed the support-multiple-sprites branch from 38cbe56 to a27f39f Compare March 8, 2025 19:42
- remove unnecessary checks
- avoid allocation by updating url inplace
- uses a Promise.all to fetch the sprite definitions
- passes the urls to stylefunction
@remogeissbuehler
Copy link
Contributor Author

Thanks a lot for your review and pointers!! I completely overlooked spriteImageUrl.

To do this properly, you could add the respective spriteImageUrl to the spriteData of each icon, and pass undefined as spriteImageUrl to the stylefunction().

I implemented your suggestion by "parametrizing" the image objects by the url. I adapted one of the examples to test and demo this multi-sprite feature.

I was wondering though, how would you feel about instead making spriteImageUrl accept an object like

{
  default: "https://example.com/default-sprites",
  alt: "https://alt-example.com/some/more/sprites"
}

on top of what it currently supports?

@ahocevar
Copy link
Member

ahocevar commented Mar 9, 2025

Unfortunately this does not work yet. I'm adding a commit with 3 fixes:

  • Call the onChange function that initializes the style function at the end of Promise.all(), not for each promise.
  • Change spriteImageUrl argument of stylefunction to also support key-value pairs of spriteImageUrls, as you suggested
  • Make multiple sprite images actually work. To ensure that, I updated the geojson-inline example's style to make use of both "default" and "bw" sprites.

Copy link
Member

@ahocevar ahocevar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ahocevar ahocevar merged commit e98973d into openlayers:main Mar 9, 2025
1 check passed
@remogeissbuehler
Copy link
Contributor Author

awesome, thanks a lot for your help @ahocevar

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants