global-mercator
Version:
883 lines (801 loc) • 24.6 kB
text/typescript
import * as Debug from 'debug'
import { range, isUndefined, keys } from 'lodash'
export const debug = {
error: Debug('global-mercator:error'),
log: Debug('global-mercator:log'),
warning: Debug('global-mercator:warning'),
}
export interface InterfaceMeters {
mx: number
my: number
zoom?: number
}
export interface InterfacePixels {
px: number
py: number
zoom: number
}
export interface InterfaceLatLng {
lat: number
lng: number
zoom?: number
z?: number
}
export interface InterfaceGoogle {
x: number
y: number
zoom: number
}
export interface InterfaceTile {
tx: number
ty: number
zoom: number
}
/**
* Converts {@link LatLng} coordinates to {@link Meters} coordinates.
*
* @name latLngToMeters
* @param {number} lat Latitude (Parallels) in decimal degrees
* @param {number} lng Longitude (Meridians) in decimal degrees
* @param {number} [zoom] Zoom level
* @returns {Object} Meters coordinates
* @example
* latLngToMeters({lat: 37, lng: 126})
* //= Meters { mx: 14026255.83995247, my: 4439106.787250587 }
*/
export function latLngToMeters(init: InterfaceLatLng) {
return mercator.latLngToMeters(init)
}
/**
* Converts {@link Meters} coordinates to {@link LatLng} coordinates.
*
* @name metersToLatLng
* @param {number} mx Longitudes (Meridians) in meters
* @param {number} my Latitudes (Parallels) in decimal degrees
* @param {number} [zoom] Zoom level
* @returns {Object} LatLng coordinates
* @example
* metersToLatLng({ mx: 14026255, my: 4439106 })
* //= LatLng { lat: 36.99999435205559, lng: 125.99999245457859 }
*/
export function metersToLatLng(init: InterfaceMeters) {
return mercator.metersToLatLng(init)
}
/**
* Converts {@link Meters} coordinates to {@link Pixels} coordinates.
*
* @name metersToPixels
* @param {number} mx Longitudes (Meridians) in meters
* @param {number} my Latitudes (Parallels) in decimal degrees
* @param {number} zoom Zoom level
* @returns {Object} Pixels coordinates
* @example
* metersToPixels({ mx: 14026255, my: 4439106, zoom: 13 })
* //= Pixels { px: 1782579.1560447346, py: 1280877.3387406059, zoom: 13 }
*/
export function metersToPixels(init: InterfaceMeters) {
return mercator.metersToPixels(init)
}
/**
* Converts {@link LatLng} coordinates to TMS {@link Tile}.
*
* @name latLngToTile
* @param {number} lat Latitude (Parallels) in decimal degrees
* @param {number} lng Longitude (Meridians) in decimal degrees
* @param {number} zoom Zoom level
* @returns {Object} TMS Tile
* @example
* latLngToTile({lat: 37, lng: 126, zoom: 13 })
* //= Tile { tx: 6963, ty: 5003, zoom: 13 }
*/
export function latLngToTile(init: InterfaceLatLng) {
return mercator.latLngToTile(init)
}
/**
* Converts {@link LatLng} coordinates to {@link Google} (XYZ) Tile.
*
* @name latLngToGoogle
* @param {number} lat Latitude (Parallels) in decimal degrees
* @param {number} lng Longitude (Meridians) in decimal degrees
* @returns {Object} Google (XYZ) Tile
* @example
* latLngToGoogle({lat: 37, lng: 126, zoom: 13 })
* //= Google { x: 6963, y: 3188, zoom: 13 }
*/
export function latLngToGoogle(init: InterfaceLatLng) {
return mercator.latLngToGoogle(init)
}
/**
* Converts {@link Meters} coordinates to TMS {@link Tile}.
*
* @name metersToTile
* @param {number} mx Longitudes (Meridians) in meters
* @param {number} my Latitudes (Parallels) in decimal degrees
* @param {number} [zoom] Zoom level
* @returns {Object} TMS Tile
* @example
* metersToTile({ mx: 14026255, my: 4439106, zoom: 13 })
* //= Tile { tx: 6963, ty: 5003, zoom: 13 }
*/
export function metersToTile(init: Meters) {
return mercator.metersToTile(init)
}
/**
* Converts {@link Pixels} coordinates to {@link Meters} coordinates.
*
* @name pixelsToMeters
* @param {number} px Pixels X
* @param {number} py Pixels Y
* @param {number} zoom Zoom level
* @returns {Object} Meters coordinates
* @example
* pixelsToMeters({ px: 1782579, py: 1280877, zoom: 13 })
* //= Meters { mx: 14026252.018101055, my: 4439099.526918683, zoom: 13 }
*/
export function pixelsToMeters(init: Pixels) {
return mercator.pixelsToMeters(init)
}
/**
* Converts {@link Pixels} coordinates to TMS {@link Tile}.
*
* @name pixelsToTile
* @param {number} px Pixels X
* @param {number} py Pixels Y
* @param {number} zoom Zoom level
* @returns {Object} TMS Tile
* @example
* pixelsToTile({ px: 1782579, py: 1280877, zoom: 13 })
* //= Tile { tx: 6963, ty: 5003, zoom: 13 }
*/
export function pixelsToTile(init: Pixels) {
return mercator.pixelsToTile(init)
}
/**
* Converts TMS {@link Tile} to {@link bbox} in {@link Meters} coordinates.
*
* @name tileBbox
* @param {number} tx TMS Tile X
* @param {number} ty TMS Tile Y
* @param {number} zoom Zoom level
* @returns {Array<number>} bbox extent in [minX, minY, maxX, maxY] order
* @example
* tileBbox({ tx: 6963, ty: 5003, zoom: 13 })
* //= [ 14025277.445990417, 4437016.617897913, 14030169.415800672, 4441908.587708164 ]
*/
export function tileBbox(init: Tile) {
return mercator.tileBbox(init)
}
/**
* Converts TMS {@link Tile} to {@link bbox} in {@link LatLng} coordinates.
*
* @name tileLatLngBbox
* @param {number} tx TMS Tile X
* @param {number} ty TMS Tile Y
* @param {number} zoom Zoom level
* @returns {Array<number>} bbox extent in [minX, minY, maxX, maxY] order
* @example
* tileLatLngBbox({ tx: 6963, ty: 5003, zoom: 13 })
* //= [ 125.99121093749999, 36.98500309285596, 126.03515625, 37.020098201368135 ]
*/
export function tileLatLngBbox(init: Tile) {
return mercator.tileLatLngBbox(init)
}
/**
* Converts {@link Google} (XYZ) Tile to {@link bbox} in {@link Meters} coordinates.
*
* @name googleBbox
* @param {number} x Google (XYZ) Tile X
* @param {number} y Google (XYZ) Tile Y
* @param {number} zoom Zoom level
* @returns {Array<number>} bbox extent in [minX, minY, maxX, maxY] order
* @example
* googleBbox({ x: 6963, y: 3188, zoom: 13 })
* //= [ 14025277.445990417, 4437016.617897913, 14030169.415800672, 4441908.587708164 ]
*/
export function googleBbox(init: Google) {
return mercator.googleBbox(init)
}
/**
* Converts {@link Google} (XYZ) Tile to {@link bbox} in {@link LatLng} coordinates.
*
* @name googleLatLngBbox
* @param {number} x Google (XYZ) Tile X
* @param {number} y Google (XYZ) Tile Y
* @param {number} zoom Zoom level
* @returns {Array<number>} bbox extent in [minX, minY, maxX, maxY] order
* @example
* googleLatLngBbox({ x: 6963, y: 3188, zoom: 13 })
* //= [ 125.99121093749999, 36.98500309285596, 126.03515625, 37.020098201368135 ]
*/
export function googleLatLngBbox(init: Google) {
return mercator.googleLatLngBbox(init)
}
/**
* Converts TMS {@link Tile} to {@link Google} (XYZ) Tile.
*
* @name tileGoogle
* @param {number} tx TMS Tile X
* @param {number} ty TMS Tile Y
* @param {number} zoom Zoom level
* @returns {Array<number>} bbox extent in [minX, minY, maxX, maxY] order
* @example
* tileGoogle({ tx: 6963, ty: 5003, zoom: 13 })
* //= Google { x: 6963, y: 3188, zoom: 13 }
*/
export function tileGoogle(init: Tile) {
return mercator.tileGoogle(init)
}
/**
* Converts {@link Google} (XYZ) Tile to TMS {@link Tile}.
*
* @name googleTile
* @param {number} x Google (XYZ) Tile X
* @param {number} y Google (XYZ) Tile Y
* @param {number} zoom Zoom level
* @returns {Object} TMS Tile
* @example
* googleTile({ x: 6963, y: 3188, zoom: 13 })
* //= Tile { tx: 6963, ty: 5003, zoom: 13 }
*/
export function googleTile(init: Google) {
return mercator.googleTile(init)
}
/**
* Converts {@link Google} (XYZ) Tile to {@link Quadkey}.
*
* @name googleQuadkey
* @param {number} x Google (XYZ) Tile X
* @param {number} y Google (XYZ) Tile Y
* @param {number} zoom Zoom level
* @returns {string} Microsoft's Quadkey schema
* @example
* googleQuadkey({ x: 6963, y: 3188, zoom: 13 })
* //= '1321102330211'
*/
export function googleQuadkey(init: Google) {
return mercator.googleQuadkey(init)
}
/**
* Converts TMS {@link Tile} to {@link QuadKey}.
*
* @name tileQuadkey
* @param {number} tx TMS Tile X
* @param {number} ty TMS Tile Y
* @param {number} zoom Zoom level
* @returns {string} Microsoft's Quadkey schema
* @example
* tileQuadkey({ tx: 6963, ty: 5003, zoom: 13 })
* //= '1321102330211'
*/
export function tileQuadkey(init: Tile) {
return mercator.tileQuadkey(init)
}
/**
* Converts {@link Quadkey} to TMS {@link Tile}.
*
* @name quadkeyTile
* @param {string} quadkey Microsoft's Quadkey schema
* @returns {Object} TMS Tile
* @example
* quadkeyTile('1321102330211')
* //= Tile { tx: 6963, ty: 5003, zoom: 13 }
*/
export function quadkeyTile(quadkey: string) {
return mercator.quadkeyTile(quadkey)
}
/**
* Converts {@link Quadkey} to {@link Google} (XYZ) Tile.
*
* @name quadkeyGoogle
* @param {string} quadkey Microsoft's Quadkey schema
* @returns {Object} Google (XYZ) Tile
* @example
* quadkeyGoogle('1321102330211')
* //= Google { x: 6963, y: 3188, zoom: 13 }
*/
export function quadkeyGoogle(quadkey: string) {
return mercator.quadkeyGoogle(quadkey)
}
/**
* Converts {@link bbox} from {@link LatLng} coordinates to {@link Meters} coordinates
*
* @name bboxLatLngToMeters
* @param {Array<number>} bbox extent in [minX, minY, maxX, maxY] order
* @returns {Array<number>} bbox extent in [minX, minY, maxX, maxY] order
* @example
* bboxLatLngToMeters([ 125, 35, 127, 37 ])
* //= [ 13914936.349159198, 4163881.1440642904, 14137575.330745745, 4439106.787250587 ]
*/
export function bboxLatLngToMeters(bbox: number[]): number[] {
return mercator.bboxLatLngToMeters(bbox)
}
/**
* Validates TMS {@link Tile}.
*
* @name validateTile
* @param {number} tx TMS Tile X
* @param {number} ty TMS Tile Y
* @param {number} zoom Zoom level
* @param {string} [name=Tile] - name used for debugging message
* @throws Will throw an error if TMS Tile is not valid.
* @returns {Object} TMS Tile
* @example
* validateTile({tx: 60, ty: 80, zoom: 5})
* //= {tx: 60, ty: 80, zoom: 5}
* validateTile({tx: 60, ty: -43, zoom: 5})
* //= Error: Tile <ty> must not be less than 0
*/
export function validateTile(init: InterfaceTile, name = 'Tile') {
const { tx, ty, zoom } = init
validateZoom(zoom, 'Tile')
if (tx < 0) {
const message = `${ name } <tx> must not be less than 0`
debug.error(message)
throw new Error(message)
} else if (ty < 0) {
const message = `${ name } <ty> must not be less than 0`
debug.error(message)
throw new Error(message)
}
return init
}
/**
* Validates {@link Zoom} level.
*
* @name validateZoom
* @param {number} zoom Zoom level
* @throws Will throw an error if zoom is not valid.
* @returns {number} zoom Zoom level
* @example
* validateZoom(12)
* //= 12
* validateZoom(-4)
* //= Error: <zoom> cannot be less than 1
* validateZoom(30)
* //= Error: <zoom> cannot be greater than 23
*/
export function validateZoom(zoom: number, name?: string) {
if (zoom < 1) {
const message = (name) ? `${ name } <zoom> cannot be less than 1` : '<zoom> cannot be less than 1'
debug.error(message)
throw new Error(message)
} else if (zoom > 23) {
const message = (name) ? `${ name } <zoom> cannot be greater than 23` : '<zoom> cannot be greater than 23'
debug.error(message)
throw new Error(message)
}
return zoom
}
/**
* Validates {@link Pixels} coordinates.
*
* @name validatePixels
* @param {Array<number>} Pixels coordinates
* @throws Will throw an error if Pixels is not valid.
* @returns {Array<number>} Pixels coordinates
* @example
* validatePixels([-115, 44])
* //= [-115, 44]
*/
export function validatePixels(init: number[]) {
if (init.length < 2 || init.length >= 3) {
const message = 'Pixels must be an Array of 2 numbers'
debug.error(message)
throw new Error(message)
}
let [px, py] = init
if (px % 1 !== 0) {
px = px - px % 1
const message = `Pixels [px] has been modified to ${ px }`
debug.warning(message)
}
if (py % 1 !== 0) {
py = py - py % 1
const message = `Pixels [py] has been modified to ${ py }`
debug.warning(message)
}
return [px, py]
}
/**
* Validates {@link Meters} coordinates.
*
* @name validateMeters
* @param {Array<number>} Meters coordinates
* @throws Will throw an error if Meters is not valid.
* @returns {Array<number>} Meters coordinates
* @example
* validateMeters([-115, 44])
* //= [-115, 44]
*/
export function validateMeters(init: number[]) {
if (init.length < 2 || init.length >= 3) {
const message = 'Meters must be an Array of 2 numbers'
debug.error(message)
throw new Error(message)
}
const max = 20037508.342789244
const min = -20037508.342789244
let [mx, my] = init
if (my > max) {
const message = `Meters [my] cannot be greater than ${ max }`
debug.error(message)
throw new Error(message)
}
if (my < min) {
const message = `Meters [my] cannot be less than ${ min }`
debug.error(message)
throw new Error(message)
}
if (mx > max) {
const message = `Meters [mx] cannot be greater than ${ max }`
debug.error(message)
throw new Error(message)
}
if (mx < min) {
const message = `Meters [mx] cannot be less than ${ min }`
debug.error(message)
throw new Error(message)
}
return [mx, my]
}
/**
* Validates {@link LatLng} coordinates.
*
* @name validateLatLng
* @param {Array<number>} LatLng coordinates
* @throws Will throw an error if LatLng is not valid.
* @returns {Array<number>} LatLng coordinates
* @example
* validateLatLng([-115, 44])
* //= [-115, 44]
*/
export function validateLatLng(init: number[]) {
const [lng, lat] = validateLngLat([init[1], init[0]])
return [lat, lng]
}
/**
* Validates {@link LngLat} coordinates.
*
* @name validateLngLat
* @param {Array<number>} LngLat coordinates
* @throws Will throw an error if LngLat is not valid.
* @returns {Array<number>} LngLat coordinates
* @example
* validateLngLat([-115, 44])
* //= [-115, 44]
*/
export function validateLngLat(init: number[]) {
if (init.length < 2 || init.length >= 3) {
const message = 'LatLng must be an Array of 2 numbers'
debug.error(message)
throw new Error(message)
}
let [lng, lat] = init
if (lat < -90 || lat > 90) {
const message = 'LatLng [lat] must be within -90 to 90 degrees'
debug.error(message)
throw new Error(message)
} else if (lng < -180 || lng > 180) {
const message = 'LatLng [lng] must be within -180 to 180 degrees'
debug.error(message)
throw new Error(message)
}
if (lat > 85) {
const message = 'LatLng [lat] has been modified to 85'
debug.warning(message)
lat = 85
}
if (lat < -85) {
const message = 'LatLng [lat] has been modified to -85'
debug.warning(message)
lat = -85
}
return [lng, lat]
}
/**
* Validates {@link bbox}.
*
* @name validateBbox
* @param {Array<number>} bbox extent in [minX, minY, maxX, maxY] order
* @throws Will throw an error if bbox is not valid.
* @returns {Array<number>} bbox extent in [minX, minY, maxX, maxY] order
* @example
* validateBbox([ -75, 44, -74, 45 ])
* //= [ -75, 44, -74, 45 ]
* validateBbox([ -75, 44, -74 ])
* //= Error: [bbox] must be an Array of 4 numbers
*/
export function validateBbox(init: number[]) {
if (init.length !== 4) {
const message = '[bbox] must be an Array of 4 numbers'
debug.error(message)
throw new Error(message)
}
return [...init]
}
/**
* Assert undefined items within object.
*
* @name assertUndefined
* @param {Object} items
* @param {string} [name] - name used for debugging message
* @throws Will throw an error if any item in Object is undefined.
* @returns {Object} items
* @example
* assertUndefined({foo: 'bar'})
* //= {foo: 'bar'}
* assertUndefined({foo: undefined})
* //= Error: <foo> is required
*/
export function assertUndefined(items: any, name?: string) {
for (let key of keys(items)) {
if (isUndefined(items[key])) {
const message = (name) ? `${ name } <${ key }> is required` : `<${ key }> is required`
debug.error(message)
throw new Error(message)
}
}
return items
}
export class Google {
public x: number
public y: number
public zoom: number
constructor(init: InterfaceGoogle) {
const {x, y, zoom} = init
this.x = x
this.y = y
this.zoom = zoom
assertUndefined(this, 'Google')
}
}
export class Tile {
public tx: number
public ty: number
public zoom: number
constructor(init: InterfaceTile) {
const {tx, ty, zoom} = init
this.tx = tx
this.ty = ty
this.zoom = zoom
assertUndefined(this, 'Tile')
validateTile(this)
}
}
export class Pixels {
public px: number
public py: number
public zoom: number
constructor(init: InterfacePixels) {
const {px, py, zoom} = init
this.px = px
this.py = py
if (!isUndefined(zoom)) { this.zoom = zoom }
assertUndefined(this, 'Pixels')
validatePixels([px, py])
}
}
export class Meters {
public mx: number
public my: number
public zoom: number
constructor(init: InterfaceMeters) {
const {mx, my, zoom} = init
this.mx = mx
this.my = my
if (!isUndefined(zoom)) { this.zoom = zoom }
assertUndefined(this, 'Meters')
validateMeters([mx, my])
}
}
export class LatLng {
public lat: number
public lng: number
public zoom: number
constructor(init: InterfaceLatLng) {
const {lng, lat} = init
this.lat = lat
this.lng = lng
if (!isUndefined(init.zoom)) { this.zoom = init.zoom }
assertUndefined(this, 'LatLng')
validateLatLng([lat, lng])
}
}
class GlobalMercator {
public name: string = 'GlobalMercator'
private TileSize: number
private initialResolution: number
private originShift: number
constructor(TileSize: number = 256) {
this.TileSize = TileSize
this.initialResolution = 2 * Math.PI * 6378137 / this.TileSize
this.originShift = 2 * Math.PI * 6378137 / 2.0
}
public Resolution(zoom: number) {
if (isUndefined(zoom)) {
const message = '<zoom> is required'
debug.error(message)
throw new Error(message)
}
return this.initialResolution / Math.pow(2, zoom)
}
public latLngToMeters(init: InterfaceLatLng) {
const { lat, lng, zoom } = new LatLng(init)
let mx: number = lng * this.originShift / 180.0
let my: number = Math.log(Math.tan((90 + lat) * Math.PI / 360.0 )) / (Math.PI / 180.0)
my = my * this.originShift / 180.0
return new Meters({ mx, my, zoom })
}
public metersToLatLng(init: InterfaceMeters) {
const { mx, my, zoom } = new Meters(init)
let lng = (mx / this.originShift) * 180.0
let lat = (my / this.originShift) * 180.0
lat = 180 / Math.PI * (2 * Math.atan( Math.exp( lat * Math.PI / 180.0)) - Math.PI / 2.0)
return new LatLng({ lat, lng, zoom })
}
public metersToPixels(init: InterfaceMeters) {
const { mx, my, zoom } = new Meters(init)
const res = this.Resolution(zoom)
const px = (mx + this.originShift) / res
const py = (my + this.originShift) / res
return new Pixels({ px, py, zoom })
}
public latLngToTile(init: InterfaceLatLng) {
const meters = this.latLngToMeters(init)
const pixels = this.metersToPixels(meters)
return this.pixelsToTile(pixels)
}
public latLngToGoogle(init: InterfaceLatLng) {
if (init.zoom === 0) { return new Google({ x: 0, y: 0, zoom: 0 })}
const tile = this.latLngToTile(init)
return this.tileGoogle(tile)
}
public metersToTile(init: Meters) {
if (init.zoom === 0) { return new Tile({ tx: 0, ty: 0, zoom: 0 })}
const Pixels = this.metersToPixels(new Meters(init))
return this.pixelsToTile(Pixels)
}
public pixelsToMeters(init: Pixels) {
const {px, py, zoom} = new Pixels(init)
const res = this.Resolution(zoom)
const mx = px * res - this.originShift
const my = py * res - this.originShift
return new Meters({ mx, my, zoom })
}
public pixelsToTile(init: Pixels) {
if (init.zoom === 0) { return new Tile({ tx: 0, ty: 0, zoom: 0 })}
const {px, py, zoom} = new Pixels(init)
let tx = Math.ceil(px / this.TileSize) - 1
let ty = Math.ceil(py / this.TileSize) - 1
if (tx < 0) { tx = 0 }
if (ty < 0) { ty = 0 }
return new Tile({ tx, ty, zoom })
}
public tileBbox(init: Tile) {
const {tx, ty, zoom} = new Tile(init)
let min = this.pixelsToMeters({ px: tx * this.TileSize, py: ty * this.TileSize, zoom })
let max = this.pixelsToMeters({ px: (tx + 1) * this.TileSize, py: (ty + 1) * this.TileSize, zoom })
return validateBbox([ min.mx, min.my, max.mx, max.my ])
}
public tileLatLngBbox(init: Tile) {
if (init.zoom === 0) { return [ -180, -85.05112877980659, 180, 85.05112877980659 ] }
const {tx, ty, zoom} = new Tile(init)
const [mx1, my1, mx2, my2] = this.tileBbox({ tx, ty, zoom })
const min = this.metersToLatLng({ mx: mx1, my: my1, zoom })
const max = this.metersToLatLng({ mx: mx2, my: my2, zoom })
return validateBbox([ min.lng, min.lat, max.lng, max.lat ])
}
public googleBbox(init: Google) {
const Tile = this.googleTile(init)
return this.tileBbox(Tile)
}
public googleLatLngBbox(init: Google) {
const Tile = this.googleTile(init)
return this.tileLatLngBbox(Tile)
}
public bboxLatLngToMeters = (bbox: number[]): number[] => {
const min = this.latLngToMeters({lat: bbox[1], lng: bbox[0]})
const max = this.latLngToMeters({lat: bbox[3], lng: bbox[2]})
return [min.mx, min.my, max.mx, max.my]
}
public tileGoogle(init: Tile) {
if (init.zoom === 0) { return new Google({ x: 0, y: 0, zoom: 0 })}
const { tx, ty, zoom } = new Tile(init)
const x = tx
const y = (Math.pow(2, zoom) - 1) - ty
return new Google({ x, y, zoom })
}
public googleTile(init: Google) {
const { x, y, zoom } = new Google(init)
const tx = x
const ty = Math.pow(2, zoom) - y - 1
return new Tile({ tx, ty, zoom })
}
public googleQuadkey(init: Google) {
const Tile = this.googleTile(init)
return this.tileQuadkey(Tile)
}
public tileQuadkey(init: Tile) {
// Zoom 0 does not exist for Quadkey
if (init.zoom === 0) { return '' }
let { tx, ty, zoom } = new Tile(init)
let quadkey = ''
ty = (Math.pow(2, zoom) - 1) - ty
range(zoom, 0, -1).map(i => {
let digit: any = 0
let mask = 1 << (i - 1)
if ((tx & mask) !== 0) { digit += 1 }
if ((ty & mask) !== 0) { digit += 2 }
quadkey = quadkey.concat(digit)
})
return quadkey
}
public quadkeyTile(quadkey: string) {
const Google = this.quadkeyGoogle(quadkey)
return this.googleTile(Google)
}
public quadkeyGoogle(quadkey: string) {
let x: number = 0
let y: number = 0
const zoom = quadkey.length
range(zoom, 0, -1).map(i => {
let mask = 1 << (i - 1)
switch (parseInt(quadkey[zoom - i], 0)) {
case 0:
break
case 1:
x += mask
break
case 2:
y += mask
break
case 3:
x += mask
y += mask
break
default:
throw new Error('Invalid Quadkey digit sequence')
}
})
return new Google({ x, y, zoom })
}
}
const mercator = new GlobalMercator()
export default {
metersToPixels,
metersToLatLng,
metersToTile,
pixelsToTile,
pixelsToMeters,
latLngToMeters,
latLngToGoogle,
tileBbox,
tileLatLngBbox,
tileGoogle,
tileQuadkey,
quadkeyGoogle,
quadkeyTile,
googleBbox,
googleLatLngBbox,
googleQuadkey,
}
/* istanbul ignore next */
if (require.main === module) {
// const bbox = bboxLatLngToMeters([ -75.01464843750001, 44.99588261816546, -74.97070312499999, 45.02695045318546 ])
// console.log(bbox)
// const meters = pixelsToMeters({ px: 611669, py: 1342753, zoom: 13 })
// console.log(meters)
// console.log(metersToPixels(meters))
// console.log(metersToLatLng(meters))
// gdalwarp -of GTiff -te -8581121.501851652 -1353354.7654779512 -8575634.45283096 -1349909.177990999 lima_imagery.mbtiles lima_imagery.tif
// console.log(validateLatLng([-120, 45, 1]))
// validateMeters([200000, 999150000])
// validateMeters([200000, 150000, 1])
// validatePixels([200000, 150000, 1])
// assertUndefined({x: null})
// console.log(metersToLatLng({mx: 10018754.171394622, my: 5621521.486192067}))
// console.log(metersToPixels({mx: 10000000, my: 5500000, zoom: 13}))
// console.log(metersToPixels({mx: 3000, my: 4000}))
console.log(latLngToMeters({lat: 23, lng: 23}))
console.log(validateBbox([ -75, 44, -74 ]))
}