vue-geojson-view-ts 1.3.18 → 1.3.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +321 -321
- package/dist/components/MapSearchAddress.vue.d.ts +1 -1
- package/dist/components/MapView.vue.d.ts +58 -20
- package/dist/data/bolivia.geojson +2084 -2084
- package/dist/data/cities/be.geojson +246 -246
- package/dist/data/cities/cbba.geojson +165 -165
- package/dist/data/cities/ch.geojson +170 -170
- package/dist/data/cities/lp.geojson +314 -314
- package/dist/data/cities/or.geojson +253 -253
- package/dist/data/cities/pd.geojson +164 -164
- package/dist/data/cities/pt.geojson +379 -379
- package/dist/data/cities/sc.geojson +224 -224
- package/dist/data/cities/tj.geojson +86 -86
- package/dist/pin.svg +4 -4
- package/dist/style.css +1 -1
- package/dist/vite-env.d.ts +1 -1
- package/dist/vue-geojson-view-ts.js +10437 -10301
- package/dist/vue-geojson-view-ts.umd.cjs +20 -17
- package/package.json +78 -78
- package/src/components/CoordinatesVerifyPolygon.vue +86 -86
- package/src/components/MapHeatComponent.vue +664 -664
- package/src/components/MapSearchAddress.vue +309 -309
- package/src/components/MapView.vue +1044 -718
|
@@ -1,718 +1,1044 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="map-container" :style="`height:${configurationMap?.height}`">
|
|
3
|
-
<div :id="idMap" style="height: 100%"></div>
|
|
4
|
-
</div>
|
|
5
|
-
</template>
|
|
6
|
-
|
|
7
|
-
<script lang="ts">
|
|
8
|
-
import { defineComponent } from "vue";
|
|
9
|
-
import { markerDefault } from "../helpers/imgBase64";
|
|
10
|
-
import homeIconUrl from "../assets/home.png";
|
|
11
|
-
import endIconUrl from "../assets/end.png";
|
|
12
|
-
import trackingIconUrl from "../assets/tracking.png";
|
|
13
|
-
import
|
|
14
|
-
import "
|
|
15
|
-
import "
|
|
16
|
-
import "
|
|
17
|
-
import
|
|
18
|
-
import
|
|
19
|
-
import "leaflet
|
|
20
|
-
import "leaflet.
|
|
21
|
-
import
|
|
22
|
-
import
|
|
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
|
-
this.
|
|
61
|
-
},
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
export default defineComponent({
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
renderGeojson: this.dataPolygon as any,
|
|
83
|
-
markerIcon: {
|
|
84
|
-
iconUrl: markerDefault,
|
|
85
|
-
iconSize: [25, 41],
|
|
86
|
-
iconAnchor: [12, 41],
|
|
87
|
-
},
|
|
88
|
-
layersFeatureGroup: null as any,
|
|
89
|
-
featuresData: null as any,
|
|
90
|
-
currentTileLayer: null as L.TileLayer | null,
|
|
91
|
-
isSatelliteView: false,
|
|
92
|
-
};
|
|
93
|
-
},
|
|
94
|
-
mounted() {
|
|
95
|
-
this.makeid(10);
|
|
96
|
-
this.renderMap();
|
|
97
|
-
},
|
|
98
|
-
methods: {
|
|
99
|
-
createSatelliteControl(): L.Control {
|
|
100
|
-
const SatelliteControl = L.Control.extend({
|
|
101
|
-
onAdd: (map: L.Map) => {
|
|
102
|
-
const container = L.DomUtil.create(
|
|
103
|
-
"div",
|
|
104
|
-
"leaflet-control-draw leaflet-bar leaflet-control"
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
const satelliteButton = L.DomUtil.create(
|
|
108
|
-
"a",
|
|
109
|
-
"leaflet-draw-draw-satellite",
|
|
110
|
-
container
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
satelliteButton.href = "#";
|
|
114
|
-
satelliteButton.title = this.isSatelliteView
|
|
115
|
-
? "Vista Normal"
|
|
116
|
-
: "Vista Satélite";
|
|
117
|
-
|
|
118
|
-
// Usar el mismo estilo que los otros botones de draw
|
|
119
|
-
satelliteButton.style.backgroundImage = this.isSatelliteView
|
|
120
|
-
? `url(${gpsIcon})`
|
|
121
|
-
: `url(${sateliteIcon})`;
|
|
122
|
-
satelliteButton.style.backgroundPosition = "center";
|
|
123
|
-
satelliteButton.style.backgroundRepeat = "no-repeat";
|
|
124
|
-
satelliteButton.style.backgroundSize = "20px 20px";
|
|
125
|
-
|
|
126
|
-
L.DomEvent.disableClickPropagation(satelliteButton);
|
|
127
|
-
L.DomEvent.on(
|
|
128
|
-
satelliteButton,
|
|
129
|
-
"click",
|
|
130
|
-
this.toggleSatelliteView,
|
|
131
|
-
this
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
return container;
|
|
135
|
-
},
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
return new SatelliteControl({ position: "topleft" });
|
|
139
|
-
},
|
|
140
|
-
toggleSatelliteView(): void {
|
|
141
|
-
if (!this.mapRender || !this.currentTileLayer) return;
|
|
142
|
-
|
|
143
|
-
this.isSatelliteView = !this.isSatelliteView;
|
|
144
|
-
|
|
145
|
-
// Remover la capa actual
|
|
146
|
-
this.mapRender.removeLayer(this.currentTileLayer as unknown as L.Layer);
|
|
147
|
-
|
|
148
|
-
// Agregar la nueva capa
|
|
149
|
-
if (this.isSatelliteView) {
|
|
150
|
-
this.currentTileLayer = L.tileLayer(
|
|
151
|
-
"https://api.maptiler.com/maps/satellite/{z}/{x}/{y}.jpg?key=t8mWT2ozs1JWBqMZOnZr",
|
|
152
|
-
{
|
|
153
|
-
attribution:
|
|
154
|
-
'© <a href="https://www.maptiler.com/">MapTiler</a> © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
|
155
|
-
}
|
|
156
|
-
);
|
|
157
|
-
} else {
|
|
158
|
-
this.currentTileLayer = L.tileLayer(
|
|
159
|
-
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
|
160
|
-
{
|
|
161
|
-
attribution:
|
|
162
|
-
'© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
|
163
|
-
}
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
this.currentTileLayer.addTo(this.mapRender as L.Map);
|
|
168
|
-
|
|
169
|
-
// Actualizar el ícono del control satelital
|
|
170
|
-
const satelliteButtons = document.querySelectorAll(
|
|
171
|
-
".leaflet-draw-draw-satellite"
|
|
172
|
-
);
|
|
173
|
-
satelliteButtons.forEach((button) => {
|
|
174
|
-
const htmlButton = button as HTMLElement;
|
|
175
|
-
htmlButton.style.backgroundImage = this.isSatelliteView
|
|
176
|
-
? `url(${gpsIcon})`
|
|
177
|
-
: `url(${sateliteIcon})`;
|
|
178
|
-
htmlButton.title = this.isSatelliteView
|
|
179
|
-
? "Vista Normal"
|
|
180
|
-
: "Vista Satélite";
|
|
181
|
-
});
|
|
182
|
-
},
|
|
183
|
-
getLayersFeaturesInGeoJson() {
|
|
184
|
-
const geojson = L.geoJSON().addTo(this.mapRender as any);
|
|
185
|
-
this.featuresData.eachLayer((layer: any) => {
|
|
186
|
-
geojson.addLayer(layer);
|
|
187
|
-
});
|
|
188
|
-
const geojsonString = [geojson.toGeoJSON()]; //JSON.stringify([geojson.toGeoJSON()]);
|
|
189
|
-
return geojsonString;
|
|
190
|
-
},
|
|
191
|
-
triggerSaveEdit(): void {
|
|
192
|
-
const buttons =
|
|
193
|
-
document.getElementsByClassName(
|
|
194
|
-
"leaflet-draw-actions leaflet-draw-actions-top"
|
|
195
|
-
) ||
|
|
196
|
-
document.getElementsByClassName(
|
|
197
|
-
"leaflet-draw-actions leaflet-draw-actions-top leaflet-draw-actions-bottom"
|
|
198
|
-
);
|
|
199
|
-
buttons[0]?.querySelector("li")?.querySelector("a")?.click();
|
|
200
|
-
},
|
|
201
|
-
makeid(length: number): void {
|
|
202
|
-
let result = "";
|
|
203
|
-
const characters = "abcdefghijklmnopqrstuvwxyz";
|
|
204
|
-
const charactersLength = characters.length;
|
|
205
|
-
let counter = 0;
|
|
206
|
-
while (counter < length) {
|
|
207
|
-
result += characters.charAt(
|
|
208
|
-
Math.floor(Math.random() * charactersLength)
|
|
209
|
-
);
|
|
210
|
-
counter += 1;
|
|
211
|
-
}
|
|
212
|
-
this.idMap = result;
|
|
213
|
-
},
|
|
214
|
-
renderMap(): void {
|
|
215
|
-
drawLocales("es");
|
|
216
|
-
setTimeout(() => {
|
|
217
|
-
if (this.loadPolygon) {
|
|
218
|
-
this.viewMap();
|
|
219
|
-
} else {
|
|
220
|
-
this.createMap();
|
|
221
|
-
}
|
|
222
|
-
}, 500);
|
|
223
|
-
},
|
|
224
|
-
createMap(): void {
|
|
225
|
-
const iconDefaultMarket = this.configurationMap
|
|
226
|
-
? this.configurationMap.iconMarker
|
|
227
|
-
? this.configurationMap.iconMarker
|
|
228
|
-
: this.markerIcon
|
|
229
|
-
: this.markerIcon;
|
|
230
|
-
window.type = true;
|
|
231
|
-
const map = L.map(this.idMap, { fullscreenControl: true } as any).setView(
|
|
232
|
-
[
|
|
233
|
-
this.renderCoordinates ? (this.renderCoordinates[0] as number) : 0,
|
|
234
|
-
this.renderCoordinates ? (this.renderCoordinates[1] as number) : 0,
|
|
235
|
-
],
|
|
236
|
-
this.renderCoordinates ? (this.renderCoordinates[2] as number) : 0
|
|
237
|
-
);
|
|
238
|
-
this.mapRender = map;
|
|
239
|
-
|
|
240
|
-
// Inicializar con la vista correcta según el prop
|
|
241
|
-
this.isSatelliteView = this.isSatelite || false;
|
|
242
|
-
if (this.isSatelliteView) {
|
|
243
|
-
this.currentTileLayer = L.tileLayer(
|
|
244
|
-
"https://api.maptiler.com/maps/satellite/{z}/{x}/{y}.jpg?key=t8mWT2ozs1JWBqMZOnZr",
|
|
245
|
-
{
|
|
246
|
-
attribution:
|
|
247
|
-
'© <a href="https://www.maptiler.com/">MapTiler</a> © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
|
248
|
-
}
|
|
249
|
-
);
|
|
250
|
-
} else {
|
|
251
|
-
this.currentTileLayer = L.tileLayer(
|
|
252
|
-
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
|
253
|
-
{
|
|
254
|
-
attribution:
|
|
255
|
-
'© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
|
256
|
-
}
|
|
257
|
-
);
|
|
258
|
-
}
|
|
259
|
-
this.currentTileLayer.addTo(map);
|
|
260
|
-
|
|
261
|
-
// Agregar el control satelital si está habilitado
|
|
262
|
-
if (this.isSatelite !== undefined) {
|
|
263
|
-
const satelliteControl = this.createSatelliteControl();
|
|
264
|
-
map.addControl(satelliteControl);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
map.setZoom(this.configurationMap?.maxZoom);
|
|
268
|
-
if (this.configurationMap
|
|
269
|
-
const marker = L.marker(
|
|
270
|
-
[
|
|
271
|
-
this.renderCoordinates ? (this.renderCoordinates[0] as number) : 0,
|
|
272
|
-
this.renderCoordinates ? (this.renderCoordinates[1] as number) : 0,
|
|
273
|
-
],
|
|
274
|
-
{
|
|
275
|
-
icon: L.icon(iconDefaultMarket),
|
|
276
|
-
draggable: this.configurationMap
|
|
277
|
-
}
|
|
278
|
-
).addTo(map);
|
|
279
|
-
this.markerRender = marker;
|
|
280
|
-
if (this.getCoodMarker) {
|
|
281
|
-
marker.on("dragend", (event) => {
|
|
282
|
-
const updatedCoordinates = event.target.getLatLng();
|
|
283
|
-
const lat = updatedCoordinates.lat;
|
|
284
|
-
const lng = updatedCoordinates.lng;
|
|
285
|
-
if (this.getCoodMarker) this.getCoodMarker(lat, lng);
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
const featuresData = L.featureGroup();
|
|
290
|
-
this.featuresData = featuresData;
|
|
291
|
-
const featureGroup = featuresData.addTo(map);
|
|
292
|
-
let contextEdit = {
|
|
293
|
-
featureGroup: featureGroup, // Crea un nuevo grupo de capas para los polígonos
|
|
294
|
-
edit: false,
|
|
295
|
-
remove: this.configurationMap?.editFigures.remove,
|
|
296
|
-
};
|
|
297
|
-
if (this.configurationMap?.editFigures.edit) {
|
|
298
|
-
contextEdit = {
|
|
299
|
-
featureGroup: featureGroup, // Crea un nuevo grupo de capas para los polígonos
|
|
300
|
-
...this.configurationMap?.editFigures.edit,
|
|
301
|
-
remove: this.configurationMap?.editFigures.remove,
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
const drawControl = new L.Control.Draw({
|
|
305
|
-
position: "topleft",
|
|
306
|
-
draw: {
|
|
307
|
-
polygon: this.configurationMap?.createFigures.polygon,
|
|
308
|
-
circle: false, //this.configurationMap?.createFigures.circle,
|
|
309
|
-
rectangle: this.configurationMap?.createFigures.rectangle
|
|
310
|
-
? {
|
|
311
|
-
shapeOptions: {
|
|
312
|
-
color: "blue",
|
|
313
|
-
},
|
|
314
|
-
}
|
|
315
|
-
: false,
|
|
316
|
-
marker: this.configurationMap?.createFigures.marker
|
|
317
|
-
? {
|
|
318
|
-
icon: L.icon(iconDefaultMarket),
|
|
319
|
-
}
|
|
320
|
-
: false,
|
|
321
|
-
polyline: this.configurationMap?.createFigures.polyline
|
|
322
|
-
? {
|
|
323
|
-
shapeOptions: {
|
|
324
|
-
color: "blue",
|
|
325
|
-
},
|
|
326
|
-
}
|
|
327
|
-
: false,
|
|
328
|
-
circlemarker: this.configurationMap?.createFigures.multipoint,
|
|
329
|
-
},
|
|
330
|
-
edit: contextEdit as any,
|
|
331
|
-
});
|
|
332
|
-
map.addControl(drawControl);
|
|
333
|
-
|
|
334
|
-
map.on("draw:created", (event: any) => {
|
|
335
|
-
const layer = event.layer;
|
|
336
|
-
featureGroup.addLayer(layer);
|
|
337
|
-
let geojson = layer.toGeoJSON();
|
|
338
|
-
// *** AGREGADO: Manejo especial para CircleMarker (convertir a MultiPoint) ***
|
|
339
|
-
if (event.layerType === "circlemarker") {
|
|
340
|
-
// Convertir CircleMarker a formato MultiPoint para consistencia
|
|
341
|
-
geojson = {
|
|
342
|
-
type: "Feature",
|
|
343
|
-
geometry: {
|
|
344
|
-
type: "MultiPoint",
|
|
345
|
-
coordinates: [geojson.geometry.coordinates],
|
|
346
|
-
},
|
|
347
|
-
properties: {
|
|
348
|
-
...geojson.properties,
|
|
349
|
-
pointType: "multipoint",
|
|
350
|
-
style: {
|
|
351
|
-
color:
|
|
352
|
-
this.configurationMap?.createFigures?.multipoint?.color ||
|
|
353
|
-
"green",
|
|
354
|
-
fillColor:
|
|
355
|
-
this.configurationMap?.createFigures?.multipoint?.fillColor ||
|
|
356
|
-
"green",
|
|
357
|
-
fillOpacity:
|
|
358
|
-
this.configurationMap?.createFigures?.multipoint
|
|
359
|
-
?.fillOpacity || 0.8,
|
|
360
|
-
radius:
|
|
361
|
-
this.configurationMap?.createFigures?.multipoint?.radius || 6,
|
|
362
|
-
weight:
|
|
363
|
-
this.configurationMap?.createFigures?.multipoint?.weight || 2,
|
|
364
|
-
},
|
|
365
|
-
},
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
if (this.getGeoJSON) this.getGeoJSON([geojson]);
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
map.on("draw:edited", (event: any) => {
|
|
372
|
-
const layers = event.layers;
|
|
373
|
-
layers.eachLayer((layer: any) => {
|
|
374
|
-
const geojson = layer.toGeoJSON();
|
|
375
|
-
if (this.getGeoJSON) this.getGeoJSON([geojson]);
|
|
376
|
-
});
|
|
377
|
-
});
|
|
378
|
-
},
|
|
379
|
-
viewMap(): void {
|
|
380
|
-
const iconDefaultMarket = this.configurationMap
|
|
381
|
-
? this.configurationMap.iconMarker
|
|
382
|
-
? this.configurationMap.iconMarker
|
|
383
|
-
: this.markerIcon
|
|
384
|
-
: this.markerIcon;
|
|
385
|
-
window.type = true;
|
|
386
|
-
const map = L.map(this.idMap, { fullscreenControl: true } as any).setView(
|
|
387
|
-
[
|
|
388
|
-
this.renderCoordinates ? (this.renderCoordinates[0] as number) : 0,
|
|
389
|
-
this.renderCoordinates ? (this.renderCoordinates[1] as number) : 0,
|
|
390
|
-
],
|
|
391
|
-
this.renderCoordinates ? (this.renderCoordinates[2] as number) : 0
|
|
392
|
-
);
|
|
393
|
-
this.mapRender = map;
|
|
394
|
-
|
|
395
|
-
// Inicializar con la vista correcta según el prop
|
|
396
|
-
this.isSatelliteView = this.isSatelite || false;
|
|
397
|
-
if (this.isSatelliteView) {
|
|
398
|
-
this.currentTileLayer = L.tileLayer(
|
|
399
|
-
"https://api.maptiler.com/maps/satellite/{z}/{x}/{y}.jpg?key=t8mWT2ozs1JWBqMZOnZr",
|
|
400
|
-
{
|
|
401
|
-
attribution:
|
|
402
|
-
'© <a href="https://www.maptiler.com/">MapTiler</a> © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
|
403
|
-
}
|
|
404
|
-
);
|
|
405
|
-
} else {
|
|
406
|
-
this.currentTileLayer = L.tileLayer(
|
|
407
|
-
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
|
408
|
-
{
|
|
409
|
-
attribution:
|
|
410
|
-
'© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
|
411
|
-
}
|
|
412
|
-
);
|
|
413
|
-
}
|
|
414
|
-
this.currentTileLayer.addTo(map);
|
|
415
|
-
|
|
416
|
-
// Agregar el control satelital si está habilitado
|
|
417
|
-
if (this.isSatelite !== undefined) {
|
|
418
|
-
const satelliteControl = this.createSatelliteControl();
|
|
419
|
-
map.addControl(satelliteControl);
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
map.setZoom(this.configurationMap?.maxZoom);
|
|
423
|
-
const featuresData = L.featureGroup();
|
|
424
|
-
this.featuresData = featuresData;
|
|
425
|
-
const featureGroup = featuresData.addTo(map);
|
|
426
|
-
let contextEdit = {
|
|
427
|
-
featureGroup: featureGroup, // Crea un nuevo grupo de capas para los polígonos
|
|
428
|
-
edit: false,
|
|
429
|
-
remove: this.configurationMap?.editFigures.remove,
|
|
430
|
-
};
|
|
431
|
-
if (this.configurationMap?.editFigures.edit) {
|
|
432
|
-
contextEdit = {
|
|
433
|
-
featureGroup: featureGroup, // Crea un nuevo grupo de capas para los polígonos
|
|
434
|
-
...this.configurationMap?.editFigures.edit,
|
|
435
|
-
remove: this.configurationMap?.editFigures.remove,
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
const
|
|
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
|
-
feature.
|
|
497
|
-
feature.
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
type
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
1
|
+
<template>
|
|
2
|
+
<div class="map-container" :style="`height:${configurationMap?.height}`">
|
|
3
|
+
<div :id="idMap" style="height: 100%"></div>
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
import { defineComponent } from "vue";
|
|
9
|
+
import { markerDefault } from "../helpers/imgBase64";
|
|
10
|
+
import homeIconUrl from "../assets/home.png";
|
|
11
|
+
import endIconUrl from "../assets/end.png";
|
|
12
|
+
import trackingIconUrl from "../assets/tracking.png";
|
|
13
|
+
import origenIconUrl from "../assets/origen.png";
|
|
14
|
+
import destinoIconUrl from "../assets/destino.png";
|
|
15
|
+
import llegadaIconUrl from "../assets/llegada.png";
|
|
16
|
+
import DestinoIconReferencia from "../assets/address.svg";
|
|
17
|
+
import LLegadaSalidaIconReferencia from "../assets/entidad.svg";
|
|
18
|
+
import * as L from "leaflet";
|
|
19
|
+
import "leaflet-draw";
|
|
20
|
+
import "leaflet/dist/leaflet.css";
|
|
21
|
+
import "leaflet-draw/dist/leaflet.draw.css";
|
|
22
|
+
import drawLocales from "leaflet-draw-locales";
|
|
23
|
+
import axios from "axios";
|
|
24
|
+
import "leaflet.fullscreen/Control.FullScreen.css";
|
|
25
|
+
import "leaflet.fullscreen";
|
|
26
|
+
import gpsIcon from "../assets/gps.svg";
|
|
27
|
+
import sateliteIcon from "../assets/satelite.svg";
|
|
28
|
+
|
|
29
|
+
declare global {
|
|
30
|
+
interface Window {
|
|
31
|
+
type: boolean;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
L.Edit.Circle = L.Edit.CircleMarker.extend({
|
|
36
|
+
_createResizeMarker: function () {
|
|
37
|
+
var center = this._shape.getLatLng(),
|
|
38
|
+
resizemarkerPoint = this._getResizeMarkerPoint(center);
|
|
39
|
+
|
|
40
|
+
this._resizeMarkers = [];
|
|
41
|
+
this._resizeMarkers.push(
|
|
42
|
+
this._createMarker(resizemarkerPoint, this.options.resizeIcon)
|
|
43
|
+
);
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
_getResizeMarkerPoint: function (latlng: any) {
|
|
47
|
+
var delta = this._shape._radius * Math.cos(Math.PI / 4),
|
|
48
|
+
point = this._map.project(latlng);
|
|
49
|
+
return this._map.unproject([point.x + delta, point.y - delta]);
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
_resize: function (latlng: any) {
|
|
53
|
+
var moveLatLng = this._moveMarker.getLatLng();
|
|
54
|
+
var radius;
|
|
55
|
+
if (L.GeometryUtil.isVersion07x()) {
|
|
56
|
+
radius = moveLatLng.distanceTo(latlng);
|
|
57
|
+
} else {
|
|
58
|
+
radius = moveLatLng.distanceTo(latlng);
|
|
59
|
+
}
|
|
60
|
+
this._shape.setRadius(radius);
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
export default defineComponent({
|
|
65
|
+
props: {
|
|
66
|
+
configurationMap: { type: Object, required: false },
|
|
67
|
+
coordinatesMap: { type: [Array, Function], required: false },
|
|
68
|
+
dataPolygon: { type: [Array, Object], required: false },
|
|
69
|
+
isSatelite: { type: Boolean, required: false },
|
|
70
|
+
getCoodMarker: { type: Function, required: false },
|
|
71
|
+
getGeoJSON: { type: Function, required: false },
|
|
72
|
+
loadPolygon: { type: Boolean, required: false },
|
|
73
|
+
},
|
|
74
|
+
data() {
|
|
75
|
+
return {
|
|
76
|
+
idMap: "",
|
|
77
|
+
mapRender: null as L.Map | null,
|
|
78
|
+
markerRender: null as L.Marker | null,
|
|
79
|
+
renderCoordinates: Array.isArray(this.coordinatesMap)
|
|
80
|
+
? (this.coordinatesMap as number[])
|
|
81
|
+
: [],
|
|
82
|
+
renderGeojson: this.dataPolygon as any,
|
|
83
|
+
markerIcon: {
|
|
84
|
+
iconUrl: markerDefault,
|
|
85
|
+
iconSize: [25, 41],
|
|
86
|
+
iconAnchor: [12, 41],
|
|
87
|
+
},
|
|
88
|
+
layersFeatureGroup: null as any,
|
|
89
|
+
featuresData: null as any,
|
|
90
|
+
currentTileLayer: null as L.TileLayer | null,
|
|
91
|
+
isSatelliteView: false,
|
|
92
|
+
};
|
|
93
|
+
},
|
|
94
|
+
mounted() {
|
|
95
|
+
this.makeid(10);
|
|
96
|
+
this.renderMap();
|
|
97
|
+
},
|
|
98
|
+
methods: {
|
|
99
|
+
createSatelliteControl(): L.Control {
|
|
100
|
+
const SatelliteControl = L.Control.extend({
|
|
101
|
+
onAdd: (map: L.Map) => {
|
|
102
|
+
const container = L.DomUtil.create(
|
|
103
|
+
"div",
|
|
104
|
+
"leaflet-control-draw leaflet-bar leaflet-control"
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const satelliteButton = L.DomUtil.create(
|
|
108
|
+
"a",
|
|
109
|
+
"leaflet-draw-draw-satellite",
|
|
110
|
+
container
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
satelliteButton.href = "#";
|
|
114
|
+
satelliteButton.title = this.isSatelliteView
|
|
115
|
+
? "Vista Normal"
|
|
116
|
+
: "Vista Satélite";
|
|
117
|
+
|
|
118
|
+
// Usar el mismo estilo que los otros botones de draw
|
|
119
|
+
satelliteButton.style.backgroundImage = this.isSatelliteView
|
|
120
|
+
? `url(${gpsIcon})`
|
|
121
|
+
: `url(${sateliteIcon})`;
|
|
122
|
+
satelliteButton.style.backgroundPosition = "center";
|
|
123
|
+
satelliteButton.style.backgroundRepeat = "no-repeat";
|
|
124
|
+
satelliteButton.style.backgroundSize = "20px 20px";
|
|
125
|
+
|
|
126
|
+
L.DomEvent.disableClickPropagation(satelliteButton);
|
|
127
|
+
L.DomEvent.on(
|
|
128
|
+
satelliteButton,
|
|
129
|
+
"click",
|
|
130
|
+
this.toggleSatelliteView,
|
|
131
|
+
this
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
return container;
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
return new SatelliteControl({ position: "topleft" });
|
|
139
|
+
},
|
|
140
|
+
toggleSatelliteView(): void {
|
|
141
|
+
if (!this.mapRender || !this.currentTileLayer) return;
|
|
142
|
+
|
|
143
|
+
this.isSatelliteView = !this.isSatelliteView;
|
|
144
|
+
|
|
145
|
+
// Remover la capa actual
|
|
146
|
+
this.mapRender.removeLayer(this.currentTileLayer as unknown as L.Layer);
|
|
147
|
+
|
|
148
|
+
// Agregar la nueva capa
|
|
149
|
+
if (this.isSatelliteView) {
|
|
150
|
+
this.currentTileLayer = L.tileLayer(
|
|
151
|
+
"https://api.maptiler.com/maps/satellite/{z}/{x}/{y}.jpg?key=t8mWT2ozs1JWBqMZOnZr",
|
|
152
|
+
{
|
|
153
|
+
attribution:
|
|
154
|
+
'© <a href="https://www.maptiler.com/">MapTiler</a> © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
} else {
|
|
158
|
+
this.currentTileLayer = L.tileLayer(
|
|
159
|
+
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
|
160
|
+
{
|
|
161
|
+
attribution:
|
|
162
|
+
'© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
|
163
|
+
}
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
this.currentTileLayer.addTo(this.mapRender as L.Map);
|
|
168
|
+
|
|
169
|
+
// Actualizar el ícono del control satelital
|
|
170
|
+
const satelliteButtons = document.querySelectorAll(
|
|
171
|
+
".leaflet-draw-draw-satellite"
|
|
172
|
+
);
|
|
173
|
+
satelliteButtons.forEach((button) => {
|
|
174
|
+
const htmlButton = button as HTMLElement;
|
|
175
|
+
htmlButton.style.backgroundImage = this.isSatelliteView
|
|
176
|
+
? `url(${gpsIcon})`
|
|
177
|
+
: `url(${sateliteIcon})`;
|
|
178
|
+
htmlButton.title = this.isSatelliteView
|
|
179
|
+
? "Vista Normal"
|
|
180
|
+
: "Vista Satélite";
|
|
181
|
+
});
|
|
182
|
+
},
|
|
183
|
+
getLayersFeaturesInGeoJson() {
|
|
184
|
+
const geojson = L.geoJSON().addTo(this.mapRender as any);
|
|
185
|
+
this.featuresData.eachLayer((layer: any) => {
|
|
186
|
+
geojson.addLayer(layer);
|
|
187
|
+
});
|
|
188
|
+
const geojsonString = [geojson.toGeoJSON()]; //JSON.stringify([geojson.toGeoJSON()]);
|
|
189
|
+
return geojsonString;
|
|
190
|
+
},
|
|
191
|
+
triggerSaveEdit(): void {
|
|
192
|
+
const buttons =
|
|
193
|
+
document.getElementsByClassName(
|
|
194
|
+
"leaflet-draw-actions leaflet-draw-actions-top"
|
|
195
|
+
) ||
|
|
196
|
+
document.getElementsByClassName(
|
|
197
|
+
"leaflet-draw-actions leaflet-draw-actions-top leaflet-draw-actions-bottom"
|
|
198
|
+
);
|
|
199
|
+
buttons[0]?.querySelector("li")?.querySelector("a")?.click();
|
|
200
|
+
},
|
|
201
|
+
makeid(length: number): void {
|
|
202
|
+
let result = "";
|
|
203
|
+
const characters = "abcdefghijklmnopqrstuvwxyz";
|
|
204
|
+
const charactersLength = characters.length;
|
|
205
|
+
let counter = 0;
|
|
206
|
+
while (counter < length) {
|
|
207
|
+
result += characters.charAt(
|
|
208
|
+
Math.floor(Math.random() * charactersLength)
|
|
209
|
+
);
|
|
210
|
+
counter += 1;
|
|
211
|
+
}
|
|
212
|
+
this.idMap = result;
|
|
213
|
+
},
|
|
214
|
+
renderMap(): void {
|
|
215
|
+
drawLocales("es");
|
|
216
|
+
setTimeout(() => {
|
|
217
|
+
if (this.loadPolygon) {
|
|
218
|
+
this.viewMap();
|
|
219
|
+
} else {
|
|
220
|
+
this.createMap();
|
|
221
|
+
}
|
|
222
|
+
}, 500);
|
|
223
|
+
},
|
|
224
|
+
createMap(): void {
|
|
225
|
+
const iconDefaultMarket = this.configurationMap
|
|
226
|
+
? this.configurationMap.iconMarker
|
|
227
|
+
? this.configurationMap.iconMarker
|
|
228
|
+
: this.markerIcon
|
|
229
|
+
: this.markerIcon;
|
|
230
|
+
window.type = true;
|
|
231
|
+
const map = L.map(this.idMap, { fullscreenControl: true } as any).setView(
|
|
232
|
+
[
|
|
233
|
+
this.renderCoordinates ? (this.renderCoordinates[0] as number) : 0,
|
|
234
|
+
this.renderCoordinates ? (this.renderCoordinates[1] as number) : 0,
|
|
235
|
+
],
|
|
236
|
+
this.renderCoordinates ? (this.renderCoordinates[2] as number) : 0
|
|
237
|
+
);
|
|
238
|
+
this.mapRender = map;
|
|
239
|
+
|
|
240
|
+
// Inicializar con la vista correcta según el prop
|
|
241
|
+
this.isSatelliteView = this.isSatelite || false;
|
|
242
|
+
if (this.isSatelliteView) {
|
|
243
|
+
this.currentTileLayer = L.tileLayer(
|
|
244
|
+
"https://api.maptiler.com/maps/satellite/{z}/{x}/{y}.jpg?key=t8mWT2ozs1JWBqMZOnZr",
|
|
245
|
+
{
|
|
246
|
+
attribution:
|
|
247
|
+
'© <a href="https://www.maptiler.com/">MapTiler</a> © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
} else {
|
|
251
|
+
this.currentTileLayer = L.tileLayer(
|
|
252
|
+
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
|
253
|
+
{
|
|
254
|
+
attribution:
|
|
255
|
+
'© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
|
256
|
+
}
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
this.currentTileLayer.addTo(map);
|
|
260
|
+
|
|
261
|
+
// Agregar el control satelital si está habilitado
|
|
262
|
+
if (this.isSatelite !== undefined) {
|
|
263
|
+
const satelliteControl = this.createSatelliteControl();
|
|
264
|
+
map.addControl(satelliteControl);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
map.setZoom(this.configurationMap?.maxZoom);
|
|
268
|
+
if (this.configurationMap?.renderMarker) {
|
|
269
|
+
const marker = L.marker(
|
|
270
|
+
[
|
|
271
|
+
this.renderCoordinates ? (this.renderCoordinates[0] as number) : 0,
|
|
272
|
+
this.renderCoordinates ? (this.renderCoordinates[1] as number) : 0,
|
|
273
|
+
],
|
|
274
|
+
{
|
|
275
|
+
icon: L.icon(iconDefaultMarket),
|
|
276
|
+
draggable: this.configurationMap?.dragMarker,
|
|
277
|
+
}
|
|
278
|
+
).addTo(map);
|
|
279
|
+
this.markerRender = marker;
|
|
280
|
+
if (this.getCoodMarker) {
|
|
281
|
+
marker.on("dragend", (event) => {
|
|
282
|
+
const updatedCoordinates = event.target.getLatLng();
|
|
283
|
+
const lat = updatedCoordinates.lat;
|
|
284
|
+
const lng = updatedCoordinates.lng;
|
|
285
|
+
if (this.getCoodMarker) this.getCoodMarker(lat, lng);
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
const featuresData = L.featureGroup();
|
|
290
|
+
this.featuresData = featuresData;
|
|
291
|
+
const featureGroup = featuresData.addTo(map);
|
|
292
|
+
let contextEdit = {
|
|
293
|
+
featureGroup: featureGroup, // Crea un nuevo grupo de capas para los polígonos
|
|
294
|
+
edit: false,
|
|
295
|
+
remove: this.configurationMap?.editFigures.remove,
|
|
296
|
+
};
|
|
297
|
+
if (this.configurationMap?.editFigures.edit) {
|
|
298
|
+
contextEdit = {
|
|
299
|
+
featureGroup: featureGroup, // Crea un nuevo grupo de capas para los polígonos
|
|
300
|
+
...this.configurationMap?.editFigures.edit,
|
|
301
|
+
remove: this.configurationMap?.editFigures.remove,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
const drawControl = new L.Control.Draw({
|
|
305
|
+
position: "topleft",
|
|
306
|
+
draw: {
|
|
307
|
+
polygon: this.configurationMap?.createFigures.polygon,
|
|
308
|
+
circle: false, //this.configurationMap?.createFigures.circle,
|
|
309
|
+
rectangle: this.configurationMap?.createFigures.rectangle
|
|
310
|
+
? {
|
|
311
|
+
shapeOptions: {
|
|
312
|
+
color: "blue",
|
|
313
|
+
},
|
|
314
|
+
}
|
|
315
|
+
: false,
|
|
316
|
+
marker: this.configurationMap?.createFigures.marker
|
|
317
|
+
? {
|
|
318
|
+
icon: L.icon(iconDefaultMarket),
|
|
319
|
+
}
|
|
320
|
+
: false,
|
|
321
|
+
polyline: this.configurationMap?.createFigures.polyline
|
|
322
|
+
? {
|
|
323
|
+
shapeOptions: {
|
|
324
|
+
color: "blue",
|
|
325
|
+
},
|
|
326
|
+
}
|
|
327
|
+
: false,
|
|
328
|
+
circlemarker: this.configurationMap?.createFigures.multipoint,
|
|
329
|
+
},
|
|
330
|
+
edit: contextEdit as any,
|
|
331
|
+
});
|
|
332
|
+
map.addControl(drawControl);
|
|
333
|
+
|
|
334
|
+
map.on("draw:created", (event: any) => {
|
|
335
|
+
const layer = event.layer;
|
|
336
|
+
featureGroup.addLayer(layer);
|
|
337
|
+
let geojson = layer.toGeoJSON();
|
|
338
|
+
// *** AGREGADO: Manejo especial para CircleMarker (convertir a MultiPoint) ***
|
|
339
|
+
if (event.layerType === "circlemarker") {
|
|
340
|
+
// Convertir CircleMarker a formato MultiPoint para consistencia
|
|
341
|
+
geojson = {
|
|
342
|
+
type: "Feature",
|
|
343
|
+
geometry: {
|
|
344
|
+
type: "MultiPoint",
|
|
345
|
+
coordinates: [geojson.geometry.coordinates],
|
|
346
|
+
},
|
|
347
|
+
properties: {
|
|
348
|
+
...geojson.properties,
|
|
349
|
+
pointType: "multipoint",
|
|
350
|
+
style: {
|
|
351
|
+
color:
|
|
352
|
+
this.configurationMap?.createFigures?.multipoint?.color ||
|
|
353
|
+
"green",
|
|
354
|
+
fillColor:
|
|
355
|
+
this.configurationMap?.createFigures?.multipoint?.fillColor ||
|
|
356
|
+
"green",
|
|
357
|
+
fillOpacity:
|
|
358
|
+
this.configurationMap?.createFigures?.multipoint
|
|
359
|
+
?.fillOpacity || 0.8,
|
|
360
|
+
radius:
|
|
361
|
+
this.configurationMap?.createFigures?.multipoint?.radius || 6,
|
|
362
|
+
weight:
|
|
363
|
+
this.configurationMap?.createFigures?.multipoint?.weight || 2,
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
if (this.getGeoJSON) this.getGeoJSON([geojson]);
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
map.on("draw:edited", (event: any) => {
|
|
372
|
+
const layers = event.layers;
|
|
373
|
+
layers.eachLayer((layer: any) => {
|
|
374
|
+
const geojson = layer.toGeoJSON();
|
|
375
|
+
if (this.getGeoJSON) this.getGeoJSON([geojson]);
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
},
|
|
379
|
+
viewMap(): void {
|
|
380
|
+
const iconDefaultMarket = this.configurationMap
|
|
381
|
+
? this.configurationMap.iconMarker
|
|
382
|
+
? this.configurationMap.iconMarker
|
|
383
|
+
: this.markerIcon
|
|
384
|
+
: this.markerIcon;
|
|
385
|
+
window.type = true;
|
|
386
|
+
const map = L.map(this.idMap, { fullscreenControl: true } as any).setView(
|
|
387
|
+
[
|
|
388
|
+
this.renderCoordinates ? (this.renderCoordinates[0] as number) : 0,
|
|
389
|
+
this.renderCoordinates ? (this.renderCoordinates[1] as number) : 0,
|
|
390
|
+
],
|
|
391
|
+
this.renderCoordinates ? (this.renderCoordinates[2] as number) : 0
|
|
392
|
+
);
|
|
393
|
+
this.mapRender = map;
|
|
394
|
+
|
|
395
|
+
// Inicializar con la vista correcta según el prop
|
|
396
|
+
this.isSatelliteView = this.isSatelite || false;
|
|
397
|
+
if (this.isSatelliteView) {
|
|
398
|
+
this.currentTileLayer = L.tileLayer(
|
|
399
|
+
"https://api.maptiler.com/maps/satellite/{z}/{x}/{y}.jpg?key=t8mWT2ozs1JWBqMZOnZr",
|
|
400
|
+
{
|
|
401
|
+
attribution:
|
|
402
|
+
'© <a href="https://www.maptiler.com/">MapTiler</a> © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
|
403
|
+
}
|
|
404
|
+
);
|
|
405
|
+
} else {
|
|
406
|
+
this.currentTileLayer = L.tileLayer(
|
|
407
|
+
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
|
408
|
+
{
|
|
409
|
+
attribution:
|
|
410
|
+
'© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
|
411
|
+
}
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
this.currentTileLayer.addTo(map);
|
|
415
|
+
|
|
416
|
+
// Agregar el control satelital si está habilitado
|
|
417
|
+
if (this.isSatelite !== undefined) {
|
|
418
|
+
const satelliteControl = this.createSatelliteControl();
|
|
419
|
+
map.addControl(satelliteControl);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
map.setZoom(this.configurationMap?.maxZoom);
|
|
423
|
+
const featuresData = L.featureGroup();
|
|
424
|
+
this.featuresData = featuresData;
|
|
425
|
+
const featureGroup = featuresData.addTo(map);
|
|
426
|
+
let contextEdit = {
|
|
427
|
+
featureGroup: featureGroup, // Crea un nuevo grupo de capas para los polígonos
|
|
428
|
+
edit: false,
|
|
429
|
+
remove: this.configurationMap?.editFigures.remove,
|
|
430
|
+
};
|
|
431
|
+
if (this.configurationMap?.editFigures.edit) {
|
|
432
|
+
contextEdit = {
|
|
433
|
+
featureGroup: featureGroup, // Crea un nuevo grupo de capas para los polígonos
|
|
434
|
+
...this.configurationMap?.editFigures.edit,
|
|
435
|
+
remove: this.configurationMap?.editFigures.remove,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const vm: any = this;
|
|
440
|
+
const renderGeojson = this.renderGeojson;
|
|
441
|
+
if (renderGeojson && renderGeojson.length) {
|
|
442
|
+
// Collect Point features to cluster (we'll render non-Point features normally)
|
|
443
|
+
const pointsToCluster: Array<any> = [];
|
|
444
|
+
// LayerGroup to hold cluster markers so we can clear on zoom
|
|
445
|
+
const clusterLayer = L.layerGroup().addTo(map);
|
|
446
|
+
|
|
447
|
+
renderGeojson.forEach((item: any) => {
|
|
448
|
+
const self = this;
|
|
449
|
+
if (item.type === "FeatureCollection") {
|
|
450
|
+
item.features.forEach((feature: any) => {
|
|
451
|
+
if (
|
|
452
|
+
feature.geometry.type === "Polygon" ||
|
|
453
|
+
feature.geometry.type === "MultiPolygon" ||
|
|
454
|
+
feature.geometry.type === "LineString" ||
|
|
455
|
+
feature.geometry.type === "MultiLineString" ||
|
|
456
|
+
feature.geometry.type === "Point"
|
|
457
|
+
) {
|
|
458
|
+
// For Point features, collect them for clustering instead of adding directly
|
|
459
|
+
if (feature.geometry.type === "Point") {
|
|
460
|
+
const coords = feature.geometry.coordinates;
|
|
461
|
+
pointsToCluster.push({
|
|
462
|
+
lat: coords[1],
|
|
463
|
+
lng: coords[0],
|
|
464
|
+
properties: feature.properties || {},
|
|
465
|
+
feature,
|
|
466
|
+
});
|
|
467
|
+
} else {
|
|
468
|
+
// Non-point features: render normally
|
|
469
|
+
L.geoJson(feature, {
|
|
470
|
+
pointToLayer: this.getPointToLayer.bind(this),
|
|
471
|
+
onEachFeature: function (feature, layer) {
|
|
472
|
+
featureGroup.addLayer(layer);
|
|
473
|
+
// Mostrar popup para Polygon y MultiPolygon al hacer clic
|
|
474
|
+
if (
|
|
475
|
+
feature.geometry.type === "Polygon" ||
|
|
476
|
+
feature.geometry.type === "MultiPolygon"
|
|
477
|
+
) {
|
|
478
|
+
var tooltipContent =
|
|
479
|
+
self.getPolygonTooltipContent(feature);
|
|
480
|
+
layer.bindPopup(tooltipContent, {
|
|
481
|
+
maxHeight: 360,
|
|
482
|
+
maxWidth: 320,
|
|
483
|
+
className: "limit-popup",
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
},
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
// Agregar markers y flechas de dirección para LineString
|
|
490
|
+
if (
|
|
491
|
+
feature.geometry.type === "LineString" &&
|
|
492
|
+
Array.isArray(feature.geometry.coordinates)
|
|
493
|
+
) {
|
|
494
|
+
const coords = feature.geometry.coordinates;
|
|
495
|
+
const coordProps =
|
|
496
|
+
feature.properties &&
|
|
497
|
+
Array.isArray(feature.properties.coordProperties)
|
|
498
|
+
? feature.properties.coordProperties
|
|
499
|
+
: [];
|
|
500
|
+
for (let i = 0; i < coords.length; i++) {
|
|
501
|
+
// Unificar círculo y flecha en un solo divIcon
|
|
502
|
+
let iconHtml =
|
|
503
|
+
'<div style="display:flex;align-items:center;justify-content:center;width:14px;height:14px;">';
|
|
504
|
+
// Círculo azul con padding, envuelve el SVG
|
|
505
|
+
let arrowSvg = "";
|
|
506
|
+
if (
|
|
507
|
+
i < coords.length - 1 &&
|
|
508
|
+
coordProps[i] &&
|
|
509
|
+
coordProps[i + 1] &&
|
|
510
|
+
coordProps[i].fecha &&
|
|
511
|
+
coordProps[i + 1].fecha &&
|
|
512
|
+
new Date(coordProps[i + 1].fecha) >
|
|
513
|
+
new Date(coordProps[i].fecha)
|
|
514
|
+
) {
|
|
515
|
+
// Calcular ángulo (bearing)
|
|
516
|
+
const toRad = (deg: number) => (deg * Math.PI) / 180;
|
|
517
|
+
const toDeg = (rad: number) => (rad * 180) / Math.PI;
|
|
518
|
+
const lat1 = toRad(coords[i][1]);
|
|
519
|
+
const lat2 = toRad(coords[i + 1][1]);
|
|
520
|
+
const dLon = toRad(coords[i + 1][0] - coords[i][0]);
|
|
521
|
+
const y = Math.sin(dLon) * Math.cos(lat2);
|
|
522
|
+
const x =
|
|
523
|
+
Math.cos(lat1) * Math.sin(lat2) -
|
|
524
|
+
Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
|
|
525
|
+
let brng = Math.atan2(y, x);
|
|
526
|
+
brng = toDeg(brng);
|
|
527
|
+
const angle = (brng + 360) % 360;
|
|
528
|
+
arrowSvg = `<svg width="10" height="10" viewBox="0 0 22 22" style="display:block;transform:rotate(${angle}deg);"><polygon points="11,3 15,17 11,14 7,17" fill="#fff" stroke="#fff" stroke-width="1.5"/></svg>`;
|
|
529
|
+
}
|
|
530
|
+
iconHtml += `<div style="background:#2196F3;border-radius:50%;border:1px solid #fff;box-shadow:0 0 2px #000;padding:1px;width:8px;height:8px;display:flex;align-items:center;justify-content:center;">${arrowSvg}</div>`;
|
|
531
|
+
iconHtml += "</div>";
|
|
532
|
+
const marker = L.marker([coords[i][1], coords[i][0]], {
|
|
533
|
+
icon: L.divIcon({
|
|
534
|
+
className: "",
|
|
535
|
+
html: iconHtml,
|
|
536
|
+
iconSize: [14, 14],
|
|
537
|
+
iconAnchor: [7, 7],
|
|
538
|
+
}),
|
|
539
|
+
zIndexOffset: -100,
|
|
540
|
+
});
|
|
541
|
+
marker.addTo(featureGroup);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
} else if (item.type === "Feature") {
|
|
547
|
+
if (
|
|
548
|
+
item.geometry.type === "Polygon" ||
|
|
549
|
+
item.geometry.type === "MultiPolygon" ||
|
|
550
|
+
item.geometry.type === "LineString" ||
|
|
551
|
+
item.geometry.type === "MultiLineString" ||
|
|
552
|
+
item.geometry.type === "Point"
|
|
553
|
+
) {
|
|
554
|
+
// If it's a Point feature, collect it for clustering
|
|
555
|
+
if (item.geometry.type === "Point") {
|
|
556
|
+
const coords = item.geometry.coordinates;
|
|
557
|
+
pointsToCluster.push({
|
|
558
|
+
lat: coords[1],
|
|
559
|
+
lng: coords[0],
|
|
560
|
+
properties: item.properties || {},
|
|
561
|
+
feature: item,
|
|
562
|
+
});
|
|
563
|
+
} else {
|
|
564
|
+
// Non-point features: render normally
|
|
565
|
+
L.geoJson(item, {
|
|
566
|
+
pointToLayer: this.getPointToLayer.bind(this),
|
|
567
|
+
onEachFeature: function (feature, layer) {
|
|
568
|
+
featureGroup.addLayer(layer);
|
|
569
|
+
// Mostrar popup para Polygon y MultiPolygon al hacer clic
|
|
570
|
+
if (
|
|
571
|
+
feature.geometry.type === "Polygon" ||
|
|
572
|
+
feature.geometry.type === "MultiPolygon"
|
|
573
|
+
) {
|
|
574
|
+
var tooltipContent =
|
|
575
|
+
self.getPolygonTooltipContent(feature);
|
|
576
|
+
layer.bindPopup(tooltipContent, {
|
|
577
|
+
maxHeight: 360,
|
|
578
|
+
maxWidth: 320,
|
|
579
|
+
className: "limit-popup",
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
},
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
// Agregar markers y flechas de dirección para LineString
|
|
586
|
+
if (
|
|
587
|
+
item.geometry.type === "LineString" &&
|
|
588
|
+
Array.isArray(item.geometry.coordinates)
|
|
589
|
+
) {
|
|
590
|
+
const coords = item.geometry.coordinates;
|
|
591
|
+
const coordProps =
|
|
592
|
+
item.properties &&
|
|
593
|
+
Array.isArray(item.properties.coordProperties)
|
|
594
|
+
? item.properties.coordProperties
|
|
595
|
+
: [];
|
|
596
|
+
for (let i = 0; i < coords.length; i++) {
|
|
597
|
+
// Unificar círculo y flecha en un solo divIcon
|
|
598
|
+
let iconHtml =
|
|
599
|
+
'<div style="display:flex;align-items:center;justify-content:center;width:14px;height:14px;">';
|
|
600
|
+
// Círculo azul con padding, envuelve el SVG
|
|
601
|
+
let arrowSvg = "";
|
|
602
|
+
if (
|
|
603
|
+
i < coords.length - 1 &&
|
|
604
|
+
coordProps[i] &&
|
|
605
|
+
coordProps[i + 1] &&
|
|
606
|
+
coordProps[i].fecha &&
|
|
607
|
+
coordProps[i + 1].fecha &&
|
|
608
|
+
new Date(coordProps[i + 1].fecha) >
|
|
609
|
+
new Date(coordProps[i].fecha)
|
|
610
|
+
) {
|
|
611
|
+
// Calcular ángulo (bearing)
|
|
612
|
+
const toRad = (deg: number) => (deg * Math.PI) / 180;
|
|
613
|
+
const toDeg = (rad: number) => (rad * 180) / Math.PI;
|
|
614
|
+
const lat1 = toRad(coords[i][1]);
|
|
615
|
+
const lat2 = toRad(coords[i + 1][1]);
|
|
616
|
+
const dLon = toRad(coords[i + 1][0] - coords[i][0]);
|
|
617
|
+
const y = Math.sin(dLon) * Math.cos(lat2);
|
|
618
|
+
const x =
|
|
619
|
+
Math.cos(lat1) * Math.sin(lat2) -
|
|
620
|
+
Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
|
|
621
|
+
let brng = Math.atan2(y, x);
|
|
622
|
+
brng = toDeg(brng);
|
|
623
|
+
const angle = (brng + 360) % 360;
|
|
624
|
+
arrowSvg = `<svg width="10" height="10" viewBox="0 0 22 22" style="display:block;transform:rotate(${angle}deg);"><polygon points="11,3 15,17 11,14 7,17" fill="#fff" stroke="#fff" stroke-width="1.5"/></svg>`;
|
|
625
|
+
}
|
|
626
|
+
iconHtml += `<div style="background:#2196F3;border-radius:50%;border:1px solid #fff;box-shadow:0 0 2px #000;padding:1.5px;width:8px;height:8px;display:flex;align-items:center;justify-content:center;">${arrowSvg}</div>`;
|
|
627
|
+
iconHtml += "</div>";
|
|
628
|
+
const marker = L.marker([coords[i][1], coords[i][0]], {
|
|
629
|
+
icon: L.divIcon({
|
|
630
|
+
className: "",
|
|
631
|
+
html: iconHtml,
|
|
632
|
+
iconSize: [14, 14],
|
|
633
|
+
iconAnchor: [7, 7],
|
|
634
|
+
}),
|
|
635
|
+
});
|
|
636
|
+
marker.addTo(featureGroup);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
// --- Render point clusters collected above ---
|
|
643
|
+
const renderPointClusters = () => {
|
|
644
|
+
clusterLayer.clearLayers();
|
|
645
|
+
if (!pointsToCluster.length) return;
|
|
646
|
+
|
|
647
|
+
// Helper to convert lat/lng to pixel point
|
|
648
|
+
const latLngToPixel = (lat: number, lng: number) =>
|
|
649
|
+
map.project([lat, lng], map.getZoom());
|
|
650
|
+
|
|
651
|
+
const pixelDistance = Math.max(30, 80 - map.getZoom() * 2); // dynamic threshold
|
|
652
|
+
const pts = pointsToCluster.slice();
|
|
653
|
+
const used = new Array(pts.length).fill(false);
|
|
654
|
+
const clusters: Array<any[]> = [];
|
|
655
|
+
|
|
656
|
+
for (let i = 0; i < pts.length; i++) {
|
|
657
|
+
if (used[i]) continue;
|
|
658
|
+
const base = pts[i];
|
|
659
|
+
const basePx = latLngToPixel(base.lat, base.lng);
|
|
660
|
+
const cluster = [base];
|
|
661
|
+
used[i] = true;
|
|
662
|
+
for (let j = i + 1; j < pts.length; j++) {
|
|
663
|
+
if (used[j]) continue;
|
|
664
|
+
const cmp = pts[j];
|
|
665
|
+
const cmpPx = latLngToPixel(cmp.lat, cmp.lng);
|
|
666
|
+
const dist = Math.sqrt(
|
|
667
|
+
Math.pow(basePx.x - cmpPx.x, 2) +
|
|
668
|
+
Math.pow(basePx.y - cmpPx.y, 2)
|
|
669
|
+
);
|
|
670
|
+
if (dist < pixelDistance) {
|
|
671
|
+
cluster.push(cmp);
|
|
672
|
+
used[j] = true;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
clusters.push(cluster);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
clusters.forEach((cluster) => {
|
|
679
|
+
// average center
|
|
680
|
+
const lat = cluster.reduce((s, p) => s + p.lat, 0) / cluster.length;
|
|
681
|
+
const lng = cluster.reduce((s, p) => s + p.lng, 0) / cluster.length;
|
|
682
|
+
|
|
683
|
+
// helper to create icon based on tipo property (match getPointToLayer)
|
|
684
|
+
const makeIconFor = (props: any) => {
|
|
685
|
+
const tipo = props?.tipo;
|
|
686
|
+
const w = 50;
|
|
687
|
+
const h = 50;
|
|
688
|
+
const ax = 16;
|
|
689
|
+
const ay = 41;
|
|
690
|
+
// popupAnchor relative to iconAnchor so popup arrow points to the visual center
|
|
691
|
+
const popupAnchor = [
|
|
692
|
+
Math.round(w / 2 - ax),
|
|
693
|
+
Math.round(h / 2 - ay),
|
|
694
|
+
] as [number, number];
|
|
695
|
+
const firstIcon = L.icon({
|
|
696
|
+
iconUrl: origenIconUrl,
|
|
697
|
+
iconSize: [w, h],
|
|
698
|
+
iconAnchor: [ax, ay],
|
|
699
|
+
popupAnchor,
|
|
700
|
+
});
|
|
701
|
+
const lastIcon = L.icon({
|
|
702
|
+
iconUrl: destinoIconUrl,
|
|
703
|
+
iconSize: [w, h],
|
|
704
|
+
iconAnchor: [ax, ay],
|
|
705
|
+
popupAnchor,
|
|
706
|
+
});
|
|
707
|
+
const pointerIcon = L.icon({
|
|
708
|
+
iconUrl: llegadaIconUrl,
|
|
709
|
+
iconSize: [w, h],
|
|
710
|
+
iconAnchor: [ax, ay],
|
|
711
|
+
popupAnchor,
|
|
712
|
+
});
|
|
713
|
+
if (tipo === 0) return firstIcon;
|
|
714
|
+
if (tipo === 1) return lastIcon;
|
|
715
|
+
return pointerIcon;
|
|
716
|
+
};
|
|
717
|
+
|
|
718
|
+
if (cluster.length === 1) {
|
|
719
|
+
const p = cluster[0];
|
|
720
|
+
const icon = makeIconFor(p.properties || {});
|
|
721
|
+
const marker = L.marker([lat, lng], { icon, zIndexOffset: 1000 });
|
|
722
|
+
// bind popup from original feature
|
|
723
|
+
const tempFeature = p.feature || {};
|
|
724
|
+
const popupContent = vm.getPopupContent(tempFeature);
|
|
725
|
+
marker.bindPopup(popupContent, {
|
|
726
|
+
maxHeight: 360,
|
|
727
|
+
maxWidth: 320,
|
|
728
|
+
className: "limit-popup",
|
|
729
|
+
});
|
|
730
|
+
clusterLayer.addLayer(marker);
|
|
731
|
+
} else {
|
|
732
|
+
// Use icon of the first point as the visible marker
|
|
733
|
+
const primary = cluster[0];
|
|
734
|
+
const icon = makeIconFor(primary.properties || {});
|
|
735
|
+
// cluster marker container: marker with icon plus side badge
|
|
736
|
+
const markerHtml =
|
|
737
|
+
`<div style="position:relative;display:inline-block;">` +
|
|
738
|
+
`<img src="${
|
|
739
|
+
(icon as any).options.iconUrl
|
|
740
|
+
}" style="width:50px;height:50px;object-fit:cover;"/>` +
|
|
741
|
+
`<div style="position:absolute;right:-2px;top:-2px;background:#2196F3;color:#fff;font-size:11px;font-weight:bold;border-radius:50%;padding:0;min-width:18px;height:18px;display:flex;align-items:center;justify-content:center;z-index:3;border:1.5px solid #fff;">${cluster.length}</div>` +
|
|
742
|
+
`</div>`;
|
|
743
|
+
|
|
744
|
+
const marker = L.marker([lat, lng], {
|
|
745
|
+
icon: L.divIcon({
|
|
746
|
+
className: "",
|
|
747
|
+
html: markerHtml,
|
|
748
|
+
iconSize: [50, 50],
|
|
749
|
+
iconAnchor: [25, 25],
|
|
750
|
+
popupAnchor: [0, 0],
|
|
751
|
+
}),
|
|
752
|
+
zIndexOffset: 1000,
|
|
753
|
+
});
|
|
754
|
+
|
|
755
|
+
// build popup by concatenating each member's popup HTML separated by a divider
|
|
756
|
+
const parts: string[] = [];
|
|
757
|
+
cluster.forEach((p) => {
|
|
758
|
+
const tempFeature = p.feature || {};
|
|
759
|
+
const content = vm.getPopupContent(tempFeature) || "";
|
|
760
|
+
parts.push(`<div style="padding:6px 0;">${content}</div>`);
|
|
761
|
+
});
|
|
762
|
+
const popupHtml = parts.join(
|
|
763
|
+
'<hr style="border:none;border-top:1px solid #ddd;margin:6px 0;"/>'
|
|
764
|
+
);
|
|
765
|
+
marker.bindPopup(
|
|
766
|
+
`<div style="max-width:320px;">${popupHtml}</div>`,
|
|
767
|
+
{ maxHeight: 360, maxWidth: 320, className: "limit-popup" }
|
|
768
|
+
);
|
|
769
|
+
clusterLayer.addLayer(marker);
|
|
770
|
+
}
|
|
771
|
+
});
|
|
772
|
+
};
|
|
773
|
+
|
|
774
|
+
// Initial render and re-render on zoom
|
|
775
|
+
renderPointClusters();
|
|
776
|
+
map.on("zoomend", () => {
|
|
777
|
+
renderPointClusters();
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
const bounds = featureGroup.getBounds();
|
|
781
|
+
map.fitBounds(bounds);
|
|
782
|
+
}
|
|
783
|
+
const drawControl = new L.Control.Draw({
|
|
784
|
+
position: "topleft",
|
|
785
|
+
draw: {
|
|
786
|
+
polygon: this.configurationMap?.createFigures.polygon,
|
|
787
|
+
circle: false, //this.configurationMap?.createFigures.circle,
|
|
788
|
+
rectangle: this.configurationMap?.createFigures.rectangle
|
|
789
|
+
? {
|
|
790
|
+
shapeOptions: {
|
|
791
|
+
color: "blue",
|
|
792
|
+
},
|
|
793
|
+
}
|
|
794
|
+
: false,
|
|
795
|
+
marker: this.configurationMap?.createFigures.marker
|
|
796
|
+
? {
|
|
797
|
+
icon: L.icon(iconDefaultMarket),
|
|
798
|
+
}
|
|
799
|
+
: false,
|
|
800
|
+
polyline: this.configurationMap?.createFigures.polyline
|
|
801
|
+
? {
|
|
802
|
+
shapeOptions: {
|
|
803
|
+
color: "blue",
|
|
804
|
+
},
|
|
805
|
+
}
|
|
806
|
+
: false,
|
|
807
|
+
circlemarker: this.configurationMap?.createFigures.multipoint,
|
|
808
|
+
},
|
|
809
|
+
edit: contextEdit as any,
|
|
810
|
+
});
|
|
811
|
+
map.addControl(drawControl);
|
|
812
|
+
|
|
813
|
+
map.on("draw:created", (event: any) => {
|
|
814
|
+
const layer = event.layer;
|
|
815
|
+
featureGroup.addLayer(layer);
|
|
816
|
+
let geojson = layer.toGeoJSON();
|
|
817
|
+
// *** AGREGADO: Manejo especial para CircleMarker (convertir a MultiPoint) ***
|
|
818
|
+
if (event.layerType === "circlemarker") {
|
|
819
|
+
// Convertir CircleMarker a formato MultiPoint para consistencia
|
|
820
|
+
geojson = {
|
|
821
|
+
type: "Feature",
|
|
822
|
+
geometry: {
|
|
823
|
+
type: "MultiPoint",
|
|
824
|
+
coordinates: [geojson.geometry.coordinates],
|
|
825
|
+
},
|
|
826
|
+
properties: {
|
|
827
|
+
...geojson.properties,
|
|
828
|
+
pointType: "multipoint",
|
|
829
|
+
style: {
|
|
830
|
+
color:
|
|
831
|
+
this.configurationMap?.createFigures?.multipoint?.color ||
|
|
832
|
+
"green",
|
|
833
|
+
fillColor:
|
|
834
|
+
this.configurationMap?.createFigures?.multipoint?.fillColor ||
|
|
835
|
+
"green",
|
|
836
|
+
fillOpacity:
|
|
837
|
+
this.configurationMap?.createFigures?.multipoint
|
|
838
|
+
?.fillOpacity || 0.8,
|
|
839
|
+
radius:
|
|
840
|
+
this.configurationMap?.createFigures?.multipoint?.radius || 6,
|
|
841
|
+
weight:
|
|
842
|
+
this.configurationMap?.createFigures?.multipoint?.weight || 2,
|
|
843
|
+
},
|
|
844
|
+
},
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
if (this.getGeoJSON) this.getGeoJSON(geojson);
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
map.on("draw:edited", (event: any) => {
|
|
851
|
+
const layers = event.layers;
|
|
852
|
+
layers.eachLayer((layer: any) => {
|
|
853
|
+
const geojson = layer.toGeoJSON();
|
|
854
|
+
if (this.getGeoJSON) this.getGeoJSON(geojson);
|
|
855
|
+
});
|
|
856
|
+
});
|
|
857
|
+
},
|
|
858
|
+
getPointToLayer(feature: any, latlng: any) {
|
|
859
|
+
const geometryType = feature.geometry?.type;
|
|
860
|
+
|
|
861
|
+
// Point: icono según properties.tipo
|
|
862
|
+
if (geometryType === "Point") {
|
|
863
|
+
const tipo = feature.properties?.tipo;
|
|
864
|
+
const firstIcon = L.icon({
|
|
865
|
+
iconUrl: origenIconUrl,
|
|
866
|
+
iconSize: [50, 50],
|
|
867
|
+
iconAnchor: [16, 41],
|
|
868
|
+
});
|
|
869
|
+
const lastIcon = L.icon({
|
|
870
|
+
iconUrl: destinoIconUrl,
|
|
871
|
+
iconSize: [50, 50],
|
|
872
|
+
iconAnchor: [16, 41],
|
|
873
|
+
});
|
|
874
|
+
const pointerIcon = L.icon({
|
|
875
|
+
iconUrl: llegadaIconUrl,
|
|
876
|
+
iconSize: [50, 50],
|
|
877
|
+
iconAnchor: [16, 41],
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
let icon = pointerIcon;
|
|
881
|
+
if (tipo === 0) icon = firstIcon;
|
|
882
|
+
else if (tipo === 1) icon = lastIcon;
|
|
883
|
+
|
|
884
|
+
return L.marker(latlng, { icon });
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// Otros tipos: icono por defecto
|
|
888
|
+
return L.marker(latlng);
|
|
889
|
+
},
|
|
890
|
+
async searchAddress(query: string) {
|
|
891
|
+
const address = await axios.get(
|
|
892
|
+
location.protocol + "//nominatim.openstreetmap.org/search?",
|
|
893
|
+
{
|
|
894
|
+
params: {
|
|
895
|
+
//query
|
|
896
|
+
q: query,
|
|
897
|
+
limit: 1,
|
|
898
|
+
format: "json",
|
|
899
|
+
},
|
|
900
|
+
}
|
|
901
|
+
);
|
|
902
|
+
if (address.data.length === 1) {
|
|
903
|
+
const lat = parseFloat(address.data[0].lat);
|
|
904
|
+
const lng = parseFloat(address.data[0].lon);
|
|
905
|
+
const coord = { lng, lat };
|
|
906
|
+
this.setCoordinates({ ...coord, moveMarker: true });
|
|
907
|
+
return address.data[0];
|
|
908
|
+
}
|
|
909
|
+
return address.data;
|
|
910
|
+
},
|
|
911
|
+
setCoordinates({
|
|
912
|
+
lat,
|
|
913
|
+
lng,
|
|
914
|
+
}: {
|
|
915
|
+
lat: number;
|
|
916
|
+
lng: number;
|
|
917
|
+
moveMarker?: boolean;
|
|
918
|
+
}): void {
|
|
919
|
+
if (this.mapRender) {
|
|
920
|
+
const newCenter = L.latLng(lat, lng);
|
|
921
|
+
this.mapRender.panTo(newCenter, { animate: true, duration: 0.5 });
|
|
922
|
+
}
|
|
923
|
+
},
|
|
924
|
+
getPopupContent(feature: any): string {
|
|
925
|
+
// Puedes personalizar el contenido del popup aquí
|
|
926
|
+
const props = feature.properties || {};
|
|
927
|
+
// Solo toma en cuenta descripcion y fechaHoraLlegada
|
|
928
|
+
const descripcion = props.descripcion || "";
|
|
929
|
+
const fechaHoraLlegada = props.fechaHoraLlegada || "";
|
|
930
|
+
// url de la foto
|
|
931
|
+
const fotoId = props.fotoId || "";
|
|
932
|
+
|
|
933
|
+
const formatIsoPreserve = (iso: string): string => {
|
|
934
|
+
const d = new Date(iso);
|
|
935
|
+
if (isNaN(d.getTime())) return iso;
|
|
936
|
+
const pad = (n: number) => n.toString().padStart(2, "0");
|
|
937
|
+
const day = pad(d.getUTCDate());
|
|
938
|
+
const month = pad(d.getUTCMonth() + 1);
|
|
939
|
+
const year = d.getUTCFullYear();
|
|
940
|
+
const hours = pad(d.getUTCHours());
|
|
941
|
+
const minutes = pad(d.getUTCMinutes());
|
|
942
|
+
const seconds = pad(d.getUTCSeconds());
|
|
943
|
+
return `${day}/${month}/${year} ${hours}:${minutes}:${seconds}`;
|
|
944
|
+
};
|
|
945
|
+
|
|
946
|
+
// Formatear fecha a dd-MM-yyyy hh:mm:ss
|
|
947
|
+
let fechaFormateada = "";
|
|
948
|
+
if (fechaHoraLlegada) {
|
|
949
|
+
fechaFormateada = formatIsoPreserve(fechaHoraLlegada);
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// Seleccionar el icono de referencia según el tipo (1 -> Destino, else -> LLegadaSalida)
|
|
953
|
+
const tipo = props.tipo;
|
|
954
|
+
const referenciaIcon =
|
|
955
|
+
tipo === 1 ? DestinoIconReferencia : LLegadaSalidaIconReferencia;
|
|
956
|
+
|
|
957
|
+
// Construir HTML del popup con mejor UI para la imagen
|
|
958
|
+
// Contenedor externo controla el ancho; el popup tiene su propia clase para limitar altura
|
|
959
|
+
let html = `<div style="max-width:320px;">`;
|
|
960
|
+
|
|
961
|
+
if (fotoId) {
|
|
962
|
+
html += `\n<div style="display:flex;justify-content:center;margin-bottom:8px;">\n <img src="${fotoId}" style="height:200px;width:200px;max-width:320px;object-fit:cover;border-radius:10px;box-shadow:0 4px 10px rgba(0,0,0,0.12);"/>\n</div>`;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
// Icono de referencia a la izquierda de la descripción
|
|
966
|
+
html += `<div style="display:flex;align-items:center;gap:8px;padding-top:6px;">`;
|
|
967
|
+
if (referenciaIcon) {
|
|
968
|
+
html += `<img src="${referenciaIcon}" style="width:20px;height:20px;flex:0 0 20px;"/>`;
|
|
969
|
+
}
|
|
970
|
+
if (fechaFormateada) {
|
|
971
|
+
html += `<div style="font-size:14px;"><b>${descripcion}:</b> ${fechaFormateada}</div>`;
|
|
972
|
+
} else {
|
|
973
|
+
html += `<div style="font-size:14px;">${descripcion}</div>`;
|
|
974
|
+
}
|
|
975
|
+
html += `</div>`;
|
|
976
|
+
|
|
977
|
+
html += `</div>`;
|
|
978
|
+
return html;
|
|
979
|
+
},
|
|
980
|
+
getPolygonTooltipContent(feature: any): string {
|
|
981
|
+
const props = feature.properties || {};
|
|
982
|
+
const descripcion = props.descripcion || props.name || "Sin descripción";
|
|
983
|
+
|
|
984
|
+
return `<div style="font-weight: bold; padding: 5px;">${descripcion}</div>`;
|
|
985
|
+
},
|
|
986
|
+
},
|
|
987
|
+
watch: {
|
|
988
|
+
coordinatesMap(newVal) {
|
|
989
|
+
this.renderCoordinates = Array.isArray(newVal)
|
|
990
|
+
? (newVal as number[])
|
|
991
|
+
: [];
|
|
992
|
+
if (
|
|
993
|
+
this.mapRender &&
|
|
994
|
+
this.markerRender &&
|
|
995
|
+
this.renderCoordinates.length >= 2
|
|
996
|
+
) {
|
|
997
|
+
const coords = this.renderCoordinates as number[];
|
|
998
|
+
const newLatLng = L.latLng([coords[0], coords[1]]);
|
|
999
|
+
this.mapRender.setView([coords[0], coords[1]], coords[2]);
|
|
1000
|
+
this.markerRender.setLatLng(newLatLng);
|
|
1001
|
+
}
|
|
1002
|
+
},
|
|
1003
|
+
dataPolygon(newVal) {
|
|
1004
|
+
this.renderGeojson = newVal;
|
|
1005
|
+
},
|
|
1006
|
+
},
|
|
1007
|
+
});
|
|
1008
|
+
</script>
|
|
1009
|
+
|
|
1010
|
+
<style>
|
|
1011
|
+
.leaflet-popup-content {
|
|
1012
|
+
width: 300px !important;
|
|
1013
|
+
margin: 0 !important;
|
|
1014
|
+
padding: 10px 15px !important;
|
|
1015
|
+
}
|
|
1016
|
+
.leaflet-popup.limit-popup .leaflet-popup-content-wrapper {
|
|
1017
|
+
/* Limit popup to viewport with some margin and force internal scroll.
|
|
1018
|
+
Remove extra right padding so the scrollbar sits at the popup edge. */
|
|
1019
|
+
overflow-y: auto !important;
|
|
1020
|
+
overflow-x: hidden !important;
|
|
1021
|
+
box-sizing: border-box;
|
|
1022
|
+
padding-right: 0 !important; /* let scrollbar be at the very edge */
|
|
1023
|
+
}
|
|
1024
|
+
.leaflet-popup.limit-popup .leaflet-popup-content {
|
|
1025
|
+
/* Reserve inner right padding so text doesn't hide behind the scrollbar */
|
|
1026
|
+
box-sizing: border-box;
|
|
1027
|
+
padding-right: 16px;
|
|
1028
|
+
}
|
|
1029
|
+
.leaflet-popup.limit-popup .leaflet-popup-close-button {
|
|
1030
|
+
/* Place close button above the content area (not creating layout padding),
|
|
1031
|
+
keep it visible above the scrollbar and give a compact circular style. */
|
|
1032
|
+
right: -8px !important;
|
|
1033
|
+
top: -8px !important;
|
|
1034
|
+
z-index: 10020 !important;
|
|
1035
|
+
background: #fff !important;
|
|
1036
|
+
border-radius: 14px !important;
|
|
1037
|
+
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12) !important;
|
|
1038
|
+
width: 28px !important;
|
|
1039
|
+
height: 28px !important;
|
|
1040
|
+
line-height: 28px !important;
|
|
1041
|
+
text-align: center !important;
|
|
1042
|
+
padding: 0 !important;
|
|
1043
|
+
}
|
|
1044
|
+
</style>
|