Files
Flight-Ops/lib/mapStyles.ts
T

73 lines
2.6 KiB
TypeScript

// 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<string, unknown>;
// 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<string, unknown>) };
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<string, unknown> = { ...(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;
}