WiP. Replace image markers resolution-independent Canvas versions.
This commit is contained in:
parent
8cadbef61e
commit
3e84297b30
6 changed files with 194 additions and 170 deletions
11
.eslintrc.json
Normal file
11
.eslintrc.json
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"extends": "eslint:recommended",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 6,
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"node": true,
|
||||||
|
"es6": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,7 +6,7 @@ This fork adds features used by BEAT, including:
|
||||||
|
|
||||||
* Marker support in the static (rendered) maps
|
* Marker support in the static (rendered) maps
|
||||||
* Prometheus compatible `/metrics` endpoint
|
* Prometheus compatible `/metrics` endpoint
|
||||||
* Improved `/health/` endpoint
|
* Improved `/health` endpoint
|
||||||
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
|
||||||
15
package.json
15
package.json
|
|
@ -36,17 +36,24 @@
|
||||||
"http-shutdown": "^1.2.0",
|
"http-shutdown": "^1.2.0",
|
||||||
"morgan": "1.9.0",
|
"morgan": "1.9.0",
|
||||||
"nomnom": "1.8.1",
|
"nomnom": "1.8.1",
|
||||||
"npm": "^5.7.1",
|
"npm": "^5.8.0",
|
||||||
"pbf": "3.0.5",
|
"pbf": "3.0.5",
|
||||||
"proj4": "2.4.4",
|
"proj4": "2.4.4",
|
||||||
|
"prom-client": "11.0.0",
|
||||||
"request": "2.83.0",
|
"request": "2.83.0",
|
||||||
"sharp": "^0.20.0",
|
"sharp": "^0.20.0",
|
||||||
"tileserver-gl-styles": "1.2.0",
|
"tileserver-gl-styles": "1.2.0"
|
||||||
"prom-client": "11.0.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"eslint-config-standard": "^11.0.0",
|
||||||
|
"eslint-plugin-import": "^2.11.0",
|
||||||
|
"eslint-plugin-node": "^6.0.1",
|
||||||
|
"eslint-plugin-promise": "^3.7.0",
|
||||||
|
"eslint-plugin-standard": "^3.1.0",
|
||||||
|
"mocha": "^3.2.0",
|
||||||
"should": "^11.2.0",
|
"should": "^11.2.0",
|
||||||
"mocha": "^3.2.0",
|
"mocha": "^3.2.0",
|
||||||
"supertest": "^3.0.0"
|
"supertest": "^3.0.0",
|
||||||
|
"eslint": "^4.19.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 474 B |
Binary file not shown.
|
Before Width: | Height: | Size: 486 B |
|
|
@ -1,33 +1,30 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var advancedPool = require('advanced-pool'),
|
let advancedPool = require('advanced-pool');
|
||||||
fs = require('fs'),
|
let fs = require('fs');
|
||||||
path = require('path'),
|
let path = require('path');
|
||||||
url = require('url'),
|
let url = require('url');
|
||||||
util = require('util'),
|
let util = require('util');
|
||||||
zlib = require('zlib');
|
let zlib = require('zlib');
|
||||||
|
|
||||||
// sharp has to be required before node-canvas
|
// sharp has to be required before node-canvas
|
||||||
// see https://github.com/lovell/sharp/issues/371
|
// see https://github.com/lovell/sharp/issues/371
|
||||||
var sharp = require('sharp');
|
let sharp = require('sharp');
|
||||||
|
let Canvas = require('canvas');
|
||||||
|
let clone = require('clone');
|
||||||
|
let Color = require('color');
|
||||||
|
let express = require('express');
|
||||||
|
let mercator = new (require('@mapbox/sphericalmercator'))();
|
||||||
|
let mbgl = require('@mapbox/mapbox-gl-native');
|
||||||
|
let mbtiles = require('@mapbox/mbtiles');
|
||||||
|
let proj4 = require('proj4');
|
||||||
|
let request = require('request');
|
||||||
|
|
||||||
var Canvas = require('canvas'),
|
let utils = require('./utils');
|
||||||
clone = require('clone'),
|
let markerSize = 15;
|
||||||
Color = require('color'),
|
let FLOAT_PATTERN = '[+-]?(?:\\d+|\\d+\.?\\d+)';
|
||||||
express = require('express'),
|
|
||||||
mercator = new (require('@mapbox/sphericalmercator'))(),
|
|
||||||
mbgl = require('@mapbox/mapbox-gl-native'),
|
|
||||||
mbtiles = require('@mapbox/mbtiles'),
|
|
||||||
proj4 = require('proj4'),
|
|
||||||
request = require('request');
|
|
||||||
|
|
||||||
var utils = require('./utils');
|
let getScale = function(scale) {
|
||||||
|
|
||||||
var markerSize = 20;
|
|
||||||
|
|
||||||
var FLOAT_PATTERN = '[+-]?(?:\\d+|\\d+\.?\\d+)';
|
|
||||||
|
|
||||||
var getScale = function(scale) {
|
|
||||||
return (scale || '@1x').slice(1, 2) | 0;
|
return (scale || '@1x').slice(1, 2) | 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -40,18 +37,18 @@ mbgl.on('message', function(e) {
|
||||||
/**
|
/**
|
||||||
* Lookup of sharp output formats by file extension.
|
* Lookup of sharp output formats by file extension.
|
||||||
*/
|
*/
|
||||||
var extensionToFormat = {
|
let extensionToFormat = {
|
||||||
'.jpg': 'jpeg',
|
'.jpg': 'jpeg',
|
||||||
'.jpeg': 'jpeg',
|
'.jpeg': 'jpeg',
|
||||||
'.png': 'png',
|
'.png': 'png',
|
||||||
'.webp': 'webp'
|
'.webp': 'webp',
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache of response data by sharp output format and color. Entry for empty
|
* Cache of response data by sharp output format and color. Entry for empty
|
||||||
* string is for unknown or unsupported formats.
|
* string is for unknown or unsupported formats.
|
||||||
*/
|
*/
|
||||||
var cachedEmptyResponses = {
|
let cachedEmptyResponses = {
|
||||||
'': new Buffer(0)
|
'': new Buffer(0)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -63,26 +60,9 @@ var cachedEmptyResponses = {
|
||||||
*/
|
*/
|
||||||
function createEmptyResponse(format, color, callback) {
|
function createEmptyResponse(format, color, callback) {
|
||||||
if (!format || format === 'pbf') {
|
if (!format || format === 'pbf') {
|
||||||
callback(null, {data: cachedEmptyResponses['']});
|
callback(null, { data: cachedEmptyResponses[''] });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format === 'jpg') {
|
|
||||||
format = 'jpeg';
|
|
||||||
}
|
|
||||||
if (!color) {
|
|
||||||
color = 'rgba(255,255,255,0)';
|
|
||||||
}
|
|
||||||
|
|
||||||
var cacheKey = format + ',' + color;
|
|
||||||
var data = cachedEmptyResponses[cacheKey];
|
|
||||||
if (data) {
|
|
||||||
callback(null, {data: data});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create an "empty" response image
|
|
||||||
var color = new Color(color);
|
|
||||||
var array = color.array();
|
var array = color.array();
|
||||||
var channels = array.length == 4 && format != 'jpeg' ? 4 : 3;
|
var channels = array.length == 4 && format != 'jpeg' ? 4 : 3;
|
||||||
sharp(new Buffer(array), {
|
sharp(new Buffer(array), {
|
||||||
|
|
@ -95,7 +75,7 @@ function createEmptyResponse(format, color, callback) {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
cachedEmptyResponses[cacheKey] = buffer;
|
cachedEmptyResponses[cacheKey] = buffer;
|
||||||
}
|
}
|
||||||
callback(null, {data: buffer});
|
callback(null, { data: buffer });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,10 +88,10 @@ module.exports = function(options, repo, params, id, dataResolver) {
|
||||||
scalePattern += i.toFixed();
|
scalePattern += i.toFixed();
|
||||||
}
|
}
|
||||||
scalePattern = '@[' + scalePattern + ']x';
|
scalePattern = '@[' + scalePattern + ']x';
|
||||||
|
4
|
||||||
var lastModified = new Date().toUTCString();
|
var lastModified = new Date().toUTCString();
|
||||||
|
|
||||||
var rootPath = options.paths.root;
|
// var rootPath = options.paths.root;
|
||||||
|
|
||||||
var watermark = params.watermark || options.watermark;
|
var watermark = params.watermark || options.watermark;
|
||||||
|
|
||||||
|
|
@ -164,9 +144,9 @@ module.exports = function(options, repo, params, id, dataResolver) {
|
||||||
utils.getFontsPbf(
|
utils.getFontsPbf(
|
||||||
null, options.paths[protocol], fontstack, range, existingFonts
|
null, options.paths[protocol], fontstack, range, existingFonts
|
||||||
).then(function(concated) {
|
).then(function(concated) {
|
||||||
callback(null, {data: concated});
|
callback(null, { data: concated });
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
callback(err, {data: null});
|
callback(err, { data: null });
|
||||||
});
|
});
|
||||||
} else if (protocol == 'mbtiles') {
|
} else if (protocol == 'mbtiles') {
|
||||||
var parts = req.url.split('/');
|
var parts = req.url.split('/');
|
||||||
|
|
@ -206,7 +186,7 @@ module.exports = function(options, repo, params, id, dataResolver) {
|
||||||
|
|
||||||
callback(null, response);
|
callback(null, response);
|
||||||
});
|
});
|
||||||
} else if (protocol == 'http' || protocol == 'https') {
|
} else if (protocol === 'http' || protocol === 'https') {
|
||||||
request({
|
request({
|
||||||
url: req.url,
|
url: req.url,
|
||||||
encoding: null,
|
encoding: null,
|
||||||
|
|
@ -266,12 +246,12 @@ module.exports = function(options, repo, params, id, dataResolver) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var markerImages = [];
|
var markerImages = [];
|
||||||
var markerImageNames = ['pickup','dropoff'];
|
var markerImageNames = ['pickup', 'dropoff'];
|
||||||
|
|
||||||
|
|
||||||
var markerLoadPromise = new Promise(function(resolveCallback, rejectCallback) {
|
var markerLoadPromise = new Promise(function(resolveCallback, rejectCallback) {
|
||||||
|
|
||||||
markerImageNames.forEach(function(imageName){
|
markerImageNames.forEach(function(imageName) {
|
||||||
fs.readFile(path.join(__dirname, "../public/resources/images/") + imageName + '-marker.png', function(err, fileData) {
|
fs.readFile(path.join(__dirname, "../public/resources/images/") + imageName + '-marker.png', function(err, fileData) {
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
@ -408,11 +388,13 @@ module.exports = function(options, repo, params, id, dataResolver) {
|
||||||
width != width || height != height) {
|
width != width || height != height) {
|
||||||
return res.status(400).send('Invalid size');
|
return res.status(400).send('Invalid size');
|
||||||
}
|
}
|
||||||
if (format == 'png' || format == 'webp') {
|
|
||||||
} else if (format == 'jpg' || format == 'jpeg') {
|
let formatIndex = ['jpg', 'jpeg', 'png', 'webp'].indexOf(format);
|
||||||
format = 'jpeg';
|
|
||||||
} else {
|
if (formatIndex == -1) {
|
||||||
return res.status(400).send('Invalid format');
|
return res.status(400).send('Invalid format');
|
||||||
|
} else if (formatIndex < 2) {
|
||||||
|
format = 'jpeg';
|
||||||
}
|
}
|
||||||
|
|
||||||
var pool = map.renderers[scale];
|
var pool = map.renderers[scale];
|
||||||
|
|
@ -471,11 +453,11 @@ module.exports = function(options, repo, params, id, dataResolver) {
|
||||||
(options.formatQuality || {})[format];
|
(options.formatQuality || {})[format];
|
||||||
|
|
||||||
if (format == 'png') {
|
if (format == 'png') {
|
||||||
image.png({adaptiveFiltering: false});
|
image.png({ adaptiveFiltering: false });
|
||||||
} else if (format == 'jpeg') {
|
} else if (format == 'jpeg') {
|
||||||
image.jpeg({quality: formatQuality || 80});
|
image.jpeg({ quality: formatQuality || 80 });
|
||||||
} else if (format == 'webp') {
|
} else if (format == 'webp') {
|
||||||
image.webp({quality: formatQuality || 90});
|
image.webp({ quality: formatQuality || 90 });
|
||||||
}
|
}
|
||||||
image.toBuffer(function(err, buffer, info) {
|
image.toBuffer(function(err, buffer, info) {
|
||||||
if (!buffer) {
|
if (!buffer) {
|
||||||
|
|
@ -539,6 +521,38 @@ module.exports = function(options, repo, params, id, dataResolver) {
|
||||||
return path;
|
return path;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var drawMarker = function(ctx, coordinates, scale, outerColour = "rgb(0,0,0)", innerColour = "rgb(255,255,255)", outerRadius = markerSize, innerRadius = markerSize * 0.35) {
|
||||||
|
|
||||||
|
[outerRadius, innerRadius, coordinates[0], coordinates[1]].map(console.log);
|
||||||
|
|
||||||
|
outerRadius = parseInt(outerRadius);
|
||||||
|
innerRadius = parseInt(innerRadius);
|
||||||
|
let x = parseInt(coordinates[0]);
|
||||||
|
let y = parseInt(coordinates[1]);
|
||||||
|
|
||||||
|
let validParams = [outerRadius, innerRadius, x, y].reduce(function(acc, element) {
|
||||||
|
if (isNaN(element)) {
|
||||||
|
console.log("element: " + element + " is invalid.");
|
||||||
|
}
|
||||||
|
return acc && !isNaN(element);
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
if (!validParams) {
|
||||||
|
console.log("invalid parameters!");
|
||||||
|
}
|
||||||
|
// outer circle.
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, outerRadius, 0, 2 * Math.PI, false);
|
||||||
|
ctx.fillStyle = outerColour;
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// inner circle.
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, innerRadius, 0, 2 * Math.PI, false);
|
||||||
|
ctx.fillStyle = innerColour;
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
|
||||||
var renderOverlay = function(z, x, y, bearing, pitch, w, h, scale,
|
var renderOverlay = function(z, x, y, bearing, pitch, w, h, scale,
|
||||||
path, query) {
|
path, query) {
|
||||||
if (!path || path.length < 2) {
|
if (!path || path.length < 2) {
|
||||||
|
|
@ -593,16 +607,8 @@ module.exports = function(options, repo, params, id, dataResolver) {
|
||||||
|
|
||||||
if (query.showMarkers && query.showMarkers == 1) {
|
if (query.showMarkers && query.showMarkers == 1) {
|
||||||
// Add the markers, if requested to do so.
|
// Add the markers, if requested to do so.
|
||||||
|
drawMarker(ctx,precisePx(path[path.length-1],z),scale, "rgba(179, 0, 0, 0.7)");
|
||||||
var markers = [
|
drawMarker(ctx,precisePx(path[0],z),scale, "rgba(0, 151, 25, 0.7)");
|
||||||
[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();
|
return canvas.toBuffer();
|
||||||
|
|
@ -632,7 +638,7 @@ module.exports = function(options, repo, params, id, dataResolver) {
|
||||||
if (options.serveStaticMaps !== false) {
|
if (options.serveStaticMaps !== false) {
|
||||||
var staticPattern =
|
var staticPattern =
|
||||||
'/' + id + '/static/:raw(raw)?/%s/:width(\\d+)x:height(\\d+)' +
|
'/' + id + '/static/:raw(raw)?/%s/:width(\\d+)x:height(\\d+)' +
|
||||||
':scale(' + scalePattern + ')?\.:format([\\w]+)';
|
':scale(' + scalePattern + ')?.:format([\\w]+)';
|
||||||
|
|
||||||
var centerPattern =
|
var centerPattern =
|
||||||
util.format(':x(%s),:y(%s),:z(%s)(@:bearing(%s)(,:pitch(%s))?)?',
|
util.format(':x(%s),:y(%s),:z(%s)(@:bearing(%s)(,:pitch(%s))?)?',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue