s2/s2/
cell.rs

1/*
2Copyright 2014 Google Inc. All rights reserved.
3Copyright 2017 Jihyun Yu. All rights reserved.
4
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9    http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations under the License.
16*/
17
18use crate::consts::*;
19use crate::r1;
20use crate::r2;
21use crate::s1;
22use crate::s1::*;
23use crate::s2;
24use crate::s2::cap::Cap;
25use crate::s2::cellid::*;
26use crate::s2::latlng::*;
27use crate::s2::metric::*;
28use crate::s2::point::*;
29use crate::s2::region::Region;
30use crate::s2::stuv::*;
31use std::f64::consts::PI;
32
33lazy_static! {
34    static ref POLE_MIN_LAT: f64 = (1. / 3f64).sqrt().asin() - 0.5 * DBL_EPSILON;
35}
36
37/// Cell is an S2 region object that represents a cell. Unlike CellIDs,
38/// it supports efficient containment and intersection tests. However, it is
39/// also a more expensive representation.
40#[derive(Debug, Clone)]
41#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
42pub struct Cell {
43    face: u8,
44    level: u8,
45    pub orientation: u8,
46    pub id: CellID,
47    pub uv: r2::rect::Rect,
48}
49
50impl<'a> From<&'a CellID> for Cell {
51    fn from(id: &'a CellID) -> Self {
52        let (f, i, j, o) = id.face_ij_orientation();
53        assert!(f < 6);
54        let level = id.level();
55        Cell {
56            face: f as u8,
57            level: level as u8,
58            orientation: o as u8,
59            id: *id,
60            uv: ij_level_to_bound_uv(i, j, level),
61        }
62    }
63}
64impl From<CellID> for Cell {
65    fn from(id: CellID) -> Self {
66        Self::from(&id)
67    }
68}
69
70impl<'a> From<&'a Point> for Cell {
71    fn from(p: &'a Point) -> Self {
72        Cell::from(CellID::from(p))
73    }
74}
75impl From<Point> for Cell {
76    fn from(p: Point) -> Self {
77        Self::from(&p)
78    }
79}
80
81impl<'a> From<&'a LatLng> for Cell {
82    fn from(ll: &'a LatLng) -> Self {
83        Self::from(CellID::from(ll))
84    }
85}
86impl From<LatLng> for Cell {
87    fn from(ll: LatLng) -> Self {
88        Self::from(&ll)
89    }
90}
91
92impl<'a> From<&'a Cell> for CellID {
93    fn from(c: &'a Cell) -> Self {
94        c.id
95    }
96}
97impl From<Cell> for CellID {
98    fn from(c: Cell) -> Self {
99        Self::from(&c)
100    }
101}
102
103impl Cell {
104    pub fn face(&self) -> u8 {
105        self.face
106    }
107
108    pub fn level(&self) -> u8 {
109        self.level
110    }
111
112    pub fn is_leaf(&self) -> bool {
113        self.level == MAX_LEVEL as u8
114    }
115
116    pub fn size_ij(&self) -> u64 {
117        size_ij(u64::from(self.level))
118    }
119
120    /// vertex returns the k-th vertex of the cell (k = 0,1,2,3) in CCW order
121    /// (lower left, lower right, upper right, upper left in the UV plane).
122    pub fn vertex(&self, k: usize) -> Point {
123        let v = &self.uv.vertices()[k];
124        Point(face_uv_to_xyz(self.face, v.x, v.y).normalize())
125    }
126
127    pub fn vertices(&self) -> [Point; 4] {
128        let verts = self.uv.vertices();
129        [
130            Point(face_uv_to_xyz(self.face, verts[0].x, verts[0].y).normalize()),
131            Point(face_uv_to_xyz(self.face, verts[1].x, verts[1].y).normalize()),
132            Point(face_uv_to_xyz(self.face, verts[2].x, verts[2].y).normalize()),
133            Point(face_uv_to_xyz(self.face, verts[3].x, verts[3].y).normalize()),
134        ]
135    }
136
137    /// edge returns the inward-facing normal of the great circle passing through
138    /// the CCW ordered edge from vertex k to vertex k+1 (mod 4) (for k = 0,1,2,3).
139    pub fn edge(&self, k: usize) -> Point {
140        match k {
141            0 => Point(vnorm(self.face, self.uv.y.lo).normalize()),
142            1 => Point(unorm(self.face, self.uv.x.hi).normalize()),
143            2 => Point((vnorm(self.face, self.uv.y.hi) * -1.).normalize()),
144            3 => Point((unorm(self.face, self.uv.x.lo) * -1.).normalize()),
145            _ => unimplemented!(),
146        }
147    }
148
149    /// bound_uv returns the bounds of this cell in (u,v)-space.
150    pub fn bound_uv(&self) -> &r2::rect::Rect {
151        &self.uv
152    }
153
154    /// center returns the direction vector corresponding to the center in
155    /// (s,t)-space of the given cell. This is the point at which the cell is
156    /// divided into four subcells; it is not necessarily the centroid of the
157    /// cell in (u,v)-space or (x,y,z)-space
158    pub fn center(&self) -> Point {
159        Point(self.id.raw_point().normalize())
160    }
161
162    /// Children returns the four direct children of this cell in traversal order
163    /// and returns true. If this is a leaf cell, or the children could not be created,
164    /// false is returned.
165    /// The C++ method is called Subdivide.
166    pub fn children(&self) -> Option<[Cell; 4]> {
167        //TODO: fix redundent copy
168        if self.is_leaf() {
169            return None;
170        }
171        //TODO
172        let mut children = [self.clone(), self.clone(), self.clone(), self.clone()];
173
174        let uv_mid = self.id.center_uv();
175
176        let mut cid = self.id.child_begin();
177        for pos in 0..4 {
178            children[pos] = Cell {
179                face: self.face,
180                level: self.level + 1,
181                orientation: self.orientation ^ (POS_TO_ORIENTATION[pos]),
182                id: cid,
183                uv: self.uv.clone(),
184            };
185
186            // We want to split the cell in half in u and v. To decide which
187            // side to set equal to the midpoint value, we look at cell's (i,j)
188            // position within its parent. The index for i is in bit 1 of ij.
189            let ij = POS_TO_IJ[self.orientation as usize][pos];
190            let i = ij >> 1;
191            let j = ij & 1;
192            if i == 1 {
193                children[pos].uv.x.hi = self.uv.x.hi;
194                children[pos].uv.x.lo = uv_mid.x;
195            } else {
196                children[pos].uv.x.lo = self.uv.x.lo;
197                children[pos].uv.x.hi = uv_mid.x;
198            }
199            if j == 1 {
200                children[pos].uv.y.hi = self.uv.y.hi;
201                children[pos].uv.y.lo = uv_mid.y;
202            } else {
203                children[pos].uv.y.lo = self.uv.y.lo;
204                children[pos].uv.y.hi = uv_mid.y;
205            }
206            cid = cid.next();
207        }
208
209        Some(children)
210    }
211
212    /// exact_area returns the area of this cell as accurately as possible.
213    pub fn exact_area(&self) -> f64 {
214        let verts = self.vertices();
215        point_area(&verts[0], &verts[1], &verts[2]) + point_area(&verts[0], &verts[2], &verts[3])
216    }
217
218    /// approx_area returns the approximate area of this cell. This method is accurate
219    /// to within 3% percent for all cell sizes and accurate to within 0.1% for cells
220    /// at level 5 or higher (i.e. squares 350km to a side or smaller on the Earth's
221    /// surface). It is moderately cheap to compute.
222    pub fn approx_area(&self) -> f64 {
223        // All cells at the first two levels have the same area.
224        if self.level < 2 {
225            return self.average_area();
226        }
227
228        // First, compute the approximate area of the cell when projected
229        // perpendicular to its normal. The cross product of its diagonals gives
230        // the normal, and the length of the normal is twice the projected area.
231        let verts = self.vertices();
232        let flat_area = 0.5
233            * (&verts[2] - &verts[0])
234                .0
235                .cross(&(&verts[3] - &verts[1]).0)
236                .norm();
237
238        // Now, compensate for the curvature of the cell surface by pretending
239        // that the cell is shaped like a spherical cap. The ratio of the
240        // area of a spherical cap to the area of its projected disc turns out
241        // to be 2 / (1 + sqrt(1 - r*r)) where r is the radius of the disself.
242        // For example, when r=0 the ratio is 1, and when r=1 the ratio is 2.
243        // Here we set Pi*r*r == flat_area to find the equivalent disself.
244        flat_area * 2. / (1. + (1. - (1. / PI * flat_area).min(1.)).sqrt())
245    }
246
247    /// average_area returns the average area of cells at the level of this cell.
248    /// This is accurate to within a factor of 1.7.
249    pub fn average_area(&self) -> f64 {
250        AVG_AREAMETRIC.value(self.level)
251    }
252
253    fn uv_from_ij(&self, i: i32, j: i32) -> (f64, f64) {
254        match (i, j) {
255            (0, 0) => (self.uv.x.lo, self.uv.y.lo),
256            (0, 1) => (self.uv.x.lo, self.uv.y.hi),
257            (1, 0) => (self.uv.x.hi, self.uv.y.lo),
258            (1, 1) => (self.uv.x.hi, self.uv.y.hi),
259            _ => panic!("i and/or j is out of bound"),
260        }
261    }
262
263    fn point_from_ij(&self, i: i32, j: i32) -> Point {
264        let (u, v) = self.uv_from_ij(i, j);
265        Point(face_uv_to_xyz(self.face, u, v))
266    }
267
268    /// latitude returns the latitude of the cell vertex given by (i,j), where "i" and "j" are either 0 or 1.
269    pub fn latitude(&self, i: i32, j: i32) -> Angle {
270        self.point_from_ij(i, j).latitude()
271    }
272
273    /// longitude returns the longitude of the cell vertex given by (i,j), where "i" and "j" are either 0 or 1.
274    pub fn longitude(&self, i: i32, j: i32) -> Angle {
275        self.point_from_ij(i, j).longitude()
276    }
277
278    /// rect_bound returns the bounding rectangle of this cell.
279    pub fn rect_bound(&self) -> s2::rect::Rect {
280        if self.level > 0 {
281            // Except for cells at level 0, the latitude and longitude extremes are
282            // attained at the vertices.  Furthermore, the latitude range is
283            // determined by one pair of diagonally opposite vertices and the
284            // longitude range is determined by the other pair.
285            //
286            // We first determine which corner (i,j) of the cell has the largest
287            // absolute latitude.  To maximize latitude, we want to find the point in
288            // the cell that has the largest absolute z-coordinate and the smallest
289            // absolute x- and y-coordinates.  To do this we look at each coordinate
290            // (u and v), and determine whether we want to minimize or maximize that
291            // coordinate based on the axis direction and the cell's (u,v) quadrant.
292            let u = self.uv.x.lo + self.uv.x.hi;
293            let v = self.uv.y.lo + self.uv.y.hi;
294            let i = if u_axis(self.face).0.z == 0. {
295                if u < 0. {
296                    1
297                } else {
298                    0
299                }
300            } else {
301                if u > 0. {
302                    1
303                } else {
304                    0
305                }
306            };
307            let j = if v_axis(self.face).0.z == 0. {
308                if v < 0. {
309                    1
310                } else {
311                    0
312                }
313            } else {
314                if v > 0. {
315                    1
316                } else {
317                    0
318                }
319            };
320
321            let lat = r1::interval::Interval::from_point(self.latitude(i, j).rad())
322                + self.latitude(1 - i, 1 - j).rad();
323            let lng = s1::interval::EMPTY
324                + self.longitude(i, 1 - j).rad()
325                + self.longitude(1 - i, j).rad();
326
327            // We grow the bounds slightly to make sure that the bounding rectangle
328            // contains LatLngFromPoint(P) for any point P inside the loop L defined by the
329            // four *normalized* vertices.  Note that normalization of a vector can
330            // change its direction by up to 0.5 * DBL_EPSILON radians, and it is not
331            // enough just to add Normalize calls to the code above because the
332            // latitude/longitude ranges are not necessarily determined by diagonally
333            // opposite vertex pairs after normalization.
334            //
335            // We would like to bound the amount by which the latitude/longitude of a
336            // contained point P can exceed the bounds computed above.  In the case of
337            // longitude, the normalization error can change the direction of rounding
338            // leading to a maximum difference in longitude of 2 * DBL_EPSILON.  In
339            // the case of latitude, the normalization error can shift the latitude by
340            // up to 0.5 * DBL_EPSILON and the other sources of error can cause the
341            // two latitudes to differ by up to another 1.5 * DBL_EPSILON, which also
342            // leads to a maximum difference of 2 * DBL_EPSILON.
343            let max_err = Angle::from(Rad(2. * DBL_EPSILON));
344            return s2::rect::Rect { lat, lng }
345                .expanded(&LatLng {
346                    lat: max_err,
347                    lng: max_err,
348                })
349                .polar_closure();
350        }
351
352        // The 4 cells around the equator extend to +/-45 degrees latitude at the
353        // midpoints of their top and bottom edges.  The two cells covering the
354        // poles extend down to +/-35.26 degrees at their vertices.  The maximum
355        // error in this calculation is 0.5 * DBL_EPSILON.
356        const PI_4: f64 = PI / 4.;
357        let bound = match self.face {
358            0 => s2::rect::Rect {
359                lat: r1::interval::Interval::new(-PI_4, PI_4),
360                lng: s1::Interval::new(-PI_4, PI_4),
361            },
362            1 => s2::rect::Rect {
363                lat: r1::interval::Interval::new(-PI_4, PI_4),
364                lng: s1::Interval::new(PI_4, 3. * PI_4),
365            },
366            2 => s2::rect::Rect {
367                lat: r1::interval::Interval::new(*POLE_MIN_LAT, PI / 2.),
368                lng: s1::interval::FULL,
369            },
370            3 => s2::rect::Rect {
371                lat: r1::interval::Interval::new(-PI_4, PI_4),
372                lng: s1::Interval::new(3. * PI_4, -3. * PI_4),
373            },
374            4 => s2::rect::Rect {
375                lat: r1::interval::Interval::new(-PI_4, PI_4),
376                lng: s1::Interval::new(-3. * PI_4, -PI_4),
377            },
378            5 => s2::rect::Rect {
379                lat: r1::interval::Interval::new(-PI / 2., -1. * (*POLE_MIN_LAT)),
380                lng: s1::interval::FULL,
381            },
382            _ => panic!("invalid face"),
383        };
384
385        // Finally, we expand the bound to account for the error when a point P is
386        // converted to an LatLng to test for containment. (The bound should be
387        // large enough so that it contains the computed LatLng of any contained
388        // point, not just the infinite-precision version.) We don't need to expand
389        // longitude because longitude is calculated via a single call to math.Atan2,
390        // which is guaranteed to be semi-monotoniself.
391        bound.expanded(&LatLng {
392            lat: Rad(DBL_EPSILON).into(),
393            lng: Rad(0.).into(),
394        })
395    }
396
397    // ContainsPoint reports whether this cell contains the given point. Note that
398    // unlike loop/Polygon, a Cell is considered to be a closed set. This means
399    // that a point on a Cell's edge or vertex belong to the Cell and the relevant
400    // adjacent Cells too.
401    //
402    // If you want every point to be contained by exactly one Cell,
403    // you will need to convert the Cell to a loop.
404    pub fn contains_point(&self, p: &Point) -> bool {
405        if let Some((u, v)) = face_xyz_to_uv(self.face, p) {
406            let uv = r2::point::Point { x: u, y: v };
407
408            // Expand the (u,v) bound to ensure that
409            //
410            //   CellFromPoint(p).ContainsPoint(p)
411            //
412            // is always true. To do this, we need to account for the error when
413            // converting from (u,v) coordinates to (s,t) coordinates. In the
414            // normal case the total error is at most DBL_EPSILON.
415            self.uv.expanded_by_margin(DBL_EPSILON).contains_point(&uv)
416        } else {
417            false
418        }
419    }
420}
421
422impl Region for Cell {
423    /// cap_bound returns the bounding cap of this cell.
424    fn cap_bound(&self) -> Cap {
425        // We use the cell center in (u,v)-space as the cap axis.  This vector is very close
426        // to GetCenter() and faster to compute.  Neither one of these vectors yields the
427        // bounding cap with minimal surface area, but they are both pretty close.
428        let center = self.uv.center();
429        let v: Point = Point(face_uv_to_xyz(self.face, center.x, center.y).normalize());
430        let mut cap = Cap::from(&v);
431
432        let vertices = self.vertices();
433        for vert in &vertices {
434            cap = cap + vert;
435        }
436        cap
437    }
438
439    /// intersects_cell reports whether the intersection of this cell and the other cell is not nil.
440    fn intersects_cell(&self, other: &Cell) -> bool {
441        self.id.intersects(&other.id)
442    }
443
444    /// contains_cell reports whether this cell contains the other cell.
445    fn contains_cell(&self, other: &Cell) -> bool {
446        self.id.contains(&other.id)
447    }
448}
449
450// BUG(roberts): Differences from C++:
451// Subdivide
452// BoundUV
453// Distance/DistanceToEdge
454// VertexChordDistance
455// */
456#[cfg(test)]
457mod tests {
458    extern crate rand;
459
460    use super::*;
461    use rand::Rng;
462    use std;
463    use std::collections::BTreeMap;
464
465    use crate::s2::random;
466
467    // maxCellSize is the upper bounds on the number of bytes we want the Cell object to ever be.
468    const MAX_CELL_SIZE: usize = 48;
469
470    #[test]
471    fn test_cell_object_size() {
472        assert!(std::mem::size_of::<Cell>() <= MAX_CELL_SIZE);
473    }
474
475    // Other than Go, Rust does not make it easy to implement maps with
476    // floating-point keys. This is intentional: Floating-point numbers
477    // should not be tested for exact equality, which is essentially what
478    // happens when using f64 as keys in a map. Nonetheless, the unit tests
479    // on S2Cell count how often the “same” point gets returned as vertex
480    // or edge. For the purpose of testing, we work with Strings that can
481    // easily be used as map keys.
482    fn format_f64(f: f64) -> String {
483        if f == 0.0 || f == -0.0 {
484            String::from("±0.0")
485        } else {
486            format!("{:0.20}", f)
487        }
488    }
489
490    fn incr(m: &mut BTreeMap<String, usize>, p: Point) {
491        let key = format!(
492            "{},{},{}",
493            format_f64(p.0.x),
494            format_f64(p.0.y),
495            format_f64(p.0.z)
496        );
497        *m.entry(key).or_insert(0) += 1;
498    }
499
500    #[test]
501    fn test_cell_faces() {
502        let mut edge_counts = BTreeMap::new();
503        let mut vert_counts = BTreeMap::new();
504        for face in 0..6 {
505            let id = CellID::from_face(face);
506            let cell = Cell::from(&id);
507
508            assert_eq!(cell.id, id);
509            assert_eq!(cell.face as u64, face);
510            assert_eq!(cell.level, 0);
511
512            // Top-level faces have alternating orientations
513            // to get RHS coordinates.
514            assert_eq!(cell.orientation, cell.face & SWAP_MASK);
515
516            assert_eq!(cell.is_leaf(), false);
517            let verts = cell.vertices();
518            for k in 0..4 {
519                let edge = cell.edge(k);
520                let vert = verts[k];
521                incr(&mut edge_counts, edge);
522                incr(&mut vert_counts, vert);
523
524                let vert_cross = verts[(k + 1) & 3];
525                assert_f64_eq!(0., vert.0.dot(&edge.0));
526                assert_f64_eq!(0., vert_cross.0.dot(&edge.0));
527                assert_f64_eq!(1., vert.0.cross(&vert_cross.0).normalize().dot(&edge.0));
528            }
529        }
530
531        // Check that edges have multiplicity 2 and vertices have
532        // multiplicity 3.
533        for (k, v) in &edge_counts {
534            assert_eq!(*v, 2, "edge {} counts wrong, got {}, want 2", k, v);
535        }
536
537        for (k, v) in &vert_counts {
538            assert_eq!(*v, 3, "vertex {} counts wrong, got {}, want 3", k, v);
539        }
540    }
541
542    fn test_cell_children_case(cell: &Cell) {
543        if cell.is_leaf() {
544            assert!(cell.children().is_none());
545            return;
546        }
547
548        let children = cell.children().expect("no children for non-leaf cell");
549        let mut child_id = cell.id.child_begin();
550        for (i, child) in children.iter().enumerate() {
551            assert_eq!(child_id, child.id);
552
553            let direct = Cell::from(&child.id);
554            assert_eq!(direct.face, child.face);
555            assert_eq!(direct.level, child.level);
556            assert_eq!(direct.orientation, child.orientation);
557            assert_eq!(true, direct.center().approx_eq(&child.center()));
558
559            let direct_verts = direct.vertices();
560            let child_verts = child.vertices();
561
562            for k in 0..4 {
563                assert!(direct_verts[k].0.approx_eq(&child_verts[k].0));
564                assert_eq!(direct.edge(k), child.edge(k));
565            }
566
567            assert_eq!(true, cell.contains_cell(&child));
568            assert_eq!(true, cell.intersects_cell(&child));
569            assert_eq!(false, child.contains_cell(&cell));
570            assert_eq!(true, child.intersects_cell(&cell));
571
572            for j in 0..4 {
573                assert_eq!(true, cell.contains_point(&child_verts[j]));
574                if j != i {
575                    assert_eq!(false, child.contains_point(&children[j].center()));
576                    assert_eq!(false, child.intersects_cell(&children[j]));
577                }
578            }
579
580            let parent_cap = cell.cap_bound();
581            let parent_rect = cell.rect_bound();
582
583            if cell.contains_point(&Point::from_coords(0., 0., 1.))
584                || cell.contains_point(&Point::from_coords(0., 0., -1.))
585            {
586                assert_eq!(true, parent_rect.lng.is_full());
587            }
588
589            let child_cap = child.cap_bound();
590            let child_rect = child.rect_bound();
591            let child_center = child.center();
592
593            assert_eq!(true, child_cap.contains_point(&child_center));
594            assert_eq!(
595                true,
596                child_rect.contains_point(&child_center),
597                "child_rect {:?}.contains_point({:?}.center()) = false, want true",
598                child_rect,
599                child
600            );
601            assert_eq!(true, parent_cap.contains_point(&child_center));
602            assert_eq!(true, parent_rect.contains_point(&child_center));
603
604            for j in 0..4 {
605                let v = &child_verts[j];
606                assert_eq!(true, child_cap.contains_point(&v));
607                assert_eq!(true, child_rect.contains_point(&v));
608                assert_eq!(true, parent_cap.contains_point(&v));
609                assert_eq!(true, parent_rect.contains_point(&v));
610
611                if j != i {
612                    // The bounding caps and rectangles should be tight enough so that
613                    // they exclude at least two vertices of each adjacent cell.
614                    let mut cap_count = 0;
615                    let mut rect_count = 0;
616                    let verts = children[j].vertices();
617
618                    for k in 0..4 {
619                        if child_cap.contains_point(&verts[k]) {
620                            cap_count += 1;
621                        }
622                        if child_rect.contains_point(&verts[k]) {
623                            rect_count += 1;
624                        }
625                    }
626                    assert!(cap_count < 3);
627                    if child_rect.lat.lo > PI / 2. && child_rect.lat.hi < PI / 2. {
628                        assert!(rect_count < 3);
629                    }
630                }
631            }
632
633            // Check all children for the first few levels, and then sample randomly.
634            // We also always subdivide the cells containing a few chosen points so
635            // that we have a better chance of sampling the minimum and maximum metric
636            // values.  kMaxSizeUV is the absolute value of the u- and v-coordinate
637            // where the cell size at a given level is maximal.
638            let max_size_uv = 0.3964182625366691;
639            let special_uv = [
640                r2::point::Point::new(DBL_EPSILON, DBL_EPSILON), // Face center
641                r2::point::Point::new(DBL_EPSILON, 1.),          // Edge midpoint
642                r2::point::Point::new(1., 1.),                   // Face corner
643                r2::point::Point::new(max_size_uv, max_size_uv), // Largest cell area
644                r2::point::Point::new(DBL_EPSILON, max_size_uv), // Longest edge/diagonal
645            ];
646
647            let mut force_subdivide = false;
648            for uv in special_uv.iter() {
649                if child.bound_uv().contains_point(uv) {
650                    force_subdivide = true;
651                }
652            }
653
654            // For a more in depth test, add an "|| oneIn(n)" to this condition
655            // to cause more children to be tested beyond the ones to level 5.
656            if force_subdivide || cell.level < 5 {
657                test_cell_children_case(child);
658            }
659
660            child_id = child_id.next();
661        }
662    }
663
664    #[test]
665    fn test_cell_children() {
666        test_cell_children_case(&Cell::from(CellID::from_face(0)));
667        test_cell_children_case(&Cell::from(CellID::from_face(3)));
668        test_cell_children_case(&Cell::from(CellID::from_face(5)));
669    }
670
671    #[test]
672    fn test_cell_areas() {
673        // relative error bounds for each type of area computation
674        let exact_error = (1. + 1e-6f64).ln();
675        let approx_error = (1.03f64).ln();
676        let avg_error = (1. + 1e-15f64).ln();
677
678        // Test 1. Check the area of a top level cell.
679        let level1_cell = CellID(0x1000000000000000);
680        let want_area = 4. * PI / 6.;
681
682        assert_f64_eq!(Cell::from(&level1_cell).exact_area(), want_area);
683
684        // Test 2. Iterate inwards from this cell, checking at every level that
685        // the sum of the areas of the children is equal to the area of the parent.
686        let mut child_index = 1;
687        let mut ci = CellID(0x1000000000000000);
688        while ci.level() < 21 {
689            let mut exact_area = 0.;
690            let mut approx_area = 0.;
691            let mut avg_area = 0.;
692
693            for child in ci.children().iter() {
694                exact_area += Cell::from(child).exact_area();
695                approx_area += Cell::from(child).approx_area();
696                avg_area += Cell::from(child).average_area();
697            }
698
699            let cell = Cell::from(&ci);
700
701            assert_f64_eq!(exact_area, cell.exact_area());
702
703            child_index = (child_index + 1) % 4;
704
705            // For exact_area(), the best relative error we can expect is about 1e-6
706            // because the precision of the unit vector coordinates is only about 1e-15
707            // and the edge length of a leaf cell is about 1e-9.
708            assert!((exact_area / cell.exact_area()).ln().abs() < exact_error);
709
710            // For ApproxArea(), the areas are accurate to within a few percent.
711            assert!((approx_area / cell.approx_area()).ln().abs() < approx_error);
712
713            // For AverageArea(), the areas themselves are not very accurate, but
714            // the average area of a parent is exactly 4 times the area of a child.
715            assert!((avg_area / cell.average_area()).ln().abs() < avg_error);
716
717            ci = ci.children()[child_index].clone();
718        }
719    }
720
721    fn test_cell_intersects_cell_case(a: CellID, b: CellID, want: bool) {
722        assert_eq!(want, Cell::from(a).intersects_cell(&Cell::from(b)));
723    }
724
725    #[test]
726    fn test_cell_intersects_cell() {
727        test_cell_intersects_cell_case(
728            CellID::from_face(0).child_begin_at_level(2),
729            CellID::from_face(0).child_begin_at_level(2),
730            true,
731        );
732
733        test_cell_intersects_cell_case(
734            CellID::from_face(0).child_begin_at_level(2),
735            CellID::from_face(0)
736                .child_begin_at_level(2)
737                .child_begin_at_level(5),
738            true,
739        );
740
741        test_cell_intersects_cell_case(
742            CellID::from_face(0).child_begin_at_level(2),
743            CellID::from_face(0).child_begin_at_level(2).next(),
744            false,
745        );
746
747        test_cell_intersects_cell_case(
748            CellID::from_face(0).child_begin_at_level(2).next(),
749            CellID::from_face(0).child_begin_at_level(2),
750            false,
751        );
752    }
753
754    fn test_cell_rect_bound_case(lat: f64, lng: f64) {
755        let c = Cell::from(LatLng::new(Deg(lat).into(), Deg(lng).into()));
756        let rect = c.rect_bound();
757        let verts = c.vertices();
758        for i in 0..4 {
759            assert!(rect.contains_latlng(&LatLng::from(&verts[i])));
760        }
761    }
762
763    #[test]
764    fn test_cell_rect_bound() {
765        test_cell_rect_bound_case(50., 50.);
766        test_cell_rect_bound_case(-50., 50.);
767        test_cell_rect_bound_case(50., -50.);
768        test_cell_rect_bound_case(-50., -50.);
769        test_cell_rect_bound_case(0., 0.);
770        test_cell_rect_bound_case(0., 180.);
771        test_cell_rect_bound_case(0., -179.);
772    }
773
774    #[test]
775    fn test_cell_cap_bound() {
776        let c = Cell::from(CellID::from_face(0).child_begin_at_level(20));
777        let s2_cap = c.cap_bound();
778
779        let verts = c.vertices();
780        for i in 0..4 {
781            assert!(s2_cap.contains_point(&verts[i]));
782        }
783    }
784
785    fn test_cell_contains_point_case(a: &Cell, b: &Point, want: bool) {
786        assert_eq!(want, a.contains_point(b));
787    }
788
789    #[test]
790    fn test_cell_contains_point() {
791        let id = CellID::from_face(0);
792
793        test_cell_contains_point_case(
794            &Cell::from(id.child_begin_at_level(2)),
795            &Cell::from(id.child_begin_at_level(2).child_begin_at_level(5)).vertices()[1],
796            true,
797        );
798
799        test_cell_contains_point_case(
800            &Cell::from(id.child_begin_at_level(2)),
801            &Cell::from(id.child_begin_at_level(2)).vertices()[1],
802            true,
803        );
804
805        test_cell_contains_point_case(
806            &Cell::from(id.child_begin_at_level(2).child_begin_at_level(5)),
807            &Cell::from(id.child_begin_at_level(2).next().child_begin_at_level(5)).vertices()[1],
808            false,
809        );
810    }
811
812    use crate::s2::edgeutil;
813
814    #[test]
815    fn test_cell_contains_point_consistent_will_s2_cellid_from_point() {
816        // Construct many points that are nearly on a Cell edge, and verify that
817        // CellFromCellID(cellIDFromPoint(p)).Contains(p) is always true.
818        let mut rng = random::rng();
819
820        for _ in 0..1000 {
821            let cell = Cell::from(&random::cellid(&mut rng));
822            let i1 = rng.gen_range(0..4);
823            let i2 = (i1 + 1) & 3;
824            let v1 = &cell.vertices()[i1];
825
826            let v2 = random::sample_point_from_cap(
827                &mut rng,
828                Cap::from_center_angle(&cell.vertex(i2), &Angle::from(Rad(EPSILON))),
829            );
830            let p = edgeutil::interpolate(rng.gen_range(0.0..1.0), &v1, &v2);
831
832            assert!(Cell::from(&CellID::from(&p)).contains_point(&p));
833        }
834    }
835
836    #[test]
837    fn test_cell_contains_point_contains_ambiguous_point() {
838        // This tests a case where S2CellId returns the "wrong" cell for a point
839        // that is very close to the cell edge. (ConsistentWithS2CellIdFromPoint
840        // generates more examples like this.)
841        //
842        // The Point below should have x = 0, but conversion from LatLng to
843        // (x,y,z) gives x = ~6.1e-17. When xyz is converted to uv, this gives
844        // u = -6.1e-17. However when converting to st, which has a range of [0,1],
845        // the low precision bits of u are lost and we wind up with s = 0.5.
846        // cellIDFromPoint then chooses an arbitrary neighboring cell.
847        //
848        // This tests that Cell.ContainsPoint() expands the cell bounds sufficiently
849        // so that the returned cell is still considered to contain p.
850        let p = Point::from(LatLng::new(Deg(-2.).into(), Deg(90.).into()));
851        let cell = Cell::from(CellID::from(&p).parent(1));
852        assert_eq!(true, cell.contains_point(&p));
853    }
854}