diff --git a/Dockerfile_light b/Dockerfile_light deleted file mode 100644 index b3926ee..0000000 --- a/Dockerfile_light +++ /dev/null @@ -1,12 +0,0 @@ -FROM node:6 -MAINTAINER Petr Sloup - -ENV NODE_ENV="production" -EXPOSE 80 -VOLUME /data -WORKDIR /data -ENTRYPOINT ["node", "/usr/src/app/", "-p", "80"] - -RUN mkdir -p /usr/src/app -COPY / /usr/src/app -RUN cd /usr/src/app && npm install --production diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..655f05b --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,3 @@ +@Library("jenkins-devops-scripts") _ +tileserver-gl { +} diff --git a/LICENSE.md b/LICENSE.md index 60d6118..1160c6d 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -2,6 +2,7 @@ TileServer GL ============= Copyright (c) 2016, Klokan Technologies GmbH +Copyright (c) 2018, Beat (markers feature) All rights reserved. diff --git a/README.md b/README.md index 044b31e..ef312ad 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,14 @@ -![tileserver-gl](https://cloud.githubusercontent.com/assets/59284/18173467/fa3aa2ca-7069-11e6-86b1-0f1266befeb6.jpeg) - - # TileServer GL -[![Build Status](https://travis-ci.org/klokantech/tileserver-gl.svg?branch=master)](https://travis-ci.org/klokantech/tileserver-gl) -[![Docker Hub](https://img.shields.io/badge/docker-hub-blue.svg)](https://hub.docker.com/r/klokantech/tileserver-gl/) Vector and raster maps with GL styles. Server side rendering by Mapbox GL Native. Map tile server for Mapbox GL JS, Android, iOS, Leaflet, OpenLayers, GIS via WMTS, etc. -## Get Started +This fork adds features used by BEAT, including: -Make sure you have Node.js version **6** installed (running `node -v` it should output something like `v6.11.3`). +* Marker support in the static (rendered) maps +* Prometheus compatible `/metrics` endpoint +* Improved `/health/` endpoint -Install `tileserver-gl` with server-side raster rendering of vector tiles with npm - -```bash -npm install -g tileserver-gl -``` - -Now download vector tiles from [OpenMapTiles](https://openmaptiles.org/downloads/). - -```bash -curl -o zurich_switzerland.mbtiles https://[GET-YOUR-LINK]/extracts/zurich_switzerland.mbtiles -``` - -Start `tileserver-gl` with the downloaded vector tiles. - -```bash -tileserver-gl zurich_switzerland.mbtiles -``` - -Alternatively, you can use the `tileserver-gl-light` package instead, which is pure javascript (does not have any native dependencies) and can run anywhere, but does not contain rasterization on the server side made with MapBox GL Native. - -## Using Docker - -An alternative to npm to start the packed software easier is to install [Docker](http://www.docker.com/) on your computer and then run in the directory with the downloaded MBTiles the command: - -```bash -docker run --rm -it -v $(pwd):/data -p 8080:80 klokantech/tileserver-gl -``` - -This will download and start a ready to use container on your computer and the maps are going to be available in webbrowser on localhost:8080. - -On laptop you can use [Docker Kitematic](https://kitematic.com/) and search "tileserver-gl" and run it, then drop in the 'data' folder the MBTiles. ## Documentation -You can read full documentation of this project at http://tileserver.readthedocs.io/. +You can read full documentation of the upstream project at http://tileserver.readthedocs.io/. diff --git a/README_light.md b/README_light.md deleted file mode 100644 index f31745d..0000000 --- a/README_light.md +++ /dev/null @@ -1,17 +0,0 @@ -# TileServer GL light -[![Build Status](https://travis-ci.org/klokantech/tileserver-gl.svg?branch=master)](https://travis-ci.org/klokantech/tileserver-gl) -[![Docker Hub](https://img.shields.io/badge/docker-hub-blue.svg)](https://hub.docker.com/r/klokantech/tileserver-gl/) - -Vector maps with GL styles. Map tile server for Mapbox Android, iOS, GL JS, Leaflet, OpenLayers, etc. without server side rendering. - -## Quickstart -Use `npm install -g tileserver-gl-light` to install the package from npm. - -Then you can simply run `tileserver-gl-light zurich_switzerland.mbtiles` to start the server for the given mbtiles. - -See also `tileserver-gl` which contains server side rendering. - -Prepared vector tiles can be downloaded from [OSM2VectorTiles](http://osm2vectortiles.org/). - -## Documentation -You can read full documentation of this project at http://tileserver.readthedocs.io/. \ No newline at end of file diff --git a/package.json b/package.json index 05bae9b..3d07070 100644 --- a/package.json +++ b/package.json @@ -19,13 +19,13 @@ "test": "mocha test/**.js --timeout 10000" }, "dependencies": { - "@mapbox/mapbox-gl-native": "3.5.4", + "@mapbox/mapbox-gl-native": "3.5.8", "@mapbox/mbtiles": "0.9.0", "@mapbox/sphericalmercator": "1.0.5", "@mapbox/vector-tile": "1.3.0", "advanced-pool": "0.3.3", "base64url": "2.0.0", - "canvas": "1.6.8", + "canvas": "^1.6.8", "clone": "2.1.1", "color": "1.0.3", "commander": "2.1.0", @@ -35,11 +35,14 @@ "handlebars": "4.0.11", "http-shutdown": "^1.2.0", "morgan": "1.9.0", + "nomnom": "1.8.1", + "npm": "^5.7.1", "pbf": "3.0.5", "proj4": "2.4.4", "request": "2.83.0", - "sharp": "0.18.2", - "tileserver-gl-styles": "1.2.0" + "sharp": "^0.20.0", + "tileserver-gl-styles": "1.2.0", + "prom-client": "11.0.0" }, "devDependencies": { "should": "^11.2.0", diff --git a/public/resources/images/dropoff-marker.png b/public/resources/images/dropoff-marker.png new file mode 100644 index 0000000..4ba2e91 Binary files /dev/null and b/public/resources/images/dropoff-marker.png differ diff --git a/public/resources/images/pickup-marker.png b/public/resources/images/pickup-marker.png new file mode 100644 index 0000000..334eaba Binary files /dev/null and b/public/resources/images/pickup-marker.png differ diff --git a/src/avgresp.js b/src/avgresp.js new file mode 100644 index 0000000..7ba796d --- /dev/null +++ b/src/avgresp.js @@ -0,0 +1,42 @@ +#!/usr/bin/env node +'use strict'; + +var prometheus = require('prom-client'); + +module.exports = { + avgresp: avgresp +}; + + +var history; +var samples; +var fullhistory = false; + +/** + * Create middleware to record and output average response times + * @param {Object} options + * @return {function} + */ +function avgresp(options) { + + var opts = options || {}; + + history = 50; + samples = new Array(history); + var currentIndex = 0; + + const respSummary = new prometheus.Summary({ + name: "tileserver_static_latency_seconds", + help: "The tileserver response time in seconds" + }); + + return function avgresp(req, res, next) { + var end = respSummary.startTimer(); + + res.on('finish', function() { + end(); + }) + + next(); + } +} diff --git a/src/serve_rendered.js b/src/serve_rendered.js index aca8295..90de152 100644 --- a/src/serve_rendered.js +++ b/src/serve_rendered.js @@ -23,6 +23,8 @@ var Canvas = require('canvas'), var utils = require('./utils'); +var markerSize = 20; + var FLOAT_PATTERN = '[+-]?(?:\\d+|\\d+\.?\\d+)'; var getScale = function(scale) { @@ -263,6 +265,27 @@ module.exports = function(options, repo, params, id, dataResolver) { styleJSON.glyphs = 'fonts://' + styleJSON.glyphs; } + var markerImages = []; + var markerImageNames = ['pickup','dropoff']; + + + var markerLoadPromise = new Promise(function(resolveCallback, rejectCallback) { + + markerImageNames.forEach(function(imageName){ + fs.readFile(path.join(__dirname, "../public/resources/images/") + imageName + '-marker.png', function(err, fileData) { + + if (err) { + rejectCallback(err); + } + + var mkrImage = new Canvas.Image(); + mkrImage.src = fileData; + markerImages.push(mkrImage); + }); + }); + resolveCallback(); + }); + var tileJSON = { 'tilejson': '2.0.0', 'name': styleJSON.name, @@ -568,6 +591,20 @@ module.exports = function(options, repo, params, id, dataResolver) { ctx.stroke(); } + if (query.showMarkers && query.showMarkers == 1) { + // Add the markers, if requested to do so. + + var markers = [ + [markerImages[0], precisePx(path[0],z).map(function(loc,idx){ return (idx ==1)? loc - markerSize/2: loc - markerSize/2;})], + [markerImages[1], precisePx(path[path.length-1],z).map(function(loc,idx){ return (idx == 1)?loc - markerSize/2: loc - markerSize/2;})] + ]; + + markers.forEach(function(imgSpec){ + var coordinates = imgSpec[1]; + ctx.drawImage(imgSpec[0], coordinates[0], coordinates[1], markerSize, markerSize); + }); + } + return canvas.toBuffer(); }; @@ -615,7 +652,7 @@ module.exports = function(options, repo, params, id, dataResolver) { format = req.params.format; if (z < 0) { - return res.status(404).send('Invalid zoom'); + return res.status(400).send('Invalid zoom'); } var transformer = raw ? @@ -751,7 +788,7 @@ module.exports = function(options, repo, params, id, dataResolver) { return res.send(info); }); - return Promise.all([fontListingPromise, renderersReadyPromise]).then(function() { + return Promise.all([markerLoadPromise, fontListingPromise, renderersReadyPromise]).then(function() { return app; }); diff --git a/src/server.js b/src/server.js index db67be4..64cf3dc 100644 --- a/src/server.js +++ b/src/server.js @@ -21,7 +21,11 @@ var packageJson = require('../package'), serve_rendered = null, serve_style = require('./serve_style'), serve_data = require('./serve_data'), - utils = require('./utils'); + utils = require('./utils'), + avgresp = require('./avgresp'), + prometheus = require('prom-client'); + +prometheus.collectDefaultMetrics({ timeout: 5000 }); var isLight = packageJson.name.slice(-6) == '-light'; if (!isLight) { @@ -52,6 +56,8 @@ function start(opts) { })); } + app.use(/\/styles\/.*\/static\/.*$/, avgresp.avgresp()); + var config = opts.config || null; var configPath = null; if (opts.configPath) { @@ -391,12 +397,30 @@ function start(opts) { console.log('Startup complete'); startupComplete = true; }); + app.get('/health', function(req, res, next) { + + var healthTemplate = { + "service": "tileserver", + "status": "200", + "message": "OK" + }; + + var statusCode = 200; + if (startupComplete) { - return res.status(200).send('OK'); + statusCode = 200; + healthTemplate.message = "OK"; } else { - return res.status(503).send('Starting'); + statusCode = 503; + healthTemplate.message = "Starting"; } + healthTemplate.status = statusCode; + return res.status(statusCode).send(healthTemplate); + }); + + app.get('/metrics', function(req, res, next){ + res.end(prometheus.register.metrics()); }); var server = app.listen(process.env.PORT || opts.port, process.env.BIND || opts.bind, function() { diff --git a/test/static.js b/test/static.js index fc3b59b..ccb5aef 100644 --- a/test/static.js +++ b/test/static.js @@ -49,7 +49,7 @@ describe('Static endpoints', function() { testStatic(prefix, '0,0,0/256x256', 'gif', 400); testStatic(prefix, '0,0,0/256x256', 'png', 404, 1); - testStatic(prefix, '0,0,-1/256x256', 'png', 404); + testStatic(prefix, '0,0,-1/256x256', 'png', 400); testStatic(prefix, '0,0,0/256.5x256.5', 'png', 404); testStatic(prefix, '0,0,0,/256x256', 'png', 404); @@ -91,6 +91,11 @@ describe('Static endpoints', function() { testStatic(prefix, 'auto/20x20', 'png', 200, 2, /image\/png/, '?path=10,10|20,20'); testStatic(prefix, 'auto/200x200', 'png', 200, 3, /image\/png/, '?path=-10,-10|-20,-20'); }); + + describe('with markers', function() { + testStatic(prefix, 'auto/20x20', 'png', 200, 2, /image\/png/, '?path=10,10|20,20&showMarkers=1'); + testStatic(prefix, 'auto/20x20', 'png', 200, 2, /image\/png/, '?path=10,10|20,20&showMarkers=0'); + }) }); describe('invalid requests return 4xx', function() {