// Map style registry + rewriting of OpenFreeMap styles for self-hosted assets. // Styles, fonts and sprites are served locally from /public (populated by // scripts/download-map-assets.mjs); only vector tiles (/planet) and the ne2_shaded // raster tiles stay remote on tiles.openfreemap.org. export interface MapStyle { id: string; label: string; } export const STYLES: MapStyle[] = [ { id: 'liberty', label: 'Liberty' }, { id: 'bright', label: 'Bright' }, { id: 'positron', label: 'Positron' }, { id: 'dark', label: 'Dark' }, { id: 'fiord', label: 'Fiord' }, ]; export const DEFAULT_STYLE_ID = 'fiord'; // localStorage key for the persisted style choice. export const STYLE_STORAGE_KEY = 'flight-ops-style'; const OFM_FONTS_PREFIX = 'https://tiles.openfreemap.org/fonts/'; const OFM_SPRITE_URL = 'https://tiles.openfreemap.org/sprites/ofm_f384/ofm'; // Sprite files are extracted flat into public/sprites/, so the sprite base URL // (MapLibre appends .json / @2x.png) points straight at them. export const LOCAL_SPRITE_URL = '/sprites/ofm'; // OFM styles ship no attribution; ODbL requires visible OSM credit. export const ATTRIBUTION = '© OpenStreetMap contributors © OpenFreeMap'; export function isStyleId(id: string): boolean { return STYLES.some((s) => s.id === id); } export function styleUrl(id: string): string { return `/styles/${id}.json`; } type Source = Record; // Takes a raw OFM style object and returns a new one with glyphs/sprite pointed at the // self-hosted copies under /fonts and /sprites. Only those two fields are rewritten — // the vector tile url (/planet) and raster tiles (/natural_earth/...) stay remote. // Sources without an attribution get one added (ODbL requires visible OSM credit). export function rewriteStyle(raw: unknown): unknown { if (typeof raw !== 'object' || raw === null) return raw; const style = { ...(raw as Record) }; const glyphs = style.glyphs; if (typeof glyphs === 'string' && glyphs.startsWith(OFM_FONTS_PREFIX)) { style.glyphs = '/fonts/' + glyphs.slice(OFM_FONTS_PREFIX.length); } if (style.sprite === OFM_SPRITE_URL) { style.sprite = LOCAL_SPRITE_URL; } const sources = style.sources; if (sources && typeof sources === 'object') { const next: Record = { ...(sources as Source) }; for (const [id, src] of Object.entries(next)) { if (src && typeof src === 'object' && !('attribution' in (src as Source))) { next[id] = { ...(src as Source), attribution: ATTRIBUTION }; } } style.sources = next; } return style; }