From b2a30261e72c29affaa8a6496b07355ef8c30ba8 Mon Sep 17 00:00:00 2001 From: ccmdi Date: Fri, 10 Oct 2025 11:28:38 -0400 Subject: [PATCH] deduplicate functions --- src/map-renderer.ts | 286 +++++++++++++++----------------------------- 1 file changed, 98 insertions(+), 188 deletions(-) diff --git a/src/map-renderer.ts b/src/map-renderer.ts index 2b92738..d6a97ba 100644 --- a/src/map-renderer.ts +++ b/src/map-renderer.ts @@ -63,6 +63,98 @@ export interface MapRendererOptions { }; } + +function hexToRgb(hex: string): [number, number, number] { + // Handle CSS variables like var(--color-accent) + if (hex.startsWith('var(')) { + const tempEl = document.body.createDiv(); + tempEl.style.color = hex; + document.body.appendChild(tempEl); + const computedColor = getComputedStyle(tempEl).color; + document.body.removeChild(tempEl); + + const rgbMatch = computedColor.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); + if (rgbMatch) { + return [parseInt(rgbMatch[1]), parseInt(rgbMatch[2]), parseInt(rgbMatch[3])]; + } + } + + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result + ? [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)] + : [118, 109, 243]; +} + +function getPointColor(point: MapPoint, tagSettings: MapTagSettings | undefined, defaultColor: string): string { + if (point.color) { + return point.color; + } + + if (tagSettings && point.tags && point.tags.length > 0) { + for (const priorityTag of tagSettings.tagPriority) { + if (point.tags.includes(priorityTag)) { + const customization = tagSettings.tagCustomizations[priorityTag]; + if (customization) { + return customization.color; + } + } + } + } + + return defaultColor; +} + +function getPointIcon(point: MapPoint, tagSettings: MapTagSettings | undefined): string | null { + if (tagSettings && point.tags && point.tags.length > 0) { + for (const priorityTag of tagSettings.tagPriority) { + if (point.tags.includes(priorityTag)) { + const customization = tagSettings.tagCustomizations[priorityTag]; + if (customization && customization.icon) { + return customization.icon; + } + } + } + } + return null; +} + +const iconSVGCache = new Map(); + +function getIconSVG(iconName: string, strokeWidth: number, fill: boolean): string | null { + const cacheKey = `${iconName}-${strokeWidth}-${fill}`; + + if (iconSVGCache.has(cacheKey)) { + return iconSVGCache.get(cacheKey)!; + } + + try { + const tempDiv = document.createElement('div'); + setIcon(tempDiv, iconName); + const svg = tempDiv.querySelector('svg'); + if (svg) { + const clonedSvg = svg.cloneNode(true); + if (clonedSvg instanceof SVGElement) { + clonedSvg.setAttribute('stroke-width', String(strokeWidth)); + if (fill) { + const fillableElements = clonedSvg.querySelectorAll('[fill]'); + fillableElements.forEach((el) => { + el.setAttribute('fill', 'white'); + }); + } + const serializer = new XMLSerializer(); + const svgContent = serializer.serializeToString(clonedSvg); + iconSVGCache.set(cacheKey, svgContent); + return svgContent; + } + } + } catch (e) { + // Icon not available + } + + iconSVGCache.set(cacheKey, null); + return null; +} + function handlePointClick(info: PickingInfo, event: MjolnirEvent, options: MapRendererOptions['options'], app: App) { if (info.object) { if (options.onMarkerClick) { @@ -88,101 +180,11 @@ export function updateMapPoints(deck: Deck, points: MapPoint[], c const markerType = options.markerType || 'pins'; const markerSize = options.markerSize || 100; - - const hexToRgb = (hex: string): [number, number, number] => { - // Handle CSS variables like var(--color-accent) - if (hex.startsWith('var(')) { - const tempEl = document.body.createDiv(); - tempEl.style.color = hex; - document.body.appendChild(tempEl); - const computedColor = getComputedStyle(tempEl).color; - document.body.removeChild(tempEl); - - const rgbMatch = computedColor.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); - if (rgbMatch) { - return [parseInt(rgbMatch[1]), parseInt(rgbMatch[2]), parseInt(rgbMatch[3])]; - } - } - - const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result - ? [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)] - : [118, 109, 243]; - }; - - const getPointColor = (point: MapPoint): string => { - if (point.color) { - return point.color; - } - - if (tagSettings && point.tags && point.tags.length > 0) { - for (const priorityTag of tagSettings.tagPriority) { - if (point.tags.includes(priorityTag)) { - const customization = tagSettings.tagCustomizations[priorityTag]; - if (customization) { - return customization.color; - } - } - } - } - - return options.markerColor || 'var(--color-accent)'; - }; - - const getPointIcon = (point: MapPoint): string | null => { - if (tagSettings && point.tags && point.tags.length > 0) { - for (const priorityTag of tagSettings.tagPriority) { - if (point.tags.includes(priorityTag)) { - const customization = tagSettings.tagCustomizations[priorityTag]; - if (customization && customization.icon) { - return customization.icon; - } - } - } - } - return null; - }; - - const iconSVGCache = new Map(); - - const getIconSVG = (iconName: string, strokeWidth: number, fill: boolean): string | null => { - const cacheKey = `${iconName}-${strokeWidth}-${fill}`; - - if (iconSVGCache.has(cacheKey)) { - return iconSVGCache.get(cacheKey)!; - } - - try { - const tempDiv = document.createElement('div'); - setIcon(tempDiv, iconName); - const svg = tempDiv.querySelector('svg'); - if (svg) { - const clonedSvg = svg.cloneNode(true); - if (clonedSvg instanceof SVGElement) { - clonedSvg.setAttribute('stroke-width', String(strokeWidth)); - if (fill) { - const fillableElements = clonedSvg.querySelectorAll('[fill]'); - fillableElements.forEach((el) => { - el.setAttribute('fill', 'white'); - }); - } - const serializer = new XMLSerializer(); - const svgContent = serializer.serializeToString(clonedSvg); - iconSVGCache.set(cacheKey, svgContent); - return svgContent; - } - } - } catch (e) { - // Icon not available - } - - iconSVGCache.set(cacheKey, null); - return null; - }; + const defaultColor = options.markerColor || 'var(--color-accent)'; const deckData: DeckDataPoint[] = points.map(point => ({ position: [point.lng, point.lat] as [number, number], - color: hexToRgb(getPointColor(point)), + color: hexToRgb(getPointColor(point, tagSettings, defaultColor)), radius: point.size || markerSize, point: point, })); @@ -199,7 +201,7 @@ export function updateMapPoints(deck: Deck, points: MapPoint[], c pickable: true, getIcon: (d: DeckDataPoint) => { const [r, g, b] = d.color; - const icon = getPointIcon(d.point); + const icon = getPointIcon(d.point, tagSettings); let innerContent = ``; @@ -263,64 +265,9 @@ export async function createMapRenderer(config: MapRendererOptions): Promise { - // Handle CSS variables like var(--color-accent) - if (hex.startsWith('var(')) { - const tempEl = document.body.createDiv(); - tempEl.style.color = hex; - document.body.appendChild(tempEl); - const computedColor = getComputedStyle(tempEl).color; - document.body.removeChild(tempEl); - - const rgbMatch = computedColor.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); - if (rgbMatch) { - return [parseInt(rgbMatch[1]), parseInt(rgbMatch[2]), parseInt(rgbMatch[3])]; - } - } - - const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result - ? [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)] - : [0, 0, 0]; - }; - - const getPointColor = (point: MapPoint): string => { - if (point.color) { - return point.color; - } - - if (tagSettings && point.tags && point.tags.length > 0) { - for (const priorityTag of tagSettings.tagPriority) { - if (point.tags.includes(priorityTag)) { - const customization = tagSettings.tagCustomizations[priorityTag]; - if (customization) { - return customization.color; - } - } - } - } - - // default - return markerColor; - }; - - const getPointIcon = (point: MapPoint): string | null => { - if (tagSettings && point.tags && point.tags.length > 0) { - for (const priorityTag of tagSettings.tagPriority) { - if (point.tags.includes(priorityTag)) { - const customization = tagSettings.tagCustomizations[priorityTag]; - if (customization && customization.icon) { - return customization.icon; - } - } - } - } - return null; - }; - const numPoints = points.length; const deckData: DeckDataPoint[] = new Array(numPoints); @@ -328,7 +275,7 @@ export async function createMapRenderer(config: MapRendererOptions): Promise(); - - const getIconSVG = (iconName: string, strokeWidth: number, fill: boolean): string | null => { - const cacheKey = `${iconName}-${strokeWidth}-${fill}`; - - if (iconSVGCache.has(cacheKey)) { - return iconSVGCache.get(cacheKey)!; - } - - try { - const tempDiv = document.createElement('div'); - setIcon(tempDiv, iconName); - const svg = tempDiv.querySelector('svg'); - if (svg) { - const clonedSvg = svg.cloneNode(true); - if (clonedSvg instanceof SVGElement) { - clonedSvg.setAttribute('stroke-width', String(strokeWidth)); - if (fill) { - const fillableElements = clonedSvg.querySelectorAll('[fill]'); - fillableElements.forEach((el) => { - el.setAttribute('fill', 'white'); - }); - } - const serializer = new XMLSerializer(); - const svgContent = serializer.serializeToString(clonedSvg); - iconSVGCache.set(cacheKey, svgContent); - return svgContent; - } - } - } catch (e) { - console.warn('Failed to get icon SVG:', iconName, e); - } - - iconSVGCache.set(cacheKey, null); - return null; - }; - const createMarkerLayer = (data: DeckDataPoint[]) => { if (markerType === 'pins') { return new IconLayer({ @@ -503,7 +413,7 @@ export async function createMapRenderer(config: MapRendererOptions): Promise { const [r, g, b] = d.color; - const icon = getPointIcon(d.point); + const icon = getPointIcon(d.point, tagSettings); let innerContent = ``;