Decaffeinate TileSource

This commit is contained in:
Christian Paul 2017-12-21 02:18:34 -08:00
parent d27e086758
commit 29b5b3ccb1
2 changed files with 180 additions and 133 deletions

View File

@ -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
View 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;