85 lines
2.7 KiB
TypeScript
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
|
|
}
|
|
}
|
|
];
|