
* first attempt to upgrade express to v5 Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * try to fix https://github.com/maptiler/tileserver-gl/issues/1411 Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * cleanup server.js Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * cleanup serve_font.js Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * cleanup sever_rendered.js Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * cleanup server_data.js Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * cleanup serve_style Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * Update serve_style.js Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * Move UV_THREADPOOL_SIZE to main thred Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * cleanup utils.js Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * Use common app.get for images and static images Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * add allowedTileSizes and option Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * cleanup error responses Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * fix /style/id.json with next('route') Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * improve sprite path Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * add parseFloadts around zxy Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * simplify server_data Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * move tile fetch and add fix verbose logging Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * add Handling request to verbose logging Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * first attempt to upgrade express to v5 Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * try to fix https://github.com/maptiler/tileserver-gl/issues/1411 Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * cleanup server.js Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * cleanup serve_font.js Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * cleanup sever_rendered.js Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * cleanup server_data.js Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * cleanup serve_style Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * Update serve_style.js Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * Move UV_THREADPOOL_SIZE to main thred Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * cleanup utils.js Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * Use common app.get for images and static images Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * add allowedTileSizes and option Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * cleanup error responses Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * fix /style/id.json with next('route') Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * improve sprite path Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * add parseFloadts around zxy Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * simplify server_data Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * move tile fetch and add fix verbose logging Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * add Handling request to verbose logging Co-Authored-By: Andrew Calcutt <acalcutt@techidiots.net> * merge elevation changes * lint format * add verbose logging, improve headers * try to fix codeql Information exposure through a stack trace * test * all tests passing * cleanup unneeded changes * cleanup * try to fix codeql error * font fixes * fix tile size issue * try to improve scale + codeql * codeql for sprite logging * codeql serve fonts * codeql fixes * fix failing test with multiple fonts * Update serve_font.js * codeql * codeql * codeql * Update utils.js * codeql * codeql * codeql * codeql * codeql sanitize * Update serve_font.js * Update serve_font.js * remove useless assignment * move isGzipped * add if-modified-since and cache-control * use consistent cache control * reformat * codeql * codeql * codeql * codeql * codeql * codeql * codeql * Update serve_font.js * Update serve_font.js * Update serve_font.js * Update serve_style.js * Update serve_style.js * Update serve_style.js * Revert "Update serve_style.js" This reverts commite0574b1887
. * Revert "Update serve_style.js" This reverts commitb1e1d72f25
. * Revert "Update serve_style.js" This reverts commit0f3629c752
. * Add readFile function * use readFile, add path.normalize * Update serve_rendered.js * simplify input checking * Update utils.js * codeql * Revert "codeql" This reverts commite18874fda0
. * Revert "Update utils.js" This reverts commit5de617dfe2
. * Revert "simplify input checking" This reverts commit62a3212629
. * move allowed functions to utils.js * use xy[0],xy[1], * uprade canvas per https://github.com/maptiler/tileserver-gl/issues/1433 * make font regex less restrictive * fix regex error * Add version and changelog * Update CHANGELOG.md * Update CHANGELOG.md
300 lines
8.9 KiB
JavaScript
300 lines
8.9 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
'use strict';
|
|
import os from 'os';
|
|
|
|
const envSize = parseInt(process.env.UV_THREADPOOL_SIZE, 10);
|
|
process.env.UV_THREADPOOL_SIZE = Math.ceil(
|
|
Math.max(4, isNaN(envSize) ? os.cpus().length * 1.5 : envSize),
|
|
);
|
|
|
|
import fs from 'node:fs';
|
|
import fsp from 'node:fs/promises';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import axios from 'axios';
|
|
import { server } from './server.js';
|
|
import { isValidHttpUrl } from './utils.js';
|
|
import { openPMtiles, getPMtilesInfo } from './pmtiles_adapter.js';
|
|
import { program } from 'commander';
|
|
import { existsP } from './promises.js';
|
|
import { openMbTilesWrapper } from './mbtiles_wrapper.js';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
const packageJson = JSON.parse(
|
|
fs.readFileSync(__dirname + '/../package.json', 'utf8'),
|
|
);
|
|
|
|
const args = process.argv;
|
|
if (args.length >= 3 && args[2][0] !== '-') {
|
|
args.splice(2, 0, '--mbtiles');
|
|
}
|
|
|
|
program
|
|
.description('tileserver-gl startup options')
|
|
.usage('tileserver-gl [mbtiles] [options]')
|
|
.option(
|
|
'--file <file>',
|
|
'MBTiles or PMTiles file\n' +
|
|
'\t ignored if the configuration file is also specified',
|
|
)
|
|
.option(
|
|
'--mbtiles <file>',
|
|
'(DEPRECIATED) MBTiles file\n' +
|
|
'\t ignored if file is also specified' +
|
|
'\t ignored if the configuration file is also specified',
|
|
)
|
|
.option(
|
|
'-c, --config <file>',
|
|
'Configuration file [config.json]',
|
|
'config.json',
|
|
)
|
|
.option('-b, --bind <address>', 'Bind address')
|
|
.option('-p, --port <port>', 'Port [8080]', 8080, parseInt)
|
|
.option('-C|--no-cors', 'Disable Cross-origin resource sharing headers')
|
|
.option(
|
|
'-u|--public_url <url>',
|
|
'Enable exposing the server on subpaths, not necessarily the root of the domain',
|
|
)
|
|
.option('-V, --verbose', 'More verbose output')
|
|
.option('-s, --silent', 'Less verbose output')
|
|
.option('-l|--log_file <file>', 'output log file (defaults to standard out)')
|
|
.option(
|
|
'-f|--log_format <format>',
|
|
'define the log format: https://github.com/expressjs/morgan#morganformat-options',
|
|
)
|
|
.version(packageJson.version, '-v, --version');
|
|
program.parse(process.argv);
|
|
const opts = program.opts();
|
|
|
|
console.log(`Starting ${packageJson.name} v${packageJson.version}`);
|
|
|
|
const startServer = (configPath, config) => {
|
|
let publicUrl = opts.public_url;
|
|
if (publicUrl && publicUrl.lastIndexOf('/') !== publicUrl.length - 1) {
|
|
publicUrl += '/';
|
|
}
|
|
return server({
|
|
configPath,
|
|
config,
|
|
bind: opts.bind,
|
|
port: opts.port,
|
|
cors: opts.cors,
|
|
verbose: opts.verbose,
|
|
silent: opts.silent,
|
|
logFile: opts.log_file,
|
|
logFormat: opts.log_format,
|
|
publicUrl,
|
|
});
|
|
};
|
|
|
|
const startWithInputFile = async (inputFile) => {
|
|
console.log(`[INFO] Automatically creating config file for ${inputFile}`);
|
|
console.log(`[INFO] Only a basic preview style will be used.`);
|
|
console.log(
|
|
`[INFO] See documentation to learn how to create config.json file.`,
|
|
);
|
|
|
|
let inputFilePath;
|
|
if (isValidHttpUrl(inputFile)) {
|
|
inputFilePath = process.cwd();
|
|
} else {
|
|
inputFile = path.resolve(process.cwd(), inputFile);
|
|
inputFilePath = path.dirname(inputFile);
|
|
|
|
const inputFileStats = await fsp.stat(inputFile);
|
|
if (!inputFileStats.isFile() || inputFileStats.size === 0) {
|
|
console.log(`ERROR: Not a valid input file: `);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
const styleDir = path.resolve(
|
|
__dirname,
|
|
'../node_modules/tileserver-gl-styles/',
|
|
);
|
|
|
|
const config = {
|
|
options: {
|
|
paths: {
|
|
root: styleDir,
|
|
fonts: 'fonts',
|
|
styles: 'styles',
|
|
mbtiles: inputFilePath,
|
|
pmtiles: inputFilePath,
|
|
},
|
|
},
|
|
styles: {},
|
|
data: {},
|
|
};
|
|
|
|
const extension = inputFile.split('.').pop().toLowerCase();
|
|
if (extension === 'pmtiles') {
|
|
const fileOpenInfo = openPMtiles(inputFile);
|
|
const metadata = await getPMtilesInfo(fileOpenInfo);
|
|
|
|
if (
|
|
metadata.format === 'pbf' &&
|
|
metadata.name.toLowerCase().indexOf('openmaptiles') > -1
|
|
) {
|
|
if (isValidHttpUrl(inputFile)) {
|
|
config['data'][`v3`] = {
|
|
pmtiles: inputFile,
|
|
};
|
|
} else {
|
|
config['data'][`v3`] = {
|
|
pmtiles: path.basename(inputFile),
|
|
};
|
|
}
|
|
|
|
const styles = await fsp.readdir(path.resolve(styleDir, 'styles'));
|
|
for (const styleName of styles) {
|
|
const styleFileRel = styleName + '/style.json';
|
|
const styleFile = path.resolve(styleDir, 'styles', styleFileRel);
|
|
if (await existsP(styleFile)) {
|
|
config['styles'][styleName] = {
|
|
style: styleFileRel,
|
|
tilejson: {
|
|
bounds: metadata.bounds,
|
|
},
|
|
};
|
|
}
|
|
}
|
|
} else {
|
|
console.log(
|
|
`WARN: PMTiles not in "openmaptiles" format. Serving raw data only...`,
|
|
);
|
|
if (isValidHttpUrl(inputFile)) {
|
|
config['data'][(metadata.id || 'pmtiles').replace(/[?/:]/g, '_')] = {
|
|
pmtiles: inputFile,
|
|
};
|
|
} else {
|
|
config['data'][(metadata.id || 'pmtiles').replace(/[?/:]/g, '_')] = {
|
|
pmtiles: path.basename(inputFile),
|
|
};
|
|
}
|
|
}
|
|
|
|
if (opts.verbose) {
|
|
console.log(JSON.stringify(config, undefined, 2));
|
|
} else {
|
|
console.log('Run with --verbose to see the config file here.');
|
|
}
|
|
|
|
return startServer(null, config);
|
|
} else {
|
|
if (isValidHttpUrl(inputFile)) {
|
|
console.log(
|
|
`ERROR: MBTiles does not support web based files. "${inputFile}" is not a valid data file.`,
|
|
);
|
|
process.exit(1);
|
|
}
|
|
let info;
|
|
try {
|
|
const mbw = await openMbTilesWrapper(inputFile);
|
|
info = await mbw.getInfo();
|
|
if (!info) throw new Error('Metadata missing in the MBTiles.');
|
|
} catch (err) {
|
|
console.log('ERROR: Unable to open MBTiles or read metadata:', err);
|
|
console.log(`Make sure ${path.basename(inputFile)} is valid MBTiles.`);
|
|
process.exit(1);
|
|
}
|
|
const bounds = info.bounds;
|
|
|
|
if (
|
|
info.format === 'pbf' &&
|
|
info.name.toLowerCase().indexOf('openmaptiles') > -1
|
|
) {
|
|
config['data'][`v3`] = {
|
|
mbtiles: path.basename(inputFile),
|
|
};
|
|
|
|
const styles = await fsp.readdir(path.resolve(styleDir, 'styles'));
|
|
for (const styleName of styles) {
|
|
const styleFileRel = styleName + '/style.json';
|
|
const styleFile = path.resolve(styleDir, 'styles', styleFileRel);
|
|
if (await existsP(styleFile)) {
|
|
config['styles'][styleName] = {
|
|
style: styleFileRel,
|
|
tilejson: {
|
|
bounds,
|
|
},
|
|
};
|
|
}
|
|
}
|
|
} else {
|
|
console.log(
|
|
`WARN: MBTiles not in "openmaptiles" format. Serving raw data only...`,
|
|
);
|
|
config['data'][(info.id || 'mbtiles').replace(/[?/:]/g, '_')] = {
|
|
mbtiles: path.basename(inputFile),
|
|
};
|
|
}
|
|
|
|
if (opts.verbose) {
|
|
console.log(JSON.stringify(config, undefined, 2));
|
|
} else {
|
|
console.log('Run with --verbose to see the config file here.');
|
|
}
|
|
|
|
return startServer(null, config);
|
|
}
|
|
};
|
|
|
|
fs.stat(path.resolve(opts.config), async (err, stats) => {
|
|
if (err || !stats.isFile() || stats.size === 0) {
|
|
let inputFile;
|
|
if (opts.file) {
|
|
inputFile = opts.file;
|
|
} else if (opts.mbtiles) {
|
|
inputFile = opts.mbtiles;
|
|
}
|
|
|
|
if (inputFile) {
|
|
return startWithInputFile(inputFile);
|
|
} else {
|
|
// try to find in the cwd
|
|
const files = await fsp.readdir(process.cwd());
|
|
for (const filename of files) {
|
|
if (filename.endsWith('.mbtiles') || filename.endsWith('.pmtiles')) {
|
|
const inputFilesStats = await fsp.stat(filename);
|
|
if (inputFilesStats.isFile() && inputFilesStats.size > 0) {
|
|
inputFile = filename;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (inputFile) {
|
|
console.log(`No input file specified, using ${inputFile}`);
|
|
return startWithInputFile(inputFile);
|
|
} else {
|
|
const url =
|
|
'https://github.com/maptiler/tileserver-gl/releases/download/v1.3.0/zurich_switzerland.mbtiles';
|
|
const filename = 'zurich_switzerland.mbtiles';
|
|
const writer = fs.createWriteStream(filename);
|
|
console.log(`No input file found`);
|
|
console.log(`[DEMO] Downloading sample data (${filename}) from ${url}`);
|
|
|
|
try {
|
|
const response = await axios({
|
|
url,
|
|
method: 'GET',
|
|
responseType: 'stream',
|
|
});
|
|
|
|
response.data.pipe(writer);
|
|
writer.on('finish', () => startWithInputFile(filename));
|
|
writer.on('error', (err) =>
|
|
console.error(`Error writing file: ${err}`),
|
|
);
|
|
} catch (error) {
|
|
console.error(`Error downloading file: ${error}`);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
console.log(`Using specified config file from ${opts.config}`);
|
|
return startServer(opts.config, null);
|
|
}
|
|
});
|