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,16 +1,17 @@
module.exports = { module.exports = {
"env": { "env": {
"es6": true,
"node": true, "node": true,
"es2021": true,
"jest": true "jest": true
}, },
"parserOptions": {
"ecmaVersion": 2018
},
"extends": [ "extends": [
"eslint:recommended", "eslint:recommended",
"plugin:jest/recommended" "plugin:jest/recommended"
], ],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"rules": { "rules": {
"indent": [ "indent": [
"error", "error",
@ -33,4 +34,4 @@ module.exports = {
"always" "always"
] ]
} }
}; }

10
main.js
View File

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

9533
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -3,10 +3,11 @@
"version": "0.3.1", "version": "0.3.1",
"description": "MapSCII is a Braille & ASCII world map renderer for your console, based on OpenStreetMap", "description": "MapSCII is a Braille & ASCII world map renderer for your console, based on OpenStreetMap",
"main": "main.js", "main": "main.js",
"type": "module",
"scripts": { "scripts": {
"lint": "eslint src", "lint": "eslint src",
"start": "node main", "start": "node main",
"test": "jest" "test": "NODE_OPTIONS=--experimental-vm-modules jest"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -32,21 +33,21 @@
"dependencies": { "dependencies": {
"@mapbox/vector-tile": "^1.3.1", "@mapbox/vector-tile": "^1.3.1",
"bresenham": "0.0.4", "bresenham": "0.0.4",
"earcut": "^2.2.2", "earcut": "^2.2.3",
"env-paths": "^2.2.0", "env-paths": "^3.0.0",
"keypress": "^0.2.1", "keypress": "^0.2.1",
"node-fetch": "^2.6.1", "node-fetch": "^3.2.4",
"pbf": "^3.2.1", "pbf": "^3.2.1",
"rbush": "^3.0.1", "rbush": "^3.0.1",
"simplify-js": "^1.2.4", "simplify-js": "^1.2.4",
"string-width": "^4.2.0", "string-width": "^5.1.2",
"term-mouse": "^0.2.2", "term-mouse": "^0.2.2",
"x256": "0.0.2", "x256": "0.0.2",
"yargs": "^15.4.1" "yargs": "^17.4.1"
}, },
"devDependencies": { "devDependencies": {
"eslint": "^7.8.1", "eslint": "^8.14.0",
"eslint-plugin-jest": "^24.0.0", "eslint-plugin-jest": "^26.1.5",
"jest": "^26.4.2" "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 Will either be merged into node-drawille or become an own module at some point
*/ */
'use strict'; import stringWidth from 'string-width';
const stringWidth = require('string-width'); import config from './config.js';
const config = require('./config'); import * as utils from './utils.js';
const utils = require('./utils');
const asciiMap = { const asciiMap = {
// '▬': [2+32, 4+64], // '▬': [2+32, 4+64],
@ -29,7 +28,7 @@ const asciiMap = {
}; };
const termReset = '\x1B[39;49m'; const termReset = '\x1B[39;49m';
class BrailleBuffer { export default class BrailleBuffer {
constructor(width, height) { constructor(width, height) {
this.brailleMap = [[0x1, 0x8],[0x2, 0x10],[0x4, 0x20],[0x40, 0x80]]; 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 Will most likely be turned into a stand alone module at some point
*/ */
'use strict'; 'use strict';
const bresenham = require('bresenham'); import bresenham from 'bresenham';
const earcut = require('earcut'); import earcut from 'earcut';
const BrailleBuffer = require('./BrailleBuffer'); import BrailleBuffer from './BrailleBuffer.js';
class Canvas { export default class Canvas {
constructor(width, height) { constructor(width, height) {
this.width = width; this.width = width;
this.height = height; this.height = height;
@ -198,5 +198,3 @@ class Canvas {
} }
Canvas.prototype.stack = []; Canvas.prototype.stack = [];
module.exports = Canvas;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
'use strict'; 'use strict';
const TileSource = require('./TileSource'); import TileSource from './TileSource.js';
describe('TileSource', () => { describe('TileSource', () => {
describe('with a HTTP source', () => { 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', language: 'en',
// TODO: adapt to osm2vectortiles successor openmaptiles v3) // TODO: adapt to osm2vectortiles successor openmaptiles v3)

View File

@ -4,15 +4,13 @@
methods used all around methods used all around
*/ */
'use strict'; import config from './config.js';
const config = require('./config');
const constants = { const constants = {
RADIUS: 6378137, RADIUS: 6378137,
}; };
const utils = { export const clamp = (num, min, max) => {
clamp: (num, min, max) => {
if (num <= min) { if (num <= min) {
return min; return min;
} else if (num >= max) { } else if (num >= max) {
@ -20,43 +18,43 @@ const utils = {
} else { } else {
return num; return num;
} }
}, };
baseZoom: (zoom) => { export const baseZoom = (zoom) => {
return Math.min(config.tileRange, Math.max(0, Math.floor(zoom))); return Math.min(config.tileRange, Math.max(0, Math.floor(zoom)));
}, };
tilesizeAtZoom: (zoom) => { export const tilesizeAtZoom = (zoom) => {
return config.projectSize * Math.pow(2, zoom-utils.baseZoom(zoom)); return config.projectSize * Math.pow(2, zoom-baseZoom(zoom));
}, };
deg2rad: (angle) => { export const deg2rad = (angle) => {
// (angle / 180) * Math.PI // (angle / 180) * Math.PI
return angle * 0.017453292519943295; return angle * 0.017453292519943295;
}, };
ll2tile: (lon, lat, zoom) => { export const ll2tile = (lon, lat, zoom) => {
return { return {
x: (lon+180)/360*Math.pow(2, zoom), 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), 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, z: zoom,
}; };
}, };
tile2ll: (x, y, zoom) => { export const tile2ll = (x, y, zoom) => {
const n = Math.PI - 2*Math.PI*y/Math.pow(2, zoom); const n = Math.PI - 2*Math.PI*y/Math.pow(2, zoom);
return { return {
lon: x/Math.pow(2, zoom)*360-180, lon: x/Math.pow(2, zoom)*360-180,
lat: 180/Math.PI*Math.atan(0.5*(Math.exp(n)-Math.exp(-n))), lat: 180/Math.PI*Math.atan(0.5*(Math.exp(n)-Math.exp(-n))),
}; };
}, };
metersPerPixel: (zoom, lat = 0) => { export const metersPerPixel = (zoom, lat = 0) => {
return (Math.cos(lat * Math.PI/180) * 2 * Math.PI * constants.RADIUS) / (256 * Math.pow(2, zoom)); return (Math.cos(lat * Math.PI/180) * 2 * Math.PI * constants.RADIUS) / (256 * Math.pow(2, zoom));
}, };
hex2rgb: (color) => { export const hex2rgb = (color) => {
if (typeof color !== 'string') return [255, 0, 0]; if (typeof color !== 'string') return [255, 0, 0];
if (!/^#[a-fA-F0-9]{3,6}$/.test(color)) { if (!/^#[a-fA-F0-9]{3,6}$/.test(color)) {
@ -74,13 +72,13 @@ const utils = {
} else { } else {
return [(decimal>>16)&255, (decimal>>8)&255, decimal&255]; return [(decimal>>16)&255, (decimal>>8)&255, decimal&255];
} }
}, };
digits: (number, digits) => { export const digits = (number, digits) => {
return Math.floor(number*Math.pow(10, digits))/Math.pow(10, digits); return Math.floor(number*Math.pow(10, digits))/Math.pow(10, digits);
}, };
normalize: (ll) => { export const normalize = (ll) => {
if (ll.lon < -180) ll.lon += 360; if (ll.lon < -180) ll.lon += 360;
if (ll.lon > 180) ll.lon -= 360; if (ll.lon > 180) ll.lon -= 360;
@ -88,16 +86,13 @@ const utils = {
if (ll.lat < -85.0511) ll.lat = -85.0511; if (ll.lat < -85.0511) ll.lat = -85.0511;
return ll; return ll;
}, };
population: (val) => { export const population = (val) => {
let bits = 0; let bits = 0;
while (val > 0) { while (val > 0) {
bits += val & 1; bits += val & 1;
val >>= 1; val >>= 1;
} }
return bits; return bits;
},
}; };
module.exports = utils;

View File

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