Skip to content

feat(map): migrate basemap from CARTO to self-hosted PMTiles on R2#1064

Merged
koala73 merged 7 commits intomainfrom
worktree-melodic-singing-kurzweil
Mar 6, 2026
Merged

feat(map): migrate basemap from CARTO to self-hosted PMTiles on R2#1064
koala73 merged 7 commits intomainfrom
worktree-melodic-singing-kurzweil

Conversation

@koala73
Copy link
Owner

@koala73 koala73 commented Mar 5, 2026

Summary

  • Eliminates CARTO dependency — replaces CARTO tile provider (frequent 403 errors causing blank maps) with self-hosted PMTiles on Cloudflare R2
  • Automatic fallback to OpenFreeMap when VITE_PMTILES_URL is unset (graceful degradation)
  • OSS-friendly defaults — PMTiles options hidden when env var is empty; community users get OpenFreeMap + CARTO out of the box
  • Settings UI — Map Tile Provider dropdown in Settings to switch between providers at runtime
  • Happy variant unchanged (uses existing local CARTO JSON styles — migration deferred)

Changes

File What
src/config/basemap.ts New — PMTiles protocol registration, style generation, provider selection
src/components/DeckGLMap.ts Replace CARTO styles with PMTiles, hardened fallback detection (2+ errors, 10s timeout)
src/components/UnifiedSettings.ts Add Map Tile Provider dropdown
src/components/MapContainer.ts Add reloadBasemap() delegation
src/app/event-handlers.ts Wire onMapProviderChange callback
vite.config.ts SW cache rules (PMTiles NetworkFirst) + chunking
index.html Preconnect hints for map tile assets
package.json Add pmtiles, @protomaps/basemaps
.env.example Add VITE_PMTILES_URL

How it works

  • With VITE_PMTILES_URL set: PMTiles self-hosted tiles are primary, OpenFreeMap fallback on error
  • Without VITE_PMTILES_URL (default): OpenFreeMap is default, CARTO available as option in Settings
  • Provider selection persists in localStorage, gracefully degrades if env var is later removed

Test plan

  • Leave VITE_PMTILES_URL empty → verify OpenFreeMap loads as default
  • Set VITE_PMTILES_URL to a PMTiles URL → verify Protomaps basemap renders
  • Toggle dark/light theme — style switches correctly
  • Settings → Map Tile Provider → switch between providers → map reloads
  • Test happy variant (VITE_VARIANT=happy) still uses local JSON styles
  • Check Network tab: no CARTO requests for default variant
  • Verify map labels localize when switching language
  • Check deck.gl overlay layers render correctly on top

Follow-up

  • Migrate happy variant styles to PMTiles (separate PR)
  • Self-host fonts/sprites (currently using protomaps.github.io CDN)

@vercel
Copy link

vercel bot commented Mar 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
worldmonitor Ready Ready Preview, Comment Mar 6, 2026 4:35am

Request Review

@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

koala73 added 7 commits March 6, 2026 08:28
…flare R2

Replace CARTO tile provider (frequent 403 errors) with self-hosted PMTiles
served from Cloudflare R2. Uses @protomaps/basemaps for style generation
with OpenFreeMap as automatic fallback when VITE_PMTILES_URL is unset.

- Add pmtiles and @protomaps/basemaps dependencies
- Create src/config/basemap.ts for PMTiles protocol registration and style building
- Update DeckGLMap.ts to use PMTiles styles (non-happy variants)
- Fix fallback detection using data event instead of style.load
- Update SW cache rules: replace CARTO/MapTiler with PMTiles NetworkFirst
- Add Protomaps preconnect hints in index.html
- Bundle pmtiles + @protomaps/basemaps in maplibre chunk
- Upload 3.4GB world tiles (zoom 0-10) to R2 bucket worldmonitor-maps
Replace r2.dev URL with custom domain backed by Cloudflare CDN edge.
Update preconnect hint and .env.example with production URL.
- Require 2+ network errors before triggering OpenFreeMap fallback
- Use persistent data listener instead of once (clears timeout on first tile load)
- Increase fallback timeout to 10s for PMTiles header + initial tile fetch
- Add console.warn for map errors to aid debugging
- Remove redundant style.load listener (fires immediately for inline styles)
Add dropdown in Settings → Map section to switch between:
- Auto (PMTiles → OpenFreeMap fallback)
- PMTiles (self-hosted)
- OpenFreeMap
- CARTO

