Merge branch 'master' into windows
Signed-off-by: acalcutt <acalcutt@techidiots.net>
This commit is contained in:
commit
c634cdf0b3
9 changed files with 219 additions and 314 deletions
|
@ -79,7 +79,7 @@
|
||||||
<div class="details">
|
<div class="details">
|
||||||
<h3>{{tileJSON.name}}</h3>
|
<h3>{{tileJSON.name}}</h3>
|
||||||
<div class="identifier">identifier: {{@key}}{{#if formatted_filesize}} | size: {{formatted_filesize}}{{/if}}</div>
|
<div class="identifier">identifier: {{@key}}{{#if formatted_filesize}} | size: {{formatted_filesize}}{{/if}}</div>
|
||||||
<div class="identifier">type: {{#is_vector}}vector{{/is_vector}}{{^is_vector}}raster{{/is_vector}} data {{#if source_type}} | ext: {{source_type}}{{/if}}</div>
|
<div class="identifier">type: {{#is_vector}}vector{{/is_vector}}{{^is_vector}}raster{{/is_vector}} data {{#if sourceType}} | ext: {{sourceType}}{{/if}}</div>
|
||||||
<p class="services">
|
<p class="services">
|
||||||
services: <a href="{{public_url}}data/{{@key}}.json{{&../key_query}}">TileJSON</a>
|
services: <a href="{{public_url}}data/{{@key}}.json{{&../key_query}}">TileJSON</a>
|
||||||
{{#if xyz_link}}
|
{{#if xyz_link}}
|
||||||
|
|
45
src/main.js
45
src/main.js
|
@ -9,7 +9,7 @@ import axios from 'axios';
|
||||||
import { server } from './server.js';
|
import { server } from './server.js';
|
||||||
import MBTiles from '@mapbox/mbtiles';
|
import MBTiles from '@mapbox/mbtiles';
|
||||||
import { isValidHttpUrl } from './utils.js';
|
import { isValidHttpUrl } from './utils.js';
|
||||||
import { PMtilesOpen, GetPMtilesInfo } from './pmtiles_adapter.js';
|
import { openPMtiles, getPMtilesInfo } from './pmtiles_adapter.js';
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
|
@ -62,14 +62,14 @@ const opts = program.opts();
|
||||||
|
|
||||||
console.log(`Starting ${packageJson.name} v${packageJson.version}`);
|
console.log(`Starting ${packageJson.name} v${packageJson.version}`);
|
||||||
|
|
||||||
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) {
|
||||||
publicUrl += '/';
|
publicUrl += '/';
|
||||||
}
|
}
|
||||||
return server({
|
return server({
|
||||||
configPath: configPath,
|
configPath,
|
||||||
config: config,
|
config,
|
||||||
bind: opts.bind,
|
bind: opts.bind,
|
||||||
port: opts.port,
|
port: opts.port,
|
||||||
cors: opts.cors,
|
cors: opts.cors,
|
||||||
|
@ -77,11 +77,11 @@ const StartServer = (configPath, config) => {
|
||||||
silent: opts.silent,
|
silent: opts.silent,
|
||||||
logFile: opts.log_file,
|
logFile: opts.log_file,
|
||||||
logFormat: opts.log_format,
|
logFormat: opts.log_format,
|
||||||
publicUrl: publicUrl,
|
publicUrl,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
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.`);
|
||||||
console.log(
|
console.log(
|
||||||
|
@ -123,8 +123,8 @@ const StartWithInputFile = async (inputFile) => {
|
||||||
|
|
||||||
const extension = inputFile.split('.').pop().toLowerCase();
|
const extension = inputFile.split('.').pop().toLowerCase();
|
||||||
if (extension === 'pmtiles') {
|
if (extension === 'pmtiles') {
|
||||||
let FileOpenInfo = PMtilesOpen(inputFile);
|
const fileOpenInfo = openPMtiles(inputFile);
|
||||||
const metadata = await GetPMtilesInfo(FileOpenInfo);
|
const metadata = await getPMtilesInfo(fileOpenInfo);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
metadata.format === 'pbf' &&
|
metadata.format === 'pbf' &&
|
||||||
|
@ -174,7 +174,7 @@ const StartWithInputFile = async (inputFile) => {
|
||||||
console.log('Run with --verbose to see the config file here.');
|
console.log('Run with --verbose to see the config file here.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return StartServer(null, config);
|
return startServer(null, config);
|
||||||
} else {
|
} else {
|
||||||
if (isValidHttpUrl(inputFile)) {
|
if (isValidHttpUrl(inputFile)) {
|
||||||
console.log(
|
console.log(
|
||||||
|
@ -215,7 +215,7 @@ const StartWithInputFile = async (inputFile) => {
|
||||||
config['styles'][styleName] = {
|
config['styles'][styleName] = {
|
||||||
style: styleFileRel,
|
style: styleFileRel,
|
||||||
tilejson: {
|
tilejson: {
|
||||||
bounds: bounds,
|
bounds,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -235,13 +235,13 @@ const StartWithInputFile = async (inputFile) => {
|
||||||
console.log('Run with --verbose to see the config file here.');
|
console.log('Run with --verbose to see the config file here.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return StartServer(null, config);
|
return startServer(null, config);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fs.stat(path.resolve(opts.config), (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;
|
||||||
if (opts.file) {
|
if (opts.file) {
|
||||||
|
@ -251,7 +251,7 @@ fs.stat(path.resolve(opts.config), (err, stats) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputFile) {
|
if (inputFile) {
|
||||||
return StartWithInputFile(inputFile);
|
return startWithInputFile(inputFile);
|
||||||
} else {
|
} else {
|
||||||
// try to find in the cwd
|
// try to find in the cwd
|
||||||
const files = fs.readdirSync(process.cwd());
|
const files = fs.readdirSync(process.cwd());
|
||||||
|
@ -266,7 +266,7 @@ fs.stat(path.resolve(opts.config), (err, stats) => {
|
||||||
}
|
}
|
||||||
if (inputFile) {
|
if (inputFile) {
|
||||||
console.log(`No input file specified, using ${inputFile}`);
|
console.log(`No input file specified, using ${inputFile}`);
|
||||||
return StartWithInputFile(inputFile);
|
return startWithInputFile(inputFile);
|
||||||
} else {
|
} else {
|
||||||
const url =
|
const url =
|
||||||
'https://github.com/maptiler/tileserver-gl/releases/download/v1.3.0/zurich_switzerland.mbtiles';
|
'https://github.com/maptiler/tileserver-gl/releases/download/v1.3.0/zurich_switzerland.mbtiles';
|
||||||
|
@ -274,25 +274,26 @@ fs.stat(path.resolve(opts.config), (err, stats) => {
|
||||||
const writer = fs.createWriteStream(filename);
|
const writer = fs.createWriteStream(filename);
|
||||||
console.log(`No input file found`);
|
console.log(`No input file found`);
|
||||||
console.log(`[DEMO] Downloading sample data (${filename}) from ${url}`);
|
console.log(`[DEMO] Downloading sample data (${filename}) from ${url}`);
|
||||||
axios({
|
|
||||||
|
try {
|
||||||
|
const response = await axios({
|
||||||
url,
|
url,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
responseType: 'stream',
|
responseType: 'stream',
|
||||||
})
|
});
|
||||||
.then((response) => {
|
|
||||||
response.data.pipe(writer);
|
response.data.pipe(writer);
|
||||||
writer.on('finish', () => StartWithInputFile(filename));
|
writer.on('finish', () => startWithInputFile(filename));
|
||||||
writer.on('error', (err) =>
|
writer.on('error', (err) =>
|
||||||
console.error(`Error writing file: ${err}`),
|
console.error(`Error writing file: ${err}`),
|
||||||
);
|
);
|
||||||
})
|
} catch (error) {
|
||||||
.catch((error) => {
|
|
||||||
console.error(`Error downloading file: ${error}`);
|
console.error(`Error downloading file: ${error}`);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(`Using specified config file from ${opts.config}`);
|
console.log(`Using specified config file from ${opts.config}`);
|
||||||
return StartServer(opts.config, null);
|
return startServer(opts.config, null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,7 +11,7 @@ class PMTilesFileSource {
|
||||||
}
|
}
|
||||||
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);
|
||||||
const ab = buffer.buffer.slice(
|
const ab = buffer.buffer.slice(
|
||||||
buffer.byteOffset,
|
buffer.byteOffset,
|
||||||
buffer.byteOffset + buffer.byteLength,
|
buffer.byteOffset + buffer.byteLength,
|
||||||
|
@ -26,7 +26,7 @@ class PMTilesFileSource {
|
||||||
* @param buffer
|
* @param buffer
|
||||||
* @param offset
|
* @param offset
|
||||||
*/
|
*/
|
||||||
async function ReadFileBytes(fd, buffer, offset) {
|
async function readFileBytes(fd, buffer, offset) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.read(fd, buffer, 0, buffer.length, offset, (err) => {
|
fs.read(fd, buffer, 0, buffer.length, offset, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -41,7 +41,7 @@ async function ReadFileBytes(fd, buffer, offset) {
|
||||||
*
|
*
|
||||||
* @param FilePath
|
* @param FilePath
|
||||||
*/
|
*/
|
||||||
export function PMtilesOpen(FilePath) {
|
export function openPMtiles(FilePath) {
|
||||||
let pmtiles = undefined;
|
let pmtiles = undefined;
|
||||||
|
|
||||||
if (isValidHttpUrl(FilePath)) {
|
if (isValidHttpUrl(FilePath)) {
|
||||||
|
@ -59,12 +59,12 @@ export function PMtilesOpen(FilePath) {
|
||||||
*
|
*
|
||||||
* @param pmtiles
|
* @param pmtiles
|
||||||
*/
|
*/
|
||||||
export async function GetPMtilesInfo(pmtiles) {
|
export async function getPMtilesInfo(pmtiles) {
|
||||||
const header = await pmtiles.getHeader();
|
const header = await pmtiles.getHeader();
|
||||||
const metadata = await pmtiles.getMetadata();
|
const metadata = await pmtiles.getMetadata();
|
||||||
|
|
||||||
//Add missing metadata from header
|
//Add missing metadata from header
|
||||||
metadata['format'] = GetPmtilesTileType(header.tileType).type;
|
metadata['format'] = getPmtilesTileType(header.tileType).type;
|
||||||
metadata['minzoom'] = header.minZoom;
|
metadata['minzoom'] = header.minZoom;
|
||||||
metadata['maxzoom'] = header.maxZoom;
|
metadata['maxzoom'] = header.maxZoom;
|
||||||
|
|
||||||
|
@ -103,23 +103,23 @@ export async function GetPMtilesInfo(pmtiles) {
|
||||||
* @param x
|
* @param x
|
||||||
* @param y
|
* @param y
|
||||||
*/
|
*/
|
||||||
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();
|
||||||
const TileType = GetPmtilesTileType(header.tileType);
|
const tileType = getPmtilesTileType(header.tileType);
|
||||||
let zxyTile = await pmtiles.getZxy(z, x, y);
|
let zxyTile = await pmtiles.getZxy(z, x, y);
|
||||||
if (zxyTile && zxyTile.data) {
|
if (zxyTile && zxyTile.data) {
|
||||||
zxyTile = Buffer.from(zxyTile.data);
|
zxyTile = Buffer.from(zxyTile.data);
|
||||||
} else {
|
} else {
|
||||||
zxyTile = undefined;
|
zxyTile = undefined;
|
||||||
}
|
}
|
||||||
return { data: zxyTile, header: TileType.header };
|
return { data: zxyTile, header: tileType.header };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param typenum
|
* @param typenum
|
||||||
*/
|
*/
|
||||||
function GetPmtilesTileType(typenum) {
|
function getPmtilesTileType(typenum) {
|
||||||
let head = {};
|
let head = {};
|
||||||
let tileType;
|
let tileType;
|
||||||
switch (typenum) {
|
switch (typenum) {
|
||||||
|
|
|
@ -12,9 +12,9 @@ import { VectorTile } from '@mapbox/vector-tile';
|
||||||
|
|
||||||
import { getTileUrls, isValidHttpUrl, fixTileJSONCenter } from './utils.js';
|
import { getTileUrls, isValidHttpUrl, fixTileJSONCenter } from './utils.js';
|
||||||
import {
|
import {
|
||||||
PMtilesOpen,
|
openPMtiles,
|
||||||
GetPMtilesInfo,
|
getPMtilesInfo,
|
||||||
GetPMtilesTile,
|
getPMtilesTile,
|
||||||
} from './pmtiles_adapter.js';
|
} from './pmtiles_adapter.js';
|
||||||
|
|
||||||
export const serve_data = {
|
export const serve_data = {
|
||||||
|
@ -53,8 +53,8 @@ export const serve_data = {
|
||||||
) {
|
) {
|
||||||
return res.status(404).send('Out of bounds');
|
return res.status(404).send('Out of bounds');
|
||||||
}
|
}
|
||||||
if (item.source_type === 'pmtiles') {
|
if (item.sourceType === 'pmtiles') {
|
||||||
let tileinfo = await GetPMtilesTile(item.source, z, x, y);
|
let tileinfo = await getPMtilesTile(item.source, z, x, y);
|
||||||
if (tileinfo == undefined || tileinfo.data == undefined) {
|
if (tileinfo == undefined || tileinfo.data == undefined) {
|
||||||
return res.status(404).send('Not found');
|
return res.status(404).send('Not found');
|
||||||
} else {
|
} else {
|
||||||
|
@ -99,7 +99,7 @@ export const serve_data = {
|
||||||
|
|
||||||
return res.status(200).send(data);
|
return res.status(200).send(data);
|
||||||
}
|
}
|
||||||
} else if (item.source_type === 'mbtiles') {
|
} else if (item.sourceType === 'mbtiles') {
|
||||||
item.source.getTile(z, x, y, (err, data, headers) => {
|
item.source.getTile(z, x, y, (err, data, headers) => {
|
||||||
let isGzipped;
|
let isGzipped;
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -223,11 +223,11 @@ export const serve_data = {
|
||||||
}
|
}
|
||||||
|
|
||||||
let source;
|
let source;
|
||||||
let source_type;
|
let sourceType;
|
||||||
if (inputType === 'pmtiles') {
|
if (inputType === 'pmtiles') {
|
||||||
source = PMtilesOpen(inputFile);
|
source = openPMtiles(inputFile);
|
||||||
source_type = 'pmtiles';
|
sourceType = 'pmtiles';
|
||||||
const metadata = await GetPMtilesInfo(source);
|
const metadata = await getPMtilesInfo(source);
|
||||||
|
|
||||||
tileJSON['name'] = id;
|
tileJSON['name'] = id;
|
||||||
tileJSON['format'] = 'pbf';
|
tileJSON['format'] = 'pbf';
|
||||||
|
@ -245,7 +245,7 @@ export const serve_data = {
|
||||||
tileJSON = options.dataDecoratorFunc(id, 'tilejson', tileJSON);
|
tileJSON = options.dataDecoratorFunc(id, 'tilejson', tileJSON);
|
||||||
}
|
}
|
||||||
} else if (inputType === 'mbtiles') {
|
} else if (inputType === 'mbtiles') {
|
||||||
source_type = 'mbtiles';
|
sourceType = 'mbtiles';
|
||||||
const sourceInfoPromise = new Promise((resolve, reject) => {
|
const sourceInfoPromise = new Promise((resolve, reject) => {
|
||||||
source = new MBTiles(inputFile + '?mode=ro', (err) => {
|
source = new MBTiles(inputFile + '?mode=ro', (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -285,7 +285,7 @@ export const serve_data = {
|
||||||
tileJSON,
|
tileJSON,
|
||||||
publicUrl,
|
publicUrl,
|
||||||
source,
|
source,
|
||||||
source_type,
|
sourceType,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,7 @@ import express from 'express';
|
||||||
|
|
||||||
import { getFontsPbf, listFonts } from './utils.js';
|
import { getFontsPbf, listFonts } from './utils.js';
|
||||||
|
|
||||||
export const serve_font = (options, allowedFonts) => {
|
export const serve_font = async (options, allowedFonts) => {
|
||||||
const app = express().disable('x-powered-by');
|
const app = express().disable('x-powered-by');
|
||||||
|
|
||||||
const lastModified = new Date().toUTCString();
|
const lastModified = new Date().toUTCString();
|
||||||
|
@ -13,25 +13,29 @@ export const serve_font = (options, allowedFonts) => {
|
||||||
|
|
||||||
const existingFonts = {};
|
const existingFonts = {};
|
||||||
|
|
||||||
app.get('/fonts/:fontstack/:range([\\d]+-[\\d]+).pbf', (req, res, next) => {
|
app.get(
|
||||||
|
'/fonts/:fontstack/:range([\\d]+-[\\d]+).pbf',
|
||||||
|
async (req, res, next) => {
|
||||||
const fontstack = decodeURI(req.params.fontstack);
|
const fontstack = decodeURI(req.params.fontstack);
|
||||||
const range = req.params.range;
|
const range = req.params.range;
|
||||||
|
|
||||||
getFontsPbf(
|
try {
|
||||||
|
const concatenated = await getFontsPbf(
|
||||||
options.serveAllFonts ? null : allowedFonts,
|
options.serveAllFonts ? null : allowedFonts,
|
||||||
fontPath,
|
fontPath,
|
||||||
fontstack,
|
fontstack,
|
||||||
range,
|
range,
|
||||||
existingFonts,
|
existingFonts,
|
||||||
).then(
|
);
|
||||||
(concated) => {
|
|
||||||
res.header('Content-type', 'application/x-protobuf');
|
res.header('Content-type', 'application/x-protobuf');
|
||||||
res.header('Last-Modified', lastModified);
|
res.header('Last-Modified', lastModified);
|
||||||
return res.send(concated);
|
return res.send(concatenated);
|
||||||
|
} catch (err) {
|
||||||
|
res.status(400).header('Content-Type', 'text/plain').send(err);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
(err) => res.status(400).header('Content-Type', 'text/plain').send(err),
|
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
app.get('/fonts.json', (req, res, next) => {
|
app.get('/fonts.json', (req, res, next) => {
|
||||||
res.header('Content-type', 'application/json');
|
res.header('Content-type', 'application/json');
|
||||||
|
@ -40,8 +44,7 @@ export const serve_font = (options, allowedFonts) => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return listFonts(options.paths.fonts).then((fonts) => {
|
const fonts = await listFonts(options.paths.fonts);
|
||||||
Object.assign(existingFonts, fonts);
|
Object.assign(existingFonts, fonts);
|
||||||
return app;
|
return app;
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,13 +1,24 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
// SECTION START
|
||||||
|
//
|
||||||
|
// The order of the two imports below is important.
|
||||||
|
// For an unknown reason, if the order is reversed, rendering can crash.
|
||||||
|
// This happens on ARM:
|
||||||
|
// > terminate called after throwing an instance of 'std::runtime_error'
|
||||||
|
// > what(): Cannot read GLX extensions.
|
||||||
|
import 'canvas';
|
||||||
|
import '@maplibre/maplibre-gl-native';
|
||||||
|
//
|
||||||
|
// SECTION END
|
||||||
|
|
||||||
import advancedPool from 'advanced-pool';
|
import advancedPool from 'advanced-pool';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
import util from 'util';
|
import util from 'util';
|
||||||
import zlib from 'zlib';
|
import zlib from 'zlib';
|
||||||
import { renderOverlay, renderWatermark, renderAttribution } from './render.js';
|
import sharp from 'sharp';
|
||||||
import sharp from 'sharp'; // sharp has to be required before node-canvas on linux but after it on windows. see https://github.com/lovell/sharp/issues/371
|
|
||||||
import clone from 'clone';
|
import clone from 'clone';
|
||||||
import Color from 'color';
|
import Color from 'color';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
|
@ -26,10 +37,11 @@ import {
|
||||||
fixTileJSONCenter,
|
fixTileJSONCenter,
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import {
|
import {
|
||||||
PMtilesOpen,
|
openPMtiles,
|
||||||
GetPMtilesInfo,
|
getPMtilesInfo,
|
||||||
GetPMtilesTile,
|
getPMtilesTile,
|
||||||
} from './pmtiles_adapter.js';
|
} from './pmtiles_adapter.js';
|
||||||
|
import { renderOverlay, renderWatermark, renderAttribution } from './render.js';
|
||||||
|
|
||||||
const FLOAT_PATTERN = '[+-]?(?:\\d+|\\d+.?\\d+)';
|
const FLOAT_PATTERN = '[+-]?(?:\\d+|\\d+.?\\d+)';
|
||||||
const PATH_PATTERN =
|
const PATH_PATTERN =
|
||||||
|
@ -97,7 +109,7 @@ function createEmptyResponse(format, color, callback) {
|
||||||
raw: {
|
raw: {
|
||||||
width: 1,
|
width: 1,
|
||||||
height: 1,
|
height: 1,
|
||||||
channels: channels,
|
channels,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.toFormat(format)
|
.toFormat(format)
|
||||||
|
@ -398,17 +410,17 @@ const respondImage = (
|
||||||
if (mode === 'tile' && tileMargin === 0) {
|
if (mode === 'tile' && tileMargin === 0) {
|
||||||
pool = item.map.renderers[scale];
|
pool = item.map.renderers[scale];
|
||||||
} else {
|
} else {
|
||||||
pool = item.map.renderers_static[scale];
|
pool = item.map.renderersStatic[scale];
|
||||||
}
|
}
|
||||||
pool.acquire((err, renderer) => {
|
pool.acquire((err, renderer) => {
|
||||||
const mlglZ = Math.max(0, z - 1);
|
const mlglZ = Math.max(0, z - 1);
|
||||||
const params = {
|
const params = {
|
||||||
zoom: mlglZ,
|
zoom: mlglZ,
|
||||||
center: [lon, lat],
|
center: [lon, lat],
|
||||||
bearing: bearing,
|
bearing,
|
||||||
pitch: pitch,
|
pitch,
|
||||||
width: width,
|
width,
|
||||||
height: height,
|
height,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (z === 0) {
|
if (z === 0) {
|
||||||
|
@ -428,24 +440,9 @@ const respondImage = (
|
||||||
return res.status(500).header('Content-Type', 'text/plain').send(err);
|
return res.status(500).header('Content-Type', 'text/plain').send(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix semi-transparent outlines on raw, premultiplied input
|
|
||||||
// https://github.com/maptiler/tileserver-gl/issues/350#issuecomment-477857040
|
|
||||||
for (let i = 0; i < data.length; i += 4) {
|
|
||||||
const alpha = data[i + 3];
|
|
||||||
const norm = alpha / 255;
|
|
||||||
if (alpha === 0) {
|
|
||||||
data[i] = 0;
|
|
||||||
data[i + 1] = 0;
|
|
||||||
data[i + 2] = 0;
|
|
||||||
} else {
|
|
||||||
data[i] = data[i] / norm;
|
|
||||||
data[i + 1] = data[i + 1] / norm;
|
|
||||||
data[i + 2] = data[i + 2] / norm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const image = sharp(data, {
|
const image = sharp(data, {
|
||||||
raw: {
|
raw: {
|
||||||
|
premultiplied: true,
|
||||||
width: params.width * scale,
|
width: params.width * scale,
|
||||||
height: params.height * scale,
|
height: params.height * scale,
|
||||||
channels: 4,
|
channels: 4,
|
||||||
|
@ -471,14 +468,14 @@ const respondImage = (
|
||||||
image.resize(width * scale, height * scale);
|
image.resize(width * scale, height * scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
const composite_array = [];
|
const composites = [];
|
||||||
if (overlay) {
|
if (overlay) {
|
||||||
composite_array.push({ input: overlay });
|
composites.push({ input: overlay });
|
||||||
}
|
}
|
||||||
if (item.watermark) {
|
if (item.watermark) {
|
||||||
const canvas = renderWatermark(width, height, scale, item.watermark);
|
const canvas = renderWatermark(width, height, scale, item.watermark);
|
||||||
|
|
||||||
composite_array.push({ input: canvas.toBuffer() });
|
composites.push({ input: canvas.toBuffer() });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode === 'static' && item.staticAttributionText) {
|
if (mode === 'static' && item.staticAttributionText) {
|
||||||
|
@ -489,11 +486,11 @@ const respondImage = (
|
||||||
item.staticAttributionText,
|
item.staticAttributionText,
|
||||||
);
|
);
|
||||||
|
|
||||||
composite_array.push({ input: canvas.toBuffer() });
|
composites.push({ input: canvas.toBuffer() });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (composite_array.length > 0) {
|
if (composites.length > 0) {
|
||||||
image.composite(composite_array);
|
image.composite(composites);
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatQuality = (options.formatQuality || {})[format];
|
const formatQuality = (options.formatQuality || {})[format];
|
||||||
|
@ -524,7 +521,7 @@ const existingFonts = {};
|
||||||
let maxScaleFactor = 2;
|
let maxScaleFactor = 2;
|
||||||
|
|
||||||
export const serve_rendered = {
|
export const serve_rendered = {
|
||||||
init: (options, repo) => {
|
init: async (options, repo) => {
|
||||||
maxScaleFactor = Math.min(Math.floor(options.maxScaleFactor || 3), 9);
|
maxScaleFactor = Math.min(Math.floor(options.maxScaleFactor || 3), 9);
|
||||||
let scalePattern = '';
|
let scalePattern = '';
|
||||||
for (let i = 2; i <= maxScaleFactor; i++) {
|
for (let i = 2; i <= maxScaleFactor; i++) {
|
||||||
|
@ -573,19 +570,10 @@ export const serve_rendered = {
|
||||||
],
|
],
|
||||||
z,
|
z,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
return respondImage(
|
return respondImage(
|
||||||
options,
|
options, item, z, tileCenter[0], tileCenter[1], 0, 0, tileSize, tileSize, scale, format, res,
|
||||||
item,
|
|
||||||
z,
|
|
||||||
tileCenter[0],
|
|
||||||
tileCenter[1],
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
tileSize,
|
|
||||||
tileSize,
|
|
||||||
scale,
|
|
||||||
format,
|
|
||||||
res,
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -641,35 +629,15 @@ export const serve_rendered = {
|
||||||
options,
|
options,
|
||||||
transformer,
|
transformer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
const overlay = await renderOverlay(
|
const overlay = await renderOverlay(
|
||||||
z,
|
z, x, y, bearing, pitch, w, h, scale, paths, markers, req.query,
|
||||||
x,
|
|
||||||
y,
|
|
||||||
bearing,
|
|
||||||
pitch,
|
|
||||||
w,
|
|
||||||
h,
|
|
||||||
scale,
|
|
||||||
paths,
|
|
||||||
markers,
|
|
||||||
req.query,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
return respondImage(
|
return respondImage(
|
||||||
options,
|
options, item, z, x, y, bearing, pitch, w, h, scale, format, res, overlay, 'static',
|
||||||
item,
|
|
||||||
z,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
bearing,
|
|
||||||
pitch,
|
|
||||||
w,
|
|
||||||
h,
|
|
||||||
scale,
|
|
||||||
format,
|
|
||||||
res,
|
|
||||||
overlay,
|
|
||||||
'static',
|
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
next(e);
|
next(e);
|
||||||
|
@ -723,34 +691,15 @@ export const serve_rendered = {
|
||||||
options,
|
options,
|
||||||
transformer,
|
transformer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
const overlay = await renderOverlay(
|
const overlay = await renderOverlay(
|
||||||
z,
|
z, x, y, bearing, pitch, w, h, scale, paths, markers, req.query,
|
||||||
x,
|
|
||||||
y,
|
|
||||||
bearing,
|
|
||||||
pitch,
|
|
||||||
w,
|
|
||||||
h,
|
|
||||||
scale,
|
|
||||||
paths,
|
|
||||||
markers,
|
|
||||||
req.query,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
return respondImage(
|
return respondImage(
|
||||||
options,
|
options, item, z, x, y, bearing, pitch, w, h, scale, format, res, overlay, 'static',
|
||||||
item,
|
|
||||||
z,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
bearing,
|
|
||||||
pitch,
|
|
||||||
w,
|
|
||||||
h,
|
|
||||||
scale,
|
|
||||||
format,
|
|
||||||
res,
|
|
||||||
overlay,
|
|
||||||
'static',
|
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
next(e);
|
next(e);
|
||||||
|
@ -856,35 +805,14 @@ export const serve_rendered = {
|
||||||
const x = center[0];
|
const x = center[0];
|
||||||
const y = center[1];
|
const y = center[1];
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
const overlay = await renderOverlay(
|
const overlay = await renderOverlay(
|
||||||
z,
|
z, x, y, bearing, pitch, w, h, scale, paths, markers, req.query,
|
||||||
x,
|
|
||||||
y,
|
|
||||||
bearing,
|
|
||||||
pitch,
|
|
||||||
w,
|
|
||||||
h,
|
|
||||||
scale,
|
|
||||||
paths,
|
|
||||||
markers,
|
|
||||||
req.query,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
return respondImage(
|
return respondImage(
|
||||||
options,
|
options, item, z, x, y, bearing, pitch, w, h, scale, format, res, overlay, 'static',
|
||||||
item,
|
|
||||||
z,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
bearing,
|
|
||||||
pitch,
|
|
||||||
w,
|
|
||||||
h,
|
|
||||||
scale,
|
|
||||||
format,
|
|
||||||
res,
|
|
||||||
overlay,
|
|
||||||
'static',
|
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
next(e);
|
next(e);
|
||||||
|
@ -909,25 +837,24 @@ export const serve_rendered = {
|
||||||
return res.send(info);
|
return res.send(info);
|
||||||
});
|
});
|
||||||
|
|
||||||
return listFonts(options.paths.fonts).then((fonts) => {
|
const fonts = await listFonts(options.paths.fonts);
|
||||||
Object.assign(existingFonts, fonts);
|
Object.assign(existingFonts, fonts);
|
||||||
return app;
|
return app;
|
||||||
});
|
|
||||||
},
|
},
|
||||||
add: async (options, repo, params, id, publicUrl, dataResolver) => {
|
add: async (options, repo, params, id, publicUrl, dataResolver) => {
|
||||||
const map = {
|
const map = {
|
||||||
renderers: [],
|
renderers: [],
|
||||||
renderers_static: [],
|
renderersStatic: [],
|
||||||
sources: {},
|
sources: {},
|
||||||
source_types: {},
|
sourceTypes: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
let styleJSON;
|
let styleJSON;
|
||||||
const createPool = (ratio, mode, min, max) => {
|
const createPool = (ratio, mode, min, max) => {
|
||||||
const createRenderer = (ratio, createCallback) => {
|
const createRenderer = (ratio, createCallback) => {
|
||||||
const renderer = new mlgl.Map({
|
const renderer = new mlgl.Map({
|
||||||
mode: mode,
|
mode,
|
||||||
ratio: ratio,
|
ratio,
|
||||||
request: async (req, callback) => {
|
request: async (req, callback) => {
|
||||||
const protocol = req.url.split(':')[0];
|
const protocol = req.url.split(':')[0];
|
||||||
// console.log('Handling request:', req);
|
// console.log('Handling request:', req);
|
||||||
|
@ -941,25 +868,24 @@ export const serve_rendered = {
|
||||||
const parts = req.url.split('/');
|
const parts = req.url.split('/');
|
||||||
const fontstack = unescape(parts[2]);
|
const fontstack = unescape(parts[2]);
|
||||||
const range = parts[3].split('.')[0];
|
const range = parts[3].split('.')[0];
|
||||||
getFontsPbf(
|
|
||||||
|
try {
|
||||||
|
const concatenated = await getFontsPbf(
|
||||||
null,
|
null,
|
||||||
options.paths[protocol],
|
options.paths[protocol],
|
||||||
fontstack,
|
fontstack,
|
||||||
range,
|
range,
|
||||||
existingFonts,
|
existingFonts,
|
||||||
).then(
|
|
||||||
(concated) => {
|
|
||||||
callback(null, { data: concated });
|
|
||||||
},
|
|
||||||
(err) => {
|
|
||||||
callback(err, { data: null });
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
callback(null, { data: concatenated });
|
||||||
|
} catch (err) {
|
||||||
|
callback(err, { data: null });
|
||||||
|
}
|
||||||
} else if (protocol === 'mbtiles' || protocol === 'pmtiles') {
|
} else if (protocol === 'mbtiles' || protocol === 'pmtiles') {
|
||||||
const parts = req.url.split('/');
|
const parts = req.url.split('/');
|
||||||
const sourceId = parts[2];
|
const sourceId = parts[2];
|
||||||
const source = map.sources[sourceId];
|
const source = map.sources[sourceId];
|
||||||
const source_type = map.source_types[sourceId];
|
const sourceType = map.sourceTypes[sourceId];
|
||||||
const sourceInfo = styleJSON.sources[sourceId];
|
const sourceInfo = styleJSON.sources[sourceId];
|
||||||
|
|
||||||
const z = parts[3] | 0;
|
const z = parts[3] | 0;
|
||||||
|
@ -967,8 +893,8 @@ export const serve_rendered = {
|
||||||
const y = parts[5].split('.')[0] | 0;
|
const y = parts[5].split('.')[0] | 0;
|
||||||
const format = parts[5].split('.')[1];
|
const format = parts[5].split('.')[1];
|
||||||
|
|
||||||
if (source_type === 'pmtiles') {
|
if (sourceType === 'pmtiles') {
|
||||||
let tileinfo = await GetPMtilesTile(source, z, x, y);
|
let tileinfo = await getPMtilesTile(source, z, x, y);
|
||||||
let data = tileinfo.data;
|
let data = tileinfo.data;
|
||||||
let headers = tileinfo.header;
|
let headers = tileinfo.header;
|
||||||
if (data == undefined) {
|
if (data == undefined) {
|
||||||
|
@ -1002,7 +928,7 @@ export const serve_rendered = {
|
||||||
|
|
||||||
callback(null, response);
|
callback(null, response);
|
||||||
}
|
}
|
||||||
} else if (source_type === 'mbtiles') {
|
} else if (sourceType === 'mbtiles') {
|
||||||
source.getTile(z, x, y, (err, data, headers) => {
|
source.getTile(z, x, y, (err, data, headers) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (options.verbose)
|
if (options.verbose)
|
||||||
|
@ -1087,8 +1013,8 @@ export const serve_rendered = {
|
||||||
createCallback(null, renderer);
|
createCallback(null, renderer);
|
||||||
};
|
};
|
||||||
return new advancedPool.Pool({
|
return new advancedPool.Pool({
|
||||||
min: min,
|
min,
|
||||||
max: max,
|
max,
|
||||||
create: createRenderer.bind(null, ratio),
|
create: createRenderer.bind(null, ratio),
|
||||||
destroy: (renderer) => {
|
destroy: (renderer) => {
|
||||||
renderer.release();
|
renderer.release();
|
||||||
|
@ -1163,7 +1089,7 @@ export const serve_rendered = {
|
||||||
|
|
||||||
const queue = [];
|
const queue = [];
|
||||||
for (const name of Object.keys(styleJSON.sources)) {
|
for (const name of Object.keys(styleJSON.sources)) {
|
||||||
let source_type;
|
let sourceType;
|
||||||
let source = styleJSON.sources[name];
|
let source = styleJSON.sources[name];
|
||||||
let url = source.url;
|
let url = source.url;
|
||||||
if (
|
if (
|
||||||
|
@ -1184,10 +1110,10 @@ export const serve_rendered = {
|
||||||
}
|
}
|
||||||
|
|
||||||
let inputFile;
|
let inputFile;
|
||||||
const DataInfo = dataResolver(dataId);
|
const dataInfo = dataResolver(dataId);
|
||||||
if (DataInfo.inputfile) {
|
if (dataInfo.inputFile) {
|
||||||
inputFile = DataInfo.inputfile;
|
inputFile = dataInfo.inputFile;
|
||||||
source_type = DataInfo.filetype;
|
sourceType = dataInfo.fileType;
|
||||||
} else {
|
} else {
|
||||||
console.error(`ERROR: data "${inputFile}" not found!`);
|
console.error(`ERROR: data "${inputFile}" not found!`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
@ -1200,10 +1126,10 @@ export const serve_rendered = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source_type === 'pmtiles') {
|
if (sourceType === 'pmtiles') {
|
||||||
map.sources[name] = PMtilesOpen(inputFile);
|
map.sources[name] = openPMtiles(inputFile);
|
||||||
map.source_types[name] = 'pmtiles';
|
map.sourceTypes[name] = 'pmtiles';
|
||||||
const metadata = await GetPMtilesInfo(map.sources[name]);
|
const metadata = await getPMtilesInfo(map.sources[name]);
|
||||||
|
|
||||||
if (!repoobj.dataProjWGStoInternalWGS && metadata.proj4) {
|
if (!repoobj.dataProjWGStoInternalWGS && metadata.proj4) {
|
||||||
// how to do this for multiple sources with different proj4 defs?
|
// how to do this for multiple sources with different proj4 defs?
|
||||||
|
@ -1248,7 +1174,7 @@ export const serve_rendered = {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
map.source_types[name] = 'mbtiles';
|
map.sourceTypes[name] = 'mbtiles';
|
||||||
|
|
||||||
if (!repoobj.dataProjWGStoInternalWGS && info.proj4) {
|
if (!repoobj.dataProjWGStoInternalWGS && info.proj4) {
|
||||||
// how to do this for multiple sources with different proj4 defs?
|
// how to do this for multiple sources with different proj4 defs?
|
||||||
|
@ -1296,7 +1222,8 @@ export const serve_rendered = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderersReadyPromise = Promise.all(queue).then(() => {
|
await Promise.all(queue);
|
||||||
|
|
||||||
// standard and @2x tiles are much more usual -> default to larger pools
|
// standard and @2x tiles are much more usual -> default to larger pools
|
||||||
const minPoolSizes = options.minRendererPoolSizes || [8, 4, 2];
|
const minPoolSizes = options.minRendererPoolSizes || [8, 4, 2];
|
||||||
const maxPoolSizes = options.maxRendererPoolSizes || [16, 8, 4];
|
const maxPoolSizes = options.maxRendererPoolSizes || [16, 8, 4];
|
||||||
|
@ -1306,16 +1233,13 @@ export const serve_rendered = {
|
||||||
const minPoolSize = minPoolSizes[i];
|
const minPoolSize = minPoolSizes[i];
|
||||||
const maxPoolSize = Math.max(minPoolSize, maxPoolSizes[j]);
|
const maxPoolSize = Math.max(minPoolSize, maxPoolSizes[j]);
|
||||||
map.renderers[s] = createPool(s, 'tile', minPoolSize, maxPoolSize);
|
map.renderers[s] = createPool(s, 'tile', minPoolSize, maxPoolSize);
|
||||||
map.renderers_static[s] = createPool(
|
map.renderersStatic[s] = createPool(
|
||||||
s,
|
s,
|
||||||
'static',
|
'static',
|
||||||
minPoolSize,
|
minPoolSize,
|
||||||
maxPoolSize,
|
maxPoolSize,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
return renderersReadyPromise;
|
|
||||||
},
|
},
|
||||||
remove: (repo, id) => {
|
remove: (repo, id) => {
|
||||||
const item = repo[id];
|
const item = repo[id];
|
||||||
|
@ -1323,7 +1247,7 @@ export const serve_rendered = {
|
||||||
item.map.renderers.forEach((pool) => {
|
item.map.renderers.forEach((pool) => {
|
||||||
pool.close();
|
pool.close();
|
||||||
});
|
});
|
||||||
item.map.renderers_static.forEach((pool) => {
|
item.map.renderersStatic.forEach((pool) => {
|
||||||
pool.close();
|
pool.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,12 @@ import { getPublicUrl } from './utils.js';
|
||||||
|
|
||||||
const httpTester = /^(http(s)?:)?\/\//;
|
const httpTester = /^(http(s)?:)?\/\//;
|
||||||
|
|
||||||
const fixUrl = (req, url, publicUrl, opt_nokey) => {
|
const fixUrl = (req, url, publicUrl) => {
|
||||||
if (!url || typeof url !== 'string' || url.indexOf('local://') !== 0) {
|
if (!url || typeof url !== 'string' || url.indexOf('local://') !== 0) {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
const queryParams = [];
|
const queryParams = [];
|
||||||
if (!opt_nokey && req.query.key) {
|
if (req.query.key) {
|
||||||
queryParams.unshift(`key=${encodeURIComponent(req.query.key)}`);
|
queryParams.unshift(`key=${encodeURIComponent(req.query.key)}`);
|
||||||
}
|
}
|
||||||
let query = '';
|
let query = '';
|
||||||
|
@ -42,20 +42,10 @@ export const serve_style = {
|
||||||
}
|
}
|
||||||
// mapbox-gl-js viewer cannot handle sprite urls with query
|
// mapbox-gl-js viewer cannot handle sprite urls with query
|
||||||
if (styleJSON_.sprite) {
|
if (styleJSON_.sprite) {
|
||||||
styleJSON_.sprite = fixUrl(
|
styleJSON_.sprite = fixUrl(req, styleJSON_.sprite, item.publicUrl);
|
||||||
req,
|
|
||||||
styleJSON_.sprite,
|
|
||||||
item.publicUrl,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (styleJSON_.glyphs) {
|
if (styleJSON_.glyphs) {
|
||||||
styleJSON_.glyphs = fixUrl(
|
styleJSON_.glyphs = fixUrl(req, styleJSON_.glyphs, item.publicUrl);
|
||||||
req,
|
|
||||||
styleJSON_.glyphs,
|
|
||||||
item.publicUrl,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return res.send(styleJSON_);
|
return res.send(styleJSON_);
|
||||||
});
|
});
|
||||||
|
|
|
@ -141,11 +141,8 @@ function start(opts) {
|
||||||
|
|
||||||
// Load all available icons into a settings object
|
// Load all available icons into a settings object
|
||||||
startupPromises.push(
|
startupPromises.push(
|
||||||
new Promise((resolve) => {
|
|
||||||
getFiles(paths.icons).then((files) => {
|
getFiles(paths.icons).then((files) => {
|
||||||
paths.availableIcons = files;
|
paths.availableIcons = files;
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -182,15 +179,15 @@ function start(opts) {
|
||||||
item,
|
item,
|
||||||
id,
|
id,
|
||||||
opts.publicUrl,
|
opts.publicUrl,
|
||||||
(StyleSourceId, protocol) => {
|
(styleSourceId, protocol) => {
|
||||||
let dataItemId;
|
let dataItemId;
|
||||||
for (const id of Object.keys(data)) {
|
for (const id of Object.keys(data)) {
|
||||||
if (id === StyleSourceId) {
|
if (id === styleSourceId) {
|
||||||
// Style id was found in data ids, return that id
|
// Style id was found in data ids, return that id
|
||||||
dataItemId = id;
|
dataItemId = id;
|
||||||
} else {
|
} else {
|
||||||
const fileType = Object.keys(data[id])[0];
|
const fileType = Object.keys(data[id])[0];
|
||||||
if (data[id][fileType] === StyleSourceId) {
|
if (data[id][fileType] === styleSourceId) {
|
||||||
// Style id was found in data filename, return the id that filename belong to
|
// Style id was found in data filename, return the id that filename belong to
|
||||||
dataItemId = id;
|
dataItemId = id;
|
||||||
}
|
}
|
||||||
|
@ -202,21 +199,21 @@ function start(opts) {
|
||||||
} else {
|
} else {
|
||||||
if (!allowMoreData) {
|
if (!allowMoreData) {
|
||||||
console.log(
|
console.log(
|
||||||
`ERROR: style "${item.style}" using unknown file "${StyleSourceId}"! Skipping...`,
|
`ERROR: style "${item.style}" using unknown file "${styleSourceId}"! Skipping...`,
|
||||||
);
|
);
|
||||||
return undefined;
|
return undefined;
|
||||||
} else {
|
} else {
|
||||||
let id =
|
let id =
|
||||||
StyleSourceId.substr(0, StyleSourceId.lastIndexOf('.')) ||
|
styleSourceId.substr(0, styleSourceId.lastIndexOf('.')) ||
|
||||||
StyleSourceId;
|
styleSourceId;
|
||||||
if (isValidHttpUrl(StyleSourceId)) {
|
if (isValidHttpUrl(styleSourceId)) {
|
||||||
id =
|
id =
|
||||||
fnv1a(StyleSourceId) + '_' + id.replace(/^.*\/(.*)$/, '$1');
|
fnv1a(styleSourceId) + '_' + id.replace(/^.*\/(.*)$/, '$1');
|
||||||
}
|
}
|
||||||
while (data[id]) id += '_'; //if the data source id already exists, add a "_" untill it doesn't
|
while (data[id]) id += '_'; //if the data source id already exists, add a "_" untill it doesn't
|
||||||
//Add the new data source to the data array.
|
//Add the new data source to the data array.
|
||||||
data[id] = {
|
data[id] = {
|
||||||
[protocol]: StyleSourceId,
|
[protocol]: styleSourceId,
|
||||||
};
|
};
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
|
@ -239,15 +236,15 @@ function start(opts) {
|
||||||
item,
|
item,
|
||||||
id,
|
id,
|
||||||
opts.publicUrl,
|
opts.publicUrl,
|
||||||
(StyleSourceId) => {
|
function dataResolver(styleSourceId) {
|
||||||
let fileType;
|
let fileType;
|
||||||
let inputFile;
|
let inputFile;
|
||||||
for (const id of Object.keys(data)) {
|
for (const id of Object.keys(data)) {
|
||||||
fileType = Object.keys(data[id])[0];
|
fileType = Object.keys(data[id])[0];
|
||||||
if (StyleSourceId == id) {
|
if (styleSourceId == id) {
|
||||||
inputFile = data[id][fileType];
|
inputFile = data[id][fileType];
|
||||||
break;
|
break;
|
||||||
} else if (data[id][fileType] == StyleSourceId) {
|
} else if (data[id][fileType] == styleSourceId) {
|
||||||
inputFile = data[id][fileType];
|
inputFile = data[id][fileType];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -256,7 +253,7 @@ function start(opts) {
|
||||||
inputFile = path.resolve(options.paths[fileType], inputFile);
|
inputFile = path.resolve(options.paths[fileType], inputFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { inputfile: inputFile, filetype: fileType };
|
return { inputFile, fileType };
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -347,7 +344,7 @@ function start(opts) {
|
||||||
result.push({
|
result.push({
|
||||||
version: styleJSON.version,
|
version: styleJSON.version,
|
||||||
name: styleJSON.name,
|
name: styleJSON.name,
|
||||||
id: id,
|
id,
|
||||||
url: `${getPublicUrl(
|
url: `${getPublicUrl(
|
||||||
opts.publicUrl,
|
opts.publicUrl,
|
||||||
req,
|
req,
|
||||||
|
@ -633,9 +630,9 @@ function start(opts) {
|
||||||
enableShutdown(server);
|
enableShutdown(server);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
app: app,
|
app,
|
||||||
server: server,
|
server,
|
||||||
startupPromise: startupPromise,
|
startupPromise,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
30
src/utils.js
30
src/utils.js
|
@ -1,7 +1,8 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'node:fs';
|
import fsPromises from 'fs/promises';
|
||||||
|
import fs, { existsSync } from 'node:fs';
|
||||||
import clone from 'clone';
|
import clone from 'clone';
|
||||||
import glyphCompose from '@mapbox/glyph-pbf-composite';
|
import glyphCompose from '@mapbox/glyph-pbf-composite';
|
||||||
|
|
||||||
|
@ -139,7 +140,7 @@ const getFontPbf = (allowedFonts, fontPath, name, range, fallbacks) =>
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export const getFontsPbf = (
|
export const getFontsPbf = async (
|
||||||
allowedFonts,
|
allowedFonts,
|
||||||
fontPath,
|
fontPath,
|
||||||
names,
|
names,
|
||||||
|
@ -160,35 +161,24 @@ export const getFontsPbf = (
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(queue).then((values) => glyphCompose.combine(values));
|
const values = await Promise.all(queue);
|
||||||
|
return glyphCompose.combine(values);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const listFonts = async (fontPath) => {
|
export const listFonts = async (fontPath) => {
|
||||||
const existingFonts = {};
|
const existingFonts = {};
|
||||||
const fontListingPromise = new Promise((resolve, reject) => {
|
|
||||||
fs.readdir(fontPath, (err, files) => {
|
const files = await fsPromises.readdir(fontPath);
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
fs.stat(path.join(fontPath, file), (err, stats) => {
|
const stats = await fsPromises.stat(path.join(fontPath, file));
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (
|
if (
|
||||||
stats.isDirectory() &&
|
stats.isDirectory() &&
|
||||||
fs.existsSync(path.join(fontPath, file, '0-255.pbf'))
|
existsSync(path.join(fontPath, file, '0-255.pbf'))
|
||||||
) {
|
) {
|
||||||
existingFonts[path.basename(file)] = true;
|
existingFonts[path.basename(file)] = true;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
await fontListingPromise;
|
|
||||||
return existingFonts;
|
return existingFonts;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue