/// <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); }
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); }
public void Test_S2CellUnion_DefaultConstructor() { var ids = new List <S2CellId>(); var empty = new S2CellUnion(ids); Assert.True(empty.IsEmpty()); }
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); } } }
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()); } }
// 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)); }
// 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); } }
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()); }
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); }
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)); }
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()); }
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()); }
// 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); }
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..], ",...");
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); }
// 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()); } }
// 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); } }
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); }
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); }
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); } }