-
Notifications
You must be signed in to change notification settings - Fork 0
v0.3.0 kickoff: self-hosted Protomaps + MapLibre basemap (feasibility + roadmap) #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| # MapLibre + Protomaps basemap prototype (v0.3.0) | ||
|
|
||
| Proof-of-concept for replacing Leaflet + Israel Hiking Map with a self-hosted | ||
| vector basemap. Verified working: | ||
|
|
||
| - Self-hosted Israel PMTiles (extracted from the Protomaps global build with | ||
| `scripts/build-tiles.sh`) rendered by MapLibre GL. | ||
| - Clean style authored in-house: places, roads, water. **No boundaries layer**, | ||
| so no green line / 67 border. | ||
| - Labels follow the UI language: Hebrew (`name:he`) with the RTL text plugin | ||
| for correct shaping, English (`name:en`). | ||
|
|
||
|  | ||
|  | ||
|
|
||
| `prototype.html` is a standalone page (loads MapLibre + pmtiles from a CDN and | ||
| a locally-served `israel.pmtiles`). It is a reference only, not wired into the | ||
|
Comment on lines
+16
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Document a runnable local setup. The prototype requests 🤖 Prompt for AI Agents |
||
| app. The production integration (markers, popups, locate, language switch, | ||
| Area A/B/C overlay, R2 hosting, self-hosted glyphs, CSP) is the v0.3.0 work in | ||
| ROADMAP.md. | ||
|
|
||
| ## Hosting | ||
|
|
||
| The Israel PMTiles is ~22 MB at zoom 12, ~45 MB at 13, ~90 MB at 14. Workers | ||
| static assets cap at 25 MiB per file, so the tile file is served from | ||
| Cloudflare R2 (no cap, HTTP range supported). Clients range-fetch only the few | ||
| KB of tiles they view, so file size affects hosting only, not user bandwidth. | ||
|
Comment on lines
+24
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== file excerpt =="
sed -n '1,80p' design/maplibre-prototype/README.md 2>/dev/null || true
echo
echo "== search for bandwidth/PMtiles/cache statements =="
rg -n "bandwidth|PMTiles|Workers|static assets|Cloudflare R2|range-fetch|-fetch" design/maplibre-prototype/README.md . 2>/dev/null | sed -n '1,120p'Repository: NX1X/OpenTaxMap Length of output: 4946 Clarify the range-request bandwidth statement. Range requests avoid downloading the complete PMTiles archive. Clients still load metadata, indexes, and tile bytes for the tiles they view. Replace “not user bandwidth” with wording such as “does not require downloading the full archive.” 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| <!doctype html><html><head><meta charset="utf-8"> | ||
| <link href="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.css" rel="stylesheet"> | ||
| <style>html,body,#map{margin:0;height:100%;width:100%}</style></head> | ||
| <body><div id="map"></div> | ||
| <script src="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.js"></script> | ||
| <script src="https://unpkg.com/pmtiles@3.0.6/dist/pmtiles.js"></script> | ||
| <script> | ||
| maplibregl.setRTLTextPlugin('https://unpkg.com/@mapbox/mapbox-gl-rtl-text@0.2.3/mapbox-gl-rtl-text.min.js', true); | ||
| const proto = new pmtiles.Protocol(); | ||
| maplibregl.addProtocol('pmtiles', proto.tile); | ||
| const LANG = new URLSearchParams(location.search).get('lang') || 'he'; | ||
| const nameField = ['coalesce', ['get', 'name:'+LANG], ['get','name']]; | ||
| const style = { | ||
| version: 8, | ||
| glyphs: 'https://protomaps.github.io/basemaps-assets/fonts/{fontstack}/{range}.pbf', | ||
| sources: { pm: { type:'vector', url:'pmtiles://http://localhost:4401/israel.pmtiles', attribution:'OpenStreetMap' } }, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files | sed -n '/design\/maplibre-prototype\/prototype.html/p;/scripts\/build-tiles.sh/p'
echo
echo "== prototype relevant lines =="
if [ -f design/maplibre-prototype/prototype.html ]; then
nl -ba design/maplibre-prototype/prototype.html | sed -n '1,80p'
fi
echo
echo "== build-tiles attribution references =="
if [ -f scripts/build-tiles.sh ]; then
nl -ba scripts/build-tiles.sh | sed -n '1,120p'
fi
echo
echo "== local references to OpenStreetMap attribution text =="
rg -n "OpenStreetMap|attribution|attributionControl|contributors" .Repository: NX1X/OpenTaxMap Length of output: 302 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== prototype relevant lines =="
if [ -f design/maplibre-prototype/prototype.html ]; then
awk '{ printf "%6d\t%s\n", NR, $0 }' design/maplibre-prototype/prototype.html | sed -n '1,80p'
fi
echo
echo "== build-tiles attribution references =="
if [ -f scripts/build-tiles.sh ]; then
awk '{ printf "%6d\t%s\n", NR, $0 }$1' scripts/build-tiles.sh | sed -n '1,120p'
fi
echo
echo "== local references to OpenStreetMap attribution text =="
rg -n "OpenStreetMap|attribution|attributionControl|contributors" .Repository: NX1X/OpenTaxMap Length of output: 8396 🌐 Web query:
💡 Result: To disable the default MapLibre attribution (which typically includes the "MapLibre" text) and show only source-specific attributions, you cannot simply configure the AttributionControl directly, as it is designed to manage all attributions collectively [1][2]. Instead, you should override the default behavior by controlling the attribution at the map level and providing your own custom configuration [3]. Here is the recommended approach to achieve source-only attribution: 1. Disable the default attribution control when initializing the map: Set Citations:
🌐 Web query:
💡 Result: In MapLibre GL JS v4, setting Citations:
Display the required OpenStreetMap attribution.
[compliance_and_legal] 🤖 Prompt for AI Agents |
||
| layers: [ | ||
| { id:'bg', type:'background', paint:{'background-color':'#f6f4ee'} }, | ||
| { id:'earth', source:'pm','source-layer':'earth', type:'fill', paint:{'fill-color':'#f2efe6'} }, | ||
| { id:'landcover', source:'pm','source-layer':'landcover', type:'fill', paint:{'fill-color':'#e6ecd8','fill-opacity':0.6} }, | ||
| { id:'water', source:'pm','source-layer':'water', type:'fill', paint:{'fill-color':'#a7c7e7'} }, | ||
| { id:'roads-minor', source:'pm','source-layer':'roads', type:'line', filter:['!=',['get','kind'],'highway'], paint:{'line-color':'#ffffff','line-width':['interpolate',['linear'],['zoom'],8,0.4,14,2]} }, | ||
| { id:'roads-major', source:'pm','source-layer':'roads', type:'line', filter:['==',['get','kind'],'highway'], paint:{'line-color':'#f6d488','line-width':['interpolate',['linear'],['zoom'],6,0.6,14,4]} }, | ||
| { id:'places', source:'pm','source-layer':'places', type:'symbol', | ||
| filter:['in',['get','kind'],['literal',['city','town','village','locality','suburb']]], | ||
| layout:{'text-field':nameField,'text-font':['Noto Sans Regular'],'text-size':['interpolate',['linear'],['zoom'],7,10,12,15],'text-max-width':7}, | ||
| paint:{'text-color':'#333','text-halo-color':'#fff','text-halo-width':1.4} }, | ||
| ], | ||
| }; | ||
| const map = new maplibregl.Map({ container:'map', style, center:[35.0,31.6], zoom:7, attributionControl:false }); | ||
| map.on('load', ()=>{ window.__ready=true; }); | ||
| map.on('error', e=>{ (window.__errs=window.__errs||[]).push(String(e.error||e)); }); | ||
| </script></body></html> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| #!/usr/bin/env bash | ||
| # Build the self-hosted Israel basemap tiles for the v0.3.0 MapLibre map. | ||
| # | ||
| # Extracts an Israel + West Bank bounding box from the Protomaps global daily | ||
| # build (over HTTP range requests, so it does not download the planet) into a | ||
| # single .pmtiles archive. That file is then uploaded to Cloudflare R2 and | ||
| # served first-party by the Worker (see wrangler.jsonc R2 binding). | ||
| # | ||
| # Requirements: the `pmtiles` CLI (https://github.com/protomaps/go-pmtiles). | ||
| # Network access to demo-bucket.protomaps.com. | ||
| # | ||
| # Usage: scripts/build-tiles.sh [maxzoom] (default maxzoom 14) | ||
| set -euo pipefail | ||
|
|
||
| MAXZOOM="${1:-14}" | ||
| # Israel + West Bank + a margin (matches DATA_BOUNDS in the app). | ||
| BBOX="34.2,29.4,35.95,33.45" | ||
| SRC="https://demo-bucket.protomaps.com/v4.pmtiles" | ||
| OUT="tiles/israel.pmtiles" | ||
|
|
||
| mkdir -p tiles | ||
| echo "Extracting bbox $BBOX at maxzoom $MAXZOOM from $SRC ..." | ||
| pmtiles extract "$SRC" "$OUT" --bbox="$BBOX" --maxzoom="$MAXZOOM" | ||
|
|
||
| echo | ||
| echo "Built $OUT ($(du -h "$OUT" | cut -f1))." | ||
| echo "Layers: $(pmtiles show --metadata "$OUT" | python3 -c 'import sys,json;print(",".join(l["id"] for l in json.load(sys.stdin)["vector_layers"]))')" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win Declare the The script uses 🤖 Prompt for AI Agents |
||
| echo | ||
| echo "Next: upload to R2, e.g." | ||
| echo " npx wrangler r2 object put opentaxmap-tiles/israel.pmtiles --file=$OUT --remote" | ||
| echo | ||
| echo "The Protomaps basemap is derived from OpenStreetMap and Natural Earth." | ||
| echo "Attribution '(c) OpenStreetMap contributors' is required in the map UI." | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Keep both roadmap entries consistent with the prototype status.
Both entries describe Area A/B/C and R2 as delivered, but
design/maplibre-prototype/prototype.htmlhas no overlay and still uses a localhost PMTiles URL.design/maplibre-prototype/README.mdlists these items as production work.ROADMAP.md#L47-L54: describe the Area A/B/C overlay and R2 hosting as planned work.ROADMAP.he.md#L37-L40: use equivalent planned wording for the overlay and R2 hosting.📍 Affects 2 files
ROADMAP.md#L47-L54(this comment)ROADMAP.he.md#L37-L40🤖 Prompt for AI Agents