Exemple #1
0
        public void Test_XYZToFaceSiTi()
        {
            // Check the conversion of random cells to center points and back.
            for (int level = 0; level <= S2.kMaxCellLevel; ++level)
            {
                for (int i = 0; i < 1000; ++i)
                {
                    S2CellId id           = S2Testing.GetRandomCellId(level);
                    int      actual_level = S2.XYZtoFaceSiTi(id.ToPoint(), out var face, out var si, out var ti);
                    Assert.Equal(level, actual_level);
                    S2CellId actual_id =
                        S2CellId.FromFaceIJ(face, (int)(si / 2), (int)(ti / 2)).Parent(level);
                    Assert.Equal(id, actual_id);

                    // Now test a point near the cell center but not equal to it.
                    S2Point p_moved = id.ToPoint() + new S2Point(1e-13, 1e-13, 1e-13);
                    actual_level = S2.XYZtoFaceSiTi(p_moved, out var face_moved, out var si_moved, out var ti_moved);
                    Assert.Equal(-1, actual_level);
                    Assert.Equal(face, face_moved);
                    Assert.Equal(si, si_moved);
                    Assert.Equal(ti, ti_moved);

                    // Finally, test some random (si,ti) values that may be at different
                    // levels, or not at a valid level at all (for example, si == 0).
                    int  face_random = S2Testing.Random.Uniform(S2CellId.kNumFaces);
                    uint si_random, ti_random;
                    uint mask = ~0U << (S2.kMaxCellLevel - level);
                    do
                    {
                        si_random = S2Testing.Random.Rand32() & mask;
                        ti_random = S2Testing.Random.Rand32() & mask;
                    } while (si_random > S2.kMaxSiTi || ti_random > S2.kMaxSiTi);
                    S2Point p_random = S2.FaceSiTitoXYZ(face_random, si_random, ti_random);
                    actual_level = S2.XYZtoFaceSiTi(p_random, out face, out si, out ti);
                    if (face != face_random)
                    {
                        // The chosen point is on the edge of a top-level face cell.
                        Assert.Equal(-1, actual_level);
                        Assert.True(si == 0 || si == S2.kMaxSiTi ||
                                    ti == 0 || ti == S2.kMaxSiTi);
                    }
                    else
                    {
                        Assert.Equal(si_random, si);
                        Assert.Equal(ti_random, ti);
                        if (actual_level >= 0)
                        {
                            Assert.Equal(p_random, S2CellId.FromFaceIJ(face, (int)(si / 2), (int)(ti / 2)).Parent(actual_level).ToPoint());
                        }
                    }
                }
            }
        }
    public void Test_GetReferencePoint_PartiallyDegenerateLoops()
    {
        for (var iter = 0; iter < 100; ++iter)
        {
            // First we construct a long convoluted edge chain that follows the
            // S2CellId Hilbert curve.  At some random point along the curve, we
            // insert a small triangular loop.
            List <List <S2Point> > loops = new(1) { new() };
            var loop         = loops[0];
            int num_vertices = 100;
            var start        = S2Testing.GetRandomCellId(S2.kMaxCellLevel - 1);
            var end          = start.AdvanceWrap(num_vertices);
            var loop_cellid  = start.AdvanceWrap(
                S2Testing.Random.Uniform(num_vertices - 2) + 1);
            var triangle = new List <S2Point>();
            for (var cellid = start; cellid != end; cellid = cellid.NextWrap())
            {
                if (cellid == loop_cellid)
                {
                    // Insert a small triangular loop.  We save the loop so that we can
                    // test whether it contains the origin later.
                    triangle.Add(cellid.Child(0).ToPoint());
                    triangle.Add(cellid.Child(1).ToPoint());
                    triangle.Add(cellid.Child(2).ToPoint());
                    loop.AddRange(triangle);
                    loop.Add(cellid.Child(0).ToPoint());
                }
                else
                {
                    loop.Add(cellid.ToPoint());
                }
            }
            // Now we retrace our steps, except that we skip the three edges that form
            // the triangular loop above.
            for (S2CellId cellid = end; cellid != start; cellid = cellid.PrevWrap())
            {
                if (cellid == loop_cellid)
                {
                    loop.Add(cellid.Child(0).ToPoint());
                }
                else
                {
                    loop.Add(cellid.ToPoint());
                }
            }
            S2LaxPolygonShape shape         = new(loops);
            S2Loop            triangle_loop = new(triangle);
            var rp = shape.GetReferencePoint();
            Assert.Equal(triangle_loop.Contains(rp.Point), rp.Contained);
        }
    }
}
 public void Test_EncodedS2PointVectorTest_LastTwoPointsAtAllLevels()
 {
     // Test encoding the last two S2CellIds at each level.
     for (int level = 0; level <= S2.kMaxCellLevel; ++level)
     {
         _logger.WriteLine("Level = " + level);
         S2CellId id = S2CellId.End(level).Prev();
         // Notice that this costs only 4 bits more than encoding the last S2CellId
         // by itself (see LastAtAllLevels).  This is because encoding a block of
         // size 1 uses 8-bit deltas (which reduces the size of "base" by 4 bits),
         // while this test uses two 4-bit deltas.
         int expected_size = 6 + (level + 2) / 4;
         TestEncodedS2PointVector(new S2Point[] { id.ToPoint(), id.Prev().ToPoint() },
                                  CodingHint.COMPACT, expected_size);
     }
 }
        public void Test_S2ClosestCellQuery_TargetPointInsideIndexedCell()
        {
            // Tests a target point in the interior of an indexed cell.
            S2CellId    cell_id = MakeCellIdOrDie("4/012");
            S2CellIndex index   = new();

            index.Add(cell_id, 1);
            index.Build();
            S2ClosestCellQuery query = new(index);

            S2ClosestCellQuery.PointTarget target = new(cell_id.ToPoint());
            var result = query.FindClosestCell(target);

            Assert.Equal(S1ChordAngle.Zero, result.Distance);
            Assert.Equal(cell_id, result.CellId);
            Assert.Equal(1, result.Label);
        }
 public void Test_EncodedS2PointVectorTest_ManyDuplicatePointsAtAllLevels()
 {
     // Test encoding 32 copies of the last S2CellId at each level.  This uses
     // between 27 and 38 bytes depending on the level.  (Note that the encoding
     // can use less than 1 byte per point in this situation.)
     for (int level = 0; level <= S2.kMaxCellLevel; ++level)
     {
         _logger.WriteLine("Level = " + level);
         S2CellId id = S2CellId.End(level).Prev();
         // Encoding: header (2 bytes), base ((level + 2) / 4 bytes), block count
         // (1 byte), block offsets (2 bytes), block headers (2 bytes), 32 deltas
         // (16 bytes).  At level 30 the encoding size goes up by 1 byte because
         // we can't encode an 8 byte "base" value, so instead this case uses a
         // base of 7 bytes plus a one-byte offset in each of the 2 blocks.
         int expected_size = 23 + (level + 2) / 4;
         if (level == 30)
         {
             expected_size += 1;
         }
         var points = new S2Point[32].Fill(id.ToPoint());
         TestEncodedS2PointVector(points, CodingHint.COMPACT, expected_size);
     }
 }
    public void Test_EncodedS2PointVectorTest_MaxFaceSiTiAtAllLevels()
    {
        // Similar to the test above, but tests encoding the S2CellId at each level
        // whose face, si, and ti values are all maximal.  This turns out to be the
        // S2CellId whose human-readable form is 5/222...22 (0xb555555555555555),
        // however for clarity we construct it using S2CellId.FromFaceIJ.
        for (int level = 0; level <= S2.kMaxCellLevel; ++level)
        {
            _logger.WriteLine("Level = " + level);
            S2CellId id = S2CellId.FromFaceIJ(5, S2.kLimitIJ - 1, S2.kLimitIJ - 1)
                          .Parent(level);

            // This encoding is one byte bigger than the previous test at levels 7, 11,
            // 15, 19, 23, and 27.  This is because in the previous test, the
            // odd-numbered value bits are all zero (except for the face number), which
            // reduces the number of base bits needed by exactly 1.  The encoding size
            // at level==3 is unaffected because for singleton blocks, the lowest 8
            // value bits are encoded in the delta.
            int expected_size = (level < 4) ? 6 : 6 + (level + 1) / 4;
            TestEncodedS2PointVector(new S2Point[] { id.ToPoint() },
                                     CodingHint.COMPACT, expected_size);
        }
    }
