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 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); } }
// Generates a covering. private List <S2CellId> GetCoveringInternal(IS2Region region) { // We check this on each call because of Options(). System.Diagnostics.Debug.Assert(Options_.MinLevel <= Options_.MaxLevel); // Strategy: Start with the 6 faces of the cube. Discard any // that do not intersect the shape. Then repeatedly choose the // largest cell that intersects the shape and subdivide it. // // result_ contains the cells that will be part of the output, while pq_ // contains cells that we may still subdivide further. Cells that are // entirely contained within the region are immediately added to the output, // while cells that do not intersect the region are immediately discarded. // Therefore pq_ only contains cells that partially intersect the region. // Candidates are prioritized first according to cell size (larger cells // first), then by the number of intersecting children they have (fewest // children first), and then by the number of fully contained children // (fewest children first). System.Diagnostics.Debug.Assert(!pq_.Any()); var result = new List <S2CellId>(); region_ = region; //candidates_created_counter_ = 0; GetInitialCandidates(result); while (pq_.Any() && (!interior_covering_ || result.Count < Options_.MaxCells)) { var top = pq_.First(); var candidate = top.Item2; pq_.Remove(top); // For interior coverings we keep subdividing no matter how many children // the candidate has. If we reach max_cells() before expanding all // children, we will just use some of them. For exterior coverings we // cannot do this, because the result has to cover the whole region, so // all children have to be used. The (candidate.num_children == 1) case // takes care of the situation when we already have more than max_cells() // in results (min_level is too high). Subdividing the candidate with one // child does no harm in this case. if (interior_covering_ || candidate.Cell.Level < Options_.MinLevel || candidate.NumChildren == 1 || (result.Count + pq_.Count + candidate.NumChildren <= Options_.MaxCells)) { // Expand this candidate into its children. for (int i = 0; i < candidate.NumChildren; ++i) { if (interior_covering_ && result.Count >= Options_.MaxCells) { DeleteCandidate(candidate.Children[i], true); } else { AddCandidate(candidate.Children[i], result); } } DeleteCandidate(candidate, false); } else { candidate.IsTerminal = true; AddCandidate(candidate, result); } } while (pq_.Any()) { var top = pq_.First(); DeleteCandidate(top.Item2, true); pq_.Remove(top); } region_ = null; // Rather than just returning the raw list of cell ids, we construct a cell // union and then denormalize it. This has the effect of replacing four // child cells with their parent whenever this does not violate the covering // parameters specified (min_level, level_mod, etc). This significantly // reduces the number of cells returned in many cases, and it is cheap // compared to computing the covering in the first place. S2CellUnion.Normalize(result); if (Options_.MinLevel > 0 || Options_.LevelMod > 1) { var result_copy = result.ToList(); S2CellUnion.Denormalize(result_copy, Options_.MinLevel, Options_.LevelMod, result); } System.Diagnostics.Debug.Assert(IsCanonical(result)); return(result); }
public void CanonicalizeCovering(List <S2CellId> covering) { // We check this on each call because of Options(). System.Diagnostics.Debug.Assert(Options_.MinLevel <= Options_.MaxLevel); // Note that when the covering parameters have their default values, almost // all of the code in this function is skipped. // If any cells are too small, or don't satisfy level_mod(), then replace // them with ancestors. if (Options_.MaxLevel < S2.kMaxCellLevel || Options_.LevelMod > 1) { for (var i = 0; i < covering.Count; i++) { var id = covering[i]; int level = id.Level(); int new_level = AdjustLevel(Math.Min(level, Options_.MaxLevel)); if (new_level != level) { covering[i] = id.Parent(new_level); } } } // Sort the cells and simplify them. S2CellUnion.Normalize(covering); // Make sure that the covering satisfies min_level() and level_mod(), // possibly at the expense of satisfying max_cells(). if (Options_.MinLevel > 0 || Options_.LevelMod > 1) { var tmp = new List <S2CellId>(); S2CellUnion.Denormalize(covering, Options_.MinLevel, Options_.LevelMod, tmp); covering.Clear(); covering.AddRange(tmp); } // If there are too many cells and the covering is very large, use the // S2RegionCoverer to compute a new covering. (This avoids possible O(n^2) // behavior of the simpler algorithm below.) var excess = covering.Count - Options_.MaxCells; if (excess <= 0 || IsCanonical(covering)) { return; } if (excess * covering.Count > 10000) { GetCovering(new S2CellUnion(covering), out covering); } else { // Repeatedly replace two adjacent cells in S2CellId order by their lowest // common ancestor until the number of cells is acceptable. while (covering.Count > Options_.MaxCells) { int best_index = -1, best_level = -1; for (int i = 0; i + 1 < covering.Count; ++i) { int level = covering[i].CommonAncestorLevel(covering[i + 1]); level = AdjustLevel(level); if (level > best_level) { best_level = level; best_index = i; } } if (best_level < Options_.MinLevel) { break; } // Replace all cells contained by the new ancestor cell. S2CellId id = covering[best_index].Parent(best_level); ReplaceCellsWithAncestor(covering, id); // Now repeatedly check whether all children of the parent cell are // present, in which case we can replace those cells with their parent. while (best_level > Options_.MinLevel) { best_level -= Options_.LevelMod; id = id.Parent(best_level); if (!ContainsAllChildren(covering, id)) { break; } ReplaceCellsWithAncestor(covering, id); } } } System.Diagnostics.Debug.Assert(IsCanonical(covering)); }