Ejemplo n.º 1
0
        /// <summary>
        ///     Merge continuous cells in cellUnion and return a list of merged GeohashRanges.
        /// </summary>
        /// <param name="cellUnion">Container for multiple cells.</param>
        /// <returns>A list of merged GeohashRanges.</returns>
        private static List <GeohashRange> MergeCells(S2CellUnion cellUnion)
        {
            var ranges = new List <GeohashRange>();

            foreach (var c in cellUnion.CellIds)
            {
                var range = new GeohashRange(c.RangeMin.Id, c.RangeMax.Id);

                var wasMerged = false;
                foreach (var r in ranges)
                {
                    if (r.TryMerge(range))
                    {
                        wasMerged = true;
                        break;
                    }
                }

                if (!wasMerged)
                {
                    ranges.Add(range);
                }
            }

            return(ranges);
        }
Ejemplo n.º 2
0
        public static S2CellUnion FindCellIds(S2LatLngRect latLngRect)
        {
            var queue = new ConcurrentQueue <S2CellId>();


            var cellIds = new List <S2CellId>();

            for (var c = S2CellId.Begin(0); !c.Equals(S2CellId.End(0)); c = c.Next)
            {
                if (ContainsGeodataToFind(c, latLngRect))
                {
                    queue.Enqueue(c);
                }
            }

            ProcessQueue(queue, cellIds, latLngRect);
            Debug.Assert(queue.Count == 0);

            queue = null;

            if (cellIds.Count > 0)
            {
                var cellUnion = new S2CellUnion();
                cellUnion.InitFromCellIds(cellIds); // This normalize the cells.
                // cellUnion.initRawCellIds(cellIds); // This does not normalize the cells.
                cellIds = null;

                return(cellUnion);
            }

            return(null);
        }
Ejemplo n.º 3
0
        public void Test_S2CellUnion_DefaultConstructor()
        {
            var ids   = new List <S2CellId>();
            var empty = new S2CellUnion(ids);

            Assert.True(empty.IsEmpty());
        }
Ejemplo n.º 4
0
    public S2CellUnion GetInteriorCovering(IS2Region region)
    {
        interior_covering_ = true;
        var result = GetCoveringInternal(region);

        return(S2CellUnion.FromVerbatim(result));
    }
        /**
         * Checks that "covering" completely covers the given region. If "check_tight"
         * is true, also checks that it does not contain any cells that do not
         * intersect the given region. ("id" is only used internally.)
         */

        protected void checkCovering(IS2Region region, S2CellUnion covering, bool checkTight, S2CellId id)
        {
            if (!id.IsValid)
            {
                for (var face = 0; face < 6; ++face)
                {
                    checkCovering(region, covering, checkTight, S2CellId.FromFacePosLevel(face, 0, 0));
                }
                return;
            }

            if (!region.MayIntersect(new S2Cell(id)))
            {
                // If region does not intersect id, then neither should the covering.
                if (checkTight)
                {
                    Assert.True(!covering.Intersects(id));
                }
            }
            else if (!covering.Contains(id))
            {
                // The region may intersect id, but we can't assert that the covering
                // intersects id because we may discover that the region does not actually
                // intersect upon further subdivision. (MayIntersect is not exact.)
                Assert.True(!region.Contains(new S2Cell(id)));
                var result = !id.IsLeaf;
                Assert.True(result);
                var end = id.ChildEnd;
                for (var child = id.ChildBegin; !child.Equals(end); child = child.Next)
                {
                    checkCovering(region, covering, checkTight, child);
                }
            }
        }
