Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions coordo-ts/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
export type { PopupOptions } from "maplibre-gl";

export { EVENTS } from "./events";
export { getLayerSymbolId } from "./layers/symbol";
export { createMap } from "./map/map";
export type {
FrictionlessField,
Expand Down
88 changes: 88 additions & 0 deletions coordo-ts/src/layers/symbol.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* Copyright COORDONNÉES 2025, 2026
* SPDX-License-Identifier: MPL-2.0
*/

import type { Map as MapLibreMap } from "maplibre-gl";

export function getLayerSymbolId({ layerId }: { layerId: string }) {
return `${layerId}-symbol-layer`;
}

function getLayerSymbolPictureId({ layerId }: { layerId: string }) {
return `${layerId}-symbol-picture-identifier`;
}

export function makeSetLayerSymbol({ map }: { map: MapLibreMap }) {
/**
* Update the icon-image of a Layer Layout.
* To use an image from your sprite, use spriteId.
* To use an external image, use symbolUrl.
* To use an svg image, use svg.
* Use only one provider. You can provide an additional fallback image via fallbackId.
* @param params.layerId — The ID of the layer to set the layout property in.
* @param params.imageUrl — The URL of the image file. Image file must be in png, webp, or jpg format.
* @param params.spriteId — The ID of the image to load from the sprite attached to the map instance.
* @param params.iconSize — The units in factor of the original icon size
* @param params.svg — The SVG to use as picture.
* @param params.fallbackId — The ID of a picture (from a sprite or from addImage)
* to use as fallback when the main image couldn't load.
*/
async function setLayerSymbol({
layerId,
iconSize = 1,
fallbackId,
imageUrl,
spriteId,
svg,
}: {
layerId: string;
iconSize?: number;
fallbackId?: string;
} & (
| { imageUrl: string; spriteId?: null; svg?: null }
| { spriteId: string; imageUrl?: null; svg?: null }
| { svg: string; imageUrl?: null; spriteId?: null }
)) {
function setLayerIconImage(pictureId: string) {
const finalId = fallbackId
? ["coalesce", ["image", pictureId], ["image", fallbackId]]
: pictureId;
map.setLayoutProperty(layerId, "icon-image", finalId);
map.setLayoutProperty(layerId, "icon-size", iconSize);
}

if (spriteId) {
setLayerIconImage(spriteId);
return;
}

if (imageUrl) {
const image = await map.loadImage(imageUrl);
const imageId = getLayerSymbolPictureId({ layerId });
map.addImage(imageId, image.data);

setLayerIconImage(imageId);
return;
}

if (svg) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

What happens if both imageUrl and svg are set ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I would have said "no worries, ts does not allow that" but not all consumers projects will be in typescript 😮‍💨 I'll add constraints

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Updated with

  • early return when it's configured
  • additional JSDoc
  • warning when nothing is provided

// Ref: https://maplibre.org/maplibre-gl-js/docs/examples/display-a-remote-svg-symbol/
const image = new Image();
const promise = new Promise((resolve) => {
image.onload = resolve;
});
image.src = svg;
await promise; // Wait for the image to load
const imageId = getLayerSymbolPictureId({ layerId });
map.addImage(imageId, image);

setLayerIconImage(imageId);
return;
}

console.warn("[setLayerSymbol] Provide one of: spriteId, imageUrl, svg");
}

return setLayerSymbol;
}
7 changes: 7 additions & 0 deletions coordo-ts/src/map/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import "../index.css";
import { EVENTS } from "../events";
import { makeSetLayerFilters } from "../layers/filters";
import { makeSetLayerPopup } from "../layers/popup";
import { makeSetLayerSymbol } from "../layers/symbol";
import { addStyleDataListener } from "./style-data";

const DEFAULT_MAP_OPTIONS: Partial<maplibregl.MapOptions> = {
Expand Down Expand Up @@ -55,10 +56,14 @@ export function createMap(
return map.getCenter().toArray();
}

const addSprite = map.addSprite;

const setLayerFilters = makeSetLayerFilters({ baseUrl, map });

const setLayerPopup = makeSetLayerPopup({ map });

const setLayerSymbol = makeSetLayerSymbol({ map });

function addEventListener<T extends keyof maplibregl.MapEventType>(
type: T,
listener: (ev: maplibregl.MapEventType[T] & Object) => void,
Expand All @@ -83,13 +88,15 @@ export function createMap(

return {
addEventListener,
addSprite,
getCenter,
getLayerMetadata,
getZoom,
hideLayer,
mapInstance: map,
setLayerFilters,
setLayerPopup,
setLayerSymbol,
showLayer,
};
}
Loading