mirror of
https://github.com/rastapasta/mapscii.git
synced 2024-11-21 23:53:08 +01:00
Decaffeinate TileSource
This commit is contained in:
parent
d27e086758
commit
29b5b3ccb1
@ -1,133 +0,0 @@
|
|||||||
###
|
|
||||||
termap - Terminal Map Viewer
|
|
||||||
by Michael Strassburger <codepoet@cpan.org>
|
|
||||||
|
|
||||||
Source for VectorTiles - supports
|
|
||||||
* remote TileServer
|
|
||||||
* local MBTiles and VectorTiles
|
|
||||||
###
|
|
||||||
|
|
||||||
Promise = require 'bluebird'
|
|
||||||
userhome = require 'userhome'
|
|
||||||
fetch = require 'node-fetch'
|
|
||||||
fs = require 'fs'
|
|
||||||
|
|
||||||
Tile = require './Tile'
|
|
||||||
config = require './config'
|
|
||||||
|
|
||||||
# https://github.com/mapbox/node-mbtiles has native build dependencies (sqlite3)
|
|
||||||
# To maximize mapscii's compatibility, MBTiles support must be manually added via
|
|
||||||
# $> npm install -g mbtiles
|
|
||||||
MBTiles = try
|
|
||||||
require 'mbtiles'
|
|
||||||
catch
|
|
||||||
null
|
|
||||||
|
|
||||||
module.exports = class TileSource
|
|
||||||
cache: {}
|
|
||||||
cacheSize: 16
|
|
||||||
cached: []
|
|
||||||
|
|
||||||
modes:
|
|
||||||
MBTiles: 1
|
|
||||||
VectorTile: 2
|
|
||||||
HTTP: 3
|
|
||||||
|
|
||||||
mode: null
|
|
||||||
mbtiles: null
|
|
||||||
styler: null
|
|
||||||
|
|
||||||
init: (@source) ->
|
|
||||||
if @source.startsWith "http"
|
|
||||||
@_initPersistence() if config.persistDownloadedTiles
|
|
||||||
|
|
||||||
@mode = @modes.HTTP
|
|
||||||
|
|
||||||
else if @source.endsWith ".mbtiles"
|
|
||||||
unless MBTiles
|
|
||||||
throw new Error "MBTiles support must be installed with following command: 'npm install -g mbtiles'"
|
|
||||||
|
|
||||||
@mode = @modes.MBTiles
|
|
||||||
@loadMBtils source
|
|
||||||
|
|
||||||
else
|
|
||||||
throw new Error "source type isn't supported yet"
|
|
||||||
|
|
||||||
loadMBtils: (source) ->
|
|
||||||
new Promise (resolve, reject) =>
|
|
||||||
new MBTiles source, (err, @mbtiles) =>
|
|
||||||
if err then reject err
|
|
||||||
else resolve()
|
|
||||||
|
|
||||||
useStyler: (@styler) ->
|
|
||||||
|
|
||||||
getTile: (z, x, y) ->
|
|
||||||
unless @mode
|
|
||||||
throw new Error "no TileSource defined"
|
|
||||||
|
|
||||||
z = Math.max 0, Math.floor z
|
|
||||||
|
|
||||||
if cached = @cache[[z,x,y].join("-")]
|
|
||||||
return Promise.resolve cached
|
|
||||||
|
|
||||||
if @cached.length > @cacheSize
|
|
||||||
for tile in @cached.splice 0, Math.abs(@cacheSize-@cached.length)
|
|
||||||
delete @cache[tile]
|
|
||||||
|
|
||||||
switch @mode
|
|
||||||
when @modes.MBTiles then @_getMBTile z, x, y
|
|
||||||
when @modes.HTTP then @_getHTTP z, x, y
|
|
||||||
|
|
||||||
_getHTTP: (z, x, y) ->
|
|
||||||
promise =
|
|
||||||
if config.persistDownloadedTiles and tile = @_getPersited z, x, y
|
|
||||||
Promise.resolve tile
|
|
||||||
else
|
|
||||||
fetch @source+[z,x,y].join("/")+".pbf"
|
|
||||||
.then (res) => res.buffer()
|
|
||||||
.then (buffer) =>
|
|
||||||
@_persistTile z, x, y, buffer if config.persistDownloadedTiles
|
|
||||||
buffer
|
|
||||||
|
|
||||||
promise
|
|
||||||
.then (buffer) =>
|
|
||||||
@_createTile z, x, y, buffer
|
|
||||||
|
|
||||||
_getMBTile: (z, x, y) ->
|
|
||||||
new Promise (resolve, reject) =>
|
|
||||||
@mbtiles.getTile z, x, y, (err, buffer) =>
|
|
||||||
return reject err if err
|
|
||||||
resolve @_createTile z, x, y, buffer
|
|
||||||
|
|
||||||
_createTile: (z, x, y, buffer) ->
|
|
||||||
name = [z,x,y].join("-")
|
|
||||||
@cached.push name
|
|
||||||
|
|
||||||
tile = @cache[name] = new Tile @styler
|
|
||||||
tile.load buffer
|
|
||||||
|
|
||||||
_initPersistence: ->
|
|
||||||
try
|
|
||||||
@_createFolder userhome ".mapscii"
|
|
||||||
@_createFolder userhome ".mapscii", "cache"
|
|
||||||
catch error
|
|
||||||
config.persistDownloadedTiles = false
|
|
||||||
return
|
|
||||||
|
|
||||||
_persistTile: (z, x, y, buffer) ->
|
|
||||||
zoom = z.toString()
|
|
||||||
@_createFolder userhome ".mapscii", "cache", zoom
|
|
||||||
fs.writeFile userhome(".mapscii", "cache", zoom, "#{x}-#{y}.pbf"), buffer, -> null
|
|
||||||
|
|
||||||
_getPersited: (z, x, y) ->
|
|
||||||
try
|
|
||||||
fs.readFileSync userhome ".mapscii", "cache", z.toString(), "#{x}-#{y}.pbf"
|
|
||||||
catch error
|
|
||||||
false
|
|
||||||
|
|
||||||
_createFolder: (path) ->
|
|
||||||
try
|
|
||||||
fs.mkdirSync path
|
|
||||||
true
|
|
||||||
catch e
|
|
||||||
e.code is "EEXIST"
|
|
180
src/TileSource.js
Normal file
180
src/TileSource.js
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
/*
|
||||||
|
termap - Terminal Map Viewer
|
||||||
|
by Michael Strassburger <codepoet@cpan.org>
|
||||||
|
|
||||||
|
Source for VectorTiles - supports
|
||||||
|
* remote TileServer
|
||||||
|
* local MBTiles and VectorTiles
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
const userhome = require('userhome');
|
||||||
|
const fetch = require('node-fetch');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const Tile = require('./Tile');
|
||||||
|
const config = require('./config');
|
||||||
|
|
||||||
|
// https://github.com/mapbox/node-mbtiles has native build dependencies (sqlite3)
|
||||||
|
// To maximize mapscii's compatibility, MBTiles support must be manually added via
|
||||||
|
// $> npm install -g mbtiles
|
||||||
|
let MBTiles = null;
|
||||||
|
try {
|
||||||
|
MBTiles = require('mbtiles');
|
||||||
|
} catch (err) {}
|
||||||
|
|
||||||
|
const cache = {};
|
||||||
|
const cacheSize = 16;
|
||||||
|
const cached = [];
|
||||||
|
|
||||||
|
const modes = {
|
||||||
|
MBTiles: 1,
|
||||||
|
VectorTile: 2,
|
||||||
|
HTTP: 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
class TileSource {
|
||||||
|
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 mbtiles'");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mode = modes.MBTiles;
|
||||||
|
this.loadMBtils(source);
|
||||||
|
} else {
|
||||||
|
throw new Error("source type isn't supported yet");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadMBtils(source) {
|
||||||
|
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) {
|
||||||
|
throw new Error("no TileSource defined");
|
||||||
|
}
|
||||||
|
|
||||||
|
const cached = this.cache[[z, x, y].join("-")];
|
||||||
|
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')
|
||||||
|
.then((res) => res.buffer())
|
||||||
|
.then((buffer) => {
|
||||||
|
if (config.persistDownloadedTiles) {
|
||||||
|
this._persistTile(z, x, y, buffer);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
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(userhome('.mapscii'));
|
||||||
|
this._createFolder(userhome('.mapscii', 'cache'));
|
||||||
|
} catch (error) {
|
||||||
|
config.persistDownloadedTiles = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_persistTile(z, x, y, buffer) {
|
||||||
|
const zoom = z.toString();
|
||||||
|
this._createFolder(userhome('.mapscii', 'cache', zoom));
|
||||||
|
const filePath = userhome('.mapscii', 'cache', zoom, `${x}-${y}.pbf`);
|
||||||
|
return fs.writeFile(filePath, buffer, () => null);
|
||||||
|
}
|
||||||
|
|
||||||
|
_getPersited(z, x, y) {
|
||||||
|
try {
|
||||||
|
return fs.readFileSync(userhome('.mapscii', 'cache', z.toString(), `${x}-${y}.pbf`));
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_createFolder(path) {
|
||||||
|
try {
|
||||||
|
fs.mkdirSync(path);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
return error.code === "EEXIST";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = TileSource;
|
Loading…
Reference in New Issue
Block a user