diff --git a/apps/client/src/widgets/collections/geomap/marker_data.ts b/apps/client/src/widgets/collections/geomap/marker_data.ts
index e3271d8f0f..11799f0177 100644
--- a/apps/client/src/widgets/collections/geomap/marker_data.ts
+++ b/apps/client/src/widgets/collections/geomap/marker_data.ts
@@ -4,24 +4,39 @@ import FNote from "../../../entities/fnote";
import { useChildNotes } from "../../react/hooks";
import { LOCATION_ATTRIBUTE } from ".";
+const DEFAULT_MARKER_COLOR = "#2A81CB";
+
// SVG marker pin shape (replaces the Leaflet marker PNG).
-export const MARKER_SVG = ``;
+export const MARKER_SVG = buildMarkerIcon();
+
+function buildMarkerIcon(color = DEFAULT_MARKER_COLOR) {
+ return `\
+
+ `;
+}
export function useMarkerData(note: FNote | null | undefined, apiRef: MutableRef) {
const childNotes = useChildNotes(note?.noteId);
- useEffect(() => {
+ async function refresh() {
const map = apiRef.current as maplibregl.Map | undefined;
if (!map) return;
- svgToImage(MARKER_SVG, (img) => {
- map.addImage("custom-marker", img, {
- pixelRatio: window.devicePixelRatio
- });
- });
+ const iconSvgCache = new Map();
+
+ function ensureIcon(color: string) {
+ const key = `marker-${color}`;
+
+ if (!iconSvgCache.has(key)) {
+ const svg = buildMarkerIcon(color);
+ iconSvgCache.set(key, svg);
+ }
+
+ return key;
+ }
const features: maplibregl.GeoJSONFeature[] = [];
for (const childNote of childNotes) {
@@ -30,6 +45,7 @@ export function useMarkerData(note: FNote | null | undefined, apiRef: MutableRef
if (!latLng) continue;
latLng.reverse();
+ const color = childNote.getLabelValue("color") ?? DEFAULT_MARKER_COLOR;
features.push({
type: "Feature",
geometry: {
@@ -39,10 +55,17 @@ export function useMarkerData(note: FNote | null | undefined, apiRef: MutableRef
properties: {
id: childNote.noteId,
name: childNote.title,
+ icon: ensureIcon(color)
}
});
}
+ // Build all the icons.
+ await Promise.all(iconSvgCache.entries().map(async ([ key, svg ]) => {
+ const image = await svgToImage(svg);
+ map.addImage(key, image);
+ }));
+
map.addSource("points", {
type: "geojson",
data: {
@@ -55,30 +78,31 @@ export function useMarkerData(note: FNote | null | undefined, apiRef: MutableRef
type: "symbol",
source: "points",
layout: {
- "icon-image": "custom-marker",
+ "icon-image": [ "get", "icon" ],
"icon-size": 1,
"icon-anchor": "bottom",
"icon-allow-overlap": true
}
});
+ }
- return () => {
- map.removeLayer("points-layer");
- map.removeSource("points");
- };
+ useEffect(() => {
+ refresh();
}, [ apiRef, childNotes ]);
}
-function svgToImage(svgString, callback) {
- const svgBlob = new Blob([svgString], { type: "image/svg+xml" });
- const url = URL.createObjectURL(svgBlob);
+function svgToImage(svgString){
+ return new Promise(resolve => {
+ const svgBlob = new Blob([svgString], { type: "image/svg+xml" });
+ const url = URL.createObjectURL(svgBlob);
- const img = new Image();
+ const img = new Image();
- img.onload = () => {
- URL.revokeObjectURL(url);
- callback(img);
- };
+ img.onload = () => {
+ URL.revokeObjectURL(url);
+ resolve(img);
+ };
- img.src = url;
+ img.src = url;
+ });
}