A single Go binary that serves web map tiles from an embedded or custom equirectangular world map image.
Zero external dependencies, no hosted services required - perfect for learning web mapping, local development, or offline/air-gapped environments.
- 🌍 Embedded World Map - NASA Blue Marble imagery (5400×2700) included in the binary
- 🗺️ Interactive Leaflet Viewer - Prototype web map interface with debug mode
- 🚀 Zero Configuration - Just run the binary and open your browser
- 📦 Single Binary - 13MB standalone executable (includes map + viewer)
- 🎨 Custom Images - Support for your own equirectangular JPEG images
- ⚡ On-Demand Tile Generation - Tiles created in real-time with CatmullRom interpolation
- 🔧 Standard XYZ Tiles - Compatible with XYZmaps and tools like OpenStreetMap, Leaflet, and other web mapping libraries
# Download and run (or build from source)
./xyztiles
# Server starts on http://localhost:8080
# Open your browser to see the interactive map viewer!That's it! The embedded map and viewer are ready to go.
# Download the latest release for your platform
# (Replace with actual release URL when available)
curl -L -o xyztiles https://github.com/xyzmaps/xyztiles/releases/latest/download/xyztiles-linux-amd64
chmod +x xyztiles
./xyztilesRequirements:
- Go 1.25.5 or later
- No other dependencies!
# Clone the repository
git clone https://github.com/xyzmaps/xyztiles.git
cd xyztiles
# Build the binary
go build -o xyztiles main.go
# Run it
./xyztiles# Start server with embedded world map on default port 8080
./xyztiles
# Custom port
./xyztiles --port 9000Then open your browser to http://localhost:8080 (or your custom port) to see the interactive map viewer.
# Use your own equirectangular world map image
./xyztiles --image path/to/your/worldmap.jpg --port 8080Image Requirements:
- Format: JPEG (PNG and TIFF support coming soon)
- Projection: Equirectangular (EPSG:4326)
- Coverage: Full world extent (-180°, -90°, 180°, 90°)
- Example: NASA Blue Marble, Natural Earth, custom satellite imagery
Flags:
-h, --help help for xyztiles
-i, --image string Path to custom equirectangular world map image
(optional, uses embedded map if not specified)
-p, --port int Port to run the server on (default 8080)
-v, --version Print version information
Tiles are served at the standard XYZ URL pattern:
/{z}/{x}/{y}.png
Examples:
http://localhost:8080/0/0/0.png- Zoom 0 (entire world)http://localhost:8080/1/0/0.png- Zoom 1, northwest quadranthttp://localhost:8080/6/32/21.png- Zoom 6, specific tile
Tile Specifications:
- Size: 512×512 pixels (256 pixel at 2x/retina)
- Format: PNG
- Projection: Web Mercator (EPSG:3857)
- Zoom Levels: 0-6 (native), 7-10 (browser-scaled)
- Interpolation: CatmullRom for high quality
- Cache Headers: 24 hours (
max-age=86400)
const map = L.map('map').setView([20, 0], 2);
L.tileLayer('http://localhost:8080/{z}/{x}/{y}.png', {
attribution: 'xyztiles',
tileSize: 256,
// 256 makes 512 tiles crisp on highres/retina, but
// you can also use 512 and adjust the zoom level with an offset
maxNativeZoom: 6,
maxZoom: 10
}).addTo(map);The embedded Leaflet viewer includes:
- 🔍 Debug Mode - Press
Dkey or click the debug button to show tile coordinates and boundaries - 📏 Scale Control - Imperial and metric measurements
- 📍 Pan & Zoom - Standard map navigation
- ℹ️ Info Panel - Server statistics and endpoint details
- 🖥️ Console Logging - Tile load events and coordinate tracking
┌─────────────────────────────────────────────────┐
│ Single Go Binary (13MB) │
│ │
│ ┌─────────────┐ ┌──────────────────────┐ │
│ │ Embedded │───▶│ Tile Generator │ │
│ │ world.jpg │ │ (on-demand + cached) │ │
│ │ (1.6MB) │ │ │ │
│ └─────────────┘ └──────────────────────┘ │
│ ▲ │ │
│ │ ▼ │
│ ┌─────────────┐ ┌──────────────────────┐ │
│ │ --image │ │ HTTP Server │ │
│ │ flag │ │ /{z}/{x}/{y}.png │ │
│ │ (optional) │ │ / (Leaflet viewer) │ │
│ └─────────────┘ └──────────────────────┘ │
│ │
└─────────────────────────────────────────────────┘
- XYZ → Geographic Bounds - Convert tile coordinates to lat/lon using Web Mercator formulas
- Geographic → Pixel Bounds - Map lat/lon to pixel coordinates in the equirectangular source
- Extract Region - Pull the relevant section from the source image
- Resample - Scale to 512×512 using CatmullRom interpolation for high quality
- Encode - Convert to PNG and serve with cache headers
- Input Image: Equirectangular (EPSG:4326) - simple linear mapping
- Output Tiles: Web Mercator (EPSG:3857) - standard for web maps
- Latitude Range: ±85.0511° (Web Mercator limit)
xyztiles/
├── cmd/ # CLI commands (Cobra)
├── src/
│ ├── imagery/ # Image loading and tile extraction
│ ├── resources/ # Embedded assets (map + viewer HTML)
│ ├── server/ # HTTP server and handlers
│ └── tilemath/ # XYZ coordinate conversions
├── res/ # Source resources (not in binary)
└── main.go # Entry point
# Run all tests
go test ./...
# With coverage
go test ./... -cover
# Verbose output
go test -v ./src/...Current Test Coverage:
src/imagery: 100.0%src/resources: 100.0%src/server: 87.9%src/tilemath: 97.7%
# Standard build
go build -o xyztiles main.go
# Build for all platforms with GoReleaser
goreleaser build --snapshot --clean
# Release build
goreleaser release --cleanThe project follows clean architecture principles:
tilemath- Pure coordinate transformation logicimagery- Image loading and tile generationserver- HTTP handlers and routingresources- Embedded assets using//go:embed
- Education - Teach students about web mapping without external dependencies
- Offline Demos - Present web mapping concepts without internet
- Air-Gapped Environments - Deploy maps in secure/isolated networks
- Custom Cartography - Serve your own custom world map designs
- Development - Test mapping applications locally
- Prototyping - Quickly spin up a tile server for proof-of-concepts
Runtime: Zero! Single static binary.
Build-time:
golang.org/x/image/draw- Image resamplinggithub.com/spf13/cobra- CLI framework
- Startup: ~100ms (loading embedded 1.6MB JPEG)
- Tile Generation: ~10-200ms depending on zoom level and complexity
- Memory: ~50MB base + loaded image
- Concurrency: Handles multiple simultaneous tile requests
- Max Zoom: Native tiles only go to zoom 6 (higher zooms are browser-scaled)
- Projection: Only equirectangular input images supported currently
- Format: JPEG input only (PNG/TIFF support planned)
- Caching: In-memory LRU cache not yet implemented (coming soon)
- PNG and TIFF input support
- CORS configuration
- Tile export to disk (directory, MBTiles. PMTiles)
- Docker image
- Prometheus metrics endpoint
- In-memory LRU tile cache
- Pre-warm cache at startup option
- Multiple embedded base maps
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Write tests for your changes
- Ensure all tests pass (
go test ./...) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT License - see LICENSE file for details
- Map Data: NASA Blue Marble (https://visibleearth.nasa.gov/)
- Mapping Library: Leaflet (https://leafletjs.com/)
- Tile Math: Based on OSM Slippy Map standard (https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames)
Built as an educational tool to help newcomers understand web mapping without the complexity of external tile services. Complements vector tile servers and helps bridge the gap between static maps and interactive web cartography.
Made with ❤️ for the mapping community