Ejemplo n.º 6
0
        public void Test_S2CellUnion_FromBeginEnd()
        {
            // Since FromMinMax() is implemented in terms of FromBeginEnd(), we
            // focus on test cases that generate an empty range.
            S2CellId initial_id = S2CellId.FromFace(3);

            // Test an empty range before the minimum S2CellId.
            S2CellUnion cell_union = new(new List <S2CellId> {
                initial_id
            });
            S2CellId id_begin      = S2CellId.Begin(S2.kMaxCellLevel);

            cell_union.InitFromBeginEnd(id_begin, id_begin);
            Assert.True(cell_union.IsEmpty());

            // Test an empty range after the maximum S2CellId.
            cell_union = new S2CellUnion(new List <S2CellId> {
                initial_id
            });
            S2CellId id_end = S2CellId.End(S2.kMaxCellLevel);

            cell_union.InitFromBeginEnd(id_end, id_end);
            Assert.True(cell_union.IsEmpty());

            // Test the full sphere.
            cell_union = S2CellUnion.FromBeginEnd(id_begin, id_end);
            Assert.Equal(6, cell_union.Size());
            foreach (S2CellId id in cell_union)
            {
                Assert.True(id.IsFace());
            }
        }
Ejemplo n.º 7
0
    // Modify "covering" if necessary so that it conforms to the current
    // covering parameters (max_cells, min_level, max_level, and level_mod).
    // There are no restrictions on the input S2CellIds (they may be unsorted,
    // overlapping, etc).
    public S2CellUnion CanonicalizeCovering(S2CellUnion covering)
    {
        var ids = covering.CellIds;

        CanonicalizeCovering(ids);
        return(new S2CellUnion(ids));
    }
    // Verifies that an arbitrary S2ShapeIndex is buffered correctly, by first
    // converting the covering to an S2Polygon and then checking that (a) the
    // S2Polygon contains the original geometry and (b) the distance between the
    // original geometry and the boundary of the S2Polygon is at least "radius".
    //
    // The "radius" parameter is an S1Angle for convenience.
    // TODO(ericv): Add Degrees, Radians, etc, methods to S1ChordAngle?
    private static void TestBufferIndex(string index_str, S1Angle radius_angle, S2RegionCoverer coverer)
    {
        var          index    = MakeIndexOrDie(index_str);
        S1ChordAngle radius   = new(radius_angle);
        var          region   = new S2ShapeIndexBufferedRegion(index, radius);
        S2CellUnion  covering = coverer.GetCovering(region);
        // Compute an S2Polygon representing the union of the cells in the covering.
        S2Polygon covering_polygon = new();

        covering_polygon.InitToCellUnionBorder(covering);
        MutableS2ShapeIndex covering_index = new();

        covering_index.Add(new S2Polygon.Shape(covering_polygon));

        // (a) Check that the covering contains the original index.
        Assert.True(S2BooleanOperation.Contains(covering_index, index));

        // (b) Check that the distance between the boundary of the covering and the
        // the original indexed geometry is at least "radius".
        S2ClosestEdgeQuery query = new(covering_index);

        query.Options_.IncludeInteriors = (false);
        var target = new S2ClosestEdgeQuery.ShapeIndexTarget(index);

        Assert.False(query.IsDistanceLess(target, radius));
    }
Ejemplo n.º 9
0
 // Convenience function that adds a collection of cells with the same label.
 public void Add(S2CellUnion cell_ids, Int32 label)
 {
     foreach (S2CellId cell_id in cell_ids)
     {
         Add(cell_id, label);
     }
 }
Ejemplo n.º 10
0
        public void Test_S2CellUnion_WholeSphere()
        {
            var whole_sphere = S2CellUnion.WholeSphere();

            Assert.Equal(whole_sphere.LeafCellsCovered(), 6 * (1UL << 60));
            whole_sphere.Expand(0);
            Assert.Equal(whole_sphere, S2CellUnion.WholeSphere());
        }
Ejemplo n.º 11
0
    public void Test_MakeCellUnion_ValidInput()
    {
        Assert.True(MakeCellUnion("1/3, 4/", out var cellUnion));
        var expected = new S2CellUnion(new List <S2CellId> {
            S2CellId.FromFace(1).Child(3), S2CellId.FromFace(4)
        });

        Assert.Equal(cellUnion, expected);
    }
Ejemplo n.º 12
0
        public void Test_S2CellUnion_S2CellIdConstructor()
        {
            var face1_id    = S2CellId.FromFace(1);
            var face1_union = new S2CellUnion(new List <S2CellId> {
                face1_id
            });

            Assert.Equal(1, face1_union.Size());
            Assert.Equal(face1_id, face1_union.CellId(0));
        }
