Ravisil commited on
Commit
95eceac
·
verified ·
1 Parent(s): 4e5697c

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +6 -4
  2. index.html +1209 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Earth
3
- emoji:
4
- colorFrom: green
5
  colorTo: pink
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: earth
3
+ emoji: 🐳
4
+ colorFrom: blue
5
  colorTo: pink
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,1209 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="pt-BR">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Explorador Terrestre | Visualização 3D do Planeta</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.min.js"></script>
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
11
+ <style>
12
+ * {
13
+ margin: 0;
14
+ padding: 0;
15
+ box-sizing: border-box;
16
+ font-family: 'Roboto', sans-serif;
17
+ }
18
+ body {
19
+ overflow: hidden;
20
+ background: #121212;
21
+ color: #fff;
22
+ }
23
+ #earth-container {
24
+ position: fixed;
25
+ top: 0;
26
+ left: 0;
27
+ width: 100%;
28
+ height: 100%;
29
+ z-index: 1;
30
+ }
31
+ .ui-panel {
32
+ position: fixed;
33
+ background: rgba(0, 0, 0, 0.7);
34
+ border-radius: 10px;
35
+ padding: 15px;
36
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
37
+ backdrop-filter: blur(10px);
38
+ z-index: 100;
39
+ border: 1px solid rgba(255, 255, 255, 0.1);
40
+ }
41
+ #search-panel {
42
+ top: 20px;
43
+ left: 50%;
44
+ transform: translateX(-50%);
45
+ width: 450px;
46
+ max-width: 90%;
47
+ transition: all 0.3s ease;
48
+ }
49
+ #search-panel:focus-within {
50
+ transform: translateX(-50%) scale(1.02);
51
+ box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
52
+ }
53
+ #search-box {
54
+ width: 100%;
55
+ padding: 12px 20px;
56
+ border-radius: 30px;
57
+ border: none;
58
+ background: rgba(255, 255, 255, 0.95);
59
+ font-size: 16px;
60
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
61
+ color: #333;
62
+ transition: all 0.3s ease;
63
+ }
64
+ #search-box:focus {
65
+ outline: none;
66
+ background: white;
67
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
68
+ }
69
+ #search-results {
70
+ max-height: 300px;
71
+ overflow-y: auto;
72
+ margin-top: 15px;
73
+ display: none;
74
+ border-radius: 8px;
75
+ background: rgba(30, 30, 30, 0.9);
76
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
77
+ }
78
+ .search-item {
79
+ padding: 12px 15px;
80
+ border-radius: 8px;
81
+ margin: 5px;
82
+ cursor: pointer;
83
+ transition: all 0.2s;
84
+ display: flex;
85
+ align-items: center;
86
+ background: rgba(255, 255, 255, 0.05);
87
+ }
88
+ .search-item:hover {
89
+ background: rgba(65, 105, 225, 0.3);
90
+ transform: translateX(2px);
91
+ }
92
+ .search-item i {
93
+ margin-right: 12px;
94
+ min-width: 20px;
95
+ text-align: center;
96
+ color: #4CAF50;
97
+ }
98
+ #controls-panel {
99
+ bottom: 25px;
100
+ left: 25px;
101
+ display: flex;
102
+ gap: 8px;
103
+ }
104
+ .control-btn {
105
+ background: rgba(255, 255, 255, 0.1);
106
+ border: none;
107
+ color: white;
108
+ width: 48px;
109
+ height: 48px;
110
+ border-radius: 50%;
111
+ cursor: pointer;
112
+ transition: all 0.2s;
113
+ font-size: 18px;
114
+ display: inline-flex;
115
+ align-items: center;
116
+ justify-content: center;
117
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
118
+ }
119
+ .control-btn:hover {
120
+ background: rgba(255, 255, 255, 0.25);
121
+ transform: scale(1.1);
122
+ }
123
+ .control-btn.active {
124
+ background: #4CAF50;
125
+ transform: scale(1.1);
126
+ }
127
+ .control-btn i {
128
+ pointer-events: none;
129
+ }
130
+ #info-panel {
131
+ bottom: 25px;
132
+ right: 25px;
133
+ width: 300px;
134
+ padding: 20px;
135
+ }
136
+ .info-row {
137
+ display: flex;
138
+ justify-content: space-between;
139
+ padding: 10px 0;
140
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
141
+ }
142
+ .info-row:last-child {
143
+ border-bottom: none;
144
+ }
145
+ .info-label {
146
+ color: #aaa;
147
+ font-size: 14px;
148
+ }
149
+ .info-value {
150
+ font-weight: 500;
151
+ color: #fff;
152
+ font-size: 15px;
153
+ }
154
+ #loading-screen {
155
+ position: fixed;
156
+ top: 0;
157
+ left: 0;
158
+ width: 100%;
159
+ height: 100%;
160
+ background: #121212;
161
+ display: flex;
162
+ flex-direction: column;
163
+ align-items: center;
164
+ justify-content: center;
165
+ z-index: 1000;
166
+ transition: opacity 0.5s;
167
+ }
168
+ .loading-spinner {
169
+ width: 60px;
170
+ height: 60px;
171
+ border: 6px solid rgba(255, 255, 255, 0.1);
172
+ border-radius: 50%;
173
+ border-top-color: #4CAF50;
174
+ animation: spin 1.2s linear infinite;
175
+ margin-bottom: 25px;
176
+ }
177
+ @keyframes spin {
178
+ to { transform: rotate(360deg); }
179
+ }
180
+ #loading-text {
181
+ color: #fff;
182
+ margin-top: 20px;
183
+ font-size: 18px;
184
+ text-align: center;
185
+ max-width: 80%;
186
+ }
187
+ #layer-panel {
188
+ top: 25px;
189
+ right: 25px;
190
+ width: 220px;
191
+ }
192
+ .panel-title {
193
+ margin-bottom: 15px;
194
+ color: #4CAF50;
195
+ font-size: 16px;
196
+ font-weight: 500;
197
+ display: flex;
198
+ align-items: center;
199
+ }
200
+ .panel-title i {
201
+ margin-right: 10px;
202
+ }
203
+ .layer-option {
204
+ display: flex;
205
+ align-items: center;
206
+ padding: 12px 15px;
207
+ cursor: pointer;
208
+ border-radius: 8px;
209
+ margin: 5px 0;
210
+ transition: all 0.2s;
211
+ background: rgba(255, 255, 255, 0.05);
212
+ }
213
+ .layer-option:hover {
214
+ background: rgba(255, 255, 255, 0.15);
215
+ }
216
+ .layer-option.active {
217
+ background: rgba(76, 175, 80, 0.2);
218
+ }
219
+ .layer-option i {
220
+ margin-right: 12px;
221
+ width: 20px;
222
+ text-align: center;
223
+ color: #4CAF50;
224
+ }
225
+ #overlay {
226
+ position: fixed;
227
+ top: 0;
228
+ left: 0;
229
+ width: 100%;
230
+ height: 100%;
231
+ background: rgba(0, 0, 0, 0.7);
232
+ display: none;
233
+ z-index: 500;
234
+ backdrop-filter: blur(5px);
235
+ }
236
+ #location-details {
237
+ position: fixed;
238
+ top: 50%;
239
+ left: 50%;
240
+ transform: translate(-50%, -50%);
241
+ background: rgba(20, 20, 20, 0.95);
242
+ border-radius: 12px;
243
+ padding: 25px;
244
+ width: 550px;
245
+ max-width: 90%;
246
+ max-height: 80vh;
247
+ overflow-y: auto;
248
+ z-index: 600;
249
+ display: none;
250
+ box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5);
251
+ border: 1px solid rgba(255, 255, 255, 0.1);
252
+ }
253
+ .details-header {
254
+ display: flex;
255
+ justify-content: space-between;
256
+ margin-bottom: 20px;
257
+ padding-bottom: 15px;
258
+ border-bottom: 1px solid rgba(255, 255, 255, 0.2);
259
+ align-items: center;
260
+ }
261
+ .details-header h3 {
262
+ font-size: 22px;
263
+ font-weight: 500;
264
+ }
265
+ .close-btn {
266
+ background: none;
267
+ border: none;
268
+ color: #aaa;
269
+ font-size: 24px;
270
+ cursor: pointer;
271
+ transition: color 0.2s;
272
+ padding: 5px;
273
+ }
274
+ .close-btn:hover {
275
+ color: white;
276
+ transform: scale(1.1);
277
+ }
278
+ .details-image {
279
+ width: 100%;
280
+ height: 220px;
281
+ background-size: cover;
282
+ background-position: center;
283
+ border-radius: 8px;
284
+ margin-bottom: 20px;
285
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
286
+ }
287
+ #location-description {
288
+ color: #ddd;
289
+ line-height: 1.6;
290
+ margin-bottom: 20px;
291
+ }
292
+ .location-stats {
293
+ display: grid;
294
+ grid-template-columns: 1fr 1fr;
295
+ gap: 15px;
296
+ }
297
+ .location-stat {
298
+ background: rgba(255, 255, 255, 0.05);
299
+ padding: 12px;
300
+ border-radius: 8px;
301
+ }
302
+ .stat-label {
303
+ color: #aaa;
304
+ font-size: 13px;
305
+ margin-bottom: 5px;
306
+ }
307
+ .stat-value {
308
+ font-weight: 500;
309
+ font-size: 15px;
310
+ }
311
+ #compass {
312
+ position: fixed;
313
+ top: 25px;
314
+ left: 25px;
315
+ width: 65px;
316
+ height: 65px;
317
+ background: rgba(0, 0, 0, 0.7);
318
+ border-radius: 50%;
319
+ display: flex;
320
+ align-items: center;
321
+ justify-content: center;
322
+ z-index: 100;
323
+ border: 2px solid rgba(255, 255, 255, 0.1);
324
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
325
+ }
326
+ .compass-arrow {
327
+ font-size: 28px;
328
+ transition: transform 0.2s;
329
+ color: #4CAF50;
330
+ }
331
+ #zoom-controls {
332
+ position: fixed;
333
+ right: 25px;
334
+ bottom: 160px;
335
+ display: flex;
336
+ flex-direction: column;
337
+ background: rgba(0, 0, 0, 0.7);
338
+ border-radius: 30px;
339
+ padding: 10px 6px;
340
+ z-index: 100;
341
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
342
+ border: 1px solid rgba(255, 255, 255, 0.1);
343
+ }
344
+ .zoom-btn {
345
+ background: rgba(255, 255, 255, 0.1);
346
+ border: none;
347
+ color: white;
348
+ width: 45px;
349
+ height: 45px;
350
+ border-radius: 50%;
351
+ margin: 5px;
352
+ cursor: pointer;
353
+ display: flex;
354
+ align-items: center;
355
+ justify-content: center;
356
+ font-size: 20px;
357
+ transition: all 0.2s;
358
+ }
359
+ .zoom-btn:hover {
360
+ background: rgba(255, 255, 255, 0.2);
361
+ transform: scale(1.1);
362
+ }
363
+ #time-controls {
364
+ position: fixed;
365
+ top: 25px;
366
+ left: 120px;
367
+ width: 280px;
368
+ z-index: 100;
369
+ }
370
+ #time-slider {
371
+ width: 100%;
372
+ margin-top: 15px;
373
+ -webkit-appearance: none;
374
+ height: 6px;
375
+ background: rgba(255, 255, 255, 0.1);
376
+ border-radius: 3px;
377
+ outline: none;
378
+ }
379
+ #time-slider::-webkit-slider-thumb {
380
+ -webkit-appearance: none;
381
+ width: 18px;
382
+ height: 18px;
383
+ background: #4CAF50;
384
+ border-radius: 50%;
385
+ cursor: pointer;
386
+ }
387
+ .time-display-container {
388
+ display: flex;
389
+ justify-content: space-between;
390
+ align-items: center;
391
+ margin-bottom: 8px;
392
+ }
393
+ .time-label {
394
+ display: flex;
395
+ align-items: center;
396
+ font-size: 14px;
397
+ color: #aaa;
398
+ }
399
+ .time-label i {
400
+ margin-right: 8px;
401
+ color: #4CAF50;
402
+ }
403
+ #time-display {
404
+ font-weight: 500;
405
+ }
406
+ @media (max-width: 768px) {
407
+ #search-panel {
408
+ width: 90%;
409
+ }
410
+
411
+ #layer-panel, #time-controls {
412
+ top: 80px;
413
+ left: 50%;
414
+ transform: translateX(-50%);
415
+ width: 90%;
416
+ }
417
+
418
+ #controls-panel {
419
+ bottom: 80px;
420
+ left: 50%;
421
+ transform: translateX(-50%);
422
+ }
423
+
424
+ #compass {
425
+ top: auto;
426
+ bottom: 180px;
427
+ left: 50%;
428
+ transform: translateX(-50%);
429
+ }
430
+
431
+ #zoom-controls {
432
+ right: 50%;
433
+ transform: translateX(50%);
434
+ bottom: 300px;
435
+ flex-direction: row;
436
+ }
437
+ }
438
+ </style>
439
+ </head>
440
+ <body>
441
+ <div id="loading-screen">
442
+ <div class="loading-spinner"></div>
443
+ <h2 style="color: #fff; margin-bottom: 10px;">Explorador Terrestre</h2>
444
+ <p id="loading-text">Inicializando renderizador 3D...</p>
445
+ </div>
446
+
447
+ <div id="earth-container"></div>
448
+
449
+ <div id="compass">
450
+ <i class="compass-arrow fas fa-arrow-up"></i>
451
+ <div id="compass-degrees">N</div>
452
+ </div>
453
+
454
+ <div class="ui-panel" id="search-panel">
455
+ <input type="text" id="search-box" placeholder="Busque por cidades, pontos turísticos, coordenadas..." autocomplete="off">
456
+ <div id="search-results"></div>
457
+ </div>
458
+
459
+ <div class="ui-panel" id="layer-panel">
460
+ <div class="panel-title"><i class="fas fa-layer-group"></i> Camadas do Mapa</div>
461
+ <div class="layer-option active" data-layer="satellite">
462
+ <i class="fas fa-satellite-dish"></i> Satélite
463
+ </div>
464
+ <div class="layer-option" data-layer="terrain">
465
+ <i class="fas fa-mountain"></i> Terreno
466
+ </div>
467
+ <div class="layer-option" data-layer="roads">
468
+ <i class="fas fa-road"></i> Estradas
469
+ </div>
470
+ <div class="layer-option" data-layer="night">
471
+ <i class="fas fa-moon"></i> Luzes Noturnas
472
+ </div>
473
+ </div>
474
+
475
+ <div class="ui-panel" id="time-controls">
476
+ <div class="time-display-container">
477
+ <span class="time-label"><i class="fas fa-clock"></i> Hora do Dia</span>
478
+ <span id="time-display">12:00 PM</span>
479
+ </div>
480
+ <input type="range" id="time-slider" min="0" max="24" step="0.1" value="12">
481
+ </div>
482
+
483
+ <div class="ui-panel" id="controls-panel">
484
+ <button class="control-btn active" id="rotate-btn" title="Rotacionar">
485
+ <i class="fas fa-sync-alt"></i>
486
+ </button>
487
+ <button class="control-btn" id="fly-btn" title="Modo Voo">
488
+ <i class="fas fa-paper-plane"></i>
489
+ </button>
490
+ <button class="control-btn" id="measure-btn" title="Medir Distância">
491
+ <i class="fas fa-ruler"></i>
492
+ </button>
493
+ <button class="control-btn" id="bookmark-btn" title="Salvar Local">
494
+ <i class="fas fa-bookmark"></i>
495
+ </button>
496
+ </div>
497
+
498
+ <div id="zoom-controls">
499
+ <button class="zoom-btn" id="zoom-in" title="Aproximar">
500
+ <i class="fas fa-plus"></i>
501
+ </button>
502
+ <button class="zoom-btn" id="zoom-out" title="Afastar">
503
+ <i class="fas fa-minus"></i>
504
+ </button>
505
+ </div>
506
+
507
+ <div class="ui-panel" id="info-panel">
508
+ <div class="info-row">
509
+ <span class="info-label">Latitude</span>
510
+ <span class="info-value" id="latitude">0°</span>
511
+ </div>
512
+ <div class="info-row">
513
+ <span class="info-label">Longitude</span>
514
+ <span class="info-value" id="longitude">0°</span>
515
+ </div>
516
+ <div class="info-row">
517
+ <span class="info-label">Altitude</span>
518
+ <span class="info-value" id="altitude">35,786 km</span>
519
+ </div>
520
+ <div class="info-row">
521
+ <span class="info-label">Ângulo de Visão</span>
522
+ <span class="info-value" id="view-angle">10°</span>
523
+ </div>
524
+ </div>
525
+
526
+ <div id="overlay"></div>
527
+
528
+ <div id="location-details">
529
+ <div class="details-header">
530
+ <h3 id="location-title">Detalhes do Local</h3>
531
+ <button class="close-btn" id="close-details">&times;</button>
532
+ </div>
533
+ <div class="details-image" id="location-image"></div>
534
+ <p id="location-description">Esta é uma descrição do local selecionado.</p>
535
+ <div class="location-stats">
536
+ <div class="location-stat">
537
+ <div class="stat-label">Coordenadas</div>
538
+ <div class="stat-value" id="location-coords">0°, 0°</div>
539
+ </div>
540
+ <div class="location-stat">
541
+ <div class="stat-label">Elevação</div>
542
+ <div class="stat-value" id="location-elevation">0 m</div>
543
+ </div>
544
+ <div class="location-stat">
545
+ <div class="stat-label">Tipo</div>
546
+ <div class="stat-value" id="location-type">Ponto Turístico</div>
547
+ </div>
548
+ <div class="location-stat">
549
+ <div class="stat-label">População</div>
550
+ <div class="stat-value" id="location-population">-</div>
551
+ </div>
552
+ </div>
553
+ </div>
554
+
555
+ <script>
556
+ // Dados de localizações - expandido com mais detalhes
557
+ const locations = [
558
+ {
559
+ name: "Torre Eiffel",
560
+ lat: 48.8584,
561
+ lng: 2.2945,
562
+ type: "landmark",
563
+ description: "A Torre Eiffel é uma torre de treliça de ferro forjado no Champ de Mars em Paris, França. Ela recebeu o nome do engenheiro Gustave Eiffel, cuja empresa projetou e construiu a torre. Construída em 1889 como o arco de entrada para a Feira Mundial de 1889, tornou-se um ícone cultural global da França e uma das estruturas mais reconhecíveis do mundo.",
564
+ image: "https://images.unsplash.com/photo-1431274172761-fca41d930114?ixlib=rb-1.2.1&auto=format&fit=crop&w=1000&q=80",
565
+ elevation: "300 m",
566
+ population: "N/A",
567
+ country: "França"
568
+ },
569
+ {
570
+ name: "Estátua da Liberdade",
571
+ lat: 40.6892,
572
+ lng: -74.0445,
573
+ type: "landmark",
574
+ description: "A Estátua da Liberdade é uma colossal escultura neoclássica na Ilha da Liberdade no porto de Nova York. A estátua foi projetada pelo escultor francês Frédéric Auguste Bartholdi e sua estrutura metálica foi construída por Gustave Eiffel. A estátua foi dedicada em 28 de outubro de 1886 como um presente dos Estados Unidos para o povo da França.",
575
+ image: "https://images.unsplash.com/photo-1499856871958-5b9627545d1a?ixlib=rb-1.2.1&auto=format&fit=crop&w=1000&q=80",
576
+ elevation: "93 m",
577
+ population: "N/A",
578
+ country: "Estados Unidos"
579
+ },
580
+ {
581
+ name: "Grande Pirâmide de Gizé",
582
+ lat: 29.9792,
583
+ lng: 31.1342,
584
+ type: "landmark",
585
+ description: "A Grande Pirâmide de Gizé é a mais antiga e maior das pirâmides no complexo de pirâmides de Gizé, no Egito. É a mais antiga das Sete Maravilhas do Mundo Antigo e a única que permanece em grande parte intacta. Egiptólogos acreditam que foi construída como um túmulo para o faraó Khufu da Quarta Dinastia Egípcia ao longo de um período de 20 anos, concluído por volta de 2560 AEC.",
586
+ image: "https://images.unsplash.com/photo-1503177119275-0ee32b63fb8a?ixlib=rb-1.2.1&auto=format&fit=crop&w=1000&q=80",
587
+ elevation: "138 m",
588
+ population: "N/A",
589
+ country: "Egito"
590
+ },
591
+ {
592
+ name: "Monte Everest",
593
+ lat: 27.9881,
594
+ lng: 86.9250,
595
+ type: "mountain",
596
+ description: "O Monte Everest é a montanha mais alta acima do nível do mar, localizada na sub-cordilheira Mahalangur Himal dos Himalaias. A fronteira China-Nepal atravessa seu ponto de cume. Sua elevação de 8.848,86 m foi estabelecida mais recentemente em 2020 pelas autoridades chinesas e nepalesas. O Everest atrai muitos alpinistas, incluindo montanhistas altamente experientes.",
597
+ image: "https://images.unsplash.com/photo-1587135991091-082eebf57ffc?ixlib=rb-1.2.1&auto=format&fit=crop&w=1000&q=80",
598
+ elevation: "8.848 m",
599
+ population: "N/A",
600
+ country: "Nepal/China"
601
+ },
602
+ {
603
+ name: "Casa da Ópera de Sydney",
604
+ lat: -33.8568,
605
+ lng: 151.2153,
606
+ type: "landmark",
607
+ description: "A Casa da Ópera de Sydney é um centro de artes cênicas de múltiplos locais no porto de Sydney, Nova Gales do Sul, Austrália. É um dos edifícios mais famosos e distintos do século XX. Projetado pelo arquiteto dinamarquês Jørn Utzon, o local foi formalmente inaugurado em 20 de outubro de 1973 após um período de gestação começando com a seleção de Utzon como vencedor de uma competição internacional de design em 1957.",
608
+ image: "https://images.unsplash.com/photo-1524820197278-540916411e20?ixlib=rb-1.2.1&auto=format&fit=crop&w=1000&q=80",
609
+ elevation: "20 m",
610
+ population: "N/A",
611
+ country: "Austrália"
612
+ },
613
+ {
614
+ name: "Nova York",
615
+ lat: 40.7128,
616
+ lng: -74.0060,
617
+ type: "city",
618
+ description: "A cidade de Nova York compreende 5 distritos situados onde o rio Hudson encontra o Oceano Atlântico. Em seu núcleo está Manhattan, um distrito densamente povoado que está entre os principais centros comerciais, financeiros e culturais do mundo. Seus locais icônicos incluem arranha-céus como o Empire State Building e o vasto Central Park. O teatro da Broadway é encenado na Times Square iluminada por neon.",
619
+ image: "https://images.unsplash.com/photo-1485871981521-5b1fd3805eee?ixlib=rb-1.2.1&auto=format&fit=crop&w=1000&q=80",
620
+ elevation: "10 m",
621
+ population: "8,4 milhões",
622
+ country: "Estados Unidos"
623
+ },
624
+ {
625
+ name: "Tóquio",
626
+ lat: 35.6762,
627
+ lng: 139.6503,
628
+ type: "city",
629
+ description: "Tóquio, a movimentada capital do Japão, mistura o ultramoderno e o tradicional, desde arranha-céus iluminados por neon até templos históricos. O opulento Santuário Xintoísta Meiji é conhecido por seu portão imponente e pelas florestas ao redor. O Palácio Imperial fica em meio a grandes jardins públicos. Os muitos museus da cidade oferecem exposições que vão desde arte clássica até um teatro kabuki reconstruído.",
630
+ image: "https://images.unsplash.com/photo-1542051841857-5f90071e7989?ixlib=rb-1.2.1&auto=format&fit=crop&w=1000&q=80",
631
+ elevation: "40 m",
632
+ population: "13,9 milhões",
633
+ country: "Japão"
634
+ },
635
+ {
636
+ name: "Grand Canyon",
637
+ lat: 36.1069,
638
+ lng: -112.1129,
639
+ type: "natural",
640
+ description: "O Grand Canyon no Arizona é uma formação natural distinguida por faixas estratificadas de rocha vermelha, revelando milhões de anos de história geológica em corte transversal. Vasto em escala, o cânion tem em média 16 km de largura e 1,6 km de profundidade ao longo de seus 446 km de comprimento. Grande parte da área é um parque nacional, com corredeiras do rio Colorado e vistas panorâmicas.",
641
+ image: "https://images.unsplash.com/photo-1509316785289-025107a2dc08?ixlib=rb-1.2.1&auto=format&fit=crop&w=1000&q=80",
642
+ elevation: "2.600 m",
643
+ population: "N/A",
644
+ country: "Estados Unidos"
645
+ },
646
+ {
647
+ name: "Floresta Amazônica",
648
+ lat: -3.4653,
649
+ lng: -62.2159,
650
+ type: "natural",
651
+ description: "A Floresta Amazônica, cobrindo grande parte do noroeste do Brasil e estendendo-se para Colômbia, Peru e outros países sul-americanos, é a maior floresta tropical do mundo, famosa por sua biodiversidade. Ela é cruzada por milhares de rios, incluindo o poderoso Amazonas. Cidades ribeirinhas, com arquitetura do século XIX dos dias de boom da borracha, incluem Manaus e Belém no Brasil e Leticia na Colômbia.",
652
+ image: "https://images.unsplash.com/photo-1584696049838-8e39294120e5?ixlib=rb-1.2.1&auto=format&fit=crop&w=1000&q=80",
653
+ elevation: "100 m",
654
+ population: "N/A",
655
+ country: "Brasil"
656
+ },
657
+ {
658
+ name: "Grande Barreira de Corais",
659
+ lat: -18.2871,
660
+ lng: 147.6992,
661
+ type: "natural",
662
+ description: "A Grande Barreira de Corais é o maior sistema de recifes de coral do mundo, composto por mais de 2.900 recifes individuais e 900 ilhas se estendendo por mais de 2.300 quilômetros em uma área de aproximadamente 344.400 quilômetros quadrados. O recife está localizado no Mar de Coral, na costa de Queensland, Austrália.",
663
+ image: "https://images.unsplash.com/photo-1544551763-46a013bb70d5?ixlib=rb-1.2.1&auto=format&fit=crop&w=1000&q=80",
664
+ elevation: "Nível do mar",
665
+ population: "N/A",
666
+ country: "Austrália"
667
+ }
668
+ ];
669
+ // Inicializar cena Three.js
670
+ let scene, camera, renderer, earthMesh, cloudsMesh;
671
+ let controls, pointOfInterest, isRotating = true;
672
+ let currentLat = 0, currentLng = 0, currentZoom = 35.786;
673
+ let selectedLocation = null;
674
+
675
+ function init() {
676
+ updateLoadingText("Criando cena 3D...");
677
+
678
+ // Criar cena
679
+ scene = new THREE.Scene();
680
+ scene.background = new THREE.Color(0x000000);
681
+
682
+ // Criar câmera
683
+ camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
684
+ camera.position.z = 15;
685
+
686
+ // Criar renderizador com configurações de melhor qualidade
687
+ renderer = new THREE.WebGLRenderer({
688
+ antialias: true,
689
+ powerPreference: "high-performance",
690
+ logarithmicDepthBuffer: true
691
+ });
692
+ renderer.setPixelRatio(window.devicePixelRatio || 1);
693
+ renderer.setSize(window.innerWidth, window.innerHeight);
694
+ renderer.shadowMap.enabled = true;
695
+ renderer.shadowMap.type = THREE.PCFSoftShadowMap;
696
+ renderer.gammaOutput = true;
697
+ renderer.gammaFactor = 2.2;
698
+ document.getElementById('earth-container').appendChild(renderer.domElement);
699
+
700
+ // Adicionar iluminação
701
+ const ambientLight = new THREE.AmbientLight(0x404040, 1.5);
702
+ scene.add(ambientLight);
703
+
704
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 1.2);
705
+ directionalLight.position.set(5, 3, 5);
706
+ directionalLight.castShadow = true;
707
+ directionalLight.shadow.mapSize.width = 2048;
708
+ directionalLight.shadow.mapSize.height = 2048;
709
+ scene.add(directionalLight);
710
+
711
+ const light = new THREE.PointLight(0xffffff, 0.2);
712
+ light.position.set(0, 20, 0);
713
+ scene.add(light);
714
+
715
+ controls = new THREE.OrbitControls(camera, renderer.domElement);
716
+ controls.enableDamping = true;
717
+ controls.dampingFactor = 0.05;
718
+ controls.rotateSpeed = 0.5;
719
+ controls.minDistance = 5;
720
+ controls.maxDistance = 50;
721
+ controls.maxPolarAngle = Math.PI;
722
+ controls.minPolarAngle = 0;
723
+
724
+ updateLoadingText("Carregando texturas da Terra...");
725
+
726
+ // Criar Terra com materiais melhorados
727
+ createEarth();
728
+
729
+ // Adicionar fundo de estrelas com mais densidade
730
+ createStars();
731
+
732
+ // Configurar interações da UI
733
+ setupUI();
734
+
735
+ // Iniciar loop de animação
736
+ animate();
737
+
738
+ // Esconder tela de carregamento quando tudo estiver pronto
739
+ setTimeout(() => {
740
+ document.getElementById('loading-screen').style.opacity = '0';
741
+ setTimeout(() => {
742
+ document.getElementById('loading-screen').style.display = 'none';
743
+ }, 500);
744
+ }, 1500);
745
+ }
746
+
747
+ function createEarth() {
748
+ const radius = 5;
749
+ const geometry = new THREE.SphereGeometry(radius, 128, 128);
750
+
751
+ // Usar texturas de melhor qualidade para a Terra
752
+ const textureLoader = new THREE.TextureLoader();
753
+
754
+ const earthMaterial = new THREE.MeshStandardMaterial({
755
+ map: textureLoader.load('https://threejs.org/examples/textures/planets/earth_atmos_2048.jpg'),
756
+ bumpMap: textureLoader.load('https://threejs.org/examples/textures/planets/earth_normal_2048.jpg'),
757
+ bumpScale: 0.05,
758
+ roughnessMap: textureLoader.load('https://threejs.org/examples/textures/planets/earth_specular_2048.jpg'),
759
+ metalness: 0.1,
760
+ roughness: 0.8,
761
+ displacementScale: 0.1
762
+ });
763
+
764
+ earthMesh = new THREE.Mesh(geometry, earthMaterial);
765
+ earthMesh.castShadow = true;
766
+ earthMesh.receiveShadow = true;
767
+ scene.add(earthMesh);
768
+
769
+ // Criar efeito de atmosfera/nuvens
770
+ const cloudsGeometry = new THREE.SphereGeometry(radius * 1.005, 128, 128);
771
+ const cloudsMaterial = new THREE.MeshStandardMaterial({
772
+ map: textureLoader.load('https://threejs.org/examples/textures/planets/earth_clouds_1024.png'),
773
+ transparent: true,
774
+ opacity: 0.3,
775
+ alphaMap: textureLoader.load('https://threejs.org/examples/textures/planets/earth_clouds_1024.png'),
776
+ side: THREE.DoubleSide,
777
+ blending: THREE.AdditiveBlending
778
+ });
779
+
780
+ cloudsMesh = new THREE.Mesh(cloudsGeometry, cloudsMaterial);
781
+ scene.add(cloudsMesh);
782
+
783
+ // Adicionar efeito de água
784
+ const waterGeometry = new THREE.SphereGeometry(radius, 128, 128);
785
+ const waterMaterial = new THREE.MeshStandardMaterial({
786
+ color: 0x44aadd,
787
+ transparent: true,
788
+ opacity: 0.2,
789
+ metalness: 0.7,
790
+ roughness: 0.1,
791
+ side: THREE.DoubleSide
792
+ });
793
+
794
+ const waterMesh = new THREE.Mesh(waterGeometry, waterMaterial);
795
+ scene.add(waterMesh);
796
+ }
797
+
798
+ function createStars() {
799
+ const starsGeometry = new THREE.BufferGeometry();
800
+ const starsMaterial = new THREE.PointsMaterial({
801
+ color: 0xffffff,
802
+ size: 0.15,
803
+ sizeAttenuation: true,
804
+ transparent: true
805
+ });
806
+
807
+ const starsVertices = [];
808
+ for (let i = 0; i < 10000; i++) {
809
+ const x = (Math.random() - 0.5) * 2000;
810
+ const y = (Math.random() - 0.5) * 2000;
811
+ const z = (Math.random() - 0.5) * 2000;
812
+ starsVertices.push(x, y, z);
813
+ }
814
+
815
+ starsGeometry.setAttribute('position', new THREE.Float32BufferAttribute(starsVertices, 3));
816
+ const stars = new THREE.Points(starsGeometry, starsMaterial);
817
+ scene.add(stars);
818
+ }
819
+
820
+ function setupUI() {
821
+ // Funcionalidade de busca aprimorada
822
+ const searchBox = document.getElementById('search-box');
823
+ const searchResults = document.getElementById('search-results');
824
+
825
+ let searchDebounce;
826
+ searchBox.addEventListener('input', function() {
827
+ clearTimeout(searchDebounce);
828
+ const query = this.value.toLowerCase().trim();
829
+
830
+ if (query.length < 2) {
831
+ searchResults.style.display = 'none';
832
+ return;
833
+ }
834
+
835
+ searchDebounce = setTimeout(() => {
836
+ const results = locations.filter(loc =>
837
+ loc.name.toLowerCase().includes(query) ||
838
+ loc.description.toLowerCase().includes(query) ||
839
+ loc.country.toLowerCase().includes(query) ||
840
+ loc.type.toLowerCase().includes(query)
841
+ );
842
+
843
+ if (results.length > 0) {
844
+ searchResults.innerHTML = results.map(loc => `
845
+ <div class="search-item" data-lat="${loc.lat}" data-lng="${loc.lng}" data-name="${loc.name}">
846
+ <i class="fas fa-${getLocationIcon(loc.type)}"></i>
847
+ <div>
848
+ <div style="font-weight:500;">${loc.name}</div>
849
+ <div style="font-size:12px;color:#aaa;">${loc.country} • ${loc.type.charAt(0).toUpperCase() + loc.type.slice(1)}</div>
850
+ </div>
851
+ </div>
852
+ `).join('');
853
+
854
+ // Adicionar eventos de clique aos resultados da busca
855
+ document.querySelectorAll('.search-item').forEach(item => {
856
+ item.addEventListener('click', function() {
857
+ const lat = parseFloat(this.getAttribute('data-lat'));
858
+ const lng = parseFloat(this.getAttribute('data-lng'));
859
+ const name = this.getAttribute('data-name');
860
+
861
+ flyToLocation(lat, lng);
862
+ showLocationDetails(name);
863
+
864
+ searchResults.style.display = 'none';
865
+ searchBox.value = '';
866
+ });
867
+ });
868
+
869
+ searchResults.style.display = 'block';
870
+ } else {
871
+ searchResults.innerHTML = '<div style="padding:15px;color:#aaa;text-align:center;">Nenhum resultado encontrado<br><small>Tente um termo diferente</small></div>';
872
+ searchResults.style.display = 'block';
873
+ }
874
+ }, 300);
875
+ });
876
+
877
+ // Fechar resultados da busca ao clicar fora
878
+ document.addEventListener('click', function(e) {
879
+ if (!searchBox.contains(e.target) && !searchResults.contains(e.target)) {
880
+ searchResults.style.display = 'none';
881
+ }
882
+ });
883
+
884
+ // Botões de modo
885
+ document.getElementById('rotate-btn').addEventListener('click', function() {
886
+ isRotating = true;
887
+ this.classList.add('active');
888
+ document.getElementById('fly-btn').classList.remove('active');
889
+ controls.enableRotate = true;
890
+ controls.autoRotate = true;
891
+ controls.autoRotateSpeed = 0.5;
892
+ });
893
+
894
+ document.getElementById('fly-btn').addEventListener('click', function() {
895
+ isRotating = false;
896
+ this.classList.add('active');
897
+ document.getElementById('rotate-btn').classList.remove('active');
898
+ controls.autoRotate = false;
899
+ });
900
+
901
+ // Controles de zoom com animação
902
+ document.getElementById('zoom-in').addEventListener('click', function() {
903
+ animateCameraPosition(camera.position.z * 0.8);
904
+ });
905
+
906
+ document.getElementById('zoom-out').addEventListener('click', function() {
907
+ animateCameraPosition(camera.position.z * 1.2);
908
+ });
909
+
910
+ // Controles de camada com feedback visual
911
+ document.querySelectorAll('.layer-option').forEach(option => {
912
+ option.addEventListener('click', function() {
913
+ document.querySelectorAll('.layer-option').forEach(opt => opt.classList.remove('active'));
914
+ this.classList.add('active');
915
+
916
+ // Simular mudança de camada (em um app real isso mudaria as texturas)
917
+ console.log('Camada alterada para:', this.dataset.layer);
918
+
919
+ // Mostrar toast de confirmação
920
+ showToast(`Mudou para visualização ${this.dataset.layer}`);
921
+ });
922
+ });
923
+
924
+ // Controle deslizante de tempo com transições suaves
925
+ const timeSlider = document.getElementById('time-slider');
926
+ timeSlider.addEventListener('input', function() {
927
+ updateTimeOfDay(this.value);
928
+ });
929
+
930
+ // Botão de fechar detalhes do local
931
+ document.getElementById('close-details').addEventListener('click', function() {
932
+ document.getElementById('overlay').style.display = 'none';
933
+ document.getElementById('location-details').style.display = 'none';
934
+ });
935
+
936
+ // Lidar com redimensionamento da janela
937
+ window.addEventListener('resize', function() {
938
+ camera.aspect = window.innerWidth / window.innerHeight;
939
+ camera.updateProjectionMatrix();
940
+ renderer.setSize(window.innerWidth, window.innerHeight);
941
+ });
942
+
943
+ // Manipulador de clique na Terra
944
+ renderer.domElement.addEventListener('click', function(event) {
945
+ if (event.target === renderer.domElement) {
946
+ const randomLoc = locations[Math.floor(Math.random() * locations.length)];
947
+ flyToLocation(randomLoc.lat, randomLoc.lng);
948
+ setTimeout(() => {
949
+ showLocationDetails(randomLoc.name);
950
+ }, 1000);
951
+ }
952
+ });
953
+
954
+ // Botão de favoritos
955
+ document.getElementById('bookmark-btn').addEventListener('click', function() {
956
+ if (selectedLocation) {
957
+ showToast(`${selectedLocation.name} salvo nos favoritos`);
958
+ } else {
959
+ showToast(`Nenhum local selecionado para favoritar`);
960
+ }
961
+ });
962
+
963
+ // Botão de medição
964
+ document.getElementById('measure-btn').addEventListener('click', function() {
965
+ showToast(`Ferramenta de medição ativada - clique em dois pontos no globo`);
966
+ // Em um app real, isso ativaria o modo de medição
967
+ });
968
+
969
+ // Inicializar hora do dia
970
+ updateTimeOfDay(timeSlider.value);
971
+ }
972
+
973
+ function getLocationIcon(type) {
974
+ switch(type) {
975
+ case 'city': return 'city';
976
+ case 'mountain': return 'mountain';
977
+ case 'natural': return 'tree';
978
+ default: return 'landmark';
979
+ }
980
+ }
981
+
982
+ function flyToLocation(lat, lng, duration = 1000) {
983
+ // Converter lat/lng para coordenadas esféricas
984
+ const phi = (90 - lat) * (Math.PI / 180);
985
+ const theta = (180 - lng) * (Math.PI / 180);
986
+
987
+ // Calcular posição alvo
988
+ const radius = camera.position.distanceTo(new THREE.Vector3(0, 0, 0));
989
+ const targetPosition = new THREE.Vector3(
990
+ radius * Math.sin(phi) * Math.cos(theta),
991
+ radius * Math.cos(phi),
992
+ radius * Math.sin(phi) * Math.sin(theta)
993
+ );
994
+
995
+ // Armazenar posição atual
996
+ const startPosition = camera.position.clone();
997
+ const startTime = Date.now();
998
+
999
+ // Função de animação
1000
+ function animateFlight() {
1001
+ const elapsed = Date.now() - startTime;
1002
+ const progress = Math.min(elapsed / duration, 1);
1003
+
1004
+ // Função ease in-out
1005
+ const easedProgress = progress < 0.5 ?
1006
+ 2 * progress * progress :
1007
+ 1 - Math.pow(-2 * progress + 2, 2) / 2;
1008
+
1009
+ // Interpolar posição
1010
+ camera.position.lerpVectors(startPosition, targetPosition, easedProgress);
1011
+ camera.lookAt(0, 0, 0);
1012
+
1013
+ // Continuar até que a duração seja alcançada
1014
+ if (progress < 1) {
1015
+ requestAnimationFrame(animateFlight);
1016
+ } else {
1017
+ // Atualizar informações da localização atual
1018
+ currentLat = lat;
1019
+ currentLng = lng;
1020
+ updateInfoPanel();
1021
+ }
1022
+ }
1023
+
1024
+ // Iniciar animação
1025
+ animateFlight();
1026
+
1027
+ // Encontrar e selecionar a localização
1028
+ selectedLocation = locations.find(loc => loc.lat === lat && loc.lng === lng);
1029
+
1030
+ // Mostrar notificação toast
1031
+ if (selectedLocation) {
1032
+ setTimeout(() => {
1033
+ showToast(`Chegou em ${selectedLocation.name}`);
1034
+ }, duration);
1035
+ }
1036
+
1037
+ // Habilitar amortecimento suave após o voo
1038
+ controls.enableDamping = true;
1039
+ }
1040
+
1041
+ function animateCameraPosition(targetZ) {
1042
+ const startZ = camera.position.z;
1043
+ const startTime = Date.now();
1044
+ const duration = 500; // milissegundos
1045
+
1046
+ function animateZoom() {
1047
+ const elapsed = Date.now() - startTime;
1048
+ const progress = Math.min(elapsed / duration, 1);
1049
+
1050
+ // Função quadratic ease-out
1051
+ const easedProgress = -1 * progress * (progress - 2);
1052
+
1053
+ camera.position.z = startZ + (targetZ - startZ) * easedProgress;
1054
+
1055
+ if (progress < 1) {
1056
+ requestAnimationFrame(animateZoom);
1057
+ }
1058
+ }
1059
+
1060
+ animateZoom();
1061
+ }
1062
+
1063
+ function showLocationDetails(name) {
1064
+ const location = locations.find(loc => loc.name === name);
1065
+ if (!location) return;
1066
+
1067
+ // Atualizar painel de detalhes
1068
+ document.getElementById('location-title').textContent = location.name;
1069
+ document.getElementById('location-description').textContent = location.description;
1070
+ document.getElementById('location-image').style.backgroundImage = `url(${location.image})`;
1071
+ document.getElementById('location-coords').textContent =
1072
+ `${Math.abs(location.lat).toFixed(4)}° ${location.lat > 0 ? 'N' : 'S'}, ` +
1073
+ `${Math.abs(location.lng).toFixed(4)}° ${location.lng > 0 ? 'E' : 'W'}`;
1074
+ document.getElementById('location-elevation').textContent = location.elevation;
1075
+ document.getElementById('location-type').textContent =
1076
+ location.type.charAt(0).toUpperCase() + location.type.slice(1);
1077
+ document.getElementById('location-population').textContent = location.population;
1078
+
1079
+ // Mostrar sobreposição e painel
1080
+ document.getElementById('overlay').style.display = 'block';
1081
+ document.getElementById('location-details').style.display = 'block';
1082
+
1083
+ selectedLocation = location;
1084
+ }
1085
+
1086
+ function updateTimeOfDay(hour) {
1087
+ // Atualizar a exibição do tempo
1088
+ const period = hour >= 12 ? 'PM' : 'AM';
1089
+ const displayHour = hour % 12 === 0 ? 12 : hour % 12;
1090
+ const minutes = Math.floor((hour % 1) * 60).toString().padStart(2, '0');
1091
+ document.getElementById('time-display').textContent = `${displayHour}:${minutes} ${period}`;
1092
+
1093
+ // Simular ciclo dia/noite girando a Terra e ajustando a iluminação
1094
+ const normalizedHour = parseFloat(hour) / 24;
1095
+ earthMesh.rotation.y = normalizedHour * Math.PI * 2;
1096
+
1097
+ // Ajustar luz ambiente baseado no tempo
1098
+ const ambientIntensity = Math.abs((normalizedHour - 0.25) % 1 - 0.5) * 2; // Picos ao meio-dia
1099
+ scene.children.forEach(child => {
1100
+ if (child instanceof THREE.AmbientLight) {
1101
+ child.intensity = ambientIntensity * 0.8 + 0.5;
1102
+ }
1103
+ });
1104
+ }
1105
+
1106
+ function updateInfoPanel() {
1107
+ const spherical = new THREE.Spherical();
1108
+ spherical.setFromVector3(camera.position);
1109
+
1110
+ // Calcular latitude/longitude
1111
+ const lat = 90 - (spherical.phi * 180 / Math.PI);
1112
+ const lng = -180 + (spherical.theta * 180 / Math.PI);
1113
+
1114
+ // Atualizar coordenadas
1115
+ document.getElementById('latitude').textContent = `${Math.abs(lat).toFixed(2)}° ${lat > 0 ? 'N' : 'S'}`;
1116
+ document.getElementById('longitude').textContent = `${Math.abs(lng).toFixed(2)}° ${lng > 0 ? 'E' : 'W'}`;
1117
+
1118
+ // Calcular altitude (distância do centro menos raio da Terra)
1119
+ const alt = (camera.position.distanceTo(new THREE.Vector3(0, 0, 0)) - 5) * 6371;
1120
+ let altitudeText;
1121
+
1122
+ if (alt < 0.1) {
1123
+ altitudeText = `${(alt).toFixed(0)} m`;
1124
+ } else if (alt < 1000) {
1125
+ altitudeText = `${(alt).toFixed(2)} km`;
1126
+ } else {
1127
+ altitudeText = `${(alt).toFixed(0)} km`;
1128
+ }
1129
+ document.getElementById('altitude').textContent = altitudeText;
1130
+
1131
+ // Calcular ângulo de visão
1132
+ const viewAngle = Math.atan(5 / camera.position.distanceTo(new THREE.Vector3(0, 0, 0))) * 180 / Math.PI;
1133
+ document.getElementById('view-angle').textContent = `${viewAngle.toFixed(1)}°`;
1134
+
1135
+ // Atualizar bússola
1136
+ updateCompass(lng);
1137
+
1138
+ currentLat = lat;
1139
+ currentLng = lng;
1140
+ currentZoom = alt;
1141
+ }
1142
+
1143
+ function updateCompass(degree) {
1144
+ const arrow = document.querySelector('.compass-arrow');
1145
+ const compassDegrees = document.getElementById('compass-degrees');
1146
+
1147
+ // Normalizar grau para 0-360
1148
+ degree = (degree % 360 + 360) % 360;
1149
+
1150
+ // Girar seta
1151
+ arrow.style.transform = `rotateZ(${-degree}deg)`;
1152
+
1153
+ // Atualizar direção da bússola
1154
+ const directions = ['N', 'NE', 'L', 'SE', 'S', 'SO', 'O', 'NO'];
1155
+ const dirIndex = Math.round(((degree % 360) / 45)) % 8;
1156
+ compassDegrees.textContent = directions[dirIndex];
1157
+ }
1158
+
1159
+ function showToast(message) {
1160
+ const toast = document.createElement('div');
1161
+ toast.style.position = 'fixed';
1162
+ toast.style.bottom = '20px';
1163
+ toast.style.left = '50%';
1164
+ toast.style.transform = 'translateX(-50%)';
1165
+ toast.style.backgroundColor = 'rgba(0,0,0,0.8)';
1166
+ toast.style.color = 'white';
1167
+ toast.style.padding = '10px 20px';
1168
+ toast.style.borderRadius = '20px';
1169
+ toast.style.zIndex = '1000';
1170
+ toast.style.opacity = '0';
1171
+ toast.style.transition = 'opacity 0.3s';
1172
+ toast.textContent = message;
1173
+
1174
+ document.body.appendChild(toast);
1175
+
1176
+ setTimeout(() => {
1177
+ toast.style.opacity = '1';
1178
+ }, 10);
1179
+
1180
+ setTimeout(() => {
1181
+ toast.style.opacity = '0';
1182
+ setTimeout(() => {
1183
+ toast.remove();
1184
+ }, 300);
1185
+ }, 3000);
1186
+ }
1187
+
1188
+ function animate() {
1189
+ requestAnimationFrame(animate);
1190
+
1191
+ if (isRotating) {
1192
+ earthMesh.rotation.y += 0.0005;
1193
+ cloudsMesh.rotation.y += 0.0006;
1194
+ }
1195
+
1196
+ controls.update();
1197
+ updateInfoPanel();
1198
+ renderer.render(scene, camera);
1199
+ }
1200
+
1201
+ function updateLoadingText(text) {
1202
+ document.getElementById('loading-text').textContent = text;
1203
+ }
1204
+
1205
+ // Iniciar a aplicação
1206
+ window.onload = init;
1207
+ </script>
1208
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Feito com <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p><p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - <a href="https://enzostvs-deepsite.hf.space?remix=Ravisil/earth" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
1209
+ </html>