test
This commit is contained in:
parent
468779b1dd
commit
8bdd14a5e9
6 changed files with 192 additions and 73 deletions
|
@ -46,7 +46,9 @@ export const serve_data = {
|
|||
*/
|
||||
app.get('/:id/:z/:x/:y.:format', async (req, res) => {
|
||||
if (verbose) {
|
||||
console.log(req.params);
|
||||
console.log(
|
||||
`Handling tile request for: /data/${req.params.id}/${req.params.z}/${req.params.x}/${req.params.y}.${req.params.format}`,
|
||||
);
|
||||
}
|
||||
const item = repo[req.params.id];
|
||||
if (!item) {
|
||||
|
@ -156,6 +158,11 @@ export const serve_data = {
|
|||
*/
|
||||
app.get('/:id/elevation/:z/:x/:y', async (req, res, next) => {
|
||||
try {
|
||||
if (verbose) {
|
||||
console.log(
|
||||
`Handling elevation request for: /data/${req.params.id}/elevation/${req.params.z}/${req.params.x}/${req.params.y}`,
|
||||
);
|
||||
}
|
||||
const item = repo?.[req.params.id];
|
||||
if (!item) return res.sendStatus(404);
|
||||
if (!item.source) return res.status(404).send('Missing source');
|
||||
|
@ -292,13 +299,18 @@ export const serve_data = {
|
|||
});
|
||||
|
||||
/**
|
||||
* Handles requests for metadata for the tiles.
|
||||
* Handles requests for tilejson for the data tiles.
|
||||
* @param {object} req - Express request object.
|
||||
* @param {object} res - Express response object.
|
||||
* @param {string} req.params.id - ID of the data source.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
app.get('/:id.json', (req, res) => {
|
||||
if (verbose) {
|
||||
console.log(
|
||||
`Handling tilejson request for: /data/${req.params.id}.json`,
|
||||
);
|
||||
}
|
||||
const item = repo[req.params.id];
|
||||
if (!item) {
|
||||
return res.sendStatus(404);
|
||||
|
|
|
@ -23,7 +23,6 @@ export async function serve_font(options, allowedFonts, programOpts) {
|
|||
|
||||
/**
|
||||
* Handles requests for a font file.
|
||||
*
|
||||
* @param {object} req - Express request object.
|
||||
* @param {object} res - Express response object.
|
||||
* @param {string} req.params.fontstack - Name of the font stack.
|
||||
|
@ -32,7 +31,9 @@ export async function serve_font(options, allowedFonts, programOpts) {
|
|||
*/
|
||||
app.get('/fonts/:fontstack/:range.pbf', async (req, res) => {
|
||||
if (verbose) {
|
||||
console.log(req.params);
|
||||
console.log(
|
||||
`Handling font request for: /fonts/${req.params.fontstack}/${req.params.range}.pbf`,
|
||||
);
|
||||
}
|
||||
const fontstack = decodeURI(req.params.fontstack);
|
||||
const range = req.params.range;
|
||||
|
@ -60,12 +61,14 @@ export async function serve_font(options, allowedFonts, programOpts) {
|
|||
|
||||
/**
|
||||
* Handles requests for a list of all available fonts.
|
||||
*
|
||||
* @param {object} req - Express request object.
|
||||
* @param {object} res - Express response object.
|
||||
* @returns {void}
|
||||
*/
|
||||
app.get('/fonts.json', (req, res) => {
|
||||
if (verbose) {
|
||||
console.log('Handling list font request for /fonts.json');
|
||||
}
|
||||
res.header('Content-type', 'application/json');
|
||||
return res.send(
|
||||
Object.keys(options.serveAllFonts ? existingFonts : allowedFonts).sort(),
|
||||
|
|
|
@ -34,6 +34,7 @@ import {
|
|||
isValidHttpUrl,
|
||||
fixTileJSONCenter,
|
||||
fetchTileData,
|
||||
allowedOptions,
|
||||
} from './utils.js';
|
||||
import { openPMtiles, getPMtilesInfo } from './pmtiles_adapter.js';
|
||||
import { renderOverlay, renderWatermark, renderAttribution } from './render.js';
|
||||
|
@ -635,6 +636,7 @@ const respondImage = async (
|
|||
* @param {object} res - Express response object.
|
||||
* @param {Function} next - Express next middleware function.
|
||||
* @param {number} maxScaleFactor - The maximum scale factor allowed.
|
||||
* @param defailtTileSize
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function handleTileRequest(
|
||||
|
@ -644,6 +646,7 @@ async function handleTileRequest(
|
|||
res,
|
||||
next,
|
||||
maxScaleFactor,
|
||||
defailtTileSize,
|
||||
) {
|
||||
const {
|
||||
id,
|
||||
|
@ -658,6 +661,7 @@ async function handleTileRequest(
|
|||
if (!item) {
|
||||
return res.sendStatus(404);
|
||||
}
|
||||
console.log(req.params);
|
||||
|
||||
const modifiedSince = req.get('if-modified-since');
|
||||
const cc = req.get('cache-control');
|
||||
|
@ -670,7 +674,19 @@ async function handleTileRequest(
|
|||
const x = parseFloat(xParam) | 0;
|
||||
const y = parseFloat(yParam) | 0;
|
||||
const scale = parseScale(scaleParam, maxScaleFactor);
|
||||
const parsedTileSize = parseInt(tileSize, 10) || 256;
|
||||
|
||||
let parsedTileSize = defailtTileSize;
|
||||
if (tileSize) {
|
||||
const allowedTileSizes = allowedOptions(['256', '512'], {
|
||||
defaultValue: null,
|
||||
});
|
||||
parsedTileSize = allowedTileSizes(tileSize);
|
||||
|
||||
if (parsedTileSize == null) {
|
||||
return res.status(400).send('Invalid Tile Size');
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
scale == null ||
|
||||
z < 0 ||
|
||||
|
@ -680,7 +696,7 @@ async function handleTileRequest(
|
|||
x >= Math.pow(2, z) ||
|
||||
y >= Math.pow(2, z)
|
||||
) {
|
||||
return res.status(404).send('Out of bounds');
|
||||
return res.status(400).send('Out of bounds');
|
||||
}
|
||||
|
||||
const tileCenter = mercator.ll(
|
||||
|
@ -722,14 +738,43 @@ async function handleStaticRequest(
|
|||
id,
|
||||
p2: raw,
|
||||
p3: staticType,
|
||||
p4: width,
|
||||
p5: height,
|
||||
p4: widthAndHeight,
|
||||
scale: scaleParam,
|
||||
format,
|
||||
} = req.params;
|
||||
console.log(req.params);
|
||||
const item = repo[id];
|
||||
const parsedWidth = parseInt(width) || 512;
|
||||
const parsedHeight = parseInt(height) || 512;
|
||||
|
||||
let parsedWidth = null;
|
||||
let parsedHeight = null;
|
||||
if (widthAndHeight) {
|
||||
const sizeMatch = widthAndHeight.match(/^(\d+)x(\d+)$/);
|
||||
if (sizeMatch) {
|
||||
const width = parseInt(sizeMatch[1], 10);
|
||||
const height = parseInt(sizeMatch[2], 10);
|
||||
|
||||
if (
|
||||
isNaN(width) ||
|
||||
isNaN(height) ||
|
||||
width !== parseFloat(sizeMatch[1]) ||
|
||||
height !== parseFloat(sizeMatch[2])
|
||||
) {
|
||||
return res
|
||||
.status(400)
|
||||
.send('Invalid width or height provided in size parameter');
|
||||
}
|
||||
parsedWidth = width;
|
||||
parsedHeight = height;
|
||||
} else {
|
||||
return res
|
||||
.status(400)
|
||||
.send('Invalid width or height provided in size parameter');
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
.status(400)
|
||||
.send('Invalid width or height provided in size parameter');
|
||||
}
|
||||
const scale = parseScale(scaleParam, maxScaleFactor);
|
||||
let isRaw = raw === 'raw';
|
||||
|
||||
|
@ -740,11 +785,12 @@ async function handleStaticRequest(
|
|||
const staticTypeMatch = staticType.match(staticTypeRegex);
|
||||
if (staticTypeMatch.groups.lon) {
|
||||
// Center Based Static Image
|
||||
const z = parseFloat(staticTypeMatch.groups.zoom) || 0;
|
||||
let x = parseFloat(staticTypeMatch.groups.lon) || 0;
|
||||
let y = parseFloat(staticTypeMatch.groups.lat) || 0;
|
||||
const bearing = parseFloat(staticTypeMatch.groups.bearing) || 0;
|
||||
const pitch = parseInt(staticTypeMatch.groups.pitch) || 0;
|
||||
const z = staticTypeMatch.groups.zoom;
|
||||
let x = staticTypeMatch.groups.lon;
|
||||
let y = staticTypeMatch.groups.lat;
|
||||
const bearing = staticTypeMatch.groups.bearing;
|
||||
const pitch = staticTypeMatch.groups.pitch;
|
||||
|
||||
if (z < 0) {
|
||||
return res.status(404).send('Invalid zoom');
|
||||
}
|
||||
|
@ -764,13 +810,13 @@ async function handleStaticRequest(
|
|||
|
||||
// prettier-ignore
|
||||
const overlay = await renderOverlay(
|
||||
z, x, y, bearing, pitch, parsedWidth, parsedHeight, scale, paths, markers, req.query,
|
||||
);
|
||||
z, x, y, bearing, pitch, parsedWidth, parsedHeight, scale, paths, markers, req.query,
|
||||
);
|
||||
|
||||
// prettier-ignore
|
||||
return await respondImage(
|
||||
options, item, z, x, y, bearing, pitch, parsedWidth, parsedHeight, scale, format, res, overlay, 'static',
|
||||
);
|
||||
options, item, z, x, y, bearing, pitch, parsedWidth, parsedHeight, scale, format, res, overlay, 'static',
|
||||
);
|
||||
} else if (staticTypeMatch.groups.minx) {
|
||||
// Area Based Static Image
|
||||
const bbox = [
|
||||
|
@ -802,15 +848,16 @@ async function handleStaticRequest(
|
|||
const pitch = 0;
|
||||
const paths = extractPathsFromQuery(req.query, transformer);
|
||||
const markers = extractMarkersFromQuery(req.query, options, transformer);
|
||||
|
||||
// prettier-ignore
|
||||
const overlay = await renderOverlay(
|
||||
z, x, y, bearing, pitch, parsedWidth, parsedHeight, scale, paths, markers, req.query,
|
||||
);
|
||||
z, x, y, bearing, pitch, parsedWidth, parsedHeight, scale, paths, markers, req.query,
|
||||
);
|
||||
|
||||
// prettier-ignore
|
||||
return await respondImage(
|
||||
options, item, z, x, y, bearing, pitch, parsedWidth, parsedHeight, scale, format, res, overlay, 'static',
|
||||
);
|
||||
options, item, z, x, y, bearing, pitch, parsedWidth, parsedHeight, scale, format, res, overlay, 'static',
|
||||
);
|
||||
} else if (staticTypeMatch.groups.auto) {
|
||||
// Area Static Image
|
||||
const bearing = 0;
|
||||
|
@ -863,13 +910,13 @@ async function handleStaticRequest(
|
|||
|
||||
// prettier-ignore
|
||||
const overlay = await renderOverlay(
|
||||
z, x, y, bearing, pitch, parsedWidth, parsedHeight, scale, paths, markers, req.query,
|
||||
);
|
||||
z, x, y, bearing, pitch, parsedWidth, parsedHeight, scale, paths, markers, req.query,
|
||||
);
|
||||
|
||||
// prettier-ignore
|
||||
return await respondImage(
|
||||
options, item, z, x, y, bearing, pitch, parsedWidth, parsedHeight, scale, format, res, overlay, 'static',
|
||||
);
|
||||
options, item, z, x, y, bearing, pitch, parsedWidth, parsedHeight, scale, format, res, overlay, 'static',
|
||||
);
|
||||
} else {
|
||||
return res.sendStatus(404);
|
||||
}
|
||||
|
@ -887,7 +934,7 @@ export const serve_rendered = {
|
|||
* @returns {Promise<express.Application>} A promise that resolves to the Express app.
|
||||
*/
|
||||
init: async function (options, repo, programOpts) {
|
||||
const { verbose } = programOpts;
|
||||
const { verbose, tileSize: defailtTileSize = 256 } = programOpts;
|
||||
maxScaleFactor = Math.min(Math.floor(options.maxScaleFactor || 3), 9);
|
||||
const app = express().disable('x-powered-by');
|
||||
|
||||
|
@ -896,7 +943,7 @@ export const serve_rendered = {
|
|||
* @param {object} req - Express request object.
|
||||
* @param {object} res - Express response object.
|
||||
* @param {string} req.params.id - The id of the style.
|
||||
* @param {string} req.params.p1 - The tile size or static parameter, if available
|
||||
* @param {string} [req.params.p1] - The tile size or static parameter, if available.
|
||||
* @param {string} req.params.p2 - The z, static, or raw parameter.
|
||||
* @param {string} req.params.p3 - The x or staticType parameter.
|
||||
* @param {string} req.params.p4 - The y or width parameter.
|
||||
|
@ -906,14 +953,24 @@ export const serve_rendered = {
|
|||
* @returns {Promise<void>}
|
||||
*/
|
||||
app.get(
|
||||
`/:id{/:p1}/:p2/:p3/:p4{x:p5}{@:scale}{.:format}`,
|
||||
`/:id{/:p1}/:p2/:p3/:p4{@:scale}{.:format}`,
|
||||
async (req, res, next) => {
|
||||
try {
|
||||
const { p1, p2, id, p3, p4, p5, scale, format } = req.params;
|
||||
const requestType =
|
||||
(!p1 && p2 === 'static') || (p1 === 'static' && p2 === 'raw')
|
||||
? 'static'
|
||||
: 'tile';
|
||||
|
||||
if (verbose) {
|
||||
console.log(req.params);
|
||||
console.log(
|
||||
`Handling rendered ${requestType} request for: /styles/${id}${p1 ? '/' + p1 : ''}/${p2}/${p3}/${p4}${p5 ? 'x' + p5 : ''}${
|
||||
scale ? '@' + scale : ''
|
||||
}.${format}`,
|
||||
);
|
||||
}
|
||||
const { p1, p2 } = req.params;
|
||||
if ((!p1 && p2 === 'static') || (p1 === 'static' && p2 === 'raw')) {
|
||||
|
||||
if (requestType === 'static') {
|
||||
// Route to static if p2 is static
|
||||
if (options.serveStaticMaps !== false) {
|
||||
return handleStaticRequest(
|
||||
|
@ -923,6 +980,7 @@ export const serve_rendered = {
|
|||
res,
|
||||
next,
|
||||
maxScaleFactor,
|
||||
defailtTileSize,
|
||||
);
|
||||
}
|
||||
return res.sendStatus(404);
|
||||
|
@ -935,6 +993,7 @@ export const serve_rendered = {
|
|||
res,
|
||||
next,
|
||||
maxScaleFactor,
|
||||
defailtTileSize,
|
||||
);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
|
@ -944,7 +1003,7 @@ export const serve_rendered = {
|
|||
);
|
||||
|
||||
/**
|
||||
* Handles requests for tile json endpoint.
|
||||
* Handles requests for rendered tilejson endpoint.
|
||||
* @param {object} req - Express request object.
|
||||
* @param {object} res - Express response object.
|
||||
* @param {string} req.params.id - The id of the tilejson
|
||||
|
@ -957,6 +1016,11 @@ export const serve_rendered = {
|
|||
return res.sendStatus(404);
|
||||
}
|
||||
const tileSize = parseInt(req.params.tileSize, 10) || undefined;
|
||||
if (verbose) {
|
||||
console.log(
|
||||
`Handling rendered tilejson request for: /styles/${tileSize ? tileSize + '/' : ''}${req.params.id}.json`,
|
||||
);
|
||||
}
|
||||
const info = clone(item.tileJSON);
|
||||
info.tiles = getTileUrls(
|
||||
req,
|
||||
|
|
|
@ -35,59 +35,95 @@ export const serve_style = {
|
|||
init: function (options, repo, programOpts) {
|
||||
const { verbose } = programOpts;
|
||||
const app = express().disable('x-powered-by');
|
||||
|
||||
/**
|
||||
* Handles requests for style.json files.
|
||||
* @param {express.Request} req - Express request object.
|
||||
* @param {express.Response} res - Express response object.
|
||||
* @param {express.NextFunction} next - Express next function.
|
||||
* @param {string} req.params.id - ID of the style.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
app.get('/:id/style.json', (req, res, next) => {
|
||||
const item = repo[req.params.id];
|
||||
if (!item) {
|
||||
return res.sendStatus(404);
|
||||
const { id } = req.params;
|
||||
if (verbose) {
|
||||
console.log(`Handling style request for: /styles/${id}/style.json`);
|
||||
}
|
||||
const styleJSON_ = clone(item.styleJSON);
|
||||
for (const name of Object.keys(styleJSON_.sources)) {
|
||||
const source = styleJSON_.sources[name];
|
||||
source.url = fixUrl(req, source.url, item.publicUrl);
|
||||
if (typeof source.data == 'string') {
|
||||
source.data = fixUrl(req, source.data, item.publicUrl);
|
||||
try {
|
||||
const item = repo[id];
|
||||
if (!item) {
|
||||
return res.sendStatus(404);
|
||||
}
|
||||
}
|
||||
// mapbox-gl-js viewer cannot handle sprite urls with query
|
||||
if (styleJSON_.sprite) {
|
||||
if (Array.isArray(styleJSON_.sprite)) {
|
||||
styleJSON_.sprite.forEach((spriteItem) => {
|
||||
spriteItem.url = fixUrl(req, spriteItem.url, item.publicUrl);
|
||||
});
|
||||
} else {
|
||||
styleJSON_.sprite = fixUrl(req, styleJSON_.sprite, item.publicUrl);
|
||||
const styleJSON_ = clone(item.styleJSON);
|
||||
for (const name of Object.keys(styleJSON_.sources)) {
|
||||
const source = styleJSON_.sources[name];
|
||||
source.url = fixUrl(req, source.url, item.publicUrl);
|
||||
if (typeof source.data == 'string') {
|
||||
source.data = fixUrl(req, source.data, item.publicUrl);
|
||||
}
|
||||
}
|
||||
if (styleJSON_.sprite) {
|
||||
if (Array.isArray(styleJSON_.sprite)) {
|
||||
styleJSON_.sprite.forEach((spriteItem) => {
|
||||
spriteItem.url = fixUrl(req, spriteItem.url, item.publicUrl);
|
||||
});
|
||||
} else {
|
||||
styleJSON_.sprite = fixUrl(req, styleJSON_.sprite, item.publicUrl);
|
||||
}
|
||||
}
|
||||
if (styleJSON_.glyphs) {
|
||||
styleJSON_.glyphs = fixUrl(req, styleJSON_.glyphs, item.publicUrl);
|
||||
}
|
||||
return res.send(styleJSON_);
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
if (styleJSON_.glyphs) {
|
||||
styleJSON_.glyphs = fixUrl(req, styleJSON_.glyphs, item.publicUrl);
|
||||
}
|
||||
return res.send(styleJSON_);
|
||||
});
|
||||
|
||||
/**
|
||||
* Handles GET requests for sprite images and JSON files.
|
||||
* @param {express.Request} req - Express request object.
|
||||
* @param {express.Response} res - Express response object.
|
||||
* @param {express.NextFunction} next - Express next function.
|
||||
* @param {string} req.params.id - ID of the sprite.
|
||||
* @param {string} [req.params.spriteID='default'] - ID of the specific sprite image, defaults to 'default'.
|
||||
* @param {string} [req.params.scale] - Scale of the sprite image, defaults to ''.
|
||||
* @param {string} req.params.format - Format of the sprite file, 'png' or 'json'.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
app.get(`/:id/sprite{/:spriteID}{@:scale}{.:format}`, (req, res, next) => {
|
||||
if (verbose) {
|
||||
console.log(req.params);
|
||||
}
|
||||
const { spriteID = 'default', id, format } = req.params;
|
||||
const spriteScale = allowedSpriteScales(req.params.scale);
|
||||
const { spriteID = 'default', id, format, scale } = req.params;
|
||||
const spriteScale = allowedSpriteScales(scale);
|
||||
|
||||
if (verbose) {
|
||||
console.log(
|
||||
`Handling sprite request for: /${id}/sprite/${spriteID}${scale}.${format}`,
|
||||
);
|
||||
}
|
||||
const item = repo[id];
|
||||
if (!item || !allowedSpriteFormats(format)) {
|
||||
if (verbose)
|
||||
console.error(
|
||||
`Sprite item or format not found for: /${id}/sprite/${spriteID}${scale}.${format}`,
|
||||
);
|
||||
return res.sendStatus(404);
|
||||
}
|
||||
|
||||
const sprite = item.spritePaths.find((sprite) => sprite.id === spriteID);
|
||||
if (!sprite) {
|
||||
if (verbose)
|
||||
console.error(
|
||||
`Sprite not found for: /${id}/sprite/${spriteID}${scale}.${format}`,
|
||||
);
|
||||
return res.status(400).send('Bad Sprite ID or Scale');
|
||||
}
|
||||
|
||||
const filename = `${sprite.path}${spriteScale}.${format}`;
|
||||
if (verbose) console.log(`Loading sprite from: ${filename}`);
|
||||
|
||||
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
||||
fs.readFile(filename, (err, data) => {
|
||||
if (err) {
|
||||
console.error('Sprite load error: %s, Error: %s', filename, err);
|
||||
if (verbose)
|
||||
console.error('Sprite load error: %s, Error: %s', filename, err);
|
||||
return res.sendStatus(404);
|
||||
}
|
||||
|
||||
|
@ -96,6 +132,10 @@ export const serve_style = {
|
|||
} else if (format === 'png') {
|
||||
res.header('Content-type', 'image/png');
|
||||
}
|
||||
if (verbose)
|
||||
console.log(
|
||||
`Responding with sprite data for /${id}/sprite/${spriteID}${scale}.${format}`,
|
||||
);
|
||||
return res.send(data);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -135,7 +135,7 @@ describe('Static endpoints', function () {
|
|||
|
||||
testStatic(prefix, '0,0,1,1/1x1', 'gif', 400);
|
||||
|
||||
testStatic(prefix, '-180,-80,180,80/0.5x2.6', 'png', 404);
|
||||
testStatic(prefix, '-180,-80,180,80/0.5x2.6', 'png', 400);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -60,16 +60,16 @@ describe('Raster tiles', function () {
|
|||
|
||||
describe('invalid requests return 4xx', function () {
|
||||
testTile('non_existent', 256, 0, 0, 0, 'png', 404);
|
||||
testTile(prefix, 256, -1, 0, 0, 'png', 404);
|
||||
testTile(prefix, 256, 25, 0, 0, 'png', 404);
|
||||
testTile(prefix, 256, 0, 1, 0, 'png', 404);
|
||||
testTile(prefix, 256, 0, 0, 1, 'png', 404);
|
||||
testTile(prefix, 256, -1, 0, 0, 'png', 400);
|
||||
testTile(prefix, 256, 25, 0, 0, 'png', 400);
|
||||
testTile(prefix, 256, 0, 1, 0, 'png', 400);
|
||||
testTile(prefix, 256, 0, 0, 1, 'png', 400);
|
||||
testTile(prefix, 256, 0, 0, 0, 'gif', 400);
|
||||
testTile(prefix, 256, 0, 0, 0, 'pbf', 400);
|
||||
|
||||
testTile(prefix, 256, 0, 0, 0, 'png', 404, 1);
|
||||
testTile(prefix, 256, 0, 0, 0, 'png', 404, 5);
|
||||
testTile(prefix, 256, 0, 0, 0, 'png', 400, 1);
|
||||
testTile(prefix, 256, 0, 0, 0, 'png', 400, 5);
|
||||
|
||||
testTile(prefix, 300, 0, 0, 0, 'png', 404);
|
||||
testTile(prefix, 300, 0, 0, 0, 'png', 400);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue