I'm trying to find out about vector tile formats. There doesn't seem to be any documentation on vector MBTiles. ( https://github.com/mapbox/mbtiles-spec/issues/43 ). Can I regard vector MBTiles as a competitor to Mapbox Vector Tiles ? Or are they doing something different?
[GIS] Vector tile formats — vector MBTiles and MVF
mbtilespbfvector
Related Solutions
The best solution for hosting MBTiles depends on the scale. The solution for 10000 tile requests per month can be very different than the best solution for handling tens or hundreds of requests per second, but S3 works pretty well for both.
S3 can be much cheaper than MapBox if your hosting your own data and not using any of their rendering. At large enough scale S3 can start to be expensive, and it can be much cheaper to serve tiles from a VPS.
I upload a lot of tiles to s3, and my current solution is to launch a small or micro ec2 instance in the aws region the bucket is in(usually US-east), copy the MBTiles file to it with scp, then extract the tiles with MBUtil, and upload them with s3cmd. If you do this it can be handy to create an AMI all setup for this purpose, then just launch it when you need it.
Update 9/2017 I've written a utility for uploading tiles from an MBTiles file to s3 in parallel, using 100 threads im able to upload about 500 tiles per second. Script is available on github
As pointed out by @Greg, instead of TileStream (my first attempt) you should use Tilelive to host your own vector tiles.
Tilelive isn't a server itself but a backend framework that deals with tiles in different formats from different sources. But it's based on Node.js so you can turn it into a server in a pretty straight-forward way. To read tiles from a .mbtiles
source as exported by Mapbox Studio, you need the node-mbtiles tilelive module.
Side note: Current Mapbox Studio has a bug under Windows and OS X that prevents an exported .mbtiles
file to show up at your chosen destination. Workaround: Just grab the latest export-xxxxxxxx.mbtiles
file in ~/.mapbox-studio/cache
.
I found two server implementations (ten20 tile server by alexbirkett and TileServer by hanchao) who both use Express.js as a web app server.
Here is my minimalistic approach which is loosely based on these implementations:
Install Node.js
Grab the node packages with
npm install @mapbox/tilelive @mapbox/mbtiles express
Implement the server in the file
server.js
:var express = require('express'); var http = require('http'); var app = express(); var tilelive = require('tilelive'); require('mbtiles').registerProtocols(tilelive); //Depending on the OS the path might need to be 'mbtiles:///' on OS X and linux tilelive.load('mbtiles://path/to/osm_roads.mbtiles', function(err, source) { if (err) { throw err; } app.set('port', 7777); app.use(function(req, res, next) { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); next(); }); app.get(/^\/v2\/tiles\/(\d+)\/(\d+)\/(\d+).pbf$/, function(req, res){ var z = req.params[0]; var x = req.params[1]; var y = req.params[2]; console.log('get tile %d, %d, %d', z, x, y); source.getTile(z, x, y, function(err, tile, headers) { if (err) { res.status(404) res.send(err.message); console.log(err.message); } else { res.set(headers); res.send(tile); } }); }); http.createServer(app).listen(app.get('port'), function() { console.log('Express server listening on port ' + app.get('port')); }); });
Note: The
Access-Control-Allow-...
headers enable cross-origin resource sharing (CORS) so webpages served from a different server may access the tiles.Run it with
node server.js
Set up the webpage using Mapbox GL JS in
minimal.html
:<!DOCTYPE html > <html> <head> <meta charset='UTF-8'/> <title>Mapbox GL JS rendering my own tiles</title> <link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.4.0/mapbox-gl.css' rel='stylesheet' /> <script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.4.0/mapbox-gl.js'></script> <style> body { margin:0; padding:0 } #map { position:absolute; top:0; bottom:50px; width:100%; } </style> </head> <body> <div id='map'> </div> <script> var map = new mapboxgl.Map({ container: 'map', center: [46.8, 8.5], zoom: 7, style: 'minimal.json' }); </script> </body> </html>
Indicate the location of the tile source and style the layers with the following
minimal.json
:{ "version": 6, "constants": { "@background": "#808080", "@road": "#000000" }, "sources": { "osm_roads": { "type": "vector", "tiles": [ "http://localhost:7777/v2/tiles/{z}/{x}/{y}.pbf" ], "minzoom": 0, "maxzoom": 12 } }, "layers": [{ "id": "background", "type": "background", "paint": { "background-color": "@background" } }, { "id": "roads", "type": "line", "source": "osm_roads", "source-layer": "roads", "paint": { "line-color": "@road" } }] }
Serve the webpage and rejoice.
Best Answer
See https://github.com/osm2vectortiles/osm2vectortiles/issues/467 . MBTiles is a container (roughly, a SQLite database). It's normally used to hold PNGs or JPGs, and that's all the specification allows. But there's a widespread habit of storing Mapbox Vector Tiles inside the MBTiles database. As far as I can tell the main advantage of this is that you have all your tiles in one file, rather than having millions of tile files around.
Note that the individual vector tiles are sometimes (always?) zipped.
https://github.com/mapbox/mbutil can be used to extract the Mapbox Vector Tiles from the MBTiles file with
mb-util --image_format=pbf
. In the case I've examined, despite having the.pbf
extension, the resulting tiles are really zips -- so unzip2698.pbf
to get2698
, which is the real PBF (Google ProtoBuf) file.