示例#1
0
    // 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);
    }
示例#2
0
    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));
    }