import maplibregl from "maplibre-gl"; import { GOONG_GLYPHS_PROXY_URL } from "@/uhm/api/config"; import { getGoongBackgroundOverlayBundle } from "@/uhm/api/tiles"; import { EMPTY_FEATURE_COLLECTION } from "@/uhm/lib/map/geo/constants"; import { PATH_ARROW_ICON_ID, PATH_ARROW_SOURCE_ID, POLYGON_LABEL_SOURCE_ID } from "@/uhm/lib/map/constants"; import { ensurePointGeotypeIcons, getAllGeotypeLabelLayers, getAllGeotypeLayers } from "@/uhm/lib/map/styles/geotypeLayers"; import { applyBackgroundLayerVisibility, ensurePathArrowIcon, } from "./mapUtils"; import { BackgroundLayerVisibility } from "@/uhm/lib/map/styles/backgroundLayers"; export function getBaseMapStyle(): maplibregl.StyleSpecification { return { version: 8, glyphs: GOONG_GLYPHS_PROXY_URL, sources: {}, layers: [ { id: "background", type: "background", paint: { "background-color": "#0b1220", }, }, ], }; } export function setupMapLayers( map: maplibregl.Map, backgroundVisibility: BackgroundLayerVisibility ) { applyBackgroundLayerVisibility(map, backgroundVisibility); void replaceBackgroundLayersWithGoong(map, backgroundVisibility).catch((error) => { console.error("Failed to load proxied background overlay bundle.", error); }); const hasPathArrowIcon = ensurePathArrowIcon(map); // preview (drawing) map.addSource("draw-preview", { type: "geojson", data: { type: "FeatureCollection", features: [] }, }); map.addLayer({ id: "draw-preview-fill", type: "fill", source: "draw-preview", filter: ["==", ["get", "type"], "fill"], paint: { "fill-color": "#22c55e", "fill-opacity": 0.4, }, }); map.addLayer({ id: "draw-preview-line", type: "line", source: "draw-preview", filter: ["!=", ["get", "type"], "fill"], paint: { "line-color": "#16a34a", "line-width": 2, }, }); map.addSource("draw-circle-preview", { type: "geojson", data: { type: "FeatureCollection", features: [] }, }); map.addLayer({ id: "draw-circle-preview-fill", type: "fill", source: "draw-circle-preview", paint: { "fill-color": "#0ea5e9", "fill-opacity": 0.25, }, }); map.addLayer({ id: "draw-circle-preview-line", type: "line", source: "draw-circle-preview", paint: { "line-color": "#0284c7", "line-width": 2, "line-opacity": 0.95, }, }); map.addSource("draw-line-preview", { type: "geojson", data: { type: "FeatureCollection", features: [] }, }); map.addLayer({ id: "draw-line-preview-line", type: "line", source: "draw-line-preview", paint: { "line-color": "#38bdf8", "line-width": 3, "line-opacity": 0.9, "line-dasharray": [1.2, 0.9], }, }); map.addSource("draw-path-preview", { type: "geojson", data: { type: "FeatureCollection", features: [] }, }); map.addLayer({ id: "draw-path-preview-line", type: "line", source: "draw-path-preview", paint: { "line-color": "#38bdf8", "line-width": 3, "line-opacity": 0.9, "line-dasharray": [1.2, 0.9], }, }); if (hasPathArrowIcon) { map.addLayer({ id: "draw-path-preview-arrows", type: "symbol", source: "draw-path-preview", layout: { "symbol-placement": "line", "symbol-spacing": 56, "icon-image": PATH_ARROW_ICON_ID, "icon-size": 0.45, "icon-allow-overlap": true, "icon-ignore-placement": true, }, }); } // data map.addSource("countries", { type: "geojson", data: { type: "FeatureCollection", features: [] }, promoteId: "id", }); map.addSource(PATH_ARROW_SOURCE_ID, { type: "geojson", data: EMPTY_FEATURE_COLLECTION, promoteId: "id", }); map.addSource("places", { type: "geojson", data: { type: "FeatureCollection", features: [] }, promoteId: "id", }); map.addSource(POLYGON_LABEL_SOURCE_ID, { type: "geojson", data: EMPTY_FEATURE_COLLECTION, promoteId: "id", }); ensurePointGeotypeIcons(map); const geotypeLayers = getAllGeotypeLayers("countries", PATH_ARROW_SOURCE_ID, "places"); for (const layer of geotypeLayers) { map.addLayer(layer); } const geotypeLabelLayers = getAllGeotypeLabelLayers(POLYGON_LABEL_SOURCE_ID, "countries"); for (const layer of geotypeLabelLayers) { map.addLayer(layer); } // editing overlays map.addSource("edit-shape", { type: "geojson", data: { type: "FeatureCollection", features: [] }, }); map.addSource("edit-handles", { type: "geojson", data: { type: "FeatureCollection", features: [] }, }); // Glowing halo under the edit shape line map.addLayer({ id: "edit-shape-glow", type: "line", source: "edit-shape", paint: { "line-color": "#38bdf8", "line-width": 8, "line-opacity": 0.35, "line-blur": 1.5, }, }); map.addLayer({ id: "edit-shape-line", type: "line", source: "edit-shape", paint: { "line-color": "#38bdf8", "line-width": 3, }, }); // Glowing halo under the edit handles map.addLayer({ id: "edit-handles-glow", type: "circle", source: "edit-handles", paint: { "circle-color": [ "match", ["get", "status"], "delete", "#ef4444", "vertex", "#22c55e", "edge", "#eab308", "unknown", "#22c55e", "#3b82f6" // default none ], "circle-radius": 22, "circle-opacity": 0.35, "circle-blur": 0.85, }, }); map.addLayer({ id: "edit-handles-circle", type: "circle", source: "edit-handles", paint: { "circle-color": [ "match", ["get", "status"], "delete", "#ef4444", "vertex", "#22c55e", "edge", "#eab308", "unknown", "#22c55e", "#3b82f6" // default none ], "circle-radius": 12, "circle-stroke-color": [ "match", ["get", "status"], "delete", "#7f1d1d", "vertex", "#14532d", "edge", "#713f12", "unknown", "#14532d", "#0f172a" // default none ], "circle-stroke-width": 3, }, }); } async function replaceBackgroundLayersWithGoong( map: maplibregl.Map, backgroundVisibility: BackgroundLayerVisibility ) { const bundle = await getGoongBackgroundOverlayBundle(); if (!bundle || map.getLayer("goong-country-labels-0")) { return; } for (const [sourceId, source] of Object.entries(bundle.sources)) { if (!map.getSource(sourceId)) { map.addSource(sourceId, source); } } const insertBeforeId = map.getLayer("draw-preview-fill") ? "draw-preview-fill" : undefined; for (const layer of bundle.layers) { if (map.getLayer(layer.id)) continue; map.addLayer(layer, insertBeforeId); } applyBackgroundLayerVisibility(map, backgroundVisibility); }