@cerca.red/mapa 0.2.0
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 +133 -0
- package/dist/capas.d.ts +29 -0
- package/dist/capas.js +489 -0
- package/dist/capas.js.map +1 -0
- package/dist/doble/estiloFalso.d.ts +19 -0
- package/dist/doble/estiloFalso.js +33 -0
- package/dist/doble/estiloFalso.js.map +1 -0
- package/dist/doble/index.d.ts +11 -0
- package/dist/doble/index.js +12 -0
- package/dist/doble/index.js.map +1 -0
- package/dist/doble/mapaFalso.d.ts +75 -0
- package/dist/doble/mapaFalso.js +535 -0
- package/dist/doble/mapaFalso.js.map +1 -0
- package/dist/encuadre.d.ts +64 -0
- package/dist/encuadre.js +83 -0
- package/dist/encuadre.js.map +1 -0
- package/dist/estilo.d.ts +88 -0
- package/dist/estilo.js +23 -0
- package/dist/estilo.js.map +1 -0
- package/dist/eventos.d.ts +127 -0
- package/dist/eventos.js +69 -0
- package/dist/eventos.js.map +1 -0
- package/dist/identificadores.d.ts +109 -0
- package/dist/identificadores.js +104 -0
- package/dist/identificadores.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/manifiesto.d.ts +97 -0
- package/dist/manifiesto.js +156 -0
- package/dist/manifiesto.js.map +1 -0
- package/dist/modelo.d.ts +154 -0
- package/dist/modelo.js +227 -0
- package/dist/modelo.js.map +1 -0
- package/dist/navegador/index.d.ts +70 -0
- package/dist/navegador/index.js +132 -0
- package/dist/navegador/index.js.map +1 -0
- package/dist/navegador/maplibre.d.ts +41 -0
- package/dist/navegador/maplibre.js +463 -0
- package/dist/navegador/maplibre.js.map +1 -0
- package/dist/navegador/protocolo.d.ts +4 -0
- package/dist/navegador/protocolo.js +55 -0
- package/dist/navegador/protocolo.js.map +1 -0
- package/dist/navegador/webgl.d.ts +24 -0
- package/dist/navegador/webgl.js +47 -0
- package/dist/navegador/webgl.js.map +1 -0
- package/dist/navegador/worker.d.ts +33 -0
- package/dist/navegador/worker.js +47 -0
- package/dist/navegador/worker.js.map +1 -0
- package/dist/paleta.d.ts +53 -0
- package/dist/paleta.js +53 -0
- package/dist/paleta.js.map +1 -0
- package/dist/proyecciones.d.ts +160 -0
- package/dist/proyecciones.js +2 -0
- package/dist/proyecciones.js.map +1 -0
- package/dist/puerto.d.ts +244 -0
- package/dist/puerto.js +2 -0
- package/dist/puerto.js.map +1 -0
- package/dist/sdk.d.ts +111 -0
- package/dist/sdk.js +552 -0
- package/dist/sdk.js.map +1 -0
- package/dist/sincronizacion.d.ts +40 -0
- package/dist/sincronizacion.js +144 -0
- package/dist/sincronizacion.js.map +1 -0
- package/dist/worker-de-maplibre.d.ts +36 -0
- package/dist/worker-de-maplibre.js +37 -0
- package/dist/worker-de-maplibre.js.map +1 -0
- package/package.json +59 -0
package/dist/sdk.js
ADDED
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
import { cajaDeCoordenadas, coleccionVacia, } from "@cerca.red/geo";
|
|
2
|
+
import { CAPAS, ANCLAS_POR_DEFECTO, FUENTES, } from "./identificadores.js";
|
|
3
|
+
import { MARGENES_POR_DEFECTO, modoParaPlan, planearEncuadre, } from "./encuadre.js";
|
|
4
|
+
import { EmisorDeEventos, } from "./eventos.js";
|
|
5
|
+
import { cajaDeNegocio, cajaDelModelo, gruposDelModelo, modeloInicial, negociosDelModelo, } from "./modelo.js";
|
|
6
|
+
import { resolverPaleta } from "./paleta.js";
|
|
7
|
+
import { aplicarCambios, fuentesQueCambiaron, montarCapaExtra, montarSobreElEstilo, } from "./sincronizacion.js";
|
|
8
|
+
/** Las capas que se consultan para saber qué se tocó, de arriba hacia abajo. */
|
|
9
|
+
const CAPAS_TOCABLES = {
|
|
10
|
+
negocios: [CAPAS.negociosToque, CAPAS.negociosPunto, CAPAS.negociosSimbolo],
|
|
11
|
+
// El área de una ubicación aproximada es tocable: no hay alfiler que tocar,
|
|
12
|
+
// y un negocio que no se puede seleccionar por ser aproximado sería peor
|
|
13
|
+
// que dibujarlo mal.
|
|
14
|
+
imprecision: [CAPAS.imprecision],
|
|
15
|
+
gruposLocales: [CAPAS.gruposToque, CAPAS.gruposCirculo, CAPAS.gruposCantidad],
|
|
16
|
+
gruposDelServidor: [
|
|
17
|
+
CAPAS.gruposServidorToque,
|
|
18
|
+
CAPAS.gruposServidorCirculo,
|
|
19
|
+
CAPAS.gruposServidorCantidad,
|
|
20
|
+
],
|
|
21
|
+
sedes: [CAPAS.sedesToque, CAPAS.sedesPunto],
|
|
22
|
+
vehiculos: [CAPAS.vehiculosToque, CAPAS.vehiculos],
|
|
23
|
+
rutas: [CAPAS.rutasToque],
|
|
24
|
+
};
|
|
25
|
+
/** Todas las capas que reciben toques, de arriba hacia abajo. */
|
|
26
|
+
const TODAS_LAS_TOCABLES = [
|
|
27
|
+
...CAPAS_TOCABLES.gruposDelServidor,
|
|
28
|
+
...CAPAS_TOCABLES.gruposLocales,
|
|
29
|
+
...CAPAS_TOCABLES.negocios,
|
|
30
|
+
...CAPAS_TOCABLES.sedes,
|
|
31
|
+
...CAPAS_TOCABLES.vehiculos,
|
|
32
|
+
...CAPAS_TOCABLES.imprecision,
|
|
33
|
+
...CAPAS_TOCABLES.rutas,
|
|
34
|
+
];
|
|
35
|
+
/**
|
|
36
|
+
* Crea el SDK sobre un motor ya construido.
|
|
37
|
+
*
|
|
38
|
+
* Las superficies no llaman a esto: llaman a `crearMapa` de
|
|
39
|
+
* `@cerca.red/mapa/navegador`, que construye el motor de MapLibre y llama
|
|
40
|
+
* aquí. Esta puerta existe para las pruebas y para cualquier motor futuro.
|
|
41
|
+
*/
|
|
42
|
+
export function crearMapaConMotor(motor, opciones) {
|
|
43
|
+
return new MapaDeCercaSobrePuerto(motor, opciones);
|
|
44
|
+
}
|
|
45
|
+
class MapaDeCercaSobrePuerto {
|
|
46
|
+
#motor;
|
|
47
|
+
#opciones;
|
|
48
|
+
#emisor = new EmisorDeEventos();
|
|
49
|
+
#anclas;
|
|
50
|
+
#suscripciones = [];
|
|
51
|
+
#capasExtra = [];
|
|
52
|
+
#simbolos = new Map();
|
|
53
|
+
#modelo;
|
|
54
|
+
#paleta;
|
|
55
|
+
#destruido = false;
|
|
56
|
+
#montado = false;
|
|
57
|
+
/**
|
|
58
|
+
* Si `mapa:listo` ya salió.
|
|
59
|
+
*
|
|
60
|
+
* Es una bandera aparte de `#montado` porque este vuelve a `false` en
|
|
61
|
+
* cada cambio de tema —el estilo nuevo hay que montarlo entero otra vez— y
|
|
62
|
+
* usarlo para decidir si avisar haría que `mapa:listo` se emitiera también
|
|
63
|
+
* al cambiar de tema. Quien lo escucha para pedir datos los pediría otra
|
|
64
|
+
* vez, cada vez.
|
|
65
|
+
*/
|
|
66
|
+
#yaAvisoListo = false;
|
|
67
|
+
#ultimoEncima;
|
|
68
|
+
constructor(motor, opciones) {
|
|
69
|
+
this.#motor = motor;
|
|
70
|
+
this.#opciones = opciones;
|
|
71
|
+
this.#anclas = opciones.anclas ?? ANCLAS_POR_DEFECTO;
|
|
72
|
+
this.#modelo = modeloInicial({
|
|
73
|
+
tema: opciones.tema ?? "claro",
|
|
74
|
+
idioma: opciones.idioma ?? "es",
|
|
75
|
+
margenes: opciones.margenes ?? MARGENES_POR_DEFECTO,
|
|
76
|
+
...(opciones.radioDeImprecisionEnMetros === undefined
|
|
77
|
+
? {}
|
|
78
|
+
: { radioDeImprecisionEnMetros: opciones.radioDeImprecisionEnMetros }),
|
|
79
|
+
});
|
|
80
|
+
this.#paleta = resolverPaleta(this.#modelo.tema, opciones.colores);
|
|
81
|
+
for (const simbolo of opciones.simbolos ?? []) {
|
|
82
|
+
this.#simbolos.set(simbolo.id, simbolo);
|
|
83
|
+
}
|
|
84
|
+
this.#emisor.alFallar((error, tipo) => {
|
|
85
|
+
this.#emisor.emitir("mapa:error", {
|
|
86
|
+
error: new Error(`un manejador de "${tipo}" lanzó: ${error.message}`, {
|
|
87
|
+
cause: error,
|
|
88
|
+
}),
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
this.#suscribirAlMotor();
|
|
92
|
+
if (opciones.estiloYaAplicado === true) {
|
|
93
|
+
// El motor nació con el estilo puesto. Solo falta montar lo de Cerca, y
|
|
94
|
+
// eso pasa cuando el estilo avise —o ya, si avisó antes de llegar aquí—.
|
|
95
|
+
if (this.#motor.estaListo()) {
|
|
96
|
+
this.#montar();
|
|
97
|
+
this.#yaAvisoListo = true;
|
|
98
|
+
queueMicrotask(() => {
|
|
99
|
+
if (this.#destruido)
|
|
100
|
+
return;
|
|
101
|
+
this.#emisor.emitir("mapa:listo", { vista: this.#motor.vista() });
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
this.#aplicarEstilo();
|
|
107
|
+
}
|
|
108
|
+
this.#motor.ponerMargenes(this.#modelo.margenes);
|
|
109
|
+
if (opciones.cajaMaxima !== undefined) {
|
|
110
|
+
this.#motor.ponerCajaMaxima(opciones.cajaMaxima);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// — datos —
|
|
114
|
+
ponerResultados(resultados) {
|
|
115
|
+
this.#exigirVivo("ponerResultados");
|
|
116
|
+
this.#ponerResultados(resultados);
|
|
117
|
+
}
|
|
118
|
+
ponerNegocios(negocios) {
|
|
119
|
+
this.#exigirVivo("ponerNegocios");
|
|
120
|
+
this.#ponerResultados({ modo: "negocios", negocios });
|
|
121
|
+
}
|
|
122
|
+
#ponerResultados(resultados) {
|
|
123
|
+
const negocios = resultados.modo === "negocios" ? resultados.negocios : [];
|
|
124
|
+
const sigueExistiendo = negocios.some((negocio) => negocio.id === this.#modelo.seleccionado);
|
|
125
|
+
this.#actualizar({
|
|
126
|
+
resultados,
|
|
127
|
+
// Un negocio que desapareció de la lista no puede seguir seleccionado:
|
|
128
|
+
// la fuente de selección quedaría con un punto que no está en ninguna
|
|
129
|
+
// otra capa, y el usuario vería un alfiler naranja suelto.
|
|
130
|
+
seleccionado: sigueExistiendo ? this.#modelo.seleccionado : undefined,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
seleccionarNegocio(id, opciones = {}) {
|
|
134
|
+
this.#exigirVivo("seleccionarNegocio");
|
|
135
|
+
const negocio = id === undefined
|
|
136
|
+
? undefined
|
|
137
|
+
: negociosDelModelo(this.#modelo).find((cada) => cada.id === id);
|
|
138
|
+
if (id !== undefined && negocio === undefined) {
|
|
139
|
+
this.#emisor.emitir("mapa:error", {
|
|
140
|
+
error: new Error(`seleccionarNegocio: no hay ningún negocio con id "${id}" entre los ` +
|
|
141
|
+
`${negociosDelModelo(this.#modelo).length} que tiene el mapa. La selección ` +
|
|
142
|
+
`no cambió.`),
|
|
143
|
+
});
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
this.#actualizar({ seleccionado: id });
|
|
147
|
+
if (opciones.encuadrar === true && negocio !== undefined) {
|
|
148
|
+
this.encuadrar(cajaDeNegocio(negocio) ?? [], { origen: "codigo" });
|
|
149
|
+
}
|
|
150
|
+
if (opciones.emitirEvento === true && negocio !== undefined) {
|
|
151
|
+
this.#emisor.emitir("negocio:clic", {
|
|
152
|
+
id: negocio.id,
|
|
153
|
+
ubicacion: negocio.ubicacion,
|
|
154
|
+
desdePuntero: false,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
ponerUbicacionDeUsuario(ubicacion) {
|
|
159
|
+
this.#exigirVivo("ponerUbicacionDeUsuario");
|
|
160
|
+
this.#actualizar({ ubicacion });
|
|
161
|
+
this.#emisor.emitir("ubicacion:cambio", {
|
|
162
|
+
ubicacion: ubicacion?.ubicacion,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
ponerVehiculos(vehiculos) {
|
|
166
|
+
this.#exigirVivo("ponerVehiculos");
|
|
167
|
+
this.#actualizar({ vehiculos });
|
|
168
|
+
}
|
|
169
|
+
moverVehiculo(id, ubicacion, rumbo) {
|
|
170
|
+
this.#exigirVivo("moverVehiculo");
|
|
171
|
+
let encontrado = false;
|
|
172
|
+
const vehiculos = this.#modelo.vehiculos.map((vehiculo) => {
|
|
173
|
+
if (vehiculo.id !== id)
|
|
174
|
+
return vehiculo;
|
|
175
|
+
encontrado = true;
|
|
176
|
+
return {
|
|
177
|
+
...vehiculo,
|
|
178
|
+
ubicacion,
|
|
179
|
+
...(rumbo === undefined ? {} : { rumbo }),
|
|
180
|
+
};
|
|
181
|
+
});
|
|
182
|
+
if (!encontrado) {
|
|
183
|
+
this.#emisor.emitir("mapa:error", {
|
|
184
|
+
error: new Error(`moverVehiculo: no hay ningún vehículo con id "${id}". Para añadir ` +
|
|
185
|
+
`uno se usa ponerVehiculos.`),
|
|
186
|
+
});
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
this.#actualizar({ vehiculos });
|
|
190
|
+
}
|
|
191
|
+
mostrarRuta(rutas) {
|
|
192
|
+
this.#exigirVivo("mostrarRuta");
|
|
193
|
+
this.#actualizar({ rutas });
|
|
194
|
+
}
|
|
195
|
+
mostrarZonaDeCobertura(zonas) {
|
|
196
|
+
this.#exigirVivo("mostrarZonaDeCobertura");
|
|
197
|
+
this.#actualizar({ zonas });
|
|
198
|
+
}
|
|
199
|
+
mostrarIsocrona(bandas) {
|
|
200
|
+
this.#exigirVivo("mostrarIsocrona");
|
|
201
|
+
this.#actualizar({ isocronas: bandas });
|
|
202
|
+
}
|
|
203
|
+
// — cámara —
|
|
204
|
+
encuadrar(que = "todo", opciones = {}) {
|
|
205
|
+
this.#exigirVivo("encuadrar");
|
|
206
|
+
const caja = this.#resolverCaja(que);
|
|
207
|
+
const plan = planearEncuadre(caja, this.#motor.vista(), {
|
|
208
|
+
...(opciones.zoomMaximo === undefined
|
|
209
|
+
? {}
|
|
210
|
+
: { zoomMaximo: opciones.zoomMaximo }),
|
|
211
|
+
});
|
|
212
|
+
const comunes = {
|
|
213
|
+
origen: opciones.origen ?? "codigo",
|
|
214
|
+
modo: opciones.modo ?? modoParaPlan(plan),
|
|
215
|
+
...(opciones.duracionEnMs === undefined
|
|
216
|
+
? {}
|
|
217
|
+
: { duracionEnMs: opciones.duracionEnMs }),
|
|
218
|
+
};
|
|
219
|
+
if (plan.tipo === "no-mover")
|
|
220
|
+
return;
|
|
221
|
+
if (plan.tipo === "camara") {
|
|
222
|
+
this.#motor.moverCamara(plan.destino, comunes);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
this.#motor.encuadrarCaja(plan.caja, {
|
|
226
|
+
...comunes,
|
|
227
|
+
margenes: opciones.margenes ?? this.#modelo.margenes,
|
|
228
|
+
...(opciones.zoomMaximo === undefined
|
|
229
|
+
? {}
|
|
230
|
+
: { zoomMaximo: opciones.zoomMaximo }),
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
volarA(destino, opciones = {}) {
|
|
234
|
+
this.#exigirVivo("volarA");
|
|
235
|
+
this.#motor.moverCamara(destino, {
|
|
236
|
+
origen: opciones.origen ?? "codigo",
|
|
237
|
+
modo: opciones.modo ?? "vuelo",
|
|
238
|
+
...(opciones.duracionEnMs === undefined
|
|
239
|
+
? {}
|
|
240
|
+
: { duracionEnMs: opciones.duracionEnMs }),
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
vista() {
|
|
244
|
+
this.#exigirVivo("vista");
|
|
245
|
+
return this.#motor.vista();
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* De coordenada a píxel del canvas.
|
|
249
|
+
*
|
|
250
|
+
* Existe porque una superficie que quiera poner algo de HTML encima del
|
|
251
|
+
* mapa —una tarjeta sobre el negocio elegido, una etiqueta— necesita saber
|
|
252
|
+
* dónde cae. Y porque es la única forma de comprobar un objetivo táctil
|
|
253
|
+
* sobre un canvas, donde no hay ningún elemento del DOM cuya caja medir: se
|
|
254
|
+
* calcula el punto y se toca dentro y fuera del radio esperado.
|
|
255
|
+
*/
|
|
256
|
+
proyectar(coordenada) {
|
|
257
|
+
this.#exigirVivo("proyectar");
|
|
258
|
+
return this.#motor.proyectar(coordenada);
|
|
259
|
+
}
|
|
260
|
+
desproyectar(punto) {
|
|
261
|
+
this.#exigirVivo("desproyectar");
|
|
262
|
+
return this.#motor.desproyectar(punto);
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Qué hay dibujado bajo un punto.
|
|
266
|
+
*
|
|
267
|
+
* Sin `capas`, mira todas las que el SDK dibuja y son tocables. Lee el
|
|
268
|
+
* índice de entidades en CPU, no el framebuffer: dice qué está ahí y no de
|
|
269
|
+
* qué color se ve, que es lo único que se puede afirmar sin comparar
|
|
270
|
+
* píxeles.
|
|
271
|
+
*/
|
|
272
|
+
queHayEn(punto, capas) {
|
|
273
|
+
this.#exigirVivo("queHayEn");
|
|
274
|
+
return this.#motor.entidadesEn(punto, capas ?? TODAS_LAS_TOCABLES);
|
|
275
|
+
}
|
|
276
|
+
ponerMargenes(margenes) {
|
|
277
|
+
this.#exigirVivo("ponerMargenes");
|
|
278
|
+
this.#modelo = { ...this.#modelo, margenes };
|
|
279
|
+
this.#motor.ponerMargenes(margenes);
|
|
280
|
+
}
|
|
281
|
+
// — apariencia —
|
|
282
|
+
ponerTema(tema) {
|
|
283
|
+
this.#exigirVivo("ponerTema");
|
|
284
|
+
if (tema === this.#modelo.tema)
|
|
285
|
+
return;
|
|
286
|
+
this.#modelo = { ...this.#modelo, tema };
|
|
287
|
+
this.#paleta = resolverPaleta(tema, this.#opciones.colores);
|
|
288
|
+
this.#aplicarEstilo();
|
|
289
|
+
}
|
|
290
|
+
ponerIdioma(idioma) {
|
|
291
|
+
this.#exigirVivo("ponerIdioma");
|
|
292
|
+
if (idioma === this.#modelo.idioma)
|
|
293
|
+
return;
|
|
294
|
+
this.#modelo = { ...this.#modelo, idioma };
|
|
295
|
+
this.#aplicarEstilo();
|
|
296
|
+
}
|
|
297
|
+
registrarSimbolos(simbolos) {
|
|
298
|
+
this.#exigirVivo("registrarSimbolos");
|
|
299
|
+
for (const simbolo of simbolos)
|
|
300
|
+
this.#simbolos.set(simbolo.id, simbolo);
|
|
301
|
+
if (this.#motor.estaListo())
|
|
302
|
+
this.#registrarImagenes();
|
|
303
|
+
}
|
|
304
|
+
agregarCapaExtra(extra) {
|
|
305
|
+
this.#exigirVivo("agregarCapaExtra");
|
|
306
|
+
this.#capasExtra.push(extra);
|
|
307
|
+
if (this.#motor.estaListo())
|
|
308
|
+
montarCapaExtra(this.#motor, extra);
|
|
309
|
+
return () => {
|
|
310
|
+
const posicion = this.#capasExtra.indexOf(extra);
|
|
311
|
+
if (posicion !== -1)
|
|
312
|
+
this.#capasExtra.splice(posicion, 1);
|
|
313
|
+
if (!this.#destruido && this.#motor.tieneCapa(extra.capa.id)) {
|
|
314
|
+
this.#motor.quitarCapa(extra.capa.id);
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
// — eventos —
|
|
319
|
+
cuando(tipo, manejador) {
|
|
320
|
+
this.#exigirVivo("cuando");
|
|
321
|
+
const cancelar = this.#emisor.cuando(tipo, manejador);
|
|
322
|
+
// `mapa:listo` es el único evento que puede haber pasado **antes** de que
|
|
323
|
+
// alguien tuviera ocasión de suscribirse: MapLibre puede dar el estilo por
|
|
324
|
+
// cargado dentro del propio constructor, y quien llamó a `crearMapa`
|
|
325
|
+
// todavía no tiene el objeto para llamar a `cuando`. Sin esto, el
|
|
326
|
+
// manejador no se ejecuta nunca y lo que se ve es un mapa que funciona
|
|
327
|
+
// pero una pantalla que se queda en «cargando» para siempre.
|
|
328
|
+
//
|
|
329
|
+
// Se avisa en un microtask y no aquí mismo para que `cuando` no pueda
|
|
330
|
+
// reentrar en quien lo está llamando.
|
|
331
|
+
if (tipo === "mapa:listo" && this.#yaAvisoListo) {
|
|
332
|
+
const avisar = manejador;
|
|
333
|
+
queueMicrotask(() => {
|
|
334
|
+
if (this.#destruido)
|
|
335
|
+
return;
|
|
336
|
+
avisar({ vista: this.#motor.vista() });
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
return cancelar;
|
|
340
|
+
}
|
|
341
|
+
unaVez(tipo, manejador) {
|
|
342
|
+
this.#exigirVivo("unaVez");
|
|
343
|
+
return this.#emisor.unaVez(tipo, manejador);
|
|
344
|
+
}
|
|
345
|
+
// — ciclo de vida —
|
|
346
|
+
destruir() {
|
|
347
|
+
if (this.#destruido)
|
|
348
|
+
return;
|
|
349
|
+
this.#destruido = true;
|
|
350
|
+
for (const desuscribir of this.#suscripciones.splice(0))
|
|
351
|
+
desuscribir();
|
|
352
|
+
this.#emisor.quitarTodos();
|
|
353
|
+
this.#capasExtra.length = 0;
|
|
354
|
+
this.#motor.destruir();
|
|
355
|
+
}
|
|
356
|
+
estaDestruido() {
|
|
357
|
+
return this.#destruido;
|
|
358
|
+
}
|
|
359
|
+
// — lo interno —
|
|
360
|
+
#exigirVivo(metodo) {
|
|
361
|
+
if (!this.#destruido)
|
|
362
|
+
return;
|
|
363
|
+
throw new Error(`${metodo}: este mapa ya fue destruido. Llamarlo después de destruir ` +
|
|
364
|
+
`no hace nada útil y suele significar que un componente se desmontó ` +
|
|
365
|
+
`pero algo suyo sigue vivo: una respuesta de red que llegó tarde, un ` +
|
|
366
|
+
`temporizador, una suscripción sin cancelar. Falla aquí para que se ` +
|
|
367
|
+
`vea dónde.`);
|
|
368
|
+
}
|
|
369
|
+
#actualizar(cambios) {
|
|
370
|
+
const anterior = this.#modelo;
|
|
371
|
+
this.#modelo = { ...anterior, ...cambios };
|
|
372
|
+
if (!this.#montado)
|
|
373
|
+
return;
|
|
374
|
+
aplicarCambios(this.#motor, this.#modelo, fuentesQueCambiaron(anterior, this.#modelo));
|
|
375
|
+
}
|
|
376
|
+
#aplicarEstilo() {
|
|
377
|
+
this.#montado = false;
|
|
378
|
+
this.#motor.ponerEstilo(this.#opciones.estilo({
|
|
379
|
+
urlDeTeselas: this.#opciones.urlDeTeselas,
|
|
380
|
+
tema: this.#modelo.tema,
|
|
381
|
+
idioma: this.#modelo.idioma,
|
|
382
|
+
}));
|
|
383
|
+
// `ponerEstilo` de un motor ya cargado emite `estilo:listo` de inmediato
|
|
384
|
+
// y el montaje pasa por ahí. Con el motor todavía cargando, se monta
|
|
385
|
+
// cuando avise.
|
|
386
|
+
if (this.#motor.estaListo() && !this.#montado)
|
|
387
|
+
this.#montar();
|
|
388
|
+
}
|
|
389
|
+
#montar() {
|
|
390
|
+
this.#registrarImagenes();
|
|
391
|
+
montarSobreElEstilo(this.#motor, this.#modelo, this.#paleta, this.#anclas, this.#capasExtra);
|
|
392
|
+
this.#montado = true;
|
|
393
|
+
}
|
|
394
|
+
#registrarImagenes() {
|
|
395
|
+
for (const simbolo of this.#simbolos.values()) {
|
|
396
|
+
if (this.#motor.tieneImagen(simbolo.id))
|
|
397
|
+
continue;
|
|
398
|
+
this.#motor.agregarImagen(simbolo.id, {
|
|
399
|
+
ancho: simbolo.ancho,
|
|
400
|
+
alto: simbolo.alto,
|
|
401
|
+
datos: simbolo.datos,
|
|
402
|
+
...(simbolo.sdf === undefined ? {} : { sdf: simbolo.sdf }),
|
|
403
|
+
...(simbolo.escalaDePixeles === undefined
|
|
404
|
+
? {}
|
|
405
|
+
: { escalaDePixeles: simbolo.escalaDePixeles }),
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
#resolverCaja(que) {
|
|
410
|
+
if (que === "todo" ||
|
|
411
|
+
que === "grupos" ||
|
|
412
|
+
que === "negocios" ||
|
|
413
|
+
que === "rutas" ||
|
|
414
|
+
que === "zonas") {
|
|
415
|
+
return cajaDelModelo(this.#modelo, que);
|
|
416
|
+
}
|
|
417
|
+
if (esCaja4(que))
|
|
418
|
+
return que;
|
|
419
|
+
return cajaDeCoordenadas(que);
|
|
420
|
+
}
|
|
421
|
+
#suscribirAlMotor() {
|
|
422
|
+
this.#suscripciones.push(this.#motor.al("estilo:listo", () => {
|
|
423
|
+
this.#montar();
|
|
424
|
+
if (this.#yaAvisoListo)
|
|
425
|
+
return;
|
|
426
|
+
this.#yaAvisoListo = true;
|
|
427
|
+
this.#emisor.emitir("mapa:listo", { vista: this.#motor.vista() });
|
|
428
|
+
}), this.#motor.al("movimiento", (evento) => {
|
|
429
|
+
this.#emisor.emitir("mapa:movimiento", evento);
|
|
430
|
+
}), this.#motor.al("zoom", (evento) => {
|
|
431
|
+
this.#emisor.emitir("mapa:zoom", evento);
|
|
432
|
+
}), this.#motor.al("error", ({ error }) => {
|
|
433
|
+
this.#emisor.emitir("mapa:error", { error });
|
|
434
|
+
}), this.#motor.al("clic", (evento) => {
|
|
435
|
+
this.#emisor.emitir("mapa:clic", evento);
|
|
436
|
+
}), this.#motor.alDeCapa("clic", [CAPAS.gruposServidorToque], ({ entidades, desdePuntero }) => {
|
|
437
|
+
const id = textoDe(entidades[0]?.propiedades["id"]);
|
|
438
|
+
if (id === undefined)
|
|
439
|
+
return;
|
|
440
|
+
const grupo = gruposDelModelo(this.#modelo).find((cada) => cada.id === id);
|
|
441
|
+
if (grupo === undefined)
|
|
442
|
+
return;
|
|
443
|
+
this.#emisor.emitir("grupo:clic", {
|
|
444
|
+
id,
|
|
445
|
+
cantidad: grupo.cantidad,
|
|
446
|
+
ubicacion: grupo.ubicacion,
|
|
447
|
+
caja: grupo.caja,
|
|
448
|
+
origen: "servidor",
|
|
449
|
+
desdePuntero,
|
|
450
|
+
});
|
|
451
|
+
this.encuadrar(grupo.caja, { origen: "codigo" });
|
|
452
|
+
}), this.#motor.alDeCapa("clic", [CAPAS.gruposToque], ({ entidades, coordenada, desdePuntero }) => {
|
|
453
|
+
if (this.#modelo.resultados.modo !== "negocios")
|
|
454
|
+
return;
|
|
455
|
+
const propiedades = entidades[0]?.propiedades;
|
|
456
|
+
const cantidad = numeroDe(propiedades?.["point_count"]);
|
|
457
|
+
const id = identificadorDe(propiedades?.["cluster_id"] ?? entidades[0]?.id);
|
|
458
|
+
if (cantidad === undefined || id === undefined)
|
|
459
|
+
return;
|
|
460
|
+
this.#emisor.emitir("grupo:clic", {
|
|
461
|
+
id,
|
|
462
|
+
cantidad,
|
|
463
|
+
ubicacion: coordenada,
|
|
464
|
+
origen: "local",
|
|
465
|
+
desdePuntero,
|
|
466
|
+
});
|
|
467
|
+
// Acercarse un nivel y medio expande casi siempre el grupo sin
|
|
468
|
+
// pasarse de largo. Es lo que espera quien toca un grupo, así que
|
|
469
|
+
// el SDK lo hace por su cuenta y el evento queda para lo que la
|
|
470
|
+
// superficie quiera hacer además.
|
|
471
|
+
this.#motor.moverCamara({ centro: coordenada, zoom: this.#motor.vista().zoom + 1.5 }, { origen: "codigo", modo: "suave" });
|
|
472
|
+
}), this.#motor.alDeCapa("clic", [...CAPAS_TOCABLES.negocios, ...CAPAS_TOCABLES.imprecision], ({ entidades, desdePuntero }) => {
|
|
473
|
+
const id = textoDe(entidades[0]?.propiedades["id"]);
|
|
474
|
+
if (id === undefined)
|
|
475
|
+
return;
|
|
476
|
+
const negocio = negociosDelModelo(this.#modelo).find((cada) => cada.id === id);
|
|
477
|
+
if (negocio === undefined)
|
|
478
|
+
return;
|
|
479
|
+
this.#actualizar({ seleccionado: id });
|
|
480
|
+
this.#emisor.emitir("negocio:clic", {
|
|
481
|
+
id,
|
|
482
|
+
ubicacion: negocio.ubicacion,
|
|
483
|
+
desdePuntero,
|
|
484
|
+
});
|
|
485
|
+
}), this.#motor.alDeCapa("clic", [...CAPAS_TOCABLES.sedes], ({ entidades, coordenada, desdePuntero }) => {
|
|
486
|
+
const propiedades = entidades[0]?.propiedades;
|
|
487
|
+
const id = textoDe(propiedades?.["id"]);
|
|
488
|
+
const negocio = textoDe(propiedades?.["negocio"]);
|
|
489
|
+
if (id === undefined || negocio === undefined)
|
|
490
|
+
return;
|
|
491
|
+
this.#emisor.emitir("sede:clic", {
|
|
492
|
+
id,
|
|
493
|
+
negocio,
|
|
494
|
+
ubicacion: coordenada,
|
|
495
|
+
desdePuntero,
|
|
496
|
+
});
|
|
497
|
+
}), this.#motor.alDeCapa("clic", [...CAPAS_TOCABLES.vehiculos], ({ entidades, coordenada, desdePuntero }) => {
|
|
498
|
+
const id = textoDe(entidades[0]?.propiedades["id"]);
|
|
499
|
+
if (id === undefined)
|
|
500
|
+
return;
|
|
501
|
+
this.#emisor.emitir("vehiculo:clic", {
|
|
502
|
+
id,
|
|
503
|
+
ubicacion: coordenada,
|
|
504
|
+
desdePuntero,
|
|
505
|
+
});
|
|
506
|
+
}), this.#motor.alDeCapa("clic", [...CAPAS_TOCABLES.rutas], ({ entidades, desdePuntero }) => {
|
|
507
|
+
const id = textoDe(entidades[0]?.propiedades["id"]);
|
|
508
|
+
if (id === undefined)
|
|
509
|
+
return;
|
|
510
|
+
this.#emisor.emitir("ruta:eleccion", { id, desdePuntero });
|
|
511
|
+
}), this.#motor.alDeCapa("encima", [...CAPAS_TOCABLES.negocios], ({ entidades }) => {
|
|
512
|
+
const id = textoDe(entidades[0]?.propiedades["id"]);
|
|
513
|
+
if (id === this.#ultimoEncima)
|
|
514
|
+
return;
|
|
515
|
+
if (this.#ultimoEncima !== undefined) {
|
|
516
|
+
this.#motor.limpiarEstadosDeEntidad(FUENTES.negocios);
|
|
517
|
+
}
|
|
518
|
+
this.#ultimoEncima = id;
|
|
519
|
+
if (id !== undefined) {
|
|
520
|
+
this.#motor.ponerEstadoDeEntidad(FUENTES.negocios, id, {
|
|
521
|
+
encima: true,
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
this.#emisor.emitir("negocio:encima", { id });
|
|
525
|
+
}), this.#motor.alDeCapa("fuera", [...CAPAS_TOCABLES.negocios], () => {
|
|
526
|
+
if (this.#ultimoEncima === undefined)
|
|
527
|
+
return;
|
|
528
|
+
this.#motor.limpiarEstadosDeEntidad(FUENTES.negocios);
|
|
529
|
+
this.#ultimoEncima = undefined;
|
|
530
|
+
this.#emisor.emitir("negocio:encima", { id: undefined });
|
|
531
|
+
}));
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
function esCaja4(valor) {
|
|
535
|
+
return (Array.isArray(valor) &&
|
|
536
|
+
valor.length === 4 &&
|
|
537
|
+
valor.every((n) => typeof n === "number"));
|
|
538
|
+
}
|
|
539
|
+
function textoDe(valor) {
|
|
540
|
+
return typeof valor === "string" ? valor : undefined;
|
|
541
|
+
}
|
|
542
|
+
function numeroDe(valor) {
|
|
543
|
+
return typeof valor === "number" ? valor : undefined;
|
|
544
|
+
}
|
|
545
|
+
function identificadorDe(valor) {
|
|
546
|
+
return typeof valor === "string" || typeof valor === "number"
|
|
547
|
+
? String(valor)
|
|
548
|
+
: undefined;
|
|
549
|
+
}
|
|
550
|
+
/** Reexportado para que quien construya una capa extra vacía no invente la forma. */
|
|
551
|
+
export { coleccionVacia };
|
|
552
|
+
//# sourceMappingURL=sdk.js.map
|
package/dist/sdk.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk.js","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type Caja,\n type Coordenada,\n cajaDeCoordenadas,\n coleccionVacia,\n} from \"@cerca.red/geo\";\n\nimport {\n CAPAS,\n type Anclas,\n ANCLAS_POR_DEFECTO,\n FUENTES,\n} from \"./identificadores.ts\";\nimport {\n MARGENES_POR_DEFECTO,\n modoParaPlan,\n planearEncuadre,\n} from \"./encuadre.ts\";\nimport {\n EmisorDeEventos,\n type EventosDelMapa,\n type TipoDeEvento,\n} from \"./eventos.ts\";\nimport type { FabricaDeEstilo, Tema } from \"./estilo.ts\";\nimport {\n type ModeloDelMapa,\n cajaDeNegocio,\n cajaDelModelo,\n gruposDelModelo,\n modeloInicial,\n negociosDelModelo,\n} from \"./modelo.ts\";\nimport { type PaletaDelMapa, resolverPaleta } from \"./paleta.ts\";\nimport type {\n Desuscribir,\n DestinoDeCamara,\n EntidadDibujada,\n MapaNativo,\n Margenes,\n OpcionesDeEncuadre,\n OpcionesDeMovimiento,\n PuntoEnPantalla,\n} from \"./puerto.ts\";\nimport type {\n BandaDeIsocrona,\n NegocioEnMapa,\n ResultadosEnMapa,\n RutaEnMapa,\n SimboloDeMapa,\n UbicacionDeUsuarioEnMapa,\n VehiculoEnMapa,\n ZonaEnMapa,\n} from \"./proyecciones.ts\";\nimport {\n type CapaExtra,\n aplicarCambios,\n fuentesQueCambiaron,\n montarCapaExtra,\n montarSobreElEstilo,\n} from \"./sincronizacion.ts\";\n\n/**\n * El SDK del mapa de Cerca, construido sobre el puerto.\n *\n * Este archivo no sabe qué es MapLibre. Recibe un `MapaNativo` ya construido\n * —el adaptador de `navegador/`, o el doble de `doble/`— y encima pone el\n * modelo, los eventos y las decisiones que no se deben repetir en cada\n * superficie.\n */\n\nexport interface OpcionesDeCrearMapa {\n /** Quien sabe construir el estilo. Función pura: mismas opciones, mismo documento. */\n readonly estilo: FabricaDeEstilo;\n /**\n * La URL del archivo de teselas, **tal como vino del manifiesto**.\n *\n * No se compone aquí ni en ninguna superficie: ADR-024 puso las URLs\n * absolutas en el manifiesto precisamente para que nadie las construya, y\n * eso es lo que hace que promover un puntero a una versión anterior revierta\n * de verdad en todas partes.\n */\n readonly urlDeTeselas: string;\n readonly tema?: Tema;\n readonly idioma?: string;\n /**\n * Los identificadores de las capas vacías donde se insertan las de Cerca.\n *\n * El valor por defecto son los que emite hoy `@cerca.red/mapa-estilos`, y\n * `anclas-del-estilo.test.ts` ejecuta ese paquete para comprobar que sigue\n * emitiéndolos. Quien use otro proveedor de estilo pasa los suyos.\n */\n readonly anclas?: Anclas;\n readonly colores?: Partial<PaletaDelMapa>;\n readonly margenes?: Margenes;\n readonly radioDeImprecisionEnMetros?: number;\n /** Hasta dónde se deja navegar. Normalmente, la caja de la región del manifiesto. */\n readonly cajaMaxima?: Caja;\n readonly simbolos?: readonly SimboloDeMapa[];\n /**\n * `true` si el motor ya nació con el estilo que produce `estilo(...)`.\n *\n * Lo usa `crearMapa` de la subruta del navegador, que construye el mapa de\n * MapLibre con el estilo ya calculado. Sin esta bandera el SDK lo pondría\n * otra vez en el constructor: MapLibre avisa por consola —«Style is not\n * done loading, rebuilding from scratch»— y vuelve a descargar los glifos,\n * que son la petición más pesada de la primera pintura.\n *\n * No es una opción para las superficies: es el cable entre las dos mitades\n * de la construcción.\n */\n readonly estiloYaAplicado?: boolean;\n}\n\n/** Qué encuadrar. */\nexport type QueEncuadrar =\n | Caja\n | readonly Coordenada[]\n | \"todo\"\n | \"grupos\"\n | \"negocios\"\n | \"rutas\"\n | \"zonas\";\n\nexport interface OpcionesDeSeleccion {\n /**\n * Si seleccionar además emite `negocio:clic`. **Por defecto no**, y esa es\n * la decisión que rompe el bucle lista↔mapa.\n *\n * La pantalla de resultados escucha `negocio:clic` para abrir la ficha. Si\n * `seleccionarNegocio` lo emitiera, tocar un resultado en la lista abriría\n * la ficha dos veces: una por el toque en la lista y otra por el eco del\n * mapa. Quien de verdad quiera el eco lo pide.\n */\n readonly emitirEvento?: boolean;\n /** Si además se lleva la cámara al negocio. Por defecto no. */\n readonly encuadrar?: boolean;\n}\n\nexport interface MapaDeCerca {\n ponerResultados(resultados: ResultadosEnMapa): void;\n ponerNegocios(negocios: readonly NegocioEnMapa[]): void;\n seleccionarNegocio(\n id: string | undefined,\n opciones?: OpcionesDeSeleccion,\n ): void;\n ponerUbicacionDeUsuario(\n ubicacion: UbicacionDeUsuarioEnMapa | undefined,\n ): void;\n ponerVehiculos(vehiculos: readonly VehiculoEnMapa[]): void;\n moverVehiculo(id: string, ubicacion: Coordenada, rumbo?: number): void;\n mostrarRuta(rutas: readonly RutaEnMapa[]): void;\n mostrarZonaDeCobertura(zonas: readonly ZonaEnMapa[]): void;\n mostrarIsocrona(bandas: readonly BandaDeIsocrona[]): void;\n encuadrar(que?: QueEncuadrar, opciones?: Partial<OpcionesDeEncuadre>): void;\n volarA(\n destino: DestinoDeCamara,\n opciones?: Partial<OpcionesDeMovimiento>,\n ): void;\n vista(): ReturnType<MapaNativo[\"vista\"]>;\n proyectar(coordenada: Coordenada): PuntoEnPantalla;\n desproyectar(punto: PuntoEnPantalla): Coordenada;\n queHayEn(\n punto: PuntoEnPantalla,\n capas?: readonly string[],\n ): readonly EntidadDibujada[];\n ponerTema(tema: Tema): void;\n ponerIdioma(idioma: string): void;\n registrarSimbolos(simbolos: readonly SimboloDeMapa[]): void;\n ponerMargenes(margenes: Margenes): void;\n agregarCapaExtra(extra: CapaExtra): Desuscribir;\n cuando<T extends TipoDeEvento>(\n tipo: T,\n manejador: (evento: EventosDelMapa[T]) => void,\n ): Desuscribir;\n unaVez<T extends TipoDeEvento>(\n tipo: T,\n manejador: (evento: EventosDelMapa[T]) => void,\n ): Desuscribir;\n destruir(): void;\n estaDestruido(): boolean;\n}\n\n/** Las capas que se consultan para saber qué se tocó, de arriba hacia abajo. */\nconst CAPAS_TOCABLES = {\n negocios: [CAPAS.negociosToque, CAPAS.negociosPunto, CAPAS.negociosSimbolo],\n // El área de una ubicación aproximada es tocable: no hay alfiler que tocar,\n // y un negocio que no se puede seleccionar por ser aproximado sería peor\n // que dibujarlo mal.\n imprecision: [CAPAS.imprecision],\n gruposLocales: [CAPAS.gruposToque, CAPAS.gruposCirculo, CAPAS.gruposCantidad],\n gruposDelServidor: [\n CAPAS.gruposServidorToque,\n CAPAS.gruposServidorCirculo,\n CAPAS.gruposServidorCantidad,\n ],\n sedes: [CAPAS.sedesToque, CAPAS.sedesPunto],\n vehiculos: [CAPAS.vehiculosToque, CAPAS.vehiculos],\n rutas: [CAPAS.rutasToque],\n} as const;\n\n/** Todas las capas que reciben toques, de arriba hacia abajo. */\nconst TODAS_LAS_TOCABLES: readonly string[] = [\n ...CAPAS_TOCABLES.gruposDelServidor,\n ...CAPAS_TOCABLES.gruposLocales,\n ...CAPAS_TOCABLES.negocios,\n ...CAPAS_TOCABLES.sedes,\n ...CAPAS_TOCABLES.vehiculos,\n ...CAPAS_TOCABLES.imprecision,\n ...CAPAS_TOCABLES.rutas,\n];\n\n/**\n * Crea el SDK sobre un motor ya construido.\n *\n * Las superficies no llaman a esto: llaman a `crearMapa` de\n * `@cerca.red/mapa/navegador`, que construye el motor de MapLibre y llama\n * aquí. Esta puerta existe para las pruebas y para cualquier motor futuro.\n */\nexport function crearMapaConMotor(\n motor: MapaNativo,\n opciones: OpcionesDeCrearMapa,\n): MapaDeCerca {\n return new MapaDeCercaSobrePuerto(motor, opciones);\n}\n\nclass MapaDeCercaSobrePuerto implements MapaDeCerca {\n readonly #motor: MapaNativo;\n readonly #opciones: OpcionesDeCrearMapa;\n readonly #emisor = new EmisorDeEventos();\n readonly #anclas: Anclas;\n readonly #suscripciones: Desuscribir[] = [];\n readonly #capasExtra: CapaExtra[] = [];\n readonly #simbolos = new Map<string, SimboloDeMapa>();\n\n #modelo: ModeloDelMapa;\n #paleta: PaletaDelMapa;\n #destruido = false;\n #montado = false;\n /**\n * Si `mapa:listo` ya salió.\n *\n * Es una bandera aparte de `#montado` porque este vuelve a `false` en\n * cada cambio de tema —el estilo nuevo hay que montarlo entero otra vez— y\n * usarlo para decidir si avisar haría que `mapa:listo` se emitiera también\n * al cambiar de tema. Quien lo escucha para pedir datos los pediría otra\n * vez, cada vez.\n */\n #yaAvisoListo = false;\n #ultimoEncima: string | undefined;\n\n constructor(motor: MapaNativo, opciones: OpcionesDeCrearMapa) {\n this.#motor = motor;\n this.#opciones = opciones;\n this.#anclas = opciones.anclas ?? ANCLAS_POR_DEFECTO;\n this.#modelo = modeloInicial({\n tema: opciones.tema ?? \"claro\",\n idioma: opciones.idioma ?? \"es\",\n margenes: opciones.margenes ?? MARGENES_POR_DEFECTO,\n ...(opciones.radioDeImprecisionEnMetros === undefined\n ? {}\n : { radioDeImprecisionEnMetros: opciones.radioDeImprecisionEnMetros }),\n });\n this.#paleta = resolverPaleta(this.#modelo.tema, opciones.colores);\n\n for (const simbolo of opciones.simbolos ?? []) {\n this.#simbolos.set(simbolo.id, simbolo);\n }\n\n this.#emisor.alFallar((error, tipo) => {\n this.#emisor.emitir(\"mapa:error\", {\n error: new Error(`un manejador de \"${tipo}\" lanzó: ${error.message}`, {\n cause: error,\n }),\n });\n });\n\n this.#suscribirAlMotor();\n if (opciones.estiloYaAplicado === true) {\n // El motor nació con el estilo puesto. Solo falta montar lo de Cerca, y\n // eso pasa cuando el estilo avise —o ya, si avisó antes de llegar aquí—.\n if (this.#motor.estaListo()) {\n this.#montar();\n this.#yaAvisoListo = true;\n queueMicrotask(() => {\n if (this.#destruido) return;\n this.#emisor.emitir(\"mapa:listo\", { vista: this.#motor.vista() });\n });\n }\n } else {\n this.#aplicarEstilo();\n }\n this.#motor.ponerMargenes(this.#modelo.margenes);\n if (opciones.cajaMaxima !== undefined) {\n this.#motor.ponerCajaMaxima(opciones.cajaMaxima);\n }\n }\n\n // — datos —\n\n ponerResultados(resultados: ResultadosEnMapa): void {\n this.#exigirVivo(\"ponerResultados\");\n this.#ponerResultados(resultados);\n }\n\n ponerNegocios(negocios: readonly NegocioEnMapa[]): void {\n this.#exigirVivo(\"ponerNegocios\");\n this.#ponerResultados({ modo: \"negocios\", negocios });\n }\n\n #ponerResultados(resultados: ResultadosEnMapa): void {\n const negocios = resultados.modo === \"negocios\" ? resultados.negocios : [];\n const sigueExistiendo = negocios.some(\n (negocio) => negocio.id === this.#modelo.seleccionado,\n );\n this.#actualizar({\n resultados,\n // Un negocio que desapareció de la lista no puede seguir seleccionado:\n // la fuente de selección quedaría con un punto que no está en ninguna\n // otra capa, y el usuario vería un alfiler naranja suelto.\n seleccionado: sigueExistiendo ? this.#modelo.seleccionado : undefined,\n });\n }\n\n seleccionarNegocio(\n id: string | undefined,\n opciones: OpcionesDeSeleccion = {},\n ): void {\n this.#exigirVivo(\"seleccionarNegocio\");\n const negocio =\n id === undefined\n ? undefined\n : negociosDelModelo(this.#modelo).find((cada) => cada.id === id);\n\n if (id !== undefined && negocio === undefined) {\n this.#emisor.emitir(\"mapa:error\", {\n error: new Error(\n `seleccionarNegocio: no hay ningún negocio con id \"${id}\" entre los ` +\n `${negociosDelModelo(this.#modelo).length} que tiene el mapa. La selección ` +\n `no cambió.`,\n ),\n });\n return;\n }\n\n this.#actualizar({ seleccionado: id });\n\n if (opciones.encuadrar === true && negocio !== undefined) {\n this.encuadrar(cajaDeNegocio(negocio) ?? [], { origen: \"codigo\" });\n }\n\n if (opciones.emitirEvento === true && negocio !== undefined) {\n this.#emisor.emitir(\"negocio:clic\", {\n id: negocio.id,\n ubicacion: negocio.ubicacion,\n desdePuntero: false,\n });\n }\n }\n\n ponerUbicacionDeUsuario(\n ubicacion: UbicacionDeUsuarioEnMapa | undefined,\n ): void {\n this.#exigirVivo(\"ponerUbicacionDeUsuario\");\n this.#actualizar({ ubicacion });\n this.#emisor.emitir(\"ubicacion:cambio\", {\n ubicacion: ubicacion?.ubicacion,\n });\n }\n\n ponerVehiculos(vehiculos: readonly VehiculoEnMapa[]): void {\n this.#exigirVivo(\"ponerVehiculos\");\n this.#actualizar({ vehiculos });\n }\n\n moverVehiculo(id: string, ubicacion: Coordenada, rumbo?: number): void {\n this.#exigirVivo(\"moverVehiculo\");\n let encontrado = false;\n const vehiculos = this.#modelo.vehiculos.map((vehiculo) => {\n if (vehiculo.id !== id) return vehiculo;\n encontrado = true;\n return {\n ...vehiculo,\n ubicacion,\n ...(rumbo === undefined ? {} : { rumbo }),\n };\n });\n\n if (!encontrado) {\n this.#emisor.emitir(\"mapa:error\", {\n error: new Error(\n `moverVehiculo: no hay ningún vehículo con id \"${id}\". Para añadir ` +\n `uno se usa ponerVehiculos.`,\n ),\n });\n return;\n }\n this.#actualizar({ vehiculos });\n }\n\n mostrarRuta(rutas: readonly RutaEnMapa[]): void {\n this.#exigirVivo(\"mostrarRuta\");\n this.#actualizar({ rutas });\n }\n\n mostrarZonaDeCobertura(zonas: readonly ZonaEnMapa[]): void {\n this.#exigirVivo(\"mostrarZonaDeCobertura\");\n this.#actualizar({ zonas });\n }\n\n mostrarIsocrona(bandas: readonly BandaDeIsocrona[]): void {\n this.#exigirVivo(\"mostrarIsocrona\");\n this.#actualizar({ isocronas: bandas });\n }\n\n // — cámara —\n\n encuadrar(\n que: QueEncuadrar = \"todo\",\n opciones: Partial<OpcionesDeEncuadre> = {},\n ): void {\n this.#exigirVivo(\"encuadrar\");\n const caja = this.#resolverCaja(que);\n const plan = planearEncuadre(caja, this.#motor.vista(), {\n ...(opciones.zoomMaximo === undefined\n ? {}\n : { zoomMaximo: opciones.zoomMaximo }),\n });\n\n const comunes = {\n origen: opciones.origen ?? (\"codigo\" as const),\n modo: opciones.modo ?? modoParaPlan(plan),\n ...(opciones.duracionEnMs === undefined\n ? {}\n : { duracionEnMs: opciones.duracionEnMs }),\n };\n\n if (plan.tipo === \"no-mover\") return;\n if (plan.tipo === \"camara\") {\n this.#motor.moverCamara(plan.destino, comunes);\n return;\n }\n this.#motor.encuadrarCaja(plan.caja, {\n ...comunes,\n margenes: opciones.margenes ?? this.#modelo.margenes,\n ...(opciones.zoomMaximo === undefined\n ? {}\n : { zoomMaximo: opciones.zoomMaximo }),\n });\n }\n\n volarA(\n destino: DestinoDeCamara,\n opciones: Partial<OpcionesDeMovimiento> = {},\n ): void {\n this.#exigirVivo(\"volarA\");\n this.#motor.moverCamara(destino, {\n origen: opciones.origen ?? \"codigo\",\n modo: opciones.modo ?? \"vuelo\",\n ...(opciones.duracionEnMs === undefined\n ? {}\n : { duracionEnMs: opciones.duracionEnMs }),\n });\n }\n\n vista(): ReturnType<MapaNativo[\"vista\"]> {\n this.#exigirVivo(\"vista\");\n return this.#motor.vista();\n }\n\n /**\n * De coordenada a píxel del canvas.\n *\n * Existe porque una superficie que quiera poner algo de HTML encima del\n * mapa —una tarjeta sobre el negocio elegido, una etiqueta— necesita saber\n * dónde cae. Y porque es la única forma de comprobar un objetivo táctil\n * sobre un canvas, donde no hay ningún elemento del DOM cuya caja medir: se\n * calcula el punto y se toca dentro y fuera del radio esperado.\n */\n proyectar(coordenada: Coordenada): PuntoEnPantalla {\n this.#exigirVivo(\"proyectar\");\n return this.#motor.proyectar(coordenada);\n }\n\n desproyectar(punto: PuntoEnPantalla): Coordenada {\n this.#exigirVivo(\"desproyectar\");\n return this.#motor.desproyectar(punto);\n }\n\n /**\n * Qué hay dibujado bajo un punto.\n *\n * Sin `capas`, mira todas las que el SDK dibuja y son tocables. Lee el\n * índice de entidades en CPU, no el framebuffer: dice qué está ahí y no de\n * qué color se ve, que es lo único que se puede afirmar sin comparar\n * píxeles.\n */\n queHayEn(\n punto: PuntoEnPantalla,\n capas?: readonly string[],\n ): readonly EntidadDibujada[] {\n this.#exigirVivo(\"queHayEn\");\n return this.#motor.entidadesEn(punto, capas ?? TODAS_LAS_TOCABLES);\n }\n\n ponerMargenes(margenes: Margenes): void {\n this.#exigirVivo(\"ponerMargenes\");\n this.#modelo = { ...this.#modelo, margenes };\n this.#motor.ponerMargenes(margenes);\n }\n\n // — apariencia —\n\n ponerTema(tema: Tema): void {\n this.#exigirVivo(\"ponerTema\");\n if (tema === this.#modelo.tema) return;\n this.#modelo = { ...this.#modelo, tema };\n this.#paleta = resolverPaleta(tema, this.#opciones.colores);\n this.#aplicarEstilo();\n }\n\n ponerIdioma(idioma: string): void {\n this.#exigirVivo(\"ponerIdioma\");\n if (idioma === this.#modelo.idioma) return;\n this.#modelo = { ...this.#modelo, idioma };\n this.#aplicarEstilo();\n }\n\n registrarSimbolos(simbolos: readonly SimboloDeMapa[]): void {\n this.#exigirVivo(\"registrarSimbolos\");\n for (const simbolo of simbolos) this.#simbolos.set(simbolo.id, simbolo);\n if (this.#motor.estaListo()) this.#registrarImagenes();\n }\n\n agregarCapaExtra(extra: CapaExtra): Desuscribir {\n this.#exigirVivo(\"agregarCapaExtra\");\n this.#capasExtra.push(extra);\n if (this.#motor.estaListo()) montarCapaExtra(this.#motor, extra);\n return () => {\n const posicion = this.#capasExtra.indexOf(extra);\n if (posicion !== -1) this.#capasExtra.splice(posicion, 1);\n if (!this.#destruido && this.#motor.tieneCapa(extra.capa.id)) {\n this.#motor.quitarCapa(extra.capa.id);\n }\n };\n }\n\n // — eventos —\n\n cuando<T extends TipoDeEvento>(\n tipo: T,\n manejador: (evento: EventosDelMapa[T]) => void,\n ): Desuscribir {\n this.#exigirVivo(\"cuando\");\n const cancelar = this.#emisor.cuando(tipo, manejador);\n\n // `mapa:listo` es el único evento que puede haber pasado **antes** de que\n // alguien tuviera ocasión de suscribirse: MapLibre puede dar el estilo por\n // cargado dentro del propio constructor, y quien llamó a `crearMapa`\n // todavía no tiene el objeto para llamar a `cuando`. Sin esto, el\n // manejador no se ejecuta nunca y lo que se ve es un mapa que funciona\n // pero una pantalla que se queda en «cargando» para siempre.\n //\n // Se avisa en un microtask y no aquí mismo para que `cuando` no pueda\n // reentrar en quien lo está llamando.\n if (tipo === \"mapa:listo\" && this.#yaAvisoListo) {\n const avisar = manejador as (\n evento: EventosDelMapa[\"mapa:listo\"],\n ) => void;\n queueMicrotask(() => {\n if (this.#destruido) return;\n avisar({ vista: this.#motor.vista() });\n });\n }\n\n return cancelar;\n }\n\n unaVez<T extends TipoDeEvento>(\n tipo: T,\n manejador: (evento: EventosDelMapa[T]) => void,\n ): Desuscribir {\n this.#exigirVivo(\"unaVez\");\n return this.#emisor.unaVez(tipo, manejador);\n }\n\n // — ciclo de vida —\n\n destruir(): void {\n if (this.#destruido) return;\n this.#destruido = true;\n for (const desuscribir of this.#suscripciones.splice(0)) desuscribir();\n this.#emisor.quitarTodos();\n this.#capasExtra.length = 0;\n this.#motor.destruir();\n }\n\n estaDestruido(): boolean {\n return this.#destruido;\n }\n\n // — lo interno —\n\n #exigirVivo(metodo: string): void {\n if (!this.#destruido) return;\n throw new Error(\n `${metodo}: este mapa ya fue destruido. Llamarlo después de destruir ` +\n `no hace nada útil y suele significar que un componente se desmontó ` +\n `pero algo suyo sigue vivo: una respuesta de red que llegó tarde, un ` +\n `temporizador, una suscripción sin cancelar. Falla aquí para que se ` +\n `vea dónde.`,\n );\n }\n\n #actualizar(cambios: Partial<ModeloDelMapa>): void {\n const anterior = this.#modelo;\n this.#modelo = { ...anterior, ...cambios };\n if (!this.#montado) return;\n aplicarCambios(\n this.#motor,\n this.#modelo,\n fuentesQueCambiaron(anterior, this.#modelo),\n );\n }\n\n #aplicarEstilo(): void {\n this.#montado = false;\n this.#motor.ponerEstilo(\n this.#opciones.estilo({\n urlDeTeselas: this.#opciones.urlDeTeselas,\n tema: this.#modelo.tema,\n idioma: this.#modelo.idioma,\n }),\n );\n // `ponerEstilo` de un motor ya cargado emite `estilo:listo` de inmediato\n // y el montaje pasa por ahí. Con el motor todavía cargando, se monta\n // cuando avise.\n if (this.#motor.estaListo() && !this.#montado) this.#montar();\n }\n\n #montar(): void {\n this.#registrarImagenes();\n montarSobreElEstilo(\n this.#motor,\n this.#modelo,\n this.#paleta,\n this.#anclas,\n this.#capasExtra,\n );\n this.#montado = true;\n }\n\n #registrarImagenes(): void {\n for (const simbolo of this.#simbolos.values()) {\n if (this.#motor.tieneImagen(simbolo.id)) continue;\n this.#motor.agregarImagen(simbolo.id, {\n ancho: simbolo.ancho,\n alto: simbolo.alto,\n datos: simbolo.datos,\n ...(simbolo.sdf === undefined ? {} : { sdf: simbolo.sdf }),\n ...(simbolo.escalaDePixeles === undefined\n ? {}\n : { escalaDePixeles: simbolo.escalaDePixeles }),\n });\n }\n }\n\n #resolverCaja(que: QueEncuadrar): Caja | undefined {\n if (\n que === \"todo\" ||\n que === \"grupos\" ||\n que === \"negocios\" ||\n que === \"rutas\" ||\n que === \"zonas\"\n ) {\n return cajaDelModelo(this.#modelo, que);\n }\n if (esCaja4(que)) return que;\n return cajaDeCoordenadas(que);\n }\n\n #suscribirAlMotor(): void {\n this.#suscripciones.push(\n this.#motor.al(\"estilo:listo\", () => {\n this.#montar();\n if (this.#yaAvisoListo) return;\n this.#yaAvisoListo = true;\n this.#emisor.emitir(\"mapa:listo\", { vista: this.#motor.vista() });\n }),\n\n this.#motor.al(\"movimiento\", (evento) => {\n this.#emisor.emitir(\"mapa:movimiento\", evento);\n }),\n\n this.#motor.al(\"zoom\", (evento) => {\n this.#emisor.emitir(\"mapa:zoom\", evento);\n }),\n\n this.#motor.al(\"error\", ({ error }) => {\n this.#emisor.emitir(\"mapa:error\", { error });\n }),\n\n this.#motor.al(\"clic\", (evento) => {\n this.#emisor.emitir(\"mapa:clic\", evento);\n }),\n\n this.#motor.alDeCapa(\n \"clic\",\n [CAPAS.gruposServidorToque],\n ({ entidades, desdePuntero }) => {\n const id = textoDe(entidades[0]?.propiedades[\"id\"]);\n if (id === undefined) return;\n const grupo = gruposDelModelo(this.#modelo).find(\n (cada) => cada.id === id,\n );\n if (grupo === undefined) return;\n this.#emisor.emitir(\"grupo:clic\", {\n id,\n cantidad: grupo.cantidad,\n ubicacion: grupo.ubicacion,\n caja: grupo.caja,\n origen: \"servidor\",\n desdePuntero,\n });\n this.encuadrar(grupo.caja, { origen: \"codigo\" });\n },\n ),\n\n this.#motor.alDeCapa(\n \"clic\",\n [CAPAS.gruposToque],\n ({ entidades, coordenada, desdePuntero }) => {\n if (this.#modelo.resultados.modo !== \"negocios\") return;\n const propiedades = entidades[0]?.propiedades;\n const cantidad = numeroDe(propiedades?.[\"point_count\"]);\n const id = identificadorDe(\n propiedades?.[\"cluster_id\"] ?? entidades[0]?.id,\n );\n if (cantidad === undefined || id === undefined) return;\n this.#emisor.emitir(\"grupo:clic\", {\n id,\n cantidad,\n ubicacion: coordenada,\n origen: \"local\",\n desdePuntero,\n });\n // Acercarse un nivel y medio expande casi siempre el grupo sin\n // pasarse de largo. Es lo que espera quien toca un grupo, así que\n // el SDK lo hace por su cuenta y el evento queda para lo que la\n // superficie quiera hacer además.\n this.#motor.moverCamara(\n { centro: coordenada, zoom: this.#motor.vista().zoom + 1.5 },\n { origen: \"codigo\", modo: \"suave\" },\n );\n },\n ),\n\n this.#motor.alDeCapa(\n \"clic\",\n [...CAPAS_TOCABLES.negocios, ...CAPAS_TOCABLES.imprecision],\n ({ entidades, desdePuntero }) => {\n const id = textoDe(entidades[0]?.propiedades[\"id\"]);\n if (id === undefined) return;\n const negocio = negociosDelModelo(this.#modelo).find(\n (cada) => cada.id === id,\n );\n if (negocio === undefined) return;\n this.#actualizar({ seleccionado: id });\n this.#emisor.emitir(\"negocio:clic\", {\n id,\n ubicacion: negocio.ubicacion,\n desdePuntero,\n });\n },\n ),\n\n this.#motor.alDeCapa(\n \"clic\",\n [...CAPAS_TOCABLES.sedes],\n ({ entidades, coordenada, desdePuntero }) => {\n const propiedades = entidades[0]?.propiedades;\n const id = textoDe(propiedades?.[\"id\"]);\n const negocio = textoDe(propiedades?.[\"negocio\"]);\n if (id === undefined || negocio === undefined) return;\n this.#emisor.emitir(\"sede:clic\", {\n id,\n negocio,\n ubicacion: coordenada,\n desdePuntero,\n });\n },\n ),\n\n this.#motor.alDeCapa(\n \"clic\",\n [...CAPAS_TOCABLES.vehiculos],\n ({ entidades, coordenada, desdePuntero }) => {\n const id = textoDe(entidades[0]?.propiedades[\"id\"]);\n if (id === undefined) return;\n this.#emisor.emitir(\"vehiculo:clic\", {\n id,\n ubicacion: coordenada,\n desdePuntero,\n });\n },\n ),\n\n this.#motor.alDeCapa(\n \"clic\",\n [...CAPAS_TOCABLES.rutas],\n ({ entidades, desdePuntero }) => {\n const id = textoDe(entidades[0]?.propiedades[\"id\"]);\n if (id === undefined) return;\n this.#emisor.emitir(\"ruta:eleccion\", { id, desdePuntero });\n },\n ),\n\n this.#motor.alDeCapa(\n \"encima\",\n [...CAPAS_TOCABLES.negocios],\n ({ entidades }) => {\n const id = textoDe(entidades[0]?.propiedades[\"id\"]);\n if (id === this.#ultimoEncima) return;\n if (this.#ultimoEncima !== undefined) {\n this.#motor.limpiarEstadosDeEntidad(FUENTES.negocios);\n }\n this.#ultimoEncima = id;\n if (id !== undefined) {\n this.#motor.ponerEstadoDeEntidad(FUENTES.negocios, id, {\n encima: true,\n });\n }\n this.#emisor.emitir(\"negocio:encima\", { id });\n },\n ),\n\n this.#motor.alDeCapa(\"fuera\", [...CAPAS_TOCABLES.negocios], () => {\n if (this.#ultimoEncima === undefined) return;\n this.#motor.limpiarEstadosDeEntidad(FUENTES.negocios);\n this.#ultimoEncima = undefined;\n this.#emisor.emitir(\"negocio:encima\", { id: undefined });\n }),\n );\n }\n}\n\nfunction esCaja4(valor: QueEncuadrar): valor is Caja {\n return (\n Array.isArray(valor) &&\n valor.length === 4 &&\n valor.every((n) => typeof n === \"number\")\n );\n}\n\nfunction textoDe(valor: unknown): string | undefined {\n return typeof valor === \"string\" ? valor : undefined;\n}\n\nfunction numeroDe(valor: unknown): number | undefined {\n return typeof valor === \"number\" ? valor : undefined;\n}\n\nfunction identificadorDe(valor: unknown): string | undefined {\n return typeof valor === \"string\" || typeof valor === \"number\"\n ? String(valor)\n : undefined;\n}\n\n/** Reexportado para que quien construya una capa extra vacía no invente la forma. */\nexport { coleccionVacia };\nexport type { CapaExtra };\n"]}
|