Ejemplo n.º 13
0
        public void Test_S2CellUnion_IsNormalized()
        {
            var id         = new S2CellId(new S2Point(1, 0, 0)).Parent(10);
            var cell_union = S2CellUnion.FromVerbatim(
                new List <S2CellId> {
                id.Child(0), id.Child(1), id.Child(2), id.Child(3)
            });

            Assert.True(cell_union.IsValid());
            Assert.False(cell_union.IsNormalized());
        }
    public void Test_S2ShapeIndexBufferedRegion_EmptyIndex()
    {
        // Test buffering an empty S2ShapeIndex.
        var         index    = new MutableS2ShapeIndex();
        var         radius   = new S1ChordAngle(S1Angle.FromDegrees(2));
        var         region   = new S2ShapeIndexBufferedRegion(index, radius);
        var         coverer  = new S2RegionCoverer();
        S2CellUnion covering = coverer.GetCovering(region);

        Assert.True(covering.IsEmpty());
    }
Ejemplo n.º 15
0
        public void Test_S2CellUnion_InvalidCellIdNotValidWithDebugFlag()
        {
            // Manually save and restore flag, to preserve test state in opensource
            // without gflags.
            Assert.False(S2CellId.None.IsValid());
            var cell_union = S2CellUnion.FromVerbatimNoCheck(new List <S2CellId> {
                S2CellId.None
            });

            Assert.False(cell_union.IsValid());
        }
Ejemplo n.º 16
0
    // Convenience function that returns the labels of all indexed cells that
    // intersect the given S2CellUnion "target".
    public SortedSet <Int32> GetIntersectingLabels(S2CellUnion target)
    {
        var labels = new SortedSet <Int32>();

        VisitIntersectingCells(target, (S2CellId cell_id, Int32 label) =>
        {
            labels.Add(label);
            return(true);
        });
        return(labels);
    }
