From 82333d67a7cd05a7b3085681923968247d976b90 Mon Sep 17 00:00:00 2001 From: Jin Igarashi Date: Sun, 4 Dec 2022 09:58:15 +0000 Subject: [PATCH] Use global-mercator instead of copied tilebelt code and removed buffer package (#17) --- package-lock.json | 96 ++------------- package.json | 2 +- src/tile/base.ts | 39 +++++- src/tilebelt.ts | 300 ---------------------------------------------- 4 files changed, 49 insertions(+), 388 deletions(-) delete mode 100644 src/tilebelt.ts diff --git a/package-lock.json b/package-lock.json index 652ed92..8948102 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,17 @@ { "name": "@watergis/terrain-rgb", - "version": "1.1.9", + "version": "1.1.11", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@watergis/terrain-rgb", - "version": "1.1.9", + "version": "1.1.11", "license": "MIT", "dependencies": { "@canvas/image-data": "^1.0.0", "axios": "^0.21.1", - "buffer": "^6.0.3", + "global-mercator": "^3.1.0", "pako": "^2.0.4", "webp-hero": "0.0.0-dev.27" }, @@ -1893,25 +1893,6 @@ "node": ">=0.10.0" } }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "/~https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1989,29 +1970,6 @@ "node-int64": "^0.4.0" } }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "/~https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -3864,6 +3822,11 @@ "node": ">= 6" } }, + "node_modules/global-mercator": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/global-mercator/-/global-mercator-3.1.0.tgz", + "integrity": "sha512-Ws/8nyCHDFzpDQjfr6fcbWY+yU/Oh6rk7+WXNr/pZl7O1WCktAnMZy+9wpJ2stNCqM2SWwvZoqWbIzTiFN++2A==" + }, "node_modules/globals": { "version": "13.17.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", @@ -4122,25 +4085,6 @@ "node": ">=0.10.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "/~https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -10047,11 +9991,6 @@ } } }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -10107,15 +10046,6 @@ "node-int64": "^0.4.0" } }, - "buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -11555,6 +11485,11 @@ "is-glob": "^4.0.1" } }, + "global-mercator": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/global-mercator/-/global-mercator-3.1.0.tgz", + "integrity": "sha512-Ws/8nyCHDFzpDQjfr6fcbWY+yU/Oh6rk7+WXNr/pZl7O1WCktAnMZy+9wpJ2stNCqM2SWwvZoqWbIzTiFN++2A==" + }, "globals": { "version": "13.17.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", @@ -11751,11 +11686,6 @@ "safer-buffer": ">= 2.1.2 < 3" } }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" - }, "ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", diff --git a/package.json b/package.json index 60a679e..99cde9b 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "dependencies": { "@canvas/image-data": "^1.0.0", "axios": "^0.21.1", - "buffer": "^6.0.3", + "global-mercator": "^3.1.0", "pako": "^2.0.4", "webp-hero": "0.0.0-dev.27" }, diff --git a/src/tile/base.ts b/src/tile/base.ts index 754b94c..9975ea4 100644 --- a/src/tile/base.ts +++ b/src/tile/base.ts @@ -1,8 +1,7 @@ import axios from 'axios'; import { WebpMachine, loadBinaryData } from 'webp-hero'; -import { Buffer } from 'buffer'; +import { lngLatToGoogle } from 'global-mercator'; import PNG from '../png'; -import * as tilebelt from '../tilebelt'; /** * Abstract class for terrain RGB tiles @@ -37,6 +36,7 @@ abstract class BaseTile { * @returns the value calculated by certain formula */ protected getValue(lnglat: number[], z: number): Promise { + // eslint-disable-next-line no-unused-vars return new Promise((resolve: (value:number)=>void, reject: (reason?: any) => void) => { const lng = lnglat[0]; const lat = lnglat[1]; @@ -46,7 +46,7 @@ abstract class BaseTile { } else if (z < this.minzoom) { zoom = this.minzoom; } - const tile = tilebelt.pointToTile(lng, lat, zoom); + const tile = lngLatToGoogle([lng, lat], zoom); const url: string = this.url .replace(/{x}/g, tile[0].toString()) .replace(/{y}/g, tile[1].toString()) @@ -114,6 +114,7 @@ abstract class BaseTile { lng: number, lat: number, ): Promise { + // eslint-disable-next-line no-unused-vars return new Promise((resolve: (value:number)=>void, reject: (reason?: any) => void) => { const webpMachine = new WebpMachine(); webpMachine.decode(binary).then((dataURI: string) => { @@ -153,7 +154,7 @@ abstract class BaseTile { const rgba = [r, g, b, a]; data.push(rgba); } - const bbox = tilebelt.tileToBBOX(tile); + const bbox = this.tileToBBOX(tile); const pixPos = this.getPixelPosition(lng, lat, bbox); const pos = pixPos[0] + pixPos[1] * this.tileSize; const rgba = data[pos]; @@ -206,6 +207,36 @@ abstract class BaseTile { } return buffer; } + + /** + * Get the bbox of a tile + * + * @name tileToBBOX + * @param {Array} tile + * @returns {Array} bbox + * @example + * var bbox = tileToBBOX([5, 10, 10]) + * //=bbox + */ + private tileToBBOX(tile: number[]): number[] { + const e = this.tile2lon(tile[0] + 1, tile[2]); + const w = this.tile2lon(tile[0], tile[2]); + const s = this.tile2lat(tile[1] + 1, tile[2]); + const n = this.tile2lat(tile[1], tile[2]); + return [w, s, e, n]; + } + + private tile2lon(x: number, z: number): number { + // eslint-disable-next-line no-restricted-properties, no-mixed-operators + return (x / Math.pow(2, z)) * 360 - 180; + } + + private tile2lat(y: number, z: number): number { + const r2d = 180 / Math.PI; + // eslint-disable-next-line no-restricted-properties, no-mixed-operators + const n = Math.PI - (2 * Math.PI * y) / Math.pow(2, z); + return r2d * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))); + } } export default BaseTile; diff --git a/src/tilebelt.ts b/src/tilebelt.ts deleted file mode 100644 index df3f7f1..0000000 --- a/src/tilebelt.ts +++ /dev/null @@ -1,300 +0,0 @@ -const d2r = Math.PI / 180; -const r2d = 180 / Math.PI; - -type Polygon = { - type: string; - coordinates: number[][][]; -} - -/** - * Get the bbox of a tile - * - * @name tileToBBOX - * @param {Array} tile - * @returns {Array} bbox - * @example - * var bbox = tileToBBOX([5, 10, 10]) - * //=bbox - */ -export const tileToBBOX = (tile: number[]): number[]=>{ - const e = tile2lon(tile[0] + 1, tile[2]); - const w = tile2lon(tile[0], tile[2]); - const s = tile2lat(tile[1] + 1, tile[2]); - const n = tile2lat(tile[1], tile[2]); - return [w, s, e, n]; -} - -/** -* Get a geojson representation of a tile -* -* @name tileToGeoJSON -* @param {Array} tile -* @returns {Feature} -* @example -* var poly = tileToGeoJSON([5, 10, 10]) -* //=poly -*/ -export const tileToGeoJSON = (tile: number[]): Polygon=> { - const bbox = tileToBBOX(tile); - const poly : Polygon = { - type: 'Polygon', - coordinates: [[ - [bbox[0], bbox[3]], - [bbox[0], bbox[1]], - [bbox[2], bbox[1]], - [bbox[2], bbox[3]], - [bbox[0], bbox[3]] - ]] - }; - return poly; -} - -const tile2lon = (x: number, z: number): number=> { - return x / Math.pow(2, z) * 360 - 180; -} - -const tile2lat = (y: number, z: number): number =>{ - const n = Math.PI - 2 * Math.PI * y / Math.pow(2, z); - return r2d * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))); -} - -/** -* Get the tile for a point at a specified zoom level -* -* @name pointToTile -* @param {number} lon -* @param {number} lat -* @param {number} z -* @returns {Array} tile -* @example -* var tile = pointToTile(1, 1, 20) -* //=tile -*/ -export const pointToTile = (lon: number, lat: number, z: number): number[]=> { - const tile = pointToTileFraction(lon, lat, z); - tile[0] = Math.floor(tile[0]); - tile[1] = Math.floor(tile[1]); - return tile; -} - -/** -* Get the 4 tiles one zoom level higher -* -* @name getChildren -* @param {Array} tile -* @returns {Array>} tiles -* @example -* var tiles = getChildren([5, 10, 10]) -* //=tiles -*/ -export const getChildren = (tile: number[]): number[][] => { - return [ - [tile[0] * 2, tile[1] * 2, tile[2] + 1], - [tile[0] * 2 + 1, tile[1] * 2, tile[2 ] + 1], - [tile[0] * 2 + 1, tile[1] * 2 + 1, tile[2] + 1], - [tile[0] * 2, tile[1] * 2 + 1, tile[2] + 1] - ]; -} - -/** -* Get the tile one zoom level lower -* -* @name getParent -* @param {Array} tile -* @returns {Array} tile -* @example -* var tile = getParent([5, 10, 10]) -* //=tile -*/ -export const getParent = (tile: number[]): number[] => { - return [tile[0] >> 1, tile[1] >> 1, tile[2] - 1]; -} - -function getSiblings(tile: number[]) { - return getChildren(getParent(tile)); -} - -/** -* Get the 3 sibling tiles for a tile -* -* @name getSiblings -* @param {Array} tile -* @returns {Array>} tiles -* @example -* var tiles = getSiblings([5, 10, 10]) -* //=tiles -*/ -export const hasSiblings = (tile: number[], tiles: number[][]): boolean =>{ - const siblings = getSiblings(tile); - for (let i = 0; i < siblings.length; i++) { - if (!hasTile(tiles, siblings[i])) return false; - } - return true; -} - -/** -* Check to see if an array of tiles contains a particular tile -* -* @name hasTile -* @param {Array>} tiles -* @param {Array} tile -* @returns {boolean} -* @example -* var tiles = [ -* [0, 0, 5], -* [0, 1, 5], -* [1, 1, 5], -* [1, 0, 5] -* ] -* hasTile(tiles, [0, 0, 5]) -* //=boolean -*/ -export const hasTile = (tiles: number[][], tile: number[]): boolean => { - for (let i = 0; i < tiles.length; i++) { - if (tilesEqual(tiles[i], tile)) return true; - } - return false; -} - -/** -* Check to see if two tiles are the same -* -* @name tilesEqual -* @param {Array} tile1 -* @param {Array} tile2 -* @returns {boolean} -* @example -* tilesEqual([0, 1, 5], [0, 0, 5]) -* //=boolean -*/ -export const tilesEqual = (tile1: number[], tile2: number[]): boolean => { - return ( - tile1[0] === tile2[0] && - tile1[1] === tile2[1] && - tile1[2] === tile2[2] - ); -} - -/** -* Get the quadkey for a tile -* -* @name tileToQuadkey -* @param {Array} tile -* @returns {string} quadkey -* @example -* var quadkey = tileToQuadkey([0, 1, 5]) -* //=quadkey -*/ -export const tileToQuadkey = (tile: number[]): string =>{ - let index = ''; - for (let z = tile[2]; z > 0; z--) { - let b = 0; - const mask = 1 << (z - 1); - if ((tile[0] & mask) !== 0) b++; - if ((tile[1] & mask) !== 0) b += 2; - index += b.toString(); - } - return index; -} - -/** -* Get the tile for a quadkey -* -* @name quadkeyToTile -* @param {string} quadkey -* @returns {Array} tile -* @example -* var tile = quadkeyToTile('00001033') -* //=tile -*/ -export const quadkeyToTile = (quadkey: string): number[] =>{ - let x = 0; - let y = 0; - const z = quadkey.length; - - for (let i = z; i > 0; i--) { - const mask = 1 << (i - 1); - const q = +quadkey[z - i]; - if (q === 1) x |= mask; - if (q === 2) y |= mask; - if (q === 3) { - x |= mask; - y |= mask; - } - } - return [x, y, z]; -} - -/** -* Get the smallest tile to cover a bbox -* -* @name bboxToTile -* @param {Array} bbox -* @returns {Array} tile -* @example -* var tile = bboxToTile([ -178, 84, -177, 85 ]) -* //=tile -*/ -export const bboxToTile = (bboxCoords: number[]): number[] => { - const min = pointToTile(bboxCoords[0], bboxCoords[1], 32); - const max = pointToTile(bboxCoords[2], bboxCoords[3], 32); - const bbox = [min[0], min[1], max[0], max[1]]; - - const z = getBboxZoom(bbox); - if (z === 0) return [0, 0, 0]; - const x = bbox[0] >>> (32 - z); - const y = bbox[1] >>> (32 - z); - return [x, y, z]; -} - -const getBboxZoom = (bbox: number[]): number => { - const MAX_ZOOM = 28; - for (let z = 0; z < MAX_ZOOM; z++) { - const mask = 1 << (32 - (z + 1)); - if (((bbox[0] & mask) !== (bbox[2] & mask)) || - ((bbox[1] & mask) !== (bbox[3] & mask))) { - return z; - } - } - - return MAX_ZOOM; -} - -/** -* Get the precise fractional tile location for a point at a zoom level -* -* @name pointToTileFraction -* @param {number} lon -* @param {number} lat -* @param {number} z -* @returns {Array} tile fraction -* var tile = pointToTileFraction(30.5, 50.5, 15) -* //=tile -*/ -export const pointToTileFraction = (lon: number, lat: number, z: number): number[] => { - const sin = Math.sin(lat * d2r); - const z2 = Math.pow(2, z); - let x = z2 * (lon / 360 + 0.5); - const y = z2 * (0.5 - 0.25 * Math.log((1 + sin) / (1 - sin)) / Math.PI); - - // Wrap Tile X - x = x % z2; - if (x < 0) x = x + z2; - return [x, y, z]; -} - -// module.exports = { -// tileToGeoJSON: tileToGeoJSON, -// tileToBBOX: tileToBBOX, -// getChildren: getChildren, -// getParent: getParent, -// getSiblings: getSiblings, -// hasTile: hasTile, -// hasSiblings: hasSiblings, -// tilesEqual: tilesEqual, -// tileToQuadkey: tileToQuadkey, -// quadkeyToTile: quadkeyToTile, -// pointToTile: pointToTile, -// bboxToTile: bboxToTile, -// pointToTileFraction: pointToTileFraction -// }; \ No newline at end of file