Merge 26b8ed2466
into 3110cab18f
This commit is contained in:
commit
736e53a1a2
5 changed files with 197 additions and 63 deletions
|
@ -1,9 +1,26 @@
|
||||||
import * as http from 'http';
|
import * as http from 'http';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for the HTTP request.
|
||||||
|
* @type {object}
|
||||||
|
* @property {number} timeout - Timeout for the request in milliseconds.
|
||||||
|
*/
|
||||||
const options = {
|
const options = {
|
||||||
timeout: 2000,
|
timeout: 2000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The URL to make the HTTP request to.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
const url = 'http://localhost:8080/health';
|
const url = 'http://localhost:8080/health';
|
||||||
const request = http.request(url, options, (res) => {
|
|
||||||
|
/**
|
||||||
|
* Makes an HTTP request to the health endpoint and checks the response.
|
||||||
|
* Exits the process with a 0 status code if the health check is successful (status 200),
|
||||||
|
* or with a 1 status code otherwise.
|
||||||
|
*/
|
||||||
|
const request = http.request(url, options, function (res) {
|
||||||
console.log(`STATUS: ${res.statusCode}`);
|
console.log(`STATUS: ${res.statusCode}`);
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
|
@ -11,8 +28,15 @@ const request = http.request(url, options, (res) => {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles errors that occur during the HTTP request.
|
||||||
|
* Logs an error message and exits the process with a 1 status code.
|
||||||
|
* @param {Error} err - The error object.
|
||||||
|
*/
|
||||||
request.on('error', function (err) {
|
request.on('error', function (err) {
|
||||||
console.log('ERROR');
|
console.log('ERROR');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
request.end();
|
request.end();
|
||||||
|
|
20
src/main.js
20
src/main.js
|
@ -70,6 +70,12 @@ const opts = program.opts();
|
||||||
|
|
||||||
console.log(`Starting ${packageJson.name} v${packageJson.version}`);
|
console.log(`Starting ${packageJson.name} v${packageJson.version}`);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the tile server with the given configuration.
|
||||||
|
* @param {string|null} configPath - The path to the configuration file, or null if not using a config file.
|
||||||
|
* @param {object|null} config - The configuration object, or null if reading from a file.
|
||||||
|
* @returns {Promise<void>} - A Promise that resolves when the server starts.
|
||||||
|
*/
|
||||||
const startServer = (configPath, config) => {
|
const startServer = (configPath, config) => {
|
||||||
let publicUrl = opts.public_url;
|
let publicUrl = opts.public_url;
|
||||||
if (publicUrl && publicUrl.lastIndexOf('/') !== publicUrl.length - 1) {
|
if (publicUrl && publicUrl.lastIndexOf('/') !== publicUrl.length - 1) {
|
||||||
|
@ -89,6 +95,12 @@ const startServer = (configPath, config) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the server with a given input file (MBTiles or PMTiles).
|
||||||
|
* Automatically creates a basic config file based on the input file.
|
||||||
|
* @param {string} inputFile - The path to the input MBTiles or PMTiles file.
|
||||||
|
* @returns {Promise<void>} - A Promise that resolves when the server starts.
|
||||||
|
*/
|
||||||
const startWithInputFile = async (inputFile) => {
|
const startWithInputFile = async (inputFile) => {
|
||||||
console.log(`[INFO] Automatically creating config file for ${inputFile}`);
|
console.log(`[INFO] Automatically creating config file for ${inputFile}`);
|
||||||
console.log(`[INFO] Only a basic preview style will be used.`);
|
console.log(`[INFO] Only a basic preview style will be used.`);
|
||||||
|
@ -242,6 +254,14 @@ const startWithInputFile = async (inputFile) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main function to start the server. Checks for a config file or input file,
|
||||||
|
* and starts the server based on the available inputs.
|
||||||
|
* If no config or input file are provided, downloads a demo file.
|
||||||
|
* @async
|
||||||
|
* @returns {Promise<void>} - A Promise that resolves when the server starts or finishes the download.
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
||||||
fs.stat(path.resolve(opts.config), async (err, stats) => {
|
fs.stat(path.resolve(opts.config), async (err, stats) => {
|
||||||
if (err || !stats.isFile() || stats.size === 0) {
|
if (err || !stats.isFile() || stats.size === 0) {
|
||||||
let inputFile;
|
let inputFile;
|
||||||
|
|
|
@ -2,7 +2,8 @@ import MBTiles from '@mapbox/mbtiles';
|
||||||
import util from 'node:util';
|
import util from 'node:util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Promise-ful wrapper around the MBTiles class.
|
* A promise-based wrapper around the `@mapbox/mbtiles` class,
|
||||||
|
* providing asynchronous access to MBTiles database functionality.
|
||||||
*/
|
*/
|
||||||
class MBTilesWrapper {
|
class MBTilesWrapper {
|
||||||
constructor(mbtiles) {
|
constructor(mbtiles) {
|
||||||
|
@ -11,27 +12,30 @@ class MBTilesWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the underlying MBTiles object.
|
* Gets the underlying MBTiles object.
|
||||||
* @returns {MBTiles}
|
* @returns {MBTiles} The underlying MBTiles object.
|
||||||
*/
|
*/
|
||||||
getMbTiles() {
|
getMbTiles() {
|
||||||
return this._mbtiles;
|
return this._mbtiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the MBTiles metadata object.
|
* Gets the MBTiles metadata object.
|
||||||
* @returns {Promise<object>}
|
* @async
|
||||||
|
* @returns {Promise<object>} A promise that resolves with the MBTiles metadata.
|
||||||
*/
|
*/
|
||||||
getInfo() {
|
async getInfo() {
|
||||||
return this._getInfoP();
|
return this._getInfoP();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open the given MBTiles file and return a promise that resolves with a
|
* Opens an MBTiles file and returns a promise that resolves with an MBTilesWrapper instance.
|
||||||
* MBTilesWrapper instance.
|
*
|
||||||
* @param inputFile Input file
|
* The MBTiles database is opened in read-only mode.
|
||||||
* @returns {Promise<MBTilesWrapper>}
|
* @param {string} inputFile - The path to the MBTiles file.
|
||||||
|
* @returns {Promise<MBTilesWrapper>} A promise that resolves with a new MBTilesWrapper instance.
|
||||||
|
* @throws {Error} If there is an error opening the MBTiles file.
|
||||||
*/
|
*/
|
||||||
export function openMbTilesWrapper(inputFile) {
|
export function openMbTilesWrapper(inputFile) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
|
@ -2,13 +2,37 @@ import fs from 'node:fs';
|
||||||
import { PMTiles, FetchSource } from 'pmtiles';
|
import { PMTiles, FetchSource } from 'pmtiles';
|
||||||
import { isValidHttpUrl } from './utils.js';
|
import { isValidHttpUrl } from './utils.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A PMTiles source that reads data from a file descriptor.
|
||||||
|
*/
|
||||||
class PMTilesFileSource {
|
class PMTilesFileSource {
|
||||||
|
/**
|
||||||
|
* Creates a new PMTilesFileSource instance.
|
||||||
|
* @param {number} fd - The file descriptor of the PMTiles file.
|
||||||
|
*/
|
||||||
constructor(fd) {
|
constructor(fd) {
|
||||||
|
/**
|
||||||
|
* @type {number} The file descriptor of the PMTiles file
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
this.fd = fd;
|
this.fd = fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the file descriptor.
|
||||||
|
* @returns {number} The file descriptor.
|
||||||
|
*/
|
||||||
getKey() {
|
getKey() {
|
||||||
return this.fd;
|
return this.fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronously retrieves a chunk of bytes from the PMTiles file.
|
||||||
|
* @async
|
||||||
|
* @param {number} offset - The byte offset to start reading from.
|
||||||
|
* @param {number} length - The number of bytes to read.
|
||||||
|
* @returns {Promise<{data: ArrayBuffer}>} A promise that resolves with an object containing the read bytes as an ArrayBuffer.
|
||||||
|
*/
|
||||||
async getBytes(offset, length) {
|
async getBytes(offset, length) {
|
||||||
const buffer = Buffer.alloc(length);
|
const buffer = Buffer.alloc(length);
|
||||||
await readFileBytes(this.fd, buffer, offset);
|
await readFileBytes(this.fd, buffer, offset);
|
||||||
|
@ -21,10 +45,13 @@ class PMTilesFileSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Asynchronously reads a specified number of bytes from a file descriptor into a buffer.
|
||||||
* @param fd
|
* @async
|
||||||
* @param buffer
|
* @param {number} fd - The file descriptor to read from.
|
||||||
* @param offset
|
* @param {Buffer} buffer - The buffer to write the read bytes into.
|
||||||
|
* @param {number} offset - The byte offset in the file to start reading from.
|
||||||
|
* @returns {Promise<void>} A promise that resolves when the read operation completes.
|
||||||
|
* @throws {Error} If there is an error during the read operation.
|
||||||
*/
|
*/
|
||||||
async function readFileBytes(fd, buffer, offset) {
|
async function readFileBytes(fd, buffer, offset) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
@ -38,8 +65,9 @@ async function readFileBytes(fd, buffer, offset) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Opens a PMTiles file (either local or remote) and returns a PMTiles instance.
|
||||||
* @param FilePath
|
* @param {string} FilePath - The path to the PMTiles file or a URL.
|
||||||
|
* @returns {PMTiles} A PMTiles instance.
|
||||||
*/
|
*/
|
||||||
export function openPMtiles(FilePath) {
|
export function openPMtiles(FilePath) {
|
||||||
let pmtiles = undefined;
|
let pmtiles = undefined;
|
||||||
|
@ -56,8 +84,10 @@ export function openPMtiles(FilePath) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Asynchronously retrieves metadata and header information from a PMTiles file.
|
||||||
* @param pmtiles
|
* @async
|
||||||
|
* @param {PMTiles} pmtiles - The PMTiles instance.
|
||||||
|
* @returns {Promise<object>} A promise that resolves with the metadata object.
|
||||||
*/
|
*/
|
||||||
export async function getPMtilesInfo(pmtiles) {
|
export async function getPMtilesInfo(pmtiles) {
|
||||||
const header = await pmtiles.getHeader();
|
const header = await pmtiles.getHeader();
|
||||||
|
@ -97,11 +127,13 @@ export async function getPMtilesInfo(pmtiles) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Asynchronously retrieves a tile from a PMTiles file.
|
||||||
* @param pmtiles
|
* @async
|
||||||
* @param z
|
* @param {PMTiles} pmtiles - The PMTiles instance.
|
||||||
* @param x
|
* @param {number} z - The zoom level of the tile.
|
||||||
* @param y
|
* @param {number} x - The x coordinate of the tile.
|
||||||
|
* @param {number} y - The y coordinate of the tile.
|
||||||
|
* @returns {Promise<{data: Buffer|undefined, header: object}>} A promise that resolves with an object containing the tile data (as a Buffer, or undefined if not found) and the appropriate headers.
|
||||||
*/
|
*/
|
||||||
export async function getPMtilesTile(pmtiles, z, x, y) {
|
export async function getPMtilesTile(pmtiles, z, x, y) {
|
||||||
const header = await pmtiles.getHeader();
|
const header = await pmtiles.getHeader();
|
||||||
|
@ -116,8 +148,9 @@ export async function getPMtilesTile(pmtiles, z, x, y) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Determines the tile type and corresponding headers based on the PMTiles tile type number.
|
||||||
* @param typenum
|
* @param {number} typenum - The tile type number from the PMTiles header.
|
||||||
|
* @returns {{type: string, header: object}} An object containing the tile type and associated headers.
|
||||||
*/
|
*/
|
||||||
function getPmtilesTileType(typenum) {
|
function getPmtilesTileType(typenum) {
|
||||||
let head = {};
|
let head = {};
|
||||||
|
|
127
src/render.js
127
src/render.js
|
@ -1,34 +1,47 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { createCanvas, Image } from 'canvas';
|
import { createCanvas, Image } from 'canvas';
|
||||||
|
|
||||||
import SphericalMercator from '@mapbox/sphericalmercator';
|
import SphericalMercator from '@mapbox/sphericalmercator';
|
||||||
|
|
||||||
const mercator = new SphericalMercator();
|
const mercator = new SphericalMercator();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms coordinates to pixels.
|
* Transforms geographical coordinates (longitude/latitude) to pixel coordinates at a given zoom level.
|
||||||
* @param {List[Number]} ll Longitude/Latitude coordinate pair.
|
* Uses spherical mercator projection and calculates pixel coordinates relative to zoom level 20 then scales it.
|
||||||
* @param {number} zoom Map zoom level.
|
* @param {number[]} ll - Longitude/Latitude coordinate pair [longitude, latitude].
|
||||||
|
* @param {number} zoom - Map zoom level.
|
||||||
|
* @returns {number[]} Pixel coordinates [x, y].
|
||||||
*/
|
*/
|
||||||
const precisePx = (ll, zoom) => {
|
function precisePx(ll, zoom) {
|
||||||
const px = mercator.px(ll, 20);
|
const px = mercator.px(ll, 20);
|
||||||
const scale = Math.pow(2, zoom - 20);
|
const scale = Math.pow(2, zoom - 20);
|
||||||
return [px[0] * scale, px[1] * scale];
|
return [px[0] * scale, px[1] * scale];
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws a marker in canvas context.
|
* Draws a marker on a canvas context.
|
||||||
* @param {object} ctx Canvas context object.
|
* The marker image is loaded asynchronously.
|
||||||
* @param {object} marker Marker object parsed by extractMarkersFromQuery.
|
* @async
|
||||||
* @param {number} z Map zoom level.
|
* @param {CanvasRenderingContext2D} ctx - Canvas context object.
|
||||||
|
* @param {object} marker - Marker object, with properties like `icon`, `location`, `offsetX`, `offsetY`, `scale`.
|
||||||
|
* @param {number} z - Map zoom level.
|
||||||
|
* @returns {Promise<void>} A promise that resolves when the marker image is loaded and drawn.
|
||||||
|
* @throws {Error} If there is an error loading the marker image.
|
||||||
*/
|
*/
|
||||||
const drawMarker = (ctx, marker, z) => {
|
function drawMarker(ctx, marker, z) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
const pixelCoords = precisePx(marker.location, z);
|
const pixelCoords = precisePx(marker.location, z);
|
||||||
|
|
||||||
const getMarkerCoordinates = (imageWidth, imageHeight, scale) => {
|
/**
|
||||||
|
* Calculates the pixel coordinates for placing the marker image on the canvas.
|
||||||
|
* Takes into account the image dimensions, scaling, and any offsets.
|
||||||
|
* @param {number} imageWidth - The width of the marker image.
|
||||||
|
* @param {number} imageHeight - The height of the marker image.
|
||||||
|
* @param {number} scale - The scaling factor.
|
||||||
|
* @returns {{x: number, y: number}} An object containing the x and y pixel coordinates.
|
||||||
|
*/
|
||||||
|
function getMarkerCoordinates(imageWidth, imageHeight, scale) {
|
||||||
// Images are placed with their top-left corner at the provided location
|
// Images are placed with their top-left corner at the provided location
|
||||||
// within the canvas but we expect icons to be centered and above it.
|
// within the canvas but we expect icons to be centered and above it.
|
||||||
|
|
||||||
|
@ -53,9 +66,13 @@ const drawMarker = (ctx, marker, z) => {
|
||||||
x: xCoordinate,
|
x: xCoordinate,
|
||||||
y: yCoordinate,
|
y: yCoordinate,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
const drawOnCanvas = () => {
|
/**
|
||||||
|
* Draws the marker image on the canvas, handling scaling if necessary.
|
||||||
|
* Resolves the parent promise once the image is drawn.
|
||||||
|
*/
|
||||||
|
function drawOnCanvas() {
|
||||||
// Check if the images should be resized before beeing drawn
|
// Check if the images should be resized before beeing drawn
|
||||||
const defaultScale = 1;
|
const defaultScale = 1;
|
||||||
const scale = marker.scale ? marker.scale : defaultScale;
|
const scale = marker.scale ? marker.scale : defaultScale;
|
||||||
|
@ -75,7 +92,7 @@ const drawMarker = (ctx, marker, z) => {
|
||||||
}
|
}
|
||||||
// Resolve the promise when image has been drawn
|
// Resolve the promise when image has been drawn
|
||||||
resolve();
|
resolve();
|
||||||
};
|
}
|
||||||
|
|
||||||
img.onload = drawOnCanvas;
|
img.onload = drawOnCanvas;
|
||||||
img.onerror = (err) => {
|
img.onerror = (err) => {
|
||||||
|
@ -83,18 +100,20 @@ const drawMarker = (ctx, marker, z) => {
|
||||||
};
|
};
|
||||||
img.src = marker.icon;
|
img.src = marker.icon;
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws a list of markers onto a canvas.
|
* Draws a list of markers onto a canvas.
|
||||||
* Wraps drawing of markers into list of promises and awaits them.
|
* Wraps drawing of markers into list of promises and awaits them.
|
||||||
* It's required because images are expected to load asynchronous in canvas js
|
* It's required because images are expected to load asynchronously in canvas js
|
||||||
* even when provided from a local disk.
|
* even when provided from a local disk.
|
||||||
* @param {object} ctx Canvas context object.
|
* @async
|
||||||
* @param {List[Object]} markers Marker objects parsed by extractMarkersFromQuery.
|
* @param {CanvasRenderingContext2D} ctx - Canvas context object.
|
||||||
* @param {number} z Map zoom level.
|
* @param {object[]} markers - Array of marker objects, see drawMarker for individual marker properties.
|
||||||
|
* @param {number} z - Map zoom level.
|
||||||
|
* @returns {Promise<void>} A promise that resolves when all marker images are loaded and drawn.
|
||||||
*/
|
*/
|
||||||
const drawMarkers = async (ctx, markers, z) => {
|
async function drawMarkers(ctx, markers, z) {
|
||||||
const markerPromises = [];
|
const markerPromises = [];
|
||||||
|
|
||||||
for (const marker of markers) {
|
for (const marker of markers) {
|
||||||
|
@ -104,17 +123,18 @@ const drawMarkers = async (ctx, markers, z) => {
|
||||||
|
|
||||||
// Await marker drawings before continuing
|
// Await marker drawings before continuing
|
||||||
await Promise.all(markerPromises);
|
await Promise.all(markerPromises);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws a list of coordinates onto a canvas and styles the resulting path.
|
* Draws a path (polyline or polygon) on a canvas with specified styles.
|
||||||
* @param {object} ctx Canvas context object.
|
* @param {CanvasRenderingContext2D} ctx - Canvas context object.
|
||||||
* @param {List[Number]} path List of coordinates.
|
* @param {number[][]} path - List of coordinates [longitude, latitude] representing the path.
|
||||||
* @param {object} query Request query parameters.
|
* @param {object} query - Request query parameters, which can include `fill`, `width`, `borderwidth`, `linecap`, `linejoin`, `border`, and `stroke` styles.
|
||||||
* @param {string} pathQuery Path query parameter.
|
* @param {string} pathQuery - Path specific query parameters, which can include `fill:`, `width:`, `stroke:` styles.
|
||||||
* @param {number} z Map zoom level.
|
* @param {number} z - Map zoom level.
|
||||||
|
* @returns {void | null}
|
||||||
*/
|
*/
|
||||||
const drawPath = (ctx, path, query, pathQuery, z) => {
|
function drawPath(ctx, path, query, pathQuery, z) {
|
||||||
const splitPaths = pathQuery.split('|');
|
const splitPaths = pathQuery.split('|');
|
||||||
|
|
||||||
if (!path || path.length < 2) {
|
if (!path || path.length < 2) {
|
||||||
|
@ -209,9 +229,26 @@ const drawPath = (ctx, path, query, pathQuery, z) => {
|
||||||
ctx.strokeStyle = 'rgba(0,64,255,0.7)';
|
ctx.strokeStyle = 'rgba(0,64,255,0.7)';
|
||||||
}
|
}
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
};
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
export const renderOverlay = async (
|
/**
|
||||||
|
* Renders an overlay on a canvas, including paths and markers.
|
||||||
|
* @async
|
||||||
|
* @param {number} z - Map zoom level.
|
||||||
|
* @param {number} x - X tile coordinate.
|
||||||
|
* @param {number} y - Y tile coordinate.
|
||||||
|
* @param {number} bearing - Map bearing in degrees.
|
||||||
|
* @param {number} pitch - Map pitch in degrees.
|
||||||
|
* @param {number} w - Width of the canvas.
|
||||||
|
* @param {number} h - Height of the canvas.
|
||||||
|
* @param {number} scale - Scaling factor.
|
||||||
|
* @param {number[][][]} paths - Array of paths, each path is an array of coordinate pairs [longitude, latitude].
|
||||||
|
* @param {object[]} markers - Array of marker objects, see drawMarker for individual marker properties.
|
||||||
|
* @param {object} query - Request query parameters.
|
||||||
|
* @returns {Promise<Buffer | null>} A promise that resolves with the canvas as a Buffer or null if nothing to draw.
|
||||||
|
*/
|
||||||
|
export async function renderOverlay(
|
||||||
z,
|
z,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
|
@ -223,7 +260,7 @@ export const renderOverlay = async (
|
||||||
paths,
|
paths,
|
||||||
markers,
|
markers,
|
||||||
query,
|
query,
|
||||||
) => {
|
) {
|
||||||
if ((!paths || paths.length === 0) && (!markers || markers.length === 0)) {
|
if ((!paths || paths.length === 0) && (!markers || markers.length === 0)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -261,9 +298,17 @@ export const renderOverlay = async (
|
||||||
await drawMarkers(ctx, markers, z);
|
await drawMarkers(ctx, markers, z);
|
||||||
|
|
||||||
return canvas.toBuffer();
|
return canvas.toBuffer();
|
||||||
};
|
}
|
||||||
|
|
||||||
export const renderWatermark = (width, height, scale, text) => {
|
/**
|
||||||
|
* Renders a watermark text on a canvas.
|
||||||
|
* @param {number} width - Width of the canvas.
|
||||||
|
* @param {number} height - Height of the canvas.
|
||||||
|
* @param {number} scale - Scaling factor.
|
||||||
|
* @param {string} text - The watermark text to render.
|
||||||
|
* @returns {HTMLCanvasElement} A canvas element with the rendered watermark text.
|
||||||
|
*/
|
||||||
|
export function renderWatermark(width, height, scale, text) {
|
||||||
const canvas = createCanvas(scale * width, scale * height);
|
const canvas = createCanvas(scale * width, scale * height);
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
ctx.scale(scale, scale);
|
ctx.scale(scale, scale);
|
||||||
|
@ -276,9 +321,17 @@ export const renderWatermark = (width, height, scale, text) => {
|
||||||
ctx.fillText(text, 5, height - 5);
|
ctx.fillText(text, 5, height - 5);
|
||||||
|
|
||||||
return canvas;
|
return canvas;
|
||||||
};
|
}
|
||||||
|
|
||||||
export const renderAttribution = (width, height, scale, text) => {
|
/**
|
||||||
|
* Renders an attribution text on a canvas with a background.
|
||||||
|
* @param {number} width - Width of the canvas.
|
||||||
|
* @param {number} height - Height of the canvas.
|
||||||
|
* @param {number} scale - Scaling factor.
|
||||||
|
* @param {string} text - The attribution text to render.
|
||||||
|
* @returns {HTMLCanvasElement} A canvas element with the rendered attribution text.
|
||||||
|
*/
|
||||||
|
export function renderAttribution(width, height, scale, text) {
|
||||||
const canvas = createCanvas(scale * width, scale * height);
|
const canvas = createCanvas(scale * width, scale * height);
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
ctx.scale(scale, scale);
|
ctx.scale(scale, scale);
|
||||||
|
@ -300,4 +353,4 @@ export const renderAttribution = (width, height, scale, text) => {
|
||||||
ctx.fillText(text, width - textWidth - padding / 2, height - textHeight + 8);
|
ctx.fillText(text, width - textWidth - padding / 2, height - textHeight + 8);
|
||||||
|
|
||||||
return canvas;
|
return canvas;
|
||||||
};
|
}
|
||||||
|
|
Loading…
Reference in a new issue