/**
         * Returns the smallest cell containing both points, or Sentinel if they are
         * not all on the same face. The points don't need to be normalized.
         */

        private static S2CellId ContainingCell(S2Point pa, S2Point pb)
        {
            var a = S2CellId.FromPoint(pa);
            var b = S2CellId.FromPoint(pb);

            if (a.Face != b.Face)
            {
                return(S2CellId.Sentinel);
            }

            while (!a.Equals(b))
            {
                a = a.Parent;
                b = b.Parent;
            }
            return(a);
        }
        /**
         * Returns the smallest cell containing all four points, or
         * {@link S2CellId#sentinel()} if they are not all on the same face. The
         * points don't need to be normalized.
         */

        private static S2CellId ContainingCell(S2Point pa, S2Point pb, S2Point pc, S2Point pd)
        {
            var a = S2CellId.FromPoint(pa);
            var b = S2CellId.FromPoint(pb);
            var c = S2CellId.FromPoint(pc);
            var d = S2CellId.FromPoint(pd);

            if (a.Face != b.Face || a.Face != c.Face || a.Face != d.Face)
            {
                return(S2CellId.Sentinel);
            }

            while (!a.Equals(b) || !a.Equals(c) || !a.Equals(d))
            {
                a = a.Parent;
                b = b.Parent;
                c = c.Parent;
                d = d.Parent;
            }
            return(a);
        }
示例#3
0
        /** Computes a set of initial candidates that cover the given region. */

        private void GetInitialCandidates()
        {
            // Optimization: if at least 4 cells are desired (the normal case),
            // start with a 4-cell covering of the region's bounding cap. This
            // lets us skip quite a few levels of refinement when the region to
            // be covered is relatively small.
            if (_maxCells >= 4)
            {
                // Find the maximum level such that the bounding cap contains at most one
                // cell vertex at that level.
                var cap   = _region.CapBound;
                var level = Math.Min(S2Projections.MinWidth.GetMaxLevel(2 * cap.Angle.Radians),
                                     Math.Min(MaxLevel, S2CellId.MaxLevel - 1));
                if (LevelMod > 1 && level > MinLevel)
                {
                    level -= (level - MinLevel) % LevelMod;
                }
                // We don't bother trying to optimize the level == 0 case, since more than
                // four face cells may be required.
                if (level > 0)
                {
                    // Find the leaf cell containing the cap axis, and determine which
                    // subcell of the parent cell contains it.
                    var @base = new List <S2CellId>(4);
                    var id    = S2CellId.FromPoint(cap.Axis);
                    id.GetVertexNeighbors(level, @base);
                    for (var i = 0; i < @base.Count; ++i)
                    {
                        AddCandidate(NewCandidate(new S2Cell(@base[i])));
                    }
                    return;
                }
            }
            // Default: start with all six cube faces.
            for (var face = 0; face < 6; ++face)
            {
                AddCandidate(NewCandidate(FaceCells[face]));
            }
        }
示例#4
0
        // This is a static method in order to provide named parameters.

        // Convenience methods.
        public S2Cell(S2Point p)
        {
            Init(S2CellId.FromPoint(p));
        }
        /**
         * Computes a cell covering of an edge. Clears edgeCovering and returns the
         * level of the s2 cells used in the covering (only one level is ever used for
         * each call).
         *
         *  If thickenEdge is true, the edge is thickened and extended by 1% of its
         * length.
         *
         *  It is guaranteed that no child of a covering cell will fully contain the
         * covered edge.
         */

        private int GetCovering(
            S2Point a, S2Point b, bool thickenEdge, List <S2CellId> edgeCovering)
        {
            edgeCovering.Clear();

            // Selects the ideal s2 level at which to cover the edge, this will be the
            // level whose S2 cells have a width roughly commensurate to the length of
            // the edge. We multiply the edge length by 2*THICKENING to guarantee the
            // thickening is honored (it's not a big deal if we honor it when we don't
            // request it) when doing the covering-by-cap trick.
            var edgeLength = a.Angle(b);
            var idealLevel = S2Projections.MinWidth.GetMaxLevel(edgeLength * (1 + 2 * Thickening));

            S2CellId containingCellId;

            if (!thickenEdge)
            {
                containingCellId = ContainingCell(a, b);
            }
            else
            {
                if (idealLevel == S2CellId.MaxLevel)
                {
                    // If the edge is tiny, instabilities are more likely, so we
                    // want to limit the number of operations.
                    // We pretend we are in a cell much larger so as to trigger the
                    // 'needs covering' case, so we won't try to thicken the edge.
                    containingCellId = (new S2CellId(0xFFF0)).ParentForLevel(3);
                }
                else
                {
                    var pq    = (b - a) * Thickening;
                    var ortho = (S2Point.Normalize(S2Point.CrossProd(pq, a))) * edgeLength * Thickening;
                    var p     = a - pq;
                    var q     = b + pq;
                    // If p and q were antipodal, the edge wouldn't be lengthened,
                    // and it could even flip! This is not a problem because
                    // idealLevel != 0 here. The farther p and q can be is roughly
                    // a quarter Earth away from each other, so we remain
                    // Theta(THICKENING).
                    containingCellId = ContainingCell(p - ortho, p + ortho, q - ortho, q + ortho);
                }
            }

            // Best case: edge is fully contained in a cell that's not too big.
            if (!containingCellId.Equals(S2CellId.Sentinel) &&
                containingCellId.Level >= idealLevel - 2)
            {
                edgeCovering.Add(containingCellId);
                return(containingCellId.Level);
            }

            if (idealLevel == 0)
            {
                // Edge is very long, maybe even longer than a face width, so the
                // trick below doesn't work. For now, we will add the whole S2 sphere.
                // TODO(user): Do something a tad smarter (and beware of the
                // antipodal case).
                for (var cellid = S2CellId.Begin(0); !cellid.Equals(S2CellId.End(0));
                     cellid = cellid.Next)
                {
                    edgeCovering.Add(cellid);
                }
                return(0);
            }
            // TODO(user): Check trick below works even when vertex is at
            // interface
            // between three faces.

            // Use trick as in S2PolygonBuilder.PointIndex.findNearbyPoint:
            // Cover the edge by a cap centered at the edge midpoint, then cover
            // the cap by four big-enough cells around the cell vertex closest to the
            // cap center.
            var middle      = S2Point.Normalize((a + b) / 2);
            var actualLevel = Math.Min(idealLevel, S2CellId.MaxLevel - 1);

            S2CellId.FromPoint(middle).GetVertexNeighbors(actualLevel, edgeCovering);
            return(actualLevel);
        }
示例#6
0
        /**
         * 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);
        }