Skip to content

Instantly share code, notes, and snippets.

@nautilytics
Created July 15, 2015 19:07
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save nautilytics/75cbbb2d854de608e81c to your computer and use it in GitHub Desktop.
Save nautilytics/75cbbb2d854de608e81c to your computer and use it in GitHub Desktop.
node-mapnik .pbf tile-server example
var config = require('../../config/database')
, _ = require('lodash-compat')
, mercator = require('./../../geo_utils/sphericalmercator')
, geographic = require('./../../geo_utils/geographic')
, crypto = require("crypto")
, zlib = require('zlib')
, mapnik = require('mapnik')
, utils = require('./../../config/utils');
if (mapnik.register_default_input_plugins)
mapnik.register_default_input_plugins();
var _geometry_field = 'geom'
, _table = 'us_county_500k'
, _fields = 'gid';
var createPostGISConnectionDetails = function (table) {
return _.defaults({table: table, geometry_field: _geometry_field}, config.postGIS);
};
module.exports = function (req, res) {
var bbox = mercator.xyz_to_envelope(+params.x, +params.y, +params.z, false);
// Create map
var map = new mapnik.Map(256, 256, mercator.proj4);
map.bufferSize = 10; // amount of edging provided for each tile rendered
// Create layer
var layer = new mapnik.Layer('tile', geographic.proj4);
var fieldTable = '(SELECT ' + [_geometry_field, _fields].join(',') + ' FROM ' + _table + ') AS "' + _table + '"';
layer.datasource = new mapnik.Datasource(new createPostGISConnectionDetails(fieldTable));
layer.styles = ['default'];
map.add_layer(layer);
var opts = {};
// Use tolerance of 32 for zoom levels below max
var _maxzoom = 14;
opts.tolerance = +params.z < _maxzoom ? 32 : 0;
res.setHeader('Content-Type', 'application/x-protobuf');
map.extent = bbox;
opts.buffer_size = map.bufferSize;
map.render(new mapnik.VectorTile(+params.z, +params.x, +params.y), opts, function (err, image) {
if (err || !image) {
res.removeHeader('Content-Encoding');
res.writeHead(500, {
'Content-Type': 'application/x-protobuf'
});
res.end();
return;
}
// Fake empty RGBA
image.isSolid(function (err, solid, key) {
if (err) {
res.writeHead(500, {
'Content-Type': 'text/plain'
});
res.end(err.message);
return;
}
// Solid handling.
var done = function (err, buffer) {
if (err) {
res.writeHead(500, {
'Content-Type': 'text/plain'
});
res.end(err.message);
return;
}
if (solid === false) {
res.send(buffer); // return response
return;
}
// Empty tiles are equivalent to no tile.
if (!key) {
res.removeHeader('Content-Encoding');
res.writeHead(404, {
'Content-Type': 'application/octet-stream'
});
res.end(); //new Buffer('Tile is blank or does not exist', "utf-8")
return;
}
// Fake a hex code by md5ing the key.
var mockrgb = crypto.createHash('md5').update(buffer).digest('hex').substr(0, 6);
buffer.solid = [parseInt(mockrgb.substr(0, 2), 16), parseInt(mockrgb.substr(2, 2), 16), parseInt(mockrgb.substr(4, 2), 16), 1].join(',');
res.send(buffer);
};
//Compress
res.setHeader('content-encoding', 'gzip');
zlib.gzip(image.getData(), done);
});
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment