Skip to content

Instantly share code, notes, and snippets.

@chriswhong
Created August 8, 2017 12:24
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chriswhong/542506728e70a9392bc104d544862628 to your computer and use it in GitHub Desktop.
Save chriswhong/542506728e70a9392bc104d544862628 to your computer and use it in GitHub Desktop.
A Vector Tile microservice for MapPLUTO (or any other data)

Background

We like building mapping applications with carto as the backend, because it gets us an instant map tiler and SQL API. However, carto's tiler is raster-based, and we would like to take advantage of mapboxGL for it's vector-based rendering. We discovered an undocumented vector tile endpoint in carto, which has enabled us to use vector tiles without any additional work

Problem

Using the undocumented MVT API has worked well enough to date, but when faced with a dataset with lots of very small contiguous polygons, the tiler seems to choke at low zoom levels, and does not include all data that should be rendered in a tile.

Solution

We have contemplated setting up a microservice to serve vector tiles of MapPLUTO, either cutting static tiles, or using some other tile server.

TileStrata is a good candidate for this task. It's an extensible nodejs-based tile server.

Using the TileStrate Vtile Plugin and a shapefile of Manhattan MapPLUTO with geometries in webmercator (EPSG:3857), a simple express.js tile server can be had with only 40 lines of code.

server.js reads a mapnik xml configuration (config.xml) that references the shapefile. After running server.js, MapPLUTO vector tiles will be available at this template: http://localhost:8080/maps/pluto/{z}/{x}/{y}/tile.pbf

Low-Maintenance Approach to Deployment

An idea for keeping this tile server downstream of the primary data source in carto is using a postinstall script to download the required shapefile from the carto SQL API. (the shapefile needs geometries in webmercator anyway, which can be quickly acoomplished with a SQL query).

  • Push code to dokku
  • Dokku runs NPM install
  • Postinstall script uses SHAPEFILE_URL environment variable to download the webmercator shapefile from the carto server
  • Postinstall script unzips the shapefile and makes sure it's named properly
  • Dokku uses a procfile to run the express server
  • Consume Vector Tiles!

Things to think about

  • Should we make it generic, so it's an "instantly turn any shapefile into a vector tile service" package?
  • Should we make it serve more that one tileset? This will add complexity but might also make things more efficient, and have a more friendly user experience as we can namespace several tilesets on the same domain.

Screenshot

This shows the vector tiles rendered from this experimental setup of TileStrata, styled in mapboxGL to show land use based on the landuse property in MapPLUTO labscommunityportal

<Map background-color="steelblue" srs="+init=epsg:3857">
<Layer name="layer0" status="on" srs="+init=epsg:3857">
<StyleName>polygon</StyleName>
<Datasource>
<Parameter name="type">shape</Parameter>
<!-- you can also point to your shapefile without the 'shp' extention -->
<Parameter name="file">data/support_mappluto.shp</Parameter>
</Datasource>
</Layer>
</Map>
const express = require('express')
const tilestrata = require('tilestrata');
const vtile = require('tilestrata-vtile');
const app = express()
const strata = tilestrata();
const common = {
xml: 'config.xml',
tileSize: 256,
metatile: 1,
bufferSize: 128,
overrideRenderOptions: function(opts, z, maxz) {
opts.simplify_distance = 1;
return opts;
}
};
strata
.layer('pluto')
.route('tile.pbf')
.use(vtile(common))
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.use(tilestrata.middleware({
server: strata,
prefix: '/maps'
}));
app.listen(3000, function () {
console.log('Tile Server listening on port 3000!')
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment