/**
         * Initialize a polygon by taking ownership of the given loops and clearing
         * the given list. This method figures out the loop nesting hierarchy and then
         * reorders the loops by following a preorder traversal. This implies that
         * each loop is immediately followed by its descendants in the nesting
         * hierarchy. (See also getParent and getLastDescendant.)
         */

        public void Init(System.Collections.Generic.IList <S2Loop> loops)
        {
            // assert isValid(loops);
            // assert (this.loops.isEmpty());

            //Dictionary<S2Loop, List<S2Loop>> loopMap =new Dictionary<S2Loop, List<S2Loop>>();
            // Note: We're using C5's HashDictionary because SCG's Dictionary<,> does not allow
            // NULL keys
            var loopMap = new Dictionary <NullObject <S2Loop>, List <S2Loop> >();

            // Yes, a null key is valid. It is used here to refer to the root of the
            // loopMap
            loopMap[null] = new List <S2Loop>();

            foreach (var loop in loops)
            {
                InsertLoop(loop, null, loopMap);
                _numVertices += loop.NumVertices;
            }
            loops.Clear();

            // Sort all of the lists of loops; in this way we guarantee a total ordering
            // on loops in the polygon. Loops will be sorted by their natural ordering,
            // while also preserving the requirement that each loop is immediately
            // followed by its descendants in the nesting hierarchy.
            //
            // TODO(andriy): as per kirilll in CL 18750833 code review comments:
            // This should work for now, but I think it's possible to guarantee the
            // correct order inside insertLoop by searching for the correct position in
            // the children list before inserting.
            SortValueLoops(loopMap);

            // Reorder the loops in depth-first traversal order.
            // Starting at null == starting at the root
            InitLoop(null, -1, loopMap);

            // TODO(dbeaumont): Add tests or preconditions for these asserts (here and elesewhere).
            // forall i != j : containsChild(loop(i), loop(j), loopMap) == loop(i).containsNested(loop(j)));

            // Compute the bounding rectangle of the entire polygon.
            _hasHoles = false;
            _bound    = S2LatLngRect.Empty;
            for (var i = 0; i < NumLoops; ++i)
            {
                if (Loop(i).Sign < 0)
                {
                    _hasHoles = true;
                }
                else
                {
                    _bound = _bound.Union(Loop(i).RectBound);
                }
            }
        }
Exemple #2
0
        public void AddPoint(S2Point b)
        {
            // assert (S2.isUnitLength(b));

            var bLatLng = new S2LatLng(b);

            if (bound.IsEmpty)
            {
                bound = bound.AddPoint(bLatLng);
            }
            else
            {
                // We can't just call bound.addPoint(bLatLng) here, since we need to
                // ensure that all the longitudes between "a" and "b" are included.
                bound = bound.Union(S2LatLngRect.FromPointPair(aLatLng, bLatLng));

                // Check whether the Min/Max latitude occurs in the edge interior.
                // We find the normal to the plane containing AB, and then a vector
                // "dir" in this plane that also passes through the equator. We use
                // RobustCrossProd to ensure that the edge normal is accurate even
                // when the two points are very close together.
                var aCrossB = S2.RobustCrossProd(a, b);
                var dir     = S2Point.CrossProd(aCrossB, new S2Point(0, 0, 1));
                var da      = dir.DotProd(a);
                var db      = dir.DotProd(b);

                if (da * db < 0)
                {
                    // Minimum/maximum latitude occurs in the edge interior. This affects
                    // the latitude bounds but not the longitude bounds.
                    var absLat = Math.Acos(Math.Abs(aCrossB[2] / aCrossB.Norm));
                    var lat    = bound.Lat;
                    if (da < 0)
                    {
                        // It's possible that absLat < lat.lo() due to numerical errors.
                        lat = new R1Interval(lat.Lo, Math.Max(absLat, bound.Lat.Hi));
                    }
                    else
                    {
                        lat = new R1Interval(Math.Min(-absLat, bound.Lat.Lo), lat.Hi);
                    }
                    bound = new S2LatLngRect(lat, bound.Lng);
                }
            }
            a       = b;
            aLatLng = bLatLng;
        }