From cbe433fef19efc9a1c1929e129a1fc7c22ef7850 Mon Sep 17 00:00:00 2001 From: araharah-executor Date: Sat, 24 Oct 2026 02:53:39 +0800 Subject: [PATCH] =?UTF-8?q?feat(04-04):=20map=20tilt=20(pitch=2035)=20+=20?= =?UTF-8?q?runtime=20building=20extrusion=20reusing=20style-owned=20colors?= =?UTF-8?q?=20=E2=80=94=20device-gate=20feedback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/map/map.ts | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/web/src/map/map.ts b/web/src/map/map.ts index f9232fc..e16a171 100644 --- a/web/src/map/map.ts +++ b/web/src/map/map.ts @@ -32,16 +32,43 @@ export function bootMap(container: HTMLElement): MapLibreMap | null { style: ramsStyle as StyleSpecification, // inlined at build time (D-14) — NEVER re-set center: KL_CENTER, zoom: 13, + pitch: 35, // device-gate feedback (2026-10-24): a slight tilt so the 3D city reads as 3D attributionControl: false, // added below at bottom-left (UI-SPEC §Shell Layout: flush bottom-left chip) }); map.addControl(new AttributionControl({ compact: true }), "bottom-left"); + // The Rams derivation ships buildings flat (fill only). Extrude them at + // runtime so the pitch reads as depth: the layer REUSES the flat layer's + // own style-owned paint values — zero invented colors (DSGN-01 audit-safe). + // Height comes from the vector tile's render_height when present. + map.on("load", () => { + try { + const flat = map.getStyle().layers.find((l) => l.id === "building"); + const src = flat && "source" in flat ? flat.source : undefined; + const srcLayer = flat && "source-layer" in flat ? (flat as { "source-layer"?: string })["source-layer"] : undefined; + const flatFill = flat && "paint" in flat ? (flat.paint as { "fill-color"?: unknown })["fill-color"] : undefined; + if (typeof src === "string" && flatFill !== undefined) { + map.addLayer({ + id: "building-3d", + type: "fill-extrusion", + source: src, + ...(srcLayer !== undefined ? { "source-layer": srcLayer } : {}), + paint: { + "fill-extrusion-color": flatFill as string, + "fill-extrusion-height": ["coalesce", ["get", "render_height"], 6], + "fill-extrusion-base": 0, + "fill-extrusion-opacity": 0.85, + }, + }); + } + } catch { + // extrusion is enhancement, never load-bearing — flat map still boots + } + console.log("RAMS-BASEMAP-READY layers=" + map.getStyle().layers.map((l) => l.id).length); + }); map.on("error", (e: { error?: { message?: string } }) => { const inner = e && e.error; console.error("RAMS-BASEMAP-ERROR", inner && inner.message ? inner.message : inner); }); - map.on("load", () => { - console.log("RAMS-BASEMAP-READY layers=" + map.getStyle().layers.map((l) => l.id).length); - }); return map; } catch (err) { if (err instanceof GPUInitializationError) return null; // Pitfall 5: shell still renders