MapLibre: «Unable to parse the tile» und «Unimplemented type: 3»
Die Vektorkacheln kommen im Browser an, aber MapLibre kann sie nicht lesen, und die Karte bleibt leer. Fast immer sind die Kacheln gzip-komprimiert, und der Server sagt es nicht. Wie Sie die Ursachen unterscheiden, und die Lösung für nginx, Apache, Caddy, S3, GitHub Pages und PMTiles.
Die Meldungen
Der Wortlaut hängt von der Version ab. Wir haben jeden Fall in Chromium mit MapLibre GL JS 3.3.0 und 6.13.0 nachgestellt:
| Der Server sendet | MapLibre 3.4.0 und neuer | MapLibre vor 3.4.0, Mapbox GL JS |
|---|---|---|
gzip-Daten ohne Content-Encoding: gzip | Unable to parse the tile at …, please make sure the data is not gzipped and that you have configured the relevant header in the server | Unimplemented type: 3 |
| doppelt komprimierte Daten, Header einmal gesendet | wie bei gzip oben | Unimplemented type: 3 |
eine HTML-Seite (Fehlerseite, Anmeldeseite, index.html) | Unable to parse the tile at …, got error: Unimplemented type: 4 | Unimplemented type: 4 |
| ein PNG-Bild (eine Rasterkachel) | Unable to parse the tile at …, got error: Unimplemented type: 3 | Unimplemented type: 3 |
Die Zahl ist der Feldtyp, den der Protocol-Buffer-Decoder in den ersten Bytes gefunden hat; sie deutet also an, was die Bytes sind. Ein Fehlercode des Kachelservers ist sie nicht. Die Meldung wiederholt sich für jede Kachel, eine Karte kann also Dutzende davon auf einmal ausgeben.
Eine Kachel ansehen
Kopieren Sie eine Kachel-URL aus dem Netzwerk-Tab des Browsers, und fragen Sie sie so an wie der Browser:
curl -s -D - -o tile.bin -H 'Accept-Encoding: gzip, br' 'https://tiles.example.com/14/8580/5737.pbf' \ | grep -i -e '^HTTP' -e content-type -e content-encoding head -c 4 tile.bin | od -An -tx1
| Erste Bytes | Der Inhalt ist | Der Header muss sein |
|---|---|---|
1a | eine Vektorkachel, unkomprimiert | kein Content-Encoding |
1f 8b | gzip | Content-Encoding: gzip, einmal |
3c («<») | HTML oder XML: eine Fehler- oder Ersatzseite | URL oder Routing korrigieren |
7b («{») | JSON: meist eine Fehlermeldung der API | lesen, sie nennt das Problem |
89 50 4e 47 | ein PNG-Bild | eine Quelle vom Typ raster verwenden |
Beginnt der Inhalt mit 1f 8b und sagt der Header Content-Encoding: gzip, entpacken Sie ihn einmal (gzip -dc tile.bin | head -c 4 | od -An -tx1). Beginnt er wieder mit 1f 8b, wurde die Kachel doppelt komprimiert.
Warum Kacheln gzip-komprimiert sind
Vektorkacheln lassen sich gut komprimieren, und die meisten Werkzeuge speichern sie deshalb komprimiert: MBTiles-Dateien von tippecanoe und planetiler, PMTiles-Archive und Ordner, die mb-util oder tippecanoe -e schreiben. Ein Kachelserver wie tileserver-gl oder martin weiss das und sendet den Header. Ein einfacher Webserver, ein Bucket oder ein CDN sieht nur Dateien, sendet sie ohne den Header, und der Browser gibt komprimierte Bytes an MapLibre weiter.
Lösung: den Header senden
Jede dieser Einstellungen wurde mit gzip-Kacheln in einem Ordner tiles/ getestet, mit eingeschalteter Kompression für den Rest der Website, und curl bestätigte ein einziges Content-Encoding: gzip und einen Inhalt, der sich zu einer Vektorkachel entpacken lässt:
location /tiles/ {
gzip off; # die Dateien sind schon komprimiert
add_header Content-Encoding gzip;
add_header Access-Control-Allow-Origin *;
types { application/vnd.mapbox-vector-tile pbf mvt; }
}
# add_header in einer location ersetzt die add_header-Zeilen des server-Blocks
# braucht mod_headers <Directory "/var/www/html/tiles"> Header set Content-Encoding gzip Header set Content-Type application/vnd.mapbox-vector-tile Header set Access-Control-Allow-Origin * SetEnv no-gzip 1 # mod_deflate von diesen Dateien fernhalten </Directory>
handle /tiles/* {
header Content-Encoding gzip
header Content-Type application/vnd.mapbox-vector-tile
header Access-Control-Allow-Origin *
file_server
}
# der Header wird mit jedem Objekt gespeichert; CloudFront gibt ihn weiter
aws s3 cp tiles/ s3://my-bucket/tiles/ --recursive \
--content-encoding gzip --content-type application/vnd.mapbox-vector-tile
Laden Sie die Seite danach mit geleertem Cache neu (oder öffnen Sie ein privates Fenster): Der Browser behält die kaputten Kacheln so lange, wie die alten Cache-Header es erlauben.
Lösung: die Kacheln entpacken
Wo Sie Header nicht pro Pfad setzen können, etwa auf GitHub Pages, speichern Sie die Kacheln unkomprimiert. Auf dem Weg zum Browser sind sie dann grösser, es sei denn, der Host komprimiert sie unterwegs.
# einen Ordner mit gzip-Kacheln an Ort und Stelle entpacken find tiles -name '*.pbf' -exec sh -c 'gzip -dc "$1" > "$1.tmp" && mv "$1.tmp" "$1"' _ {} \; # oder sie gleich unkomprimiert schreiben tippecanoe -e tiles --no-tile-compression -zg data.geojson
Doppelt komprimiert
Sind die Dateien schon gzip und komprimiert der Server oder ein CDN sie noch einmal, entfernt der Browser eine Schicht, und MapLibre erhält die andere. Schalten Sie die Kompression unterwegs für den Pfad der Kacheln aus (gzip off in nginx, SetEnv no-gzip in Apache), und prüfen Sie die Einstellungen des CDN, das den Typ application/x-protobuf womöglich komprimiert.
Eine HTML-Seite statt einer Kachel
«got error: Unimplemented type: 4» und erste Bytes 3c heissen, dass der Server eine Webseite geschickt hat. Häufige Gründe:
- Eine Single-Page-App liefert für jeden unbekannten Pfad
index.html(try_files $uri /index.html, Rewrites auf Netlify oder Vercel); fehlende Kacheln kommen also als App mit Status 200 zurück. Nehmen Sie den Pfad der Kacheln davon aus, und beantworten Sie fehlende Kacheln mit 404 oder 204. - Eine Anmeldeseite, eine Fehlerseite eines Proxys oder eine Seite «nicht gefunden» mit Status 200.
- Eine falsche URL-Vorlage: ein fehlender Teil des Pfads, oder
{z}/{y}/{x}in falscher Reihenfolge.
PMTiles
Entpacken Sie ein PMTiles-Archiv nicht in einen Ordner des Webservers, um das zu beheben. Liefern Sie das Archiv als eine Datei mit HTTP-Range-Requests aus, und lassen Sie es von der Bibliothek pmtiles lesen; sie entpackt die Kacheln selbst:
import { Protocol } from "pmtiles";
maplibregl.addProtocol("pmtiles", new Protocol().tile);
// im Stil:
// "sources": { "world": { "type": "vector", "url": "pmtiles://https://example.com/world.pmtiles" } }
Eine Rasterkachel in einer Vektorquelle
Ist die Kachel ein PNG oder JPEG, muss die Quelle im Stil "type": "raster" sein, bei den meisten Rasterdiensten mit "tileSize": 256. Ein Satz Rasterkacheln lässt sich nur als Bilder zeigen; er hat keine Layer, die man gestalten könnte.
Oder lassen Sie jemand anderen die Kacheln liefern
overpass.ch liefert Vektorkacheln der ganzen Welt mit den richtigen Headern, ETags und CORS, in neun fertigen Stilen für MapLibre, mit Beschriftung in 41 Sprachen und 3D-Gelände. 100'000 Kacheln im Monat sind kostenlos. Fehler kommen als JSON mit einem Code zurück.