Convert to esm module syntax (#606)

* switch to esm module

* Update package.json

* change to maplibre package

* fix tests

* eslint

* remove extra package updates

* Delete package-lock.json

* change 'fs' to 'node:fs'

* put back node 10.

without the package updates this still works

* remove trailing commas

* remove unassociated fix / formatting

* remove unassociated fix

* remove eslint from this PR

* remove unassociated fix

* lint

* Merge remote-tracking branch 'upstream/master' into esm_update

* fix mlgl

* update maplibre-native to new version with arm64

* update minor version
This commit is contained in:
Andrew Calcutt
2022-09-28 14:41:55 -04:00
committed by GitHub
parent 7cfcc413c4
commit b2bd5eaa96
16 changed files with 412 additions and 401 deletions

View File

@@ -1,37 +1,33 @@
'use strict';
const advancedPool = require('advanced-pool');
const fs = require('fs');
const path = require('path');
const url = require('url');
const util = require('util');
const zlib = require('zlib');
// sharp has to be required before node-canvas
// see https://github.com/lovell/sharp/issues/371
const sharp = require('sharp');
const { createCanvas } = require('canvas');
const clone = require('clone');
const Color = require('color');
const express = require('express');
const mercator = new (require('@mapbox/sphericalmercator'))();
const mbgl = require('@maplibre/maplibre-gl-native');
const MBTiles = require('@mapbox/mbtiles');
const proj4 = require('proj4');
const request = require('request');
const utils = require('./utils');
import advancedPool from 'advanced-pool';
import fs from 'node:fs';
import path from 'path';
import url from 'url';
import util from 'util';
import zlib from 'zlib';
import sharp from 'sharp'; // sharp has to be required before node-canvas. see https://github.com/lovell/sharp/issues/371
import pkg from 'canvas';
import clone from 'clone';
import Color from 'color';
import express from 'express';
import SphericalMercator from '@mapbox/sphericalmercator';
import mlgl from '@maplibre/maplibre-gl-native';
import MBTiles from '@mapbox/mbtiles';
import proj4 from 'proj4';
import request from 'request';
import {getFontsPbf, getTileUrls, fixTileJSONCenter} from './utils.js';
const FLOAT_PATTERN = '[+-]?(?:\\d+|\\d+\.?\\d+)';
const httpTester = /^(http(s)?:)?\/\//;
const getScale = scale => (scale || '@1x').slice(1, 2) | 0;
const {createCanvas} = pkg;
const mercator = new SphericalMercator();
const getScale = (scale) => (scale || '@1x').slice(1, 2) | 0;
mbgl.on('message', e => {
mlgl.on('message', (e) => {
if (e.severity === 'WARNING' || e.severity === 'ERROR') {
console.log('mbgl:', e);
console.log('mlgl:', e);
}
});
@@ -54,14 +50,14 @@ const cachedEmptyResponses = {
};
/**
* Create an appropriate mbgl response for http errors.
* Create an appropriate mlgl response for http errors.
* @param {string} format The format (a sharp format or 'pbf').
* @param {string} color The background color (or empty string for transparent).
* @param {Function} callback The mbgl callback.
* @param {Function} callback The mlgl callback.
*/
function createEmptyResponse(format, color, callback) {
if (!format || format === 'pbf') {
callback(null, { data: cachedEmptyResponses[''] });
callback(null, {data: cachedEmptyResponses['']});
return;
}
@@ -75,7 +71,7 @@ function createEmptyResponse(format, color, callback) {
const cacheKey = `${format},${color}`;
const data = cachedEmptyResponses[cacheKey];
if (data) {
callback(null, { data: data });
callback(null, {data: data});
return;
}
@@ -93,7 +89,7 @@ function createEmptyResponse(format, color, callback) {
if (!err) {
cachedEmptyResponses[cacheKey] = buffer;
}
callback(null, { data: buffer });
callback(null, {data: buffer});
});
}
@@ -119,7 +115,7 @@ const extractPathFromQuery = (query, transformer) => {
};
const renderOverlay = (z, x, y, bearing, pitch, w, h, scale,
path, query) => {
path, query) => {
if (!path || path.length < 2) {
return null;
}
@@ -179,14 +175,14 @@ const calcZForBBox = (bbox, w, h, query) => {
const padding = query.padding !== undefined ?
parseFloat(query.padding) : 0.1;
const minCorner = mercator.px([bbox[0], bbox[3]], z),
maxCorner = mercator.px([bbox[2], bbox[1]], z);
const minCorner = mercator.px([bbox[0], bbox[3]], z);
const maxCorner = mercator.px([bbox[2], bbox[1]], z);
const w_ = w / (1 + 2 * padding);
const h_ = h / (1 + 2 * padding);
z -= Math.max(
Math.log((maxCorner[0] - minCorner[0]) / w_),
Math.log((maxCorner[1] - minCorner[1]) / h_)
Math.log((maxCorner[0] - minCorner[0]) / w_),
Math.log((maxCorner[1] - minCorner[1]) / h_)
) / Math.LN2;
z = Math.max(Math.log(Math.max(w, h) / 256) / Math.LN2, Math.min(25, z));
@@ -197,7 +193,7 @@ const calcZForBBox = (bbox, w, h, query) => {
const existingFonts = {};
let maxScaleFactor = 2;
module.exports = {
export const serve_rendered = {
init: (options, repo) => {
const fontListingPromise = new Promise((resolve, reject) => {
fs.readdir(options.paths.fonts, (err, files) => {
@@ -253,9 +249,9 @@ module.exports = {
pool = item.map.renderers_static[scale];
}
pool.acquire((err, renderer) => {
const mbglZ = Math.max(0, z - 1);
const mlglZ = Math.max(0, z - 1);
const params = {
zoom: mbglZ,
zoom: mlglZ,
center: [lon, lat],
bearing: bearing,
pitch: pitch,
@@ -282,9 +278,9 @@ module.exports = {
// Fix semi-transparent outlines on raw, premultiplied input
// https://github.com/maptiler/tileserver-gl/issues/350#issuecomment-477857040
for (var i = 0; i < data.length; i += 4) {
var alpha = data[i + 3];
var norm = alpha / 255;
for (let i = 0; i < data.length; i += 4) {
const alpha = data[i + 3];
const norm = alpha / 255;
if (alpha === 0) {
data[i] = 0;
data[i + 1] = 0;
@@ -321,7 +317,7 @@ module.exports = {
}
if (opt_overlay) {
image.composite([{ input: opt_overlay }]);
image.composite([{input: opt_overlay}]);
}
if (item.watermark) {
const canvas = createCanvas(scale * width, scale * height);
@@ -334,17 +330,17 @@ module.exports = {
ctx.fillStyle = 'rgba(0,0,0,.4)';
ctx.fillText(item.watermark, 5, height - 5);
image.composite([{ input: canvas.toBuffer() }]);
image.composite([{input: canvas.toBuffer()}]);
}
const formatQuality = (options.formatQuality || {})[format];
if (format === 'png') {
image.png({ adaptiveFiltering: false });
image.png({adaptiveFiltering: false});
} else if (format === 'jpeg') {
image.jpeg({ quality: formatQuality || 80 });
image.jpeg({quality: formatQuality || 80});
} else if (format === 'webp') {
image.webp({ quality: formatQuality || 90 });
image.webp({quality: formatQuality || 90});
}
image.toBuffer((err, buffer, info) => {
if (!buffer) {
@@ -367,18 +363,18 @@ module.exports = {
return res.sendStatus(404);
}
const modifiedSince = req.get('if-modified-since'), cc = req.get('cache-control');
const modifiedSince = req.get('if-modified-since'); const cc = req.get('cache-control');
if (modifiedSince && (!cc || cc.indexOf('no-cache') === -1)) {
if (new Date(item.lastModified) <= new Date(modifiedSince)) {
return res.sendStatus(304);
}
}
const z = req.params.z | 0,
x = req.params.x | 0,
y = req.params.y | 0,
scale = getScale(req.params.scale),
format = req.params.format;
const z = req.params.z | 0;
const x = req.params.x | 0;
const y = req.params.y | 0;
const scale = getScale(req.params.scale);
const format = req.params.format;
if (z < 0 || x < 0 || y < 0 ||
z > 22 || x >= Math.pow(2, z) || y >= Math.pow(2, z)) {
return res.status(404).send('Out of bounds');
@@ -397,8 +393,8 @@ module.exports = {
const centerPattern =
util.format(':x(%s),:y(%s),:z(%s)(@:bearing(%s)(,:pitch(%s))?)?',
FLOAT_PATTERN, FLOAT_PATTERN, FLOAT_PATTERN,
FLOAT_PATTERN, FLOAT_PATTERN);
FLOAT_PATTERN, FLOAT_PATTERN, FLOAT_PATTERN,
FLOAT_PATTERN, FLOAT_PATTERN);
app.get(util.format(staticPattern, centerPattern), (req, res, next) => {
const item = repo[req.params.id];
@@ -406,15 +402,15 @@ module.exports = {
return res.sendStatus(404);
}
const raw = req.params.raw;
let z = +req.params.z,
x = +req.params.x,
y = +req.params.y,
bearing = +(req.params.bearing || '0'),
pitch = +(req.params.pitch || '0'),
w = req.params.width | 0,
h = req.params.height | 0,
scale = getScale(req.params.scale),
format = req.params.format;
const z = +req.params.z;
let x = +req.params.x;
let y = +req.params.y;
const bearing = +(req.params.bearing || '0');
const pitch = +(req.params.pitch || '0');
const w = req.params.width | 0;
const h = req.params.height | 0;
const scale = getScale(req.params.scale);
const format = req.params.format;
if (z < 0) {
return res.status(404).send('Invalid zoom');
@@ -441,8 +437,7 @@ module.exports = {
return res.sendStatus(404);
}
const raw = req.params.raw;
const bbox = [+req.params.minx, +req.params.miny,
+req.params.maxx, +req.params.maxy];
const bbox = [+req.params.minx, +req.params.miny, +req.params.maxx, +req.params.maxy];
let center = [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2];
const transformer = raw ?
@@ -458,30 +453,31 @@ module.exports = {
center = transformer(center);
}
const w = req.params.width | 0,
h = req.params.height | 0,
scale = getScale(req.params.scale),
format = req.params.format;
const w = req.params.width | 0;
const h = req.params.height | 0;
const scale = getScale(req.params.scale);
const format = req.params.format;
const z = calcZForBBox(bbox, w, h, req.query),
x = center[0],
y = center[1],
bearing = 0,
pitch = 0;
const z = calcZForBBox(bbox, w, h, req.query);
const x = center[0];
const y = center[1];
const bearing = 0;
const pitch = 0;
const path = extractPathFromQuery(req.query, transformer);
const overlay = renderOverlay(z, x, y, bearing, pitch, w, h, scale, path, req.query);
return respondImage(item, z, x, y, bearing, pitch, w, h, scale, format, res, next, overlay, 'static');
};
const boundsPattern =
util.format(':minx(%s),:miny(%s),:maxx(%s),:maxy(%s)',
FLOAT_PATTERN, FLOAT_PATTERN, FLOAT_PATTERN, FLOAT_PATTERN);
FLOAT_PATTERN, FLOAT_PATTERN, FLOAT_PATTERN, FLOAT_PATTERN);
app.get(util.format(staticPattern, boundsPattern), serveBounds);
app.get('/:id/static/', (req, res, next) => {
for (let key in req.query) {
for (const key in req.query) {
req.query[key.toLowerCase()] = req.query[key];
}
req.params.raw = true;
@@ -510,12 +506,12 @@ module.exports = {
return res.sendStatus(404);
}
const raw = req.params.raw;
const w = req.params.width | 0,
h = req.params.height | 0,
bearing = 0,
pitch = 0,
scale = getScale(req.params.scale),
format = req.params.format;
const w = req.params.width | 0;
const h = req.params.height | 0;
const bearing = 0;
const pitch = 0;
const scale = getScale(req.params.scale);
const format = req.params.format;
const transformer = raw ?
mercator.inverse.bind(mercator) : item.dataProjWGStoInternalWGS;
@@ -535,12 +531,12 @@ module.exports = {
const bbox_ = mercator.convert(bbox, '900913');
const center = mercator.inverse(
[(bbox_[0] + bbox_[2]) / 2, (bbox_[1] + bbox_[3]) / 2]
[(bbox_[0] + bbox_[2]) / 2, (bbox_[1] + bbox_[3]) / 2]
);
const z = calcZForBBox(bbox, w, h, req.query),
x = center[0],
y = center[1];
const z = calcZForBBox(bbox, w, h, req.query);
const x = center[0];
const y = center[1];
const overlay = renderOverlay(z, x, y, bearing, pitch, w, h, scale, path, req.query);
@@ -554,8 +550,8 @@ module.exports = {
return res.sendStatus(404);
}
const info = clone(item.tileJSON);
info.tiles = utils.getTileUrls(req, info.tiles,
`styles/${req.params.id}`, info.format, item.publicUrl);
info.tiles = getTileUrls(req, info.tiles,
`styles/${req.params.id}`, info.format, item.publicUrl);
return res.send(info);
});
@@ -571,38 +567,38 @@ module.exports = {
let styleJSON;
const createPool = (ratio, mode, min, max) => {
const createRenderer = (ratio, createCallback) => {
const renderer = new mbgl.Map({
const renderer = new mlgl.Map({
mode: mode,
ratio: ratio,
request: (req, callback) => {
const protocol = req.url.split(':')[0];
//console.log('Handling request:', req);
// console.log('Handling request:', req);
if (protocol === 'sprites') {
const dir = options.paths[protocol];
const file = unescape(req.url).substring(protocol.length + 3);
fs.readFile(path.join(dir, file), (err, data) => {
callback(err, { data: data });
callback(err, {data: data});
});
} else if (protocol === 'fonts') {
const parts = req.url.split('/');
const fontstack = unescape(parts[2]);
const range = parts[3].split('.')[0];
utils.getFontsPbf(
null, options.paths[protocol], fontstack, range, existingFonts
).then(concated => {
callback(null, { data: concated });
}, err => {
callback(err, { data: null });
getFontsPbf(
null, options.paths[protocol], fontstack, range, existingFonts
).then((concated) => {
callback(null, {data: concated});
}, (err) => {
callback(err, {data: null});
});
} else if (protocol === 'mbtiles') {
const parts = req.url.split('/');
const sourceId = parts[2];
const source = map.sources[sourceId];
const sourceInfo = styleJSON.sources[sourceId];
const z = parts[3] | 0,
x = parts[4] | 0,
y = parts[5].split('.')[0] | 0,
format = parts[5].split('.')[1];
const z = parts[3] | 0;
const x = parts[4] | 0;
const y = parts[5].split('.')[0] | 0;
const format = parts[5].split('.')[1];
source.getTile(z, x, y, (err, data, headers) => {
if (err) {
if (options.verbose) console.log('MBTiles error, serving empty', err);
@@ -619,11 +615,11 @@ module.exports = {
try {
response.data = zlib.unzipSync(data);
} catch (err) {
console.log("Skipping incorrect header for tile mbtiles://%s/%s/%s/%s.pbf", id, z, x, y);
console.log('Skipping incorrect header for tile mbtiles://%s/%s/%s/%s.pbf', id, z, x, y);
}
if (options.dataDecoratorFunc) {
response.data = options.dataDecoratorFunc(
sourceId, 'data', response.data, z, x, y);
sourceId, 'data', response.data, z, x, y);
}
} else {
response.data = data;
@@ -670,7 +666,7 @@ module.exports = {
min: min,
max: max,
create: createRenderer.bind(null, ratio),
destroy: renderer => {
destroy: (renderer) => {
renderer.release();
}
});
@@ -688,8 +684,8 @@ module.exports = {
if (styleJSON.sprite && !httpTester.test(styleJSON.sprite)) {
styleJSON.sprite = 'sprites://' +
styleJSON.sprite
.replace('{style}', path.basename(styleFile, '.json'))
.replace('{styleJsonFolder}', path.relative(options.paths.sprites, path.dirname(styleJSONPath)));
.replace('{style}', path.basename(styleFile, '.json'))
.replace('{styleJsonFolder}', path.relative(options.paths.sprites, path.dirname(styleJSONPath)));
}
if (styleJSON.glyphs && !httpTester.test(styleJSON.glyphs)) {
styleJSON.glyphs = `fonts://${styleJSON.glyphs}`;
@@ -720,9 +716,9 @@ module.exports = {
const attributionOverride = params.tilejson && params.tilejson.attribution;
Object.assign(tileJSON, params.tilejson || {});
tileJSON.tiles = params.domains || options.domains;
utils.fixTileJSONCenter(tileJSON);
fixTileJSONCenter(tileJSON);
repo[id] = {
const repoobj = {
tileJSON,
publicUrl,
map,
@@ -730,6 +726,7 @@ module.exports = {
lastModified: new Date().toUTCString(),
watermark: params.watermark || options.watermark
};
repo[id] = repoobj;
const queue = [];
for (const name of Object.keys(styleJSON.sources)) {
@@ -770,11 +767,11 @@ module.exports = {
return;
}
if (!repo[id].dataProjWGStoInternalWGS && info.proj4) {
if (!repoobj.dataProjWGStoInternalWGS && info.proj4) {
// how to do this for multiple sources with different proj4 defs?
const to3857 = proj4('EPSG:3857');
const toDataProj = proj4(info.proj4);
repo[id].dataProjWGStoInternalWGS = xy => to3857.inverse(toDataProj.forward(xy));
repoobj.dataProjWGStoInternalWGS = (xy) => to3857.inverse(toDataProj.forward(xy));
}
const type = source.type;
@@ -823,15 +820,15 @@ module.exports = {
return Promise.all([renderersReadyPromise]);
},
remove: (repo, id) => {
let item = repo[id];
const item = repo[id];
if (item) {
item.map.renderers.forEach(pool => {
item.map.renderers.forEach((pool) => {
pool.close();
});
item.map.renderers_static.forEach(pool => {
item.map.renderers_static.forEach((pool) => {
pool.close();
});
}
delete repo[id];
},
}
};