const API_BASE = "http://localhost:8000"; const DEFAULT_SCENARIO_ID = "silk_board_whitefield_capacity_safe_v1"; const TILE_SIZE = 256; const MAP_MIN_ZOOM = 10; const MAP_MAX_ZOOM = 15; const STATIC_PREFIX = location.pathname.includes("/frontend/") ? "../" : "./"; function staticAsset(path) { return STATIC_PREFIX + path.replace(/^\/+/, ""); } const mapState = { autoFit: true, center: null, context: null, dragging: false, lastPointer: null, routeKey: "", zoom: null, maplibre: null, maplibreMarkers: [], maplibreRouteKey: "", maplibreReady: false }; const audienceState = { active: "control", evidence: null }; const fallbackEvidence = { passed: true, scenario_id: DEFAULT_SCENARIO_ID, scenario_name: "Silk Board to Whitefield capacity-safe activation", generated_at: "not loaded", corridor: { name: "Bengaluru ORR: Silk Board to Whitefield", junctions: [ { id: "silk_board", lat: 12.917, lon: 77.6228, name: "Silk Board", order: 0 }, { id: "hsr_layout", lat: 12.9116, lon: 77.6365, name: "HSR Layout", order: 1 }, { id: "sony_world", lat: 12.9352, lon: 77.654, name: "Sony World", order: 2 }, { id: "marathahalli", lat: 12.9591, lon: 77.7014, name: "Marathahalli", order: 3 }, { id: "doddanekkundi", lat: 12.9724, lon: 77.7185, name: "Doddanekkundi", order: 4 }, { id: "itpl", lat: 12.985, lon: 77.736, name: "ITPL", order: 5 }, { id: "whitefield", lat: 12.9698, lon: 77.748, name: "Whitefield", order: 6 } ], smart_links: [ { id: "sl_marathahalli_whitefield", from_junction_id: "marathahalli", to_junction_id: "whitefield", destination_id: "whitefield", min_confidence: 0.65, capacity_vpm: 23 } ] }, cycles: [ { cycle: 3, observation: { junction_id: "marathahalli" }, posterior: { doddanekkundi: 0.1492537313, itpl: 0.1791044776, whitefield: 0.6716417910 }, best_destination_id: "whitefield", best_confidence: 0.6716417910, density_tw_per_km: [66.67, 74.87, 82.26, 87.97, 86.84, 83.73], decisions: [{ link_id: "sl_marathahalli_whitefield", activate: true, confidence: 0.6716417910, available_capacity_vpm: 14.5, q_expected_vpm: 14.5, q_commanded_vpm: 14.35, reason: "fallback evidence" }] } ], assertions: [ { name: "expected_link_activates", passed: true }, { name: "activation_confidence_floor", passed: true }, { name: "capacity_safe_decisions", passed: true } ] }; const fallbackSuite = { passed: true, passed_count: 0, scenario_count: 0, scenarios: [] }; const fallbackBatch = { passed: true, metrics: { vehicle_count: 0, observation_count: 0, activation_count: 0, capacity_violation_count: 0, overcommand_count: 0, false_positive_activations: 0, mean_activation_confidence: 0, mean_abs_demand_error_vpm: 0 }, aggregate_decisions: [], assertions: [] }; const fallbackExtremeBatch = fallbackBatch; const fallbackCctv = { passed: false, metrics: { metadata_rows: 0, coco_image_count: 0, coco_annotation_count: 0, category_count: 0, local_image_count: 0, sample_annotation_count: 0 }, sample_images: [], assertions: [] }; const fallbackField = { field_proven: false, status: "waiting_for_field_replay_data", metrics: {}, assertions: [] }; const fallbackRemoteAggregate = { passed: false, field_proven: false, remote_aggregate_replay: false, status: "waiting_for_remote_aggregate_counts", metrics: {}, assertions: [] }; const fallbackOfficial = { status: "missing", source_documents: [], planning_facts: [], junction_counts: [], screenline_counts: [] }; const fallbackRouteGeometry = { status: "missing", coordinate_count: 0, distance_m: 0, features: [] }; async function fetchWithTimeout(url, timeoutMs = 1400) { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeoutMs); try { return await fetch(url, { cache: "no-store", signal: controller.signal }); } finally { clearTimeout(timer); } } async function fetchJson(apiPath, localPath, fallback) { try { const api = await fetchWithTimeout(`${API_BASE}${apiPath}`); if (api.ok) return api.json(); } catch (_) {} try { const local = await fetchWithTimeout(localPath, 3000); if (local.ok) return local.json(); } catch (_) {} return fallback; } async function postJson(apiPath) { const response = await fetch(`${API_BASE}${apiPath}`, { method: "POST" }); if (!response.ok) throw new Error(`API returned ${response.status}`); return response.json(); } async function loadAllEvidence() { const [scenario, suite, batch, extremeBatch, official, cctv, field, remoteAggregate, routeGeometry] = await Promise.all([ fetchJson("/api/evidence/latest", staticAsset("evidence/latest_run.json"), fallbackEvidence), fetchJson("/api/evidence/suite", staticAsset("evidence/suite_report.json"), fallbackSuite), fetchJson("/api/evidence/batch", staticAsset("evidence/batch_report.json"), fallbackBatch), fetchJson("/api/evidence/extreme-batch", staticAsset("evidence/extreme_batch_report.json"), fallbackExtremeBatch), fetchJson("/api/official-reference", staticAsset("data/official_reference_bengaluru_cmp_2020.json"), fallbackOfficial), fetchJson("/api/evidence/cctv", staticAsset("evidence/cctv_bmd45_report.json"), fallbackCctv), fetchJson("/api/evidence/field-replay", staticAsset("evidence/field_replay_report.json"), fallbackField), fetchJson("/api/evidence/remote-aggregate", staticAsset("evidence/remote_aggregate_replay_report.json"), fallbackRemoteAggregate), fetchJson("/api/route-geometry", staticAsset("data/orr_silk_board_whitefield_route_osrm.geojson"), fallbackRouteGeometry) ]); renderAll({ scenario, suite, batch, extremeBatch, official, cctv, field, remoteAggregate, routeGeometry }); } async function runScenario() { try { await postJson(`/api/scenarios/${DEFAULT_SCENARIO_ID}/run`); } catch (error) { console.info("API replay unavailable; using saved evidence", error.message); } loadAllEvidence(); } async function runSuite() { try { await postJson("/api/suite/run"); } catch (error) { console.info("API suite unavailable; using saved evidence", error.message); } loadAllEvidence(); } async function runBatch() { try { await postJson("/api/batch/run"); } catch (error) { console.info("API batch unavailable; using saved evidence", error.message); } loadAllEvidence(); } async function runExtremeBatch() { try { await postJson("/api/extreme-batch/run"); } catch (error) { console.info("API extreme stress unavailable; using saved evidence", error.message); } loadAllEvidence(); } function renderAll({ scenario, suite, batch, extremeBatch, official, cctv, field, remoteAggregate, routeGeometry }) { const cycles = scenario.cycles || fallbackEvidence.cycles; const latest = cycles[cycles.length - 1]; const decision = cycles.flatMap(c => c.decisions || []).find(d => d.activate); const posterior = latest.posterior || {}; const bestId = latest.best_destination_id || Object.entries(posterior).sort((a, b) => b[1] - a[1])[0]?.[0] || "unknown"; const confidence = latest.best_confidence ?? posterior[bestId] ?? 0; const softwarePassed = Boolean(scenario.passed && suite.passed && batch.passed && (extremeBatch?.passed ?? true)); const cctvPassed = Boolean(cctv.passed); const officialReady = Boolean(official.source_documents?.length && official.status !== "missing"); const fieldPassed = Boolean(field.field_proven); const remotePassed = Boolean(remoteAggregate?.passed); const context = { scenario, suite, batch, extremeBatch, official, cctv, field, remoteAggregate, routeGeometry, latest, decision, bestId, confidence }; renderHeader(scenario, confidence, softwarePassed, cctvPassed, fieldPassed); renderUnderstanding(context); renderStats({ suite, batch, extremeBatch, official, cctv, field, remoteAggregate, routeGeometry, softwarePassed, cctvPassed, officialReady, fieldPassed, remotePassed }); renderGeoMap(scenario, latest, bestId, decision, extremeBatch, routeGeometry); renderPosterior(posterior, bestId, latest, decision); renderDecision(decision, confidence); renderCapacity(decision); renderCctv(cctv); renderDensity(scenario, latest); renderTraceStrip(extremeBatch); renderAudienceViews(context); renderScenarioLibrary(suite); renderBottomMetrics({ batch, extremeBatch, cctv, official, field, remoteAggregate, decision }); renderDetails(context); } function renderUnderstanding(context) { const proof = proofMetrics(context); const latest = context.latest || {}; const junctions = [...(context.scenario?.corridor?.junctions || [])].sort((a, b) => a.order - b.order); const activeId = latest.observation?.junction_id; const activeOrder = junctions.find(item => item.id === activeId)?.order ?? -1; const selectedIds = ["silk_board", "hsr_layout", "sony_world", "marathahalli", "whitefield"]; const selected = selectedIds.map(id => junctions.find(item => item.id === id)).filter(Boolean); const confidence = context.confidence || 0; const decision = context.decision; const action = decision?.activate ? "simulated activation" : "standby"; const status = document.getElementById("understand-status"); if (status) status.textContent = `${proof.suiteScore} scenarios passed / ${proof.capacityViolations} capacity violations / field ${proof.fieldGate}`; setText("route-story-title", `${proof.checkpoint} -> ${proof.bestDestination} ${proof.confidence}`); const story = document.getElementById("simple-route-story"); if (story) { story.innerHTML = selected.map((junction, index) => { const cls = ["simple-node"]; let state = "possible"; if (junction.order < activeOrder) { cls.push("passed"); state = "passed"; } if (junction.id === activeId) { cls.push("checkpoint"); state = "checkpoint"; } if (junction.id === context.bestId) { cls.push("target"); state = "likely"; } return `
${index + 1} ${escapeHtml(shortLabel(junction.name))} ${escapeHtml(state)}
`; }).join(""); } const gate = document.getElementById("simple-gate-story"); if (gate) { gate.innerHTML = `
Confidence ${escapeHtml(proof.confidence)}

${escapeHtml(`${proof.bestDestination} is the strongest destination belief.`)}

Capacity ${escapeHtml(decision ? `${number(decision.available_capacity_vpm)} vpm` : "waiting")}

${escapeHtml(decision?.activate ? "Receiving capacity is enough in replay." : "Action stays closed until capacity is safe.")}

Replay action ${escapeHtml(action)}
`; } const proofStrip = document.getElementById("simple-proof-strip"); if (proofStrip) { proofStrip.innerHTML = [ simpleProofItem("Software", proof.suiteScore, "scenario suite"), simpleProofItem("Stress", proof.vehicleCount, `${proof.observationCount} observations`), simpleProofItem("Safety", proof.capacityViolations, "capacity violations"), simpleProofItem("Route", proof.routePoints, proof.routeDistance), simpleProofItem("CCTV", proof.cctvBoxes, "BMD-45 boxes"), simpleProofItem("Field", proof.fieldGate, "not claimed yet") ].join(""); } } function simpleProofItem(labelText, value, detail) { return `
${escapeHtml(labelText)}${escapeHtml(value)}

${escapeHtml(detail)}

`; } function renderHeader(scenario, confidence, softwarePassed, cctvPassed, fieldPassed) { setText("corridor-label", scenario.corridor?.name || "Bengaluru ORR evidence console"); setText("scenario-name", scenario.scenario_name || "Scenario"); setText("wf-probability", percent(confidence)); const status = document.getElementById("status-pill"); const passed = softwarePassed && cctvPassed; status.textContent = passed ? (fieldPassed ? "field replay passed" : "software + CCTV passed; field pending") : "evidence review"; status.className = `badge ${passed ? (fieldPassed ? "pass" : "wait") : "fail"}`; } function renderStats({ suite, batch, extremeBatch, official, cctv, field, remoteAggregate, routeGeometry, softwarePassed, cctvPassed, officialReady, fieldPassed, remotePassed }) { const m = extremeBatch?.metrics || batch.metrics || fallbackBatch.metrics; const cm = cctv.metrics || fallbackCctv.metrics; const routeReady = Boolean(routeGeometry?.coordinate_count); const rm = remoteAggregate?.metrics || fallbackRemoteAggregate.metrics; document.getElementById("stats-row").innerHTML = [ statTile("Proof Suite", suite.scenario_count ? `${suite.passed_count}/${suite.scenario_count}` : "--", softwarePassed ? "software assertions passed" : "review needed"), statTile("Stress Replay", `${integer(m.vehicle_count)} veh`, `${integer(m.observation_count)} checkpoint observations`), statTile("Route Geometry", routeReady ? `${integer(routeGeometry.coordinate_count)} pts` : "--", routeGeometry?.distance_m ? `${number(routeGeometry.distance_m / 1000)} km OSRM/OpenStreetMap` : "route reference pending"), statTile("CCTV Audit", `${integer(cm.coco_annotation_count)} boxes`, cctvPassed ? `${integer(cm.local_image_count)} BMD-45 frames` : "waiting"), statTile("Remote Replay", remotePassed ? `${integer(rm.row_count || 0)} rows` : "pending", remotePassed ? `${number(rm.peak_total_vpm || 0)} vpm peak observed` : "aggregate counts needed"), statTile("Field Gate", fieldPassed ? "passed" : "pending", field.status || "observed replay needed") ].join(""); } function renderGeoMap(report, latest, bestId, decision, extremeBatch, routeGeometry) { mapState.context = { report, latest, bestId, decision, extremeBatch, routeGeometry }; const svg = document.getElementById("geo-map"); const stack = document.getElementById("map-stack"); const junctions = [...(report.corridor?.junctions || [])].sort((a, b) => a.order - b.order); if (!junctions.length) { svg.innerHTML = ""; setText("map-status", "Corridor geometry unavailable"); return; } const rect = stack?.getBoundingClientRect(); const width = Math.max(320, Math.round(rect?.width || 980)); const height = Math.max(260, Math.round(rect?.height || 330)); svg.setAttribute("viewBox", `0 0 ${width} ${height}`); const roadCoords = extractRouteCoordinates(routeGeometry); const routeCoords = roadCoords.length ? roadCoords : junctions.map(j => [Number(j.lon), Number(j.lat)]); const allCoords = [...routeCoords, ...junctions.map(j => [Number(j.lon), Number(j.lat)])]; const lats = allCoords.map(coord => Number(coord[1])); const lons = allCoords.map(coord => Number(coord[0])); const minLat = Math.min(...lats); const maxLat = Math.max(...lats); const minLon = Math.min(...lons); const maxLon = Math.max(...lons); const routePointCount = routeGeometry?.coordinate_count || routeCoords.length; const routeKey = `${routePointCount}:${minLat.toFixed(6)}:${maxLat.toFixed(6)}:${minLon.toFixed(6)}:${maxLon.toFixed(6)}`; if (!mapState.center || mapState.zoom == null || mapState.autoFit || mapState.routeKey !== routeKey) { const fitted = fitMapView(minLat, maxLat, minLon, maxLon, width, height); mapState.center = fitted.center; mapState.zoom = fitted.zoom; mapState.routeKey = routeKey; mapState.autoFit = false; } const maplibreReady = renderMapLibreShowcase({ routeCoords, junctions, latest, bestId, decision, routeGeometry, stress: extremeBatch?.metrics || fallbackBatch.metrics, bounds: { minLat, maxLat, minLon, maxLon } }); renderTileLayer(width, height); const project = (lon, lat) => projectMapPoint(lon, lat, width, height); const points = junctions.map(j => ({ ...j, ...project(j.lon, j.lat) })); const routePoints = routeCoords.map(coord => ({ lon: Number(coord[0]), lat: Number(coord[1]), ...project(coord[0], coord[1]) })); const byId = Object.fromEntries(points.map(p => [p.id, p])); const routeD = pathFromPoints(routePoints); const nearestIndexById = Object.fromEntries(junctions.map(j => [j.id, nearestRouteIndex(routeCoords, j)])); const activeOrder = junctions.find(j => j.id === latest.observation?.junction_id)?.order ?? -1; const activeIndex = nearestIndexById[latest.observation?.junction_id] ?? -1; const activeD = activeIndex > 0 ? pathFromPoints(routePoints.slice(0, activeIndex + 1)) : ""; const link = report.corridor?.smart_links?.[0]; const from = link ? byId[link.from_junction_id] : null; const to = link ? byId[link.to_junction_id] : null; const fromIndex = link ? nearestIndexById[link.from_junction_id] : -1; const toIndex = link ? nearestIndexById[link.to_junction_id] : -1; const linkD = from && to ? pathFromPoints(routeSlice(routePoints, fromIndex, toIndex)) : ""; const density = latest.density_tw_per_km || []; const stress = extremeBatch?.metrics || fallbackBatch.metrics; const routeFlowCount = flowDotCount(stress.vehicle_count || 0); const divertFlowCount = decision?.activate ? Math.max(8, Math.round(routeFlowCount * 0.34)) : 0; const maxDensity = Math.max(100, ...density.map(Number)); const routeSegments = buildRouteSegments(routePoints, junctions, nearestIndexById); const densitySegments = buildDensitySegments(routeSegments, density, maxDensity); const routeDots = buildFlowDots("route-flow-path", routeFlowCount, "route", stress.vehicle_count || 0); const divertDots = linkD && decision?.activate ? buildFlowDots("divert-flow-path", divertFlowCount, "divert", stress.vehicle_count || 0) : ""; const panelLines = flowPanelLines(stress, decision, routeGeometry); const routeKm = routeGeometry?.distance_m ? `${number(routeGeometry.distance_m / 1000)} km` : "junction fallback"; const nodes = points.map(p => { const cls = ["map-node"]; if (p.order < activeOrder) cls.push("passed"); if (p.id === latest.observation?.junction_id) cls.push("active"); if (p.id === bestId) cls.push("target"); const pulse = p.id === latest.observation?.junction_id ? `` : ""; const halo = p.id === bestId ? `` : ""; return ` ${pulse}${halo} ${escapeHtml(abbr(p.name))} ${escapeHtml(shortLabel(p.name))} `; }).join(""); svg.innerHTML = ` ${densitySegments} ${activeD ? `` : ""} ${linkD ? `` : ""} ${linkD ? `` : ""} ${linkD && decision?.activate ? `` : ""} ${routeDots} ${divertDots} ${nodes} ${escapeHtml(panelLines[0])} ${escapeHtml(panelLines[1])} ${escapeHtml(`${integer(routePointCount)} routed points / zoom ${mapState.zoom} / ${routeKm}`)} Built-in OpenStreetMap tile viewport / OSRM route geometry tiles, route, markers and flow share one Web Mercator transform `; const action = decision?.activate ? "capacity-safe activation" : "standby"; renderRouteCheckpoints(junctions, latest, bestId, decision, routeGeometry, activeOrder); setText("route-proof", routeGeometry?.coordinate_count ? `${integer(routeGeometry.coordinate_count)} pts / ${routeKm}` : "junction fallback"); setText("map-mode", maplibreReady ? "MapLibre route showcase" : (routeGeometry?.coordinate_count ? `${integer(routeGeometry.coordinate_count)}-point fallback map` : "route fallback")); setText("map-status", `${label(latest.observation?.junction_id || "checkpoint")} checkpoint -> ${label(bestId)} ${percent(latest.best_confidence || 0)} -> ${action}. ${maplibreReady ? "MapLibre/CARTO basemap with OSRM route overlay" : "Fallback map with OSRM route overlay"}; verified replay, not live field traffic.`); setText("flow-vehicles", stress.vehicle_count ? `${integer(stress.vehicle_count)} veh` : "scenario replay"); setText("flow-observations", stress.observation_count ? `${integer(stress.observation_count)} obs` : `${integer((report.cycles || []).length)} cycles`); setText("flow-safety", `${integer(stress.capacity_violation_count || 0)} violations`); } function renderMapLibreShowcase({ routeCoords, junctions, latest, bestId, decision, routeGeometry, stress, bounds }) { const container = document.getElementById("maplibre-map"); const stack = document.getElementById("map-stack"); if (!container || !window.maplibregl || !routeCoords.length) { stack?.classList.remove("maplibre-ready"); return false; } const routeKey = `${routeCoords.length}:${bounds.minLat.toFixed(5)}:${bounds.maxLat.toFixed(5)}:${bounds.minLon.toFixed(5)}:${bounds.maxLon.toFixed(5)}`; const routeGeoJson = { type: "FeatureCollection", features: [{ type: "Feature", properties: {}, geometry: { type: "LineString", coordinates: routeCoords } }] }; const activeId = latest.observation?.junction_id; const pointFeatures = junctions.map(junction => ({ type: "Feature", properties: { id: junction.id, name: junction.name, abbr: abbr(junction.name), state: junction.id === bestId ? "target" : (junction.id === activeId ? "checkpoint" : "route") }, geometry: { type: "Point", coordinates: [Number(junction.lon), Number(junction.lat)] } })); const pointsGeoJson = { type: "FeatureCollection", features: pointFeatures }; if (!mapState.maplibre) { mapState.maplibre = new maplibregl.Map({ container, style: "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json", center: [(bounds.minLon + bounds.maxLon) / 2, (bounds.minLat + bounds.maxLat) / 2], zoom: 11, pitch: 38, bearing: -18, attributionControl: true }); mapState.maplibre.dragRotate.disable(); mapState.maplibre.touchZoomRotate.disableRotation(); mapState.maplibre.on("load", () => updateMapLibreLayers(routeGeoJson, pointsGeoJson, bounds)); } else if (mapState.maplibre.loaded()) { updateMapLibreLayers(routeGeoJson, pointsGeoJson, bounds); } if (mapState.maplibre.loaded() && mapState.maplibreRouteKey !== routeKey) { fitMapLibreBounds(bounds, false); mapState.maplibreRouteKey = routeKey; } stack?.classList.add("maplibre-ready"); renderMapLibreOverlay(stress, decision, routeGeometry); return true; } function updateMapLibreLayers(routeGeoJson, pointsGeoJson, bounds) { const map = mapState.maplibre; if (!map) return; if (!map.getSource("ptis-route")) { map.addSource("ptis-route", { type: "geojson", data: routeGeoJson }); map.addLayer({ id: "ptis-route-shadow", type: "line", source: "ptis-route", paint: { "line-color": "#0f172a", "line-width": 11, "line-opacity": 0.18, "line-blur": 1.2 } }); map.addLayer({ id: "ptis-route-line", type: "line", source: "ptis-route", paint: { "line-color": "#087f68", "line-width": 5.5, "line-opacity": 0.96 } }); } else { map.getSource("ptis-route").setData(routeGeoJson); } if (!map.getSource("ptis-points")) { map.addSource("ptis-points", { type: "geojson", data: pointsGeoJson }); map.addLayer({ id: "ptis-point-halo", type: "circle", source: "ptis-points", paint: { "circle-radius": ["match", ["get", "state"], "target", 18, "checkpoint", 16, 11], "circle-color": "#ffffff", "circle-opacity": 0.92, "circle-stroke-color": "#d8e1dc", "circle-stroke-width": 1 } }); map.addLayer({ id: "ptis-point-dot", type: "circle", source: "ptis-points", paint: { "circle-radius": ["match", ["get", "state"], "target", 10, "checkpoint", 9, 6], "circle-color": ["match", ["get", "state"], "target", "#087f68", "checkpoint", "#255fbd", "#334155"], "circle-stroke-color": "#ffffff", "circle-stroke-width": 2 } }); map.addLayer({ id: "ptis-point-label", type: "symbol", source: "ptis-points", layout: { "text-field": ["get", "abbr"], "text-size": 11, "text-font": ["Open Sans Semibold", "Arial Unicode MS Bold"], "text-offset": [0, 1.85], "text-anchor": "top", "text-allow-overlap": false }, paint: { "text-color": "#0f172a", "text-halo-color": "#ffffff", "text-halo-width": 1.5 } }); } else { map.getSource("ptis-points").setData(pointsGeoJson); } fitMapLibreBounds(bounds, true); } function fitMapLibreBounds(bounds, initialOnly) { const map = mapState.maplibre; if (!map) return; if (initialOnly && mapState.maplibreReady) return; map.fitBounds([[bounds.minLon, bounds.minLat], [bounds.maxLon, bounds.maxLat]], { padding: { top: 74, right: 56, bottom: 56, left: 56 }, pitch: 38, bearing: -18, duration: initialOnly ? 0 : 700 }); mapState.maplibreReady = true; } function renderMapLibreOverlay(stress, decision, routeGeometry) { const container = document.getElementById("maplibre-map"); if (!container) return; const overlay = container.querySelector(".mapcn-overlay") || document.createElement("div"); overlay.className = "mapcn-overlay"; overlay.innerHTML = `
Stress replay${integer(stress.vehicle_count || 0)} vehicles
Observations${integer(stress.observation_count || 0)}
Safety${integer(stress.capacity_violation_count || 0)} violations
Route${integer(routeGeometry?.coordinate_count || 0)} pts

${escapeHtml(decision?.activate ? "Capacity-safe simulated activation" : "Gate remains on standby")}

`; if (!overlay.parentElement) container.appendChild(overlay); } function fitMapView(minLat, maxLat, minLon, maxLon, width, height) { const pad = 48; const nw = lonLatToWorld(minLon, maxLat); const se = lonLatToWorld(maxLon, minLat); const dx = Math.max(0.000001, Math.abs(se.x - nw.x)); const dy = Math.max(0.000001, Math.abs(se.y - nw.y)); const usableWidth = Math.max(180, width - pad * 2); const usableHeight = Math.max(160, height - pad * 2); const zoomX = Math.log2(usableWidth / (dx * TILE_SIZE)); const zoomY = Math.log2(usableHeight / (dy * TILE_SIZE)); const zoom = clamp(Math.floor(Math.min(zoomX, zoomY)), MAP_MIN_ZOOM, MAP_MAX_ZOOM); const centerWorld = { x: (nw.x + se.x) / 2, y: (nw.y + se.y) / 2 }; return { center: worldToLonLat(centerWorld.x, centerWorld.y), zoom }; } function renderTileLayer(width, height) { const tileMap = document.getElementById("tile-map"); if (!tileMap || !mapState.center || mapState.zoom == null) return; const z = clamp(Math.round(mapState.zoom), MAP_MIN_ZOOM, MAP_MAX_ZOOM); const center = lonLatToPixel(mapState.center.lon, mapState.center.lat, z); const leftWorld = center.x - width / 2; const topWorld = center.y - height / 2; const startX = Math.floor(leftWorld / TILE_SIZE) - 1; const endX = Math.floor((leftWorld + width) / TILE_SIZE) + 1; const startY = Math.floor(topWorld / TILE_SIZE) - 1; const endY = Math.floor((topWorld + height) / TILE_SIZE) + 1; const tileCount = 2 ** z; const tiles = [`
`]; for (let x = startX; x <= endX; x += 1) { for (let y = startY; y <= endY; y += 1) { if (y < 0 || y >= tileCount) continue; const wrappedX = ((x % tileCount) + tileCount) % tileCount; const left = Math.round(x * TILE_SIZE - leftWorld); const top = Math.round(y * TILE_SIZE - topWorld); tiles.push(``); } } tileMap.innerHTML = tiles.join(""); } function projectMapPoint(lon, lat, width, height) { const z = clamp(Math.round(mapState.zoom ?? MAP_MIN_ZOOM), MAP_MIN_ZOOM, MAP_MAX_ZOOM); const center = lonLatToPixel(mapState.center.lon, mapState.center.lat, z); const point = lonLatToPixel(lon, lat, z); return { x: width / 2 + (point.x - center.x), y: height / 2 + (point.y - center.y) }; } function lonLatToPixel(lon, lat, zoom) { const world = lonLatToWorld(lon, lat); const scale = TILE_SIZE * (2 ** zoom); return { x: world.x * scale, y: world.y * scale }; } function pixelToLonLat(x, y, zoom) { const scale = TILE_SIZE * (2 ** zoom); return worldToLonLat(x / scale, y / scale); } function lonLatToWorld(lon, lat) { const safeLat = clamp(Number(lat), -85.05112878, 85.05112878); const sin = Math.sin((safeLat * Math.PI) / 180); return { x: (Number(lon) + 180) / 360, y: 0.5 - Math.log((1 + sin) / (1 - sin)) / (4 * Math.PI) }; } function worldToLonLat(x, y) { const lon = x * 360 - 180; const n = Math.PI - 2 * Math.PI * y; const lat = (180 / Math.PI) * Math.atan(Math.sinh(n)); return { lon, lat }; } function renderPosterior(posterior, bestId, latest, decision) { const entries = Object.entries(posterior).sort((a, b) => b[1] - a[1]); setText("posterior-title", `${label(bestId)} ${percent(latest.best_confidence || posterior[bestId] || 0)}`); setText("posterior-cycle", `cycle ${latest.cycle ?? "--"}`); document.getElementById("dest-bars").innerHTML = entries.map(([id, value]) => `
${escapeHtml(shortLabel(label(id)))}
${percent(value)}
`).join(""); const alert = document.getElementById("activation-alert"); alert.textContent = decision?.activate ? `Gate open at ${percent(decision.confidence)}. Command is capped by receiving capacity.` : "Gate closed. Confidence or capacity threshold not met."; alert.className = `alert-line ${decision?.activate ? "active" : ""}`; } function renderDecision(decision, confidence) { const card = document.getElementById("decision-card"); const active = Boolean(decision?.activate); card.className = `panel decision-card ${active ? "active" : ""}`; setText("decision-state", active ? "ACTIVE" : "STANDBY"); setText("decision-detail", active ? `P=${percent(confidence)} / ${number(decision.q_expected_vpm)} vpm expected / ${number(decision.available_capacity_vpm)} vpm available` : `P=${percent(confidence)} / waiting for safe gate`); } function renderCapacity(decision) { document.getElementById("capacity-ledger").innerHTML = decision ? [ ledgerCell("Confidence", percent(decision.confidence), "gate input"), ledgerCell("Expected", `${number(decision.q_expected_vpm)} vpm`, "capped demand"), ledgerCell("Available", `${number(decision.available_capacity_vpm)} vpm`, "receiving route"), ledgerCell("Commanded", `${number(decision.q_commanded_vpm)} vpm`, "after compliance") ].join("") : [ ledgerCell("Confidence", "below gate", "standby"), ledgerCell("Expected", "0.00 vpm", "no command"), ledgerCell("Available", "--", "not used"), ledgerCell("Commanded", "0.00 vpm", "standby") ].join(""); } function renderCctv(cctv) { const metrics = cctv.metrics || fallbackCctv.metrics; setText("cctv-state", cctv.passed ? "audited" : "waiting"); setText("cctv-count", cctv.passed ? `${integer(metrics.coco_annotation_count)} boxes` : "--"); const canShowLocalFrames = location.pathname.includes("/frontend/"); document.getElementById("cctv-strip").innerHTML = (cctv.sample_images || []).slice(0, 3).map(item => { const src = encodeURI(staticAsset(`Real data/BMD-45-Val/${item.file_name}`)); const frame = canShowLocalFrames ? `BMD-45 validation frame ${escapeHtml(item.file_name)}` : `
BMD-45${integer(item.coco_annotation_count)}
`; return `
${frame}
${escapeHtml(item.file_name.replace("images_000/", ""))}${integer(item.coco_annotation_count)}
`; }).join("") || `
BMD-45--
waiting--
`; } function renderDensity(report, latest) { const density = latest.density_tw_per_km || []; const junctions = [...(report.corridor?.junctions || [])].sort((a, b) => a.order - b.order); const max = Math.max(100, ...density); document.getElementById("density-bars").innerHTML = density.map((value, index) => { const from = junctions[index]?.name || `S${index + 1}`; const to = junctions[index + 1]?.name || `S${index + 2}`; const color = value > 95 ? "var(--amber)" : value > 78 ? "var(--blue)" : "var(--green)"; const height = Math.max(14, (Number(value) / max) * 82); return `
${escapeHtml(abbr(from))}-${escapeHtml(abbr(to))}
`; }).join(""); } function renderRouteCheckpoints(junctions, latest, bestId, decision, routeGeometry, activeOrder) { const element = document.getElementById("route-checkpoints"); if (!element) return; const activeId = latest.observation?.junction_id; element.innerHTML = junctions.map(junction => { const classes = ["checkpoint-item"]; let state = "queued"; if (junction.order < activeOrder) { classes.push("passed"); state = "seen"; } if (junction.id === activeId) { classes.push("active"); state = "checkpoint"; } if (junction.id === bestId) { classes.push("target"); state = decision?.activate ? "target" : "likely"; } const meta = `${Number(junction.lat).toFixed(4)}, ${Number(junction.lon).toFixed(4)}`; return `
${escapeHtml(junction.name)}
${escapeHtml(meta)}
${escapeHtml(state)}
`; }).join(""); if (!routeGeometry?.coordinate_count) setText("route-proof", "junction fallback"); } function renderTraceStrip(extremeBatch) { const element = document.getElementById("trace-strip"); if (!element) return; const traces = (extremeBatch?.sample_vehicle_traces || []).slice(0, 4); if (!traces.length) { element.innerHTML = `
waiting--

Run the stress replay to populate sample traces.

`; return; } element.innerHTML = traces.map(trace => { const stops = (trace.observations || []).map(item => item.junction_id).filter(Boolean); const route = stops.map((stop, index) => { const hop = `${escapeHtml(shortLabel(label(stop)))}`; return index < stops.length - 1 ? `${hop}>` : hop; }).join(""); const destination = shortLabel(label(trace.actual_destination_id || trace.best_destination_id || "--")); const traceId = trace.vehicle_id_hash || trace.vehicle_id || "sample"; return `
${escapeHtml(traceId)}${escapeHtml(destination)}
${route}

${integer(stops.length)} checkpoints replayed; posterior best ${percent(trace.best_confidence || 0)}

`; }).join(""); } function renderAudienceViews(context) { audienceState.evidence = context; const content = document.getElementById("audience-content"); if (!content) return; document.querySelectorAll("[data-audience-tab]").forEach(button => { button.classList.toggle("active", button.dataset.audienceTab === audienceState.active); }); const views = { control: renderControlRoomView, citizen: renderCitizenView, officer: renderOfficerView, engineer: renderEngineerView, press: renderPressView }; content.innerHTML = (views[audienceState.active] || renderControlRoomView)(context); } function renderControlRoomView(context) { const proof = proofMetrics(context); return `
Operational story

${escapeHtml(proof.actionTitle)}

${escapeHtml(`At ${proof.checkpoint}, the replay posterior for ${proof.bestDestination} is ${proof.confidence}. The smart-link gate is ${proof.decisionState.toLowerCase()} and the command is bounded by available receiving capacity.`)}

${renderNarrativeCard(context)}
${proofCard("Route", proof.routePoints, `${proof.routeDistance} OSRM/OpenStreetMap reference`)} ${proofCard("Stress", proof.vehicleCount, `${proof.observationCount} replay observations`)} ${proofCard("Safety", proof.capacityViolations, "capacity violations")} ${proofCard("Overcommands", proof.overcommands, "stress replay audit")} ${proofCard("CCTV", proof.cctvBoxes, `${proof.cctvFrames} local frames audited`)} ${proofCard("Remote", proof.remoteGate, proof.remoteDetail)} ${proofCard("Field Gate", proof.fieldGate, "observed CSV still required")}
`; } function renderCitizenView(context) { const proof = proofMetrics(context); return `
Citizen-safe explanation

What this screen can honestly show today

${escapeHtml(`This is a verified software replay for the ${proof.corridorName} corridor. It shows how the system estimates where flow is likely going and opens a managed connector only when confidence and receiving capacity pass the gate.`)}

${escapeHtml("Safe public wording: PTIS has passed reproducible software tests on a Bengaluru ORR scenario, uses real OSRM/OpenStreetMap route geometry for visualization, and includes a real BMD-45 CCTV detection audit. Field impact is not claimed until observed replay data or a pilot is completed.")}
Boundary

No field-impact claim yet

${escapeHtml("Do not say travel time was reduced, congestion was reduced, or public roads were controlled. The current proof is software replay plus data provenance, not a live deployment.")}

`; } function renderOfficerView(context) { const proof = proofMetrics(context); return `
Officer / operations view

${escapeHtml(proof.decisionState === "ACTIVE" ? "Advisory activation is capacity-safe in replay" : "Replay gate remains on standby")}

${escapeHtml(`The replay recommendation is ${proof.decisionState}. If used in a pilot, this should stay advisory: officers approve, reject, or override every connector action. The system has ${proof.capacityViolations} capacity violations and ${proof.overcommands} overcommands in the stress audit.`)}

${auditRow("Current checkpoint", proof.checkpoint)} ${auditRow("Best destination", `${proof.bestDestination} at ${proof.confidence}`)} ${auditRow("Expected flow", proof.expectedFlow)} ${auditRow("Commanded flow", proof.commandedFlow)}
${renderNarrativeCard(context)}
`; } function renderEngineerView(context) { const proof = proofMetrics(context); return `
Engineer proof

Evidence chain and failure boundary

${escapeHtml("The frontend is reading saved evidence/API outputs, not random UI state. The route uses an in-app Web Mercator tile viewport, so tiles, markers, density bands, and animated route flow share one transform during zoom and pan.")}

${auditRow("Scenario suite", proof.suiteScore)} ${auditRow("Extreme replay", `${proof.vehicleCount} / ${proof.observationCount}`)} ${auditRow("Route geometry", `${proof.routePoints} / ${proof.routeDistance}`)} ${auditRow("CCTV detection audit", `${proof.cctvBoxes} boxes`)} ${auditRow("Remote aggregate", proof.remoteGate)} ${auditRow("Field replay", proof.fieldGate)}
${proofCard("Tests", proof.suiteScore, "published scenarios")} ${proofCard("Vehicles", proof.vehicleCount, "extreme stress replay")} ${proofCard("Observations", proof.observationCount, "checkpoint events")} ${proofCard("False Positives", proof.falsePositives, "activation audit")} ${proofCard("Capacity", proof.capacityViolations, "violations")} ${proofCard("Route Pts", proof.routePoints, "rendered geometry")}
`; } function renderPressView(context) { const proof = proofMetrics(context); return `
Press-safe summary

One paragraph you can post without overclaiming

${escapeHtml(`PTIS v2.0 is a reproducible traffic-intelligence software prototype for Bengaluru ORR. In the current evidence bundle it passes ${proof.suiteScore} scenarios, stress-replays ${proof.vehicleCount} vehicles with ${proof.capacityViolations} capacity violations, renders a ${proof.routePoints}-point OSRM/OpenStreetMap route, and audits BMD-45 CCTV detection data. It is not yet field-proven; observed field replay data is the next milestone.`)}
Do not claim yet

No live deployment language

${escapeHtml("Avoid percent congestion-reduction claims, rupee benefit claims, live BTP/FASTag/Google/Waze integration, and field-proven performance until actual observed field replay or a pilot validates them.")}

`; } function renderNarrativeCard(context) { const cycles = context.scenario.cycles || []; const rows = cycles.map(cycle => { const active = (cycle.decisions || []).some(item => item.activate); const checkpoint = label(cycle.observation?.junction_id || "checkpoint"); const best = label(cycle.best_destination_id || "unknown"); const confidence = percent(cycle.best_confidence || 0); const detail = `Best destination ${best}; confidence ${confidence}`; return `
${escapeHtml(cycle.cycle ?? "-")}
${escapeHtml(checkpoint)}

${escapeHtml(detail)}

${active ? "activate" : "standby"}
`; }).join(""); return `
Replay narrative

How the decision forms

${rows || `
-
Waiting

No cycles loaded.

pending
`}
`; } function proofMetrics(context) { const m = context.extremeBatch?.metrics || context.batch?.metrics || fallbackBatch.metrics; const cm = context.cctv?.metrics || fallbackCctv.metrics; const decision = context.decision; const checkpoint = label(context.latest?.observation?.junction_id || "checkpoint"); const bestDestination = label(context.bestId || "unknown"); const routePoints = integer(context.routeGeometry?.coordinate_count || 0); const routeDistance = context.routeGeometry?.distance_m ? `${number(context.routeGeometry.distance_m / 1000)} km` : "route fallback"; const suiteScore = context.suite?.scenario_count ? `${integer(context.suite.passed_count)}/${integer(context.suite.scenario_count)}` : "--"; const fieldGate = context.field?.field_proven ? "passed" : "pending"; const remoteMetrics = context.remoteAggregate?.metrics || fallbackRemoteAggregate.metrics; const remoteGate = context.remoteAggregate?.passed ? "passed" : "pending"; const remoteDetail = context.remoteAggregate?.passed ? `${integer(remoteMetrics.row_count || 0)} rows / ${number(remoteMetrics.peak_total_vpm || 0)} vpm peak` : "aggregate counts not loaded"; const decisionState = decision?.activate ? "ACTIVE" : "STANDBY"; return { actionTitle: decision?.activate ? "Smart link activates inside the replay gate" : "Smart link stays closed inside the replay gate", bestDestination, capacityViolations: integer(m.capacity_violation_count || 0), cctvBoxes: integer(cm.coco_annotation_count || 0), cctvFrames: integer(cm.local_image_count || 0), checkpoint, commandedFlow: decision ? `${number(decision.q_commanded_vpm)} vpm` : "0.00 vpm", confidence: percent(context.confidence || 0), corridorName: context.scenario?.corridor?.name || "Bengaluru ORR", decisionState, expectedFlow: decision ? `${number(decision.q_expected_vpm)} vpm` : "0.00 vpm", falsePositives: integer(m.false_positive_activations || 0), fieldGate, remoteDetail, remoteGate, observationCount: integer(m.observation_count || 0), overcommands: integer(m.overcommand_count || 0), routeDistance, routePoints, suiteScore, vehicleCount: integer(m.vehicle_count || 0) }; } function proofCard(labelText, value, detail) { return `
${escapeHtml(labelText)}${escapeHtml(value)}

${escapeHtml(detail)}

`; } function auditRow(labelText, value) { return `
${escapeHtml(labelText)}${escapeHtml(value)}
`; } function renderScenarioLibrary(suite) { const element = document.getElementById("scenario-library"); if (!element) return; const scenarios = suite?.scenarios || []; setText("scenario-library-state", scenarios.length ? `${integer(scenarios.length)} verified cases` : "suite unavailable"); if (!scenarios.length) { element.innerHTML = `
Waiting for suite reportRun the verifier or suite replay to populate this section.
pending
`; return; } element.innerHTML = scenarios.map(renderScenarioCard).join(""); } function renderScenarioCard(item) { const cycles = item.cycles || []; const latest = cycles[cycles.length - 1] || {}; const decisions = cycles.flatMap(cycle => cycle.decisions || []); const activeDecision = decisions.find(decision => decision.activate); const finalDecision = decisions[decisions.length - 1]; const assertionCount = (item.assertions || []).length; const passedAssertions = (item.assertions || []).filter(assertion => assertion.passed).length; const passed = Boolean(item.passed && assertionCount === passedAssertions); const outcome = activeDecision ? "activate" : "standby"; const reason = activeDecision?.reason || finalDecision?.reason || scenarioReadableReason(item.scenario_id, outcome); const best = latest.best_destination_id ? label(latest.best_destination_id) : "--"; const confidence = percent(latest.best_confidence || 0); const cycleCount = integer(cycles.length || 0); const assertionChips = (item.assertions || []).slice(0, 4).map(assertion => `${escapeHtml(assertion.name.replaceAll("_", " "))}`).join(""); return `
${escapeHtml(item.scenario_name || item.scenario_id || "Scenario")} ${escapeHtml(item.scenario_id || "scenario")}
${escapeHtml(outcome)}
${scenarioMetric("Assertions", `${integer(passedAssertions)}/${integer(assertionCount)}`)} ${scenarioMetric("Best", best)} ${scenarioMetric("Confidence", confidence)} ${scenarioMetric("Cycles", cycleCount)} ${scenarioMetric("Safety", passed ? "passed" : "review")} ${scenarioMetric("Decision", outcome)}

${escapeHtml(reason)}

${assertionChips}
`; } function scenarioMetric(labelText, value) { return `
${escapeHtml(labelText)}${escapeHtml(value)}
`; } function scenarioReadableReason(id, outcome) { const clean = String(id || "scenario").replaceAll("_", " "); if (outcome === "standby") return `No activation in this case; ${clean}.`; return `Activation case; ${clean}.`; } function renderBottomMetrics({ batch, extremeBatch, cctv, official, field, remoteAggregate, decision }) { const m = extremeBatch?.metrics || batch.metrics || fallbackBatch.metrics; const cm = cctv.metrics || fallbackCctv.metrics; const rm = remoteAggregate?.metrics || fallbackRemoteAggregate.metrics; document.getElementById("bottom-metrics").innerHTML = [ metricTile("Overcommands", integer(m.overcommand_count || 0), "batch stress"), metricTile("False Positives", integer(m.false_positive_activations || 0), "activation audit"), metricTile("Mean Confidence", percent(m.mean_activation_confidence || decision?.confidence || 0), "batch activation"), metricTile("CCTV Images", integer(cm.coco_image_count || 0), "BMD-45 records"), metricTile("Remote Replay", remoteAggregate?.passed ? integer(rm.row_count || 0) : "pending", remoteAggregate?.passed ? "aggregate count rows" : "no-travel count gate"), metricTile("Field Replay", field.field_proven ? "passed" : "pending", "observed CSV gate") ].join(""); } function renderDetails({ scenario, suite, batch, extremeBatch, official, cctv, field, remoteAggregate, routeGeometry }) { const m = extremeBatch?.metrics || batch.metrics || fallbackBatch.metrics; const cm = cctv.metrics || fallbackCctv.metrics; document.getElementById("detail-grid").innerHTML = [ detailItem("Generated", scenario.generated_at || "--"), detailItem("Scenario Hash", scenario.scenario_sha256 ? scenario.scenario_sha256.slice(0, 12) : "--"), detailItem("Official Docs", official.source_documents?.length || 0), detailItem("Route Points", routeGeometry?.coordinate_count || 0), detailItem("CCTV Categories", cm.category_count || 0), detailItem("Observations", m.observation_count || 0), detailItem("Activations", m.activation_count || 0), detailItem("Field Status", field.status || "pending"), detailItem("Remote Aggregate", remoteAggregate?.passed ? "passed" : (remoteAggregate?.status || "pending")), detailItem("CCTV Audit", cctv.passed ? "passed" : "waiting") ].join(""); document.getElementById("scenario-table").innerHTML = ` CycleCheckpointBestConfidenceDecision ${(scenario.cycles || []).map(cycle => { const active = (cycle.decisions || []).some(d => d.activate); return `${escapeHtml(cycle.cycle ?? "--")}${escapeHtml(label(cycle.observation?.junction_id || "--"))}${escapeHtml(label(cycle.best_destination_id || "--"))}${percent(cycle.best_confidence || 0)}${active ? "ACTIVATE" : "STANDBY"}`; }).join("")} `; setText("truth-boundary", "Validated software replay + real BMD-45 CCTV detection audit. Remote aggregate replay is optional no-travel evidence; field impact remains pending observed OD replay data."); } function statTile(labelText, value, detail) { return `
${escapeHtml(labelText)}${escapeHtml(value)}

${escapeHtml(detail)}

`; } function metricTile(labelText, value, detail) { return `
${escapeHtml(labelText)}${escapeHtml(value)}

${escapeHtml(detail)}

`; } function ledgerCell(labelText, value, detail) { return `
${escapeHtml(labelText)}${escapeHtml(value)}

${escapeHtml(detail)}

`; } function detailItem(labelText, value) { return `
${escapeHtml(labelText)}${escapeHtml(value)}
`; } function extractRouteCoordinates(routeGeometry) { const coords = routeGeometry?.features?.[0]?.geometry?.coordinates || routeGeometry?.geometry?.coordinates || []; return coords .filter(coord => Array.isArray(coord) && coord.length >= 2) .map(coord => [Number(coord[0]), Number(coord[1])]) .filter(coord => Number.isFinite(coord[0]) && Number.isFinite(coord[1])); } function nearestRouteIndex(routeCoords, junction) { let bestIndex = 0; let bestDistance = Number.POSITIVE_INFINITY; routeCoords.forEach((coord, index) => { const dx = Number(coord[0]) - Number(junction.lon); const dy = Number(coord[1]) - Number(junction.lat); const distance = dx * dx + dy * dy; if (distance < bestDistance) { bestDistance = distance; bestIndex = index; } }); return bestIndex; } function routeSlice(routePoints, fromIndex, toIndex) { const start = Math.max(0, Math.min(fromIndex, toIndex)); const end = Math.min(routePoints.length - 1, Math.max(fromIndex, toIndex)); const slice = routePoints.slice(start, end + 1); return fromIndex <= toIndex ? slice : slice.reverse(); } function pathFromPoints(points) { return points.map((p, index) => `${index ? "L" : "M"} ${p.x.toFixed(1)} ${p.y.toFixed(1)}`).join(" "); } function buildRouteSegments(routePoints, junctions, nearestIndexById) { return junctions.slice(0, -1).map((junction, index) => { const next = junctions[index + 1]; const start = nearestIndexById[junction.id] ?? 0; const end = nearestIndexById[next.id] ?? start; const segment = routeSlice(routePoints, start, end); return segment.length >= 2 ? segment : [routePoints[start], routePoints[end]].filter(Boolean); }); } function buildDensitySegments(segments, density, maxDensity) { return segments.map((segment, index) => { const value = Number(density[index] || 0); const ratio = Math.max(0.18, Math.min(1, value / maxDensity)); const width = 7 + ratio * 12; const opacity = 0.18 + ratio * 0.40; const color = densityColor(value); return ``; }).join(""); } function buildFlowDots(pathId, count, kind, vehicleCount) { const safeCount = Math.max(0, Math.min(64, count)); const baseDuration = kind === "divert" ? 9.2 : 13.6; const loadFactor = vehicleCount ? Math.max(0, Math.min(2.4, Math.log10(vehicleCount) - 2.2)) : 0; const duration = Math.max(6.6, baseDuration - loadFactor); return Array.from({ length: safeCount }, (_, index) => { const lane = index % 3; const radius = kind === "divert" ? (lane === 0 ? 3.3 : 2.4) : (lane === 0 ? 2.8 : 2.1); const begin = -1 * ((duration / safeCount) * index + lane * 0.23); return ` `; }).join(""); } function flowDotCount(vehicleCount) { if (!vehicleCount) return 22; return Math.round(Math.max(22, Math.min(58, Math.sqrt(Number(vehicleCount)) * 0.62))); } function densityColor(value) { if (value >= 90) return "#ffb347"; if (value >= 78) return "#4a9eff"; return "#00d4aa"; } function flowPanelLines(metrics, decision, routeGeometry) { const vehicles = metrics.vehicle_count ? `${integer(metrics.vehicle_count)} vehicles` : "scenario vehicles"; const observations = metrics.observation_count ? `${integer(metrics.observation_count)} observations` : "checkpoint observations"; const safety = `${integer(metrics.capacity_violation_count || 0)} capacity violations`; const commanded = decision?.q_commanded_vpm ? `${number(decision.q_commanded_vpm)} vpm command` : "standby command"; const source = routeGeometry?.source ? "OSRM/OpenStreetMap route" : "junction fallback route"; return [`${vehicles} / ${observations}`, `${safety} / ${commanded} / ${source}`]; } function curvePath(a, b) { const mx = (a.x + b.x) / 2; const my = (a.y + b.y) / 2; const lift = Math.max(32, Math.abs(a.x - b.x) * 0.22); return `M ${a.x.toFixed(1)} ${a.y.toFixed(1)} Q ${mx.toFixed(1)} ${(my + lift).toFixed(1)} ${b.x.toFixed(1)} ${b.y.toFixed(1)}`; } function abbr(name) { const clean = String(name).replace("Layout", "").replace("World", "").trim(); return clean.split(/\s+/).map(part => part[0]).join("").slice(0, 3).toUpperCase(); } function label(id) { return String(id || "--").replaceAll("_", " ").replace(/\b\w/g, c => c.toUpperCase()); } function shortLabel(value) { return String(value) .replace("Doddanekkundi", "D.Kundi") .replace("Doddanekundi", "D.Kundi") .replace("Marathahalli", "Maratha.") .replace("Sony World", "Sony") .replace("HSR Layout", "HSR"); } function percent(value) { const parsed = Number(value); return Number.isFinite(parsed) ? `${(parsed * 100).toFixed(1)}%` : "--"; } function number(value) { const parsed = Number(value); return Number.isFinite(parsed) ? parsed.toFixed(2) : "--"; } function integer(value) { const parsed = Number(value); return Number.isFinite(parsed) ? parsed.toLocaleString("en-IN") : "--"; } function setText(id, value) { const element = document.getElementById(id); if (element) element.textContent = value; } function escapeHtml(value) { return String(value).replace(/[&<>\'"]/g, c => ({ "&": "&", "<": "<", ">": ">", "'": "'", '"': """ }[c])); } function clamp(value, min, max) { return Math.min(max, Math.max(min, Number(value))); } function rerenderStoredMap() { if (!mapState.context) return; renderGeoMap( mapState.context.report, mapState.context.latest, mapState.context.bestId, mapState.context.decision, mapState.context.extremeBatch, mapState.context.routeGeometry ); } function zoomBuiltInMap(delta) { if (mapState.maplibre && mapState.maplibreReady) { const nextZoom = Math.max(9, Math.min(16, mapState.maplibre.getZoom() + delta)); mapState.maplibre.easeTo({ zoom: nextZoom, duration: 240 }); return; } if (mapState.zoom == null) return; const nextZoom = clamp(Math.round(mapState.zoom) + delta, MAP_MIN_ZOOM, MAP_MAX_ZOOM); if (nextZoom === mapState.zoom) return; mapState.zoom = nextZoom; mapState.autoFit = false; rerenderStoredMap(); } function resetBuiltInMap() { mapState.autoFit = true; mapState.maplibreReady = false; mapState.maplibreRouteKey = ""; rerenderStoredMap(); } function panBuiltInMap(dx, dy) { if (mapState.maplibre && mapState.maplibreReady) return; if (!mapState.center || mapState.zoom == null) return; const z = clamp(Math.round(mapState.zoom), MAP_MIN_ZOOM, MAP_MAX_ZOOM); const centerPx = lonLatToPixel(mapState.center.lon, mapState.center.lat, z); mapState.center = pixelToLonLat(centerPx.x - dx, centerPx.y - dy, z); mapState.autoFit = false; rerenderStoredMap(); } function initMapInteractions() { const stack = document.getElementById("map-stack"); if (!stack) return; stack.addEventListener("wheel", event => { if (stack.classList.contains("maplibre-ready")) return; event.preventDefault(); zoomBuiltInMap(event.deltaY < 0 ? 1 : -1); }, { passive: false }); stack.addEventListener("pointerdown", event => { if (stack.classList.contains("maplibre-ready")) return; if (event.target.closest(".map-controls")) return; mapState.dragging = true; mapState.lastPointer = { x: event.clientX, y: event.clientY }; stack.classList.add("is-dragging"); stack.setPointerCapture?.(event.pointerId); }); stack.addEventListener("pointermove", event => { if (stack.classList.contains("maplibre-ready")) return; if (!mapState.dragging || !mapState.lastPointer) return; const dx = event.clientX - mapState.lastPointer.x; const dy = event.clientY - mapState.lastPointer.y; mapState.lastPointer = { x: event.clientX, y: event.clientY }; panBuiltInMap(dx, dy); }); const stopDrag = event => { mapState.dragging = false; mapState.lastPointer = null; stack.classList.remove("is-dragging"); if (event?.pointerId != null) stack.releasePointerCapture?.(event.pointerId); }; stack.addEventListener("pointerup", stopDrag); stack.addEventListener("pointercancel", stopDrag); stack.addEventListener("pointerleave", stopDrag); } let resizeTimer = null; function initResponsiveMapRedraw() { window.addEventListener("resize", () => { window.clearTimeout(resizeTimer); resizeTimer = window.setTimeout(() => rerenderStoredMap(), 120); }); } function initAudienceTabs() { document.querySelectorAll("[data-audience-tab]").forEach(button => { button.addEventListener("click", () => { audienceState.active = button.dataset.audienceTab || "control"; if (audienceState.evidence) renderAudienceViews(audienceState.evidence); }); }); } document.getElementById("load-evidence")?.addEventListener("click", loadAllEvidence); document.getElementById("run-scenario")?.addEventListener("click", runScenario); document.getElementById("run-suite")?.addEventListener("click", runSuite); document.getElementById("run-batch")?.addEventListener("click", runBatch); document.getElementById("run-extreme")?.addEventListener("click", runExtremeBatch); document.getElementById("map-zoom-in")?.addEventListener("click", () => zoomBuiltInMap(1)); document.getElementById("map-zoom-out")?.addEventListener("click", () => zoomBuiltInMap(-1)); document.getElementById("map-reset")?.addEventListener("click", resetBuiltInMap); initMapInteractions(); initResponsiveMapRedraw(); initAudienceTabs(); loadAllEvidence();