feat: allow web https based pmtiles to be loaded

Signed-off-by: Andrew Calcutt <acalcutt@techidiots.net>
This commit is contained in:
Andrew Calcutt 2023-10-10 16:06:19 -04:00
parent 3f45c38663
commit fde9ed96a5
6 changed files with 80 additions and 39 deletions

View file

@ -8,6 +8,7 @@ import { fileURLToPath } from 'url';
import request from 'request'; import request from 'request';
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 { import {
PMtilesOpen, PMtilesOpen,
PMtilesClose, PMtilesClose,
@ -91,13 +92,19 @@ const startWithinputFile = async (inputFile) => {
`[INFO] See documentation to learn how to create config.json file.`, `[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); inputFile = path.resolve(process.cwd(), inputFile);
inputFilePath = path.dirname(inputFile);
const inputFileStats = fs.statSync(inputFile); const inputFileStats = fs.statSync(inputFile);
if (!inputFileStats.isFile() || inputFileStats.size === 0) { if (!inputFileStats.isFile() || inputFileStats.size === 0) {
console.log(`ERROR: Not a valid input file: ${inputFile}`); console.log(`ERROR: Not a valid input file: `);
process.exit(1); process.exit(1);
} }
}
const styleDir = path.resolve( const styleDir = path.resolve(
__dirname, __dirname,
@ -110,8 +117,8 @@ const startWithinputFile = async (inputFile) => {
root: styleDir, root: styleDir,
fonts: 'fonts', fonts: 'fonts',
styles: 'styles', styles: 'styles',
mbtiles: path.dirname(inputFile), mbtiles: inputFilePath,
pmtiles: path.dirname(inputFile), pmtiles: inputFilePath,
}, },
}, },
styles: {}, styles: {},
@ -132,9 +139,15 @@ const startWithinputFile = async (inputFile) => {
metadata.format === 'pbf' && metadata.format === 'pbf' &&
metadata.name.toLowerCase().indexOf('openmaptiles') > -1 metadata.name.toLowerCase().indexOf('openmaptiles') > -1
) { ) {
if (isValidHttpUrl(inputFile)) {
config['data'][`v3`] = {
pmtiles: inputFile,
};
} else {
config['data'][`v3`] = { config['data'][`v3`] = {
pmtiles: path.basename(inputFile), pmtiles: path.basename(inputFile),
}; };
}
const styles = fs.readdirSync(path.resolve(styleDir, 'styles')); const styles = fs.readdirSync(path.resolve(styleDir, 'styles'));
for (const styleName of styles) { for (const styleName of styles) {
@ -153,10 +166,16 @@ const startWithinputFile = async (inputFile) => {
console.log( console.log(
`WARN: PMTiles not in "openmaptiles" format. Serving raw data only...`, `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, '_')] = { config['data'][(metadata.id || 'pmtiles').replace(/[?/:]/g, '_')] = {
pmtiles: path.basename(inputFile), pmtiles: path.basename(inputFile),
}; };
} }
}
if (opts.verbose) { if (opts.verbose) {
console.log(JSON.stringify(config, undefined, 2)); console.log(JSON.stringify(config, undefined, 2));
@ -166,6 +185,10 @@ const startWithinputFile = async (inputFile) => {
return startServer(null, config); return startServer(null, config);
} else { } else {
if (isValidHttpUrl(inputFile)) {
console.log(`ERROR: MBTiles does not support web based files: `);
process.exit(1);
}
const instance = new MBTiles(inputFile + '?mode=ro', (err) => { const instance = new MBTiles(inputFile + '?mode=ro', (err) => {
if (err) { if (err) {
console.log('ERROR: Unable to open MBTiles.'); console.log('ERROR: Unable to open MBTiles.');

View file

@ -1,5 +1,6 @@
import fs from 'node:fs'; import fs from 'node:fs';
import PMTiles from 'pmtiles'; import PMTiles from 'pmtiles';
import { isValidHttpUrl } from './utils.js';
const PMTilesFileSource = class { const PMTilesFileSource = class {
constructor(fd) { constructor(fd) {
@ -122,19 +123,3 @@ const ArrayBufferToBuffer = (array_buffer) => {
} }
return buffer; return buffer;
}; };
/**
*
* @param string
*/
function isValidHttpUrl(string) {
let url;
try {
url = new URL(string);
} catch (_) {
return false;
}
return url.protocol === 'http:' || url.protocol === 'https:';
}

View file

@ -10,7 +10,7 @@ import MBTiles from '@mapbox/mbtiles';
import Pbf from 'pbf'; import Pbf from 'pbf';
import { VectorTile } from '@mapbox/vector-tile'; import { VectorTile } from '@mapbox/vector-tile';
import { getTileUrls, fixTileJSONCenter } from './utils.js'; import { getTileUrls, isValidHttpUrl, fixTileJSONCenter } from './utils.js';
import { import {
PMtilesOpen, PMtilesOpen,
GetPMtilesInfo, GetPMtilesInfo,
@ -194,21 +194,33 @@ export const serve_data = {
let inputFile; let inputFile;
let inputType; let inputType;
if (params.pmtiles) { if (params.pmtiles) {
inputFile = path.resolve(options.paths.pmtiles, params.pmtiles);
inputType = 'pmtiles'; inputType = 'pmtiles';
if (isValidHttpUrl(params.pmtiles)) {
inputFile = params.pmtiles;
} else {
inputFile = path.resolve(options.paths.pmtiles, params.pmtiles);
}
} else if (params.mbtiles) { } else if (params.mbtiles) {
inputFile = path.resolve(options.paths.mbtiles, params.mbtiles);
inputType = 'mbtiles'; inputType = 'mbtiles';
if (isValidHttpUrl(params.pmtiles)) {
throw Error(
`ERROR: MBTiles does not support web based files: ${inputFile}`,
);
} else {
inputFile = path.resolve(options.paths.mbtiles, params.mbtiles);
}
} }
let tileJSON = { let tileJSON = {
tiles: params.domains || options.domains, tiles: params.domains || options.domains,
}; };
if (!isValidHttpUrl(inputFile)) {
const inputFileStats = fs.statSync(inputFile); const inputFileStats = fs.statSync(inputFile);
if (!inputFileStats.isFile() || inputFileStats.size === 0) { if (!inputFileStats.isFile() || inputFileStats.size === 0) {
throw Error(`Not valid input file: ${inputFile}`); throw Error(`Not valid input file: ${inputFile}`);
} }
}
let source; let source;
let source_type; let source_type;

View file

@ -18,7 +18,12 @@ import MBTiles from '@mapbox/mbtiles';
import polyline from '@mapbox/polyline'; import polyline from '@mapbox/polyline';
import proj4 from 'proj4'; import proj4 from 'proj4';
import request from 'request'; import request from 'request';
import { getFontsPbf, getTileUrls, fixTileJSONCenter } from './utils.js'; import {
getFontsPbf,
getTileUrls,
isValidHttpUrl,
fixTileJSONCenter,
} from './utils.js';
import { import {
PMtilesOpen, PMtilesOpen,
GetPMtilesInfo, GetPMtilesInfo,
@ -1489,9 +1494,11 @@ export const serve_rendered = {
} }
} }
if (!isValidHttpUrl(inputFile)) {
const inputFileStats = fs.statSync(inputFile); const inputFileStats = fs.statSync(inputFile);
if (!inputFileStats.isFile() || inputFileStats.size === 0) { if (!inputFileStats.isFile() || inputFileStats.size === 0) {
throw Error(`Not valid MBTiles file: ${inputFile}`); throw Error(`Not valid PMTiles file: ${inputFile}`);
}
} }
if (source_type === 'pmtiles') { if (source_type === 'pmtiles') {

View file

@ -93,6 +93,7 @@ function start(opts) {
paths.fonts = path.resolve(paths.root, paths.fonts || ''); paths.fonts = path.resolve(paths.root, paths.fonts || '');
paths.sprites = path.resolve(paths.root, paths.sprites || ''); paths.sprites = path.resolve(paths.root, paths.sprites || '');
paths.mbtiles = path.resolve(paths.root, paths.mbtiles || ''); paths.mbtiles = path.resolve(paths.root, paths.mbtiles || '');
paths.pmtiles = path.resolve(paths.root, paths.pmtiles || '');
paths.icons = path.resolve(paths.root, paths.icons || ''); paths.icons = path.resolve(paths.root, paths.icons || '');
const startupPromises = []; const startupPromises = [];
@ -109,6 +110,7 @@ function start(opts) {
checkPath('fonts'); checkPath('fonts');
checkPath('sprites'); checkPath('sprites');
checkPath('mbtiles'); checkPath('mbtiles');
checkPath('pmtiles');
checkPath('icons'); checkPath('icons');
/** /**

View file

@ -162,3 +162,15 @@ export const getFontsPbf = (
return Promise.all(queue).then((values) => glyphCompose.combine(values)); return Promise.all(queue).then((values) => glyphCompose.combine(values));
}; };
export const isValidHttpUrl = (string) => {
let url;
try {
url = new URL(string);
} catch (_) {
return false;
}
return url.protocol === 'http:' || url.protocol === 'https:';
};