Ejemplo n.º 17
0
        public void Test_S2CellUnion_ToStringOver500Cells()
        {
            List <S2CellId> ids = new();

            new S2CellUnion(new List <S2CellId> {
                S2CellId.FromFace(1)
            }).Denormalize(6, 1, ids);                                                           // 4096 cells
            var result = S2CellUnion.FromVerbatim(ids).ToString();

            Assert.Equal(result.Count(t => t == ','), 500);
            Assert.Equal(result[^ 4..], ",...");
Ejemplo n.º 18
0
        public void Test_S2CellUnion_EncodeDecodeEmpty()
        {
            S2CellUnion empty_cell_union = new();

            Encoder encoder = new();

            empty_cell_union.Encode(encoder);
            var decoder = encoder.Decoder();

            var(success, decoded_cell_union) = S2CellUnion.Decode(decoder);
            Assert.True(success);
            Assert.Equal(empty_cell_union, decoded_cell_union);
        }
Ejemplo n.º 19
0
    // Visits all (cell_id, label) pairs in the given index that intersect the
    // given S2CellUnion "target", terminating early if the given CellVisitor
    // function returns false (in which case VisitIntersectingCells returns false
    // as well).  Each (cell_id, label) pair in the index is visited at most
    // once.  (If the index contains duplicates, then each copy is visited.)
    public bool VisitIntersectingCells(S2CellUnion target, CellVisitor visitor)
    {
        if (!target.CellIds.Any())
        {
            return(true);
        }

        var contents = new ContentsEnumerator(this);

        var targetIndex = 0;
        var targetLimit = target.CellIds.Count;
        var rangeIndex  = 0;

        do
        {
            var limitId = range_nodes_[rangeIndex + 1].StartId;
            if (limitId <= target.CellIds[targetIndex].RangeMin())
            {
                // Only seek when necessary.
                var rangeTarget = target.CellIds[targetIndex].RangeMin();
                rangeIndex = range_nodes_.GetUpperBound(new RangeNode(rangeTarget, -1)) - 1;
            }
            for (; range_nodes_[rangeIndex].StartId <= target.CellIds[targetIndex].RangeMax(); rangeIndex++)
            {
                var rNode = range_nodes_[rangeIndex];
                contents.StartUnion(rNode);
                while (contents.MoveNext())
                {
                    if (!visitor(contents.Current.CellId, contents.Current.Label))
                    {
                        return(false);
                    }
                }
            }
            // Check whether the next target cell is also contained by the leaf cell
            // range that we just processed.  If so, we can skip over all such cells
            // using binary search.  This speeds up benchmarks by between 2x and 10x
            // when the average number of intersecting cells is small (< 1).
            if (++targetIndex < targetLimit && target.CellIds[targetIndex].RangeMax() < range_nodes_[rangeIndex].StartId)
            {
                // Skip to the first target cell that extends past the previous range.
                targetIndex = target.CellIds.GetLowerBound(range_nodes_[rangeIndex].StartId, targetIndex + 1, targetLimit);
                if (target.CellIds[targetIndex - 1].RangeMax() >= range_nodes_[rangeIndex].StartId)
                {
                    --targetIndex;
                }
            }
        } while (targetIndex < targetLimit);
        return(true);
    }
    public void Test_S2ShapeIndexBufferedRegion_PointZeroRadius()
    {
        // Test that buffering a point using a zero radius produces a non-empty
        // covering.  (This requires using "less than or equal to" distance tests.)
        var         index    = MakeIndexOrDie("34:25 # #");
        var         region   = new S2ShapeIndexBufferedRegion(index, S1ChordAngle.Zero);
        var         coverer  = new S2RegionCoverer();
        S2CellUnion covering = coverer.GetCovering(region);

        Assert.Equal(1, covering.Size());
        foreach (S2CellId id in covering)
        {
            Assert.True(id.IsLeaf());
        }
    }
    public void Test_S2ShapeIndexBufferedRegion_FullPolygon()
    {
        // Test buffering an S2ShapeIndex that contains a full polygon.
        var         index    = MakeIndexOrDie("# # full");
        var         radius   = new S1ChordAngle(S1Angle.FromDegrees(2));
        var         region   = new S2ShapeIndexBufferedRegion(index, radius);
        var         coverer  = new S2RegionCoverer();
        S2CellUnion covering = coverer.GetCovering(region);

        Assert.Equal(6, covering.Size());
        foreach (S2CellId id in covering)
        {
            Assert.True(id.IsFace());
        }
    }
Ejemplo n.º 22
0
    // As above, but does not Debug.Assert-fail on invalid input. Returns true if
    // conversion is successful.
    public static bool MakeCellUnion(string str, out S2CellUnion cell_union)
    {
        cell_union = null;
        var cellIds = new List <S2CellId>();

        foreach (var cell_str in SplitString(str, ','))
        {
            if (!MakeCellId(cell_str, out var cellId))
            {
                return(false);
            }
            cellIds.Add(cellId);
        }
        cell_union = new S2CellUnion(cellIds);
        return(true);
    }
    public void Test_S2ShapeIndexBufferedRegion_BufferedPointVsCap()
    {
        // Compute an S2Cell covering of a buffered S2Point, then make sure that the
        // covering is equivalent to the corresponding S2Cap.
        var             index   = MakeIndexOrDie("3:5 # #");
        S2Point         point   = MakePointOrDie("3:5");
        var             radius  = new S1ChordAngle(S1Angle.FromDegrees(2));
        var             region  = new S2ShapeIndexBufferedRegion(index, radius);
        S2RegionCoverer coverer = new();

        coverer.Options_.MaxCells = 50;
        S2CellUnion covering       = coverer.GetCovering(region);
        S2Cap       equivalent_cap = new(point, radius);

        S2Testing.CheckCovering(equivalent_cap, covering, true);
    }
        public void checkCovering(
            S2RegionCoverer coverer, IS2Region region, List <S2CellId> covering, bool interior)
        {
            // Keep track of how many cells have the same coverer.min_level() ancestor.
            IDictionary <S2CellId, int> minLevelCells = new Dictionary <S2CellId, int>();

            for (var i = 0; i < covering.Count; ++i)
            {
                var level = covering[i].Level;
                assertTrue(level >= coverer.MinLevel);
                assertTrue(level <= coverer.MaxLevel);
                assertEquals((level - coverer.MinLevel) % coverer.LevelMod, 0);
                var key = covering[i].ParentForLevel(coverer.MinLevel);
                if (!minLevelCells.ContainsKey(key))
                {
                    minLevelCells.Add(key, 1);
                }
                else
                {
                    minLevelCells[key] = minLevelCells[key] + 1;
                }
            }
            if (covering.Count > coverer.MaxCells)
            {
                // If the covering has more than the requested number of cells, then check
                // that the cell count cannot be reduced by using the parent of some cell.
                foreach (var i in minLevelCells.Values)
                {
                    assertEquals(i, 1);
                }
            }

            if (interior)
            {
                for (var i = 0; i < covering.Count; ++i)
                {
                    assertTrue(region.Contains(new S2Cell(covering[i])));
                }
            }
            else
            {
                var cellUnion = new S2CellUnion();
                cellUnion.InitFromCellIds(covering);
                checkCovering(region, cellUnion, true, new S2CellId());
            }
        }
    public void Test_S2ShapeIndexBufferedRegion_FullAfterBuffering()
    {
        // Test a region that becomes the full polygon after buffering.
        var index   = MakeIndexOrDie("0:0 | 0:90 | 0:180 | 0:-90 | 90:0 | -90:0 # #");
        var radius  = new S1ChordAngle(S1Angle.FromDegrees(60));
        var region  = new S2ShapeIndexBufferedRegion(index, radius);
        var coverer = new S2RegionCoverer();

        coverer.Options_.MaxCells = 1000;
        S2CellUnion covering = coverer.GetCovering(region);

        Assert.Equal(6, covering.Size());
        foreach (S2CellId id in covering)
        {
            Assert.True(id.IsFace());
        }
    }
        public void testRandomCaps()
        {
            Console.WriteLine("TestRandomCaps");

            var kMaxLevel = S2CellId.MaxLevel;
            var coverer   = new S2RegionCoverer();

            for (var i = 0; i < 1000; ++i)
            {
                do
                {
                    coverer.MinLevel = random(kMaxLevel + 1);
                    coverer.MaxLevel = random(kMaxLevel + 1);
                } while (coverer.MinLevel > coverer.MaxLevel);
                coverer.MaxCells = skewed(10);
                coverer.LevelMod = 1 + random(3);
                var maxArea = Math.Min(
                    4 * S2.Pi, (3 * coverer.MaxCells + 1) * S2Cell.AverageArea(coverer.MinLevel));
                var cap      = getRandomCap(0.1 * S2Cell.AverageArea(kMaxLevel), maxArea);
                var covering = new List <S2CellId>();
                var interior = new List <S2CellId>();

                coverer.GetCovering(cap, covering);
                checkCovering(coverer, cap, covering, false);

                coverer.GetInteriorCovering(cap, interior);
                checkCovering(coverer, cap, interior, true);


                // Check that GetCovering is deterministic.
                var covering2 = new List <S2CellId>();
                coverer.GetCovering(cap, covering2);
                assertTrue(covering.SequenceEqual(covering2));

                // Also check S2CellUnion.denormalize(). The denormalized covering
                // may still be different and smaller than "covering" because
                // S2RegionCoverer does not guarantee that it will not output all four
                // children of the same parent.
                var cells = new S2CellUnion();
                cells.InitFromCellIds(covering);
                var denormalized = new List <S2CellId>();
                cells.Denormalize(coverer.MinLevel, coverer.LevelMod, denormalized);
                checkCovering(coverer, cap, denormalized, false);
            }
        }
        public void checkCovering(
            S2RegionCoverer coverer, IS2Region region, List<S2CellId> covering, bool interior)
        {
            // Keep track of how many cells have the same coverer.min_level() ancestor.
            IDictionary<S2CellId, int> minLevelCells = new Dictionary<S2CellId, int>();
            for (var i = 0; i < covering.Count; ++i)
            {
                var level = covering[i].Level;
                assertTrue(level >= coverer.MinLevel);
                assertTrue(level <= coverer.MaxLevel);
                assertEquals((level - coverer.MinLevel)%coverer.LevelMod, 0);
                var key = covering[i].ParentForLevel(coverer.MinLevel);
                if (!minLevelCells.ContainsKey(key))
                {
                    minLevelCells.Add(key, 1);
                }
                else
                {
                    minLevelCells[key] = minLevelCells[key] + 1;
                }
            }
            if (covering.Count > coverer.MaxCells)
            {
                // If the covering has more than the requested number of cells, then check
                // that the cell count cannot be reduced by using the parent of some cell.
                foreach (var i in minLevelCells.Values)
                {
                    assertEquals(i, 1);
                }
            }

            if (interior)
            {
                for (var i = 0; i < covering.Count; ++i)
                {
                    assertTrue(region.Contains(new S2Cell(covering[i])));
                }
            }
            else
            {
                var cellUnion = new S2CellUnion();
                cellUnion.InitFromCellIds(covering);
                checkCovering(region, cellUnion, true, new S2CellId());
            }
        }
        public void testRandomCells()
        {
            Console.WriteLine("TestRandomCells");

            var coverer = new S2RegionCoverer();

            coverer.MaxCells = 1;

            // Test random cell ids at all levels.
            for (var i = 0; i < 10000; ++i)
            {
                var id       = getRandomCellId();
                var covering = new S2CellUnion();
                coverer.GetCovering(new S2Cell(id), covering.CellIds);
                assertEquals(covering.Count, 1);
                assertEquals(covering.CellId(0), id);
            }
        }
Ejemplo n.º 29
0
        public void Test_S2CellUnion_EncodeDecode()
        {
            var cell_ids = new List <S2CellId> {
                new S2CellId(0x33),
                new S2CellId(0x8e3748fab),
                new S2CellId(0x91230abcdef83427)
            };
            var cell_union = S2CellUnion.FromVerbatim(cell_ids);

            Encoder encoder = new();

            cell_union.Encode(encoder);
            var decoder = encoder.Decoder();

            var(success, decoded_cell_union) = S2CellUnion.Decode(decoder);
            Assert.True(success);
            Assert.Equal(cell_union, decoded_cell_union);
        }
Ejemplo n.º 30
0
        public void Test_S2CellUnion_RefuseToDecode()
        {
            List <S2CellId> cellids = new();
            S2CellId        id      = S2CellId.Begin(S2.kMaxCellLevel);

            for (int i = 0; i <= S2CellUnion.Union_decode_max_num_cells; ++i)
            {
                cellids.Add(id);
                id = id.Next();
            }
            S2CellUnion cell_union = S2CellUnion.FromVerbatim(cellids);
            Encoder     encoder    = new();

            cell_union.Encode(encoder);
            var decoder = encoder.Decoder();

            var(success, _) = S2CellUnion.Decode(decoder);
            Assert.False(success);
        }
Ejemplo n.º 31
0
        public void Test_S2CellUnion_LeafCellsCovered()
        {
            S2CellUnion cell_union = new();

            Assert.Equal(0UL, cell_union.LeafCellsCovered());

            var ids = new List <S2CellId>
            {
                // One leaf cell on face 0.
                S2CellId.FromFace(0).ChildBegin(S2.kMaxCellLevel)
            };

            cell_union = new S2CellUnion(ids);
            Assert.Equal(1UL, cell_union.LeafCellsCovered());

            // Face 0 itself (which includes the previous leaf cell).
            ids.Add(S2CellId.FromFace(0));
            cell_union = new S2CellUnion(ids);
            Assert.Equal(1UL << 60, cell_union.LeafCellsCovered());
            // Five faces.
            cell_union.Expand(0);
            Assert.Equal(5UL << 60, cell_union.LeafCellsCovered());
            // Whole world.
            cell_union.Expand(0);
            Assert.Equal(6UL << 60, cell_union.LeafCellsCovered());

            // Add some disjoint cells.
            ids.Add(S2CellId.FromFace(1).ChildBegin(1));
            ids.Add(S2CellId.FromFace(2).ChildBegin(2));
            ids.Add(S2CellId.FromFace(2).ChildEnd(2).Prev());
            ids.Add(S2CellId.FromFace(3).ChildBegin(14));
            ids.Add(S2CellId.FromFace(4).ChildBegin(27));
            ids.Add(S2CellId.FromFace(4).ChildEnd(15).Prev());
            ids.Add(S2CellId.FromFace(5).ChildBegin(30));
            cell_union = new S2CellUnion(ids);
            UInt64 expected = 1UL + (1UL << 6) + (1UL << 30) + (1UL << 32) +
                              (2UL << 56) + (1UL << 58) + (1UL << 60);

            Assert.Equal(expected, cell_union.LeafCellsCovered());
        }
        public void testRandomCaps()
        {
            Console.WriteLine("TestRandomCaps");

            var kMaxLevel = S2CellId.MaxLevel;
            var coverer = new S2RegionCoverer();
            for (var i = 0; i < 1000; ++i)
            {
                do
                {
                    coverer.MinLevel = random(kMaxLevel + 1);
                    coverer.MaxLevel = random(kMaxLevel + 1);
                } while (coverer.MinLevel > coverer.MaxLevel);
                coverer.MaxCells = skewed(10);
                coverer.LevelMod = 1 + random(3);
                var maxArea = Math.Min(
                    4*S2.Pi, (3*coverer.MaxCells + 1)*S2Cell.AverageArea(coverer.MinLevel));
                var cap = getRandomCap(0.1*S2Cell.AverageArea(kMaxLevel), maxArea);
                var covering = new List<S2CellId>();
                var interior = new List<S2CellId>();

                coverer.GetCovering(cap, covering);
                checkCovering(coverer, cap, covering, false);

                coverer.GetInteriorCovering(cap, interior);
                checkCovering(coverer, cap, interior, true);


                // Check that GetCovering is deterministic.
                var covering2 = new List<S2CellId>();
                coverer.GetCovering(cap, covering2);
                assertTrue(covering.SequenceEqual(covering2));

                // Also check S2CellUnion.denormalize(). The denormalized covering
                // may still be different and smaller than "covering" because
                // S2RegionCoverer does not guarantee that it will not output all four
                // children of the same parent.
                var cells = new S2CellUnion();
                cells.InitFromCellIds(covering);
                var denormalized = new List<S2CellId>();
                cells.Denormalize(coverer.MinLevel, coverer.LevelMod, denormalized);
                checkCovering(coverer, cap, denormalized, false);
            }
        }
        /**
         * Checks that "covering" completely covers the given region. If "check_tight"
         * is true, also checks that it does not contain any cells that do not
         * intersect the given region. ("id" is only used internally.)
         */

        protected void checkCovering(IS2Region region, S2CellUnion covering, bool checkTight, S2CellId id)
        {
            if (!id.IsValid)
            {
                for (var face = 0; face < 6; ++face)
                {
                    checkCovering(region, covering, checkTight, S2CellId.FromFacePosLevel(face, 0, 0));
                }
                return;
            }

            if (!region.MayIntersect(new S2Cell(id)))
            {
                // If region does not intersect id, then neither should the covering.
                if (checkTight)
                {
                    Assert.True(!covering.Intersects(id));
                }
            }
            else if (!covering.Contains(id))
            {
                // The region may intersect id, but we can't assert that the covering
                // intersects id because we may discover that the region does not actually
                // intersect upon further subdivision. (MayIntersect is not exact.)
                Assert.True(!region.Contains(new S2Cell(id)));
                var result = !id.IsLeaf;
                Assert.True(result);
                var end = id.ChildEnd;
                for (var child = id.ChildBegin; !child.Equals(end); child = child.Next)
                {
                    checkCovering(region, covering, checkTight, child);
                }
            }
        }
        public void testRandomCells()
        {
            Console.WriteLine("TestRandomCells");

            var coverer = new S2RegionCoverer();
            coverer.MaxCells = 1;

            // Test random cell ids at all levels.
            for (var i = 0; i < 10000; ++i)
            {
                var id = getRandomCellId();
                var covering = new S2CellUnion();
                coverer.GetCovering(new S2Cell(id), covering.CellIds);
                assertEquals(covering.Count, 1);
                assertEquals(covering.CellId(0), id);
            }
        }