mapscii/src/TileSource.js

177 wiersze
4.1 KiB
JavaScript

2017-12-21 10:18:34 +00:00
/*
termap - Terminal Map Viewer
by Michael Strassburger <codepoet@cpan.org>
Source for VectorTiles - supports
* remote TileServer
* local MBTiles and VectorTiles
*/
2022-04-29 20:08:49 +00:00
import fs from 'fs';
import path from 'path';
import fetch from 'node-fetch';
import envPaths from 'env-paths';
const paths = envPaths('mapscii');
2017-12-21 10:18:34 +00:00
2022-04-29 20:08:49 +00:00
import Tile from './Tile.js';
import config from './config.js';
2017-12-21 10:18:34 +00:00
// https://github.com/mapbox/node-mbtiles has native build dependencies (sqlite3)
2018-11-19 02:47:24 +00:00
// To maximize MapSCIIs compatibility, MBTiles support must be manually added via
// $> npm install -g @mapbox/mbtiles
2017-12-21 10:18:34 +00:00
let MBTiles = null;
try {
MBTiles = require('@mapbox/mbtiles');
2017-12-23 08:07:23 +00:00
} catch (err) {void 0;}
2017-12-21 10:18:34 +00:00
const modes = {
MBTiles: 1,
VectorTile: 2,
HTTP: 3,
};
2022-04-29 20:08:49 +00:00
export default class TileSource {
2017-12-21 10:18:34 +00:00
init(source) {
this.source = source;
this.cache = {};
this.cacheSize = 16;
this.cached = [];
this.mode = null;
this.mbtiles = null;
this.styler = null;
if (this.source.startsWith('http')) {
if (config.persistDownloadedTiles) {
this._initPersistence();
}
this.mode = modes.HTTP;
} else if (this.source.endsWith('.mbtiles')) {
if (!MBTiles) {
throw new Error('MBTiles support must be installed with following command: \'npm install -g @mapbox/mbtiles\'');
2017-12-21 10:18:34 +00:00
}
this.mode = modes.MBTiles;
2018-11-19 08:29:22 +00:00
this.loadMBTiles(source);
2017-12-21 10:18:34 +00:00
} else {
2017-12-23 08:07:23 +00:00
throw new Error('source type isn\'t supported yet');
2017-12-21 10:18:34 +00:00
}
}
2018-11-19 08:29:22 +00:00
loadMBTiles(source) {
2017-12-21 10:18:34 +00:00
return new Promise((resolve, reject) => {
new MBTiles(source, (err, mbtiles) => {
if (err) {
reject(err);
}
this.mbtiles = mbtiles;
resolve();
});
});
}
useStyler(styler) {
this.styler = styler;
}
getTile(z, x, y) {
if (!this.mode) {
2017-12-23 08:07:23 +00:00
throw new Error('no TileSource defined');
2017-12-21 10:18:34 +00:00
}
2017-12-23 08:07:23 +00:00
const cached = this.cache[[z, x, y].join('-')];
2017-12-21 10:18:34 +00:00
if (cached) {
return Promise.resolve(cached);
}
if (this.cached.length > this.cacheSize) {
const overflow = Math.abs(this.cacheSize - this.cache.length);
for (const tile in this.cached.splice(0, overflow)) {
delete this.cache[tile];
}
}
switch (this.mode) {
case modes.MBTiles:
return this._getMBTile(z, x, y);
case modes.HTTP:
return this._getHTTP(z, x, y);
}
}
_getHTTP(z, x, y) {
let promise;
const persistedTile = this._getPersited(z, x, y);
if (config.persistDownloadedTiles && persistedTile) {
promise = Promise.resolve(persistedTile);
} else {
promise = fetch(this.source + [z,x,y].join('/') + '.pbf')
2022-04-29 20:08:49 +00:00
.then((res) => res.arrayBuffer())
.then((arrayBuffer) => {
const buffer = Buffer.from(arrayBuffer);
2017-12-23 08:07:23 +00:00
if (config.persistDownloadedTiles) {
this._persistTile(z, x, y, buffer);
return buffer;
}
});
2017-12-21 10:18:34 +00:00
}
return promise.then((buffer) => {
return this._createTile(z, x, y, buffer);
});
}
_getMBTile(z, x, y) {
return new Promise((resolve, reject) => {
this.mbtiles.getTile(z, x, y, (err, buffer) => {
if (err) {
reject(err);
}
resolve(this._createTile(z, x, y, buffer));
});
});
}
_createTile(z, x, y, buffer) {
const name = [z, x, y].join('-');
this.cached.push(name);
const tile = this.cache[name] = new Tile(this.styler);
return tile.load(buffer);
}
_initPersistence() {
try {
this._createFolder(paths.cache);
2017-12-21 10:18:34 +00:00
} catch (error) {
config.persistDownloadedTiles = false;
}
}
_persistTile(z, x, y, buffer) {
const zoom = z.toString();
this._createFolder(path.join(paths.cache, zoom));
const filePath = path.join(paths.cache, zoom, `${x}-${y}.pbf`);
2017-12-21 10:18:34 +00:00
return fs.writeFile(filePath, buffer, () => null);
}
_getPersited(z, x, y) {
try {
return fs.readFileSync(path.join(paths.cache, z.toString(), `${x}-${y}.pbf`));
2017-12-21 10:18:34 +00:00
} catch (error) {
return false;
}
}
_createFolder(path) {
try {
fs.mkdirSync(path);
return true;
} catch (error) {
if (error.code === 'EEXIST') return true;
throw error;
2017-12-21 10:18:34 +00:00
}
}
}