gistools/geometry/s2/
metrics.rs

1use crate::geometry::K_MAX_CELL_LEVEL;
2use core::f64::consts::{PI, SQRT_2};
3use libm::{ilogb, ldexp, sqrt};
4
5/// sqrt(2/3)
6#[cfg(feature = "tan")]
7const SQRT_2_3: f64 = 0.816496580927726;
8
9/// The following are various constants that describe the shapes and sizes of
10/// S2Cells (see s2coords.h and s2cell_id.h).  They are useful for deciding
11/// which cell level to use in order to satisfy a given condition (e.g. that
12/// cell vertices must be no further than "x" apart).  All of the raw constants
13/// are differential quantities; you can use the getValue(level) method to
14/// compute the corresponding length or area on the unit sphere for cells at a
15/// given level.  The minimum and maximum bounds are valid for cells at all
16/// levels, but they may be somewhat conservative for very large cells
17/// (e.g. face cells).
18///
19/// All of the values below were obtained by a combination of hand analysis and
20/// Mathematica.  In general, S2_TAN_PROJECTION produces the most uniform
21/// shapes and sizes of cells, S2_LINEAR_PROJECTION is considerably worse, and
22/// S2_QUADRATIC_PROJECTION is somewhere in between (but generally closer to
23/// the tangent projection than the linear one).
24///
25/// Note that S2_LINEAR_PROJECTION can be useful for analysis even when another
26/// projection is being used, since it allows many cell metrics to be bounded
27/// in terms of (u,v) coordinates rather than (s,t) coordinates.  (With the
28/// linear projection, u = 2 * s - 1 and similarly for v.)  Similarly,
29/// S2_TAN_PROJECTION allows cell metrics to be bounded in terms of (u,v)
30/// coordinate changes when they are measured as distances on the unit sphere.
31///
32/// Defines a cell metric of the given dimension (1 == length, 2 == area).
33#[derive(Debug, Copy, Clone)]
34pub struct Metric<const DIM: i32> {
35    /// The "deriv" value of a metric is a derivative, and must be multiplied by
36    /// a length or area in (s,t)-space to get a useful value.
37    pub deriv: f64,
38}
39impl<const DIM: i32> Metric<DIM> {
40    /// Return the value of a metric for cells at the given level. The value is
41    /// either a length or an area on the unit sphere, depending on the
42    /// particular metric.
43    pub fn get_value(&self, level: i32) -> f64 {
44        self.deriv * ldexp(2.0, -DIM * level)
45    }
46
47    /// Return the level at which the metric has approximately the given value.
48    /// For example, K_AVG_EDGE.getClosestLevel(0.1) returns the level at which
49    /// the average cell edge length is approximately 0.1. The return value is
50    pub fn get_closest_level(&self, value: f64) -> i32 {
51        self.get_level_for_max_value((if DIM == 1 { sqrt(2.) } else { 2. }) * value)
52    }
53
54    /// Return the minimum level such that the metric is at most the given value,
55    /// or S2CellId::kMaxLevel if there is no such level. For example,
56    /// K_MAX_DIAG.get_level_for_max_value(0.1) returns the minimum level such
57    /// that all cell diagonal lengths are 0.1 or smaller.  The return value
58    /// is always a valid level.
59    pub fn get_level_for_max_value(&self, value: f64) -> i32 {
60        let k_max_cell_level: i32 = K_MAX_CELL_LEVEL.into();
61        if value <= 0. {
62            return k_max_cell_level;
63        }
64        let mut level = ilogb(value / self.deriv);
65        level = (-(level >> (DIM - 1))).clamp(0, k_max_cell_level);
66
67        level
68    }
69
70    /// Return the maximum level such that the metric is at least the given value,
71    /// or 0 if there is no such level.  For example,
72    /// K_MAX_DIAG.getLevelForMinValue(0.1) returns the maximum level such that
73    /// all cells have a minimum width of 0.1 or larger.  The return value is
74    /// always a valid level.
75    pub fn get_level_for_min_value(&self, value: f64) -> i32 {
76        let k_max_cell_level: i32 = K_MAX_CELL_LEVEL.into();
77        if value <= 0. {
78            return k_max_cell_level;
79        }
80        let mut level = ilogb(self.deriv / value);
81        level = (level >> (DIM - 1)).clamp(0, k_max_cell_level);
82
83        level
84    }
85}
86
87/// Length based metrics
88pub type LengthMetric = Metric<1>;
89/// Area based metrics
90pub type AreaMetric = Metric<2>;
91
92/// Each cell is bounded by four planes passing through its four edges and
93/// the center of the sphere.  These metrics relate to the angle between each
94/// pair of opposite bounding planes, or equivalently, between the planes
95/// corresponding to two different s-values or two different t-values.  For
96/// example, the maximum angle between opposite bounding planes for a cell at
97/// level k is kMaxAngleSpan.GetValue(k), and the average angle span for all
98/// cells at level k is approximately kAvgAngleSpan.GetValue(k).
99#[cfg(any(feature = "quadratic", feature = "tan", feature = "linear"))]
100pub const K_MIN_ANGLE_SPAN: LengthMetric = LengthMetric {
101    deriv: match () {
102        #[cfg(feature = "quadratic")]
103        _ => 4. / 3.,
104        #[cfg(feature = "tan")]
105        _ => PI / 2.,
106        #[cfg(feature = "linear")]
107        _ => 1.,
108    },
109};
110/// Each cell is bounded by four planes passing through its four edges and
111/// the center of the sphere.  These metrics relate to the angle between each
112/// pair of opposite bounding planes, or equivalently, between the planes
113/// corresponding to two different s-values or two different t-values.  For
114/// example, the maximum angle between opposite bounding planes for a cell at
115/// level k is kMaxAngleSpan.GetValue(k), and the average angle span for all
116/// cells at level k is approximately kAvgAngleSpan.GetValue(k).
117pub const K_MAX_ANGLE_SPAN: LengthMetric = LengthMetric {
118    deriv: match () {
119        #[cfg(feature = "quadratic")]
120        _ => 1.704_897_179_199_218_5, // 1.705
121        #[cfg(feature = "tan")]
122        _ => PI / 2., // 1.571
123        #[cfg(feature = "linear")]
124        _ => 2., // 2
125    },
126};
127/// Each cell is bounded by four planes passing through its four edges and
128/// the center of the sphere.  These metrics relate to the angle between each
129/// pair of opposite bounding planes, or equivalently, between the planes
130/// corresponding to two different s-values or two different t-values.  For
131/// example, the maximum angle between opposite bounding planes for a cell at
132/// level k is kMaxAngleSpan.GetValue(k), and the average angle span for all
133/// cells at level k is approximately kAvgAngleSpan.GetValue(k).
134pub const K_AVG_ANGLE_SPAN: LengthMetric = LengthMetric { deriv: PI / 2. }; // 1.571
135
136/// The width of geometric figure is defined as the distance between two
137/// parallel bounding lines in a given direction.  For cells, the minimum
138/// width is always attained between two opposite edges, and the maximum
139/// width is attained between two opposite vertices.  However, for our
140/// purposes we redefine the width of a cell as the perpendicular distance
141/// between a pair of opposite edges.  A cell therefore has two widths, one
142/// in each direction.  The minimum width according to this definition agrees
143/// with the classic geometric one, but the maximum width is different.  (The
144/// maximum geometric width corresponds to kMaxDiag defined below.)
145///
146/// For a cell at level k, the distance between opposite edges is at least
147/// kMinWidth.getValue(k) and at most kMaxWidth.getValue(k).  The average
148/// width in both directions for all cells at level k is approximately
149/// kAvgWidth.getValue(k).
150///
151/// The width is useful for bounding the minimum or maximum distance from a
152/// point on one edge of a cell to the closest point on the opposite edge.
153/// For example, this is useful when "growing" regions by a fixed distance.
154///
155/// Note that because S2Cells are not usually rectangles, the minimum width of
156/// a cell is generally smaller than its minimum edge length.  (The interior
157/// angles of an S2Cell range from 60 to 120 degrees.)
158///
159/// Linear -> sqrt(2.0 / 3.0)            (0.816)
160/// Tan ->  pi / (2.0 * @sqrt(2.0))      (1.111)
161/// Quadratic -> 2.0 * @sqrt(2.0) / 3.0  (0.943) [Default]
162pub const K_MIN_WIDTH: LengthMetric = LengthMetric {
163    deriv: match () {
164        #[cfg(feature = "quadratic")]
165        _ => 2. * SQRT_2 / 3., // 0.943
166        #[cfg(feature = "tan")]
167        _ => PI / (2. * SQRT_2), // 1.111
168        #[cfg(feature = "linear")]
169        _ => sqrt(2. / 3.), // 0.816
170    },
171};
172
173/// The same as kMaxAngleSpan's derivative.
174pub const K_MAX_WIDTH: LengthMetric = LengthMetric { deriv: K_MAX_ANGLE_SPAN.deriv };
175
176/// - Linear -> 1.411459345844456965
177/// - Tan -> 1.437318638925160885
178/// - Quadratic -> 1.434523672886099389
179pub const K_AVG_WIDTH: LengthMetric = LengthMetric {
180    deriv: match () {
181        #[cfg(feature = "quadratic")]
182        _ => 1.434_523_672_886_099_5, // 1.435
183        #[cfg(feature = "tan")]
184        _ => 1.437318638925160885, // 1.437
185        #[cfg(feature = "linear")]
186        _ => 1.411459345844456965, // 1.411
187    },
188};
189
190/// The minimum edge length of any cell at level k is at least
191/// kMinEdge.getValue(k), and the maximum is at most kMaxEdge.getValue(k).
192/// The average edge length is approximately kAvgEdge.getValue(k).
193///
194/// The edge length metrics can also be used to bound the minimum, maximum,
195/// or average distance from the center of one cell to the center of one of
196/// its edge neighbors.  In particular, it can be used to bound the distance
197/// between adjacent cell centers along the space-filling Hilbert curve for
198/// cells at any given level.
199///
200/// - linear -> 2.0 * sqrt(2.0) / 3.0     (0.943)
201/// - tan -> pi / (2.0 * sqrt(2.0))       (1.111)
202/// - quadratic -> 2.0 * sqrt(2.0) / 3.0  (0.943) [Default]
203pub const K_MIN_EDGE: LengthMetric = LengthMetric {
204    deriv: match () {
205        #[cfg(feature = "quadratic")]
206        _ => 2. * SQRT_2 / 3., // 0.943
207        #[cfg(feature = "tan")]
208        _ => PI / (2. * SQRT_2), // 1.111
209        #[cfg(feature = "linear")]
210        _ => 2. * SQRT_2 / 3., // 0.943
211    },
212};
213
214/// The same as kMaxAngleSpan's derivative.
215pub const K_MAX_EDGE: LengthMetric = LengthMetric { deriv: K_MAX_ANGLE_SPAN.deriv };
216
217/// Average edge length
218pub const K_AVG_EDGE: LengthMetric = LengthMetric {
219    deriv: match () {
220        #[cfg(feature = "quadratic")]
221        _ => 1.459_213_746_386_106_1, // 1.459
222        #[cfg(feature = "tan")]
223        _ => 1.461667032546739266, // 1.462
224        #[cfg(feature = "linear")]
225        _ => 1.440034192955603643, // 1.440
226    },
227};
228
229/// The minimum diagonal length of any cell at level k is at least
230/// kMinDiag.getValue(k), and the maximum is at most kMaxDiag.getValue(k).
231/// The average diagonal length is approximately kAvgDiag.getValue(k).
232///
233/// The maximum diagonal also happens to be the maximum diameter of any cell,
234/// and also the maximum geometric width (see the discussion above).  So for
235/// example, the distance from an arbitrary point to the closest cell center
236/// at a given level is at most half the maximum diagonal length.
237///
238/// - Linear -> 2.0 * @sqrt(2.0) / 3.0     (0.943)
239/// - Tan -> pi * @sqrt(2.0) / 3.0         (1.481)
240/// - Quadratic -> 8.0 * @sqrt(2.0) / 9.0  (1.257) [Default]
241pub const K_MIN_DIAG: LengthMetric = LengthMetric {
242    deriv: match () {
243        #[cfg(feature = "quadratic")]
244        _ => 8. * SQRT_2 / 9., // 1.257
245        #[cfg(feature = "tan")]
246        _ => PI * SQRT_2 / 3., // 1.481
247        #[cfg(feature = "linear")]
248        _ => 2. * SQRT_2 / 3., // 0.943
249    },
250};
251
252/// - Linear -> 2.0 * @sqrt(2.0)        (2.828)
253/// - Tan -> pi * @sqrt(2.0 / 3.0)      (2.565)
254/// - Quadratic -> 2.438654594434021032 [Default]
255pub const K_MAX_DIAG: LengthMetric = LengthMetric {
256    deriv: match () {
257        #[cfg(feature = "quadratic")]
258        _ => 2.438_654_594_434_021, // 2.439
259        #[cfg(feature = "tan")]
260        _ => PI * SQRT_2_3, // 2.565
261        #[cfg(feature = "linear")]
262        _ => 2. * SQRT_2, // 2.828
263    },
264};
265
266/// - Linear -> 2.031817866418812674
267/// - Tan -> 2.063623197195635753
268/// - Quadratic -> 2.060422738998471683 [Default]
269pub const K_AVG_DIAG: LengthMetric = LengthMetric {
270    deriv: match () {
271        #[cfg(feature = "quadratic")]
272        _ => 2.060_422_738_998_471_7, // 2.060
273        #[cfg(feature = "tan")]
274        _ => 2.063623197195635753, // 2.064
275        #[cfg(feature = "linear")]
276        _ => 2.031817866418812674, // 2.032
277    },
278};
279
280/// The minimum area of any cell at level k is at least kMinArea.getValue(k),
281/// and the maximum is at most kMaxArea.getValue(k).  The average area of all
282/// cells at level k is exactly kAvgArea.getValue(k).
283///
284/// - Linear -> 4.0 / (3.0 * @sqrt(3.0))   (0.770)
285/// - Tan -> pi * pi / (4.0 * @sqrt(2.0))  (1.745)
286/// - Quadratic -> 8.0 * @sqrt(2.0) / 9.0  (1.257) [Default]
287pub const K_MIN_AREA: AreaMetric = AreaMetric {
288    deriv: match () {
289        #[cfg(feature = "quadratic")]
290        _ => 8. * SQRT_2 / 9., // 1.257
291        #[cfg(feature = "tan")]
292        _ => (PI * PI) / (4. * SQRT_2), // 1.745
293        #[cfg(feature = "linear")]
294        _ => 4. / (3. * SQRT_3), // 0.770
295    },
296};
297
298/// Max Area Metric
299/// - Linear -> 4.0
300/// - Tan -> pi * pi / 4.0              (2.467)
301/// - Quadratic -> 2.635799256963161491 [Default]
302pub const K_MAX_AREA: AreaMetric = AreaMetric {
303    deriv: match () {
304        #[cfg(feature = "quadratic")]
305        _ => 2.635_799_256_963_161_4, // 2.636
306        #[cfg(feature = "tan")]
307        _ => (PI * PI) / 4., // 2.467
308        #[cfg(feature = "linear")]
309        _ => 4., // 4.000
310    },
311};
312
313/// returns the AreaMetric of 4 * Math.PI / 6) (true for all projections)
314pub const K_AVG_AREA: AreaMetric = AreaMetric { deriv: 4. * PI / 6. };
315
316/// This is the maximum edge aspect ratio over all cells at any level, where
317/// the edge aspect ratio of a cell is defined as the ratio of its longest
318/// edge length to its shortest edge length.
319///
320/// - linear ->    sqrt(2)
321/// - tan ->       sqrt(2)
322/// - quadratic -> 1.4426 (default)
323#[cfg(feature = "quadratic")]
324pub const K_MAX_EDGE_ASPECT: f64 = SQRT_2; // 1.414
325#[cfg(feature = "tan")]
326pub const K_MAX_EDGE_ASPECT: f64 = SQRT_2; // 1.414
327#[cfg(feature = "linear")]
328pub const K_MAX_EDGE_ASPECT: f64 = 1.442615274452682920;
329
330/// The maximum aspect ratio of a diagonal of a cell.
331/// NOTE: This is SQRT_3, but currently its behind a flag in rust
332#[allow(clippy::excessive_precision)]
333pub const K_MAX_DIAG_ASPECT: f64 = 1.732050807568877293527446341505872367_f64;