Choice persists in localStorage and reloads basemap instantly.
…es when unconfigured

- Default to OpenFreeMap when VITE_PMTILES_URL is unset (zero config for OSS users)
- Hide PMTiles/Auto options from settings dropdown when no PMTiles URL configured
- If user previously selected PMTiles but env var is removed, gracefully fall back
- Remove production URL from .env.example to avoid exposing hosted tiles
- Add docs link for self-hosting PMTiles in .env.example
Document the tile provider system (OpenFreeMap, CARTO, PMTiles) in
MAP_ENGINE.md with self-hosting instructions, fallback behavior, and
OSS-friendly defaults. Update README to reference tile providers in
the feature list, tech stack, and environment variables table.
- Restore OSS-friendly basemap defaults (MAP_PROVIDER_OPTIONS as IIFE,
  getMapProvider with hasTilesUrl check)
- Fix markdown lint: add blank lines after ### headings in README
- Reconcile UnifiedSettings import with MAP_PROVIDER_OPTIONS constant
@koala73 koala73 force-pushed the worktree-melodic-singing-kurzweil branch from aa4dc78 to bd1b776 Compare March 6, 2026 04:34
@koala73 koala73 merged commit fce8360 into main Mar 6, 2026
4 checks passed
aldoyh pushed a commit to aldoyh/worldmonitor that referenced this pull request Mar 6, 2026
…oala73#1064)

* feat(map): migrate basemap from CARTO to self-hosted PMTiles on Cloudflare R2

Replace CARTO tile provider (frequent 403 errors) with self-hosted PMTiles
served from Cloudflare R2. Uses @protomaps/basemaps for style generation
with OpenFreeMap as automatic fallback when VITE_PMTILES_URL is unset.

- Add pmtiles and @protomaps/basemaps dependencies
- Create src/config/basemap.ts for PMTiles protocol registration and style building
- Update DeckGLMap.ts to use PMTiles styles (non-happy variants)
- Fix fallback detection using data event instead of style.load
- Update SW cache rules: replace CARTO/MapTiler with PMTiles NetworkFirst
- Add Protomaps preconnect hints in index.html
- Bundle pmtiles + @protomaps/basemaps in maplibre chunk
- Upload 3.4GB world tiles (zoom 0-10) to R2 bucket worldmonitor-maps

* fix(map): use CDN custom domain maps.worldmonitor.app for PMTiles

Replace r2.dev URL with custom domain backed by Cloudflare CDN edge.
Update preconnect hint and .env.example with production URL.

* fix(map): harden PMTiles fallback detection to prevent false triggers

- Require 2+ network errors before triggering OpenFreeMap fallback
- Use persistent data listener instead of once (clears timeout on first tile load)
- Increase fallback timeout to 10s for PMTiles header + initial tile fetch
- Add console.warn for map errors to aid debugging
- Remove redundant style.load listener (fires immediately for inline styles)

* feat(settings): add Map Tile Provider selector in settings

Add dropdown in Settings → Map section to switch between:
- Auto (PMTiles → OpenFreeMap fallback)
- PMTiles (self-hosted)
- OpenFreeMap
- CARTO

Choice persists in localStorage and reloads basemap instantly.

* fix(map): make OSS-friendly — default to free OpenFreeMap, hide PMTiles when unconfigured

- Default to OpenFreeMap when VITE_PMTILES_URL is unset (zero config for OSS users)
- Hide PMTiles/Auto options from settings dropdown when no PMTiles URL configured
- If user previously selected PMTiles but env var is removed, gracefully fall back
- Remove production URL from .env.example to avoid exposing hosted tiles
- Add docs link for self-hosting PMTiles in .env.example

* docs: add map tile provider documentation to README and MAP_ENGINE.md

Document the tile provider system (OpenFreeMap, CARTO, PMTiles) in
MAP_ENGINE.md with self-hosting instructions, fallback behavior, and
OSS-friendly defaults. Update README to reference tile providers in
the feature list, tech stack, and environment variables table.

* fix: resolve rebase conflicts and fix markdown lint errors

- Restore OSS-friendly basemap defaults (MAP_PROVIDER_OPTIONS as IIFE,
  getMapProvider with hasTilesUrl check)
- Fix markdown lint: add blank lines after ### headings in README
- Reconcile UnifiedSettings import with MAP_PROVIDER_OPTIONS constant
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant