fix(map-explorer): pre-fetch style+TileJSON to bypass stale Cloudflare CDN - #168
Merged
Conversation
Promote to production: fix blank tiles after GeolocateControl flyTo
The OpenFreeMap /styles/liberty endpoint serves different content from different Cloudflare edge nodes — some return the actual MapLibre style JSON, others return a TypeScript schema representation that MapLibre cannot parse, causing blank tiles. Instead of letting MapLibre fetch the style URL internally (where it hits the same stale edge), we pre-fetch the style with cache:'no-cache' to force revalidation with the origin server. We also resolve TileJSON-backed vector sources (openmaptiles) to direct tile URL arrays inline, so MapLibre never needs a separate TileJSON fetch at all. Also caps GeolocateControl and myLocation() zoom at 14 (the vector tile source's maxzoom), avoiding overzoom tile-loading edge cases in MapLibre v6. Removes the moveend→rAF→resize() pattern (the canvas size doesn't change during pan/zoom — the ResizeObserver handles real container size changes). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
goodwebtools | fc9c50b | Aug 08 2026, 03:25 AM |
slaveofcode
added a commit
that referenced
this pull request
Aug 8, 2026
Promote to production: fix blank tiles in Map Explorer (PR #168)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Root cause
The OpenFreeMap
/styles/libertyendpoint returns different content from different Cloudflare edge nodes. The Singapore CDN edge (serving users in Jakarta/Indonesia) had a stale cached response returning a TypeScript schema representation instead of the actual MapLibre style JSON — invalid JSON that MapLibre silently fails to parse, leaving the map blank.Reproducing the schema response:
(unquoted keys, type annotations as values — not parseable as JSON)
Meanwhile, other edge nodes return the correct minified style JSON starting with
{"version":8,...}.Fix
Pre-fetch style with
cache: 'no-cache'before passing it to MapLibre — forces Cloudflare to revalidate with the origin server (which serves valid JSON), bypassing the stale edge cache.Validate the response (
version+sourcesmust exist) and fall back to the URL if it looks wrong.Inline TileJSON tile URLs — the
openmaptilesvector source usesurl: "https://tiles.openfreemap.org/planet"(TileJSON reference). We now fetch and resolve this totiles: [...]before giving the style to MapLibre, so MapLibre never needs its own separate TileJSON fetch.Cap zoom at 14 for both
GeolocateControl(fitBoundsOptions: { maxZoom: 14 }) andmyLocation()flyTo — the vector tile source'smaxzoomis 14; overzooming beyond it had previously caused additional rendering edge cases in MapLibre v6.Remove the
moveend→ rAF →resize()pattern — the canvas size doesn't change during pan/zoom. TheResizeObserveralready handles real container-size changes. This eliminates the potential forresize()to cause internal state conflicts. Replaced with a simpletriggerRepaint()onmoveendto nudge the render loop.Test plan
🤖 Generated with Claude Code