s2-tools
Version:
A collection of geospatial tools primarily designed for WGS84, Web Mercator, and S2.
380 lines • 10.7 kB
JavaScript
import { degToRad, radToDeg } from '../util';
export const K_LIMIT_IJ = 1073741824; // 1 << 30;
/**
* Convert a [0, 1] to a [-1, 1] in a linear fashion
* @param s - input S or T coordinate
* @returns output U or V coordinate
*/
export function linearSTtoUV(s) {
return 2 * s - 1;
}
/**
* Convert a [-1, 1] to a [0, 1] in a linear fashion
* @param u - input U or V coordinate
* @returns output S or T coordinate
*/
export function linearUVtoST(u) {
return 0.5 * (u + 1);
}
/**
* Convert a [0, 1] to a [-1, 1] in a tangential fashion
* @param s - input S or T coordinate
* @returns output U or V coordinate
*/
export function tanSTtoUV(s) {
const { PI, tan } = Math;
return tan((PI / 2) * s - PI / 4);
}
/**
* Convert a [-1, 1] to a [0, 1] in a tangential fashion
* @param u - input U or V coordinate
* @returns output S or T coordinate
*/
export function tanUVtoST(u) {
const { PI, atan } = Math;
return 2 * (1 / PI) * (atan(u) + PI / 4);
}
/**
* Convert a [0, 1] to a [-1, 1] in a quadratic fashion
* @param s - input S or T coordinate
* @returns output U or V coordinate
*/
export function quadraticSTtoUV(s) {
if (s >= 0.5)
return (1 / 3) * (4 * s * s - 1);
return (1 / 3) * (1 - 4 * (1 - s) * (1 - s));
}
/**
* Convert a [-1, 1] to a [0, 1] in a quadratic fashion
* @param u - input U or V coordinate
* @returns output S or T coordinate
*/
export function quadraticUVtoST(u) {
const { sqrt } = Math;
if (u >= 0)
return 0.5 * sqrt(1 + 3 * u);
return 1 - 0.5 * sqrt(1 - 3 * u);
}
/**
* Convert from st space to ij space (ij are whole numbers ranging an entire u30)
* @param s - input S or T
* @returns output I or J
*/
export function STtoIJ(s) {
const { max, min, floor } = Math;
return max(0, min(K_LIMIT_IJ - 1, floor(K_LIMIT_IJ * s)));
}
/**
* Convert from ij space to st space (ij are whole numbers ranging an entire u30)
* @param i - input I or J
* @returns output S or T
*/
export function IJtoST(i) {
return i / K_LIMIT_IJ;
}
/**
* Convert SiTi to ST.
* @param si - input Si or Ti
* @returns output S or T
*/
export function SiTiToST(si) {
return (1 / 2_147_483_648) * si;
}
/**
* Convert a face-u-v coords to left-hand-rule XYZ Point coords
* @param face - input face
* @param u - input u
* @param v - input v
* @returns output
*/
export function faceUVtoXYZ(face, u, v) {
switch (face) {
case 0:
return [1, u, v];
case 1:
return [-u, 1, v];
case 2:
return [-u, -v, 1];
case 3:
return [-1, -v, -u];
case 4:
return [v, -1, -u];
default:
return [v, u, -1];
}
}
/**
* Convert a face-u-v coords to right-hand-rule XYZ Point coords
* @param face - input face
* @param u - input u
* @param v - input v
* @returns output
*/
export function faceUVtoXYZGL(face, u, v) {
switch (face) {
case 0:
return [u, v, 1];
case 1:
return [1, v, -u];
case 2:
return [-v, 1, -u];
case 3:
return [-v, -u, -1];
case 4:
return [-1, -u, v];
default:
return [u, -1, v];
}
}
/**
* Convert from a face and left-hand-rule XYZ Point to u-v coords
* @param face - input face
* @param xyz - input
* @returns output
*/
export function faceXYZtoUV(face, xyz) {
const [x, y, z] = xyz;
switch (face) {
case 0:
return [y / x, z / x];
case 1:
return [-x / y, z / y];
case 2:
return [-x / z, -y / z];
case 3:
return [z / x, y / x];
case 4:
return [z / y, -x / y];
default:
return [-y / z, -x / z];
}
}
/**
* Find the face the point is located at
* @param xyz - point3D input
* @returns - outputs the associated face
*/
export function XYZtoFace(xyz) {
const temp = xyz.map((n) => Math.abs(n));
let face = temp[0] > temp[1] ? (temp[0] > temp[2] ? 0 : 2) : temp[1] > temp[2] ? 1 : 2;
if (xyz[face] < 0)
face += 3;
return face;
}
/**
* Convert from an left-hand-rule XYZ Point to a Face-U-V coordinate
* @param xyz - point3D input
* @returns output's a face, u, and v
*/
export function XYZtoFaceUV(xyz) {
const face = XYZtoFace(xyz);
return [face, ...faceXYZtoUV(face, xyz)];
}
/**
* Convert from a face and right-hand-rule XYZ Point to u-v coords
* @param face - input face
* @param xyz - input Point3D
* @returns output WebGL oriented UV coords
*/
export function faceXYZGLtoUV(face, xyz) {
const [x, y, z] = xyz;
switch (face) {
case 0:
return [x / z, y / z];
case 1:
return [-z / x, y / x];
case 2:
return [-z / y, -x / y];
case 3:
return [y / z, x / z];
case 4:
return [y / x, -z / x];
default:
return [-x / y, -z / y];
}
}
/**
* Convert from an left-hand-rule XYZ Point to a lon-lat coord
* @param xyz - point3D input
* @returns - lon-lat coordinates
*/
export function xyzToLonLat(xyz) {
const { atan2, sqrt } = Math;
const [x, y, z] = xyz;
return [radToDeg(atan2(y, x)), radToDeg(atan2(z, sqrt(x * x + y * y)))];
}
/**
* Convert from a lon-lat coord to an left-hand-rule XYZ Point
* @param lon - longitude
* @param lat - latitude
* @returns - Point3D
*/
export function lonLatToXYZ(lon, lat) {
const { sin, cos } = Math;
lon = degToRad(lon);
lat = degToRad(lat);
return [
cos(lat) * cos(lon), // x
cos(lat) * sin(lon), // y
sin(lat), // z
];
}
/**
* Convert from a lon-lat coord to an right-hand-rule XYZ Point
* @param lon - longitude
* @param lat - latitude
* @returns - WebGL oriented Point3D
*/
export function lonLatToXYZGL(lon, lat) {
const { sin, cos } = Math;
lon = degToRad(lon);
lat = degToRad(lat);
return [
cos(lat) * sin(lon), // y
sin(lat), // z
cos(lat) * cos(lon), // x
];
}
/**
* Convert an u-v-zoom coordinate to a tile coordinate
* @param u - u coordinate
* @param v - v coordinate
* @param zoom - zoom level
* @returns - tile X-Y coordinate
*/
export function tileXYFromUVZoom(u, v, zoom) {
return tileXYFromSTZoom(quadraticUVtoST(u), quadraticUVtoST(v), zoom);
}
/**
* Convert an x-y-zoom coordinate to a tile coordinate
* @param x - x coordinate
* @param y - y coordinate
* @param zoom - zoom level
* @returns - tile X-Y coordinate
*/
export function tileXYFromSTZoom(x, y, zoom) {
const { floor } = Math;
const divisionFactor = (2 / (1 << zoom)) * 0.5;
return [floor(x / divisionFactor), floor(y / divisionFactor)];
}
/**
* Given a quad-based tile schema of "zoom-x-y", get the local UV bounds of said tile.
* @param u - u coordinate
* @param v - v coordinate
* @param zoom - zoom level
* @returns - local UV bounds for the tile
*/
export function bboxUV(u, v, zoom) {
const divisionFactor = 2 / (1 << zoom);
return [
divisionFactor * u - 1,
divisionFactor * v - 1,
divisionFactor * (u + 1) - 1,
divisionFactor * (v + 1) - 1,
];
}
/**
* Given a quad-based tile schema of "zoom-x-y", get the local ST bounds of said tile.
* @param s - s coordinate
* @param t - t coordinate
* @param zoom - zoom level
* @returns - local ST bounds for the tile
*/
export function bboxST(s, t, zoom) {
const divisionFactor = (2 / (1 << zoom)) * 0.5;
return [
divisionFactor * s,
divisionFactor * t,
divisionFactor * (s + 1),
divisionFactor * (t + 1),
];
}
/**
* Find the face-i-j coordinates of neighbors for a specific face-i-j coordinate.
* Define an adjusted level (zoom) for the i-j coordinates. The level is 30 by default.
* @param face - face of the cell
* @param i - i coordinate
* @param j - j coordinate
* @param level - zoom level
* @returns - Face-i-j coordinates
*/
export function neighborsIJ(face, i, j, level = 30) {
const size = 1 << (30 - level);
if (level !== 30) {
i = i << (30 - level);
j = j << (30 - level);
}
return [
fromIJWrap(face, i, j - size, level, j - size >= 0),
fromIJWrap(face, i + size, j, level, i + size < size),
fromIJWrap(face, i, j + size, level, j + size < size),
fromIJWrap(face, i - size, j, level, i - size >= 0),
];
}
/**
* Adjust a manipulated face-i-j coordinate to a legal one if necessary.
* @param face - face of the cell
* @param i - i coordinate
* @param j - j coordinate
* @param level - zoom level
* @param sameFace - if the face should be the same
* @returns - Face-i-j coordinates
*/
function fromIJWrap(face, i, j, level, sameFace = false) {
if (sameFace)
return [face, i >> (30 - level), j >> (30 - level)];
const { max, min } = Math;
const kMaxSize = 1073741824;
i = max(-1, min(kMaxSize, i));
j = max(-1, min(kMaxSize, j));
const kScale = 1 / kMaxSize;
const kLimit = 1 + 2.2204460492503131e-16;
const u = max(-kLimit, min(kLimit, kScale * (2 * (i - kMaxSize / 2) + 1)));
const v = max(-kLimit, min(kLimit, kScale * (2 * (j - kMaxSize / 2) + 1)));
const [nFace, nU, nV] = XYZtoFaceUV(faceUVtoXYZ(face, u, v));
return [nFace, STtoIJ(0.5 * (nU + 1)) >> (30 - level), STtoIJ(0.5 * (nV + 1)) >> (30 - level)];
}
/**
* Return the right-handed normal (not necessarily unit length) for an
* edge in the direction of the positive v-axis at the given u-value on
* the given face. (This vector is perpendicular to the plane through
* the sphere origin that contains the given edge.)
* @param face - face
* @param u - u
* @returns - the 3D vector normal relative to the u
*/
export function getUNorm(face, u) {
if (face === 0)
return [u, -1.0, 0.0];
if (face === 1)
return [1.0, u, 0.0];
if (face === 2)
return [1.0, 0.0, u];
if (face === 3)
return [-u, 0.0, 1.0];
if (face === 4)
return [0.0, -u, 1.0];
return [0.0, -1, -u];
}
/**
* Return the right-handed normal (not necessarily unit length) for an
* edge in the direction of the positive u-axis at the given v-value on
* the given face.
* @param face - face
* @param v - v
* @returns - the 3D vector normal relative to the v
*/
export function getVNorm(face, v) {
if (face === 0)
return [-v, 0.0, 1.0];
if (face === 1)
return [0.0, -v, 1.0];
if (face === 2)
return [0.0, -1.0, -v];
if (face === 3)
return [v, -1.0, 0.0];
if (face === 4)
return [1.0, v, 0.0];
return [1.0, 0.0, v];
}
//# sourceMappingURL=coords.js.map