@sailingrotevista/rotevista-dash 6.1.4 → 6.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,490 @@
1
+ <!DOCTYPE html>
2
+ <html lang="it">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Wind Radar - Twin Light Compass Calibrata (v6.0)</title>
7
+ <style>
8
+ :root {
9
+ /* Dimensione dinamica reattiva del widget quadrato */
10
+ --box-size: 400px;
11
+ }
12
+
13
+ body {
14
+ background-color: #ffffff;
15
+ color: #000000;
16
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
17
+ display: flex;
18
+ flex-direction: column;
19
+ align-items: center;
20
+ justify-content: center;
21
+ min-height: 100vh;
22
+ margin: 0;
23
+ padding: 20px;
24
+ box-sizing: border-box;
25
+ }
26
+
27
+ h1 {
28
+ color: #000;
29
+ font-size: 1.3rem;
30
+ margin-bottom: 5px;
31
+ letter-spacing: 1px;
32
+ text-transform: uppercase;
33
+ font-weight: 700;
34
+ }
35
+
36
+ p {
37
+ color: #666;
38
+ font-size: 0.85rem;
39
+ margin-top: 0;
40
+ margin-bottom: 25px;
41
+ text-align: center;
42
+ }
43
+
44
+ .container {
45
+ display: flex;
46
+ flex-wrap: wrap;
47
+ gap: 30px;
48
+ align-items: flex-start;
49
+ justify-content: center;
50
+ }
51
+
52
+ .controls-panel {
53
+ background: #fdfdfd;
54
+ border: 1px solid #e5e5e5;
55
+ padding: 20px;
56
+ border-radius: 12px;
57
+ width: 320px;
58
+ box-sizing: border-box;
59
+ box-shadow: 0 4px 12px rgba(0,0,0,0.05);
60
+ }
61
+
62
+ .control-group {
63
+ margin-bottom: 15px;
64
+ }
65
+
66
+ .control-group label {
67
+ display: block;
68
+ font-size: 0.75rem;
69
+ font-weight: bold;
70
+ color: #666;
71
+ text-transform: uppercase;
72
+ margin-bottom: 8px;
73
+ letter-spacing: 0.5px;
74
+ }
75
+
76
+ .control-group input[type="range"] {
77
+ width: 100%;
78
+ accent-color: #00C851;
79
+ background: #ddd;
80
+ height: 4px;
81
+ border-radius: 2px;
82
+ outline: none;
83
+ cursor: pointer;
84
+ }
85
+
86
+ .val-display {
87
+ float: right;
88
+ color: #000;
89
+ font-weight: bold;
90
+ }
91
+
92
+ /* BOX STRUTTURALE SCALABILE */
93
+ .data-box-square {
94
+ position: relative;
95
+ width: var(--box-size);
96
+ height: var(--box-size);
97
+ background: rgb(255, 255, 255);
98
+ border: 1px solid #eee;
99
+ border-radius: 12px;
100
+ box-sizing: border-box;
101
+ display: flex;
102
+ align-items: center;
103
+ justify-content: center;
104
+ overflow: hidden;
105
+ transition: width 0.1s ease, height 0.1s ease;
106
+ }
107
+
108
+ .label-row {
109
+ position: absolute;
110
+ top: 10px;
111
+ left: 12px;
112
+ right: 12px;
113
+ display: flex;
114
+ justify-content: space-between;
115
+ align-items: baseline;
116
+ pointer-events: none;
117
+ z-index: 10;
118
+ }
119
+
120
+ .label {
121
+ color: #888;
122
+ font-size: 0.65rem;
123
+ font-weight: bold;
124
+ text-transform: uppercase;
125
+ }
126
+
127
+ .unit {
128
+ color: #aaa;
129
+ font-size: 0.6rem;
130
+ font-weight: bold;
131
+ }
132
+
133
+ /* Forza l'SVG a riempire lo spazio a disposizione mantenendo le proporzioni */
134
+ svg {
135
+ display: block;
136
+ width: 100%;
137
+ height: 100%;
138
+ max-height: 100%;
139
+ object-fit: contain;
140
+ }
141
+ </style>
142
+ </head>
143
+ <body>
144
+
145
+ <h1>Tactical Wind Radar (v6.0)</h1>
146
+ <p>Calibrazione Spaziatura: Compressione proporzionale degli anelli con luce di 5px dal dial</p>
147
+
148
+ <div class="container">
149
+
150
+ <div class="controls-panel">
151
+
152
+ <!-- SLIDER DI ZOOM SCALABILITÀ -->
153
+ <div class="control-group" style="padding-bottom: 15px; border-bottom: 1px solid #eee; margin-bottom: 20px;">
154
+ <label style="color: #0088cc;">Zoom Box / Scalabilità <span class="val-display" id="scale-val" style="color: #0088cc;">400 px</span></label>
155
+ <input type="range" id="slider-scale" min="200" max="800" value="400" step="10" oninput="resizeBox()">
156
+ </div>
157
+
158
+ <div class="control-group">
159
+ <label>Reef 1 (Limite 1) <span class="val-display" id="r1-val">18 kts</span></label>
160
+ <input type="range" id="slider-r1" min="10" max="22" value="18" step="1" oninput="updateReefs()">
161
+ </div>
162
+
163
+ <div class="control-group">
164
+ <label>Reef 2 (Limite 2) <span class="val-display" id="r2-val">24 kts</span></label>
165
+ <input type="range" id="slider-r2" min="16" max="32" value="24" step="1" oninput="updateReefs()">
166
+ </div>
167
+
168
+ <div class="control-group" style="border-top: 1px solid #eee; padding-top: 15px; margin-bottom: 0;">
169
+ <label>Reef 3 (Storm) <span class="val-display" id="r3-val" style="color: #9c27b0;">30 kts</span></label>
170
+ <span style="font-size: 0.7rem; color: #888; display: block; margin-top: -3px; line-height: 1.2;">
171
+ Calcolato dinamicamente: R2 + (R2 - R1)
172
+ </span>
173
+ </div>
174
+ </div>
175
+
176
+ <div class="data-box-square">
177
+ <div class="label-row">
178
+ <span class="label">TWD (HISTORICAL)</span>
179
+ <span class="unit">ECMWF + N2K</span>
180
+ </div>
181
+
182
+ <!-- BUSSOLA RADAR SVG (Esatto ViewBox dell'originale, nativamente scalabile) -->
183
+ <svg id="wind-radar" viewBox="35 38 330 395" preserveAspectRatio="xMidYMid meet">
184
+ <defs id="radar-gradients">
185
+ <clipPath id="boat-clip"><circle cx="200" cy="200" r="50" /></clipPath>
186
+ <linearGradient id="axiom-grad" x1="0%" y1="0%" x2="0%" y2="100%">
187
+ <stop offset="0%" style="stop-color:#333333;stop-opacity:1" />
188
+ <stop offset="100%" style="stop-color:#999999;stop-opacity:1" />
189
+ </linearGradient>
190
+
191
+ <filter id="center-glow" x="-20%" y="-20%" width="140%" height="140%">
192
+ <feDropShadow dx="0" dy="0" stdDeviation="8" flood-color="#aaaaaa" flood-opacity="0.5" />
193
+ </filter>
194
+ </defs>
195
+
196
+ <!-- I tre sfondi speculari originali diurni con coordinate RGB esatte -->
197
+ <circle cx="200" cy="200" r="160" fill="rgb(252, 252, 252)" />
198
+ <circle cx="200" cy="200" r="125" fill="rgb(240, 240, 240)" />
199
+
200
+ <!-- Ticks originali generati dinamicamente -->
201
+ <g id="ticks"></g>
202
+
203
+ <!-- Etichette cardinali N/S/E/W -->
204
+ <g id="tick-labels" fill="#000000" text-anchor="middle" dominant-baseline="middle" font-family="Arial" font-weight="bold">
205
+ <text font-size="16" transform="translate(200, 74)">N</text>
206
+ <text font-size="16" transform="translate(326, 200) rotate(90)">E</text>
207
+ <text font-size="16" transform="translate(74, 200) rotate(-90)">W</text>
208
+ <text font-size="16" transform="translate(200, 326) rotate(180)">S</text>
209
+
210
+ <text font-size="11" transform="translate(262.5, 91.7) rotate(30)">30</text>
211
+ <text font-size="11" transform="translate(308.3, 137.5) rotate(60)">60</text>
212
+ <text font-size="11" transform="translate(308.3, 262.5) rotate(120)">120</text>
213
+ <text font-size="11" transform="translate(262.5, 308.3) rotate(150)">150</text>
214
+ <text font-size="11" transform="translate(137.5, 91.7) rotate(-30)">30</text>
215
+ <text font-size="11" transform="translate(91.7, 137.5) rotate(-60)">60</text>
216
+ <text font-size="11" transform="translate(91.7, 262.5) rotate(-120)">120</text>
217
+ <text font-size="11" transform="translate(137.5, 308.3) rotate(-150)">150</text>
218
+ </g>
219
+
220
+ <!-- 1. ORBITA DEL PRESENTE "ORA" SUL LIVELLO DI SFONDO (Sotto gli archi) -->
221
+ <!-- Posizionata esattamente sulla mezzeria del raggio dell'Anello 1 (67.2px) -->
222
+ <circle id="ora-orbit" cx="200" cy="200" r="67.2" fill="none" stroke="#cfd8dc" stroke-width="1.2" stroke-dasharray="4, 4" />
223
+
224
+ <!-- Gli anelli radar degli archi verranno stampati qui -->
225
+ <g id="radar-rings"></g>
226
+
227
+ <!-- Cerchio centrale interattivo originale e Barca NERA SOLIDA -->
228
+ <circle id="fullscreen-hotspot" cx="200" cy="200" r="55" fill="rgb(238, 238, 238)" stroke="#e0e0e0" stroke-width="1" filter="url(#center-glow)" cursor="pointer" />
229
+ <path id="boat-icon" d="M200,150 Q165,185 170,250 Q165,190 200,173 Q235,190 230,250 Q235,185 200,150 Z"
230
+ fill="#000000" transform="translate(0, 5)" clip-path="url(#boat-clip)" style="pointer-events: none;" />
231
+ </svg>
232
+ </div>
233
+ </div>
234
+
235
+ <script>
236
+ // Funzione per testare dinamicamente il ridimensionamento del div
237
+ function resizeBox() {
238
+ const size = document.getElementById('slider-scale').value;
239
+ // Modifichiamo la variabile CSS, l'SVG seguirà nativamente
240
+ document.documentElement.style.setProperty('--box-size', size + 'px');
241
+ document.getElementById('scale-val').innerText = size + " px";
242
+ }
243
+
244
+ const CALM_THRESHOLD_KTS = 1.5;
245
+
246
+ // Default Reef settings
247
+ let REEF1 = 18;
248
+ let REEF2 = 24;
249
+ let REEF3 = 30;
250
+
251
+ // Dati di simulazione calibrati su 8 anelli reali con la correzione ripristinata dei diametri
252
+ const mockData = [
253
+ { isFuture: true, twdMin: 235, twdMax: 245, twsPeak: 5.0 }, // Anello 0 (r:59.0px): Previsione futura (Bianco Solido - 100% OPACITÀ CON TRATTEGGIO)
254
+ { isActive: true, twdMin: 220, twdMax: 260, twsPeak: 11.5 }, // Anello 1 (r:67.2px): Presente Mobile (Sfumatura Bianco -> Verde -> Bianco)
255
+ { twdMin: 225, twdMax: 255, twsPeak: 13.5 }, // Anello 2 (r:75.4px): Ora -0.5 (Verde Solido)
256
+ { twdMin: 230, twdMax: 250, twsPeak: 16.0 }, // Anello 3 (r:83.6px): Ora -1.0 (Sfumatura Verde -> Arancio -> Verde)
257
+ { twdMin: 235, twdMax: 245, twsPeak: 20.0 }, // Anello 4 (r:91.8px): Ora -1.5 (Arancio Solido)
258
+ { twdMin: 220, twdMax: 260, twsPeak: 22.5 }, // Anello 5 (r:100.0px): Ora -2.0 (Sfumatura Arancio -> Rosso -> Arancio)
259
+ { twdMin: 230, twdMax: 250, twsPeak: 25.5 }, // Anello 6 (r:108.2px): Ora -2.5 (Rosso Solido)
260
+ { twdMin: 215, twdMax: 265, twsPeak: 28.5 } // Anello 7 (r:116.4px): Ora -3.0 (Sfumatura Rosso -> Viola -> Rosso)
261
+ ];
262
+
263
+ // --- MAPPA RADIALE SPECULARE COMPRESSA AL MILLIMETRO SU 8 ANELLI ---
264
+ // r_i = 59.0px + 8.2px * i (Incastonati in sicurezza, massimo raggio a 116.4px)
265
+ const ringRadii = [59.0, 67.2, 75.4, 83.6, 91.8, 100.0, 108.2, 116.4];
266
+ const chronologicalMapping = [0, 1, 2, 3, 4, 5, 6, 7];
267
+
268
+ const ARC_STROKE_WIDTH = 5.0; // Spessore arco ridotto a 5.0px per una visualizzazione affilatissima
269
+ const BORDER_STROKE_WIDTH = ARC_STROKE_WIDTH + 2; // Spessore 7.0px (bordo da 1px per lato)
270
+
271
+ function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
272
+ const angleInRadians = (angleInDegrees - 90) * Math.PI / 180.0;
273
+ return {
274
+ x: centerX + (radius * Math.cos(angleInRadians)),
275
+ y: centerY + (radius * Math.sin(angleInRadians))
276
+ };
277
+ }
278
+
279
+ function describeArc(centerX, centerY, radius, startAngle, endAngle) {
280
+ const start = polarToCartesian(centerX, centerY, radius, endAngle);
281
+ const end = polarToCartesian(centerX, centerY, radius, startAngle);
282
+
283
+ let arcSweep = endAngle - startAngle;
284
+ if (arcSweep < 0) arcSweep += 360;
285
+
286
+ const largeArcFlag = arcSweep <= 180 ? "0" : "1";
287
+
288
+ return [
289
+ "M", start.x, start.y,
290
+ "A", radius, radius, 0, largeArcFlag, 0, end.x, end.y
291
+ ].join(" ");
292
+ }
293
+
294
+ function drawCompassTicks() {
295
+ const ticksGroup = document.getElementById('ticks');
296
+ ticksGroup.innerHTML = '';
297
+ for (let i = 0; i < 360; i += 10) {
298
+ const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
299
+ const isMajor = (i % 30 === 0);
300
+
301
+ line.setAttribute("x1", "200");
302
+ line.setAttribute("y1", "40");
303
+ line.setAttribute("x2", "200");
304
+ line.setAttribute("y2", isMajor ? "60" : "50");
305
+
306
+ line.setAttribute("stroke", isMajor ? "#000000" : "#bbbbbb");
307
+ line.setAttribute("stroke-width", isMajor ? "2" : "1");
308
+ line.setAttribute("transform", `rotate(${i}, 200, 200)`);
309
+ ticksGroup.appendChild(line);
310
+ }
311
+ }
312
+
313
+ function updateReefs() {
314
+ REEF1 = parseInt(document.getElementById('slider-r1').value);
315
+ REEF2 = parseInt(document.getElementById('slider-r2').value);
316
+ REEF3 = REEF2 + (REEF2 - REEF1);
317
+
318
+ document.getElementById('r1-val').innerText = REEF1 + " kts";
319
+ document.getElementById('r2-val').innerText = REEF2 + " kts";
320
+ document.getElementById('r3-val').innerText = REEF3 + " kts";
321
+
322
+ renderRadar();
323
+ }
324
+
325
+ // --- LA NUOVA SCALA A COLORE DI PROSSIMITÀ CON CONTRASTO RICALIBRATO ---
326
+ function getChordAlignedGradient(id, radius, tws, startAngle, endAngle) {
327
+ const startPt = polarToCartesian(200, 200, radius, endAngle);
328
+ const endPt = polarToCartesian(200, 200, radius, startAngle);
329
+
330
+ let stops = '';
331
+ const R1 = REEF1;
332
+ const R2 = REEF2;
333
+ const R3 = REEF3;
334
+ const colorEdge = 'rgb(240, 240, 240)';
335
+
336
+ // 1. BIANCO SOLIDO (0 - 40% di R1)
337
+ if (tws < R1 * 0.4) {
338
+ return { type: 'solid', color: '#ffffff' };
339
+ }
340
+ // 2. TRANSIZIONE BIANCO -> VERDE (40% - 60% di R1)
341
+ else if (tws >= R1 * 0.4 && tws < R1 * 0.6) {
342
+ stops = `
343
+ <stop offset="0%" stop-color="#ffffff" />
344
+ <stop offset="50%" stop-color="#00C851" />
345
+ <stop offset="100%" stop-color="#ffffff" />
346
+ `;
347
+ }
348
+ // 3. VERDE SOLIDO (60% - 75% di R1) -> Brezza stabile
349
+ else if (tws >= R1 * 0.6 && tws < R1 * 0.75) {
350
+ return { type: 'solid', color: '#00C851' };
351
+ }
352
+ // 4. TRANSIZIONE VERDE -> ARANCIONE (75% R1 - R1) -> Raffiche in aumento
353
+ else if (tws >= R1 * 0.75 && tws < R1) {
354
+ stops = `
355
+ <stop offset="0%" stop-color="#00C851" />
356
+ <stop offset="50%" stop-color="#ff9800" />
357
+ <stop offset="100%" stop-color="#00C851" />
358
+ `;
359
+ }
360
+ // 5. ARANCIONE SOLIDO (R1 -> Metà di R2) -> Vento forte stabile
361
+ else if (tws >= R1 && tws < R1 + (R2 - R1) * 0.5) {
362
+ return { type: 'solid', color: '#ff9800' };
363
+ }
364
+ // 6. TRANSIZIONE ARANCIONE -> ROSSO (Metà R2 -> R2) -> RICALIBRATO AD ALTO CONTRASTO (Dorato -> Cremisi)
365
+ else if (tws >= R1 + (R2 - R1) * 0.5 && tws < R2) {
366
+ stops = `
367
+ <stop offset="0%" stop-color="#ffaa00" /> <!-- Arancio Dorato Brillante sui bordi -->
368
+ <stop offset="50%" stop-color="#d50000" /> <!-- Rosso Cremisi Intenso e saturo al centro -->
369
+ <stop offset="100%" stop-color="#ffaa00" />
370
+ `;
371
+ }
372
+ // 7. ROSSO SOLIDO (R2 -> Metà di R3) -> Burrasca stabile
373
+ else if (tws >= R2 && tws < R2 + (R3 - R2) * 0.5) {
374
+ return { type: 'solid', color: '#ff3b30' };
375
+ }
376
+ // 8. TRANSIZIONE ROSSO -> VIOLA (Metà R3 -> R3) -> Transizione Spettrale (Rosso vivo -> Viola)
377
+ else if (tws >= R2 + (R3 - R2) * 0.5 && tws < R3) {
378
+ stops = `
379
+ <stop offset="0%" stop-color="#ff3b30" />
380
+ <stop offset="50%" stop-color="#9c27b0" />
381
+ <stop offset="100%" stop-color="#ff3b30" />
382
+ `;
383
+ }
384
+ // 9. VIOLA SOLIDO (Oltre R3) -> Tempesta stabile
385
+ else {
386
+ return { type: 'solid', color: '#9c27b0' };
387
+ }
388
+
389
+ const xml = `
390
+ <linearGradient id="${id}" x1="${startPt.x.toFixed(1)}" y1="${startPt.y.toFixed(1)}" x2="${endPt.x.toFixed(1)}" y2="${endPt.y.toFixed(1)}" gradientUnits="userSpaceOnUse">
391
+ ${stops}
392
+ </linearGradient>
393
+ `;
394
+
395
+ return { type: 'gradient', xml: xml, url: `url(#${id})` };
396
+ }
397
+
398
+ function renderRadar() {
399
+ const ringsContainer = document.getElementById('radar-rings');
400
+ const defsContainer = document.getElementById('radar-gradients');
401
+
402
+ defsContainer.innerHTML = `
403
+ <clipPath id="boat-clip"><circle cx="200" cy="200" r="50" /></clipPath>
404
+ <linearGradient id="axiom-grad" x1="0%" y1="0%" x2="0%" y2="100%">
405
+ <stop offset="0%" style="stop-color:#333333;stop-opacity:1" />
406
+ <stop offset="100%" style="stop-color:#999999;stop-opacity:1" />
407
+ </linearGradient>
408
+ <filter id="center-glow" x="-20%" y="-20%" width="140%" height="140%">
409
+ <feDropShadow dx="0" dy="0" stdDeviation="8" flood-color="#aaaaaa" flood-opacity="0.5" />
410
+ </filter>
411
+ `;
412
+ ringsContainer.innerHTML = '';
413
+
414
+ // Imposta dinamicamente i raggi in base a X per renderli scalabili
415
+ const oraOrbit = document.getElementById('ora-orbit');
416
+ if (oraOrbit) oraOrbit.setAttribute("r", 67.2);
417
+
418
+ mockData.forEach((data, index) => {
419
+ const radius = ringRadii[index];
420
+ const gradId = `chord-gradient-${index}`;
421
+
422
+ // IMPORTANTE: Rimosso il dimezzamento dell'opacità per la previsione.
423
+ // Ora l'opacità è sempre 1 (100% solida) per tutti gli anelli,
424
+ // garantendo che il Bianco sia puro e brillante, senza sbiadire in grigio!
425
+ const opacityValue = 1;
426
+
427
+ // --- 1. CASO CALMA PIATTA (Cerchio grigio-azzurro discreto sottile) ---
428
+ if (data.twsPeak < CALM_THRESHOLD_KTS) {
429
+ const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
430
+ circle.setAttribute("cx", "200");
431
+ circle.setAttribute("cy", "200");
432
+ circle.setAttribute("r", radius);
433
+ circle.setAttribute("fill", "none");
434
+ circle.setAttribute("stroke", "#b0bec5");
435
+ circle.setAttribute("stroke-width", "1.2");
436
+ circle.setAttribute("stroke-dasharray", "4, 4");
437
+ ringsContainer.appendChild(circle);
438
+ return;
439
+ }
440
+
441
+ // --- 2. CASO ARCO DIREZIONALE ---
442
+ const grad = getChordAlignedGradient(gradId, radius, data.twsPeak, data.twdMin, data.twdMax);
443
+ let strokeColor = '';
444
+
445
+ if (grad.type === 'gradient') {
446
+ defsContainer.innerHTML += grad.xml;
447
+ strokeColor = grad.url;
448
+ } else {
449
+ strokeColor = grad.color;
450
+ }
451
+
452
+ const pathData = describeArc(200, 200, radius, data.twdMin, data.twdMax);
453
+
454
+ const borderPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
455
+ borderPath.setAttribute("d", pathData);
456
+ borderPath.setAttribute("fill", "none");
457
+ borderPath.setAttribute("stroke", "#000000");
458
+ borderPath.setAttribute("stroke-width", BORDER_STROKE_WIDTH);
459
+ borderPath.setAttribute("stroke-linecap", "round");
460
+ borderPath.setAttribute("opacity", opacityValue);
461
+
462
+ // Tratteggio per l'Anello Previsione (Anello 0)
463
+ if (data.isFuture) {
464
+ borderPath.setAttribute("stroke-dasharray", "10, 6");
465
+ }
466
+ ringsContainer.appendChild(borderPath);
467
+
468
+ const mainPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
469
+ mainPath.setAttribute("d", pathData);
470
+ mainPath.setAttribute("fill", "none");
471
+ mainPath.setAttribute("stroke", strokeColor);
472
+ mainPath.setAttribute("stroke-width", ARC_STROKE_WIDTH);
473
+ mainPath.setAttribute("stroke-linecap", "round");
474
+ mainPath.setAttribute("opacity", opacityValue);
475
+
476
+ // Tratteggio per l'Anello Previsione (Anello 0)
477
+ if (data.isFuture) {
478
+ mainPath.setAttribute("stroke-dasharray", "10, 6");
479
+ }
480
+ ringsContainer.appendChild(mainPath);
481
+ });
482
+ }
483
+
484
+ window.onload = function() {
485
+ drawCompassTicks();
486
+ renderRadar();
487
+ };
488
+ </script>
489
+ </body>
490
+ </html>