added capability of staticmap endpoint to draw multiple paths

This commit is contained in:
Benedikt Brandtner 2022-10-07 17:39:11 +02:00
parent ec0ab3da5c
commit 4ba9bac7d6
2 changed files with 66 additions and 28 deletions

View file

@ -38,6 +38,7 @@ Static images
* ``path`` - comma-separated ``lng,lat``, pipe-separated pairs
* e.g. ``5.9,45.8|5.9,47.8|10.5,47.8|10.5,45.8|5.9,45.8``
* can be provided multiple times
* ``latlng`` - indicates coordinates are in ``lat,lng`` order rather than the usual ``lng,lat``
* ``fill`` - color to use as the fill (e.g. ``red``, ``rgba(255,255,255,0.5)``, ``#0000ff``)
@ -63,8 +64,8 @@ Static images
* scales with ``scale`` parameter since image placement is relative to it's size
* e.g. ``2,-4`` - Image will be moved 2 pixel to the right and 4 pixel in the upwards direction from the provided location
* can be provided multiple times
* e.g. ``5.9,45.8|marker-start.svg|scale:0.5|offset:2,-4``
* can be provided multiple times
* ``padding`` - "percentage" padding for fitted endpoints (area-based and path autofit)

View file

@ -135,11 +135,37 @@ const parseCoordinates = (coordinatePair, query, transformer) => {
return parsedCoordinates;
};
const extractPathFromQuery = (query, transformer) => {
const pathParts = (query.path || '').split('|');
const path = [];
/**
* Parses paths provided via query into a list of path objects.
* @param {Object} query Request query parameters.
* @param {Function} transformer Optional transform function.
*/
const extractPathsFromQuery = (query, transformer) => {
// Return an empty list if no paths have been provided
if (!query.path) {
return [];
}
const paths = [];
// Check if multiple paths have been provided and mimic a list if it's a
// single path.
const providedPaths = Array.isArray(query.path) ? query.path : [query.path];
// Iterate through paths, parse and validate them
for (const provided_path of providedPaths) {
const currentPath = [];
// Extract coordinate-list from path
const pathParts = (provided_path || '').split('|');
// Iterate through coordinate-list, parse the coordinates and validate them
for (const pair of pathParts) {
// Extract coordinates from coordinate pair
const pairParts = pair.split(',');
// Ensure we have two coordinates
if (pairParts.length === 2) {
const pair = parseCoordinates(pairParts, query, transformer);
@ -148,10 +174,18 @@ const extractPathFromQuery = (query, transformer) => {
continue;
}
path.push(pair);
// Add the coordinate-pair to the current path if they are valid
currentPath.push(pair);
}
}
return path;
// Extend list of paths with current path if it contains coordinates
if (currentPath.length) {
paths.push(currentPath)
}
}
return paths;
};
/**
@ -371,11 +405,11 @@ const drawMarkers = async (ctx, markers, z) => {
/**
* Draws a list of coordinates onto a canvas and styles the resulting path.
* @param {Object} ctx Canvas context object.
* @param {List[Number]} path List of coordinates parsed by extractPathFromQuery.
* @param {List[Number]} path List of coordinates.
* @param {Object} query Request query parameters.
* @param {Number} z Map zoom level.
*/
const drawPath = async (ctx, path, query, z) => {
const drawPath = (ctx, path, query, z) => {
if (!path || path.length < 2) {
return null;
}
@ -435,8 +469,8 @@ const drawPath = async (ctx, path, query, z) => {
}
}
const renderOverlay = async (z, x, y, bearing, pitch, w, h, scale, path, markers, query) => {
if ((!path || path.length < 2) && (!markers || markers.length === 0)) {
const renderOverlay = async (z, x, y, bearing, pitch, w, h, scale, paths, markers, query) => {
if ((!paths || paths.length === 0) && (!markers || markers.length === 0)) {
return null;
}
@ -463,7 +497,10 @@ const renderOverlay = async (z, x, y, bearing, pitch, w, h, scale, path, markers
ctx.translate(-center[0] + w / 2, -center[1] + h / 2);
}
// Draw provided paths if any
for (const path of paths) {
drawPath(ctx, path, query, z);
}
// Await drawing of markers before rendering the canvas
await drawMarkers(ctx, markers, z);
@ -727,9 +764,9 @@ export const serve_rendered = {
y = ll[1];
}
const path = extractPathFromQuery(req.query, transformer);
const paths = extractPathsFromQuery(req.query, transformer);
const markers = extractMarkersFromQuery(req.query, options, transformer);
const overlay = await renderOverlay(z, x, y, bearing, pitch, w, h, scale, path, markers, req.query);
const overlay = await renderOverlay(z, x, y, bearing, pitch, w, h, scale, paths, markers, req.query);
return respondImage(item, z, x, y, bearing, pitch, w, h, scale, format, res, next, overlay, 'static');
});
@ -767,9 +804,9 @@ export const serve_rendered = {
const bearing = 0;
const pitch = 0;
const path = extractPathFromQuery(req.query, transformer);
const paths = extractPathsFromQuery(req.query, transformer);
const markers = extractMarkersFromQuery(req.query, options, transformer);
const overlay = await renderOverlay(z, x, y, bearing, pitch, w, h, scale, path, markers, req.query);
const overlay = await renderOverlay(z, x, y, bearing, pitch, w, h, scale, paths, markers, req.query);
return respondImage(item, z, x, y, bearing, pitch, w, h, scale, format, res, next, overlay, 'static');
};
@ -819,7 +856,7 @@ export const serve_rendered = {
const transformer = raw ?
mercator.inverse.bind(mercator) : item.dataProjWGStoInternalWGS;
const path = extractPathFromQuery(req.query, transformer);
const paths = extractPathsFromQuery(req.query, transformer);
const markers = extractMarkersFromQuery(req.query, options, transformer);
// Extract coordinates from markers
@ -829,7 +866,7 @@ export const serve_rendered = {
}
// Create array with coordinates from markers and path
const coords = new Array().concat(path).concat(markerCoordinates);
const coords = new Array().concat(paths.flat()).concat(markerCoordinates);
// Check if we have at least one coordinate to calculate a bounding box
if (coords.length < 1) {
@ -859,7 +896,7 @@ export const serve_rendered = {
const x = center[0];
const y = center[1];
const overlay = await renderOverlay(z, x, y, bearing, pitch, w, h, scale, path, markers, req.query);
const overlay = await renderOverlay(z, x, y, bearing, pitch, w, h, scale, paths, markers, req.query);
return respondImage(item, z, x, y, bearing, pitch, w, h, scale, format, res, next, overlay, 'static');
});