GitHub - markuman/sms: simple mbtiles server

6 min read Original article β†—

πŸ—ΊοΈ OSM - self host the entire planet 🌎 in ~30 minutes πŸš€

simple mbtiles server

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

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://<YOUR_SMS_SERVICE_DEPLOYMENT>/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 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:

{
  "contours": true | false
}

Indicates whether contours.mbtiles was detected and 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/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, 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/ 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