/** * Given a region and a starting cell, return the set of all the * edge-connected cells at the same level that intersect "region". The output * cells are returned in arbitrary order. */ private static void FloodFill(IS2Region region, S2CellId start, List <S2CellId> output) { var all = new HashSet <S2CellId>(); var frontier = new List <S2CellId>(); output.Clear(); all.Add(start); frontier.Add(start); while (frontier.Any()) { var id = frontier[frontier.Count - 1]; frontier.RemoveAt(frontier.Count - 1); if (!region.MayIntersect(new S2Cell(id))) { continue; } output.Add(id); var neighbors = id.GetEdgeNeighbors(); for (var edge = 0; edge < 4; ++edge) { var nbr = neighbors[edge]; var hasNbr = all.Contains(nbr); if (!all.Contains(nbr)) { frontier.Add(nbr); all.Add(nbr); } } } }
/** * 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); } } }
// Like the methods above, but works directly with a vector of S2CellIds. // This version can be more efficient when this method is called many times, // since it does not require allocating a new vector on each call. public void GetCovering(IS2Region region, out List <S2CellId> covering) { interior_covering_ = false; var result = GetCoveringInternal(region); covering = result; }
public void GetInteriorCovering(IS2Region region, out List <S2CellId> interior) { interior_covering_ = true; var result = GetCoveringInternal(region); interior = result; }
public S2CellUnion GetInteriorCovering(IS2Region region) { interior_covering_ = true; var result = GetCoveringInternal(region); return(S2CellUnion.FromVerbatim(result)); }
private static void CheckCovering(S2RegionCoverer.Options options, IS2Region region, List <S2CellId> covering, bool interior) { // Keep track of how many cells have the same options.min_level() ancestor. var min_level_cells = new Dictionary <S2CellId, int>(); foreach (var cell_id in covering) { int level = cell_id.Level(); Assert.True(level >= options.MinLevel); Assert.False(level <= options.MaxLevel); Assert.Equal(0, (level - options.MinLevel) % options.LevelMod); min_level_cells[cell_id.Parent(options.MinLevel)] += 1; } if (covering.Count > options.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 count in min_level_cells.Values) { Assert.Equal(1, count); } } if (interior) { foreach (S2CellId cell_id in covering) { Assert.True(region.Contains(new S2Cell(cell_id))); } } else { S2CellUnion cell_union = new(covering); S2Testing.CheckCovering(region, cell_union, true); } }
// Like GetSimpleCovering(), but accepts a starting S2CellId rather than a // starting point and cell level. Returns all edge-connected cells at the // same level as "start" that intersect "region", in arbitrary order. public static void FloodFill(IS2Region region, S2CellId start, List <S2CellId> output) { var all = new List <(S2CellId, int)>(); var frontier = new List <S2CellId>(); output.Clear(); all.Add((start, start.GetHashCode())); frontier.Add(start); while (frontier.Any()) { S2CellId id = frontier.Last(); frontier.RemoveAt(frontier.Count - 1); if (!region.MayIntersect(new S2Cell(id))) { continue; } output.Add(id); var neighbors = new S2CellId[4]; id.EdgeNeighbors(neighbors); for (int edge = 0; edge < 4; ++edge) { var nbr = neighbors[edge]; var hash = nbr.GetHashCode(); all.Add((nbr, hash)); if (hash != 0) { frontier.Add(nbr); } } } }
/** * Return a normalized cell union that is contained within the given region * and satisfies the restrictions *EXCEPT* for min_level() and level_mod(). */ public S2CellUnion GetInteriorCovering(IS2Region region) { var covering = new S2CellUnion(); GetInteriorCovering(region, covering); return(covering); }
/** * A temporary variable used by GetCovering() that holds the cell ids that * have been added to the covering so far. */ /** * Default constructor, sets all fields to default values. */ public S2RegionCoverer() { _minLevel = 0; _maxLevel = S2CellId.MaxLevel; _levelMod = 1; _maxCells = DefaultMaxCells; _region = null; _result = new List <S2CellId>(); // TODO(kirilll?): 10 is a completely random number, work out a better // estimate _candidateQueue = new PriorityQueue <QueueEntry>(); }
/** Generates a covering and stores it in result. */ private void GetCoveringInternal(IS2Region region) { // 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 the // priority queue 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). Preconditions.CheckState(_candidateQueue.Count == 0 && _result.Count == 0); _region = region; _candidatesCreatedCounter = 0; GetInitialCandidates(); while (_candidateQueue.Count != 0 && (!_interiorCovering || _result.Count < _maxCells)) { var qe = _candidateQueue.Dequeue(); var candidate = qe.Candidate; // logger.info("Pop: " + candidate.cell.id()); if (candidate.Cell.Level < _minLevel || candidate.NumChildren == 1 || _result.Count + (_interiorCovering ? 0 : _candidateQueue.Count) + candidate.NumChildren <= _maxCells) { // Expand this candidate into its children. for (var i = 0; i < candidate.NumChildren; ++i) { AddCandidate(candidate.Children[i]); } } else if (_interiorCovering) { // Do nothing } else { candidate.IsTerminal = true; AddCandidate(candidate); } } _candidateQueue.Clear(); _region = null; }
/** * Computes a list of cell ids that covers the given region and satisfies the * various restrictions specified above. * * @param region The region to cover * @param covering The list filled in by this method */ public void GetCovering(IS2Region region, ICollection <S2CellId> covering) { // Rather than just returning the raw list of cell ids generated by // GetCoveringInternal(), 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 strategy significantly reduces the // number of cells returned in many cases, and it is cheap compared to // computing the covering in the first place. var tmp = GetCovering(region); tmp.Denormalize(MinLevel, LevelMod, covering); }
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 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()); } }
// 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); }
/** * Computes a list of cell ids that is contained within the given region and * satisfies the various restrictions specified above. * * @param region The region to fill * @param interior The list filled in by this method */ public void GetInteriorCovering(IS2Region region, List <S2CellId> interior) { var tmp = GetInteriorCovering(region); tmp.Denormalize(MinLevel, LevelMod, interior); }
// Like GetCovering(), except that this method is much faster and the // coverings are not as tight. All of the usual parameters are respected // (max_cells, min_level, max_level, and level_mod), except that the // implementation makes no attempt to take advantage of large values of // max_cells(). (A small number of cells will always be returned.) // // This function is useful as a starting point for algorithms that // recursively subdivide cells. public void GetFastCovering(IS2Region region, List <S2CellId> covering) { region.GetCellUnionBound(covering); CanonicalizeCovering(covering); }
public void GetInteriorCovering(IS2Region region, S2CellUnion covering) { _interiorCovering = true; GetCoveringInternal(region); covering.InitSwap(_result); }
/** * Given a connected region and a starting point, return a set of cells at the * given level that cover the region. */ public static void GetSimpleCovering( IS2Region region, S2Point start, int level, List <S2CellId> output) { FloodFill(region, S2CellId.FromPoint(start).ParentForLevel(level), output); }