multinom_mapa 1.1.0 → 1.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 CHANGED
@@ -1,379 +1,381 @@
1
- # multinom_mapa
2
-
3
- Web Component `<multinom-mapa>`: mapa de los 2,457 municipios de México coloreado por un KPI de 0 a 100 o por cantidades, con vista nacional por entidad, detalle por municipio, panel de resumen y búsqueda por coordenada. Incluye además **código postal y colonia** de las 32 entidades, que solo entran al build de tu app si las importas.
4
-
5
- No depende de ningún framework. Funciona en **cualquier versión de Angular** (y en React, Vue o HTML plano), porque el navegador lo trata como una etiqueta nativa.
6
-
7
- - Modo `kpi` (por omisión): el valor de cada entidad (y el nacional) es el promedio de sus municipios **ponderado por población 2020**. Valores fuera de 0–100 se recortan; vacíos o no numéricos cuentan como "sin dato".
8
- - Modo `conteo`: cantidades (obras, quejas, visitas…) con escala de 0 al máximo visible; entidad y nacional son la **suma**.
9
- - La geometría (~2.9 MB) se carga **diferida**, una sola vez por página, y no entra a tu bundle principal.
10
-
11
- ---
12
-
13
- ## Instalación
14
-
15
- El paquete es público en npmjs.com como **`multinom_mapa`**. La etiqueta HTML que registra es `<multinom-mapa>`. Se instala como cualquier otra librería, sin sesión ni token:
16
-
17
- ```bash
18
- npm install multinom_mapa
19
- ```
20
-
21
- ---
22
-
23
- ## Uso en Angular
24
-
25
- ### 1. Registrar el elemento
26
-
27
- Importa el paquete una vez, en `main.ts`:
28
-
29
- ```ts
30
- import 'multinom_mapa';
31
- ```
32
-
33
- ### 2. Permitir la etiqueta en las plantillas
34
-
35
- **Componentes standalone** (Angular 15+):
36
-
37
- ```ts
38
- import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
39
-
40
- @Component({
41
- selector: 'app-tablero',
42
- standalone: true,
43
- schemas: [CUSTOM_ELEMENTS_SCHEMA],
44
- templateUrl: './tablero.component.html',
45
- })
46
- export class TableroComponent {}
47
- ```
48
-
49
- **Con NgModule** (cualquier versión):
50
-
51
- ```ts
52
- import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
53
-
54
- @NgModule({
55
- // ...
56
- schemas: [CUSTOM_ELEMENTS_SCHEMA],
57
- })
58
- export class AppModule {}
59
- ```
60
-
61
- ### 3. Usarlo
62
-
63
- ```html
64
- <multinom-mapa
65
- titulo="Satisfacción por municipio"
66
- nombre-kpi="Satisfacción"
67
- [kpi]="valores"
68
- (seleccion)="alSeleccionar($event)"
69
- (nivel-cambio)="alCambiarNivel($event)">
70
- </multinom-mapa>
71
- ```
72
-
73
- ```ts
74
- import type { DetalleSeleccion, DetalleNivel } from 'multinom_mapa';
75
-
76
- export class TableroComponent {
77
- valores: Record<string, number> = { '22014': 78.5, '22011': 41 };
78
-
79
- alSeleccionar(e: Event) {
80
- const d = (e as CustomEvent<DetalleSeleccion | null>).detail;
81
- console.log(d?.municipio, d?.kpi);
82
- }
83
- alCambiarNivel(e: Event) {
84
- const d = (e as CustomEvent<DetalleNivel>).detail;
85
- console.log(d.nivel, d.entidad);
86
- }
87
- }
88
- ```
89
-
90
- Cada vez que asignes un objeto nuevo a `valores`, el mapa se actualiza. Puedes pasarle datos antes de que termine de cargar la geometría: los guarda y los aplica al terminar.
91
-
92
- ### Llamar métodos desde el componente
93
-
94
- ```ts
95
- import { ElementRef, ViewChild } from '@angular/core';
96
- import type { MapaKpi } from 'multinom_mapa';
97
-
98
- @ViewChild('mapa') mapa!: ElementRef<MapaKpi>;
99
-
100
- async ubicarOficina() {
101
- await this.mapa.nativeElement.listo;
102
- const r = this.mapa.nativeElement.localizar(20.5888, -100.3899);
103
- // r = { cve_ent: '22', cve_geo: '22014', entidad: 'Querétaro', municipio: 'Querétaro', kpi: 78.5 }
104
- }
105
- ```
106
-
107
- ```html
108
- <multinom-mapa #mapa></multinom-mapa>
109
- ```
110
-
111
- ### SSR (Angular Universal / `@angular/ssr`)
112
-
113
- Importar el paquete en el servidor no truena: el elemento solo se registra en el navegador. En el servidor la etiqueta queda vacía y se dibuja al hidratar.
114
-
115
- ---
116
-
117
- ## Códigos postales y colonias
118
-
119
- Vienen en el mismo paquete (`npm install multinom_mapa`, una sola instalación). Lo que decide si entran al build de tu app es **qué importas** en `main.ts`:
120
-
121
- ```ts
122
- import 'multinom_mapa'; // estados y municipios (siempre)
123
- import 'multinom_mapa/qro'; // + CP y colonias de Querétaro
124
- import 'multinom_mapa/jal'; // + CP y colonias de Jalisco
125
- import 'multinom_mapa/todo'; // + CP y colonias de las 32 entidades
126
- ```
127
-
128
- Con solo `import 'multinom_mapa'` el build queda igual que sin CP ni colonias. Con una entidad importada, la geometría de cada municipio va en su propio chunk y **solo se descarga cuando se abre ese municipio**: si usas `raiz="22011"`, el navegador descarga únicamente El Marqués. Con `multinom_mapa/todo` el build genera un chunk por municipio (unos 2,450 archivos en `dist/` de tu app), pero el navegador sigue descargando solo los que se abren.
129
-
130
- Abreviaturas: `ags` `bc` `bcs` `camp` `coah` `col` `chis` `chih` `cdmx` `dgo` `gto` `gro` `hgo` `jal` `mex` `mich` `mor` `nay` `nl` `oax` `pue` `qro` `qroo` `slp` `sin` `son` `tab` `tamps` `tlax` `ver` `yuc` `zac`.
131
-
132
- ### Navegación: `raiz` y `niveles`
133
-
134
- México (1) → entidad: sus municipios (2) → municipio: sus CP o colonias (3) → CP: sus colonias (4).
135
-
136
- - `raiz` fija dónde empieza; el usuario no puede subir más arriba.
137
- - `niveles` fija hasta dónde puede bajar (1–4, por omisión 4). Al llegar al último nivel, el clic selecciona en lugar de abrir.
138
-
139
- ```html
140
- <multinom-mapa niveles="2"></multinom-mapa> <!-- México → municipios, sin CP ni colonias -->
141
- <multinom-mapa raiz="22" niveles="2"></multinom-mapa> <!-- Querétaro y sus municipios -->
142
- <multinom-mapa raiz="22"></multinom-mapa> <!-- Querétaro → municipio → CP → colonia -->
143
- <multinom-mapa raiz="22011" niveles="3"></multinom-mapa> <!-- El Marqués por CP o colonias, sin abrir cada CP -->
144
- ```
145
-
146
- Los niveles 3 y 4 además requieren haber importado la entidad (`multinom_mapa/qro`); si no, el mapa se queda en municipios. Si `raiz` es un municipio, `niveles` nunca baja de 3.
147
-
148
- Dentro del municipio hay un selector **CP / Colonias** (atributo `capa-municipio` para el valor inicial). Con colonias se ve todo el municipio de una vez.
149
-
150
- ### Ejemplo: obras públicas en El Marqués
151
-
152
- ```html
153
- <multinom-mapa
154
- raiz="22011" modo="conteo" capa-municipio="colonias"
155
- titulo="Obras públicas" nombre-kpi="Obras"
156
- [kpi]="obrasPorMunicipio"
157
- [kpiCp]="obrasPorCp"
158
- [kpiColonias]="obrasPorColonia"
159
- (seleccion)="alSeleccionar($event)">
160
- </multinom-mapa>
161
- ```
162
-
163
- ```ts
164
- obrasPorMunicipio = { '22011': 47 };
165
- obrasPorCp = { '76246': 12, '76240': 5 };
166
- obrasPorColonia = { '2201100010001': 3 }; // cvegeo de INEGI (13 dígitos)
167
- ```
168
-
169
- Cada nivel muestra **los valores que le pases a ese nivel**; el mapa no los suma de colonias a CP ni de CP a municipio, porque un CP puede cruzar municipios y no todas las colonias están delimitadas. Lo único que agrega es entidad y nacional a partir de municipios. En modo `conteo`, lo que no venga en los datos de un nivel cuenta como 0.
170
-
171
- ### Qué hay que saber de los datos
172
-
173
- - **CP:** polígonos de SEPOMEX, recortados con el límite de cada municipio. Si un CP cruza dos municipios, aparece en ambos (el tooltip dice "También en…").
174
- - **Colonias:** *Delimitación de colonias y otros asentamientos humanos 2025* de INEGI. **No cubre todo el país**: cada municipio entrega la suya, y muchos municipios rurales no tienen ninguna (ahí se ven solo los CP). En Querétaro, por ejemplo, faltan Querétaro capital, San Juan del Río, Colón, Huimilpan y Amealco, y El Marqués tiene 137 colonias (faltan fraccionamientos como Zibatá). Las zonas sin colonia se ven en gris.
175
- - La clave de colonia es el `cvegeo` de INEGI (entidad + municipio + localidad + asentamiento). El CP del atributo de INEGI no siempre coincide con el polígono de SEPOMEX; cada colonia se dibuja dentro del CP donde cae, y el tooltip muestra ambos si difieren.
176
-
177
- ### Regenerar los datos (desarrollo)
178
-
179
- 1. Fuentes en `build/fuentes/` (no se versionan; ~1.4 GB). El script las busca en cualquier subcarpeta:
180
- - SEPOMEX, "Códigos postales por entidad federativa" (datos.gob.mx) → carpetas `CP_<Abrev>` (p. ej. `CP/CP_Qro`).
181
- - INEGI, [Delimitación de colonias y otros asentamientos humanos](https://www.inegi.org.mx/programas/dcah/) → carpetas `<ee>_<nombre>` (p. ej. `DCAH/22_queretaro`).
182
- 2. `npm run region -- 22` (una entidad) o `npm run region -- todas` (las 32; agrega `--forzar` para rehacer las que ya existen) → `build/regiones/<ee>.json` (estos sí se versionan).
183
- 3. `npm run build` → genera `dist/regiones/<abrev>/` y `dist/regiones/todo.js`.
184
-
185
- ---
186
-
187
- ## Referencia
188
-
189
- ### Atributos
190
-
191
- | Atributo | Valor | Qué hace |
192
- |---|---|---|
193
- | `titulo` | texto | Título del encabezado. Por omisión "KPI municipal de México". |
194
- | `nombre-kpi` | texto | Nombre del indicador en la leyenda. Por omisión "KPI". |
195
- | `tema` | `auto` · `claro` · `oscuro` | `auto` (por omisión) sigue el modo del sistema. |
196
- | `sin-encabezado` | — | Oculta el encabezado. La atribución de las fuentes pasa a la leyenda. |
197
- | `sin-panel` | — | Oculta el panel lateral: queda solo el mapa. |
198
- | `sin-localizar` | — | Oculta la tarjeta "Localizar coordenada". |
199
- | `con-carga` | — | Muestra la tarjeta para generar datos aleatorios o cargar un CSV/JSON a mano. |
200
- | `demo` | semilla opcional | Arranca con datos aleatorios reproducibles. |
201
- | `datos-url` | URL | Descarga la geometría desde un JSON en lugar del módulo diferido (ver abajo). |
202
- | `modo` | `kpi` · `conteo` | `kpi` (por omisión): 0–100, semáforo, promedio ponderado. `conteo`: cantidades, escala secuencial, suma. |
203
- | `raiz` | clave de entidad o municipio | Dónde empieza la navegación (`22`, `22011`). Sin él, en México. |
204
- | `niveles` | `1` · `2` · `3` · `4` | Hasta dónde se puede bajar: entidades, municipios, CP/colonias del municipio, colonias del CP. Por omisión `4`. |
205
- | `capa-municipio` | `cp` · `colonias` | Cómo se ve un municipio abierto (requiere importar la entidad). Por omisión `cp`. |
206
-
207
- ### Propiedades
208
-
209
- | Propiedad | Tipo | |
210
- |---|---|---|
211
- | `kpi` | `Record<string, number>` o arreglo de filas | Leer o asignar los valores por municipio. |
212
- | `kpiCp` | igual | Valores por código postal. |
213
- | `kpiColonias` | igual | Valores por colonia (`cvegeo` de 13 dígitos). |
214
- | `modo` | `'kpi' \| 'conteo'` | Solo lectura (refleja el atributo). |
215
- | `niveles` | `1 \| 2 \| 3 \| 4` | Refleja el atributo `niveles`. |
216
- | `colorear` | `boolean` | `false` pinta todo en gris. |
217
- | `listo` | `Promise<void>` | Se resuelve cuando el mapa está dibujado. |
218
-
219
- ### Métodos
220
-
221
- | Método | Devuelve |
222
- |---|---|
223
- | `setKpi(datos, opciones?)` | `{ cargados, ignoradas }`, o `null` si aún carga (se aplica al terminar). `opciones`: texto de origen o `{ nivel: 'municipio' \| 'cp' \| 'colonia', origen }`. |
224
- | `getKpi(nivel?)` | Valores de un nivel (municipio por omisión). |
225
- | `kpiEntidades()` | Valor por entidad (`cve_ent` → valor o `null`). |
226
- | `kpiNacional()` | Valor nacional (ponderado o suma, según el modo). |
227
- | `abrirEntidad(cve_ent)` | Muestra los municipios de una entidad. |
228
- | `abrirMunicipio(cve_geo)` | `Promise`. Abre el municipio con sus CP y colonias (sin la entidad importada o con `niveles` < 3, abre la entidad y lo selecciona). |
229
- | `abrirCp(cp, cve_geo?)` | `Promise`. Abre un CP y muestra sus colonias (con `niveles="3"`, solo lo selecciona). |
230
- | `seleccionar(cve_geo)` | Abre la entidad y selecciona el municipio. |
231
- | `seleccionarColonia(cvegeo)` | `Promise`. Abre municipio y CP de la colonia y la selecciona. |
232
- | `subir()` | Sube un nivel, sin pasar de `raiz`. También con la tecla Escape. |
233
- | `nacional()` | Vuelve a la vista nacional (o a la raíz). |
234
- | `localizar(lat, lon, mover = true)` | `{ cve_ent, cve_geo, entidad, municipio, kpi }` y, si la geometría del municipio ya está cargada, `cp`, `kpi_cp`, `cve_col`, `colonia`, `kpi_colonia`. Con `mover`, navega y marca el punto. |
235
- | `localizarDetalle(lat, lon, mover = true)` | `Promise` con lo mismo, cargando antes la geometría de CP/colonias. |
236
- | `aleatorio(semilla?, huecos?)` | Genera datos de prueba (también para CP y colonias de las entidades importadas). |
237
- | `encuadrarVista()` | Reencuadra la vista actual. |
238
- | `redibujar()` | Vuelve a leer los colores CSS y redibuja. |
239
-
240
- ### Formatos aceptados por `kpi` / `setKpi`
241
-
242
- ```js
243
- { "22014": 78.5, "22011": 41 } // objeto clave → valor
244
- [{ cve_geo: "22014", kpi: 78.5 }, { cve: 22011, valor: 41 }] // filas (cve_geo|cve|clave|cp|cvegeo, kpi|valor|value)
245
- ```
246
-
247
- Las claves se rellenan con ceros a la izquierda (`1001` → `01001`). Para leer un CSV ya tienes `leerCsv(texto)` exportado por el paquete.
248
-
249
- ### Eventos
250
-
251
- Todos son `CustomEvent`, burbujean y salen del Shadow DOM.
252
-
253
- | Evento | `detail` |
254
- |---|---|
255
- | `mapa-listo` | — |
256
- | `mapa-error` | `{ error }` (no se pudo cargar la geometría) |
257
- | `kpi-cambio` | `{ cargados, ignoradas, origen, nivel }` |
258
- | `seleccion` | `{ tipo: 'municipio' \| 'cp' \| 'colonia', clave, nombre, cve_geo, cve_ent, municipio, entidad, kpi, poblacion }` (+ `cp`, `cve_col`, `colonia`, `tipo_asentamiento` según el tipo), o `null` al deseleccionar |
259
- | `nivel-cambio` | `{ nivel: 'nacional' \| 'entidad' \| 'municipio' \| 'cp', cve_ent, entidad, cve_geo, municipio, cp }` |
260
-
261
- ---
262
-
263
- ## Estilos
264
-
265
- El componente toma la **tipografía de tu página**. Necesita una altura: por omisión mide 640 px.
266
-
267
- ```css
268
- multinom-mapa {
269
- height: calc(100vh - 64px);
270
- --mk-accent: #c2185b; /* color de selección y enlaces */
271
- --mk-bg: transparent; /* fondo detrás de las tarjetas */
272
- --mk-relleno: 0; /* margen interno */
273
- }
274
- ```
275
-
276
- Variables disponibles: `--mk-bg`, `--mk-panel`, `--mk-ink`, `--mk-ink2`, `--mk-muted`, `--mk-line`, `--mk-land`, `--mk-edge`, `--mk-accent`, `--mk-nodata`, `--mk-hatch`, `--mk-halo`, `--mk-mono`, `--mk-relleno`. Si las cambias en caliente, llama a `redibujar()` para que el canvas las tome.
277
-
278
- Partes para `::part()`: `app`, `encabezado`, `mapa`, `leyenda`, `panel`.
279
-
280
- Por debajo de 760 px **de ancho del componente** (no de la ventana), el panel pasa debajo del mapa.
281
-
282
- ---
283
-
284
- ## La geometría
285
-
286
- Por omisión el componente hace `import('./datos.js')` del propio paquete; tu bundler (Angular CLI con webpack o esbuild, Vite, etc.) lo separa en su propio chunk y solo se descarga cuando aparece el primer mapa. No hay que configurar nada.
287
-
288
- Si prefieres servirla como archivo estático (por ejemplo, desde un CDN), copia el JSON a tus assets en `angular.json`:
289
-
290
- ```json
291
- "assets": [
292
- { "glob": "mexico.json", "input": "node_modules/multinom_mapa/dist", "output": "assets/multinom-mapa" }
293
- ]
294
- ```
295
-
296
- y apúntalo con el atributo o de forma global:
297
-
298
- ```html
299
- <multinom-mapa datos-url="assets/multinom-mapa/mexico.json"></multinom-mapa>
300
- ```
301
-
302
- ```ts
303
- import { configurarDatosUrl } from 'multinom_mapa';
304
- configurarDatosUrl('assets/multinom-mapa/mexico.json');
305
- ```
306
-
307
- ---
308
-
309
- ## Otros entornos
310
-
311
- **HTML plano**
312
-
313
- ```html
314
- <script type="module">import 'https://cdn.jsdelivr.net/npm/multinom_mapa@1/dist/index.js';</script>
315
- <multinom-mapa demo></multinom-mapa>
316
- ```
317
-
318
- **React 19+** pasa propiedades y eventos a custom elements de forma nativa:
319
- `<multinom-mapa kpi={valores} onseleccion={e => …} />`.
320
-
321
- ---
322
-
323
- ## Desarrollo y publicación
324
-
325
- El código fuente vive en el repo de GitHub; npmjs.com solo recibe el paquete compilado.
326
-
327
- ```bash
328
- npm install # instala dependencias y compila (script prepare)
329
- npm run build # recompila dist/
330
- npm pack # opcional: genera el .tgz para revisarlo antes de publicar
331
- ```
332
-
333
- ### Primera publicación (una sola vez, desde tu computadora)
334
-
335
- Trusted Publishing solo se puede configurar en un paquete que ya existe, así que la primera versión se publica a mano:
336
-
337
- ```bash
338
- npm login # cuenta de npmjs.com con 2FA
339
- npm publish # compila (script prepare) y sube multinom_mapa@<versión de package.json>
340
- ```
341
-
342
- Luego, en npmjs.com → paquete `multinom_mapa` → *Settings* → *Trusted Publisher* → **GitHub Actions**:
343
-
344
- | Campo | Valor |
345
- |---|---|
346
- | Organization or user | `MULTIEVAL` |
347
- | Repository | `multinom_mapa` |
348
- | Workflow filename | `publicar.yml` |
349
- | Environment | (vacío) |
350
-
351
- Con eso GitHub Actions publica sin token guardado (npm verifica la identidad del flujo con OIDC). Opcional y recomendado: en el mismo *Settings* → *Publishing access*, exige 2FA y deshabilita los tokens, para que solo se pueda publicar desde el flujo o con tu 2FA.
352
-
353
- ### Publicar una versión nueva
354
-
355
- La publicación es automática con GitHub Actions (`.github/workflows/publicar.yml`): se dispara al subir una etiqueta `v*`.
356
-
357
- ```bash
358
- npm version minor # 1.1.0 → 1.2.0: cambia package.json, hace commit y crea la etiqueta v1.2.0
359
- git push --follow-tags # sube el commit y la etiqueta; Actions publica en npm
360
- ```
361
-
362
- Usa `patch` para correcciones (1.1.1), `minor` para funciones nuevas compatibles (1.2.0) y `major` si rompes algo de la API (2.0.0). El flujo se detiene si la etiqueta no coincide con la versión de `package.json`.
363
-
364
- En los proyectos Angular actualizas con `npm install multinom_mapa@latest` (o la versión que quieras).
365
-
366
- ### Dueños del paquete
367
-
368
- El paquete queda a nombre de la cuenta que lo publica primero. Para pasarlo a otra cuenta u organización, sin cambiar el nombre ni los `import` de los proyectos:
369
-
370
- - **Otra cuenta como dueña:** `npm owner add <usuario> multinom_mapa` (todos los dueños tienen los mismos permisos; luego `npm owner rm <usuario>` para quitar la anterior).
371
- - **Una organización de npm:** en npmjs.com → paquete → *Settings* → transferir a la organización; los permisos se manejan después por equipos.
372
-
373
- ### Visibilidad
374
-
375
- `multinom_mapa` es un nombre **sin scope**, y en npm esos paquetes siempre son públicos. Si algún día necesitaras uno privado, tendría que publicarse con otro nombre que lleve scope (por ejemplo `@tu-org/multinom_mapa`) y cambiar el `import` en los proyectos.
376
-
377
- ## Licencia
378
-
379
- El código es [MIT](./LICENSE), © MULTIEVAL. Los datos geográficos y estadísticos tienen licencias propias (geoBoundaries ODbL, INEGI términos de libre uso, SEPOMEX CC BY 4.0); en conjunto se distribuyen bajo ODbL. Ver [LICENCIA-DATOS.md](./LICENCIA-DATOS.md).
1
+ # multinom_mapa
2
+
3
+ Web Component `<multinom-mapa>`: mapa de los 2,457 municipios de México coloreado por un KPI de 0 a 100 o por cantidades, con vista nacional por entidad, detalle por municipio, panel de resumen y búsqueda por coordenada. Incluye además **código postal y colonia** de las 32 entidades, que solo entran al build de tu app si las importas.
4
+
5
+ No depende de ningún framework. Funciona en **cualquier versión de Angular** (y en React, Vue o HTML plano), porque el navegador lo trata como una etiqueta nativa.
6
+
7
+ - Modo `kpi` (por omisión): el valor de cada entidad (y el nacional) es el promedio de sus municipios **ponderado por población 2020**. Valores fuera de 0–100 se recortan; vacíos o no numéricos cuentan como "sin dato".
8
+ - Modo `conteo`: cantidades (obras, quejas, visitas…) con escala de 0 al máximo visible; entidad y nacional son la **suma**.
9
+ - La geometría (~2.9 MB) se carga **diferida**, una sola vez por página, y no entra a tu bundle principal.
10
+
11
+ ---
12
+
13
+ ## Instalación
14
+
15
+ El paquete es público en npmjs.com como **`multinom_mapa`**. La etiqueta HTML que registra es `<multinom-mapa>`. Se instala como cualquier otra librería, sin sesión ni token:
16
+
17
+ ```bash
18
+ npm install multinom_mapa
19
+ ```
20
+
21
+ ---
22
+
23
+ ## Uso en Angular
24
+
25
+ ### 1. Registrar el elemento
26
+
27
+ Importa el paquete una vez, en `main.ts`:
28
+
29
+ ```ts
30
+ import 'multinom_mapa';
31
+ ```
32
+
33
+ ### 2. Permitir la etiqueta en las plantillas
34
+
35
+ **Componentes standalone** (Angular 15+):
36
+
37
+ ```ts
38
+ import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
39
+
40
+ @Component({
41
+ selector: 'app-tablero',
42
+ standalone: true,
43
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
44
+ templateUrl: './tablero.component.html',
45
+ })
46
+ export class TableroComponent {}
47
+ ```
48
+
49
+ **Con NgModule** (cualquier versión):
50
+
51
+ ```ts
52
+ import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
53
+
54
+ @NgModule({
55
+ // ...
56
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
57
+ })
58
+ export class AppModule {}
59
+ ```
60
+
61
+ ### 3. Usarlo
62
+
63
+ ```html
64
+ <multinom-mapa
65
+ titulo="Satisfacción por municipio"
66
+ nombre-kpi="Satisfacción"
67
+ [kpi]="valores"
68
+ (seleccion)="alSeleccionar($event)"
69
+ (nivel-cambio)="alCambiarNivel($event)">
70
+ </multinom-mapa>
71
+ ```
72
+
73
+ ```ts
74
+ import type { DetalleSeleccion, DetalleNivel } from 'multinom_mapa';
75
+
76
+ export class TableroComponent {
77
+ valores: Record<string, number> = { '22014': 78.5, '22011': 41 };
78
+
79
+ alSeleccionar(e: Event) {
80
+ const d = (e as CustomEvent<DetalleSeleccion | null>).detail;
81
+ console.log(d?.municipio, d?.kpi);
82
+ }
83
+ alCambiarNivel(e: Event) {
84
+ const d = (e as CustomEvent<DetalleNivel>).detail;
85
+ console.log(d.nivel, d.entidad);
86
+ }
87
+ }
88
+ ```
89
+
90
+ Cada vez que asignes un objeto nuevo a `valores`, el mapa se actualiza. Puedes pasarle datos antes de que termine de cargar la geometría: los guarda y los aplica al terminar.
91
+
92
+ ### Llamar métodos desde el componente
93
+
94
+ ```ts
95
+ import { ElementRef, ViewChild } from '@angular/core';
96
+ import type { MapaKpi } from 'multinom_mapa';
97
+
98
+ @ViewChild('mapa') mapa!: ElementRef<MapaKpi>;
99
+
100
+ async ubicarOficina() {
101
+ await this.mapa.nativeElement.listo;
102
+ const r = this.mapa.nativeElement.localizar(20.5888, -100.3899);
103
+ // r = { cve_ent: '22', cve_geo: '22014', entidad: 'Querétaro', municipio: 'Querétaro', kpi: 78.5 }
104
+ }
105
+ ```
106
+
107
+ ```html
108
+ <multinom-mapa #mapa></multinom-mapa>
109
+ ```
110
+
111
+ ### SSR (Angular Universal / `@angular/ssr`)
112
+
113
+ Importar el paquete en el servidor no truena: el elemento solo se registra en el navegador. En el servidor la etiqueta queda vacía y se dibuja al hidratar.
114
+
115
+ ---
116
+
117
+ ## Códigos postales y colonias
118
+
119
+ Vienen en el mismo paquete (`npm install multinom_mapa`, una sola instalación). Lo que decide si entran al build de tu app es **qué importas** en `main.ts`:
120
+
121
+ ```ts
122
+ import 'multinom_mapa'; // estados y municipios (siempre)
123
+ import 'multinom_mapa/qro'; // + CP y colonias de Querétaro
124
+ import 'multinom_mapa/jal'; // + CP y colonias de Jalisco
125
+ import 'multinom_mapa/todo'; // + CP y colonias de las 32 entidades
126
+ ```
127
+
128
+ Con solo `import 'multinom_mapa'` el build queda igual que sin CP ni colonias. Con una entidad importada, la geometría de cada municipio va en su propio chunk y **solo se descarga cuando se abre ese municipio**: si usas `raiz="22011"`, el navegador descarga únicamente El Marqués. Con `multinom_mapa/todo` el build genera un chunk por municipio (unos 2,450 archivos en `dist/` de tu app), pero el navegador sigue descargando solo los que se abren.
129
+
130
+ Abreviaturas: `ags` `bc` `bcs` `camp` `coah` `col` `chis` `chih` `cdmx` `dgo` `gto` `gro` `hgo` `jal` `mex` `mich` `mor` `nay` `nl` `oax` `pue` `qro` `qroo` `slp` `sin` `son` `tab` `tamps` `tlax` `ver` `yuc` `zac`.
131
+
132
+ ### Navegación: `raiz` y `niveles`
133
+
134
+ México (1) → entidad: sus municipios (2) → municipio: sus CP o colonias (3) → CP: sus colonias (4).
135
+
136
+ - `raiz` fija dónde empieza; el usuario no puede subir más arriba.
137
+ - `niveles` fija hasta dónde puede bajar (1–4, por omisión 4). Al llegar al último nivel, el clic selecciona en lugar de abrir.
138
+
139
+ ```html
140
+ <multinom-mapa niveles="2"></multinom-mapa> <!-- México → municipios, sin CP ni colonias -->
141
+ <multinom-mapa raiz="22" niveles="2"></multinom-mapa> <!-- Querétaro y sus municipios -->
142
+ <multinom-mapa raiz="22"></multinom-mapa> <!-- Querétaro → municipio → CP → colonia -->
143
+ <multinom-mapa raiz="22011" niveles="3"></multinom-mapa> <!-- El Marqués por CP o colonias, sin abrir cada CP -->
144
+ ```
145
+
146
+ Los niveles 3 y 4 además requieren haber importado la entidad (`multinom_mapa/qro`); si no, el mapa se queda en municipios. Si `raiz` es un municipio, `niveles` nunca baja de 3.
147
+
148
+ Dentro del municipio hay un selector **CP / Colonias** (atributo `capa-municipio` para el valor inicial). Con colonias se ve todo el municipio de una vez.
149
+
150
+ ### Ejemplo: obras públicas en El Marqués
151
+
152
+ ```html
153
+ <multinom-mapa
154
+ raiz="22011" modo="conteo" capa-municipio="colonias"
155
+ titulo="Obras públicas" nombre-kpi="Obras"
156
+ [kpi]="obrasPorMunicipio"
157
+ [kpiCp]="obrasPorCp"
158
+ [kpiColonias]="obrasPorColonia"
159
+ (seleccion)="alSeleccionar($event)">
160
+ </multinom-mapa>
161
+ ```
162
+
163
+ ```ts
164
+ obrasPorMunicipio = { '22011': 47 };
165
+ obrasPorCp = { '76246': 12, '76240': 5 };
166
+ obrasPorColonia = { '2201100010001': 3 }; // cvegeo de INEGI (13 dígitos)
167
+ ```
168
+
169
+ Cada nivel muestra **los valores que le pases a ese nivel**; el mapa no los suma de colonias a CP ni de CP a municipio, porque un CP puede cruzar municipios y no todas las colonias están delimitadas. Lo único que agrega es entidad y nacional a partir de municipios. En modo `conteo`, lo que no venga en los datos de un nivel cuenta como 0.
170
+
171
+ ### Qué hay que saber de los datos
172
+
173
+ - **CP:** polígonos de SEPOMEX, recortados con el límite de cada municipio. Si un CP cruza dos municipios, aparece en ambos (el tooltip dice "También en…").
174
+ - **Colonias:** *Delimitación de colonias y otros asentamientos humanos 2025* de INEGI. **No cubre todo el país**: cada municipio entrega la suya, y muchos municipios rurales no tienen ninguna (ahí se ven solo los CP). En Querétaro, por ejemplo, faltan Querétaro capital, San Juan del Río, Colón, Huimilpan y Amealco, y El Marqués tiene 137 colonias (faltan fraccionamientos como Zibatá). Las zonas sin colonia se ven en gris.
175
+ - La clave de colonia es el `cvegeo` de INEGI (entidad + municipio + localidad + asentamiento). El CP del atributo de INEGI no siempre coincide con el polígono de SEPOMEX; cada colonia se dibuja dentro del CP donde cae, y el tooltip muestra ambos si difieren.
176
+
177
+ ### Regenerar los datos (desarrollo)
178
+
179
+ 1. Fuentes en `build/fuentes/` (no se versionan; ~1.4 GB). El script las busca en cualquier subcarpeta:
180
+ - SEPOMEX, "Códigos postales por entidad federativa" (datos.gob.mx) → carpetas `CP_<Abrev>` (p. ej. `CP/CP_Qro`).
181
+ - INEGI, [Delimitación de colonias y otros asentamientos humanos](https://www.inegi.org.mx/programas/dcah/) → carpetas `<ee>_<nombre>` (p. ej. `DCAH/22_queretaro`).
182
+ 2. `npm run region -- 22` (una entidad) o `npm run region -- todas` (las 32; agrega `--forzar` para rehacer las que ya existen) → `build/regiones/<ee>.json` (estos sí se versionan).
183
+ 3. `npm run build` → genera `dist/regiones/<abrev>/` y `dist/regiones/todo.js`.
184
+
185
+ ---
186
+
187
+ ## Referencia
188
+
189
+ ### Atributos
190
+
191
+ | Atributo | Valor | Qué hace |
192
+ |---|---|---|
193
+ | `titulo` | texto | Título del encabezado. Por omisión "KPI municipal de México". |
194
+ | `nombre-kpi` | texto | Nombre del indicador en la leyenda. Por omisión "KPI". |
195
+ | `tema` | `auto` · `claro` · `oscuro` | `auto` (por omisión) sigue el modo del sistema. |
196
+ | `sin-encabezado` | — | Oculta el encabezado. La atribución de las fuentes pasa a la leyenda. |
197
+ | `sin-panel` | — | Oculta el panel lateral: queda solo el mapa. |
198
+ | `sin-localizar` | — | Oculta la tarjeta "Localizar coordenada". |
199
+ | `estadisticas` | — | Muestra población, superficie y densidad en tooltips y panel. Sin el atributo se ocultan. |
200
+ | `con-carga` | — | Muestra la tarjeta para generar datos aleatorios o cargar un CSV/JSON a mano. |
201
+ | `demo` | semilla opcional | Arranca con datos aleatorios reproducibles. |
202
+ | `datos-url` | URL | Descarga la geometría desde un JSON en lugar del módulo diferido (ver abajo). |
203
+ | `modo` | `kpi` · `conteo` | `kpi` (por omisión): 0–100, semáforo, promedio ponderado. `conteo`: cantidades, escala secuencial, suma. |
204
+ | `raiz` | clave de entidad o municipio | Dónde empieza la navegación (`22`, `22011`). Sin él, en México. |
205
+ | `niveles` | `1` · `2` · `3` · `4` | Hasta dónde se puede bajar: entidades, municipios, CP/colonias del municipio, colonias del CP. Por omisión `4`. |
206
+ | `capa-municipio` | `cp` · `colonias` | Cómo se ve un municipio abierto (requiere importar la entidad). Por omisión `cp`. |
207
+
208
+ ### Propiedades
209
+
210
+ | Propiedad | Tipo | |
211
+ |---|---|---|
212
+ | `kpi` | `Record<string, number>` o arreglo de filas | Leer o asignar los valores por municipio. |
213
+ | `kpiCp` | igual | Valores por código postal. |
214
+ | `kpiColonias` | igual | Valores por colonia (`cvegeo` de 13 dígitos). |
215
+ | `modo` | `'kpi' \| 'conteo'` | Solo lectura (refleja el atributo). |
216
+ | `niveles` | `1 \| 2 \| 3 \| 4` | Refleja el atributo `niveles`. |
217
+ | `estadisticas` | `boolean` | `true` muestra población, superficie y densidad; `false` (por omisión) las oculta. Refleja el atributo `estadisticas`. En Angular: `[estadisticas]="true"`. |
218
+ | `colorear` | `boolean` | `false` pinta todo en gris. |
219
+ | `listo` | `Promise<void>` | Se resuelve cuando el mapa está dibujado. |
220
+
221
+ ### Métodos
222
+
223
+ | Método | Devuelve |
224
+ |---|---|
225
+ | `setKpi(datos, opciones?)` | `{ cargados, ignoradas }`, o `null` si aún carga (se aplica al terminar). `opciones`: texto de origen o `{ nivel: 'municipio' \| 'cp' \| 'colonia', origen }`. |
226
+ | `getKpi(nivel?)` | Valores de un nivel (municipio por omisión). |
227
+ | `kpiEntidades()` | Valor por entidad (`cve_ent` → valor o `null`). |
228
+ | `kpiNacional()` | Valor nacional (ponderado o suma, según el modo). |
229
+ | `abrirEntidad(cve_ent)` | Muestra los municipios de una entidad. |
230
+ | `abrirMunicipio(cve_geo)` | `Promise`. Abre el municipio con sus CP y colonias (sin la entidad importada o con `niveles` < 3, abre la entidad y lo selecciona). |
231
+ | `abrirCp(cp, cve_geo?)` | `Promise`. Abre un CP y muestra sus colonias (con `niveles="3"`, solo lo selecciona). |
232
+ | `seleccionar(cve_geo)` | Abre la entidad y selecciona el municipio. |
233
+ | `seleccionarColonia(cvegeo)` | `Promise`. Abre municipio y CP de la colonia y la selecciona. |
234
+ | `subir()` | Sube un nivel, sin pasar de `raiz`. También con la tecla Escape. |
235
+ | `nacional()` | Vuelve a la vista nacional (o a la raíz). |
236
+ | `localizar(lat, lon, mover = true)` | `{ cve_ent, cve_geo, entidad, municipio, kpi }` y, si la geometría del municipio ya está cargada, `cp`, `kpi_cp`, `cve_col`, `colonia`, `kpi_colonia`. Con `mover`, navega y marca el punto. |
237
+ | `localizarDetalle(lat, lon, mover = true)` | `Promise` con lo mismo, cargando antes la geometría de CP/colonias. |
238
+ | `aleatorio(semilla?, huecos?)` | Genera datos de prueba (también para CP y colonias de las entidades importadas). |
239
+ | `encuadrarVista()` | Reencuadra la vista actual. |
240
+ | `redibujar()` | Vuelve a leer los colores CSS y redibuja. |
241
+
242
+ ### Formatos aceptados por `kpi` / `setKpi`
243
+
244
+ ```js
245
+ { "22014": 78.5, "22011": 41 } // objeto clave → valor
246
+ [{ cve_geo: "22014", kpi: 78.5 }, { cve: 22011, valor: 41 }] // filas (cve_geo|cve|clave|cp|cvegeo, kpi|valor|value)
247
+ ```
248
+
249
+ Las claves se rellenan con ceros a la izquierda (`1001` → `01001`). Para leer un CSV ya tienes `leerCsv(texto)` exportado por el paquete.
250
+
251
+ ### Eventos
252
+
253
+ Todos son `CustomEvent`, burbujean y salen del Shadow DOM.
254
+
255
+ | Evento | `detail` |
256
+ |---|---|
257
+ | `mapa-listo` | — |
258
+ | `mapa-error` | `{ error }` (no se pudo cargar la geometría) |
259
+ | `kpi-cambio` | `{ cargados, ignoradas, origen, nivel }` |
260
+ | `seleccion` | `{ tipo: 'municipio' \| 'cp' \| 'colonia', clave, nombre, cve_geo, cve_ent, municipio, entidad, kpi, poblacion }` (+ `cp`, `cve_col`, `colonia`, `tipo_asentamiento` según el tipo), o `null` al deseleccionar |
261
+ | `nivel-cambio` | `{ nivel: 'nacional' \| 'entidad' \| 'municipio' \| 'cp', cve_ent, entidad, cve_geo, municipio, cp }` |
262
+
263
+ ---
264
+
265
+ ## Estilos
266
+
267
+ El componente toma la **tipografía de tu página**. Necesita una altura: por omisión mide 640 px.
268
+
269
+ ```css
270
+ multinom-mapa {
271
+ height: calc(100vh - 64px);
272
+ --mk-accent: #c2185b; /* color de selección y enlaces */
273
+ --mk-bg: transparent; /* fondo detrás de las tarjetas */
274
+ --mk-relleno: 0; /* margen interno */
275
+ }
276
+ ```
277
+
278
+ Variables disponibles: `--mk-bg`, `--mk-panel`, `--mk-ink`, `--mk-ink2`, `--mk-muted`, `--mk-line`, `--mk-land`, `--mk-edge`, `--mk-accent`, `--mk-nodata`, `--mk-hatch`, `--mk-halo`, `--mk-mono`, `--mk-relleno`. Si las cambias en caliente, llama a `redibujar()` para que el canvas las tome.
279
+
280
+ Partes para `::part()`: `app`, `encabezado`, `mapa`, `leyenda`, `panel`.
281
+
282
+ Por debajo de 760 px **de ancho del componente** (no de la ventana), el panel pasa debajo del mapa.
283
+
284
+ ---
285
+
286
+ ## La geometría
287
+
288
+ Por omisión el componente hace `import('./datos.js')` del propio paquete; tu bundler (Angular CLI con webpack o esbuild, Vite, etc.) lo separa en su propio chunk y solo se descarga cuando aparece el primer mapa. No hay que configurar nada.
289
+
290
+ Si prefieres servirla como archivo estático (por ejemplo, desde un CDN), copia el JSON a tus assets en `angular.json`:
291
+
292
+ ```json
293
+ "assets": [
294
+ { "glob": "mexico.json", "input": "node_modules/multinom_mapa/dist", "output": "assets/multinom-mapa" }
295
+ ]
296
+ ```
297
+
298
+ y apúntalo con el atributo o de forma global:
299
+
300
+ ```html
301
+ <multinom-mapa datos-url="assets/multinom-mapa/mexico.json"></multinom-mapa>
302
+ ```
303
+
304
+ ```ts
305
+ import { configurarDatosUrl } from 'multinom_mapa';
306
+ configurarDatosUrl('assets/multinom-mapa/mexico.json');
307
+ ```
308
+
309
+ ---
310
+
311
+ ## Otros entornos
312
+
313
+ **HTML plano**
314
+
315
+ ```html
316
+ <script type="module">import 'https://cdn.jsdelivr.net/npm/multinom_mapa@1/dist/index.js';</script>
317
+ <multinom-mapa demo></multinom-mapa>
318
+ ```
319
+
320
+ **React 19+** pasa propiedades y eventos a custom elements de forma nativa:
321
+ `<multinom-mapa kpi={valores} onseleccion={e => …} />`.
322
+
323
+ ---
324
+
325
+ ## Desarrollo y publicación
326
+
327
+ El código fuente vive en el repo de GitHub; npmjs.com solo recibe el paquete compilado.
328
+
329
+ ```bash
330
+ npm install # instala dependencias y compila (script prepare)
331
+ npm run build # recompila dist/
332
+ npm pack # opcional: genera el .tgz para revisarlo antes de publicar
333
+ ```
334
+
335
+ ### Primera publicación (una sola vez, desde tu computadora)
336
+
337
+ Trusted Publishing solo se puede configurar en un paquete que ya existe, así que la primera versión se publica a mano:
338
+
339
+ ```bash
340
+ npm login # cuenta de npmjs.com con 2FA
341
+ npm publish # compila (script prepare) y sube multinom_mapa@<versión de package.json>
342
+ ```
343
+
344
+ Luego, en npmjs.com → paquete `multinom_mapa` → *Settings* → *Trusted Publisher* → **GitHub Actions**:
345
+
346
+ | Campo | Valor |
347
+ |---|---|
348
+ | Organization or user | `MULTIEVAL` |
349
+ | Repository | `multinom_mapa` |
350
+ | Workflow filename | `publicar.yml` |
351
+ | Environment | (vacío) |
352
+
353
+ Con eso GitHub Actions publica sin token guardado (npm verifica la identidad del flujo con OIDC). Opcional y recomendado: en el mismo *Settings* → *Publishing access*, exige 2FA y deshabilita los tokens, para que solo se pueda publicar desde el flujo o con tu 2FA.
354
+
355
+ ### Publicar una versión nueva
356
+
357
+ La publicación es automática con GitHub Actions (`.github/workflows/publicar.yml`): se dispara al subir una etiqueta `v*`.
358
+
359
+ ```bash
360
+ npm version minor # 1.1.0 → 1.2.0: cambia package.json, hace commit y crea la etiqueta v1.2.0
361
+ git push --follow-tags # sube el commit y la etiqueta; Actions publica en npm
362
+ ```
363
+
364
+ Usa `patch` para correcciones (1.1.1), `minor` para funciones nuevas compatibles (1.2.0) y `major` si rompes algo de la API (2.0.0). El flujo se detiene si la etiqueta no coincide con la versión de `package.json`.
365
+
366
+ En los proyectos Angular actualizas con `npm install multinom_mapa@latest` (o la versión que quieras).
367
+
368
+ ### Dueños del paquete
369
+
370
+ El paquete queda a nombre de la cuenta que lo publica primero. Para pasarlo a otra cuenta u organización, sin cambiar el nombre ni los `import` de los proyectos:
371
+
372
+ - **Otra cuenta como dueña:** `npm owner add <usuario> multinom_mapa` (todos los dueños tienen los mismos permisos; luego `npm owner rm <usuario>` para quitar la anterior).
373
+ - **Una organización de npm:** en npmjs.com → paquete → *Settings* → transferir a la organización; los permisos se manejan después por equipos.
374
+
375
+ ### Visibilidad
376
+
377
+ `multinom_mapa` es un nombre **sin scope**, y en npm esos paquetes siempre son públicos. Si algún día necesitaras uno privado, tendría que publicarse con otro nombre que lleve scope (por ejemplo `@tu-org/multinom_mapa`) y cambiar el `import` en los proyectos.
378
+
379
+ ## Licencia
380
+
381
+ El código es [MIT](./LICENSE), © MULTIEVAL. Los datos geográficos y estadísticos tienen licencias propias (geoBoundaries ODbL, INEGI términos de libre uso, SEPOMEX CC BY 4.0); en conjunto se distribuyen bajo ODbL. Ver [LICENCIA-DATOS.md](./LICENCIA-DATOS.md).