# πŸ—ΊοΈ OSM - self host the entire planet 🌎 in ~30 minutes πŸš€ **s**imple **m**btiles **s**erver #### SETUP - TL;DR ``` mkdir osm wget --continue -O osm/planet.mbtiles https://nx93856.your-storageshare.de/public.php/dav/files/apP4zEacCwqPfWd/planet.mbtiles wget --continue -O osm/contours.mbtiles https://nx93856.your-storageshare.de/public.php/dav/files/apP4zEacCwqPfWd/contours_90m-Z11-Z13_30m-Z14.mbtiles podman run -ti --rm -p 9000:9000 --name sms -v "$(pwd)/osm/:/data/" registry.gitlab.com/markuman/sms:latest firefox http://localhost:9000 ``` requirements: * `podman` (_or `docker`_) * 100 GB storage is required (_1 core and 512MB memory are sufficient_) * optional 315 GB storage is required for contour lines notes: * "_~30 minutes_" depends on your bandwidth ...and the hidrive performance of ionos. # credits * https://github.com/onthegomap/planetiler is used to generate the planet.mbtiles file * https://github.com/uktrade/mbtiles-s3-server is the origin code-base of my `sms` project # Usage ## nextcloud GpxPod 1. Deploy the container/service behind a webproxy (_caddy, nginx, traefik,...you name it._) to get a valid SSL certificate. 2. Goto GpxPod Settings -> Tile Servers * Type: Vector * Server address: `https:///v1/styles/osm-bright-gl-style@1.0.0/style.json?fonts=fonts-gl@1.0.0&tiles=mytiles@1.0.0` ## URL Parameter You can add coordinates and Zoomlevel. `https://maps.osuv.de/?lat=48.1374&lng=11.5752&zoom=9` ## Reverse Geolocation / Photon Integration * sms supports [photon](https://github.com/rtuszik/photon-docker) reverse geocoding server. Just set `-e PHOTONSERVER="https://photon.osuv.de"` for your sms Container. ## HELP WANTED * Improve Style * special for Nextcloud GxpPod * provide more style? remove some? ## planet.mbtiles My provided planet.mbtiles is generated by using https://github.com/onthegomap/planetiler I just followed this tutorial: https://github.com/onthegomap/planetiler/blob/main/PLANET.md ## contours.mbtiles Contours will be displayed if a file called `contours.mbtiles` is places next to `planet.mbtiles`. There are two different contours files provided. 90m resolution in Zoom Level 11 to 14 and 90m resolution in Zoom Level 11 to 13 with 30m resolution in Zoom Level 14. > GLO-30: "produced using Copernicus WorldDEM-30 Β© DLR e.V. 2010-2014 and Β© Airbus Defence and Space GmbH 2014-2018 provided under COPERNICUS by the European Union and ESA; all rights reserved" > GLO-90: "produced using Copernicus WorldDEM-90 Β© DLR e.V. 2010-2014 and Β© Airbus Defence and Space GmbH 2014-2018 provided under COPERNICUS by the European Union and ESA; all rights reserved" ## Core API ### `GET /` Serves the map UI frontend (index.html) with optional URL parameters for coordinates and zoom level. **URL Parameters:** - `lat` β€” Latitude coordinate - `lng` β€” Longitude coordinate - `zoom` β€” Zoom level **Example:** `https://maps.osuv.de/?lat=48.1374&lng=11.5752&zoom=9` ### `GET /v1/capabilities` Returns JSON capabilities indicating available optional features. **Response:** ```json { "contours": true | false, "routing": true } ``` - `contours` β€” whether `contours.mbtiles` was detected and loaded - `routing` β€” always `true`; indicates the `/v1/route/` endpoint is available ### `GET /v1/poi/{identifier}@{version}?lat={lat}&lon={lon}&category={category}&radius={radius}` Search for Points of Interest (POI) within a radius and return GeoJSON features. **Path Parameters:** - `identifier` β€” Tileset identifier - `version` β€” Tileset version **Query Parameters:** - `lat` β€” Latitude coordinate (required) - `lon` β€” Longitude coordinate (required) - `category` β€” POI category: `supermarket`, `pharmacy`, `hospital`, `fuel`, `charging_station`, or `alpine_hut` (required) - `radius` β€” Search radius in km, default 15, max 50 (optional) **Response:** GeoJSON FeatureCollection of POIs ### `GET /v1/tiles/{identifier}@{version}/{z}/{x}/{y}.mvt` Fetch a tile in Mapbox Vector Tile (MVT) format. **Path Parameters:** - `identifier` β€” Tileset identifier (e.g., from `MBTILES__1__IDENTIFIER`) - `version` β€” Tileset version (e.g., from `MBTILES__1__VERSION`) - `z` β€” Zoom level - `x` β€” Tile column coordinate - `y` β€” Tile row coordinate (converts from Web Mercator to TMS) **Special:** `contours@1.0.0` is auto-loaded if `contours.mbtiles` exists. **Gzip Negotiation:** Returns gzip-compressed tiles if client sends `Accept-Encoding: gzip`. **Response:** `application/vnd.mapbox-vector-tile` (MVT format) ### `GET /v1/styles/{identifier}@{version}/style.json` Returns a MapLibre GL style JSON with injected tile, font, and sprite URLs. **Path Parameters:** - `identifier` β€” Style identifier (e.g., `osm-bright-gl-style`) - `version` β€” Style version (e.g., `1.0.0`) **Query Parameters:** - `tiles` β€” Tile source in format `{tile_id}@{tile_version}` (required) - `fonts` β€” Font source in format `{font_id}@{font_version}` (required) **Available Styles:** - `dark-matter-gl-style@1.0.0` - `fiord-color-gl-style@1.0.0` - `maptiler-3d-gl-style@1.0.0` - `maptiler-terrain-gl-style@1.0.0` - `maptiler-basic-gl-style@1.0.0` - `maptiler-toner-gl-style@1.0.0` - `osm-bright-gl-style@1.0.0` - `positron-gl-style@1.0.0` - `osuv-style@1.0.0` **Response:** `application/json` with complete style JSON including: - Tile source URL: `/v1/tiles/{tiles}@{version}/{z}/{x}/{y}.mvt` - Glyph URL: `/v1/fonts/{fonts}@{version}/{fontstack}/{range}.pbf` - Sprite URL: `/v1/styles/{identifier}@{version}/sprite` - Contour layers (if contours.mbtiles detected): `contour-line` and `contour-label` (visible when toggled in frontend) ### `GET /v1/styles/{identifier}@{version}/sprite.json` Returns the sprite JSON index file. **Path Parameters:** - `identifier` β€” Style identifier - `version` β€” Style version **Response:** `application/json` with sprite image references and bounds ### `GET /v1/styles/{identifier}@{version}/sprite@2x.json` Returns the high-resolution (2x) sprite JSON index file. **Path Parameters:** - `identifier` β€” Style identifier - `version` β€” Style version **Response:** `application/json` with high-res sprite references ### `GET /v1/styles/{identifier}@{version}/sprite.png` Returns the sprite image (1x resolution). **Path Parameters:** - `identifier` β€” Style identifier - `version` β€” Style version **Response:** `image/png` ### `GET /v1/styles/{identifier}@{version}/sprite@2x.png` Returns the sprite image (2x resolution for retina displays). **Path Parameters:** - `identifier` β€” Style identifier - `version` β€” Style version **Response:** `image/png` ### `GET /v1/fonts/{identifier}@{version}/{stack}/{range}.pbf` Returns merged glyph data in Protocol Buffer format for font rendering. **Path Parameters:** - `identifier` β€” Font identifier (`fonts-gl`) - `version` β€” Font version (`1.0.0`) - `stack` β€” Comma-separated font stack (max 4 fonts, e.g., `Noto Sans Regular,Noto Sans Bold`) - `range` β€” Glyph range (e.g., `0-255`) **Response:** `application/vnd.google.protobuf` - Gzip-compressed if client sends `Accept-Encoding: gzip` - Merges glyphs from all fonts in stack, deduplicating by glyph ID ### `GET /v1/route/{identifier}@{version}?from={lat},{lon}&to={lat},{lon}&profile={profile}` Compute a walking or cycling route between two coordinates using the vector tile road network. No external routing engine required β€” routing is performed entirely server-side from the MBTiles data. **Path Parameters:** - `identifier` β€” Tileset identifier (same as used for `/v1/tiles/`) - `version` β€” Tileset version **Query Parameters:** - `from` β€” Start point as `lat,lon` (required) - `to` β€” End point as `lat,lon` (required) - `profile` β€” Routing profile: `foot` (default) or `bike` **Example:** ``` GET /v1/route/mytiles@1.0.0?from=48.137,11.575&to=48.155,11.602&profile=foot ``` **Response:** GeoJSON Feature with LineString geometry ```json { "type": "Feature", "geometry": { "type": "LineString", "coordinates": [[11.575, 48.137], ...] }, "properties": { "distance_km": 3.241, "duration_min": 43.2, "profile": "foot", "tiles_loaded": 36, "nodes": 12847 } } ``` **Profile weights** (OpenMapTiles `transportation` layer `class` attribute): | Road class | `foot` | `bike` | |---|---|---| | `footway`, `path`, `pedestrian` | preferred (1.0) | not passable | | `cycleway` | allowed (1.3) | preferred (1.0) | | `track` | 1.1 | 1.2 | | `residential`, `living_street` | 1.2–1.3 | 1.1 | | `tertiary` | 1.8 | 1.4 | | `secondary` | 2.5 | 1.6 | | `primary` | not passable | 2.5 | | `motorway`, `trunk` | not passable | not passable | | `steps` | 1.2 | not passable | **Duration estimate:** 4.5 km/h for `foot`, 15 km/h for `bike`. **Limits:** - Maximum bounding box: 500 tiles at zoom 14 (~30 km routes) - Start and end points must be within 500 m of a routable road - No turn restrictions (OSM relations are not stored in vector tiles) - Graph is built fresh per request (no caching); expect 0.5–3 s for 10–30 km routes **Error responses:** - `400` β€” invalid parameters or bounding box too large - `404` β€” no road network in area, or no route found between the two points ### `GET /v1/static/{identifier}@{version}/{file}` Serves vendored MapLibre GL JS/CSS static assets. **Path Parameters:** - `identifier` β€” Asset identifier (`maplibre-gl`) - `version` β€” Asset version (`5.19.0`) - `file` β€” File name: `maplibre-gl.css` or `maplibre-gl.js` **Response:** CSS or JavaScript file ## For the curious, advanced, or developers of this server itself Hosting your own vector map tiles to show them in a browser requires quite a few components: 1. **JavaScript and CSS** A Javascript and CSS library, such as [MapLibre GL](https://github.com/maplibre/maplibre-gl-js), and your own code to run this library, pointing it to a style file 2. **Style file** A JSON file that defines how the library should visually style the map data, and where it should find the map tiles, glyphs (fonts), and the sprite. This server transforms the built-in Style files on the fly to be able to refer to any map data. 3. **Glyphs** (fonts) Different fonts can be used for different labels and zoom levels, as defined in the Style file. The fonts must Signed Distance Field (SDF) fonts wrapped in a particular Protocol Buffer format. The Style file can refer to "stacks" of fonts; but unlike CSS, the server combines the fonts on the fly in an API where the resulting "font" has at most one glyph from each source font. 4. **Sprite** A sprite is actually 4 URLs: a JSON index file and a single PNG file, and a "@2x" JSON index file and PNG files for higher pixel ratio devices (e.g. Retina). The JSON files contains the offsets and sizes of images within corresponding PNG file. The style file refers the common "base" of these. For example, if the style file has `"sprite":"https://my.test/sprite"` then the 4 files must be at `https://my.test/sprite.json`, `https://my.test/sprite.png`, `https://my.test/sprite@2x.json` and `https://my.test/sprite@2x.png`. 5. **Vector map tiles** A set of often millions of tiles each covering a different location and different zoom level. These can be distributed as a single mbtiles file, but this is not the format that the Javascript library accepts. This on-the-fly conversion from the mbtiles file to tiles is the main feature of this server. The mbtiles file is a SQLite file, containing gzipped Mapbox Vector Tile tiles. This server leaves the un-gzipping to the browser, by sending tiles with a `content-encoding: gzip` header, which results in browser un-gzipping the tile data before it hits the Javascript. ## Licenses The code of the server itself is released under the MIT license. However, several components included in [mbtiles_s3_server/vendor/](./mbtiles_s3_server/vendor/) are released under different licenses. # support * My Project: https://github.com/markuman/sms * For the large planet.mbtiles generation and hosting, donations are welcome πŸ™‚ * paypal.me/MarkusBergholz * bc1qz33cf70vq82gxf8kps06j7lv7m2903hsnjak6k