Exemple #7
0
        static void TestSubdivide(S2Cell cell)
        {
            GatherStats(cell);
            if (cell.IsLeaf())
            {
                return;
            }

            var children = new S2Cell[4];

            Assert.True(cell.Subdivide(children));
            S2CellId child_id     = cell.Id.ChildBegin();
            double   exact_area   = 0;
            double   approx_area  = 0;
            double   average_area = 0;

            for (int i = 0; i < 4; ++i, child_id = child_id.Next())
            {
                exact_area   += children[i].ExactArea();
                approx_area  += children[i].ApproxArea();
                average_area += children[i].AverageArea();

                // Check that the child geometry is consistent with its cell ID.
                Assert.Equal(child_id, children[i].Id);
                Assert.True(S2.ApproxEquals(children[i].Center(), child_id.ToPoint()));
                S2Cell direct = new(child_id);
                Assert.Equal(direct.Face, children[i].Face);
                Assert.Equal(direct.Level, children[i].Level);
                Assert.Equal(direct.Orientation, children[i].Orientation);
                Assert.Equal(direct.CenterRaw(), children[i].CenterRaw());
                for (int k = 0; k < 4; ++k)
                {
                    Assert.Equal(direct.VertexRaw(k), children[i].VertexRaw(k));
                    Assert.Equal(direct.EdgeRaw(k), children[i].EdgeRaw(k));
                }

                // Test Contains() and MayIntersect().
                Assert.True(cell.Contains(children[i]));
                Assert.True(cell.MayIntersect(children[i]));
                Assert.False(children[i].Contains(cell));
                Assert.True(cell.Contains(children[i].CenterRaw()));
                for (int j = 0; j < 4; ++j)
                {
                    Assert.True(cell.Contains(children[i].VertexRaw(j)));
                    if (j != i)
                    {
                        Assert.False(children[i].Contains(children[j].CenterRaw()));
                        Assert.False(children[i].MayIntersect(children[j]));
                    }
                }

                // Test GetCapBound and GetRectBound.
                S2Cap        parent_cap  = cell.GetCapBound();
                S2LatLngRect parent_rect = cell.GetRectBound();
                if (cell.Contains(new S2Point(0, 0, 1)) || cell.Contains(new S2Point(0, 0, -1)))
                {
                    Assert.True(parent_rect.Lng.IsFull());
                }
                S2Cap        child_cap  = children[i].GetCapBound();
                S2LatLngRect child_rect = children[i].GetRectBound();
                Assert.True(child_cap.Contains(children[i].Center()));
                Assert.True(child_rect.Contains(children[i].CenterRaw()));
                Assert.True(parent_cap.Contains(children[i].Center()));
                Assert.True(parent_rect.Contains(children[i].CenterRaw()));
                for (int j = 0; j < 4; ++j)
                {
                    Assert.True(child_cap.Contains(children[i].Vertex(j)));
                    Assert.True(child_rect.Contains(children[i].Vertex(j)));
                    Assert.True(child_rect.Contains(children[i].VertexRaw(j)));
                    Assert.True(parent_cap.Contains(children[i].Vertex(j)));
                    Assert.True(parent_rect.Contains(children[i].Vertex(j)));
                    Assert.True(parent_rect.Contains(children[i].VertexRaw(j)));
                    if (j != i)
                    {
                        // The bounding caps and rectangles should be tight enough so that
                        // they exclude at least two vertices of each adjacent cell.
                        int cap_count  = 0;
                        int rect_count = 0;
                        for (int k = 0; k < 4; ++k)
                        {
                            if (child_cap.Contains(children[j].Vertex(k)))
                            {
                                ++cap_count;
                            }
                            if (child_rect.Contains(children[j].VertexRaw(k)))
                            {
                                ++rect_count;
                            }
                        }
                        Assert.True(cap_count <= 2);
                        if (child_rect.LatLo().Radians > -S2.M_PI_2 &&
                            child_rect.LatHi().Radians < S2.M_PI_2)
                        {
                            // Bounding rectangles may be too large at the poles because the
                            // pole itself has an arbitrary fixed longitude.
                            Assert.True(rect_count <= 2);
                        }
                    }
                }

                // Check all children for the first few levels, and then sample randomly.
                // We also always subdivide the cells containing a few chosen points so
                // that we have a better chance of sampling the minimum and maximum metric
                // values.  kMaxSizeUV is the absolute value of the u- and v-coordinate
                // where the cell size at a given level is maximal.
                double    kMaxSizeUV = 0.3964182625366691;
                R2Point[] special_uv =
                {
                    new R2Point(S2.DoubleEpsilon, S2.DoubleEpsilon), // Face center
                    new R2Point(S2.DoubleEpsilon, 1),                // Edge midpoint
                    new R2Point(1, 1),                               // Face corner
                    new R2Point(kMaxSizeUV, kMaxSizeUV),             // Largest cell area
                    new R2Point(S2.DoubleEpsilon, kMaxSizeUV),       // Longest edge/diagonal
                };
                bool force_subdivide = false;
                foreach (R2Point uv in special_uv)
                {
                    if (children[i].BoundUV.Contains(uv))
                    {
                        force_subdivide = true;
                    }
                }

                var debugFlag =
#if s2debug
                    true;
#else
                    false;
#endif

                if (force_subdivide ||
                    cell.Level < (debugFlag ? 5 : 6) ||
                    S2Testing.Random.OneIn(debugFlag ? 5 : 4))
                {
                    TestSubdivide(children[i]);
                }
            }

            // Check sum of child areas equals parent area.
            //
            // For ExactArea(), the best relative error we can expect is about 1e-6
            // because the precision of the unit vector coordinates is only about 1e-15
            // and the edge length of a leaf cell is about 1e-9.
            //
            // For ApproxArea(), the areas are accurate to within a few percent.
            //
            // For AverageArea(), the areas themselves are not very accurate, but
            // the average area of a parent is exactly 4 times the area of a child.

            Assert.True(Math.Abs(Math.Log(exact_area / cell.ExactArea())) <= Math.Abs(Math.Log((1 + 1e-6))));
            Assert.True(Math.Abs(Math.Log((approx_area / cell.ApproxArea()))) <= Math.Abs(Math.Log((1.03))));
            Assert.True(Math.Abs(Math.Log((average_area / cell.AverageArea()))) <= Math.Abs(Math.Log((1 + 1e-15))));
        }