feat: allow 'pmtiles:' in configs. open file less
Signed-off-by: Andrew Calcutt <acalcutt@techidiots.net>
This commit is contained in:
parent
98995ab1be
commit
0b99d8ddd1
6 changed files with 223 additions and 154 deletions
67
src/main.js
67
src/main.js
|
|
@ -8,7 +8,11 @@ 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 { GetPMtilesInfo } from './pmtiles_adapter.js';
|
import {
|
||||||
|
PMtilesOpen,
|
||||||
|
PMtilesClose,
|
||||||
|
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);
|
||||||
|
|
@ -80,18 +84,18 @@ const startServer = (configPath, config) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
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(
|
||||||
`[INFO] See documentation to learn how to create config.json file.`,
|
`[INFO] See documentation to learn how to create config.json file.`,
|
||||||
);
|
);
|
||||||
|
|
||||||
inputfile = path.resolve(process.cwd(), inputfile);
|
inputFile = path.resolve(process.cwd(), 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: ${inputFile}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,24 +110,27 @@ const startWithInputFile = async (inputfile) => {
|
||||||
root: styleDir,
|
root: styleDir,
|
||||||
fonts: 'fonts',
|
fonts: 'fonts',
|
||||||
styles: 'styles',
|
styles: 'styles',
|
||||||
mbtiles: path.dirname(inputfile),
|
mbtiles: path.dirname(inputFile),
|
||||||
|
pmtiles: path.dirname(inputFile),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
styles: {},
|
styles: {},
|
||||||
data: {},
|
data: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
const extension = inputfile.split('.').pop().toLowerCase();
|
const extension = inputFile.split('.').pop().toLowerCase();
|
||||||
if (extension === 'pmtiles') {
|
if (extension === 'pmtiles') {
|
||||||
const info = await GetPMtilesInfo(inputfile);
|
const FileDescriptor = PMtilesOpen(inputFile);
|
||||||
|
const info = await GetPMtilesInfo(FileDescriptor);
|
||||||
const metadata = info.metadata;
|
const metadata = info.metadata;
|
||||||
|
PMtilesClose(FileDescriptor);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
metadata.format === 'pbf' &&
|
metadata.format === 'pbf' &&
|
||||||
metadata.name.toLowerCase().indexOf('openmaptiles') > -1
|
metadata.name.toLowerCase().indexOf('openmaptiles') > -1
|
||||||
) {
|
) {
|
||||||
config['data'][`v3`] = {
|
config['data'][`v3`] = {
|
||||||
mbtiles: path.basename(inputfile),
|
pmtiles: path.basename(inputFile),
|
||||||
};
|
};
|
||||||
|
|
||||||
const styles = fs.readdirSync(path.resolve(styleDir, 'styles'));
|
const styles = fs.readdirSync(path.resolve(styleDir, 'styles'));
|
||||||
|
|
@ -143,8 +150,8 @@ 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...`,
|
||||||
);
|
);
|
||||||
config['data'][(metadata.id || 'mbtiles').replace(/[?/:]/g, '_')] = {
|
config['data'][(metadata.id || 'pmtiles').replace(/[?/:]/g, '_')] = {
|
||||||
mbtiles: path.basename(inputfile),
|
pmtiles: path.basename(inputFile),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -156,10 +163,10 @@ const startWithInputFile = async (inputfile) => {
|
||||||
|
|
||||||
return startServer(null, config);
|
return startServer(null, config);
|
||||||
} else {
|
} else {
|
||||||
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.');
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -167,7 +174,7 @@ const startWithInputFile = async (inputfile) => {
|
||||||
if (err || !info) {
|
if (err || !info) {
|
||||||
console.log('ERROR: Metadata missing in the MBTiles.');
|
console.log('ERROR: Metadata missing in the MBTiles.');
|
||||||
console.log(
|
console.log(
|
||||||
`Make sure ${path.basename(inputfile)} is valid MBTiles.`,
|
`Make sure ${path.basename(inputFile)} is valid MBTiles.`,
|
||||||
);
|
);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
@ -178,7 +185,7 @@ const startWithInputFile = async (inputfile) => {
|
||||||
info.name.toLowerCase().indexOf('openmaptiles') > -1
|
info.name.toLowerCase().indexOf('openmaptiles') > -1
|
||||||
) {
|
) {
|
||||||
config['data'][`v3`] = {
|
config['data'][`v3`] = {
|
||||||
mbtiles: path.basename(inputfile),
|
mbtiles: path.basename(inputFile),
|
||||||
};
|
};
|
||||||
|
|
||||||
const styles = fs.readdirSync(path.resolve(styleDir, 'styles'));
|
const styles = fs.readdirSync(path.resolve(styleDir, 'styles'));
|
||||||
|
|
@ -199,7 +206,7 @@ const startWithInputFile = async (inputfile) => {
|
||||||
`WARN: MBTiles not in "openmaptiles" format. Serving raw data only...`,
|
`WARN: MBTiles not in "openmaptiles" format. Serving raw data only...`,
|
||||||
);
|
);
|
||||||
config['data'][(info.id || 'mbtiles').replace(/[?/:]/g, '_')] = {
|
config['data'][(info.id || 'mbtiles').replace(/[?/:]/g, '_')] = {
|
||||||
mbtiles: path.basename(inputfile),
|
mbtiles: path.basename(inputFile),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,30 +224,30 @@ const startWithInputFile = async (inputfile) => {
|
||||||
|
|
||||||
fs.stat(path.resolve(opts.config), (err, stats) => {
|
fs.stat(path.resolve(opts.config), (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) {
|
||||||
inputfile = opts.file;
|
inputFile = opts.file;
|
||||||
} else if (opts.mbtiles) {
|
} else if (opts.mbtiles) {
|
||||||
inputfile = opts.mbtiles;
|
inputFile = opts.mbtiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
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());
|
||||||
for (const filename of files) {
|
for (const filename of files) {
|
||||||
if (filename.endsWith('.mbtiles') || filename.endsWith('.pmtiles')) {
|
if (filename.endsWith('.mbtiles') || filename.endsWith('.pmtiles')) {
|
||||||
const InputFilesStats = fs.statSync(filename);
|
const inputFilesStats = fs.statSync(filename);
|
||||||
if (InputFilesStats.isFile() && InputFilesStats.size > 0) {
|
if (inputFilesStats.isFile() && inputFilesStats.size > 0) {
|
||||||
inputfile = filename;
|
inputFile = filename;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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';
|
||||||
|
|
@ -248,7 +255,7 @@ fs.stat(path.resolve(opts.config), (err, stats) => {
|
||||||
const stream = fs.createWriteStream(filename);
|
const stream = 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}`);
|
||||||
stream.on('finish', () => startWithInputFile(filename));
|
stream.on('finish', () => startWithinputFile(filename));
|
||||||
return request.get(url).pipe(stream);
|
return request.get(url).pipe(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,25 @@
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import PMTiles from 'pmtiles';
|
import PMTiles from 'pmtiles';
|
||||||
|
|
||||||
const PMTilesLocalSource = class {
|
export const PMtilesOpen = (FilePath) => {
|
||||||
constructor(file) {
|
const fd = fs.openSync(FilePath, 'r');
|
||||||
this.file = file;
|
return fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PMtilesClose = (fd) => {
|
||||||
|
fs.closeSync(fd);
|
||||||
|
};
|
||||||
|
|
||||||
|
const PMTilesFileDescriptorSource = class {
|
||||||
|
constructor(fd) {
|
||||||
|
this.fd = fd;
|
||||||
}
|
}
|
||||||
getKey() {
|
getKey() {
|
||||||
return this.file.name;
|
return this.fd;
|
||||||
}
|
}
|
||||||
async getBytes(offset, length) {
|
async getBytes(offset, length) {
|
||||||
const buffer = Buffer.alloc(length);
|
const buffer = Buffer.alloc(length);
|
||||||
const fd = fs.openSync(this.file, 'r'); //Open the file in read mode
|
await ReadBytes(this.fd, buffer, offset);
|
||||||
await ReadBytes(fd, buffer, offset); //Read the specifed bytes from the file
|
|
||||||
fs.closeSync(fd); //close the file
|
|
||||||
return { data: BufferToArrayBuffer(buffer) };
|
return { data: BufferToArrayBuffer(buffer) };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -28,8 +35,8 @@ const ReadBytes = async (fd, buffer, offset) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GetPMtilesInfo = async (pmtilesFile) => {
|
export const GetPMtilesInfo = async (fd) => {
|
||||||
const source = new PMTilesLocalSource(pmtilesFile);
|
const source = new PMTilesFileDescriptorSource(fd);
|
||||||
const pmtiles = new PMTiles.PMTiles(source);
|
const pmtiles = new PMTiles.PMTiles(source);
|
||||||
const header = await pmtiles.getHeader();
|
const header = await pmtiles.getHeader();
|
||||||
const metadata = await pmtiles.getMetadata();
|
const metadata = await pmtiles.getMetadata();
|
||||||
|
|
@ -47,8 +54,8 @@ export const GetPMtilesInfo = async (pmtilesFile) => {
|
||||||
return { header: header, metadata: metadata };
|
return { header: header, metadata: metadata };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GetPMtilesTile = async (pmtilesFile, z, x, y) => {
|
export const GetPMtilesTile = async (fd, z, x, y) => {
|
||||||
const source = new PMTilesLocalSource(pmtilesFile);
|
const source = new PMTilesFileDescriptorSource(fd);
|
||||||
const pmtiles = new PMTiles.PMTiles(source);
|
const pmtiles = new PMTiles.PMTiles(source);
|
||||||
const header = await pmtiles.getHeader();
|
const header = await pmtiles.getHeader();
|
||||||
const TileType = GetPmtilesTileType(header.tileType);
|
const TileType = GetPmtilesTileType(header.tileType);
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,12 @@ import Pbf from 'pbf';
|
||||||
import { VectorTile } from '@mapbox/vector-tile';
|
import { VectorTile } from '@mapbox/vector-tile';
|
||||||
|
|
||||||
import { getTileUrls, fixTileJSONCenter } from './utils.js';
|
import { getTileUrls, fixTileJSONCenter } from './utils.js';
|
||||||
import { GetPMtilesInfo, GetPMtilesTile } from './pmtiles_adapter.js';
|
import {
|
||||||
|
PMtilesOpen,
|
||||||
|
PMtilesClose,
|
||||||
|
GetPMtilesInfo,
|
||||||
|
GetPMtilesTile,
|
||||||
|
} from './pmtiles_adapter.js';
|
||||||
|
|
||||||
export const serve_data = {
|
export const serve_data = {
|
||||||
init: (options, repo) => {
|
init: (options, repo) => {
|
||||||
|
|
@ -187,22 +192,33 @@ export const serve_data = {
|
||||||
return app;
|
return app;
|
||||||
},
|
},
|
||||||
add: async (options, repo, params, id, publicUrl) => {
|
add: async (options, repo, params, id, publicUrl) => {
|
||||||
const mbtilesFile = path.resolve(options.paths.mbtiles, params.mbtiles);
|
let inputFile;
|
||||||
|
let inputType;
|
||||||
|
if (params.pmtiles) {
|
||||||
|
inputFile = path.resolve(options.paths.pmtiles, params.pmtiles);
|
||||||
|
inputType = 'pmtiles';
|
||||||
|
} else if (params.mbtiles) {
|
||||||
|
inputFile = path.resolve(options.paths.mbtiles, params.mbtiles);
|
||||||
|
inputType = 'mbtiles';
|
||||||
|
}
|
||||||
|
|
||||||
let tileJSON = {
|
let tileJSON = {
|
||||||
tiles: params.domains || options.domains,
|
tiles: params.domains || options.domains,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mbtilesFileStats = fs.statSync(mbtilesFile);
|
const inputFileStats = fs.statSync(inputFile);
|
||||||
if (!mbtilesFileStats.isFile() || mbtilesFileStats.size === 0) {
|
if (!inputFileStats.isFile() || inputFileStats.size === 0) {
|
||||||
throw Error(`Not valid MBTiles file: ${mbtilesFile}`);
|
throw Error(`Not valid input file: ${inputFile}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const extension = mbtilesFile.split('.').pop().toLowerCase();
|
|
||||||
let source;
|
let source;
|
||||||
if (extension === 'pmtiles') {
|
let source_type;
|
||||||
const info = await GetPMtilesInfo(mbtilesFile);
|
if (inputType === 'pmtiles') {
|
||||||
|
const FileDescriptor = PMtilesOpen(inputFile);
|
||||||
|
const info = await GetPMtilesInfo(FileDescriptor);
|
||||||
const metadata = info.metadata;
|
const metadata = info.metadata;
|
||||||
source = mbtilesFile;
|
source = FileDescriptor;
|
||||||
|
source_type = 'pmtiles';
|
||||||
|
|
||||||
tileJSON['name'] = id;
|
tileJSON['name'] = id;
|
||||||
tileJSON['format'] = 'pbf';
|
tileJSON['format'] = 'pbf';
|
||||||
|
|
@ -220,9 +236,10 @@ export const serve_data = {
|
||||||
if (options.dataDecoratorFunc) {
|
if (options.dataDecoratorFunc) {
|
||||||
tileJSON = options.dataDecoratorFunc(id, 'tilejson', tileJSON);
|
tileJSON = options.dataDecoratorFunc(id, 'tilejson', tileJSON);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (inputType === 'mbtiles') {
|
||||||
|
source_type = 'mbtiles';
|
||||||
const sourceInfoPromise = new Promise((resolve, reject) => {
|
const sourceInfoPromise = new Promise((resolve, reject) => {
|
||||||
source = new MBTiles(mbtilesFile + '?mode=ro', (err) => {
|
source = new MBTiles(inputFile + '?mode=ro', (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
return;
|
return;
|
||||||
|
|
@ -261,6 +278,7 @@ export const serve_data = {
|
||||||
tileJSON,
|
tileJSON,
|
||||||
publicUrl,
|
publicUrl,
|
||||||
source,
|
source,
|
||||||
|
source_type,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,9 @@ 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 sharp from 'sharp'; // sharp has to be required before node-canvas. see https://github.com/lovell/sharp/issues/371
|
|
||||||
import { createCanvas, Image } from 'canvas';
|
import { createCanvas, Image } from 'canvas';
|
||||||
|
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 { createCanvas, Image } from 'canvas';
|
||||||
import clone from 'clone';
|
import clone from 'clone';
|
||||||
import Color from 'color';
|
import Color from 'color';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
|
|
@ -19,7 +20,12 @@ 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, fixTileJSONCenter } from './utils.js';
|
||||||
import { GetPMtilesInfo, GetPMtilesTile } from './pmtiles_adapter.js';
|
import {
|
||||||
|
PMtilesOpen,
|
||||||
|
PMtilesClose,
|
||||||
|
GetPMtilesInfo,
|
||||||
|
GetPMtilesTile,
|
||||||
|
} from './pmtiles_adapter.js';
|
||||||
|
|
||||||
const FLOAT_PATTERN = '[+-]?(?:\\d+|\\d+.?\\d+)';
|
const FLOAT_PATTERN = '[+-]?(?:\\d+|\\d+.?\\d+)';
|
||||||
const PATH_PATTERN =
|
const PATH_PATTERN =
|
||||||
|
|
@ -1211,6 +1217,7 @@ export const serve_rendered = {
|
||||||
renderers: [],
|
renderers: [],
|
||||||
renderers_static: [],
|
renderers_static: [],
|
||||||
sources: {},
|
sources: {},
|
||||||
|
source_types: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
let styleJSON;
|
let styleJSON;
|
||||||
|
|
@ -1221,7 +1228,7 @@ export const serve_rendered = {
|
||||||
ratio: 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);
|
||||||
if (protocol === 'sprites') {
|
if (protocol === 'sprites') {
|
||||||
const dir = options.paths[protocol];
|
const dir = options.paths[protocol];
|
||||||
const file = unescape(req.url).substring(protocol.length + 3);
|
const file = unescape(req.url).substring(protocol.length + 3);
|
||||||
|
|
@ -1246,10 +1253,11 @@ export const serve_rendered = {
|
||||||
callback(err, { data: null });
|
callback(err, { data: null });
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else if (protocol === 'mbtiles') {
|
} 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 sourceInfo = styleJSON.sources[sourceId];
|
const sourceInfo = styleJSON.sources[sourceId];
|
||||||
|
|
||||||
const z = parts[3] | 0;
|
const z = parts[3] | 0;
|
||||||
|
|
@ -1257,17 +1265,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 (
|
if (source_type === 'pmtiles') {
|
||||||
typeof map.sources[sourceId] === 'string' &&
|
let tileinfo = await GetPMtilesTile(source, z, x, y);
|
||||||
map.sources[sourceId].split('.').pop().toLowerCase() ===
|
|
||||||
'pmtiles'
|
|
||||||
) {
|
|
||||||
let tileinfo = await GetPMtilesTile(
|
|
||||||
map.sources[sourceId],
|
|
||||||
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) {
|
||||||
|
|
@ -1301,7 +1300,7 @@ export const serve_rendered = {
|
||||||
|
|
||||||
callback(null, response);
|
callback(null, response);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (source_type === '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)
|
||||||
|
|
@ -1463,39 +1462,46 @@ 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 = styleJSON.sources[name];
|
const source = styleJSON.sources[name];
|
||||||
const url = source.url;
|
const url = source.url;
|
||||||
|
let source_type;
|
||||||
|
|
||||||
if (url && url.lastIndexOf('mbtiles:', 0) === 0) {
|
if (url && url.lastIndexOf('mbtiles:', 0) === 0) {
|
||||||
// found mbtiles source, replace with info from local file
|
// found mbtiles source, replace with info from local file
|
||||||
delete source.url;
|
delete source.url;
|
||||||
|
|
||||||
let mbtilesFile = url.substring('mbtiles://'.length);
|
let inputFile = url.replace('pmtiles://', '').replace('mbtiles://', '');
|
||||||
const fromData =
|
const fromData =
|
||||||
mbtilesFile[0] === '{' && mbtilesFile[mbtilesFile.length - 1] === '}';
|
inputFile[0] === '{' && inputFile[inputFile.length - 1] === '}';
|
||||||
|
|
||||||
if (fromData) {
|
if (fromData) {
|
||||||
mbtilesFile = mbtilesFile.substr(1, mbtilesFile.length - 2);
|
inputFile = inputFile.substr(1, inputFile.length - 2);
|
||||||
const mapsTo = (params.mapping || {})[mbtilesFile];
|
const mapsTo = (params.mapping || {})[inputFile];
|
||||||
if (mapsTo) {
|
if (mapsTo) {
|
||||||
mbtilesFile = mapsTo;
|
inputFile = mapsTo;
|
||||||
}
|
}
|
||||||
mbtilesFile = dataResolver(mbtilesFile);
|
|
||||||
if (!mbtilesFile) {
|
const DataInfo = dataResolver(inputFile);
|
||||||
console.error(`ERROR: data "${mbtilesFile}" not found!`);
|
if (DataInfo.inputfile) {
|
||||||
|
inputFile = DataInfo.inputfile;
|
||||||
|
source_type = DataInfo.filetype;
|
||||||
|
} else {
|
||||||
|
console.error(`ERROR: data "${inputFile}" not found!`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mbtilesFileStats = fs.statSync(mbtilesFile);
|
const inputFileStats = fs.statSync(inputFile);
|
||||||
if (!mbtilesFileStats.isFile() || mbtilesFileStats.size === 0) {
|
if (!inputFileStats.isFile() || inputFileStats.size === 0) {
|
||||||
throw Error(`Not valid MBTiles file: ${mbtilesFile}`);
|
throw Error(`Not valid MBTiles file: ${inputFile}`);
|
||||||
}
|
}
|
||||||
const extension = mbtilesFile.split('.').pop().toLowerCase();
|
|
||||||
if (extension === 'pmtiles') {
|
if (source_type === 'pmtiles') {
|
||||||
const info = await GetPMtilesInfo(mbtilesFile);
|
let FileDescriptor = PMtilesOpen(inputFile);
|
||||||
|
const info = await GetPMtilesInfo(FileDescriptor);
|
||||||
const metadata = info.metadata;
|
const metadata = info.metadata;
|
||||||
map.sources[metadata.name.toLowerCase()] = mbtilesFile;
|
map.sources[metadata.name.toLowerCase()] = FileDescriptor;
|
||||||
|
map.source_types[metadata.name.toLowerCase()] = 'pmtiles';
|
||||||
|
|
||||||
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?
|
||||||
|
|
@ -1514,7 +1520,6 @@ export const serve_rendered = {
|
||||||
`mbtiles://${name}/{z}/{x}/{y}.${metadata.format || 'pbf'}`,
|
`mbtiles://${name}/{z}/{x}/{y}.${metadata.format || 'pbf'}`,
|
||||||
];
|
];
|
||||||
delete source.scheme;
|
delete source.scheme;
|
||||||
//console.log(source);
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!attributionOverride &&
|
!attributionOverride &&
|
||||||
|
|
@ -1531,62 +1536,60 @@ export const serve_rendered = {
|
||||||
} else {
|
} else {
|
||||||
queue.push(
|
queue.push(
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
mbtilesFile = path.resolve(options.paths.mbtiles, mbtilesFile);
|
inputFile = path.resolve(options.paths.mbtiles, inputFile);
|
||||||
const mbtilesFileStats = fs.statSync(mbtilesFile);
|
const inputFileStats = fs.statSync(inputFile);
|
||||||
if (!mbtilesFileStats.isFile() || mbtilesFileStats.size === 0) {
|
if (!inputFileStats.isFile() || inputFileStats.size === 0) {
|
||||||
throw Error(`Not valid MBTiles file: ${mbtilesFile}`);
|
throw Error(`Not valid MBTiles file: ${inputFile}`);
|
||||||
}
|
}
|
||||||
map.sources[name] = new MBTiles(
|
map.sources[name] = new MBTiles(inputFile + '?mode=ro', (err) => {
|
||||||
mbtilesFile + '?mode=ro',
|
map.sources[name].getInfo((err, info) => {
|
||||||
(err) => {
|
if (err) {
|
||||||
map.sources[name].getInfo((err, info) => {
|
console.error(err);
|
||||||
if (err) {
|
return;
|
||||||
console.error(err);
|
}
|
||||||
return;
|
map.source_types[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?
|
||||||
const to3857 = proj4('EPSG:3857');
|
const to3857 = proj4('EPSG:3857');
|
||||||
const toDataProj = proj4(info.proj4);
|
const toDataProj = proj4(info.proj4);
|
||||||
repoobj.dataProjWGStoInternalWGS = (xy) =>
|
repoobj.dataProjWGStoInternalWGS = (xy) =>
|
||||||
to3857.inverse(toDataProj.forward(xy));
|
to3857.inverse(toDataProj.forward(xy));
|
||||||
}
|
}
|
||||||
|
|
||||||
const type = source.type;
|
const type = source.type;
|
||||||
info['extension'] = 'mbtiles';
|
info['extension'] = 'mbtiles';
|
||||||
Object.assign(source, info);
|
Object.assign(source, info);
|
||||||
source.type = type;
|
source.type = type;
|
||||||
source.tiles = [
|
source.tiles = [
|
||||||
// meta url which will be detected when requested
|
// meta url which will be detected when requested
|
||||||
`mbtiles://${name}/{z}/{x}/{y}.${info.format || 'pbf'}`,
|
`mbtiles://${name}/{z}/{x}/{y}.${info.format || 'pbf'}`,
|
||||||
];
|
];
|
||||||
delete source.scheme;
|
delete source.scheme;
|
||||||
|
|
||||||
if (options.dataDecoratorFunc) {
|
if (options.dataDecoratorFunc) {
|
||||||
source = options.dataDecoratorFunc(
|
source = options.dataDecoratorFunc(
|
||||||
name,
|
name,
|
||||||
'tilejson',
|
'tilejson',
|
||||||
source,
|
source,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!attributionOverride &&
|
!attributionOverride &&
|
||||||
source.attribution &&
|
source.attribution &&
|
||||||
source.attribution.length > 0
|
source.attribution.length > 0
|
||||||
) {
|
) {
|
||||||
if (!tileJSON.attribution.includes(source.attribution)) {
|
if (!tileJSON.attribution.includes(source.attribution)) {
|
||||||
if (tileJSON.attribution.length > 0) {
|
if (tileJSON.attribution.length > 0) {
|
||||||
tileJSON.attribution += ' | ';
|
tileJSON.attribution += ' | ';
|
||||||
}
|
|
||||||
tileJSON.attribution += source.attribution;
|
|
||||||
}
|
}
|
||||||
|
tileJSON.attribution += source.attribution;
|
||||||
}
|
}
|
||||||
resolve();
|
}
|
||||||
});
|
resolve();
|
||||||
},
|
});
|
||||||
);
|
});
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,19 +111,26 @@ export const serve_style = {
|
||||||
for (const name of Object.keys(styleJSON.sources)) {
|
for (const name of Object.keys(styleJSON.sources)) {
|
||||||
const source = styleJSON.sources[name];
|
const source = styleJSON.sources[name];
|
||||||
const url = source.url;
|
const url = source.url;
|
||||||
if (url && url.lastIndexOf('mbtiles:', 0) === 0) {
|
if (
|
||||||
let mbtilesFile = url.substring('mbtiles://'.length);
|
url &&
|
||||||
|
(url.lastIndexOf('pmtiles:', 0) === 0 ||
|
||||||
|
url.lastIndexOf('mbtiles:', 0) === 0)
|
||||||
|
) {
|
||||||
|
let inputSource = url
|
||||||
|
.replace('pmtiles://', '')
|
||||||
|
.replace('mbtiles://', '');
|
||||||
|
|
||||||
const fromData =
|
const fromData =
|
||||||
mbtilesFile[0] === '{' && mbtilesFile[mbtilesFile.length - 1] === '}';
|
inputSource[0] === '{' && inputSource[inputSource.length - 1] === '}';
|
||||||
|
|
||||||
if (fromData) {
|
if (fromData) {
|
||||||
mbtilesFile = mbtilesFile.substr(1, mbtilesFile.length - 2);
|
inputSource = inputSource.substr(1, inputSource.length - 2);
|
||||||
const mapsTo = (params.mapping || {})[mbtilesFile];
|
const mapsTo = (params.mapping || {})[inputSource];
|
||||||
if (mapsTo) {
|
if (mapsTo) {
|
||||||
mbtilesFile = mapsTo;
|
inputSource = mapsTo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const identifier = reportTiles(mbtilesFile, fromData);
|
const identifier = reportTiles(inputSource, fromData);
|
||||||
if (!identifier) {
|
if (!identifier) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -181,15 +181,28 @@ function start(opts) {
|
||||||
item,
|
item,
|
||||||
id,
|
id,
|
||||||
opts.publicUrl,
|
opts.publicUrl,
|
||||||
(mbtiles, fromData) => {
|
(fileid, fromData) => {
|
||||||
let dataItemId;
|
let dataItemId;
|
||||||
for (const id of Object.keys(data)) {
|
for (const id of Object.keys(data)) {
|
||||||
if (fromData) {
|
if (fromData) {
|
||||||
if (id === mbtiles) {
|
if (id === fileid) {
|
||||||
dataItemId = id;
|
dataItemId = id;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (data[id].mbtiles === mbtiles) {
|
if (
|
||||||
|
data[id].mbtiles !== undefined &&
|
||||||
|
data[id].mbtiles === fileid
|
||||||
|
) {
|
||||||
|
dataItemId = id;
|
||||||
|
} else if (
|
||||||
|
data[id].pmtiles !== undefined &&
|
||||||
|
data[id].pmtiles === fileid
|
||||||
|
) {
|
||||||
|
dataItemId = id;
|
||||||
|
} else if (
|
||||||
|
data[id].filename !== undefined &&
|
||||||
|
data[id].filename === fileid
|
||||||
|
) {
|
||||||
dataItemId = id;
|
dataItemId = id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -200,14 +213,14 @@ function start(opts) {
|
||||||
} else {
|
} else {
|
||||||
if (fromData || !allowMoreData) {
|
if (fromData || !allowMoreData) {
|
||||||
console.log(
|
console.log(
|
||||||
`ERROR: style "${item.style}" using unknown mbtiles "${mbtiles}"! Skipping...`,
|
`ERROR: style "${item.style}" using unknown mbtiles "${fileid}"! Skipping...`,
|
||||||
);
|
);
|
||||||
return undefined;
|
return undefined;
|
||||||
} else {
|
} else {
|
||||||
let id = mbtiles.substr(0, mbtiles.lastIndexOf('.')) || mbtiles;
|
let id = fileid.substr(0, fileid.lastIndexOf('.')) || fileid;
|
||||||
while (data[id]) id += '_';
|
while (data[id]) id += '_';
|
||||||
data[id] = {
|
data[id] = {
|
||||||
mbtiles: mbtiles,
|
filename: fileid,
|
||||||
};
|
};
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
@ -229,14 +242,24 @@ function start(opts) {
|
||||||
item,
|
item,
|
||||||
id,
|
id,
|
||||||
opts.publicUrl,
|
opts.publicUrl,
|
||||||
(mbtiles) => {
|
(fileid) => {
|
||||||
let mbtilesFile;
|
let inputFile;
|
||||||
|
let fileType;
|
||||||
for (const id of Object.keys(data)) {
|
for (const id of Object.keys(data)) {
|
||||||
if (id === mbtiles) {
|
if (id === fileid) {
|
||||||
mbtilesFile = data[id].mbtiles;
|
if (data[id].pmtiles !== undefined) {
|
||||||
|
inputFile = data[id].pmtiles;
|
||||||
|
fileType = 'pmtiles';
|
||||||
|
} else if (data[id].mbtiles !== undefined) {
|
||||||
|
inputFile = data[id].mbtiles;
|
||||||
|
fileType = 'mbtiles';
|
||||||
|
} else if (data[id].filename !== undefined) {
|
||||||
|
inputFile = data[id].fileid;
|
||||||
|
fileType = inputFile.split('.').pop().toLowerCase();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mbtilesFile;
|
return { inputfile: inputFile, filetype: fileType };
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -264,8 +287,12 @@ function start(opts) {
|
||||||
|
|
||||||
for (const id of Object.keys(data)) {
|
for (const id of Object.keys(data)) {
|
||||||
const item = data[id];
|
const item = data[id];
|
||||||
if (!item.mbtiles || item.mbtiles.length === 0) {
|
if (item.pmtiles && item.pmtiles.length !== 0) {
|
||||||
console.log(`Missing "mbtiles" property for ${id}`);
|
// valid pmtiles
|
||||||
|
} else if (item.mbtiles && item.mbtiles.length !== 0) {
|
||||||
|
// valid mbtiles
|
||||||
|
} else {
|
||||||
|
console.log(`Missing "pmtiles" or "mbtiles" property for ${id}`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue