ignaciomdr commited on
Commit
ac09981
verified
1 Parent(s): e1fb413

Add 3 files

Browse files
Files changed (3) hide show
  1. README.md +7 -5
  2. index.html +841 -19
  3. prompts.txt +4 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Geocam2
3
- emoji: 馃弳
4
- colorFrom: red
5
- colorTo: green
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: geocam2
3
+ emoji: 馃惓
4
+ colorFrom: blue
5
+ colorTo: red
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,841 @@
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="es">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>GeoFoto - Fotograf铆a a KML</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ @keyframes fadeIn {
11
+ from { opacity: 0; transform: translateY(10px); }
12
+ to { opacity: 1; transform: translateY(0); }
13
+ }
14
+
15
+ .fade-in {
16
+ animation: fadeIn 0.5s ease-out forwards;
17
+ }
18
+
19
+ #preview {
20
+ transform: scaleX(-1); /* Espejo para selfie */
21
+ }
22
+
23
+ .watermark {
24
+ text-shadow: 1px 1px 2px black, -1px -1px 2px black;
25
+ }
26
+
27
+ #map {
28
+ height: 300px;
29
+ width: 100%;
30
+ border-radius: 0.5rem;
31
+ }
32
+
33
+ .leaflet-container {
34
+ background: #1e293b;
35
+ }
36
+
37
+ .custom-marker {
38
+ background: transparent;
39
+ border: none;
40
+ }
41
+
42
+ .banner-container {
43
+ width: 100%;
44
+ overflow: hidden;
45
+ border-radius: 0.5rem;
46
+ margin-bottom: 1rem;
47
+ }
48
+
49
+ .banner-img {
50
+ width: 100%;
51
+ height: auto;
52
+ display: block;
53
+ }
54
+
55
+ body {
56
+ background-image: url('https://huggingface.co/datasets/ignaciomdr/fondos/resolve/main/HD%20wallpaper_%20vertical%2C%20landscape%2C%20science%2C%20nature%2C%20water%2C%20no%20people%2C%20red.jpeg');
57
+ background-size: cover;
58
+ background-attachment: fixed;
59
+ background-position: center;
60
+ }
61
+
62
+ .bg-semi-transparent {
63
+ background-color: rgba(15, 23, 42, 0.85);
64
+ }
65
+ </style>
66
+ <!-- Leaflet CSS -->
67
+ <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
68
+ </head>
69
+ <body class="text-slate-100 min-h-screen font-sans">
70
+ <div class="container mx-auto px-4 py-8 max-w-3xl bg-semi-transparent rounded-xl">
71
+ <!-- Header -->
72
+ <header class="mb-4 fade-in">
73
+ <div class="banner-container">
74
+ <img src="https://cdn-uploads.huggingface.co/production/uploads/noauth/7vTxYaZUq3T2toaTItUKz.webp" alt="GeoFoto Banner" class="banner-img">
75
+ </div>
76
+ <div class="flex justify-between items-center">
77
+ <div>
78
+ <p class="text-slate-400">Fotograf铆a a KML</p>
79
+ </div>
80
+ <div class="flex items-center space-x-2">
81
+ <div id="gps-status" class="flex items-center text-sm">
82
+ <div class="w-3 h-3 rounded-full bg-rose-500 mr-2"></div>
83
+ <span>GPS: Desconectado</span>
84
+ </div>
85
+ </div>
86
+ </div>
87
+ </header>
88
+
89
+ <!-- Main Content -->
90
+ <div class="grid grid-cols-1 gap-8">
91
+ <!-- User Input Section -->
92
+ <div class="bg-slate-800 rounded-xl p-6 shadow-xl fade-in">
93
+ <h2 class="text-xl font-semibold mb-4 flex items-center">
94
+ <i class="fas fa-user-circle text-blue-400 mr-2"></i>
95
+ Registrado por
96
+ </h2>
97
+ <div class="space-y-4">
98
+ <div>
99
+ <label for="operator-name" class="block text-sm font-medium text-slate-300 mb-1">Nombre</label>
100
+ <input type="text" id="operator-name" class="w-full bg-slate-700 border border-slate-600 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 transition" placeholder="Ej: Juan P茅rez" required>
101
+ </div>
102
+
103
+ <div>
104
+ <label class="block text-sm font-medium text-slate-300 mb-1">脕rea</label>
105
+ <div class="grid grid-cols-3 gap-2">
106
+ <button id="ambiente-btn" class="area-btn px-3 py-2 bg-emerald-600 hover:bg-emerald-700 text-white rounded-lg flex items-center justify-center transition">
107
+ <i class="fas fa-leaf mr-2"></i> Medio Ambiente
108
+ </button>
109
+ <button id="sso-btn" class="area-btn px-3 py-2 bg-amber-600 hover:bg-amber-700 text-white rounded-lg flex items-center justify-center transition">
110
+ <i class="fas fa-hard-hat mr-2"></i> SSO
111
+ </button>
112
+ <button id="calidad-btn" class="area-btn px-3 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg flex items-center justify-center transition">
113
+ <i class="fas fa-award mr-2"></i> Calidad
114
+ </button>
115
+ </div>
116
+ <input type="hidden" id="selected-area" value="">
117
+ </div>
118
+ </div>
119
+ </div>
120
+
121
+ <!-- Camera Section -->
122
+ <div class="bg-slate-800 rounded-xl p-6 shadow-xl fade-in">
123
+ <h2 class="text-xl font-semibold mb-4 flex items-center">
124
+ <i class="fas fa-camera text-blue-400 mr-2"></i>
125
+ C谩mara
126
+ </h2>
127
+
128
+ <div class="relative">
129
+ <!-- Camera Preview -->
130
+ <div id="camera-container" class="relative rounded-lg overflow-hidden bg-slate-900">
131
+ <video id="preview" autoplay muted class="w-full h-auto"></video>
132
+ <canvas id="canvas" class="hidden"></canvas>
133
+
134
+ <!-- Watermark Overlay -->
135
+ <div id="watermark-overlay" class="absolute bottom-0 left-0 right-0 p-4 bg-gradient-to-t from-black/70 to-transparent">
136
+ <div id="watermark-text" class="text-white text-sm watermark">
137
+ <div class="font-bold text-lg">Q72 Ampliaci贸n Ruta S-839</div>
138
+ <div id="watermark-date" class="mt-1"></div>
139
+ <div id="watermark-coords" class="mt-1"></div>
140
+ </div>
141
+ </div>
142
+ </div>
143
+
144
+ <!-- Capture Button -->
145
+ <div class="flex justify-center mt-4">
146
+ <button id="capture-btn" class="w-16 h-16 rounded-full bg-blue-600 hover:bg-blue-700 text-white flex items-center justify-center shadow-lg transition transform hover:scale-105">
147
+ <i class="fas fa-camera text-2xl"></i>
148
+ </button>
149
+ </div>
150
+ </div>
151
+
152
+ <!-- Photo Gallery -->
153
+ <div id="photo-gallery" class="mt-6 grid grid-cols-3 gap-2 hidden">
154
+ <h3 class="col-span-3 text-lg font-medium mb-2">Fotos tomadas:</h3>
155
+ <!-- Photos will be added here dynamically -->
156
+ </div>
157
+ </div>
158
+
159
+ <!-- Map Section -->
160
+ <div class="bg-slate-800 rounded-xl p-6 shadow-xl fade-in">
161
+ <h2 class="text-xl font-semibold mb-4 flex items-center">
162
+ <i class="fas fa-map-marked-alt text-blue-400 mr-2"></i>
163
+ Ubicaci贸n
164
+ </h2>
165
+
166
+ <div id="map"></div>
167
+
168
+ <div class="mt-4 grid grid-cols-1 md:grid-cols-2 gap-4">
169
+ <div class="bg-slate-700/50 p-3 rounded-lg">
170
+ <div class="text-sm text-slate-400">Coordenadas UTM</div>
171
+ <div id="utm-coords" class="font-mono text-blue-400">Esperando GPS...</div>
172
+ </div>
173
+
174
+ <div class="bg-slate-700/50 p-3 rounded-lg">
175
+ <div class="text-sm text-slate-400">Huso</div>
176
+ <div id="utm-zone" class="font-mono text-blue-400">H18 (WGS84)</div>
177
+ </div>
178
+ </div>
179
+ </div>
180
+
181
+ <!-- Export Section -->
182
+ <div class="bg-slate-800 rounded-xl p-6 shadow-xl fade-in">
183
+ <h2 class="text-xl font-semibold mb-4 flex items-center">
184
+ <i class="fas fa-file-export text-blue-400 mr-2"></i>
185
+ Exportar
186
+ </h2>
187
+
188
+ <div class="flex flex-wrap gap-3">
189
+ <button id="export-kml" class="px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-white rounded-lg font-medium transition flex items-center disabled:opacity-50" disabled>
190
+ <i class="fas fa-map-marked-alt mr-2"></i> Exportar KML
191
+ </button>
192
+
193
+ <button id="export-zip" class="px-4 py-2 bg-amber-600 hover:bg-amber-700 text-white rounded-lg font-medium transition flex items-center disabled:opacity-50" disabled>
194
+ <i class="fas fa-file-archive mr-2"></i> Exportar ZIP
195
+ </button>
196
+
197
+ <button id="clear-all" class="px-4 py-2 bg-rose-600 hover:bg-rose-700 text-white rounded-lg font-medium transition flex items-center">
198
+ <i class="fas fa-trash-alt mr-2"></i> Limpiar todo
199
+ </button>
200
+ </div>
201
+ </div>
202
+ </div>
203
+ </div>
204
+
205
+ <!-- Photo Modal -->
206
+ <div id="photo-modal" class="fixed inset-0 bg-black bg-opacity-80 flex items-center justify-center z-50 hidden">
207
+ <div class="bg-slate-800 rounded-xl p-4 w-full max-w-2xl mx-4 shadow-2xl fade-in">
208
+ <div class="flex justify-between items-center mb-4">
209
+ <h3 class="text-xl font-semibold">Vista previa de foto</h3>
210
+ <button id="close-photo-modal" class="text-slate-400 hover:text-white">
211
+ <i class="fas fa-times"></i>
212
+ </button>
213
+ </div>
214
+
215
+ <div class="flex flex-col md:flex-row gap-4">
216
+ <div class="flex-1">
217
+ <img id="modal-photo" src="" alt="Foto" class="w-full h-auto rounded-lg">
218
+ </div>
219
+
220
+ <div class="flex-1 bg-slate-700/50 p-4 rounded-lg">
221
+ <h4 class="font-medium mb-2">Metadatos:</h4>
222
+ <div id="photo-metadata" class="space-y-2 text-sm">
223
+ <!-- Metadata will be added here -->
224
+ </div>
225
+
226
+ <button id="delete-photo" class="mt-4 px-3 py-1 bg-rose-600 hover:bg-rose-700 text-white rounded-lg text-sm font-medium transition">
227
+ <i class="fas fa-trash-alt mr-1"></i> Eliminar foto
228
+ </button>
229
+ </div>
230
+ </div>
231
+ </div>
232
+ </div>
233
+
234
+ <!-- Leaflet JS -->
235
+ <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
236
+ <!-- JSZip -->
237
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
238
+ <!-- FileSaver -->
239
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
240
+
241
+ <script>
242
+ document.addEventListener('DOMContentLoaded', function() {
243
+ // Variables
244
+ let photos = [];
245
+ let currentStream = null;
246
+ let currentPosition = null;
247
+ let map = null;
248
+ let marker = null;
249
+ let watchId = null;
250
+ let selectedArea = '';
251
+
252
+ // DOM Elements
253
+ const preview = document.getElementById('preview');
254
+ const canvas = document.getElementById('canvas');
255
+ const captureBtn = document.getElementById('capture-btn');
256
+ const photoGallery = document.getElementById('photo-gallery');
257
+ const operatorNameInput = document.getElementById('operator-name');
258
+ const selectedAreaInput = document.getElementById('selected-area');
259
+ const watermarkCoords = document.getElementById('watermark-coords');
260
+ const watermarkDate = document.getElementById('watermark-date');
261
+ const gpsStatus = document.getElementById('gps-status');
262
+ const utmCoords = document.getElementById('utm-coords');
263
+ const utmZone = document.getElementById('utm-zone');
264
+ const exportKmlBtn = document.getElementById('export-kml');
265
+ const exportZipBtn = document.getElementById('export-zip');
266
+ const clearAllBtn = document.getElementById('clear-all');
267
+ const photoModal = document.getElementById('photo-modal');
268
+ const modalPhoto = document.getElementById('modal-photo');
269
+ const photoMetadata = document.getElementById('photo-metadata');
270
+ const closePhotoModal = document.getElementById('close-photo-modal');
271
+ const deletePhotoBtn = document.getElementById('delete-photo');
272
+ const ambienteBtn = document.getElementById('ambiente-btn');
273
+ const ssoBtn = document.getElementById('sso-btn');
274
+ const calidadBtn = document.getElementById('calidad-btn');
275
+
276
+ // Constants for UTM Zone 18H
277
+ const UTM_ZONE = 18;
278
+ const UTM_ZONE_LETTER = 'H';
279
+ const FALSE_EASTING = 500000; // Standard false easting for UTM
280
+ const FALSE_NORTHING = 10000000; // For southern hemisphere (not used here)
281
+ const SCALE_FACTOR = 0.9996; // Standard scale factor for UTM
282
+ const EQUATORIAL_RADIUS = 6378137.0; // WGS84 equatorial radius in meters
283
+ const FLATTENING = 1 / 298.257223563; // WGS84 flattening
284
+
285
+ // Initialize components
286
+ initMap();
287
+ initCamera();
288
+ startGPS();
289
+ setupAreaButtons();
290
+ updateWatermark();
291
+
292
+ // Event Listeners
293
+ operatorNameInput.addEventListener('input', updateWatermark);
294
+ captureBtn.addEventListener('click', capturePhoto);
295
+ exportKmlBtn.addEventListener('click', exportKML);
296
+ exportZipBtn.addEventListener('click', exportZIP);
297
+ clearAllBtn.addEventListener('click', clearAll);
298
+ closePhotoModal.addEventListener('click', () => photoModal.classList.add('hidden'));
299
+
300
+ // Functions
301
+ function setupAreaButtons() {
302
+ ambienteBtn.addEventListener('click', () => {
303
+ selectedArea = 'Medio Ambiente';
304
+ selectedAreaInput.value = selectedArea;
305
+ resetAreaButtons();
306
+ ambienteBtn.classList.add('ring-2', 'ring-offset-2', 'ring-emerald-400');
307
+ });
308
+
309
+ ssoBtn.addEventListener('click', () => {
310
+ selectedArea = 'SSO';
311
+ selectedAreaInput.value = selectedArea;
312
+ resetAreaButtons();
313
+ ssoBtn.classList.add('ring-2', 'ring-offset-2', 'ring-amber-400');
314
+ });
315
+
316
+ calidadBtn.addEventListener('click', () => {
317
+ selectedArea = 'Calidad';
318
+ selectedAreaInput.value = selectedArea;
319
+ resetAreaButtons();
320
+ calidadBtn.classList.add('ring-2', 'ring-offset-2', 'ring-blue-400');
321
+ });
322
+ }
323
+
324
+ function resetAreaButtons() {
325
+ ambienteBtn.classList.remove('ring-2', 'ring-offset-2', 'ring-emerald-400');
326
+ ssoBtn.classList.remove('ring-2', 'ring-offset-2', 'ring-amber-400');
327
+ calidadBtn.classList.remove('ring-2', 'ring-offset-2', 'ring-blue-400');
328
+ }
329
+
330
+ function initCamera() {
331
+ if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
332
+ alert("La API de c谩mara no est谩 disponible en este navegador");
333
+ return;
334
+ }
335
+
336
+ navigator.mediaDevices.getUserMedia({
337
+ video: {
338
+ width: { ideal: 1280 },
339
+ height: { ideal: 720 },
340
+ facingMode: 'environment'
341
+ }
342
+ })
343
+ .then(stream => {
344
+ currentStream = stream;
345
+ preview.srcObject = stream;
346
+
347
+ // Set canvas size to match video
348
+ preview.addEventListener('loadedmetadata', () => {
349
+ canvas.width = preview.videoWidth;
350
+ canvas.height = preview.videoHeight;
351
+ });
352
+
353
+ return new Promise((resolve) => {
354
+ preview.onplaying = resolve;
355
+ });
356
+ })
357
+ .then(() => {
358
+ console.log("C谩mara inicializada correctamente");
359
+ })
360
+ .catch(err => {
361
+ console.error("Error al acceder a la c谩mara:", err);
362
+ alert("No se pudo acceder a la c谩mara. Aseg煤rate de dar los permisos necesarios.");
363
+ });
364
+ }
365
+
366
+ function initMap() {
367
+ try {
368
+ map = L.map('map').setView([-34.6037, -58.3816], 13); // Default to Buenos Aires
369
+
370
+ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
371
+ attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
372
+ maxZoom: 19
373
+ }).addTo(map);
374
+
375
+ marker = L.marker([0, 0], {
376
+ icon: L.divIcon({
377
+ className: 'custom-marker',
378
+ html: '<i class="fas fa-map-marker-alt text-3xl text-blue-500"></i>',
379
+ iconSize: [30, 30],
380
+ iconAnchor: [15, 30]
381
+ }),
382
+ draggable: false
383
+ }).addTo(map);
384
+
385
+ console.log("Mapa inicializado correctamente");
386
+ } catch (error) {
387
+ console.error("Error al inicializar el mapa:", error);
388
+ alert("Error al cargar el mapa. Intenta recargar la p谩gina.");
389
+ }
390
+ }
391
+
392
+ function startGPS() {
393
+ if (!navigator.geolocation) {
394
+ alert("Geolocalizaci贸n no soportada por tu navegador");
395
+ return;
396
+ }
397
+
398
+ gpsStatus.querySelector('div').classList.remove('bg-rose-500');
399
+ gpsStatus.querySelector('div').classList.add('bg-amber-500');
400
+ gpsStatus.querySelector('span').textContent = 'GPS: Conectando...';
401
+
402
+ // First try to get current position quickly
403
+ navigator.geolocation.getCurrentPosition(
404
+ position => {
405
+ handlePositionSuccess(position);
406
+ },
407
+ error => {
408
+ console.warn("Error inicial de GPS (getCurrentPosition):", error);
409
+ // Fall back to watchPosition if getCurrentPosition fails
410
+ startWatchingGPS();
411
+ },
412
+ {
413
+ enableHighAccuracy: true,
414
+ timeout: 5000,
415
+ maximumAge: 0
416
+ }
417
+ );
418
+ }
419
+
420
+ function startWatchingGPS() {
421
+ watchId = navigator.geolocation.watchPosition(
422
+ handlePositionSuccess,
423
+ handlePositionError,
424
+ {
425
+ enableHighAccuracy: true,
426
+ maximumAge: 30000,
427
+ timeout: 27000
428
+ }
429
+ );
430
+ }
431
+
432
+ function handlePositionSuccess(position) {
433
+ currentPosition = position;
434
+ updatePositionInfo(position);
435
+ updateWatermark();
436
+
437
+ gpsStatus.querySelector('div').classList.remove('bg-amber-500', 'bg-rose-500');
438
+ gpsStatus.querySelector('div').classList.add('bg-emerald-500');
439
+ gpsStatus.querySelector('span').textContent = 'GPS: Conectado';
440
+
441
+ console.log("Posici贸n GPS obtenida:", position);
442
+ }
443
+
444
+ function handlePositionError(error) {
445
+ console.error("Error de GPS:", error);
446
+ gpsStatus.querySelector('div').classList.remove('bg-amber-500', 'bg-emerald-500');
447
+ gpsStatus.querySelector('div').classList.add('bg-rose-500');
448
+
449
+ switch(error.code) {
450
+ case error.PERMISSION_DENIED:
451
+ gpsStatus.querySelector('span').textContent = 'GPS: Permiso denegado';
452
+ break;
453
+ case error.POSITION_UNAVAILABLE:
454
+ gpsStatus.querySelector('span').textContent = 'GPS: No disponible';
455
+ break;
456
+ case error.TIMEOUT:
457
+ gpsStatus.querySelector('span').textContent = 'GPS: Tiempo agotado';
458
+ break;
459
+ default:
460
+ gpsStatus.querySelector('span').textContent = 'GPS: Error desconocido';
461
+ }
462
+ }
463
+
464
+ function updatePositionInfo(position) {
465
+ const lat = position.coords.latitude;
466
+ const lng = position.coords.longitude;
467
+
468
+ // Update map
469
+ if (map) {
470
+ map.setView([lat, lng], 18);
471
+ marker.setLatLng([lat, lng]);
472
+ }
473
+
474
+ // Convert to UTM Zone 18H
475
+ const utm = convertToUTMZone18H(lat, lng);
476
+
477
+ // Update UTM display
478
+ if (utmCoords) utmCoords.textContent = `Este: ${utm.easting.toFixed(2)}m, Norte: ${utm.northing.toFixed(2)}m`;
479
+ if (utmZone) utmZone.textContent = `H${UTM_ZONE}${UTM_ZONE_LETTER} (WGS84)`;
480
+
481
+ return utm;
482
+ }
483
+
484
+ function convertToUTMZone18H(lat, lng) {
485
+ // This is a simplified UTM conversion for Zone 18H
486
+ // Based on Karney's algorithm (simplified for demo purposes)
487
+
488
+ // Check if longitude is within zone 18H (-78掳 to -72掳)
489
+ if (lng < -78 || lng >= -72) {
490
+ console.warn(`Longitud ${lng}掳 est谩 fuera del huso 18H (-78掳 a -72掳)`);
491
+ }
492
+
493
+ // Convert to radians
494
+ const latRad = lat * Math.PI / 180;
495
+ const lngRad = lng * Math.PI / 180;
496
+
497
+ // Central meridian for zone 18H (-75掳)
498
+ const lng0Rad = (-75) * Math.PI / 180;
499
+
500
+ // Calculate delta longitude
501
+ const deltaLng = lngRad - lng0Rad;
502
+
503
+ // WGS84 parameters
504
+ const a = EQUATORIAL_RADIUS;
505
+ const f = FLATTENING;
506
+ const k0 = SCALE_FACTOR;
507
+
508
+ // Intermediate calculations
509
+ const e = Math.sqrt(f * (2 - f));
510
+ const n = f / (2 - f);
511
+ const A = a / (1 + n) * (1 + n*n/4 + n*n*n*n/64);
512
+
513
+ const t = Math.sinh(Math.atanh(Math.sin(latRad)) - (2 * Math.sqrt(n)) / (1 + n) * Math.atanh((2 * Math.sqrt(n)) / (1 + n) * Math.sin(latRad)));
514
+ const xiPrim = Math.atan(t / Math.cos(deltaLng));
515
+ const etaPrim = Math.atanh(Math.sin(deltaLng) / Math.sqrt(1 + t * t));
516
+
517
+ // Calculate easting and northing
518
+ const easting = FALSE_EASTING + k0 * A * etaPrim * (1 +
519
+ (etaPrim*etaPrim)/6 * (1 - t*t + (etaPrim*etaPrim)/20 * (5 - 18*t*t + t*t*t*t + 14*etaPrim*etaPrim - 58*t*t*etaPrim*etaPrim)));
520
+
521
+ const northing = k0 * A * xiPrim * (1 +
522
+ (etaPrim*etaPrim)/2 * (1 + (etaPrim*etaPrim)/12 * (5 - t*t + 9*etaPrim*etaPrim + 4*etaPrim*etaPrim*etaPrim*etaPrim)));
523
+
524
+ return {
525
+ easting: easting,
526
+ northing: northing,
527
+ zone: UTM_ZONE,
528
+ zoneLetter: UTM_ZONE_LETTER
529
+ };
530
+ }
531
+
532
+ function updateWatermark() {
533
+ const now = new Date();
534
+ watermarkDate.textContent = now.toLocaleString();
535
+
536
+ if (currentPosition) {
537
+ const utm = updatePositionInfo(currentPosition);
538
+ watermarkCoords.textContent = `UTM H${utm.zone}${utm.zoneLetter} (WGS84): E ${utm.easting.toFixed(2)}m, N ${utm.northing.toFixed(2)}m`;
539
+ } else {
540
+ watermarkCoords.textContent = "Coordenadas no disponibles";
541
+ }
542
+ }
543
+
544
+ function capturePhoto() {
545
+ if (!currentStream) {
546
+ alert("La c谩mara no est谩 lista. Intenta recargar la p谩gina.");
547
+ return;
548
+ }
549
+
550
+ if (!currentPosition) {
551
+ alert("Esperando conexi贸n GPS... Por favor espera.");
552
+ return;
553
+ }
554
+
555
+ if (!selectedArea) {
556
+ alert("Por favor selecciona un 谩rea antes de tomar la foto");
557
+ return;
558
+ }
559
+
560
+ // Get canvas context
561
+ const context = canvas.getContext('2d');
562
+
563
+ // Draw current frame to canvas
564
+ context.drawImage(preview, 0, 0, canvas.width, canvas.height);
565
+
566
+ // Add watermark text directly to canvas
567
+ context.font = '16px Arial';
568
+ context.fillStyle = 'white';
569
+ context.textAlign = 'left';
570
+ context.textBaseline = 'bottom';
571
+
572
+ const name = operatorNameInput.value || "Operador no especificado";
573
+
574
+ const utm = updatePositionInfo(currentPosition);
575
+ const now = new Date();
576
+
577
+ // Draw watermark text with shadow effect
578
+ context.shadowColor = 'black';
579
+ context.shadowBlur = 5;
580
+
581
+ // T铆tulo fijo
582
+ context.font = 'bold 20px Arial';
583
+ context.fillText('Q72 Ampliaci贸n Ruta S-839', 20, canvas.height - 60);
584
+
585
+ // Fecha y hora
586
+ context.font = '16px Arial';
587
+ context.fillText(now.toLocaleString(), 20, canvas.height - 40);
588
+
589
+ // Coordenadas UTM
590
+ context.fillText(`UTM H${utm.zone}${utm.zoneLetter} (WGS84): E ${utm.easting.toFixed(2)}m, N ${utm.northing.toFixed(2)}m`, 20, canvas.height - 20);
591
+
592
+ context.shadowBlur = 0;
593
+
594
+ // Create photo object with metadata
595
+ const photo = {
596
+ id: Date.now(),
597
+ dataUrl: canvas.toDataURL('image/jpeg', 0.9),
598
+ timestamp: now.toISOString(),
599
+ operator: name,
600
+ area: selectedArea,
601
+ coordinates: {
602
+ lat: currentPosition.coords.latitude,
603
+ lng: currentPosition.coords.longitude,
604
+ utm: utm
605
+ }
606
+ };
607
+
608
+ photos.push(photo);
609
+ savePhotos();
610
+ renderPhotoGallery();
611
+ updateExportButtons();
612
+
613
+ // Show success animation
614
+ captureBtn.innerHTML = '<i class="fas fa-check"></i>';
615
+ captureBtn.classList.remove('bg-blue-600', 'hover:bg-blue-700');
616
+ captureBtn.classList.add('bg-emerald-600', 'hover:bg-emerald-700');
617
+
618
+ setTimeout(() => {
619
+ captureBtn.innerHTML = '<i class="fas fa-camera"></i>';
620
+ captureBtn.classList.remove('bg-emerald-600', 'hover:bg-emerald-700');
621
+ captureBtn.classList.add('bg-blue-600', 'hover:bg-blue-700');
622
+ }, 1000);
623
+ }
624
+
625
+ function renderPhotoGallery() {
626
+ if (photos.length === 0) {
627
+ photoGallery.classList.add('hidden');
628
+ return;
629
+ }
630
+
631
+ photoGallery.classList.remove('hidden');
632
+ photoGallery.innerHTML = '<h3 class="col-span-3 text-lg font-medium mb-2">Fotos tomadas:</h3>';
633
+
634
+ photos.forEach((photo, index) => {
635
+ const photoElement = document.createElement('div');
636
+ photoElement.className = 'relative group cursor-pointer';
637
+ photoElement.innerHTML = `
638
+ <img src="${photo.dataUrl}" alt="Foto ${index + 1}" class="w-full h-24 object-cover rounded-lg">
639
+ <div class="absolute inset-0 bg-black/50 flex items-center justify-center opacity-0 group-hover:opacity-100 transition">
640
+ <span class="text-white text-sm">${index + 1}</span>
641
+ </div>
642
+ `;
643
+
644
+ photoElement.addEventListener('click', () => showPhotoModal(photo));
645
+ photoGallery.appendChild(photoElement);
646
+ });
647
+ }
648
+
649
+ function showPhotoModal(photo) {
650
+ modalPhoto.src = photo.dataUrl;
651
+
652
+ // Format metadata
653
+ const date = new Date(photo.timestamp);
654
+ photoMetadata.innerHTML = `
655
+ <div><strong>Operador:</strong> ${photo.operator}</div>
656
+ <div><strong>脕rea:</strong> ${photo.area}</div>
657
+ <div><strong>Fecha:</strong> ${date.toLocaleString()}</div>
658
+ <div><strong>Coordenadas:</strong></div>
659
+ <div class="pl-4">Lat: ${photo.coordinates.lat.toFixed(6)}</div>
660
+ <div class="pl-4">Lng: ${photo.coordinates.lng.toFixed(6)}</div>
661
+ <div class="pl-4">UTM H${photo.coordinates.utm.zone}${photo.coordinates.utm.zoneLetter}: E ${photo.coordinates.utm.easting.toFixed(2)}m, N ${photo.coordinates.utm.northing.toFixed(2)}m</div>
662
+ `;
663
+
664
+ // Set up delete button
665
+ deletePhotoBtn.onclick = () => {
666
+ photos = photos.filter(p => p.id !== photo.id);
667
+ savePhotos();
668
+ renderPhotoGallery();
669
+ updateExportButtons();
670
+ photoModal.classList.add('hidden');
671
+ };
672
+
673
+ photoModal.classList.remove('hidden');
674
+ }
675
+
676
+ function exportKML() {
677
+ if (photos.length === 0) return;
678
+
679
+ // Group photos by area
680
+ const photosByArea = {};
681
+ photos.forEach(photo => {
682
+ if (!photosByArea[photo.area]) {
683
+ photosByArea[photo.area] = [];
684
+ }
685
+ photosByArea[photo.area].push(photo);
686
+ });
687
+
688
+ // Create a ZIP file with one KML per area
689
+ const zip = new JSZip();
690
+
691
+ for (const area in photosByArea) {
692
+ // Create KML content for this area
693
+ let kml = `<?xml version="1.0" encoding="UTF-8"?>
694
+ <kml xmlns="http://www.opengis.net/kml/2.2">
695
+ <Document>
696
+ <name>Fotos ${area}</name>
697
+ <description>Fotos tomadas para el 谩rea de ${area}</description>`;
698
+
699
+ photosByArea[area].forEach(photo => {
700
+ // Convert image to base64 without data URL prefix
701
+ const base64Image = photo.dataUrl.split(',')[1];
702
+
703
+ kml += `
704
+ <Placemark>
705
+ <name>Foto ${new Date(photo.timestamp).toLocaleString()}</name>
706
+ <description>
707
+ <![CDATA[
708
+ <h3>Foto georreferenciada</h3>
709
+ <p><strong>Operador:</strong> ${photo.operator}</p>
710
+ <p><strong>脕rea:</strong> ${photo.area}</p>
711
+ <p><strong>Fecha:</strong> ${new Date(photo.timestamp).toLocaleString()}</p>
712
+ <p><strong>Coordenadas UTM:</strong> H${photo.coordinates.utm.zone}${photo.coordinates.utm.zoneLetter} E ${photo.coordinates.utm.easting.toFixed(2)}m, N ${photo.coordinates.utm.northing.toFixed(2)}m</p>
713
+ <img src="data:image/jpeg;base64,${base64Image}" width="400" />
714
+ ]]>
715
+ </description>
716
+ <Point>
717
+ <coordinates>${photo.coordinates.lng},${photo.coordinates.lat},0</coordinates>
718
+ </Point>
719
+ </Placemark>`;
720
+ });
721
+
722
+ kml += `
723
+ </Document>
724
+ </kml>`;
725
+
726
+ // Add to ZIP
727
+ const filename = `fotos_${area.replace(/\s+/g, '_').toLowerCase()}.kml`;
728
+ zip.file(filename, kml);
729
+ }
730
+
731
+ // Generate ZIP and download
732
+ zip.generateAsync({ type: "blob" }).then(content => {
733
+ saveAs(content, "fotos_georreferenciadas_por_area.zip");
734
+ });
735
+ }
736
+
737
+ function exportZIP() {
738
+ if (photos.length === 0) return;
739
+
740
+ const zip = new JSZip();
741
+ const imgFolder = zip.folder("fotos");
742
+ const kmlFolder = zip.folder("kml");
743
+
744
+ // Group photos by area
745
+ const photosByArea = {};
746
+ photos.forEach(photo => {
747
+ if (!photosByArea[photo.area]) {
748
+ photosByArea[photo.area] = [];
749
+ }
750
+ photosByArea[photo.area].push(photo);
751
+ });
752
+
753
+ // Add photos and create KML for each area
754
+ for (const area in photosByArea) {
755
+ const areaImgFolder = imgFolder.folder(area);
756
+ let photoIndex = 1;
757
+
758
+ // Create KML content for this area
759
+ let kml = `<?xml version="1.0" encoding="UTF-8"?>
760
+ <kml xmlns="http://www.opengis.net/kml/2.2">
761
+ <Document>
762
+ <name>Fotos ${area}</name>
763
+ <description>Fotos tomadas para el 谩rea de ${area}</description>`;
764
+
765
+ photosByArea[area].forEach(photo => {
766
+ // Extract base64 data
767
+ const base64Data = photo.dataUrl.split(',')[1];
768
+ areaImgFolder.file(`foto_${photoIndex}.jpg`, base64Data, { base64: true });
769
+
770
+ kml += `
771
+ <Placemark>
772
+ <name>Foto ${new Date(photo.timestamp).toLocaleString()}</name>
773
+ <description>
774
+ <![CDATA[
775
+ <h3>Foto georreferenciada</h3>
776
+ <p><strong>Operador:</strong> ${photo.operator}</p>
777
+ <p><strong>脕rea:</strong> ${photo.area}</p>
778
+ <p><strong>Fecha:</strong> ${new Date(photo.timestamp).toLocaleString()}</p>
779
+ <p><strong>Coordenadas UTM:</strong> H${photo.coordinates.utm.zone}${photo.coordinates.utm.zoneLetter} E ${photo.coordinates.utm.easting.toFixed(2)}m, N ${photo.coordinates.utm.northing.toFixed(2)}m</p>
780
+ <img src="../fotos/${area}/foto_${photoIndex}.jpg" width="400" />
781
+ ]]>
782
+ </description>
783
+ <Point>
784
+ <coordinates>${photo.coordinates.lng},${photo.coordinates.lat},0</coordinates>
785
+ </Point>
786
+ </Placemark>`;
787
+
788
+ photoIndex++;
789
+ });
790
+
791
+ kml += `
792
+ </Document>
793
+ </kml>`;
794
+
795
+ // Add KML for this area
796
+ const filename = `fotos_${area.replace(/\s+/g, '_').toLowerCase()}.kml`;
797
+ kmlFolder.file(filename, kml);
798
+ }
799
+
800
+ // Generate ZIP and download
801
+ zip.generateAsync({ type: "blob" }).then(content => {
802
+ saveAs(content, "fotos_georreferenciadas_completas.zip");
803
+ });
804
+ }
805
+
806
+ function clearAll() {
807
+ if (confirm("驴Est谩s seguro de que deseas eliminar todas las fotos?")) {
808
+ photos = [];
809
+ savePhotos();
810
+ renderPhotoGallery();
811
+ updateExportButtons();
812
+ }
813
+ }
814
+
815
+ function savePhotos() {
816
+ localStorage.setItem('geoPhotos', JSON.stringify(photos));
817
+ }
818
+
819
+ function loadPhotos() {
820
+ const savedPhotos = localStorage.getItem('geoPhotos');
821
+ if (savedPhotos) {
822
+ photos = JSON.parse(savedPhotos);
823
+ renderPhotoGallery();
824
+ updateExportButtons();
825
+ }
826
+ }
827
+
828
+ function updateExportButtons() {
829
+ exportKmlBtn.disabled = photos.length === 0;
830
+ exportZipBtn.disabled = photos.length === 0;
831
+ }
832
+
833
+ // Initialize from storage
834
+ loadPhotos();
835
+
836
+ // Update watermark periodically
837
+ setInterval(updateWatermark, 1000);
838
+ });
839
+ </script>
840
+ <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=ignaciomdr/geocam2" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
841
+ </html>
prompts.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ rescata el ultimo codigo que no se guardo
2
+ modifica el titulo por el siguiente banner, ajustado para que se vea la imagen completa https://cdn-uploads.huggingface.co/production/uploads/noauth/7vTxYaZUq3T2toaTItUKz.webp
3
+ reemplaza el texto fotografia georeferenciada para contratos por "Fotograf铆a a KML", cambia "Informaci贸n del operador" por "Registrado por", Cambia "Nombre del operador" por "Nombre", Cambia "n霉mero de contrato" por 3 botones de selecci贸n de 谩reas que ser铆an: "Medio Ambiente", "SSO" y Calidad, a cada uno ponle un icono acorde, Estampa en la foto el titulo "Q72 Ampliaci贸n Ruta S-839", abajo de ese titulo estampa fecha y hora, y abajo coordenadas utm en datum wgs84 huso 18 sur, todo esto en esquina inferior, y en la exportaci贸n genera un archivo distinto para cada 谩rea registrada, finalmente cambia el fondo por el siguiente: https://huggingface.co/datasets/ignaciomdr/fondos/resolve/main/HD%20wallpaper_%20vertical%2C%20landscape%2C%20science%2C%20nature%2C%20water%2C%20no%20people%2C%20red.jpeg , no modifiques nada mas
4
+ revisa el codigo por que no carga el gps ni la camara ni el mapa de referencia