Files
next-tjwater-frontend/src/features/workbench/map/city-scene-layers.ts
T

85 lines
2.7 KiB
TypeScript

import type { LayerSpecification, LightSpecification, SourceSpecification } from "maplibre-gl";
import { z } from "zod";
import { INTERACTIVE_HIT_LAYER_IDS } from "./layers";
import { MAP_STYLE_TOKENS as colors } from "./map-colors";
export const CITY_ROOT = "/city-model";
export const CITY_PREFIX = "fengxian-city-";
export const CITY_BUILDING_LAYER_ID = CITY_PREFIX + "buildings";
export const CITY_LIGHT = {
anchor: "map",
position: [1.5, 225, 35],
color: "#EAF2F4",
intensity: 0.42
} satisfies LightSpecification;
export function findCitySceneInsertionLayerId(layers: readonly LayerSpecification[]) {
// Layers painted after a 3D extrusion bypass depth testing, so ground overlays stay below it.
const hitLayerIds = new Set<string>(INTERACTIVE_HIT_LAYER_IDS);
return layers.find((layer) => hitLayerIds.has(layer.id))?.id;
}
export const cityManifestSchema = z
.object({
schema_version: z.literal(1),
name: z.string(),
generated_at: z.string().datetime({ offset: true }).optional(),
bbox: z.tuple([
z.number().min(-180).max(180),
z.number().min(-90).max(90),
z.number().min(-180).max(180),
z.number().min(-90).max(90)
]),
building_count: z.number().int().nonnegative(),
pipe_count: z.number().int().nonnegative(),
asset_count: z.number().int().nonnegative(),
notice: z.string(),
attribution: z.string(),
tiles: z
.object({
buildings: z.object({
sha256: z
.string()
.regex(/^[a-f0-9]{64}$/)
.optional()
})
})
.optional()
})
.refine(({ bbox: [w, s, e, n] }) => w < e && s < n, "Invalid scene bounds");
export function cityBuildingArchiveUrl(origin = window.location.origin) {
return new URL(`${CITY_ROOT}/buildings.pmtiles`, origin).href;
}
export function citySources(): Record<string, SourceSpecification> {
return {
[CITY_PREFIX + "buildings"]: {
type: "vector",
url: `pmtiles://${cityBuildingArchiveUrl()}`,
minzoom: 12,
maxzoom: 16,
attribution:
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a> · <a href="https://docs.overturemaps.org/attribution/">Overture Maps</a>',
promoteId: "id"
}
};
}
export const CITY_LAYERS: LayerSpecification[] = [
{
id: CITY_BUILDING_LAYER_ID,
source: CITY_PREFIX + "buildings",
"source-layer": "buildings",
type: "fill-extrusion",
minzoom: 12,
paint: {
"fill-extrusion-color": colors.canvas.secondary,
"fill-extrusion-height": ["to-number", ["get", "height_m"], 6],
"fill-extrusion-base": 0,
"fill-extrusion-opacity": 1,
"fill-extrusion-vertical-gradient": true
}
}
];