Static images: Allow styling each path differently (#972)

* fix: allow to style each individual path for static images

Signed-off-by: Samuel Leihkamm <s.leihkamm@gmx.com>

* chore: cleanup drawPath render function

Signed-off-by: Samuel Leihkamm <s.leihkamm@gmx.com>

---------

Signed-off-by: Samuel Leihkamm <s.leihkamm@gmx.com>
This commit is contained in:
Samuel 2023-09-22 04:55:54 +02:00 committed by GitHub
parent ad185479f2
commit 02ee629f30
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -442,113 +442,104 @@ const drawMarkers = async (ctx, markers, z) => {
* @param {object} ctx Canvas context object. * @param {object} ctx Canvas context object.
* @param {List[Number]} path List of coordinates. * @param {List[Number]} path List of coordinates.
* @param {object} query Request query parameters. * @param {object} query Request query parameters.
* @param {string} pathQuery Path query parameter.
* @param {number} z Map zoom level. * @param {number} z Map zoom level.
*/ */
const drawPath = (ctx, path, query, z) => { const drawPath = (ctx, path, query, pathQuery, z) => {
const renderPath = (splitPaths) => { const splitPaths = decodeURIComponent(pathQuery).split('|');
if (!path || path.length < 2) {
return null; if (!path || path.length < 2) {
return null;
}
ctx.beginPath();
// Transform coordinates to pixel on canvas and draw lines between points
for (const pair of path) {
const px = precisePx(pair, z);
ctx.lineTo(px[0], px[1]);
}
// Check if first coordinate matches last coordinate
if (
path[0][0] === path[path.length - 1][0] &&
path[0][1] === path[path.length - 1][1]
) {
ctx.closePath();
}
// Optionally fill drawn shape with a rgba color from query
const pathHasFill = splitPaths.filter((x) => x.startsWith('fill')).length > 0;
if (query.fill !== undefined || pathHasFill) {
if ('fill' in query) {
ctx.fillStyle = query.fill || 'rgba(255,255,255,0.4)';
} }
if (pathHasFill) {
ctx.beginPath(); ctx.fillStyle = splitPaths
.find((x) => x.startsWith('fill:'))
// Transform coordinates to pixel on canvas and draw lines between points .replace('fill:', '');
for (const pair of path) {
const px = precisePx(pair, z);
ctx.lineTo(px[0], px[1]);
} }
ctx.fill();
}
// Check if first coordinate matches last coordinate // Get line width from query and fall back to 1 if not provided
if ( const pathHasWidth =
path[0][0] === path[path.length - 1][0] && splitPaths.filter((x) => x.startsWith('width')).length > 0;
path[0][1] === path[path.length - 1][1] if (query.width !== undefined || pathHasWidth) {
) { let lineWidth = 1;
ctx.closePath(); // Get line width from query
if ('width' in query) {
lineWidth = Number(query.width);
} }
// Get line width from path in query
// Optionally fill drawn shape with a rgba color from query if (pathHasWidth) {
const pathHasFill = lineWidth = Number(
splitPaths.filter((x) => x.startsWith('fill')).length > 0; splitPaths.find((x) => x.startsWith('width:')).replace('width:', ''),
if (query.fill !== undefined || pathHasFill) { );
if ('fill' in query) {
ctx.fillStyle = query.fill || 'rgba(255,255,255,0.4)';
}
if (pathHasFill) {
ctx.fillStyle = splitPaths
.find((x) => x.startsWith('fill:'))
.replace('fill:', '');
}
ctx.fill();
} }
// Get border width from query and fall back to 10% of line width
const borderWidth =
query.borderwidth !== undefined
? parseFloat(query.borderwidth)
: lineWidth * 0.1;
// Get line width from query and fall back to 1 if not provided // Set rendering style for the start and end points of the path
const pathHasWidth = // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineCap
splitPaths.filter((x) => x.startsWith('width')).length > 0; ctx.lineCap = query.linecap || 'butt';
if (query.width !== undefined || pathHasWidth) {
let lineWidth = 1;
// Get line width from query
if ('width' in query) {
lineWidth = Number(query.width);
}
// Get line width from path in query
if (pathHasWidth) {
lineWidth = Number(
splitPaths.find((x) => x.startsWith('width:')).replace('width:', ''),
);
}
// Get border width from query and fall back to 10% of line width
const borderWidth =
query.borderwidth !== undefined
? parseFloat(query.borderwidth)
: lineWidth * 0.1;
// Set rendering style for the start and end points of the path // Set rendering style for overlapping segments of the path with differing directions
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineCap // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin
ctx.lineCap = query.linecap || 'butt'; ctx.lineJoin = query.linejoin || 'miter';
// Set rendering style for overlapping segments of the path with differing directions // In order to simulate a border we draw the path two times with the first
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin // beeing the wider border part.
ctx.lineJoin = query.linejoin || 'miter'; if (query.border !== undefined && borderWidth > 0) {
// We need to double the desired border width and add it to the line width
// In order to simulate a border we draw the path two times with the first // in order to get the desired border on each side of the line.
// beeing the wider border part. ctx.lineWidth = lineWidth + borderWidth * 2;
if (query.border !== undefined && borderWidth > 0) { // Set border style as rgba
// We need to double the desired border width and add it to the line width ctx.strokeStyle = query.border;
// in order to get the desired border on each side of the line. ctx.stroke();
ctx.lineWidth = lineWidth + borderWidth * 2;
// Set border style as rgba
ctx.strokeStyle = query.border;
ctx.stroke();
}
ctx.lineWidth = lineWidth;
} }
ctx.lineWidth = lineWidth;
}
const pathHasStroke = const pathHasStroke =
splitPaths.filter((x) => x.startsWith('stroke')).length > 0; splitPaths.filter((x) => x.startsWith('stroke')).length > 0;
if (query.stroke !== undefined || pathHasStroke) { if (query.stroke !== undefined || pathHasStroke) {
if ('stroke' in query) { if ('stroke' in query) {
ctx.strokeStyle = query.stroke; ctx.strokeStyle = query.stroke;
}
// Path Width gets higher priority
if (pathHasWidth) {
ctx.strokeStyle = splitPaths
.find((x) => x.startsWith('stroke:'))
.replace('stroke:', '');
}
} else {
ctx.strokeStyle = 'rgba(0,64,255,0.7)';
} }
ctx.stroke(); // Path Width gets higher priority
}; if (pathHasWidth) {
ctx.strokeStyle = splitPaths
// Check if path in query is valid .find((x) => x.startsWith('stroke:'))
if (Array.isArray(query.path)) { .replace('stroke:', '');
for (let i = 0; i < query.path.length; i += 1) {
renderPath(decodeURIComponent(query.path.at(i)).split('|'));
} }
} else { } else {
renderPath(decodeURIComponent(query.path).split('|')); ctx.strokeStyle = 'rgba(0,64,255,0.7)';
} }
ctx.stroke();
}; };
const renderOverlay = async ( const renderOverlay = async (
@ -592,9 +583,10 @@ const renderOverlay = async (
} }
// Draw provided paths if any // Draw provided paths if any
for (const path of paths) { paths.forEach((path, i) => {
drawPath(ctx, path, query, z); const pathQuery = Array.isArray(query.path) ? query.path.at(i) : query.path;
} drawPath(ctx, path, query, pathQuery, z);
});
// Await drawing of markers before rendering the canvas // Await drawing of markers before rendering the canvas
await drawMarkers(ctx, markers, z); await drawMarkers(ctx, markers, z);