diff --git a/scripts/mapa/BlankMap-Americas.svg b/scripts/mapa/BlankMap-Americas.svg deleted file mode 100644 index f7e5cab..0000000 --- a/scripts/mapa/BlankMap-Americas.svg +++ /dev/null @@ -1,2880 +0,0 @@ - - - - - - image/svg+xml - - - Blank World Map - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/scripts/mapa/BlankMap-World.svg b/scripts/mapa/BlankMap-World.svg new file mode 100644 index 0000000..f29e15c --- /dev/null +++ b/scripts/mapa/BlankMap-World.svg @@ -0,0 +1,3272 @@ + + + World Map + + + + + Sudan + + + South Sudan + + + Georgia + + + Abkhazia + + + + + South Ossetia + + + + + + Peru + + + Burkina Faso + + + Libya + + + Belarus + + + Pakistan + + + Azad Jammu and Kashmir + + + + Indonesia + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Yemen + + + + + + + Madagascar + + + + + + Bolivia, Plurinational State of + + + + + Serbia + + + Kosovo + + + + + + Cote d'Ivoire + + + Algeria + + + Switzerland + + + Cameroon + + + North Macedonia, Republic of + + + Botswana + + + Kenya + + + Jordan + + + Mexico + + + + + + + + + + + + + + + + + + United Arab Emirates + + + + + Belize + + + + + Brazil + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sierra Leone + + + + + Mali + + + Congo, Democratic Republic of the + + + Italy + + + + + + + + Somalia + + + Somaliland + + + + Afghanistan + + + Bangladesh + + + + + + + + + + + + + + Dominican Republic + + + + + Guinea-Bissau + + + + + + + + + + + Ghana + + + Austria + + + Sweden + + + + + + + + + + + + + + + Turkey + + + + + + Uganda + + + Mozambique + + + + + + + Japan + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + New Zealand + + + + + + + + + + + + Cuba + + + + + + + + + + + Venezuela, Bolivarian Republic of + + + + + + + + + + + + + + + + Portugal + + + + + + + + + + + Colombia + + + Mauritania + + + + + Angola + + + + + Germany + + + + + + + + Thailand + + + + + + + + + + + + Papua New Guinea + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Iraq + + + Croatia + + + + + + + + + + + + + + + + + + + + + Greenland + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Niger + + + Denmark + + + + + + + + + + + + + + + + + + Latvia + + + Romania + + + Zambia + + + Myanmar + + + + + + + + + + + + + + + + + + + + + + + Ethiopia + + + Guatemala + + + Suriname + + + Czech Republic + + + Chad + + + Albania + + + Finland + + + + + + + + + + + + + + + + + + + + + + Syrian Arab Republic + + + Kyrgyzstan + + + Solomon Islands + + + + + + + + + + + + + + + + + + + + + + + + + + + Oman + + + + + + Panama + + + + + + + + Argentina + + + + + + + + United Kingdom + + + + + + + + + + + + + + + + + + + + + + + + + + + + Costa Rica + + + + + Paraguay + + + Guinea + + + + + Ireland + + + + + + Nigeria + + + + + + Tunisia + + + + + Poland + + + Namibia + + + South Africa + + + Egypt + + + Tanzania, United Republic of + + + + + + + Saudi Arabia + + + + + + + + Vietnam, Socialist Republic of + + + + + + + + + + + + + Russian Federation + + + + + + + + + + + + + + Southern Kuril Islands + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Crimea + + + + Haiti + + + + + + + Bosnia and Herzegovina + + + India + + + + + + + + + + + + + + + Canada + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + El Salvador + + + Guyana + + + Belgium + + + Equatorial Guinea + + + + + Lesotho + + + Bulgaria + + + Burundi + + + Djibouti + + + Azerbaijan + + + + + + Artsakh, Republic of + + + + + + Iran, Islamic Republic of + + + + + + + Malaysia + + + + + + + + + + + + + + + + + + Philippines + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Uruguay + + + Congo, Republic of the + + + Estonia + + + + + + + + Rwanda + + + Armenia + + + Senegal + + + Togo + + + Spain + + + + + + + + + + + + + + + Gabon + + + + + Hungary + + + Malawi + + + Tajikistan + + + Cambodia + + + + + + + Korea, Republic of + + + + + + + + + + + + Honduras + + + + + Iceland + + + Nicaragua + + + Chile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Morocco + + + + Western Sahara + + + Sahrawi Arab Democratic Republic + + + + + Liberia + + + Central African Republic + + + Slovakia + + + Lithuania + + + Zimbabwe + + + Sri Lanka + + + + + + Israel + + + + State of Palestine + + + Gaza Strip + + + West Bank + + + + + + Lao People's Democratic Republic + + + Korea, Democratic People's Republic of + + + Greece + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Turkmenistan + + + Ecuador + + + + + + + + + + + + + Benin + + + Slovenia + + + Norway + + Svalbard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Moldova, Republic of + + + Transnistria + + + + + + Ukraine + + + Donetsk People's Republic + + + + + Luhansk People's Republic + + + + + + Nepal + + + Eritrea + + + + + United States of America + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Kazakhstan + + + + + French Southern Territories + + + + + Uzbekistan + + + Mongolia + + + Bhutan + + + Antarctica + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Australia + + + + + + + + + + + + + + + + + + + + + + + Christmas Island + + + + + Cocos (Keeling) Islands + + + + + Heard Island and McDonald Islands + + + + + Norfolk Island + + + + + + China + + China, People's Republic of + + + + + + + + + + + + + + + + + + + + + + + Hong Kong + + + + + + + + + Macao + + + + + Taiwan + + + + + + + + + + France + + + + + + + + + + French Guiana + + + Guadeloupe + + + + + + + + + Martinique + + + + + Reunion + + + + + Mayotte + + + + + + Netherlands + + + + + + + + + + + + Bonaire, Sint Eustatius and Saba + + + + + + + + + + Lebanon + + + + + Montenegro + + + + + Eswatini + + + + + New Caledonia + + + + + + + + + + + + Fiji + + + + + + + + + + + + + + Kuwait + + + + + + + + Timor-Leste + + + + + + + + Bahamas + + + + + + + + + + + + + + + + + + + + + + + + Vanuatu + + + + + + + + + + + + + + + + + + + + Falkland Islands (Islas Malvinas) + + + + + + + + + + + + + + South Georgia and the South Sandwich Islands + + + + + Gambia + + + + + Qatar + + + + + Jamaica + + + + + Cyprus + + + Northern Cyprus + + + + + + + Puerto Rico + + + + + Brunei + + + + + + + + Trinidad and Tobago + + + + + + + + Cape Verde + + + + + + + + + + + + + + French Polynesia + + + + + + + + + + + + + + Samoa + + + + + + + + Luxembourg + + + + + Comoros + + + + + + + + + Mauritius + + + + + Faroe Islands + + + + + + + + + + + + Sao Tome and Principe + + + + + + + + Virgin Islands, U.S. + + + + + + + + Curaçao + + + + + Sint Maarten (Dutch Part) + + + + + Dominica + + + + + Tonga + + + + + + + + + Kiribati + + + + + + + + Micronesia, Federated States of + + + + + Bahrain + + + + + + Northern Mariana Islands + + + + + Palau + + + + + Seychelles, Republic of + + + + + British Indian Ocean Territory + + + + + Antigua and Barbuda + + + + + + + + Barbados + + + + + Turks and Caicos Islands + + + + + + + + + Saint Vincent and the Grenadines + + + + + Saint Lucia + + + + + Grenada + + + + + Malta + + + + + Maldives, Republic of + + + + + Cayman Islands + + + + + Saint Kitts and Nevis + + + + + + + + Montserrat + + + + + Saint Barthélemy + + + + + Niue + + + + + Saint Pierre and Miquelon + + + + + Cook Islands + + + + + Wallis and Futuna + + + + + American Samoa + + + + + Marshall Islands, Republic of the + + + + + Aruba + + + + + Liechtenstein + + + + + Virgin Islands, British + + + + + + + + Saint Helena, Ascension and Tristan da Cunha + + + + + Jersey + + + + + Anguilla + + + + + Saint Martin (French Part) + + + + + Guernsey + + + + + San Marino + + + + + Bermuda + + + + + Tuvalu + + + + + Nauru + + + + + Gibraltar + + + + + Pitcairn Islands + + + + + Monaco + + + + + Vatican City + + + + + Isle of Man + + + + + Guam + + + + + Singapore + + + + + Tokelau + + + + + + + + \ No newline at end of file diff --git a/scripts/mapa/generar.mjs b/scripts/mapa/generar.mjs index ee996c2..227be3e 100644 --- a/scripts/mapa/generar.mjs +++ b/scripts/mapa/generar.mjs @@ -25,11 +25,19 @@ import fs from 'node:fs'; -const ORIGEN = process.argv[2] ?? 'scripts/mapa/BlankMap-Americas.svg'; +const ORIGEN = process.argv[2] ?? 'scripts/mapa/BlankMap-World.svg'; const DESTINO = 'src/lib/mapa/iberoamerica.ts'; -/** Los veinte, en el orden en que se leen de norte a sur. */ +/** + * Los veintidós de Iberoamérica: los veinte de América más España y Portugal. + * + * Estaban fuera en la primera versión, hecha sobre un mapa de las Américas. + * Iberoamérica es también la Península —y España está en los datos: el bolero + * se adoptó ahí—, así que el origen pasa a ser el mapa del mundo. + */ const PAISES = [ + 'es', + 'pt', 'mx', 'gt', 'sv', @@ -55,6 +63,38 @@ const PAISES = [ /** Un trazado más pequeño que esto no se ve, y sí se nota en el peso. */ const MINIMO = 2; +/** + * El nombre en español de cada uno. + * + * Va aquí y no se lee de la tabla `pais`: el mapa dibuja los veintidós desde el + * primer día y la tabla solo tiene los países que ha traído alguna semilla. Un + * país sin nombre en la lista de al lado no se puede ni pulsar. + */ +const NOMBRES = { + es: 'España', + pt: 'Portugal', + mx: 'México', + gt: 'Guatemala', + sv: 'El Salvador', + hn: 'Honduras', + ni: 'Nicaragua', + cr: 'Costa Rica', + pa: 'Panamá', + cu: 'Cuba', + do: 'República Dominicana', + pr: 'Puerto Rico', + ve: 'Venezuela', + co: 'Colombia', + ec: 'Ecuador', + pe: 'Perú', + bo: 'Bolivia', + py: 'Paraguay', + cl: 'Chile', + ar: 'Argentina', + uy: 'Uruguay', + br: 'Brasil' +}; + const svg = fs.readFileSync(ORIGEN, 'utf8'); /* @@ -70,23 +110,99 @@ for (const [, cierre, etiqueta, atributos, suelta] of svg.matchAll( )) { if (etiqueta === 'g') { if (cierre) pila.pop(); - else pila.push(atributos.match(/id="([^"]+)"/)?.[1] ?? null); + else pila.push(atributos.match(/(?:^|\s)id="([^"]+)"/)?.[1] ?? null); continue; } if (cierre) continue; - const propio = atributos.match(/id="([^"]+)"/)?.[1] ?? null; + const propio = atributos.match(/(?:^|\s)id="([^"]+)"/)?.[1] ?? null; const codigo = [propio, ...[...pila].reverse()].find((c) => c && PAISES.includes(c)); - const d = atributos.match(/d="([^"]+)"/)?.[1]; + /* + * El límite del atributo importa: sin él, `d="…"` encaja dentro de + * `id="Mexico_mainland"` y el trazado que se extrae es el nombre del + * país. Salía un mapa vacío sin que nada fallara. + */ + const d = atributos.match(/(?:^|\s)d="([^"]+)"/)?.[1]; if (codigo && d) trozos[codigo].push(d); void suelta; } +/** + * Recorre un trazado y devuelve sus puntos, ya en absolutas. + * + * Hace falta porque el mapa del mundo está escrito en relativas —`m 1244,328 c + * 0.1,-0.12 …`— y ahí los números no son coordenadas sino desplazamientos: + * sumarlos como si lo fueran daba un rectángulo sin sentido y el encuadre salía + * infinito. + * + * No convierte el trazado, solo lo mide: el `d` se guarda tal cual, que dibuja + * igual y ocupa menos. + */ +function puntosDe(d) { + const piezas = d.match(/[a-zA-Z]|-?\d*\.?\d+(?:e[-+]?\d+)?/gi) ?? []; + const puntos = []; + + let x = 0; + let y = 0; + let inicioX = 0; + let inicioY = 0; + let comando = 'M'; + let i = 0; + + /** Cuántos números lleva cada comando. */ + const cuantos = { M: 2, L: 2, T: 2, H: 1, V: 1, C: 6, S: 4, Q: 4, A: 7, Z: 0 }; + + while (i < piezas.length) { + if (/[a-zA-Z]/.test(piezas[i])) { + comando = piezas[i]; + i++; + if (comando.toUpperCase() === 'Z') { + x = inicioX; + y = inicioY; + continue; + } + } + + const letra = comando.toUpperCase(); + const relativo = comando !== letra; + const n = cuantos[letra]; + if (n === undefined) break; + + const args = piezas.slice(i, i + n).map(Number); + i += n; + if (args.length < n) break; + + if (letra === 'H') x = relativo ? x + args[0] : args[0]; + else if (letra === 'V') y = relativo ? y + args[0] : args[0]; + else if (letra === 'A') { + // De la elipse solo interesa dónde acaba. + x = relativo ? x + args[5] : args[5]; + y = relativo ? y + args[6] : args[6]; + } else { + // El punto final es la última pareja; los de control quedan fuera del + // cálculo, y para medir un país la diferencia no se ve. + x = relativo ? x + args[n - 2] : args[n - 2]; + y = relativo ? y + args[n - 1] : args[n - 1]; + } + + if (letra === 'M') { + inicioX = x; + inicioY = y; + // Una `m` con más parejas sigue como `l`; una `M`, como `L`. + comando = relativo ? 'l' : 'L'; + } + + puntos.push([x, y]); + } + + return puntos; +} + /** El rectángulo que ocupa un trazado, para saber si merece la pena. */ function tamano(d) { - const numeros = [...d.matchAll(/-?\d+(?:\.\d+)?/g)].map((m) => Number(m[0])); - const xs = numeros.filter((_, i) => i % 2 === 0); - const ys = numeros.filter((_, i) => i % 2 === 1); + const puntos = puntosDe(d); + const xs = puntos.map((p) => p[0]); + const ys = puntos.map((p) => p[1]); if (xs.length === 0) return { ancho: 0, alto: 0, xs, ys }; return { ancho: Math.max(...xs) - Math.min(...xs), @@ -98,8 +214,21 @@ function tamano(d) { const unaDecimal = (d) => d.replace(/-?\d+\.\d+/g, (n) => String(Math.round(Number(n) * 10) / 10)); +/** + * Une varios trazados en uno. + * + * El primer salto de cada uno pasa a ser absoluto. Dentro de un `` la + * posición de partida es el origen, así que `m 1244,328` y `M 1244,328` son lo + * mismo; pero al pegar el segundo detrás del primero, su `m` se sumaba a donde + * había acabado el anterior y México acababa a cuarenta mil unidades de su + * sitio. El encuadre salía de cuarenta y dos mil por cincuenta y ocho mil. + */ +const unir = (trazados) => trazados.map((d) => d.replace(/^\s*m/, 'M')).join(' '); + const contorno = {}; const centro = {}; +/** El trazado mayor de cada país: es con el que se encuadra el inserto. */ +const principal = {}; let descartados = 0; for (const codigo of PAISES) { @@ -109,7 +238,7 @@ for (const codigo of PAISES) { if (!vale) descartados++; return vale; }); - contorno[codigo] = unaDecimal(buenos.join(' ')); + contorno[codigo] = unaDecimal(unir(buenos)); /* * Un punto donde agarrar el país. @@ -127,27 +256,48 @@ for (const codigo of PAISES) { const { xs, ys } = tamano(mayor); const medio = (ns) => ns.reduce((t, n) => t + n, 0) / ns.length; centro[codigo] = [medio(xs), medio(ys)].map((n) => Math.round(n * 10) / 10); + principal[codigo] = mayor; } -/* El encuadre sale de lo que queda, no del mapa entero. */ -const xs = []; -const ys = []; -for (const d of Object.values(contorno)) { - const t = tamano(d); - xs.push(...t.xs); - ys.push(...t.ys); +/** + * Dos encuadres, no uno. + * + * Iberoamérica son dos orillas y en medio hay un océano: con un solo encuadre, + * la mitad del dibujo es Atlántico vacío y todo lo demás queda diminuto. Se + * calcula uno para América y otro para la Península, y el mapa pinta el segundo + * en un recuadro —la misma solución que usa cualquier mapa de España con + * Canarias—. + */ +const IBERIA = ['es', 'pt']; +const MARGEN = 10; + +function encuadrar(codigos, soloPrincipal = false) { + const xs = []; + const ys = []; + for (const c of codigos) { + const t = tamano(soloPrincipal ? principal[c] : contorno[c]); + xs.push(...t.xs); + ys.push(...t.ys); + } + const x0 = Math.min(...xs) - MARGEN; + const y0 = Math.min(...ys) - MARGEN; + return [x0, y0, Math.max(...xs) - x0 + MARGEN, Math.max(...ys) - y0 + MARGEN] + .map((n) => n.toFixed(1)) + .join(' '); } -const MARGEN = 10; -const x0 = Math.min(...xs) - MARGEN; -const y0 = Math.min(...ys) - MARGEN; -const encuadre = [x0, y0, Math.max(...xs) - x0 + MARGEN, Math.max(...ys) - y0 + MARGEN] - .map((n) => n.toFixed(1)) - .join(' '); +const encuadre = encuadrar(PAISES.filter((c) => !IBERIA.includes(c))); +/* + * El inserto encuadra solo la parte continental. Con Canarias dentro, el + * rectángulo se estiraba mil kilómetros al suroeste y la Península quedaba del + * tamaño de una mota. Las islas se siguen dibujando; lo que cambia es el + * encuadre. + */ +const encuadreIberia = encuadrar(IBERIA, true); const cuerpo = PAISES.map( (c) => - `\t{ codigo: '${c.toUpperCase()}', centro: [${centro[c].join(', ')}], d: '${contorno[c]}' }` + `\t{ codigo: '${c.toUpperCase()}', nombre: '${NOMBRES[c]}', centro: [${centro[c].join(', ')}], d: '${contorno[c]}' }` ).join(',\n'); fs.writeFileSync( @@ -168,15 +318,23 @@ fs.writeFileSync( export interface PaisDelMapa { /** ISO 3166-1 alfa-2, como en la tabla \`pais\`. */ codigo: string; + /** En español. Lo lleva el mapa porque dibuja países que aún no están en la base. */ + nombre: string; /** Un punto dentro del país, para colgar de él lo que haya que pulsar. */ centro: [number, number]; /** El contorno, con todas sus islas en un solo trazado. */ d: string; } -/** El encuadre justo de los veinte países, no el del mapa original. */ +/** El encuadre de la orilla americana. */ export const ENCUADRE = '${encuadre}'; +/** El de la Península, que va en su propio recuadro. */ +export const ENCUADRE_IBERIA = '${encuadreIberia}'; + +/** Quiénes van en ese recuadro. */ +export const IBERIA = ['ES', 'PT']; + export const PAISES: PaisDelMapa[] = [ ${cuerpo} ]; @@ -187,4 +345,5 @@ const kb = (fs.statSync(DESTINO).size / 1024).toFixed(0); console.log(` países ${PAISES.length}`); console.log(` descartados ${descartados} trazados por debajo de ${MINIMO} unidades`); console.log(` encuadre ${encuadre}`); +console.log(` — península ${encuadreIberia}`); console.log(` tamaño ${kb} KB`); diff --git a/src/app.css b/src/app.css index 85f750c..e4ec166 100644 --- a/src/app.css +++ b/src/app.css @@ -52,6 +52,18 @@ --c-accent-brillo: oklch(0.94 0.095 96); --c-accent-sombra: oklch(0.63 0.13 72); + /* + * El verdemar: el tercer color de la paleta, para datos. + * + * El mapa necesitaba dos intensidades de algo que no fuera el dorado, y + * mezclarlo con el fondo marino daba barro —literalmente un marrón sucio—. + * Este es de la misma familia que el marino, un paso hacia el verde, así que + * conviven sin pelearse; y deja el dorado para lo que se pulsa y lo que se + * elige, que es su trabajo en el resto del sitio. + */ + --c-dato: oklch(0.78 0.13 192); + --c-dato-tenue: oklch(0.52 0.085 200); + --c-danger: oklch(0.72 0.16 22); --c-focus: oklch(0.84 0.12 88); @@ -199,6 +211,10 @@ --c-accent-brillo: oklch(0.9 0.1 92); --c-accent-sombra: oklch(0.6 0.12 74); + /* En claro se invierten: el dato pintado tiene que ser oscuro sobre papel. */ + --c-dato: oklch(0.52 0.09 200); + --c-dato-tenue: oklch(0.82 0.045 200); + --c-danger: oklch(0.5 0.17 25); --c-focus: oklch(0.5 0.13 264); } diff --git a/src/lib/components/MapaDeGeneros.svelte b/src/lib/components/MapaDeGeneros.svelte index e2c5e84..9030920 100644 --- a/src/lib/components/MapaDeGeneros.svelte +++ b/src/lib/components/MapaDeGeneros.svelte @@ -11,18 +11,18 @@ * SVG, no un `` con un `onclick`: así entra en el orden de tabulación * y se anuncia con su nombre. */ - import { ENCUADRE, PAISES } from '$lib/mapa/iberoamerica'; + import { ENCUADRE, ENCUADRE_IBERIA, IBERIA, PAISES } from '$lib/mapa/iberoamerica'; import type { GeneroEnElMapa } from '$lib/server/taller'; interface Props { generos: GeneroEnElMapa[]; /** El país elegido, en ISO alfa-2. Vacío si no hay ninguno. */ - pais: string; + paisElegido: string; /** Se llama al pulsar un país; el mismo otra vez lo deselecciona. */ alElegir: (codigo: string) => void; } - let { generos, pais, alElegir }: Props = $props(); + let { generos, paisElegido, alElegir }: Props = $props(); /** Lo que tiene cada país, para saber cómo pintarlo y qué anunciar. */ const porPais = $derived.by(() => { @@ -38,6 +38,56 @@ return mapa; }); + /* + * La lista de al lado: España primero y el resto por orden alfabético. Es la + * misma información que el mapa, en la forma en que se busca un país cuando + * ya se sabe cuál —y la única que sirve si el mapa no se puede usar—. + */ + const enLista = $derived.by(() => { + const resto = PAISES.filter((p) => p.codigo !== 'ES').sort((a, b) => + a.nombre.localeCompare(b.nombre, 'es') + ); + const espana = PAISES.find((p) => p.codigo === 'ES'); + return espana ? [espana, ...resto] : resto; + }); + + /** El ancho de un encuadre, para medir con él lo que se dibuja encima. */ + const anchoDe = (encuadre: string) => Number(encuadre.split(' ')[2]); + + const enAmerica = PAISES.filter((p) => !IBERIA.includes(p.codigo)); + const enIberia = PAISES.filter((p) => IBERIA.includes(p.codigo)); + + /* + * El recuadro de la Península, colocado a mano dentro del mismo lienzo. + * + * Se probó con un `` anidado, que es lo que parece corresponder, y no + * pintaba nada: sus porcentajes se miden contra el viewport y no contra el + * `viewBox` del padre. Con una traslación y una escala calculadas de los dos + * encuadres, el sitio es exactamente el que se pide. + */ + const numeros = (encuadre: string) => encuadre.split(' ').map(Number); + + const inserto = (() => { + const [ax, ay, aAncho] = numeros(ENCUADRE); + const [ix, iy, iAncho, iAlto] = numeros(ENCUADRE_IBERIA); + + const escala = (aAncho * 0.44) / iAncho; + const margen = 8; + const x = ax + aAncho - iAncho * escala - margen; + const y = ay + margen; + + return { + escala, + transform: `translate(${(x - ix * escala).toFixed(1)} ${(y - iy * escala).toFixed(1)}) scale(${escala.toFixed(3)})`, + marco: { + x: x.toFixed(1), + y: y.toFixed(1), + ancho: (iAncho * escala).toFixed(1), + alto: (iAlto * escala).toFixed(1) + } + }; + })(); + /** Lo que lee un lector de pantalla y lo que sale al pasar el ratón. */ function resumen(codigo: string): string { const suyo = porPais[codigo]; @@ -50,46 +100,144 @@ } -
- - {#each PAISES as p (p.codigo)} +
+
+ + {#each enAmerica as p (p.codigo)} + {@render pais(p, anchoDe(ENCUADRE))} + {/each} + + + + + {#each enIberia as p (p.codigo)} + {@render pais(p, anchoDe(ENCUADRE_IBERIA))} + {/each} + +
+ + +
    + {#each enLista as p (p.codigo)} {@const suyo = porPais[p.codigo]} - {#if suyo} - - 0} - class:elegido={pais === p.codigo} - role="button" - tabindex="0" - aria-pressed={pais === p.codigo} - aria-label={resumen(p.codigo)} +
  • + +
  • {/each} - +
+{#snippet pais(p: (typeof PAISES)[number], anchoDelLienzo: number)} + {@const suyo = porPais[p.codigo]} + {#if suyo} + + 0} + class:elegido={paisElegido === p.codigo} + role="button" + tabindex="0" + aria-pressed={paisElegido === p.codigo} + aria-label={resumen(p.codigo)} + onclick={() => alElegir(p.codigo)} + onkeydown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + alElegir(p.codigo); + } + }} + > + {resumen(p.codigo)} + + + + + {:else} + + + {/if} +{/snippet} +