fix(map): arreglar 3 bugs criticos que impedian ver el mapa

Bug 1: Content-Type vacio para .pmtiles
- Vite/Astro sirven .pmtiles sin Content-Type (extension desconocida)
- pmtiles del cliente falla al parsear el header del archivo
- Solucion: plugin Vite inline 'assetMimePlugin' en astro.config.mjs que
  setea application/octet-stream para .pmtiles, application/geo+json
  para .geojson, application/json para /data/*.json

Bug 2: MapLibre override de 'position: absolute'
- El <div id='map' class='absolute inset-0'> era override por la clase
  '.maplibregl-map { position: relative }' que MapLibre anade al container
  en runtime. Resultado: #map con 0px de alto, canvas de 300x1280
  (default) en lugar de 660x1280
- Solucion: cambiar a inline style 'position: absolute; inset: 0;' que
  no es override por clases externas

Bug 3: evento 'load' se dispara antes del handler
- Los <script> de Astro 7 se cargan como modulo defer, asi que el
  evento 'load' del map puede dispararse ANTES de que registremos el
  handler. El loading overlay se queda visible encima del mapa
- Solucion: patron seguro que chequea isStyleLoaded() y si ya paso
  ejecuta el callback inmediatamente, si no registra once('load', ...).
  Tambien setTimeout(3000) como safety net

Bug 4: zoom 8 demasiado bajo para ver secciones censales
- A zoom 8 las 674 secciones censales de las Balears son 1-2px cada
  una: aparecen como ruido gris plano
- Solucion: zoom inicial 9.5 centrado en Mallorca (lat 39.6, lon 2.85)
  para que las secciones se vean como poligonos de 3-6px

Cambios adicionales:
- URL del source pmtiles con window.location.origin (protocolo pmtiles
  requiere URL absoluta, no funciona con path relativo)
- Data del source airbnb con window.location.origin
- Sintaxis del style arreglada (faltaba { de apertura de la primera layer
  tras un edit anterior)
- TypeScript 5.9.3 mantiene typecheck 0/0/0
This commit is contained in:
opencode
2026-07-14 13:02:20 +02:00
parent ba9f353263
commit 97ebcdfe03
2 changed files with 51 additions and 8 deletions
+24 -1
View File
@@ -2,9 +2,32 @@
import { defineConfig } from 'astro/config';
import tailwindcss from '@tailwindcss/vite';
// Vite plugin inline: añade Content-Type correcto para .pmtiles y .geojson
// en dev server. Sin esto, Astro/Vite sirve .pmtiles con Content-Type vacío
// y la librería pmtiles del cliente falla al parsear el header del archivo.
function assetMimePlugin() {
return {
name: 'cases-tancades-asset-mime',
configureServer(/** @type {any} */ server) {
// @ts-expect-error tipos de Vite no exportados
server.middlewares.use((_req, res, next) => {
const url = _req.url ?? '';
if (url.endsWith('.pmtiles')) {
res.setHeader('Content-Type', 'application/octet-stream');
} else if (url.endsWith('.geojson')) {
res.setHeader('Content-Type', 'application/geo+json');
} else if (url.endsWith('.json') && url.includes('/data/')) {
res.setHeader('Content-Type', 'application/json; charset=utf-8');
}
next();
});
},
};
}
// https://astro.build/config
export default defineConfig({
vite: {
plugins: [tailwindcss()],
plugins: [assetMimePlugin(), tailwindcss()],
},
});
+27 -7
View File
@@ -30,7 +30,7 @@ const popupTitleTmpl = t(lang, "map.popup_title");
data-lang={lang}
data-popup-title-tmpl={popupTitleTmpl}
>
<div id="map" class="absolute inset-0"></div>
<div id="map" style="position: absolute; inset: 0;"></div>
<div
id="map-loading"
@@ -167,12 +167,12 @@ const popupTitleTmpl = t(lang, "map.popup_title");
sources: {
cases: {
type: "vector",
url: "pmtiles:///tiles/dataset.pmtiles",
url: `pmtiles://${window.location.origin}/tiles/dataset.pmtiles`,
attribution: "INE + Inside Airbnb + Govern d'Eivissa + IBESTAT",
},
airbnb: {
type: "geojson",
data: "/data/airbnb_listings.geojson",
data: `${window.location.origin}/data/airbnb_listings.geojson`,
cluster: true,
clusterMaxZoom: 12,
clusterRadius: 40,
@@ -277,19 +277,39 @@ const popupTitleTmpl = t(lang, "map.popup_title");
],
},
center: [2.85, 39.6],
zoom: 8,
minZoom: 7,
zoom: 9.5,
minZoom: 6,
maxZoom: 14,
});
map.addControl(new maplibregl.NavigationControl({ showCompass: false }), "top-right");
map.addControl(new maplibregl.ScaleControl({ unit: "metric" }), "bottom-left");
map.on("load", () => {
// En Astro 7 los <script> se cargan como modulo defer, asi que el
// evento "load" del map puede dispararse ANTES de que registremos
// el handler. Patron seguro: chequear isStyleLoaded() y si ya paso,
// ejecutar el callback inmediatamente.
let readyDone = false;
const onReady = (cb: () => void) => {
if (readyDone) return;
readyDone = true;
cb();
};
if (map.isStyleLoaded()) {
onReady(hideLoading);
} else {
map.once("load", () => onReady(hideLoading));
}
// Safety: si el source pmtiles rompe, isStyleLoaded nunca sera true.
setTimeout(() => onReady(hideLoading), 3000);
function hideLoading() {
const loading = document.getElementById("map-loading");
if (loading) loading.style.display = "none";
}
const applyFilter = (isla: string) => {
map.on("load", () => {
const applyFilter = (isla: string) => {
const filterExpr =
isla === "all"
? null