refactor: add promisified wrapper for MBTiles
Signed-off-by: Aarni Koskela <akx@iki.fi>
This commit is contained in:
parent
00d9189ae5
commit
cc687ea59b
4 changed files with 153 additions and 146 deletions
27
src/main.js
27
src/main.js
|
@ -8,9 +8,11 @@ import path from 'path';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { server } from './server.js';
|
import { server } from './server.js';
|
||||||
import MBTiles from '@mapbox/mbtiles';
|
|
||||||
import { isValidHttpUrl } from './utils.js';
|
import { isValidHttpUrl } from './utils.js';
|
||||||
import { openPMtiles, getPMtilesInfo } from './pmtiles_adapter.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 __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
|
@ -23,8 +25,6 @@ if (args.length >= 3 && args[2][0] !== '-') {
|
||||||
args.splice(2, 0, '--mbtiles');
|
args.splice(2, 0, '--mbtiles');
|
||||||
}
|
}
|
||||||
|
|
||||||
import { program } from 'commander';
|
|
||||||
import { existsP } from './promises.js';
|
|
||||||
program
|
program
|
||||||
.description('tileserver-gl startup options')
|
.description('tileserver-gl startup options')
|
||||||
.usage('tileserver-gl [mbtiles] [options]')
|
.usage('tileserver-gl [mbtiles] [options]')
|
||||||
|
@ -184,21 +184,16 @@ const startWithInputFile = async (inputFile) => {
|
||||||
);
|
);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
const instance = new MBTiles(inputFile + '?mode=ro', (err) => {
|
let info;
|
||||||
if (err) {
|
try {
|
||||||
console.log('ERROR: Unable to open MBTiles.');
|
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.`);
|
console.log(`Make sure ${path.basename(inputFile)} is valid MBTiles.`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.getInfo(async (err, info) => {
|
|
||||||
if (err || !info) {
|
|
||||||
console.log('ERROR: Metadata missing in the MBTiles.');
|
|
||||||
console.log(
|
|
||||||
`Make sure ${path.basename(inputFile)} is valid MBTiles.`,
|
|
||||||
);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
const bounds = info.bounds;
|
const bounds = info.bounds;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -238,8 +233,6 @@ const startWithInputFile = async (inputFile) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return startServer(null, config);
|
return startServer(null, config);
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
46
src/mbtiles_wrapper.js
Normal file
46
src/mbtiles_wrapper.js
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import MBTiles from '@mapbox/mbtiles';
|
||||||
|
import util from 'node:util';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Promise-ful wrapper around the MBTiles class.
|
||||||
|
*/
|
||||||
|
class MBTilesWrapper {
|
||||||
|
constructor(mbtiles) {
|
||||||
|
this._mbtiles = mbtiles;
|
||||||
|
this._getInfoP = util.promisify(mbtiles.getInfo.bind(mbtiles));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the underlying MBTiles object.
|
||||||
|
* @returns {MBTiles}
|
||||||
|
*/
|
||||||
|
getMbTiles() {
|
||||||
|
return this._mbtiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the MBTiles metadata object.
|
||||||
|
* @returns {Promise<object>}
|
||||||
|
*/
|
||||||
|
getInfo() {
|
||||||
|
return this._getInfoP();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the given MBTiles file and return a promise that resolves with a
|
||||||
|
* MBTilesWrapper instance.
|
||||||
|
* @param inputFile Input file
|
||||||
|
* @returns {Promise<MBTilesWrapper>}
|
||||||
|
*/
|
||||||
|
export function openMbTilesWrapper(inputFile) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const mbtiles = new MBTiles(inputFile + '?mode=ro', (err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve(new MBTilesWrapper(mbtiles));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -5,7 +5,6 @@ import path from 'path';
|
||||||
|
|
||||||
import clone from 'clone';
|
import clone from 'clone';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
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';
|
||||||
|
|
||||||
|
@ -16,6 +15,7 @@ import {
|
||||||
openPMtiles,
|
openPMtiles,
|
||||||
} from './pmtiles_adapter.js';
|
} from './pmtiles_adapter.js';
|
||||||
import { gunzipP, gzipP } from './promises.js';
|
import { gunzipP, gzipP } from './promises.js';
|
||||||
|
import { openMbTilesWrapper } from './mbtiles_wrapper.js';
|
||||||
|
|
||||||
export const serve_data = {
|
export const serve_data = {
|
||||||
init: (options, repo) => {
|
init: (options, repo) => {
|
||||||
|
@ -242,17 +242,9 @@ export const serve_data = {
|
||||||
}
|
}
|
||||||
} else if (inputType === 'mbtiles') {
|
} else if (inputType === 'mbtiles') {
|
||||||
sourceType = 'mbtiles';
|
sourceType = 'mbtiles';
|
||||||
const sourceInfoPromise = new Promise((resolve, reject) => {
|
const mbw = await openMbTilesWrapper(inputFile);
|
||||||
source = new MBTiles(inputFile + '?mode=ro', (err) => {
|
const info = await mbw.getInfo();
|
||||||
if (err) {
|
source = mbw.getMbTiles();
|
||||||
reject(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
source.getInfo((err, info) => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
tileJSON['name'] = id;
|
tileJSON['name'] = id;
|
||||||
tileJSON['format'] = 'pbf';
|
tileJSON['format'] = 'pbf';
|
||||||
|
|
||||||
|
@ -269,12 +261,6 @@ export const serve_data = {
|
||||||
if (options.dataDecoratorFunc) {
|
if (options.dataDecoratorFunc) {
|
||||||
tileJSON = options.dataDecoratorFunc(id, 'tilejson', tileJSON);
|
tileJSON = options.dataDecoratorFunc(id, 'tilejson', tileJSON);
|
||||||
}
|
}
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
await sourceInfoPromise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
repo[id] = {
|
repo[id] = {
|
||||||
|
|
|
@ -24,7 +24,6 @@ import express from 'express';
|
||||||
import sanitize from 'sanitize-filename';
|
import sanitize from 'sanitize-filename';
|
||||||
import SphericalMercator from '@mapbox/sphericalmercator';
|
import SphericalMercator from '@mapbox/sphericalmercator';
|
||||||
import mlgl from '@maplibre/maplibre-gl-native';
|
import mlgl from '@maplibre/maplibre-gl-native';
|
||||||
import MBTiles from '@mapbox/mbtiles';
|
|
||||||
import polyline from '@mapbox/polyline';
|
import polyline from '@mapbox/polyline';
|
||||||
import proj4 from 'proj4';
|
import proj4 from 'proj4';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
@ -43,6 +42,7 @@ import {
|
||||||
import { renderOverlay, renderWatermark, renderAttribution } from './render.js';
|
import { renderOverlay, renderWatermark, renderAttribution } from './render.js';
|
||||||
import fsp from 'node:fs/promises';
|
import fsp from 'node:fs/promises';
|
||||||
import { gunzipP } from './promises.js';
|
import { gunzipP } from './promises.js';
|
||||||
|
import { openMbTilesWrapper } from './mbtiles_wrapper.js';
|
||||||
|
|
||||||
const FLOAT_PATTERN = '[+-]?(?:\\d+|\\d+.?\\d+)';
|
const FLOAT_PATTERN = '[+-]?(?:\\d+|\\d+.?\\d+)';
|
||||||
const PATH_PATTERN =
|
const PATH_PATTERN =
|
||||||
|
@ -1131,7 +1131,6 @@ export const serve_rendered = {
|
||||||
};
|
};
|
||||||
repo[id] = repoobj;
|
repo[id] = repoobj;
|
||||||
|
|
||||||
const queue = [];
|
|
||||||
for (const name of Object.keys(styleJSON.sources)) {
|
for (const name of Object.keys(styleJSON.sources)) {
|
||||||
let sourceType;
|
let sourceType;
|
||||||
let source = styleJSON.sources[name];
|
let source = styleJSON.sources[name];
|
||||||
|
@ -1205,19 +1204,13 @@ export const serve_rendered = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
queue.push(
|
|
||||||
new Promise(async (resolve, reject) => {
|
|
||||||
inputFile = path.resolve(options.paths.mbtiles, inputFile);
|
|
||||||
const inputFileStats = await fsp.stat(inputFile);
|
const inputFileStats = await fsp.stat(inputFile);
|
||||||
if (!inputFileStats.isFile() || inputFileStats.size === 0) {
|
if (!inputFileStats.isFile() || inputFileStats.size === 0) {
|
||||||
throw Error(`Not valid MBTiles file: "${inputFile}"`);
|
throw Error(`Not valid MBTiles file: "${inputFile}"`);
|
||||||
}
|
}
|
||||||
map.sources[name] = new MBTiles(inputFile + '?mode=ro', (err) => {
|
const mbw = await openMbTilesWrapper(inputFile);
|
||||||
map.sources[name].getInfo((err, info) => {
|
const info = await mbw.getInfo();
|
||||||
if (err) {
|
map.sources[name] = mbw.getMbTiles();
|
||||||
console.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
map.sourceTypes[name] = 'mbtiles';
|
map.sourceTypes[name] = 'mbtiles';
|
||||||
|
|
||||||
if (!repoobj.dataProjWGStoInternalWGS && info.proj4) {
|
if (!repoobj.dataProjWGStoInternalWGS && info.proj4) {
|
||||||
|
@ -1238,11 +1231,7 @@ export const serve_rendered = {
|
||||||
delete source.scheme;
|
delete source.scheme;
|
||||||
|
|
||||||
if (options.dataDecoratorFunc) {
|
if (options.dataDecoratorFunc) {
|
||||||
source = options.dataDecoratorFunc(
|
source = options.dataDecoratorFunc(name, 'tilejson', source);
|
||||||
name,
|
|
||||||
'tilejson',
|
|
||||||
source,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -1257,17 +1246,10 @@ export const serve_rendered = {
|
||||||
tileJSON.attribution += source.attribution;
|
tileJSON.attribution += source.attribution;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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];
|
||||||
|
|
Loading…
Reference in a new issue