-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathindex.html
More file actions
498 lines (476 loc) · 33.3 KB
/
index.html
File metadata and controls
498 lines (476 loc) · 33.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon"
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%2210 0 100 100%22><text y=%22.90em%22 font-size=%2290%22>🗺️</text></svg>">
</link>
<title>GeoParquet Visualizer</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#4F46E5',
secondary: '#10B981',
}
}
}
}
</script>
<link href="https://unpkg.com/maplibre-gl@5.11.0/dist/maplibre-gl.css" rel="stylesheet" />
<!-- Geocoder Plugin -->
<script src="https://unpkg.com/@maplibre/maplibre-gl-geocoder@1.2.0/dist/maplibre-gl-geocoder.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/@maplibre/maplibre-gl-geocoder@1.2.0/dist/maplibre-gl-geocoder.css"
type="text/css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css"
crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="style.css" type="text/css" />
</head>
<body class="h-screen w-screen overflow-hidden bg-gray-50">
<!-- Main Layout -->
<div class="relative h-full w-full">
<!-- Map Container (Full Screen) -->
<div id="map" class="absolute inset-0 w-full h-full"></div>
<!-- Geocoder Container -->
<div id="geocoder-container"></div>
<!-- NEW: Pitch/Bearing display -->
<div
class="absolute bottom-11 right-3 z-10 bg-white/80 backdrop-blur rounded-md shadow px-2 py-1 text-xs text-gray-700 font-mono pointer-events-none">
<span id="map-bearing-display">Bearing: 0.0°</span> | <span id="map-pitch-display">Pitch: 0.0°</span>
</div>
<!-- Toggle Sidebar Button (only visible when sidebar is closed) -->
<button id="toggle-sidebar-outer"
class="fixed top-4 left-4 z-20 p-2 bg-white rounded-lg shadow-lg hover:bg-gray-50 transition-opacity duration-300">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16m-7 6h7"></path>
</svg>
</button>
<!-- Sidebar -->
<div id="sidebar"
class="fixed top-0 left-0 z-10 w-full md:w-[420px] lg:w-[480px] h-full transform transition-transform duration-300"
style="z-index: 10000;">
<!-- Sidebar Content -->
<div class="h-full bg-white/95 backdrop-blur shadow-xl overflow-y-auto flex flex-col">
<!-- Header -->
<div class="px-6 py-5 border-b border-gray-200 bg-white">
<div class="flex items-center justify-between">
<h1 class="text-2xl font-bold text-gray-900">GeoParquet Visualizer</h1>
<div class="flex items-center gap-2 -mr-2">
<button id="toggle-sidebar-inner"
class="p-2 bg-white rounded-lg shadow-lg hover:bg-gray-50">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 6h16M4 12h16m-7 6h7"></path>
</svg>
</button>
</div>
</div>
<p class="text-sm text-gray-600 mt-1">
Visualize local or remote <strong>Parquet</strong> and <strong>GeoParquet</strong> files easily
in your browser without any setup. Tweak the map settings and share the URL with your friends!
Free and open source software by
<a href="https://www.linkedin.com/in/dominik-weckm%C3%BCller/" target="_blank"
rel="noopener noreferrer" class="text-blue-600 hover:underline">
Dominik Weckmüller</a>. <a href="https://github.com/do-me/geoparquet-visualizer" target="_blank" class="text-blue-600 hover:underline">GitHub</a><br><br>
</p>
<div class="pt-2 border-t border-gray-200">
<div id="shr-container" class="flex flex-wrap justify-center items-center gap-1.5 relative">
<!-- Native Share -->
<button id="shr-native" title="Share"
class="shr-btn bg-gray-700 hover:scale-110 transition-transform rounded-full text-white w-8 h-8 flex items-center justify-center">
<i class="fa-solid fa-share-nodes text-[13px]"></i>
</button>
<!-- Platforms -->
<a id="shr-whatsapp" title="WhatsApp" target="_blank"
class="shr-btn bg-[#25D366] hover:scale-110 transition-transform rounded-full text-white w-8 h-8 flex items-center justify-center">
<i class="fa-brands fa-whatsapp text-[14px]"></i>
</a>
<a id="shr-telegram" title="Telegram" target="_blank"
class="shr-btn bg-[#0088cc] hover:scale-110 transition-transform rounded-full text-white w-8 h-8 flex items-center justify-center">
<i class="fa-brands fa-telegram text-[14px]"></i>
</a>
<a id="shr-twitter" title="Twitter" target="_blank"
class="shr-btn bg-[#1DA1F2] hover:scale-110 transition-transform rounded-full text-white w-8 h-8 flex items-center justify-center">
<i class="fa-brands fa-x-twitter text-[14px]"></i>
</a>
<a id="shr-facebook" title="Facebook" target="_blank"
class="shr-btn bg-[#1877F2] hover:scale-110 transition-transform rounded-full text-white w-8 h-8 flex items-center justify-center">
<i class="fa-brands fa-facebook-f text-[14px]"></i>
</a>
<a id="shr-linkedin" title="LinkedIn" target="_blank"
class="shr-btn bg-[#0077b5] hover:scale-110 transition-transform rounded-full text-white w-8 h-8 flex items-center justify-center">
<i class="fa-brands fa-linkedin-in text-[14px]"></i>
</a>
<a id="shr-reddit" title="Reddit" target="_blank"
class="shr-btn bg-[#ff4500] hover:scale-110 transition-transform rounded-full text-white w-8 h-8 flex items-center justify-center">
<i class="fa-brands fa-reddit-alien text-[14px]"></i>
</a>
<a id="shr-pinterest" title="Pinterest" target="_blank"
class="shr-btn bg-[#E60023] hover:scale-110 transition-transform rounded-full text-white w-8 h-8 flex items-center justify-center">
<i class="fa-brands fa-pinterest-p text-[14px]"></i>
</a>
<a id="shr-email" title="Email"
class="shr-btn bg-gray-500 hover:scale-110 transition-transform rounded-full text-white w-8 h-8 flex items-center justify-center">
<i class="fa-solid fa-envelope text-[13px]"></i>
</a>
<!-- Copy Link -->
<button id="shr-copy" title="Copy Link"
class="shr-btn bg-green-600 hover:scale-110 transition-transform rounded-full text-white w-8 h-8 flex items-center justify-center">
<i class="fa-solid fa-link text-[13px]"></i>
</button>
</div>
</div>
</div>
<!-- NEW: Map Settings -->
<div class="p-6 border-b border-gray-200">
<details>
<summary
class="flex items-center justify-between cursor-pointer font-medium text-lg text-gray-800 hover:text-primary">
<span class="flex items-center gap-3">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24"
stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round"
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
<path stroke-linecap="round" stroke-linejoin="round"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
Map Settings
</span>
<svg class="w-5 h-5 transition-transform duration-200 collapsible-arrow" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 9l-7 7-7-7"></path>
</svg>
</summary>
<div class="mt-6 space-y-6">
<div>
<label for="style-url-input" class="block text-sm font-medium text-gray-900">
Basemap Style URL
</label>
<div class="mt-1 flex gap-2">
<input type="url" id="style-url-input" placeholder="https://..."
class="block w-full rounded-lg border-gray-200 px-4 text-gray-900 placeholder:text-gray-400 focus:border-primary focus:ring-primary sm:text-sm sm:leading-6 transition-colors duration-200">
<button id="set-style-button"
class="px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary text-sm font-semibold flex-shrink-0">Set</button>
</div>
</div>
<div>
<label for="background-color-input" class="block text-sm font-medium text-gray-900">
Background Color
</label>
<div class="mt-1 flex items-center gap-3">
<input type="color" id="background-color-input" value="#f8fafc">
<span id="background-color-hex"
class="font-mono text-sm text-gray-600">#f8fafc</span>
</div>
</div>
<!-- Auto-spin controls -->
<div class="pt-4 border-t border-gray-200">
<label class="block text-sm font-medium text-gray-900">
Map Animation
</label>
<div class="mt-3 flex items-center gap-4">
<button id="play-pause-button"
class="p-2 bg-white rounded-lg shadow hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary">
<!-- SVG icon will be inserted here by JS -->
</button>
<div class="w-full space-y-3">
<div>
<label for="spin-speed-x-slider" class="block text-xs font-medium text-gray-700 mb-1">
Pan Speed (X)
</label>
<div class="flex items-center gap-2">
<input type="range" id="spin-speed-x-slider" min="-1" max="1" step="0.05" value="0.1" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
<input type="number" id="spin-speed-x-number" step="0.0001" value="0.1" class="w-24 rounded-md border-gray-300 shadow-sm text-sm p-1">
</div>
</div>
<div>
<label for="spin-speed-y-slider" class="block text-xs font-medium text-gray-700 mb-1">
Pan Speed (Y)
</label>
<div class="flex items-center gap-2">
<input type="range" id="spin-speed-y-slider" min="-1" max="1" step="0.05" value="0" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
<input type="number" id="spin-speed-y-number" step="0.0001" value="0" class="w-24 rounded-md border-gray-300 shadow-sm text-sm p-1">
</div>
</div>
</div>
</div>
</div>
<!-- Print Map Button -->
<div class="pt-4 border-t border-gray-200">
<button id="print-map-button"
class="w-full flex items-center justify-center gap-2 px-4 py-2 bg-secondary text-white rounded-lg hover:bg-secondary/90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-secondary text-sm font-semibold">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
</svg>
Print Map
</button>
</div>
</div>
</details>
</div>
<!-- Collapsible Add Layer section -->
<div class="p-6 border-b border-gray-200 bg-gray-50/50">
<details open>
<summary
class="flex items-center justify-between cursor-pointer font-medium text-lg text-gray-800 hover:text-primary">
<span class="flex items-center gap-3">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24"
stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round"
d="M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
Add New Layer
</span>
<svg class="w-5 h-5 transition-transform duration-200 collapsible-arrow" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 9l-7 7-7-7"></path>
</svg>
</summary>
<div class="mt-6 space-y-6">
<!-- Upload Zone -->
<div id="drop-zone"
class="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center hover:border-primary transition-colors duration-200 cursor-pointer bg-white/50 backdrop-blur">
<div class="space-y-3">
<div class="inline-flex p-2 rounded-full bg-primary/10">
<svg class="w-6 h-6 text-primary" stroke="currentColor" fill="none"
viewBox="0 0 48 48">
<path
d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4-4m4-4h8m-4-4v8m-12 4h.02"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
</div>
<div>
<label for="file-input"
class="relative cursor-pointer rounded-md font-medium text-primary hover:text-primary-dark focus-within:outline-none">
<span class="text-base">Upload files</span>
<input id="file-input" type="file" class="sr-only"
accept=".parquet,.geoparquet" multiple>
</label>
<p class="text-sm text-gray-500 mt-1">or drag and drop</p>
</div>
</div>
</div>
<!-- URL Input -->
<div class="relative flex items-center">
<div class="flex-grow border-t border-gray-300"></div>
<span class="flex-shrink mx-4 text-gray-400 text-xs font-semibold">OR</span>
<div class="flex-grow border-t border-gray-300"></div>
</div>
<div class="space-y-2">
<label for="url-input" class="block text-sm font-medium text-gray-900">
Load from URL
</label>
<div class="flex gap-2">
<input type="url" id="url-input" placeholder="https://..."
class="block w-full rounded-lg border-gray-200 px-4 text-gray-900 placeholder:text-gray-400 focus:border-primary focus:ring-primary sm:text-sm sm:leading-6 transition-colors duration-200">
<button id="load-url-button"
class="px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary text-sm font-semibold flex-shrink-0">Load</button>
</div>
</div>
<!-- Feature Limit -->
<div class="relative pt-2">
<label for="limit-input" class="block text-sm font-medium text-gray-900 mb-1">
Feature Limit
<span class="ml-1 text-xs text-gray-500">(per file)</span>
</label>
<div class="relative mt-1 rounded-md shadow-sm">
<input type="number" id="limit-input" value="100000" min="1"
class="block w-full rounded-lg border-gray-200 px-4 pr-16 text-gray-900 placeholder:text-gray-400 focus:border-primary focus:ring-primary sm:text-sm sm:leading-6 transition-colors duration-200">
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-4">
<span class="text-gray-400 sm:text-sm">points</span>
</div>
</div>
</div>
<!-- Advanced Options (Nested Collapsible) -->
<details class="pt-2">
<summary class="cursor-pointer text-sm font-medium text-gray-700 hover:text-primary">
<span class="flex items-center gap-1">
<svg class="w-4 h-4 transition-transform duration-200 collapsible-arrow"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 9l-7 7-7-7"></path>
</svg>
Advanced Options
</span>
</summary>
<div class="relative mt-4 pl-5">
<label class="block text-sm font-medium text-gray-900 mb-1">
Update Strategy
<span class="ml-1 text-xs text-gray-500">(how to refresh the map)</span>
</label>
<div class="mt-2 space-y-2">
<div class="flex items-center"><input type="radio" id="strategy-final"
name="update-strategy" value="final" checked
class="h-4 w-4 border-gray-300 text-primary focus:ring-primary"><label
for="strategy-final" class="ml-2 block text-sm text-gray-900">Only when
finished
<span class="text-xs text-gray-500 block">Best for large
files</span></label>
</div>
<div class="flex items-center"><input type="radio" id="strategy-live"
name="update-strategy" value="live"
class="h-4 w-4 border-gray-300 text-primary focus:ring-primary"><label
for="strategy-live" class="ml-2 block text-sm text-gray-900">Live
updates <span class="text-xs text-gray-500 block">For smaller
files</span></label></div>
</div>
</div>
</details>
</div>
</details>
</div>
<!-- Layer List -->
<div class="p-6 border-b border-gray-200 bg-gray-50/50">
<details open>
<summary
class="flex items-center justify-between cursor-pointer font-medium text-lg text-gray-800 hover:text-primary">
<span class="flex items-center gap-3">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24"
stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 2L3 7l9 5 9-5-9-5z" />
<path stroke-linecap="round" stroke-linejoin="round"
d="M3 12l9 5 9-5M3 17l9 5 9-5" />
</svg>
Layers
</span>
<svg class="w-5 h-5 transition-transform duration-200 collapsible-arrow" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 9l-7 7-7-7"></path>
</svg>
</summary>
<div id="layer-list-container" class="flex-grow p-6 space-y-4 overflow-y-auto">
<!-- Layer cards will be inserted here -->
<div id="initial-message" class="text-center text-gray-500 pt-8">
<p>No layers loaded.</p>
<p class="text-sm">Click 'Add New Layer' to begin.</p>
</div>
</div>
</details>
</div>
</div>
</div>
</div>
<!-- Template for a single layer card -->
<template id="layer-card-template">
<div class="bg-gradient-to-r from-primary/5 to-secondary/5 rounded-lg p-4">
<!-- Draggable Handle Area -->
<div class="draggable-handle cursor-grab">
<!-- Main Info -->
<div class="flex items-center justify-between">
<div class="flex items-center space-x-3 overflow-hidden">
<svg class="w-5 h-5 text-primary flex-shrink-0" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" />
</svg>
<h3 class="file-info text-sm font-medium text-gray-900 truncate" title="Filename">No file
selected
</h3>
</div>
<div class="flex gap-1 flex-shrink-0">
<button
class="toggle-visibility p-2 text-gray-500 hover:text-primary hover:bg-primary/10 rounded-lg"
title="Toggle visibility">
<!-- Eye icon will be inserted here -->
</button>
<button class="reload-file p-2 text-gray-500 hover:text-primary hover:bg-primary/10 rounded-lg"
title="Reload file with current settings">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
</button>
<button class="remove-file p-2 text-gray-500 hover:text-red-500 hover:bg-red-50 rounded-lg"
title="Remove file">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
</div>
</div>
<!-- Statistics -->
<div class="grid grid-cols-3 gap-2 mt-3">
<div class="bg-white/70 rounded px-2.5 py-2">
<dt class="text-xs font-medium text-gray-500">Processed</dt>
<dd class="processed-counter mt-1 text-sm font-semibold text-primary">0</dd>
</div>
<div class="bg-white/70 rounded px-2.5 py-2">
<dt class="text-xs font-medium text-gray-500">On Map</dt>
<dd class="map-counter mt-1 text-sm font-semibold text-primary">0</dd>
</div>
<div class="bg-white/70 rounded px-2.5 py-2">
<dt class="text-xs font-medium text-gray-500">Batches</dt>
<dd class="batch-counter mt-1 text-sm font-semibold text-primary">0</dd>
</div>
</div>
<!-- Status -->
<div class="mt-3">
<pre
class="status text-xs bg-gray-50 rounded p-2 font-mono whitespace-pre-wrap text-gray-600 border border-gray-100">Waiting...</pre>
</div>
</div>
<!-- Style Controls -->
<details class="mt-3" draggable="false">
<summary class="cursor-pointer text-sm font-medium text-gray-700 hover:text-primary">
<span class="flex items-center gap-1">
<svg class="w-4 h-4 transition-transform duration-200 collapsible-arrow" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7">
</path>
</svg>
Layer Styling
</span>
</summary>
<div class="mt-3 pl-5 grid grid-cols-1 gap-y-3">
<!-- Point Styles -->
<div class="style-control hidden" data-geom-type="Point">
<label class="text-sm text-gray-800">Points</label>
<div class="flex items-center gap-2 mt-1">
<input type="color" class="style-input" data-style-type="pointColor" value="#ff0000">
<input type="range" min="0" max="1" step="0.05" class="w-full style-input"
data-style-type="pointOpacity" value="0.7">
</div>
</div>
<!-- Line Styles -->
<div class="style-control hidden" data-geom-type="LineString">
<label class="text-sm text-gray-800">Lines</label>
<div class="flex items-center gap-2 mt-1">
<input type="color" class="style-input" data-style-type="lineColor" value="#10B981">
<input type="range" min="0" max="1" step="0.05" class="w-full style-input"
data-style-type="lineOpacity" value="1">
</div>
</div>
<!-- Polygon Fill Styles -->
<div class="style-control hidden" data-geom-type="Polygon">
<label class="text-sm text-gray-800">Polygon Fill</label>
<div class="flex items-center gap-2 mt-1">
<input type="color" class="style-input" data-style-type="polygonFillColor" value="#4F46E5">
<input type="range" min="0" max="1" step="0.05" class="w-full style-input"
data-style-type="polygonFillOpacity" value="0.5">
</div>
</div>
<!-- Polygon Outline Styles -->
<div class="style-control hidden" data-geom-type="Polygon">
<label class="text-sm text-gray-800">Polygon Outline</label>
<div class="flex items-center gap-2 mt-1">
<input type="color" class="style-input" data-style-type="polygonOutlineColor"
value="#4F46E5">
<input type="range" min="0" max="1" step="0.05" class="w-full style-input"
data-style-type="polygonOutlineOpacity" value="1">
</div>
</div>
</div>
</details>
</div>
</template>
<script src="https://unpkg.com/maplibre-gl@5.11.0/dist/maplibre-gl.js"></script>
<script src="main.js" type="module"></script>
</body>
</html>