@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.
Files changed (68) hide show
  1. package/README.md +133 -0
  2. package/dist/capas.d.ts +29 -0
  3. package/dist/capas.js +489 -0
  4. package/dist/capas.js.map +1 -0
  5. package/dist/doble/estiloFalso.d.ts +19 -0
  6. package/dist/doble/estiloFalso.js +33 -0
  7. package/dist/doble/estiloFalso.js.map +1 -0
  8. package/dist/doble/index.d.ts +11 -0
  9. package/dist/doble/index.js +12 -0
  10. package/dist/doble/index.js.map +1 -0
  11. package/dist/doble/mapaFalso.d.ts +75 -0
  12. package/dist/doble/mapaFalso.js +535 -0
  13. package/dist/doble/mapaFalso.js.map +1 -0
  14. package/dist/encuadre.d.ts +64 -0
  15. package/dist/encuadre.js +83 -0
  16. package/dist/encuadre.js.map +1 -0
  17. package/dist/estilo.d.ts +88 -0
  18. package/dist/estilo.js +23 -0
  19. package/dist/estilo.js.map +1 -0
  20. package/dist/eventos.d.ts +127 -0
  21. package/dist/eventos.js +69 -0
  22. package/dist/eventos.js.map +1 -0
  23. package/dist/identificadores.d.ts +109 -0
  24. package/dist/identificadores.js +104 -0
  25. package/dist/identificadores.js.map +1 -0
  26. package/dist/index.d.ts +37 -0
  27. package/dist/index.js +33 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/manifiesto.d.ts +97 -0
  30. package/dist/manifiesto.js +156 -0
  31. package/dist/manifiesto.js.map +1 -0
  32. package/dist/modelo.d.ts +154 -0
  33. package/dist/modelo.js +227 -0
  34. package/dist/modelo.js.map +1 -0
  35. package/dist/navegador/index.d.ts +70 -0
  36. package/dist/navegador/index.js +132 -0
  37. package/dist/navegador/index.js.map +1 -0
  38. package/dist/navegador/maplibre.d.ts +41 -0
  39. package/dist/navegador/maplibre.js +463 -0
  40. package/dist/navegador/maplibre.js.map +1 -0
  41. package/dist/navegador/protocolo.d.ts +4 -0
  42. package/dist/navegador/protocolo.js +55 -0
  43. package/dist/navegador/protocolo.js.map +1 -0
  44. package/dist/navegador/webgl.d.ts +24 -0
  45. package/dist/navegador/webgl.js +47 -0
  46. package/dist/navegador/webgl.js.map +1 -0
  47. package/dist/navegador/worker.d.ts +33 -0
  48. package/dist/navegador/worker.js +47 -0
  49. package/dist/navegador/worker.js.map +1 -0
  50. package/dist/paleta.d.ts +53 -0
  51. package/dist/paleta.js +53 -0
  52. package/dist/paleta.js.map +1 -0
  53. package/dist/proyecciones.d.ts +160 -0
  54. package/dist/proyecciones.js +2 -0
  55. package/dist/proyecciones.js.map +1 -0
  56. package/dist/puerto.d.ts +244 -0
  57. package/dist/puerto.js +2 -0
  58. package/dist/puerto.js.map +1 -0
  59. package/dist/sdk.d.ts +111 -0
  60. package/dist/sdk.js +552 -0
  61. package/dist/sdk.js.map +1 -0
  62. package/dist/sincronizacion.d.ts +40 -0
  63. package/dist/sincronizacion.js +144 -0
  64. package/dist/sincronizacion.js.map +1 -0
  65. package/dist/worker-de-maplibre.d.ts +36 -0
  66. package/dist/worker-de-maplibre.js +37 -0
  67. package/dist/worker-de-maplibre.js.map +1 -0
  68. package/package.json +59 -0
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Si este navegador puede dibujar el mapa.
3
+ *
4
+ * ## Por qué se pregunta y no se intenta
5
+ *
6
+ * MapLibre 6 **exige WebGL2**. No hay camino alterno por WebGL1: la versión
7
+ * anterior lo tenía y esta no. Cuando no hay contexto, lo que se ve no es un
8
+ * error — es un canvas en blanco, sin excepción y sin mensaje, que es la peor
9
+ * forma de romperse porque parece que el mapa está cargando.
10
+ *
11
+ * SDD §13 pide la degradación mapa→lista, y esta función es lo que la hace
12
+ * posible: se pregunta **antes** de construir nada, y quien pregunta puede
13
+ * enseñar la lista en vez de un rectángulo vacío.
14
+ *
15
+ * El contexto de prueba se libera en el mismo momento. Un navegador tiene un
16
+ * número pequeño de contextos WebGL simultáneos —del orden de 16—, y dejar
17
+ * uno colgado por cada comprobación es como se agota el presupuesto de la
18
+ * pantalla que sí quería dibujar.
19
+ */
20
+ export function hayWebGL2() {
21
+ if (typeof document === "undefined")
22
+ return false;
23
+ try {
24
+ const lienzo = document.createElement("canvas");
25
+ const contexto = lienzo.getContext("webgl2");
26
+ if (contexto === null)
27
+ return false;
28
+ contexto.getExtension("WEBGL_lose_context")?.loseContext();
29
+ return true;
30
+ }
31
+ catch {
32
+ // Un navegador con WebGL deshabilitado por política puede lanzar en vez
33
+ // de devolver null. Para el que pregunta es lo mismo: no hay mapa.
34
+ return false;
35
+ }
36
+ }
37
+ /** El error que lanza `crearMapa` cuando no hay dónde dibujar. */
38
+ export class SinWebGL2 extends Error {
39
+ constructor() {
40
+ super("Este navegador no tiene WebGL2, y MapLibre 6 lo exige sin alternativa. " +
41
+ "Lo que corresponde aquí es enseñar la lista en vez del mapa (SDD §13): " +
42
+ "el componente <Mapa> de @cerca.red/mapa-react lo hace con su prop " +
43
+ "`respaldo`. Se puede preguntar antes con hayWebGL2().");
44
+ this.name = "SinWebGL2";
45
+ }
46
+ }
47
+ //# sourceMappingURL=webgl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webgl.js","sourceRoot":"","sources":["../../src/navegador/webgl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,SAAS;IACvB,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAO,KAAK,CAAC;IAElD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC7C,IAAI,QAAQ,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC;QACpC,QAAQ,CAAC,YAAY,CAAC,oBAAoB,CAAC,EAAE,WAAW,EAAE,CAAC;QAC3D,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,wEAAwE;QACxE,mEAAmE;QACnE,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,kEAAkE;AAClE,MAAM,OAAO,SAAU,SAAQ,KAAK;IAClC;QACE,KAAK,CACH,yEAAyE;YACvE,yEAAyE;YACzE,oEAAoE;YACpE,uDAAuD,CAC1D,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;IAC1B,CAAC;CACF","sourcesContent":["/**\n * Si este navegador puede dibujar el mapa.\n *\n * ## Por qué se pregunta y no se intenta\n *\n * MapLibre 6 **exige WebGL2**. No hay camino alterno por WebGL1: la versión\n * anterior lo tenía y esta no. Cuando no hay contexto, lo que se ve no es un\n * error — es un canvas en blanco, sin excepción y sin mensaje, que es la peor\n * forma de romperse porque parece que el mapa está cargando.\n *\n * SDD §13 pide la degradación mapa→lista, y esta función es lo que la hace\n * posible: se pregunta **antes** de construir nada, y quien pregunta puede\n * enseñar la lista en vez de un rectángulo vacío.\n *\n * El contexto de prueba se libera en el mismo momento. Un navegador tiene un\n * número pequeño de contextos WebGL simultáneos —del orden de 16—, y dejar\n * uno colgado por cada comprobación es como se agota el presupuesto de la\n * pantalla que sí quería dibujar.\n */\nexport function hayWebGL2(): boolean {\n if (typeof document === \"undefined\") return false;\n\n try {\n const lienzo = document.createElement(\"canvas\");\n const contexto = lienzo.getContext(\"webgl2\");\n if (contexto === null) return false;\n contexto.getExtension(\"WEBGL_lose_context\")?.loseContext();\n return true;\n } catch {\n // Un navegador con WebGL deshabilitado por política puede lanzar en vez\n // de devolver null. Para el que pregunta es lo mismo: no hay mapa.\n return false;\n }\n}\n\n/** El error que lanza `crearMapa` cuando no hay dónde dibujar. */\nexport class SinWebGL2 extends Error {\n constructor() {\n super(\n \"Este navegador no tiene WebGL2, y MapLibre 6 lo exige sin alternativa. \" +\n \"Lo que corresponde aquí es enseñar la lista en vez del mapa (SDD §13): \" +\n \"el componente <Mapa> de @cerca.red/mapa-react lo hace con su prop \" +\n \"`respaldo`. Se puede preguntar antes con hayWebGL2().\",\n );\n this.name = \"SinWebGL2\";\n }\n}\n"]}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * De dónde saca MapLibre su Web Worker, y por qué a veces hay que decírselo.
3
+ *
4
+ * MapLibre 6 reparte el motor en dos archivos: `maplibre-gl.mjs`, que corre en
5
+ * el hilo principal, y `maplibre-gl-worker.mjs`, que corre en el worker y
6
+ * descomprime y teselas y geometrías. El segundo no se importa: se carga por
7
+ * URL, y esa URL la deduce el primero de su propio `import.meta.url`.
8
+ *
9
+ * Ahí está el problema. Un bundler que reescriba `import.meta.url` —Next.js
10
+ * lo hace, con Turbopack y con webpack— deja al motor deduciendo la URL del
11
+ * worker a partir de la de la página, y `new Worker(...)` termina pidiendo un
12
+ * archivo que no existe. Con Vite no pasa, y por eso el mismo paquete pinta el
13
+ * mapa en `laboratorio/` y no lo pinta bajo Next.
14
+ *
15
+ * La salida que da MapLibre es `setWorkerUrl`, que fija `config.WORKER_URL` y
16
+ * gana sobre la deducción. Esta función es esa salida con nombre en español y
17
+ * con la única comprobación que hace falta.
18
+ *
19
+ * ## Cuándo surte efecto
20
+ *
21
+ * Leído en `maplibre-gl@6.8.0/dist/maplibre-gl-dev.mjs`: `workerFactory()`
22
+ * (línea 2093) lee `config.WORKER_URL` cada vez que se le llama, y solo se le
23
+ * llama desde `WorkerPool.acquire` (línea 2122) cuando el pool todavía no
24
+ * tiene workers. El pool es único para toda la página, y `release` solo los
25
+ * termina cuando no queda ningún mapa vivo.
26
+ *
27
+ * O sea: **la URL se fija antes del primer mapa**. Cambiarla con un mapa ya
28
+ * construido no mueve nada y tampoco avisa. Por eso `crearMapa` acepta
29
+ * `urlDelWorker` —lo hace en el orden correcto sin que nadie se acuerde— y
30
+ * esta función suelta existe para la superficie que prefiere fijarla una vez
31
+ * al arrancar.
32
+ */
33
+ export declare function fijarUrlDelWorker(url: string): void;
@@ -0,0 +1,47 @@
1
+ import { setWorkerUrl } from "maplibre-gl";
2
+ /**
3
+ * De dónde saca MapLibre su Web Worker, y por qué a veces hay que decírselo.
4
+ *
5
+ * MapLibre 6 reparte el motor en dos archivos: `maplibre-gl.mjs`, que corre en
6
+ * el hilo principal, y `maplibre-gl-worker.mjs`, que corre en el worker y
7
+ * descomprime y teselas y geometrías. El segundo no se importa: se carga por
8
+ * URL, y esa URL la deduce el primero de su propio `import.meta.url`.
9
+ *
10
+ * Ahí está el problema. Un bundler que reescriba `import.meta.url` —Next.js
11
+ * lo hace, con Turbopack y con webpack— deja al motor deduciendo la URL del
12
+ * worker a partir de la de la página, y `new Worker(...)` termina pidiendo un
13
+ * archivo que no existe. Con Vite no pasa, y por eso el mismo paquete pinta el
14
+ * mapa en `laboratorio/` y no lo pinta bajo Next.
15
+ *
16
+ * La salida que da MapLibre es `setWorkerUrl`, que fija `config.WORKER_URL` y
17
+ * gana sobre la deducción. Esta función es esa salida con nombre en español y
18
+ * con la única comprobación que hace falta.
19
+ *
20
+ * ## Cuándo surte efecto
21
+ *
22
+ * Leído en `maplibre-gl@6.8.0/dist/maplibre-gl-dev.mjs`: `workerFactory()`
23
+ * (línea 2093) lee `config.WORKER_URL` cada vez que se le llama, y solo se le
24
+ * llama desde `WorkerPool.acquire` (línea 2122) cuando el pool todavía no
25
+ * tiene workers. El pool es único para toda la página, y `release` solo los
26
+ * termina cuando no queda ningún mapa vivo.
27
+ *
28
+ * O sea: **la URL se fija antes del primer mapa**. Cambiarla con un mapa ya
29
+ * construido no mueve nada y tampoco avisa. Por eso `crearMapa` acepta
30
+ * `urlDelWorker` —lo hace en el orden correcto sin que nadie se acuerde— y
31
+ * esta función suelta existe para la superficie que prefiere fijarla una vez
32
+ * al arrancar.
33
+ */
34
+ export function fijarUrlDelWorker(url) {
35
+ // Una cadena vacía no es «sin configurar»: MapLibre resuelve
36
+ // `config.WORKER_URL || defaultWorkerUrl()`, así que caería otra vez en la
37
+ // deducción por `import.meta.url` — justo lo que esta función existe para
38
+ // evitar. Quien pase una variable de entorno sin definir se llevaría una
39
+ // llamada que devuelve sin quejarse y un mapa que no pinta.
40
+ if (url.trim() === "") {
41
+ throw new Error("fijarUrlDelWorker: la URL del worker de MapLibre llegó vacía. Con " +
42
+ "una cadena vacía MapLibre vuelve a deducirla de `import.meta.url`, " +
43
+ "que es lo que no funciona bajo Next.js.");
44
+ }
45
+ setWorkerUrl(url);
46
+ }
47
+ //# sourceMappingURL=worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/navegador/worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,6DAA6D;IAC7D,2EAA2E;IAC3E,0EAA0E;IAC1E,yEAAyE;IACzE,4DAA4D;IAC5D,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,oEAAoE;YAClE,qEAAqE;YACrE,yCAAyC,CAC5C,CAAC;IACJ,CAAC;IAED,YAAY,CAAC,GAAG,CAAC,CAAC;AACpB,CAAC","sourcesContent":["import { setWorkerUrl } from \"maplibre-gl\";\n\n/**\n * De dónde saca MapLibre su Web Worker, y por qué a veces hay que decírselo.\n *\n * MapLibre 6 reparte el motor en dos archivos: `maplibre-gl.mjs`, que corre en\n * el hilo principal, y `maplibre-gl-worker.mjs`, que corre en el worker y\n * descomprime y teselas y geometrías. El segundo no se importa: se carga por\n * URL, y esa URL la deduce el primero de su propio `import.meta.url`.\n *\n * Ahí está el problema. Un bundler que reescriba `import.meta.url` —Next.js\n * lo hace, con Turbopack y con webpack— deja al motor deduciendo la URL del\n * worker a partir de la de la página, y `new Worker(...)` termina pidiendo un\n * archivo que no existe. Con Vite no pasa, y por eso el mismo paquete pinta el\n * mapa en `laboratorio/` y no lo pinta bajo Next.\n *\n * La salida que da MapLibre es `setWorkerUrl`, que fija `config.WORKER_URL` y\n * gana sobre la deducción. Esta función es esa salida con nombre en español y\n * con la única comprobación que hace falta.\n *\n * ## Cuándo surte efecto\n *\n * Leído en `maplibre-gl@6.8.0/dist/maplibre-gl-dev.mjs`: `workerFactory()`\n * (línea 2093) lee `config.WORKER_URL` cada vez que se le llama, y solo se le\n * llama desde `WorkerPool.acquire` (línea 2122) cuando el pool todavía no\n * tiene workers. El pool es único para toda la página, y `release` solo los\n * termina cuando no queda ningún mapa vivo.\n *\n * O sea: **la URL se fija antes del primer mapa**. Cambiarla con un mapa ya\n * construido no mueve nada y tampoco avisa. Por eso `crearMapa` acepta\n * `urlDelWorker` —lo hace en el orden correcto sin que nadie se acuerde— y\n * esta función suelta existe para la superficie que prefiere fijarla una vez\n * al arrancar.\n */\nexport function fijarUrlDelWorker(url: string): void {\n // Una cadena vacía no es «sin configurar»: MapLibre resuelve\n // `config.WORKER_URL || defaultWorkerUrl()`, así que caería otra vez en la\n // deducción por `import.meta.url` — justo lo que esta función existe para\n // evitar. Quien pase una variable de entorno sin definir se llevaría una\n // llamada que devuelve sin quejarse y un mapa que no pinta.\n if (url.trim() === \"\") {\n throw new Error(\n \"fijarUrlDelWorker: la URL del worker de MapLibre llegó vacía. Con \" +\n \"una cadena vacía MapLibre vuelve a deducirla de `import.meta.url`, \" +\n \"que es lo que no funciona bajo Next.js.\",\n );\n }\n\n setWorkerUrl(url);\n}\n"]}
@@ -0,0 +1,53 @@
1
+ import type { Tema } from "./estilo.ts";
2
+ /**
3
+ * Los colores con los que el SDK dibuja **lo de Cerca**: negocios, rutas,
4
+ * zonas, vehículos, ubicación. Los del mapa base no están aquí — los pone el
5
+ * sabor de `@cerca.red/mapa-estilos`.
6
+ *
7
+ * ## Por qué hay una paleta aquí y no se importa de `packages/ui`
8
+ *
9
+ * Porque `cerca-maps` no comparte `packages/ui` con la PWA, igual que la
10
+ * landing (ADR-021, y ADR-027 lo repite en sus consecuencias «en contra»):
11
+ * los colores del mapa se declaran otra vez y **pueden divergir**. Está
12
+ * asumido a sabiendas, con la salida escrita por si duele —publicar los
13
+ * tokens como paquete—, que es distinto de no haberlo pensado.
14
+ *
15
+ * Mientras tanto, esto es un valor por defecto, no una imposición: `crearMapa`
16
+ * acepta una paleta y la superficie puede pasarle la suya entera o por
17
+ * partes.
18
+ *
19
+ * ## Lo que no se puede negociar: el color no lleva información solo
20
+ *
21
+ * CLAUDE.md §1 pide que nada dependa solo del color, y en un mapa eso es más
22
+ * fácil de incumplir que en un formulario. Por eso los tres énfasis se
23
+ * distinguen **también por tamaño**, la selección lleva **anillo** además de
24
+ * color, y una ubicación aproximada se dibuja **como área con borde
25
+ * discontinuo**, que es una forma distinta y no un tono distinto. Quien
26
+ * cambie estos valores puede cambiar los colores; los tamaños y las formas
27
+ * son la accesibilidad.
28
+ */
29
+ export interface PaletaDelMapa {
30
+ readonly negocio: string;
31
+ readonly negocioDestacado: string;
32
+ readonly negocioAtenuado: string;
33
+ readonly contornoDePunto: string;
34
+ readonly seleccion: string;
35
+ readonly grupo: string;
36
+ readonly textoDeGrupo: string;
37
+ readonly ubicacion: string;
38
+ readonly haloDeUbicacion: string;
39
+ readonly vehiculo: string;
40
+ readonly ruta: string;
41
+ readonly contornoDeRuta: string;
42
+ readonly rutaAlternativa: string;
43
+ readonly zona: string;
44
+ readonly bordeDeZona: string;
45
+ readonly isocrona: string;
46
+ readonly imprecision: string;
47
+ readonly bordeDeImprecision: string;
48
+ readonly rotulo: string;
49
+ readonly haloDeRotulo: string;
50
+ }
51
+ export declare const PALETAS_POR_DEFECTO: Readonly<Record<Tema, PaletaDelMapa>>;
52
+ /** La paleta del tema, con lo que la superficie haya querido cambiar encima. */
53
+ export declare function resolverPaleta(tema: Tema, cambios?: Partial<PaletaDelMapa>): PaletaDelMapa;
package/dist/paleta.js ADDED
@@ -0,0 +1,53 @@
1
+ const CLARA = {
2
+ negocio: "#1f6feb",
3
+ negocioDestacado: "#0b4fc4",
4
+ negocioAtenuado: "#8ba3c7",
5
+ contornoDePunto: "#ffffff",
6
+ seleccion: "#d9480f",
7
+ grupo: "#1f6feb",
8
+ textoDeGrupo: "#ffffff",
9
+ ubicacion: "#0b7285",
10
+ haloDeUbicacion: "#0b7285",
11
+ vehiculo: "#087f5b",
12
+ ruta: "#1f6feb",
13
+ contornoDeRuta: "#ffffff",
14
+ rutaAlternativa: "#94a3b8",
15
+ zona: "#1f6feb",
16
+ bordeDeZona: "#0b4fc4",
17
+ isocrona: "#087f5b",
18
+ imprecision: "#7c4dff",
19
+ bordeDeImprecision: "#5b2fd6",
20
+ rotulo: "#1b2431",
21
+ haloDeRotulo: "#ffffff",
22
+ };
23
+ const OSCURA = {
24
+ negocio: "#79b8ff",
25
+ negocioDestacado: "#c9e2ff",
26
+ negocioAtenuado: "#4a5b74",
27
+ contornoDePunto: "#0d1117",
28
+ seleccion: "#ff9f45",
29
+ grupo: "#79b8ff",
30
+ textoDeGrupo: "#0d1117",
31
+ ubicacion: "#63d3e8",
32
+ haloDeUbicacion: "#63d3e8",
33
+ vehiculo: "#5fd6a8",
34
+ ruta: "#79b8ff",
35
+ contornoDeRuta: "#0d1117",
36
+ rutaAlternativa: "#55637a",
37
+ zona: "#79b8ff",
38
+ bordeDeZona: "#c9e2ff",
39
+ isocrona: "#5fd6a8",
40
+ imprecision: "#b39dff",
41
+ bordeDeImprecision: "#d5c8ff",
42
+ rotulo: "#e6edf3",
43
+ haloDeRotulo: "#0d1117",
44
+ };
45
+ export const PALETAS_POR_DEFECTO = {
46
+ claro: CLARA,
47
+ oscuro: OSCURA,
48
+ };
49
+ /** La paleta del tema, con lo que la superficie haya querido cambiar encima. */
50
+ export function resolverPaleta(tema, cambios) {
51
+ return { ...PALETAS_POR_DEFECTO[tema], ...cambios };
52
+ }
53
+ //# sourceMappingURL=paleta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paleta.js","sourceRoot":"","sources":["../src/paleta.ts"],"names":[],"mappings":"AAoDA,MAAM,KAAK,GAAkB;IAC3B,OAAO,EAAE,SAAS;IAClB,gBAAgB,EAAE,SAAS;IAC3B,eAAe,EAAE,SAAS;IAC1B,eAAe,EAAE,SAAS;IAC1B,SAAS,EAAE,SAAS;IACpB,KAAK,EAAE,SAAS;IAChB,YAAY,EAAE,SAAS;IACvB,SAAS,EAAE,SAAS;IACpB,eAAe,EAAE,SAAS;IAC1B,QAAQ,EAAE,SAAS;IACnB,IAAI,EAAE,SAAS;IACf,cAAc,EAAE,SAAS;IACzB,eAAe,EAAE,SAAS;IAC1B,IAAI,EAAE,SAAS;IACf,WAAW,EAAE,SAAS;IACtB,QAAQ,EAAE,SAAS;IACnB,WAAW,EAAE,SAAS;IACtB,kBAAkB,EAAE,SAAS;IAC7B,MAAM,EAAE,SAAS;IACjB,YAAY,EAAE,SAAS;CACxB,CAAC;AAEF,MAAM,MAAM,GAAkB;IAC5B,OAAO,EAAE,SAAS;IAClB,gBAAgB,EAAE,SAAS;IAC3B,eAAe,EAAE,SAAS;IAC1B,eAAe,EAAE,SAAS;IAC1B,SAAS,EAAE,SAAS;IACpB,KAAK,EAAE,SAAS;IAChB,YAAY,EAAE,SAAS;IACvB,SAAS,EAAE,SAAS;IACpB,eAAe,EAAE,SAAS;IAC1B,QAAQ,EAAE,SAAS;IACnB,IAAI,EAAE,SAAS;IACf,cAAc,EAAE,SAAS;IACzB,eAAe,EAAE,SAAS;IAC1B,IAAI,EAAE,SAAS;IACf,WAAW,EAAE,SAAS;IACtB,QAAQ,EAAE,SAAS;IACnB,WAAW,EAAE,SAAS;IACtB,kBAAkB,EAAE,SAAS;IAC7B,MAAM,EAAE,SAAS;IACjB,YAAY,EAAE,SAAS;CACxB,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAA0C;IACxE,KAAK,EAAE,KAAK;IACZ,MAAM,EAAE,MAAM;CACf,CAAC;AAEF,gFAAgF;AAChF,MAAM,UAAU,cAAc,CAC5B,IAAU,EACV,OAAgC;IAEhC,OAAO,EAAE,GAAG,mBAAmB,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC;AACtD,CAAC","sourcesContent":["import type { Tema } from \"./estilo.ts\";\n\n/**\n * Los colores con los que el SDK dibuja **lo de Cerca**: negocios, rutas,\n * zonas, vehículos, ubicación. Los del mapa base no están aquí — los pone el\n * sabor de `@cerca.red/mapa-estilos`.\n *\n * ## Por qué hay una paleta aquí y no se importa de `packages/ui`\n *\n * Porque `cerca-maps` no comparte `packages/ui` con la PWA, igual que la\n * landing (ADR-021, y ADR-027 lo repite en sus consecuencias «en contra»):\n * los colores del mapa se declaran otra vez y **pueden divergir**. Está\n * asumido a sabiendas, con la salida escrita por si duele —publicar los\n * tokens como paquete—, que es distinto de no haberlo pensado.\n *\n * Mientras tanto, esto es un valor por defecto, no una imposición: `crearMapa`\n * acepta una paleta y la superficie puede pasarle la suya entera o por\n * partes.\n *\n * ## Lo que no se puede negociar: el color no lleva información solo\n *\n * CLAUDE.md §1 pide que nada dependa solo del color, y en un mapa eso es más\n * fácil de incumplir que en un formulario. Por eso los tres énfasis se\n * distinguen **también por tamaño**, la selección lleva **anillo** además de\n * color, y una ubicación aproximada se dibuja **como área con borde\n * discontinuo**, que es una forma distinta y no un tono distinto. Quien\n * cambie estos valores puede cambiar los colores; los tamaños y las formas\n * son la accesibilidad.\n */\nexport interface PaletaDelMapa {\n readonly negocio: string;\n readonly negocioDestacado: string;\n readonly negocioAtenuado: string;\n readonly contornoDePunto: string;\n readonly seleccion: string;\n readonly grupo: string;\n readonly textoDeGrupo: string;\n readonly ubicacion: string;\n readonly haloDeUbicacion: string;\n readonly vehiculo: string;\n readonly ruta: string;\n readonly contornoDeRuta: string;\n readonly rutaAlternativa: string;\n readonly zona: string;\n readonly bordeDeZona: string;\n readonly isocrona: string;\n readonly imprecision: string;\n readonly bordeDeImprecision: string;\n readonly rotulo: string;\n readonly haloDeRotulo: string;\n}\n\nconst CLARA: PaletaDelMapa = {\n negocio: \"#1f6feb\",\n negocioDestacado: \"#0b4fc4\",\n negocioAtenuado: \"#8ba3c7\",\n contornoDePunto: \"#ffffff\",\n seleccion: \"#d9480f\",\n grupo: \"#1f6feb\",\n textoDeGrupo: \"#ffffff\",\n ubicacion: \"#0b7285\",\n haloDeUbicacion: \"#0b7285\",\n vehiculo: \"#087f5b\",\n ruta: \"#1f6feb\",\n contornoDeRuta: \"#ffffff\",\n rutaAlternativa: \"#94a3b8\",\n zona: \"#1f6feb\",\n bordeDeZona: \"#0b4fc4\",\n isocrona: \"#087f5b\",\n imprecision: \"#7c4dff\",\n bordeDeImprecision: \"#5b2fd6\",\n rotulo: \"#1b2431\",\n haloDeRotulo: \"#ffffff\",\n};\n\nconst OSCURA: PaletaDelMapa = {\n negocio: \"#79b8ff\",\n negocioDestacado: \"#c9e2ff\",\n negocioAtenuado: \"#4a5b74\",\n contornoDePunto: \"#0d1117\",\n seleccion: \"#ff9f45\",\n grupo: \"#79b8ff\",\n textoDeGrupo: \"#0d1117\",\n ubicacion: \"#63d3e8\",\n haloDeUbicacion: \"#63d3e8\",\n vehiculo: \"#5fd6a8\",\n ruta: \"#79b8ff\",\n contornoDeRuta: \"#0d1117\",\n rutaAlternativa: \"#55637a\",\n zona: \"#79b8ff\",\n bordeDeZona: \"#c9e2ff\",\n isocrona: \"#5fd6a8\",\n imprecision: \"#b39dff\",\n bordeDeImprecision: \"#d5c8ff\",\n rotulo: \"#e6edf3\",\n haloDeRotulo: \"#0d1117\",\n};\n\nexport const PALETAS_POR_DEFECTO: Readonly<Record<Tema, PaletaDelMapa>> = {\n claro: CLARA,\n oscuro: OSCURA,\n};\n\n/** La paleta del tema, con lo que la superficie haya querido cambiar encima. */\nexport function resolverPaleta(\n tema: Tema,\n cambios?: Partial<PaletaDelMapa>,\n): PaletaDelMapa {\n return { ...PALETAS_POR_DEFECTO[tema], ...cambios };\n}\n"]}
@@ -0,0 +1,160 @@
1
+ import type { Caja, Coordenada, LineaGeoJSON, MultiLineaGeoJSON, MultiPoligonoGeoJSON, PoligonoGeoJSON } from "@cerca.red/geo";
2
+ /**
3
+ * Las proyecciones cartográficas: lo que este SDK sabe dibujar.
4
+ *
5
+ * ## Lo que un `NegocioEnMapa` no es
6
+ *
7
+ * **No es un negocio.** No tiene calificación, ni banda de precio, ni
8
+ * horario, ni categoría, ni si está abierto. Tiene lo que hace falta para
9
+ * ponerlo en un mapa: dónde va, con qué símbolo, con qué rótulo, con cuánto
10
+ * énfasis, y si su ubicación está verificada.
11
+ *
12
+ * Eso no es minimalismo: es la frontera de ADR-027 puesta en un tipo. Cerca
13
+ * decide **qué** se muestra; Cerca Maps decide **cómo** se dibuja. Quien tiene
14
+ * un `FilaResultado` de la API v1 lo traduce a esto antes de entregarlo, y esa
15
+ * traducción vive en la superficie.
16
+ *
17
+ * El ADR también dice cómo muere esta frontera si muere: «no de un salto, sino
18
+ * por acumulación» — proyecciones que crecen campo a campo hasta ser el
19
+ * contrato de la API v1 con otro nombre. Así que la pregunta para cada campo
20
+ * que alguien quiera añadir aquí es una sola: **¿el motor de dibujo lo usa, o
21
+ * lo usa una decisión de producto?** Colorear un alfiler según la calificación
22
+ * es lo segundo: la superficie traduce la calificación a un `enfasis` y
23
+ * entrega eso.
24
+ */
25
+ /**
26
+ * Si la ubicación está verificada o es una estimación.
27
+ *
28
+ * Es un dato que **decide Cerca** —la PWA ya lo distingue en su contrato y su
29
+ * tarjeta dice «no se da por exacta una ubicación que nadie verificó»— y que
30
+ * aquí cambia el dibujo: una aproximada se pinta como **área**, no como
31
+ * alfiler. Un alfiler afirma un punto; un área afirma una zona, que es lo
32
+ * único que se sabe. En una lista esa diferencia es una etiqueta que se puede
33
+ * pasar por alto; en un mapa se ve o no se ve.
34
+ */
35
+ export type PrecisionDeUbicacion = "exacta" | "aproximada";
36
+ /**
37
+ * Cuánto pesa un negocio en el dibujo.
38
+ *
39
+ * Es el campo al que la superficie traduce lo que quiera destacar —una
40
+ * calificación alta, un patrocinio, el resultado que el usuario tiene bajo el
41
+ * dedo en la lista—. El SDK no sabe por qué está destacado, solo lo dibuja
42
+ * más grande.
43
+ */
44
+ export type EnfasisEnMapa = "normal" | "atenuado" | "destacado";
45
+ /** Una sede de un negocio: la misma cosa en otro sitio. */
46
+ export interface SedeEnMapa {
47
+ readonly id: string;
48
+ readonly ubicacion: Coordenada;
49
+ readonly rotulo?: string;
50
+ readonly precision?: PrecisionDeUbicacion;
51
+ }
52
+ /** Un negocio, tal como se dibuja. */
53
+ export interface NegocioEnMapa {
54
+ readonly id: string;
55
+ readonly ubicacion: Coordenada;
56
+ /**
57
+ * El identificador de un símbolo registrado con `registrarSimbolos`.
58
+ *
59
+ * Si no se da, o si el símbolo no está registrado, se dibuja el punto por
60
+ * defecto. **No se cae**: un mapa sin el icono de peluquería sigue
61
+ * mostrando dónde está la peluquería, y ese es el fallo correcto.
62
+ */
63
+ readonly simbolo?: string;
64
+ readonly rotulo?: string;
65
+ readonly enfasis?: EnfasisEnMapa;
66
+ /** Por defecto `"exacta"`. */
67
+ readonly precision?: PrecisionDeUbicacion;
68
+ readonly sedes?: readonly SedeEnMapa[];
69
+ }
70
+ /**
71
+ * Un agregado calculado por el backend consumidor.
72
+ *
73
+ * El SDK no sabe cómo se formó ni qué negocios contiene: solo necesita dónde
74
+ * dibujarlo, cuántos representa y qué caja debe encuadrar al tocarlo.
75
+ */
76
+ export interface GrupoEnMapa {
77
+ readonly id: string;
78
+ readonly cantidad: number;
79
+ readonly ubicacion: Coordenada;
80
+ readonly caja: Caja;
81
+ readonly rotulo?: string;
82
+ }
83
+ /**
84
+ * Los dos modos mutuamente excluyentes de resultados.
85
+ *
86
+ * Los grupos vienen ya agregados por un servidor. Los negocios son puntos
87
+ * individuales sobre los que MapLibre conserva su agrupación visual local.
88
+ */
89
+ export type ResultadosEnMapa = {
90
+ readonly modo: "grupos";
91
+ readonly grupos: readonly GrupoEnMapa[];
92
+ } | {
93
+ readonly modo: "negocios";
94
+ readonly negocios: readonly NegocioEnMapa[];
95
+ };
96
+ /** Dónde está el usuario, según quien se lo haya preguntado al navegador. */
97
+ export interface UbicacionDeUsuarioEnMapa {
98
+ readonly ubicacion: Coordenada;
99
+ /**
100
+ * El radio de incertidumbre que reportó la fuente, en metros.
101
+ *
102
+ * Se dibuja. Un punto azul solo dice «estás aquí»; el mismo punto con su
103
+ * halo dice además «con este margen», que es la diferencia entre confiar en
104
+ * una ubicación de GPS y confiar en una triangulada por antenas.
105
+ */
106
+ readonly precisionEnMetros?: number;
107
+ /** Grados desde el norte. Si viene, el símbolo se orienta. */
108
+ readonly rumbo?: number;
109
+ }
110
+ /** Un vehículo en movimiento: un domiciliario, un transporte. */
111
+ export interface VehiculoEnMapa {
112
+ readonly id: string;
113
+ readonly ubicacion: Coordenada;
114
+ readonly rumbo?: number;
115
+ readonly simbolo?: string;
116
+ readonly rotulo?: string;
117
+ }
118
+ /** Una ruta dibujable. */
119
+ export interface RutaEnMapa {
120
+ readonly id: string;
121
+ readonly geometria: LineaGeoJSON | MultiLineaGeoJSON;
122
+ readonly rotulo?: string;
123
+ /**
124
+ * La ruta elegida. Se dibuja encima y con más peso; las demás quedan como
125
+ * alternativas tocables, y tocarlas emite `ruta:eleccion`.
126
+ *
127
+ * Si ninguna se marca, la primera es la principal. Si se marcan varias, la
128
+ * primera marcada gana: dos rutas principales no significan nada y elegir
129
+ * en silencio es mejor que no dibujar.
130
+ */
131
+ readonly principal?: boolean;
132
+ }
133
+ /** Una zona de cobertura: dónde se reparte, dónde se atiende. */
134
+ export interface ZonaEnMapa {
135
+ readonly id: string;
136
+ readonly geometria: PoligonoGeoJSON | MultiPoligonoGeoJSON;
137
+ readonly rotulo?: string;
138
+ readonly enfasis?: EnfasisEnMapa;
139
+ }
140
+ /** Una banda de isócrona: hasta dónde se llega en `minutos`. */
141
+ export interface BandaDeIsocrona {
142
+ readonly minutos: number;
143
+ readonly geometria: PoligonoGeoJSON | MultiPoligonoGeoJSON;
144
+ }
145
+ /**
146
+ * Un símbolo registrable.
147
+ *
148
+ * `sdf` marca las imágenes de campo de distancia con signo, que son las que
149
+ * se pueden recolorear desde el estilo. Los iconos de Cerca son de trazo, así
150
+ * que encajan, y eso evita generar una imagen por cada combinación de icono y
151
+ * estado.
152
+ */
153
+ export interface SimboloDeMapa {
154
+ readonly id: string;
155
+ readonly ancho: number;
156
+ readonly alto: number;
157
+ readonly datos: Uint8ClampedArray | Uint8Array;
158
+ readonly sdf?: boolean;
159
+ readonly escalaDePixeles?: number;
160
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=proyecciones.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proyecciones.js","sourceRoot":"","sources":["../src/proyecciones.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n Caja,\n Coordenada,\n LineaGeoJSON,\n MultiLineaGeoJSON,\n MultiPoligonoGeoJSON,\n PoligonoGeoJSON,\n} from \"@cerca.red/geo\";\n\n/**\n * Las proyecciones cartográficas: lo que este SDK sabe dibujar.\n *\n * ## Lo que un `NegocioEnMapa` no es\n *\n * **No es un negocio.** No tiene calificación, ni banda de precio, ni\n * horario, ni categoría, ni si está abierto. Tiene lo que hace falta para\n * ponerlo en un mapa: dónde va, con qué símbolo, con qué rótulo, con cuánto\n * énfasis, y si su ubicación está verificada.\n *\n * Eso no es minimalismo: es la frontera de ADR-027 puesta en un tipo. Cerca\n * decide **qué** se muestra; Cerca Maps decide **cómo** se dibuja. Quien tiene\n * un `FilaResultado` de la API v1 lo traduce a esto antes de entregarlo, y esa\n * traducción vive en la superficie.\n *\n * El ADR también dice cómo muere esta frontera si muere: «no de un salto, sino\n * por acumulación» — proyecciones que crecen campo a campo hasta ser el\n * contrato de la API v1 con otro nombre. Así que la pregunta para cada campo\n * que alguien quiera añadir aquí es una sola: **¿el motor de dibujo lo usa, o\n * lo usa una decisión de producto?** Colorear un alfiler según la calificación\n * es lo segundo: la superficie traduce la calificación a un `enfasis` y\n * entrega eso.\n */\n\n/**\n * Si la ubicación está verificada o es una estimación.\n *\n * Es un dato que **decide Cerca** —la PWA ya lo distingue en su contrato y su\n * tarjeta dice «no se da por exacta una ubicación que nadie verificó»— y que\n * aquí cambia el dibujo: una aproximada se pinta como **área**, no como\n * alfiler. Un alfiler afirma un punto; un área afirma una zona, que es lo\n * único que se sabe. En una lista esa diferencia es una etiqueta que se puede\n * pasar por alto; en un mapa se ve o no se ve.\n */\nexport type PrecisionDeUbicacion = \"exacta\" | \"aproximada\";\n\n/**\n * Cuánto pesa un negocio en el dibujo.\n *\n * Es el campo al que la superficie traduce lo que quiera destacar —una\n * calificación alta, un patrocinio, el resultado que el usuario tiene bajo el\n * dedo en la lista—. El SDK no sabe por qué está destacado, solo lo dibuja\n * más grande.\n */\nexport type EnfasisEnMapa = \"normal\" | \"atenuado\" | \"destacado\";\n\n/** Una sede de un negocio: la misma cosa en otro sitio. */\nexport interface SedeEnMapa {\n readonly id: string;\n readonly ubicacion: Coordenada;\n readonly rotulo?: string;\n readonly precision?: PrecisionDeUbicacion;\n}\n\n/** Un negocio, tal como se dibuja. */\nexport interface NegocioEnMapa {\n readonly id: string;\n readonly ubicacion: Coordenada;\n /**\n * El identificador de un símbolo registrado con `registrarSimbolos`.\n *\n * Si no se da, o si el símbolo no está registrado, se dibuja el punto por\n * defecto. **No se cae**: un mapa sin el icono de peluquería sigue\n * mostrando dónde está la peluquería, y ese es el fallo correcto.\n */\n readonly simbolo?: string;\n readonly rotulo?: string;\n readonly enfasis?: EnfasisEnMapa;\n /** Por defecto `\"exacta\"`. */\n readonly precision?: PrecisionDeUbicacion;\n readonly sedes?: readonly SedeEnMapa[];\n}\n\n/**\n * Un agregado calculado por el backend consumidor.\n *\n * El SDK no sabe cómo se formó ni qué negocios contiene: solo necesita dónde\n * dibujarlo, cuántos representa y qué caja debe encuadrar al tocarlo.\n */\nexport interface GrupoEnMapa {\n readonly id: string;\n readonly cantidad: number;\n readonly ubicacion: Coordenada;\n readonly caja: Caja;\n readonly rotulo?: string;\n}\n\n/**\n * Los dos modos mutuamente excluyentes de resultados.\n *\n * Los grupos vienen ya agregados por un servidor. Los negocios son puntos\n * individuales sobre los que MapLibre conserva su agrupación visual local.\n */\nexport type ResultadosEnMapa =\n | {\n readonly modo: \"grupos\";\n readonly grupos: readonly GrupoEnMapa[];\n }\n | {\n readonly modo: \"negocios\";\n readonly negocios: readonly NegocioEnMapa[];\n };\n\n/** Dónde está el usuario, según quien se lo haya preguntado al navegador. */\nexport interface UbicacionDeUsuarioEnMapa {\n readonly ubicacion: Coordenada;\n /**\n * El radio de incertidumbre que reportó la fuente, en metros.\n *\n * Se dibuja. Un punto azul solo dice «estás aquí»; el mismo punto con su\n * halo dice además «con este margen», que es la diferencia entre confiar en\n * una ubicación de GPS y confiar en una triangulada por antenas.\n */\n readonly precisionEnMetros?: number;\n /** Grados desde el norte. Si viene, el símbolo se orienta. */\n readonly rumbo?: number;\n}\n\n/** Un vehículo en movimiento: un domiciliario, un transporte. */\nexport interface VehiculoEnMapa {\n readonly id: string;\n readonly ubicacion: Coordenada;\n readonly rumbo?: number;\n readonly simbolo?: string;\n readonly rotulo?: string;\n}\n\n/** Una ruta dibujable. */\nexport interface RutaEnMapa {\n readonly id: string;\n readonly geometria: LineaGeoJSON | MultiLineaGeoJSON;\n readonly rotulo?: string;\n /**\n * La ruta elegida. Se dibuja encima y con más peso; las demás quedan como\n * alternativas tocables, y tocarlas emite `ruta:eleccion`.\n *\n * Si ninguna se marca, la primera es la principal. Si se marcan varias, la\n * primera marcada gana: dos rutas principales no significan nada y elegir\n * en silencio es mejor que no dibujar.\n */\n readonly principal?: boolean;\n}\n\n/** Una zona de cobertura: dónde se reparte, dónde se atiende. */\nexport interface ZonaEnMapa {\n readonly id: string;\n readonly geometria: PoligonoGeoJSON | MultiPoligonoGeoJSON;\n readonly rotulo?: string;\n readonly enfasis?: EnfasisEnMapa;\n}\n\n/** Una banda de isócrona: hasta dónde se llega en `minutos`. */\nexport interface BandaDeIsocrona {\n readonly minutos: number;\n readonly geometria: PoligonoGeoJSON | MultiPoligonoGeoJSON;\n}\n\n/**\n * Un símbolo registrable.\n *\n * `sdf` marca las imágenes de campo de distancia con signo, que son las que\n * se pueden recolorear desde el estilo. Los iconos de Cerca son de trazo, así\n * que encajan, y eso evita generar una imagen por cada combinación de icono y\n * estado.\n */\nexport interface SimboloDeMapa {\n readonly id: string;\n readonly ancho: number;\n readonly alto: number;\n readonly datos: Uint8ClampedArray | Uint8Array;\n readonly sdf?: boolean;\n readonly escalaDePixeles?: number;\n}\n"]}