Interactive site to explore historical maps of Bahrain from the 20th century. This is a comprehensive digital archive of Bahrain's recent cartographic history with tools for detailed exploration, comparison, and research.
Live Site: https://www.mapbh.org
Tileserver: https://map.mapbh.org
Local Dev: localhost:1212
mapBH serves as a digital repository and visualization platform for historical maps of Bahrain, featuring:
- Interactive Map Viewer: two modes (transparency, side-by-side)
- Bilingual: Arabic and English
- Articles
- Catalogue: all maps in archive with metadata
- ClojureScript: Primary application language
- Shadow-CLJS: Build tool and development environment
- Reagent: React wrapper for ClojureScript
- Re-frame: State management framework
- Bulma CSS: Modern CSS framework for styling
- Leaflet.js: Interactive mapping library
- Note: Includes several modified Leaflet plugins as well
- Tileserver-GL: Map tile server for hosting georeferenced maps
- Nginx: Reverse proxy server
- Linode VPS: Hosting platform at
/var/www/mapbh - Cloudflare R2: CDN storage for source files, thumbnails, and mbtiles. (cdn.mapbh.org)
- GitHub Actions: Continuous deployment
- Java 8+ (OpenJDK recommended)
- Node.js 14+ and npm
- Git
- Internet
-
Clone the repository
git clone https://github.com/ahmed-machine/mapbh.git cd mapbh -
Install dependencies
npm install
-
Start development server
# Option 1: Direct shadow-cljs command npx shadow-cljs watch app # Option 2: For Emacs/CIDER users M-x cider-jack-in-cljs, select shadow, then :app
- Server: http://localhost:1212
Historical maps are processed through a georeferencing pipeline:
- Preparation: Scan physical map, and cleanup in photoshop
- Ground Control Points (GCPs): Identification of reference coordinates, typically corner coordinates or points on the map itself
- Geometric Transformation: Using GDAL for spatial rectification from source projection to target projection using GCPs, with additional cutlines and stitching for multi-sheet sets. Generates a GeoTiff file.
- Tile Generation: Converting to MBTiles format for web serving
Where coordinates and projection information are unavailable, we get creative with landmarks, research, and approximation. Each map is a puzzle to be solved whether in pre-processing or research or code. Due to these reasons as well as historical inaccuracy in maps, not all maps align perfectly. Most maps on the site are a combination of preprocessing tricks in Photoshop, projection translation with GDAL via CLI, and fine-tuning in QGIS.
Convert GeoTIFF to MBTiles (to serve on tileserver)
./scripts/tif2mbtiles.sh <input-path> <output-path>UTM Zone 39 (Ain al Abd) Projection (most common Bahrain standard)
./scripts/utm-zone39-translate.sh "map-name" "x1 y1 x2 y2"
# Example:
./scripts/utm-zone39-translate.sh "1969.5000.Manama & AlJufayr.1-5" "453000 2901300 456600 2898900"It's a lazy blog:
- Write in Markdown
- Convert to Hiccup: Using the
markdown-to-hiccuplibrary - Integration: Articles are compiled into ClojureScript namespaces
;; Converting markdown to hiccup syntax
user=> (require '[markdown-to-hiccup.core :as m])
user=> (m/file->hiccup "article.md")
[:div {} [:h1 {} "Article Title"] ...]All map entries are in data.cljs with associated translations, URLs, and descriptions.
GitHub Actions automatically:
- Builds the Shadow-CLJS application (produces single main.js file)
- SSHs into the production server
- Copies files to the web server directory
- Restarts necessary services
- Nginx: Reverse proxy configuration in
server-config/ - Tileserver-GL: Map tile serving with
tile-config.json - Systemd: Service management for background processes on server
Large files are stored in Cloudflare R2 and served via CDN (https://cdn.mapbh.org/):
- Source files (TIF, PDF, JP2) and thumbnails (PNG) served directly from R2
- MBTiles synced from R2 to production server (
/var/www/mapbh/public/maps/) for local tile serving
Workflow for new maps:
# 1. Upload source + generate thumbnail → R2
./scripts/deploy-map-to-r2.sh public/maps/2025-NewMap.tif
# 2. Generate mbtiles + upload → R2
./scripts/tif2mbtiles.sh public/maps/2025-NewMap.tif public/maps/2025-NewMap.mbtiles
./scripts/upload-mbtiles-to-r2.sh public/maps/2025-NewMap.mbtiles
# 3. Add entry to data.cljs. Pushing to main will automatically sync the server.
Alternatively:
ssh user@server "cd /var/www/mapbh && ./scripts/server-sync-all.sh"To view maps locally with full functionality:
# Install tileserver-gl globally
npm install -g tileserver-gl
# Download mbtiles from R2 (./scripts/sync-mbtiles-from-r2.sh)
# Comment out the remote url in data.cljs and use localhost
# Start local tile server
tileserver-gl -c tile-config.json
# Access at http://localhost:8080
Last updated: 2025