Translate to ESM (ECMAScript Module)

This commit is contained in:
Christian Paul 2022-04-29 22:08:49 +02:00
parent 76f541db46
commit 034f561fa4
16 changed files with 6246 additions and 3712 deletions

View File

@ -1,22 +1,23 @@
module.exports = {
"env": {
"es6": true,
"node": true,
"es2021": true,
"jest": true
},
"parserOptions": {
"ecmaVersion": 2018
},
"extends": [
"eslint:recommended",
"plugin:jest/recommended"
],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"rules": {
"indent": [
"error",
2,
{
"SwitchCase": 1
"SwitchCase": 1
}
],
"linebreak-style": [
@ -33,4 +34,4 @@ module.exports = {
"always"
]
}
};
}

10
main.js
View File

@ -7,11 +7,11 @@
TODO: params parsing and so on
#*/
'use strict';
const config = require('./src/config');
const Mapscii = require('./src/Mapscii');
const argv = require('yargs')
.option('latitude', {
import config from './src/config.js';
import Mapscii from './src/Mapscii.js';
import yargs from 'yargs';
const argv = yargs().option('latitude', {
alias: 'lat',
description: 'Latitude of initial centre',
default: config.initialLat,

9541
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -3,10 +3,11 @@
"version": "0.3.1",
"description": "MapSCII is a Braille & ASCII world map renderer for your console, based on OpenStreetMap",
"main": "main.js",
"type": "module",
"scripts": {
"lint": "eslint src",
"start": "node main",
"test": "jest"
"test": "NODE_OPTIONS=--experimental-vm-modules jest"
},
"repository": {
"type": "git",
@ -32,21 +33,21 @@
"dependencies": {
"@mapbox/vector-tile": "^1.3.1",
"bresenham": "0.0.4",
"earcut": "^2.2.2",
"env-paths": "^2.2.0",
"earcut": "^2.2.3",
"env-paths": "^3.0.0",
"keypress": "^0.2.1",
"node-fetch": "^2.6.1",
"node-fetch": "^3.2.4",
"pbf": "^3.2.1",
"rbush": "^3.0.1",
"simplify-js": "^1.2.4",
"string-width": "^4.2.0",
"string-width": "^5.1.2",
"term-mouse": "^0.2.2",
"x256": "0.0.2",
"yargs": "^15.4.1"
"yargs": "^17.4.1"
},
"devDependencies": {
"eslint": "^7.8.1",
"eslint-plugin-jest": "^24.0.0",
"jest": "^26.4.2"
"eslint": "^8.14.0",
"eslint-plugin-jest": "^26.1.5",
"jest": "^28.0.3"
}
}

View File

@ -11,10 +11,9 @@
Will either be merged into node-drawille or become an own module at some point
*/
'use strict';
const stringWidth = require('string-width');
const config = require('./config');
const utils = require('./utils');
import stringWidth from 'string-width';
import config from './config.js';
import * as utils from './utils.js';
const asciiMap = {
// '▬': [2+32, 4+64],
@ -29,7 +28,7 @@ const asciiMap = {
};
const termReset = '\x1B[39;49m';
class BrailleBuffer {
export default class BrailleBuffer {
constructor(width, height) {
this.brailleMap = [[0x1, 0x8],[0x2, 0x10],[0x4, 0x20],[0x40, 0x80]];
@ -207,5 +206,3 @@ class BrailleBuffer {
}
}
}
module.exports = BrailleBuffer;

View File

@ -11,11 +11,11 @@
Will most likely be turned into a stand alone module at some point
*/
'use strict';
const bresenham = require('bresenham');
const earcut = require('earcut');
const BrailleBuffer = require('./BrailleBuffer');
import bresenham from 'bresenham';
import earcut from 'earcut';
import BrailleBuffer from './BrailleBuffer.js';
class Canvas {
export default class Canvas {
constructor(width, height) {
this.width = width;
this.height = height;
@ -198,5 +198,3 @@ class Canvas {
}
Canvas.prototype.stack = [];
module.exports = Canvas;

View File

@ -5,11 +5,10 @@
Using 2D spatial indexing to avoid overlapping labels and markers
and to find labels underneath a mouse cursor's position
*/
'use strict';
const RBush = require('rbush');
const stringWidth = require('string-width');
import RBush from 'rbush';
import stringWidth from 'string-width';
module.exports = class LabelBuffer {
export default class LabelBuffer {
constructor() {
this.tree = new RBush();
@ -54,4 +53,4 @@ module.exports = class LabelBuffer {
maxY: y+margin/2,
};
}
};
}

View File

@ -4,17 +4,16 @@
UI and central command center
*/
'use strict';
const fs = require('fs');
const keypress = require('keypress');
const TermMouse = require('term-mouse');
import fs from 'fs';
import keypress from 'keypress';
import TermMouse from 'term-mouse';
const Renderer = require('./Renderer');
const TileSource = require('./TileSource');
const utils = require('./utils');
let config = require('./config');
import Renderer from './Renderer.js';
import TileSource from './TileSource.js';
import * as utils from './utils.js';
import MapsciiConfig from './config.js';
class Mapscii {
export default class Mapscii {
constructor(options) {
this.width = null;
this.height = null;
@ -32,16 +31,19 @@ class Mapscii {
this.zoom = 0;
this.minZoom = null;
config = Object.assign(config, options);
this.config = {
...MapsciiConfig,
...options,
};
this.center = {
lat: config.initialLat,
lon: config.initialLon
lat: this.config.initialLat,
lon: this.config.initialLon,
};
}
async init() {
if (!config.headless) {
if (!this.config.headless) {
this._initKeyboard();
this._initMouse();
}
@ -54,23 +56,23 @@ class Mapscii {
_initTileSource() {
this.tileSource = new TileSource();
this.tileSource.init(config.source);
this.tileSource.init(this.config.source);
}
_initKeyboard() {
keypress(config.input);
if (config.input.setRawMode) {
config.input.setRawMode(true);
keypress(this.config.input);
if (this.config.input.setRawMode) {
this.config.input.setRawMode(true);
}
config.input.resume();
this.config.input.resume();
config.input.on('keypress', (ch, key) => this._onKey(key));
this.config.input.on('keypress', (ch, key) => this._onKey(key));
}
_initMouse() {
this.mouse = TermMouse({
input: config.input,
output: config.output,
input: this.config.input,
output: this.config.output,
});
this.mouse.start();
@ -80,21 +82,21 @@ class Mapscii {
}
_initRenderer() {
const style = JSON.parse(fs.readFileSync(config.styleFile, 'utf8'));
this.renderer = new Renderer(config.output, this.tileSource, style);
const style = JSON.parse(fs.readFileSync(this.config.styleFile, 'utf8'));
this.renderer = new Renderer(this.config.output, this.tileSource, style);
config.output.on('resize', () => {
this.config.output.on('resize', () => {
this._resizeRenderer();
this._draw();
});
this._resizeRenderer();
this.zoom = (config.initialZoom !== null) ? config.initialZoom : this.minZoom;
this.zoom = (this.config.initialZoom !== null) ? this.config.initialZoom : this.minZoom;
}
_resizeRenderer() {
this.width = config.size && config.size.width ? config.size.width * 2 : config.output.columns >> 1 << 2;
this.height = config.size && config.size.height ? config.size.height * 4 : config.output.rows * 4 - 4;
this.width = this.config.size && this.config.size.width ? this.config.size.width * 2 : this.config.output.columns >> 1 << 2;
this.height = this.config.size && this.config.size.height ? this.config.size.height * 4 : this.config.output.rows * 4 - 4;
this.minZoom = 4-Math.log(4096/this.width)/Math.LN2;
@ -142,7 +144,7 @@ class Mapscii {
const targetMouseLonLat = this._colrow2ll(event.x, event.y);
// zoom toward the center
this.zoomBy(config.zoomStep * (event.button === 'up' ? 1 : -1));
this.zoomBy(this.config.zoomStep * (event.button === 'up' ? 1 : -1));
// the location the pointer ended up after zooming
const offsetMouseLonLat = this._colrow2ll(event.x, event.y);
@ -171,7 +173,7 @@ class Mapscii {
if (event.x < 0 || event.x > this.width/2 || event.y < 0 || event.y > this.height/4) {
return;
}
if (config.mouseCallback && !config.mouseCallback(event)) {
if (this.config.mouseCallback && !this.config.mouseCallback(event)) {
return;
}
@ -207,25 +209,25 @@ class Mapscii {
}
_onKey(key) {
if (config.keyCallback && !config.keyCallback(key)) return;
if (this.config.keyCallback && !this.config.keyCallback(key)) return;
if (!key || !key.name) return;
// check if the pressed key is configured
let draw = true;
switch (key.name) {
case 'q':
if (config.quitCallback) {
config.quitCallback();
if (this.config.quitCallback) {
this.config.quitCallback();
} else {
process.exit(0);
}
break;
case 'a':
this.zoomBy(config.zoomStep);
this.zoomBy(this.config.zoomStep);
break;
case 'y':
case 'z':
this.zoomBy(-config.zoomStep);
this.zoomBy(-this.config.zoomStep);
break;
case 'left':
case 'h':
@ -244,7 +246,7 @@ class Mapscii {
this.moveBy(-6/Math.pow(2, this.zoom), 0);
break;
case 'c':
config.useBraille = !config.useBraille;
this.config.useBraille = !this.config.useBraille;
break;
default:
draw = false;
@ -277,22 +279,22 @@ class Mapscii {
}
notify(text) {
config.onUpdate && config.onUpdate();
if (!config.headless) {
this.config.onUpdate && this.config.onUpdate();
if (!this.config.headless) {
this._write('\r\x1B[K' + text);
}
}
_write(output) {
config.output.write(output);
this.config.output.write(output);
}
zoomBy(step) {
if (this.zoom+step < this.minZoom) {
return this.zoom = this.minZoom;
}
if (this.zoom+step > config.maxZoom) {
return this.zoom = config.maxZoom;
if (this.zoom+step > this.config.maxZoom) {
return this.zoom = this.config.maxZoom;
}
this.zoom += step;
@ -309,5 +311,3 @@ class Mapscii {
});
}
}
module.exports = Mapscii;

View File

@ -5,16 +5,16 @@
The Console Vector Tile renderer - bäm!
*/
'use strict';
const x256 = require('x256');
const simplify = require('simplify-js');
import x256 from 'x256';
import simplify from 'simplify-js';
const Canvas = require('./Canvas');
const LabelBuffer = require('./LabelBuffer');
const Styler = require('./Styler');
const utils = require('./utils');
const config = require('./config');
import Canvas from './Canvas.js';
import LabelBuffer from './LabelBuffer.js';
import Styler from './Styler.js';
import * as utils from './utils.js';
import config from './config.js';
class Renderer {
export default class Renderer {
constructor(output, tileSource, style) {
this.output = output;
this.tileSource = tileSource;
@ -331,5 +331,3 @@ Renderer.prototype.lastDrawAt = 0;
Renderer.prototype.labelBuffer = null;
Renderer.prototype.tileSource = null;
Renderer.prototype.tilePadding = 64;
module.exports = Renderer;

View File

@ -8,9 +8,8 @@
Compiles layer filter instructions into a chain of true/false returning
anonymous functions to improve rendering speed compared to realtime parsing.
*/
'use strict';
class Styler {
export default class Styler {
constructor(style) {
this.styleById = {};
this.styleByLayer = {};
@ -129,5 +128,3 @@ class Styler {
}
}
}
module.exports = Styler;

View File

@ -4,17 +4,16 @@
Handling of and access to single VectorTiles
*/
'use strict';
const VectorTile = require('@mapbox/vector-tile').VectorTile;
const Protobuf = require('pbf');
const zlib = require('zlib');
const RBush = require('rbush');
const x256 = require('x256');
import { VectorTile } from '@mapbox/vector-tile';
import Protobuf from 'pbf';
import zlib from 'zlib';
import RBush from 'rbush';
import x256 from 'x256';
const config = require('./config');
const utils = require('./utils');
import config from './config.js';
import * as utils from './utils.js';
class Tile {
export default class Tile {
constructor(styler) {
this.styler = styler;
}
@ -122,10 +121,10 @@ class Tile {
}
_addBoundaries(deep, data) {
let minX = 2e308;
let maxX = -2e308;
let minY = 2e308;
let maxY = -2e308;
let minX = 2e307;
let maxX = -2e307;
let minY = 2e307;
let maxY = -2e307;
const points = (deep ? data.points[0] : data.points);
for (const p of points) {
if (p.x < minX) minX = p.x;
@ -163,5 +162,3 @@ class Tile {
}
Tile.prototype.layers = {};
module.exports = Tile;

View File

@ -6,15 +6,14 @@
* remote TileServer
* local MBTiles and VectorTiles
*/
'use strict';
const fs = require('fs');
const path = require('path');
const fetch = require('node-fetch');
const envPaths = require('env-paths');
import fs from 'fs';
import path from 'path';
import fetch from 'node-fetch';
import envPaths from 'env-paths';
const paths = envPaths('mapscii');
const Tile = require('./Tile');
const config = require('./config');
import Tile from './Tile.js';
import config from './config.js';
// https://github.com/mapbox/node-mbtiles has native build dependencies (sqlite3)
// To maximize MapSCIIs compatibility, MBTiles support must be manually added via
@ -30,7 +29,7 @@ const modes = {
HTTP: 3,
};
class TileSource {
export default class TileSource {
init(source) {
this.source = source;
@ -109,8 +108,9 @@ class TileSource {
promise = Promise.resolve(persistedTile);
} else {
promise = fetch(this.source + [z,x,y].join('/') + '.pbf')
.then((res) => res.buffer())
.then((buffer) => {
.then((res) => res.arrayBuffer())
.then((arrayBuffer) => {
const buffer = Buffer.from(arrayBuffer);
if (config.persistDownloadedTiles) {
this._persistTile(z, x, y, buffer);
return buffer;
@ -174,5 +174,3 @@ class TileSource {
}
}
}
module.exports = TileSource;

View File

@ -1,5 +1,5 @@
'use strict';
const TileSource = require('./TileSource');
import TileSource from './TileSource.js';
describe('TileSource', () => {
describe('with a HTTP source', () => {

View File

@ -1,4 +1,10 @@
module.exports = {
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export default {
language: 'en',
// TODO: adapt to osm2vectortiles successor openmaptiles v3)
@ -7,7 +13,7 @@ module.exports = {
//source: __dirname+"/../mbtiles/regensburg.mbtiles",
styleFile: __dirname+'/../styles/dark.json',
styleFile: __dirname + '/../styles/dark.json',
initialZoom: null,
maxZoom: 18,

View File

@ -4,100 +4,95 @@
methods used all around
*/
'use strict';
const config = require('./config');
import config from './config.js';
const constants = {
RADIUS: 6378137,
};
const utils = {
clamp: (num, min, max) => {
if (num <= min) {
return min;
} else if (num >= max) {
return max;
} else {
return num;
}
},
baseZoom: (zoom) => {
return Math.min(config.tileRange, Math.max(0, Math.floor(zoom)));
},
tilesizeAtZoom: (zoom) => {
return config.projectSize * Math.pow(2, zoom-utils.baseZoom(zoom));
},
deg2rad: (angle) => {
// (angle / 180) * Math.PI
return angle * 0.017453292519943295;
},
ll2tile: (lon, lat, zoom) => {
return {
x: (lon+180)/360*Math.pow(2, zoom),
y: (1-Math.log(Math.tan(lat*Math.PI/180)+1/Math.cos(lat*Math.PI/180))/Math.PI)/2*Math.pow(2, zoom),
z: zoom,
};
},
tile2ll: (x, y, zoom) => {
const n = Math.PI - 2*Math.PI*y/Math.pow(2, zoom);
return {
lon: x/Math.pow(2, zoom)*360-180,
lat: 180/Math.PI*Math.atan(0.5*(Math.exp(n)-Math.exp(-n))),
};
},
metersPerPixel: (zoom, lat = 0) => {
return (Math.cos(lat * Math.PI/180) * 2 * Math.PI * constants.RADIUS) / (256 * Math.pow(2, zoom));
},
hex2rgb: (color) => {
if (typeof color !== 'string') return [255, 0, 0];
if (!/^#[a-fA-F0-9]{3,6}$/.test(color)) {
throw new Error(`${color} isn't a supported hex color`);
}
color = color.substr(1);
const decimal = parseInt(color, 16);
if (color.length === 3) {
const rgb = [decimal>>8, (decimal>>4)&15, decimal&15];
return rgb.map((c) => {
return c + (c<<4);
});
} else {
return [(decimal>>16)&255, (decimal>>8)&255, decimal&255];
}
},
digits: (number, digits) => {
return Math.floor(number*Math.pow(10, digits))/Math.pow(10, digits);
},
normalize: (ll) => {
if (ll.lon < -180) ll.lon += 360;
if (ll.lon > 180) ll.lon -= 360;
if (ll.lat > 85.0511) ll.lat = 85.0511;
if (ll.lat < -85.0511) ll.lat = -85.0511;
return ll;
},
population: (val) => {
let bits = 0;
while (val > 0) {
bits += val & 1;
val >>= 1;
}
return bits;
},
export const clamp = (num, min, max) => {
if (num <= min) {
return min;
} else if (num >= max) {
return max;
} else {
return num;
}
};
module.exports = utils;
export const baseZoom = (zoom) => {
return Math.min(config.tileRange, Math.max(0, Math.floor(zoom)));
};
export const tilesizeAtZoom = (zoom) => {
return config.projectSize * Math.pow(2, zoom-baseZoom(zoom));
};
export const deg2rad = (angle) => {
// (angle / 180) * Math.PI
return angle * 0.017453292519943295;
};
export const ll2tile = (lon, lat, zoom) => {
return {
x: (lon+180)/360*Math.pow(2, zoom),
y: (1-Math.log(Math.tan(lat*Math.PI/180)+1/Math.cos(lat*Math.PI/180))/Math.PI)/2*Math.pow(2, zoom),
z: zoom,
};
};
export const tile2ll = (x, y, zoom) => {
const n = Math.PI - 2*Math.PI*y/Math.pow(2, zoom);
return {
lon: x/Math.pow(2, zoom)*360-180,
lat: 180/Math.PI*Math.atan(0.5*(Math.exp(n)-Math.exp(-n))),
};
};
export const metersPerPixel = (zoom, lat = 0) => {
return (Math.cos(lat * Math.PI/180) * 2 * Math.PI * constants.RADIUS) / (256 * Math.pow(2, zoom));
};
export const hex2rgb = (color) => {
if (typeof color !== 'string') return [255, 0, 0];
if (!/^#[a-fA-F0-9]{3,6}$/.test(color)) {
throw new Error(`${color} isn't a supported hex color`);
}
color = color.substr(1);
const decimal = parseInt(color, 16);
if (color.length === 3) {
const rgb = [decimal>>8, (decimal>>4)&15, decimal&15];
return rgb.map((c) => {
return c + (c<<4);
});
} else {
return [(decimal>>16)&255, (decimal>>8)&255, decimal&255];
}
};
export const digits = (number, digits) => {
return Math.floor(number*Math.pow(10, digits))/Math.pow(10, digits);
};
export const normalize = (ll) => {
if (ll.lon < -180) ll.lon += 360;
if (ll.lon > 180) ll.lon -= 360;
if (ll.lat > 85.0511) ll.lat = 85.0511;
if (ll.lat < -85.0511) ll.lat = -85.0511;
return ll;
};
export const population = (val) => {
let bits = 0;
while (val > 0) {
bits += val & 1;
val >>= 1;
}
return bits;
};

View File

@ -1,5 +1,5 @@
'use strict';
const utils = require('./utils');
import * as utils from './utils.js';
describe('utils', () => {
describe('hex2rgb', () => {