UNPKG

s2-tools

Version:

A collection of geospatial tools primarily designed for WGS84, Web Mercator, and S2.

277 lines 12.9 kB
import { K_MAX_LEVEL } from '../id'; /** * The following are various constants that describe the shapes and sizes of * S2Cells (see s2coords.h and s2cell_id.h). They are useful for deciding * which cell level to use in order to satisfy a given condition (e.g. that * cell vertices must be no further than "x" apart). All of the raw constants * are differential quantities; you can use the getValue(level) method to * compute the corresponding length or area on the unit sphere for cells at a * given level. The minimum and maximum bounds are valid for cells at all * levels, but they may be somewhat conservative for very large cells * (e.g. face cells). * * All of the values below were obtained by a combination of hand analysis and * Mathematica. In general, S2_TAN_PROJECTION produces the most uniform * shapes and sizes of cells, S2_LINEAR_PROJECTION is considerably worse, and * S2_QUADRATIC_PROJECTION is somewhere in between (but generally closer to * the tangent projection than the linear one). * * Note that S2_LINEAR_PROJECTION can be useful for analysis even when another * projection is being used, since it allows many cell metrics to be bounded * in terms of (u,v) coordinates rather than (s,t) coordinates. (With the * linear projection, u = 2 * s - 1 and similarly for v.) Similarly, * S2_TAN_PROJECTION allows cell metrics to be bounded in terms of (u,v) * coordinate changes when they are measured as distances on the unit sphere. * * Defines a cell metric of the given dimension (1 == length, 2 == area). */ export class Metric { dim; deriv; /** * @param dim - The dimension of the metric * @param deriv - The "deriv" value of a metric is a derivative, and must be multiplied by * a length or area in (s,t)-space to get a useful value. */ constructor(dim, deriv) { this.dim = dim; this.deriv = deriv; } /** * Return the value of a metric for cells at the given level. The value is * either a length or an area on the unit sphere, depending on the * particular metric. * @param level - The level at which to compute the metric * @returns The value of the metric */ getValue(level) { return this.deriv * Math.pow(2, -this.dim * level); } /** * Return the level at which the metric has approximately the given value. * For example, K_AVG_EDGE.getClosestLevel(0.1) returns the level at which * the average cell edge length is approximately 0.1. The return value is * @param value - The value at which to compute the level * @returns - the minimum level such that the metric is at most the given value */ getClosestLevel(value) { return this.getLevelForMaxValue((this.dim === 1 ? Math.sqrt(2) : 2) * value); } /** * Return the minimum level such that the metric is at most the given value, * or S2CellId::kMaxLevel if there is no such level. For example, * K_MAX_DIAG.getLevelForMaxValue(0.1) returns the minimum level such * that all cell diagonal lengths are 0.1 or smaller. The return value * is always a valid level. * @param value - The value at which to compute the level * @returns - the minimum level such that the metric is at most the given value */ getLevelForMaxValue(value) { if (value <= 0) return K_MAX_LEVEL; const { min, max, log2, floor } = Math; // This code is equivalent to computing a floating-point "level" value and // rounding up. ilogb() returns the exponent corresponding to a fraction in // the range [1,2). // level = ilogb(value / this.deriv); let level = floor(log2(value / this.deriv)); level = max(0, min(K_MAX_LEVEL, -(level >> (this.dim - 1)))); return level; } /** * Return the maximum level such that the metric is at least the given value, * or 0 if there is no such level. For example, * K_MAX_DIAG.getLevelForMinValue(0.1) returns the maximum level such that * all cells have a minimum width of 0.1 or larger. The return value is * always a valid level. * @param value - The value at which to compute the level * @returns - the maximum level such that the metric is at least the given value */ getLevelForMinValue(value) { if (value <= 0) return K_MAX_LEVEL; const { min, max, log2, floor } = Math; // This code is equivalent to computing a floating-point "level" value and // rounding down. // level = ilogb(self.deriv / value); let level = floor(log2(this.deriv / value)); level = max(0, min(K_MAX_LEVEL, level >> (this.dim - 1))); return level; } } /** 1D Length Metric */ export class LengthMetric extends Metric { /** * @param deriv - The "deriv" value of a metric is a derivative, and must be multiplied by * a length or area in (s,t)-space to get a useful value. */ constructor(deriv) { super(1, deriv); } } /** 2D Area Metric */ export class AreaMetric extends Metric { /** * @param deriv - The "deriv" value of a metric is a derivative, and must be multiplied by * a length or area in (s,t)-space to get a useful value. */ constructor(deriv) { super(2, deriv); } } /** * Each cell is bounded by four planes passing through its four edges and * the center of the sphere. These metrics relate to the angle between each * pair of opposite bounding planes, or equivalently, between the planes * corresponding to two different s-values or two different t-values. For * example, the maximum angle between opposite bounding planes for a cell at * level k is K_MAX_ANGLE_SPAN.getValue(k), and the average angle span for all * cells at level k is approximately kAvgAngleSpan.getValue(k). * Linear -> 1 * Tan -> pi / 2.0 (1.571) * Quadratic -> 4.0 / 3.0 (1.333) [Default] * @returns - `new LengthMetric(4 / 3)` */ export const K_MIN_ANGLE_SPAN = () => new LengthMetric(4 / 3); /** * Each cell is bounded by four planes passing through its four edges and * the center of the sphere. These metrics relate to the angle between each * pair of opposite bounding planes, or equivalently, between the planes * corresponding to two different s-values or two different t-values. For * example, the maximum angle between opposite bounding planes for a cell at * level k is K_MAX_ANGLE_SPAN.getValue(k), and the average angle span for all * cells at level k is approximately kAvgAngleSpan.getValue(k). * Linear -> 2 * Tan -> pi / 2.0 (1.571) * Quadratic -> 1.704897179199218452 [Default] * @returns - `new LengthMetric(1.704897179199218)` */ export const K_MAX_ANGLE_SPAN = () => new LengthMetric(1.704897179199218); /** @returns - `new LengthMetric(Math.PI / 2)` */ export const K_AVG_ANGLE_SPAN = () => new LengthMetric(Math.PI / 2); // 1.571 /** * The width of geometric figure is defined as the distance between two * parallel bounding lines in a given direction. For cells, the minimum * width is always attained between two opposite edges, and the maximum * width is attained between two opposite vertices. However, for our * purposes we redefine the width of a cell as the perpendicular distance * between a pair of opposite edges. A cell therefore has two widths, one * in each direction. The minimum width according to this definition agrees * with the classic geometric one, but the maximum width is different. (The * maximum geometric width corresponds to kMaxDiag defined below.) * * For a cell at level k, the distance between opposite edges is at least * kMinWidth.getValue(k) and at most kMaxWidth.getValue(k). The average * width in both directions for all cells at level k is approximately * kAvgWidth.getValue(k). * * The width is useful for bounding the minimum or maximum distance from a * point on one edge of a cell to the closest point on the opposite edge. * For example, this is useful when "growing" regions by a fixed distance. * * Note that because S2Cells are not usually rectangles, the minimum width of * a cell is generally smaller than its minimum edge length. (The interior * angles of an S2Cell range from 60 to 120 degrees.) * * Linear -> sqrt(2.0 / 3.0) (0.816) * Tan -> pi / (2.0 * @sqrt(2.0)) (1.111) * Quadratic -> 2.0 * @sqrt(2.0) / 3.0 (0.943) [Default] * @returns - `new LengthMetric((2 * Math.sqrt(2)) / 3.0)` */ export const K_MIN_WIDTH = () => new LengthMetric((2 * Math.sqrt(2)) / 3.0); /** @returns - `new LengthMetric(4 / 3)` */ export const K_MAX_WIDTH = () => new LengthMetric(1.704897179199218); // K_MAX_ANGLE_SPAN.deriv /** * Linear -> 1.411459345844456965 * Tan -> 1.437318638925160885 * Quadratic -> 1.434523672886099389 * @returns - `new LengthMetric(1.4345236728860993)` */ export const K_AVG_WIDTH = () => new LengthMetric(1.4345236728860993); /** * The minimum edge length of any cell at level k is at least * kMinEdge.getValue(k), and the maximum is at most kMaxEdge.getValue(k). * The average edge length is approximately kAvgEdge.getValue(k). * * The edge length metrics can also be used to bound the minimum, maximum, * or average distance from the center of one cell to the center of one of * its edge neighbors. In particular, it can be used to bound the distance * between adjacent cell centers along the space-filling Hilbert curve for * cells at any given level. * * linear -> 2.0 * sqrt(2.0) / 3.0 (0.943) * tan -> pi / (2.0 * sqrt(2.0)) (1.111) * quadratic -> 2.0 * sqrt(2.0) / 3.0 (0.943) [Default] * @returns - `new LengthMetric((2 * Math.sqrt(2)) / 3)` */ export const K_MIN_EDGE = () => new LengthMetric((2 * Math.sqrt(2)) / 3); /** @returns - `new LengthMetric(4 / 3)` */ export const K_MAX_EDGE = () => new LengthMetric(1.704897179199218); // K_MAX_ANGLE_SPAN.deriv /** @returns - `new LengthMetric(1.459213746386106)` */ export const K_AVG_EDGE = () => new LengthMetric(1.459213746386106); /** * The minimum diagonal length of any cell at level k is at least * kMinDiag.getValue(k), and the maximum is at most kMaxDiag.getValue(k). * The average diagonal length is approximately kAvgDiag.getValue(k). * * The maximum diagonal also happens to be the maximum diameter of any cell, * and also the maximum geometric width (see the discussion above). So for * example, the distance from an arbitrary point to the closest cell center * at a given level is at most half the maximum diagonal length. * * Linear -> 2.0 * @sqrt(2.0) / 3.0 (0.943) * Tan -> pi * @sqrt(2.0) / 3.0 (1.481) * Quadratic -> 8.0 * @sqrt(2.0) / 9.0 (1.257) [Default] * @returns - `new LengthMetric((8 * Math.sqrt(2)) / 9)` */ export const K_MIN_DIAG = () => new LengthMetric((8 * Math.sqrt(2)) / 9); /** * Linear -> 2.0 * @sqrt(2.0) (2.828) * Tan -> pi * @sqrt(2.0 / 3.0) (2.565) * Quadratic -> 2.438654594434021032 [Default] * @returns - `new LengthMetric(2.438654594434021)` */ export const K_MAX_DIAG = () => new LengthMetric(2.438654594434021); /** * Linear -> 2.031817866418812674 * Tan -> 2.063623197195635753 * Quadratic -> 2.060422738998471683 [Default] * @returns - `new LengthMetric(2.060422738998471)` */ export const K_AVG_DIAG = () => new LengthMetric(2.060422738998471); /** * The minimum area of any cell at level k is at least kMinArea.getValue(k), * and the maximum is at most kMaxArea.getValue(k). The average area of all * cells at level k is exactly kAvgArea.getValue(k). * * Linear -> 4.0 / (3.0 * @sqrt(3.0)) (0.770) * Tan -> pi * pi / (4.0 * @sqrt(2.0)) (1.745) * Quadratic -> 8.0 * @sqrt(2.0) / 9.0 (1.257) [Default] * @returns - `new AreaMetric((8 * Math.sqrt(2)) / 9)` */ export const K_MIN_AREA = () => new AreaMetric((8 * Math.sqrt(2)) / 9); /** * Linear -> 4.0 * Tan -> pi * pi / 4.0 (2.467) * Quadratic -> 2.635799256963161491 [Default] * @returns - `new AreaMetric(2.6357992569631614)` */ export const K_MAX_AREA = () => new AreaMetric(2.6357992569631614); /** @returns - `new AreaMetric(4 * Math.PI / 6)` */ export const K_AVG_AREA = () => new AreaMetric((4 * Math.PI) / 6); // 2.094 /** * This is the maximum edge aspect ratio over all cells at any level, where * the edge aspect ratio of a cell is defined as the ratio of its longest * edge length to its shortest edge length. * linear -> sqrt(2) * tan -> sqrt(2) * quadratic -> 1.4426 [default] */ export const K_MAX_EDGE_ASPECT = 1.2010892033702918; // Math.sqrt(1.442615274452682); /** * This is the maximum diagonal aspect ratio over all cells at any level, * where the diagonal aspect ratio of a cell is defined as the ratio of its * longest diagonal length to its shortest diagonal length. */ export const K_MAX_DIAG_ASPECT = 1.7320508075688772; // Math.sqrt(3); //# sourceMappingURL=metrics.js.map