Update index.html
Browse files- index.html +126 -88
index.html
CHANGED
|
@@ -42,7 +42,7 @@
|
|
| 42 |
display: flex;
|
| 43 |
flex-wrap: wrap;
|
| 44 |
gap: 20px;
|
| 45 |
-
max-width:
|
| 46 |
margin: 0 auto;
|
| 47 |
}
|
| 48 |
|
|
@@ -140,7 +140,7 @@
|
|
| 140 |
|
| 141 |
<header>
|
| 142 |
<h1>ποΈ SILVERSTONE BRITISH GP β CROWD FLOW OPTIMISER</h1>
|
| 143 |
-
<p>Real-time crowd
|
| 144 |
</header>
|
| 145 |
|
| 146 |
<div class="dashboard">
|
|
@@ -156,32 +156,33 @@
|
|
| 156 |
</div>
|
| 157 |
|
| 158 |
<div class="slider-group">
|
| 159 |
-
<label for="becketts">Becketts GS: <span id="becketts-val" class="value-badge">
|
| 160 |
-
<input type="range" id="becketts" min="10" max="100" value="
|
| 161 |
</div>
|
| 162 |
|
| 163 |
<div class="slider-group">
|
| 164 |
-
<label for="stowe">Stowe GS: <span id="stowe-val" class="value-badge">
|
| 165 |
-
<input type="range" id="stowe" min="10" max="100" value="
|
| 166 |
</div>
|
| 167 |
|
| 168 |
<div class="slider-group">
|
| 169 |
-
<label for="village">Village GS: <span id="village-val" class="value-badge">
|
| 170 |
-
<input type="range" id="village" min="10" max="100" value="
|
| 171 |
</div>
|
| 172 |
|
| 173 |
<div class="slider-group">
|
| 174 |
-
<label for="club">Club GS: <span id="club-val" class="value-badge">
|
| 175 |
-
<input type="range" id="club" min="10" max="100" value="
|
| 176 |
</div>
|
| 177 |
|
| 178 |
<hr style="border-color: #333; margin: 20px 0;">
|
| 179 |
|
| 180 |
<div style="font-size: 12px; color: #aaa; line-height: 1.4;">
|
| 181 |
-
<p><strong>π‘
|
| 182 |
-
<p>β’ <strong>
|
| 183 |
-
<p>β’
|
| 184 |
-
<p>β’
|
|
|
|
| 185 |
</div>
|
| 186 |
</div>
|
| 187 |
|
|
@@ -189,7 +190,7 @@
|
|
| 189 |
<!-- RIGHT PANEL: HTML5 CANVAS MAP & TEXT ANALYSIS REPORT -->
|
| 190 |
<!-- ============================================================== -->
|
| 191 |
<div class="map-panel">
|
| 192 |
-
<canvas id="silverstoneCanvas" width="
|
| 193 |
<div id="reportBox" class="report-box">
|
| 194 |
<!-- Dynamic Text Report Injected Here -->
|
| 195 |
</div>
|
|
@@ -200,8 +201,9 @@
|
|
| 200 |
<!-- 2. JAVASCRIPT SIMULATION & CANVAS DRAWING LOGIC -->
|
| 201 |
<!-- ================================================================== -->
|
| 202 |
<script>
|
| 203 |
-
// Gate Capacity
|
| 204 |
-
const GATE_1_CAPACITY =
|
|
|
|
| 205 |
|
| 206 |
function updateSimulation() {
|
| 207 |
// 1. READ SLIDER VALUES FROM HTML INPUTS
|
|
@@ -219,23 +221,28 @@
|
|
| 219 |
document.getElementById('club-val').innerText = clubCrowd;
|
| 220 |
|
| 221 |
// 2. MATHEMATICAL TRAFFIC LOAD CALCULATIONS
|
| 222 |
-
const gate1Load = Math.round(hamiltonCrowd * 0.5 + beckettsCrowd * 0.3 + villageCrowd * 0.
|
| 223 |
-
const gate2Load = Math.round(hamiltonCrowd * 0.2 +
|
| 224 |
-
const gate3Load = Math.round(stoweCrowd * 0.
|
| 225 |
-
const gate4Load = Math.round(villageCrowd * 0.
|
| 226 |
|
| 227 |
-
//
|
| 228 |
-
const
|
| 229 |
-
const
|
|
|
|
|
|
|
| 230 |
|
| 231 |
// 3. DRAW MAP ON HTML5 CANVAS
|
| 232 |
-
drawCanvasMap(
|
|
|
|
|
|
|
|
|
|
| 233 |
|
| 234 |
// 4. UPDATE TEXT REPORT
|
| 235 |
-
updateReport(gate1Load, gate2Load, gate3Load, gate4Load,
|
| 236 |
}
|
| 237 |
|
| 238 |
-
function drawCanvasMap(hamilton, becketts, stowe, village, club, gate1Load,
|
| 239 |
const canvas = document.getElementById('silverstoneCanvas');
|
| 240 |
const ctx = canvas.getContext('2d');
|
| 241 |
|
|
@@ -246,8 +253,8 @@
|
|
| 246 |
// Map coordinate converter
|
| 247 |
function toCanvas(x, y) {
|
| 248 |
return {
|
| 249 |
-
x: x * 65 +
|
| 250 |
-
y: -y *
|
| 251 |
};
|
| 252 |
}
|
| 253 |
|
|
@@ -260,18 +267,18 @@
|
|
| 260 |
{x: 1, y: 4}, {x: 3, y: 4}, {x: 4, y: 2}, {x: 2, y: 0}, {x: -1, y: 0}
|
| 261 |
];
|
| 262 |
|
| 263 |
-
//
|
| 264 |
ctx.beginPath();
|
| 265 |
trackPoints.forEach((pt, idx) => {
|
| 266 |
const c = toCanvas(pt.x, pt.y);
|
| 267 |
if (idx === 0) ctx.moveTo(c.x, c.y);
|
| 268 |
else ctx.lineTo(c.x, c.y);
|
| 269 |
});
|
| 270 |
-
ctx.strokeStyle = '#
|
| 271 |
ctx.lineWidth = 14;
|
| 272 |
ctx.stroke();
|
| 273 |
|
| 274 |
-
//
|
| 275 |
ctx.beginPath();
|
| 276 |
ctx.setLineDash([6, 6]);
|
| 277 |
trackPoints.forEach((pt, idx) => {
|
|
@@ -279,13 +286,13 @@
|
|
| 279 |
if (idx === 0) ctx.moveTo(c.x, c.y);
|
| 280 |
else ctx.lineTo(c.x, c.y);
|
| 281 |
});
|
| 282 |
-
ctx.strokeStyle = '#
|
| 283 |
ctx.lineWidth = 1.5;
|
| 284 |
ctx.stroke();
|
| 285 |
ctx.setLineDash([]); // Reset dash
|
| 286 |
|
| 287 |
// -------------------------------------------------------------
|
| 288 |
-
// B. DEFINE MAP NODES (Grandstands, Food
|
| 289 |
// -------------------------------------------------------------
|
| 290 |
const nodes = {
|
| 291 |
// Grandstands
|
|
@@ -301,38 +308,42 @@
|
|
| 301 |
"Village Plaza": { pos: toCanvas(-1.8, 2.2), type: "food", label: "π Village Plaza" },
|
| 302 |
"Club Eats": { pos: toCanvas(0.2, -2.2), type: "food", label: "π¦ Club Eats" },
|
| 303 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 304 |
// Gates
|
| 305 |
-
"Main Gate 1": { pos: toCanvas(-3.
|
| 306 |
"South Gate 2": { pos: toCanvas(1, -4.2), type: "gate", label: "πͺ South Gate 2" },
|
| 307 |
-
"East Gate 3": { pos: toCanvas(4.
|
| 308 |
-
"West Gate 4": { pos: toCanvas(-
|
| 309 |
};
|
| 310 |
|
| 311 |
// -------------------------------------------------------------
|
| 312 |
-
// C.
|
| 313 |
// -------------------------------------------------------------
|
| 314 |
const paths = [
|
| 315 |
-
//
|
| 316 |
-
{ start: "Hamilton GS", end: "
|
| 317 |
-
{ start: "
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
{ start: "Becketts GS", end: "
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
{ start: "
|
| 326 |
-
|
| 327 |
-
|
|
|
|
| 328 |
{ start: "Village GS", end: "Village Plaza", color: "#00cc66" },
|
| 329 |
{ start: "Village Plaza", end: "West Gate 4", color: "#00cc66" },
|
| 330 |
-
{ start: "
|
| 331 |
-
|
| 332 |
-
// Club & South Gate Paths
|
| 333 |
{ start: "Club GS", end: "Club Eats", color: "#00cc66" },
|
| 334 |
-
{ start: "Club Eats", end: "South Gate 2", color: "#00cc66" }
|
| 335 |
-
{ start: "Hamilton GS", end: "Club Eats", color: "#00cc66" }
|
| 336 |
];
|
| 337 |
|
| 338 |
paths.forEach(p => {
|
|
@@ -345,28 +356,43 @@
|
|
| 345 |
});
|
| 346 |
|
| 347 |
// -------------------------------------------------------------
|
| 348 |
-
// D.
|
| 349 |
// -------------------------------------------------------------
|
| 350 |
-
if (
|
| 351 |
-
// Detour
|
| 352 |
ctx.beginPath();
|
| 353 |
-
ctx.setLineDash([8,
|
| 354 |
ctx.moveTo(nodes["Main Fan Zone"].pos.x, nodes["Main Fan Zone"].pos.y);
|
|
|
|
| 355 |
ctx.lineTo(nodes["Club Eats"].pos.x, nodes["Club Eats"].pos.y);
|
| 356 |
ctx.lineTo(nodes["South Gate 2"].pos.x, nodes["South Gate 2"].pos.y);
|
| 357 |
-
ctx.strokeStyle = "#a020f0";
|
| 358 |
ctx.lineWidth = 5;
|
| 359 |
ctx.stroke();
|
| 360 |
|
| 361 |
-
//
|
| 362 |
ctx.beginPath();
|
| 363 |
-
ctx.moveTo(nodes["
|
|
|
|
| 364 |
ctx.lineTo(nodes["West Gate 4"].pos.x, nodes["West Gate 4"].pos.y);
|
| 365 |
ctx.strokeStyle = "#a020f0";
|
| 366 |
ctx.lineWidth = 5;
|
| 367 |
ctx.stroke();
|
| 368 |
-
|
| 369 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 370 |
}
|
| 371 |
|
| 372 |
// -------------------------------------------------------------
|
|
@@ -376,12 +402,17 @@
|
|
| 376 |
const n = nodes[key];
|
| 377 |
ctx.font = "bold 11px Arial";
|
| 378 |
const textWidth = ctx.measureText(n.label).width;
|
| 379 |
-
const boxPadding =
|
| 380 |
|
| 381 |
-
let boxColor = "#3498db"; // Blue for
|
| 382 |
-
if (n.type === "food") boxColor = "#f39c12"; // Orange for Food
|
|
|
|
| 383 |
if (n.type === "gate") {
|
| 384 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 385 |
}
|
| 386 |
|
| 387 |
// Draw background rounded box
|
|
@@ -399,7 +430,7 @@
|
|
| 399 |
ctx.lineWidth = 1;
|
| 400 |
ctx.stroke();
|
| 401 |
|
| 402 |
-
// Draw
|
| 403 |
ctx.fillStyle = "#ffffff";
|
| 404 |
ctx.textAlign = "center";
|
| 405 |
ctx.textBaseline = "middle";
|
|
@@ -410,34 +441,41 @@
|
|
| 410 |
ctx.font = "11px Arial";
|
| 411 |
ctx.fillStyle = "#ffffff";
|
| 412 |
ctx.textAlign = "left";
|
| 413 |
-
ctx.fillText("π’ Clear
|
| 414 |
-
ctx.fillText("π΄ Congested
|
| 415 |
-
ctx.fillText("π£ Google Maps Detour",
|
| 416 |
}
|
| 417 |
|
| 418 |
-
function updateReport(gate1, gate2, gate3, gate4,
|
| 419 |
const reportDiv = document.getElementById('reportBox');
|
| 420 |
|
| 421 |
-
if (
|
| 422 |
reportDiv.className = "report-box alert";
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
<p>β’ <strong>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 427 |
<hr style="border-color: #552222; margin: 10px 0;">
|
| 428 |
-
<h4 style="color: #a020f0;">π Dynamic Rerouting
|
| 429 |
-
<p>
|
| 430 |
-
<p>
|
| 431 |
`;
|
|
|
|
| 432 |
} else {
|
| 433 |
reportDiv.className = "report-box";
|
| 434 |
reportDiv.innerHTML = `
|
| 435 |
-
<h3 style="color: #00cc66;">π’ ALL
|
| 436 |
-
<p>β’ <strong>Main Gate 1:</strong> <code>${gate1}</code> / ${GATE_1_CAPACITY} fans/min (Safe)</p>
|
| 437 |
-
<p>β’ <strong>South Gate 2:</strong> <code>${gate2}</code> / 50 fans/min (Safe)</p>
|
| 438 |
-
<p>β’ <strong>East Gate 3:</strong> <code>${gate3}</code> /
|
| 439 |
-
<p>β’ <strong>West Gate 4:</strong> <code>${gate4}</code> / 45 fans/min (Safe)</p>
|
| 440 |
-
<p style="margin-top: 5px; color: #aaa;"><em>Status:
|
| 441 |
`;
|
| 442 |
}
|
| 443 |
}
|
|
|
|
| 42 |
display: flex;
|
| 43 |
flex-wrap: wrap;
|
| 44 |
gap: 20px;
|
| 45 |
+
max-width: 1450px;
|
| 46 |
margin: 0 auto;
|
| 47 |
}
|
| 48 |
|
|
|
|
| 140 |
|
| 141 |
<header>
|
| 142 |
<h1>ποΈ SILVERSTONE BRITISH GP β CROWD FLOW OPTIMISER</h1>
|
| 143 |
+
<p>Real-time interconnected crowd routing, dynamic bottleneck propagation & multi-path detours</p>
|
| 144 |
</header>
|
| 145 |
|
| 146 |
<div class="dashboard">
|
|
|
|
| 156 |
</div>
|
| 157 |
|
| 158 |
<div class="slider-group">
|
| 159 |
+
<label for="becketts">Becketts GS: <span id="becketts-val" class="value-badge">60</span> fans/min</label>
|
| 160 |
+
<input type="range" id="becketts" min="10" max="100" value="60" oninput="updateSimulation()">
|
| 161 |
</div>
|
| 162 |
|
| 163 |
<div class="slider-group">
|
| 164 |
+
<label for="stowe">Stowe GS: <span id="stowe-val" class="value-badge">40</span> fans/min</label>
|
| 165 |
+
<input type="range" id="stowe" min="10" max="100" value="40" oninput="updateSimulation()">
|
| 166 |
</div>
|
| 167 |
|
| 168 |
<div class="slider-group">
|
| 169 |
+
<label for="village">Village GS: <span id="village-val" class="value-badge">55</span> fans/min</label>
|
| 170 |
+
<input type="range" id="village" min="10" max="100" value="55" oninput="updateSimulation()">
|
| 171 |
</div>
|
| 172 |
|
| 173 |
<div class="slider-group">
|
| 174 |
+
<label for="club">Club GS: <span id="club-val" class="value-badge">50</span> fans/min</label>
|
| 175 |
+
<input type="range" id="club" min="10" max="100" value="50" oninput="updateSimulation()">
|
| 176 |
</div>
|
| 177 |
|
| 178 |
<hr style="border-color: #333; margin: 20px 0;">
|
| 179 |
|
| 180 |
<div style="font-size: 12px; color: #aaa; line-height: 1.4;">
|
| 181 |
+
<p><strong>π‘ Dynamic Flow Rules:</strong></p>
|
| 182 |
+
<p>β’ <strong>Gate 1 Threshold:</strong> 50 fans/min</p>
|
| 183 |
+
<p>β’ <strong>Gate 3 Threshold:</strong> 45 fans/min</p>
|
| 184 |
+
<p>β’ Bottlenecks cause cascading <span class="badge badge-red">RED</span> corridor congestions.</p>
|
| 185 |
+
<p>β’ Active rerouting renders complex multi-node <span class="badge badge-purple">PURPLE</span> Google Maps detours.</p>
|
| 186 |
</div>
|
| 187 |
</div>
|
| 188 |
|
|
|
|
| 190 |
<!-- RIGHT PANEL: HTML5 CANVAS MAP & TEXT ANALYSIS REPORT -->
|
| 191 |
<!-- ============================================================== -->
|
| 192 |
<div class="map-panel">
|
| 193 |
+
<canvas id="silverstoneCanvas" width="900" height="520"></canvas>
|
| 194 |
<div id="reportBox" class="report-box">
|
| 195 |
<!-- Dynamic Text Report Injected Here -->
|
| 196 |
</div>
|
|
|
|
| 201 |
<!-- 2. JAVASCRIPT SIMULATION & CANVAS DRAWING LOGIC -->
|
| 202 |
<!-- ================================================================== -->
|
| 203 |
<script>
|
| 204 |
+
// Gate Capacity Limits
|
| 205 |
+
const GATE_1_CAPACITY = 50;
|
| 206 |
+
const GATE_3_CAPACITY = 45;
|
| 207 |
|
| 208 |
function updateSimulation() {
|
| 209 |
// 1. READ SLIDER VALUES FROM HTML INPUTS
|
|
|
|
| 221 |
document.getElementById('club-val').innerText = clubCrowd;
|
| 222 |
|
| 223 |
// 2. MATHEMATICAL TRAFFIC LOAD CALCULATIONS
|
| 224 |
+
const gate1Load = Math.round(hamiltonCrowd * 0.5 + beckettsCrowd * 0.3 + villageCrowd * 0.3);
|
| 225 |
+
const gate2Load = Math.round(hamiltonCrowd * 0.2 + clubCrowd * 0.4);
|
| 226 |
+
const gate3Load = Math.round(stoweCrowd * 0.6 + beckettsCrowd * 0.4);
|
| 227 |
+
const gate4Load = Math.round(villageCrowd * 0.4 + hamiltonCrowd * 0.2);
|
| 228 |
|
| 229 |
+
// Bottleneck flags
|
| 230 |
+
const isGate1Bottleneck = gate1Load > GATE_1_CAPACITY;
|
| 231 |
+
const isGate3Bottleneck = gate3Load > GATE_3_CAPACITY;
|
| 232 |
+
const excessGate1 = isGate1Bottleneck ? (gate1Load - GATE_1_CAPACITY) : 0;
|
| 233 |
+
const excessGate3 = isGate3Bottleneck ? (gate3Load - GATE_3_CAPACITY) : 0;
|
| 234 |
|
| 235 |
// 3. DRAW MAP ON HTML5 CANVAS
|
| 236 |
+
drawCanvasMap(
|
| 237 |
+
hamiltonCrowd, beckettsCrowd, stoweCrowd, villageCrowd, clubCrowd,
|
| 238 |
+
gate1Load, gate3Load, isGate1Bottleneck, isGate3Bottleneck
|
| 239 |
+
);
|
| 240 |
|
| 241 |
// 4. UPDATE TEXT REPORT
|
| 242 |
+
updateReport(gate1Load, gate2Load, gate3Load, gate4Load, isGate1Bottleneck, isGate3Bottleneck, excessGate1, excessGate3);
|
| 243 |
}
|
| 244 |
|
| 245 |
+
function drawCanvasMap(hamilton, becketts, stowe, village, club, gate1Load, gate3Load, isGate1Bottleneck, isGate3Bottleneck) {
|
| 246 |
const canvas = document.getElementById('silverstoneCanvas');
|
| 247 |
const ctx = canvas.getContext('2d');
|
| 248 |
|
|
|
|
| 253 |
// Map coordinate converter
|
| 254 |
function toCanvas(x, y) {
|
| 255 |
return {
|
| 256 |
+
x: x * 65 + 430,
|
| 257 |
+
y: -y * 48 + 260
|
| 258 |
};
|
| 259 |
}
|
| 260 |
|
|
|
|
| 267 |
{x: 1, y: 4}, {x: 3, y: 4}, {x: 4, y: 2}, {x: 2, y: 0}, {x: -1, y: 0}
|
| 268 |
];
|
| 269 |
|
| 270 |
+
// Outer track border
|
| 271 |
ctx.beginPath();
|
| 272 |
trackPoints.forEach((pt, idx) => {
|
| 273 |
const c = toCanvas(pt.x, pt.y);
|
| 274 |
if (idx === 0) ctx.moveTo(c.x, c.y);
|
| 275 |
else ctx.lineTo(c.x, c.y);
|
| 276 |
});
|
| 277 |
+
ctx.strokeStyle = '#333333';
|
| 278 |
ctx.lineWidth = 14;
|
| 279 |
ctx.stroke();
|
| 280 |
|
| 281 |
+
// Racing line
|
| 282 |
ctx.beginPath();
|
| 283 |
ctx.setLineDash([6, 6]);
|
| 284 |
trackPoints.forEach((pt, idx) => {
|
|
|
|
| 286 |
if (idx === 0) ctx.moveTo(c.x, c.y);
|
| 287 |
else ctx.lineTo(c.x, c.y);
|
| 288 |
});
|
| 289 |
+
ctx.strokeStyle = '#aaaaaa';
|
| 290 |
ctx.lineWidth = 1.5;
|
| 291 |
ctx.stroke();
|
| 292 |
ctx.setLineDash([]); // Reset dash
|
| 293 |
|
| 294 |
// -------------------------------------------------------------
|
| 295 |
+
// B. DEFINE MAP NODES (Grandstands, Food, Hubs, Gates)
|
| 296 |
// -------------------------------------------------------------
|
| 297 |
const nodes = {
|
| 298 |
// Grandstands
|
|
|
|
| 308 |
"Village Plaza": { pos: toCanvas(-1.8, 2.2), type: "food", label: "π Village Plaza" },
|
| 309 |
"Club Eats": { pos: toCanvas(0.2, -2.2), type: "food", label: "π¦ Club Eats" },
|
| 310 |
|
| 311 |
+
// Intermediate Junction Hubs
|
| 312 |
+
"Arena Crossroad": { pos: toCanvas(-1.2, 0.8), type: "hub", label: "π Arena Hub" },
|
| 313 |
+
"Becketts Interchange": { pos: toCanvas(0.5, 2.2), type: "hub", label: "π Becketts Hub" },
|
| 314 |
+
"South Bypass": { pos: toCanvas(-0.8, -1.5), type: "hub", label: "π South Hub" },
|
| 315 |
+
|
| 316 |
// Gates
|
| 317 |
+
"Main Gate 1": { pos: toCanvas(-3.5, 0), type: "gate", label: "πͺ Main Gate 1" },
|
| 318 |
"South Gate 2": { pos: toCanvas(1, -4.2), type: "gate", label: "πͺ South Gate 2" },
|
| 319 |
+
"East Gate 3": { pos: toCanvas(4.5, -1), type: "gate", label: "πͺ East Gate 3" },
|
| 320 |
+
"West Gate 4": { pos: toCanvas(-4.0, 2.8), type: "gate", label: "πͺ West Gate 4" }
|
| 321 |
};
|
| 322 |
|
| 323 |
// -------------------------------------------------------------
|
| 324 |
+
// C. INTERCONNECTED STANDARD PEDESTRIAN PATHS (GREEN / RED)
|
| 325 |
// -------------------------------------------------------------
|
| 326 |
const paths = [
|
| 327 |
+
// Network towards Gate 1 (Turns Red on Gate 1 Bottleneck)
|
| 328 |
+
{ start: "Hamilton GS", end: "Arena Crossroad", color: isGate1Bottleneck ? "#ff4444" : "#00cc66" },
|
| 329 |
+
{ start: "Arena Crossroad", end: "Main Fan Zone", color: isGate1Bottleneck ? "#ff4444" : "#00cc66" },
|
| 330 |
+
{ start: "Main Fan Zone", end: "Main Gate 1", color: isGate1Bottleneck ? "#ff4444" : "#00cc66" },
|
| 331 |
+
{ start: "Village GS", end: "Arena Crossroad", color: isGate1Bottleneck ? "#ff4444" : "#00cc66" },
|
| 332 |
+
|
| 333 |
+
// Network towards Becketts & Gate 3
|
| 334 |
+
{ start: "Becketts GS", end: "Becketts Interchange", color: isGate3Bottleneck ? "#ff4444" : "#00cc66" },
|
| 335 |
+
{ start: "Becketts Interchange", end: "Becketts Food", color: isGate3Bottleneck ? "#ff4444" : "#00cc66" },
|
| 336 |
+
{ start: "Becketts Interchange", end: "Main Fan Zone", color: isGate1Bottleneck ? "#ff4444" : "#00cc66" },
|
| 337 |
+
{ start: "Becketts Food", end: "Stowe GS", color: isGate3Bottleneck ? "#ff4444" : "#00cc66" },
|
| 338 |
+
{ start: "Stowe GS", end: "East Gate 3", color: isGate3Bottleneck ? "#ff4444" : "#00cc66" },
|
| 339 |
+
|
| 340 |
+
// West & South Paths
|
| 341 |
{ start: "Village GS", end: "Village Plaza", color: "#00cc66" },
|
| 342 |
{ start: "Village Plaza", end: "West Gate 4", color: "#00cc66" },
|
| 343 |
+
{ start: "Hamilton GS", end: "South Bypass", color: "#00cc66" },
|
| 344 |
+
{ start: "South Bypass", end: "Club Eats", color: "#00cc66" },
|
|
|
|
| 345 |
{ start: "Club GS", end: "Club Eats", color: "#00cc66" },
|
| 346 |
+
{ start: "Club Eats", end: "South Gate 2", color: "#00cc66" }
|
|
|
|
| 347 |
];
|
| 348 |
|
| 349 |
paths.forEach(p => {
|
|
|
|
| 356 |
});
|
| 357 |
|
| 358 |
// -------------------------------------------------------------
|
| 359 |
+
// D. EXPANDED GOOGLE MAPS MULTI-NODE DETOURS (PURPLE)
|
| 360 |
// -------------------------------------------------------------
|
| 361 |
+
if (isGate1Bottleneck) {
|
| 362 |
+
// Multi-leg Detour 1: Fan Zone -> South Bypass -> Club Eats -> South Gate 2
|
| 363 |
ctx.beginPath();
|
| 364 |
+
ctx.setLineDash([8, 6]);
|
| 365 |
ctx.moveTo(nodes["Main Fan Zone"].pos.x, nodes["Main Fan Zone"].pos.y);
|
| 366 |
+
ctx.lineTo(nodes["South Bypass"].pos.x, nodes["South Bypass"].pos.y);
|
| 367 |
ctx.lineTo(nodes["Club Eats"].pos.x, nodes["Club Eats"].pos.y);
|
| 368 |
ctx.lineTo(nodes["South Gate 2"].pos.x, nodes["South Gate 2"].pos.y);
|
| 369 |
+
ctx.strokeStyle = "#a020f0";
|
| 370 |
ctx.lineWidth = 5;
|
| 371 |
ctx.stroke();
|
| 372 |
|
| 373 |
+
// Multi-leg Detour 2: Arena Hub -> Village Plaza -> West Gate 4
|
| 374 |
ctx.beginPath();
|
| 375 |
+
ctx.moveTo(nodes["Arena Crossroad"].pos.x, nodes["Arena Crossroad"].pos.y);
|
| 376 |
+
ctx.lineTo(nodes["Village Plaza"].pos.x, nodes["Village Plaza"].pos.y);
|
| 377 |
ctx.lineTo(nodes["West Gate 4"].pos.x, nodes["West Gate 4"].pos.y);
|
| 378 |
ctx.strokeStyle = "#a020f0";
|
| 379 |
ctx.lineWidth = 5;
|
| 380 |
ctx.stroke();
|
| 381 |
+
ctx.setLineDash([]);
|
| 382 |
+
}
|
| 383 |
+
|
| 384 |
+
if (isGate3Bottleneck) {
|
| 385 |
+
// Detour 3: Becketts Interchange -> Arena Hub -> South Bypass -> South Gate 2
|
| 386 |
+
ctx.beginPath();
|
| 387 |
+
ctx.setLineDash([8, 6]);
|
| 388 |
+
ctx.moveTo(nodes["Becketts Interchange"].pos.x, nodes["Becketts Interchange"].pos.y);
|
| 389 |
+
ctx.lineTo(nodes["Arena Crossroad"].pos.x, nodes["Arena Crossroad"].pos.y);
|
| 390 |
+
ctx.lineTo(nodes["South Bypass"].pos.x, nodes["South Bypass"].pos.y);
|
| 391 |
+
ctx.lineTo(nodes["South Gate 2"].pos.x, nodes["South Gate 2"].pos.y);
|
| 392 |
+
ctx.strokeStyle = "#a020f0";
|
| 393 |
+
ctx.lineWidth = 5;
|
| 394 |
+
ctx.stroke();
|
| 395 |
+
ctx.setLineDash([]);
|
| 396 |
}
|
| 397 |
|
| 398 |
// -------------------------------------------------------------
|
|
|
|
| 402 |
const n = nodes[key];
|
| 403 |
ctx.font = "bold 11px Arial";
|
| 404 |
const textWidth = ctx.measureText(n.label).width;
|
| 405 |
+
const boxPadding = 7;
|
| 406 |
|
| 407 |
+
let boxColor = "#3498db"; // Blue for Grandstands
|
| 408 |
+
if (n.type === "food") boxColor = "#f39c12"; // Orange for Food
|
| 409 |
+
if (n.type === "hub") boxColor = "#7f8c8d"; // Grey for Hubs
|
| 410 |
if (n.type === "gate") {
|
| 411 |
+
if ((key === "Main Gate 1" && isGate1Bottleneck) || (key === "East Gate 3" && isGate3Bottleneck)) {
|
| 412 |
+
boxColor = "#ff4444";
|
| 413 |
+
} else {
|
| 414 |
+
boxColor = "#00cc66";
|
| 415 |
+
}
|
| 416 |
}
|
| 417 |
|
| 418 |
// Draw background rounded box
|
|
|
|
| 430 |
ctx.lineWidth = 1;
|
| 431 |
ctx.stroke();
|
| 432 |
|
| 433 |
+
// Draw label text
|
| 434 |
ctx.fillStyle = "#ffffff";
|
| 435 |
ctx.textAlign = "center";
|
| 436 |
ctx.textBaseline = "middle";
|
|
|
|
| 441 |
ctx.font = "11px Arial";
|
| 442 |
ctx.fillStyle = "#ffffff";
|
| 443 |
ctx.textAlign = "left";
|
| 444 |
+
ctx.fillText("π’ Clear Pathway", 20, 25);
|
| 445 |
+
ctx.fillText("π΄ Congested Network Segment", 140, 25);
|
| 446 |
+
ctx.fillText("π£ Google Maps Active Multi-Leg Detour", 330, 25);
|
| 447 |
}
|
| 448 |
|
| 449 |
+
function updateReport(gate1, gate2, gate3, gate4, isG1Alert, isG3Alert, excess1, excess3) {
|
| 450 |
const reportDiv = document.getElementById('reportBox');
|
| 451 |
|
| 452 |
+
if (isG1Alert || isG3Alert) {
|
| 453 |
reportDiv.className = "report-box alert";
|
| 454 |
+
let alertsHtml = `<h3 style="color: #ff4444;">π΄ NETWORK BOTTLENECK ALERT ACTIVATED</h3>`;
|
| 455 |
+
|
| 456 |
+
if (isG1Alert) {
|
| 457 |
+
alertsHtml += `<p>β’ <strong>Main Gate 1 Bottleneck:</strong> Load <code>${gate1}</code> fans/min (Max limit: ${GATE_1_CAPACITY}). Overflow: <code>${excess1}</code> fans.</p>`;
|
| 458 |
+
}
|
| 459 |
+
if (isG3Alert) {
|
| 460 |
+
alertsHtml += `<p>β’ <strong>East Gate 3 Bottleneck:</strong> Load <code>${gate3}</code> fans/min (Max limit: ${GATE_3_CAPACITY}). Overflow: <code>${excess3}</code> fans.</p>`;
|
| 461 |
+
}
|
| 462 |
+
|
| 463 |
+
alertsHtml += `
|
| 464 |
<hr style="border-color: #552222; margin: 10px 0;">
|
| 465 |
+
<h4 style="color: #a020f0;">π Dynamic Rerouting Protocol:</h4>
|
| 466 |
+
<p>β’ Activating interconnected <span class="badge badge-purple">PURPLE DETOURS</span> across <strong>Arena Hub</strong>, <strong>South Bypass</strong>, and <strong>Becketts Interchange</strong>.</p>
|
| 467 |
+
<p>β’ Directing overflow pedestrian traffic to under-utilized exit points at <strong>South Gate 2</strong> and <strong>West Gate 4</strong>.</p>
|
| 468 |
`;
|
| 469 |
+
reportDiv.innerHTML = alertsHtml;
|
| 470 |
} else {
|
| 471 |
reportDiv.className = "report-box";
|
| 472 |
reportDiv.innerHTML = `
|
| 473 |
+
<h3 style="color: #00cc66;">π’ ALL INTERCONNECTED PATHWAYS OPTIMAL</h3>
|
| 474 |
+
<p>β’ <strong>Main Gate 1 Load:</strong> <code>${gate1}</code> / ${GATE_1_CAPACITY} fans/min (Safe)</p>
|
| 475 |
+
<p>β’ <strong>South Gate 2 Load:</strong> <code>${gate2}</code> / 50 fans/min (Safe)</p>
|
| 476 |
+
<p>β’ <strong>East Gate 3 Load:</strong> <code>${gate3}</code> / ${GATE_3_CAPACITY} fans/min (Safe)</p>
|
| 477 |
+
<p>β’ <strong>West Gate 4 Load:</strong> <code>${gate4}</code> / 45 fans/min (Safe)</p>
|
| 478 |
+
<p style="margin-top: 5px; color: #aaa;"><em>Status: Grid flow is balanced across all junctions. No purple detour routing required.</em></p>
|
| 479 |
`;
|
| 480 |
}
|
| 481 |
}
|