Exemplo n.º 1
0
 // Constructs a region representing all points within the given radius of
 // any point in the given S2ShapeIndex.
 public S2ShapeIndexBufferedRegion(S2ShapeIndex index, S1ChordAngle radius)
 {
     Radius            = radius;
     radius_successor_ = radius.Successor();
     query_            = new S2ClosestEdgeQuery(index);
     query_.Options_.IncludeInteriors = (true);
 }
Exemplo n.º 2
0
 public EdgeEnumerator(S2ShapeIndex index)
 {
     index_     = index;
     shape_id_  = -1;
     num_edges_ = 0;
     edge_id_   = -1;
 }
Exemplo n.º 3
0
 // Equivalent to the constructor above.
 public void Init(S2ShapeIndex index, S1ChordAngle radius)
 {
     Radius            = radius;
     radius_successor_ = radius.Successor();
     query_.Init(index);
     query_.Options_.IncludeInteriors = (true);
 }
Exemplo n.º 4
0
 /// <summary>
 /// Returns a vector containing all edges in the given S2ShapeIndexCell vector.
 /// (The result is returned as an output parameter so that the same storage can
 /// be reused, rather than allocating a new temporary vector each time.)
 /// </summary>
 private static void GetShapeEdges(S2ShapeIndex index, List <S2ShapeIndexCell> cells, ShapeEdgeVector shape_edges)
 {
     shape_edges.Clear();
     foreach (var cell in cells)
     {
         AppendShapeEdges(index, cell, shape_edges);
     }
 }
Exemplo n.º 5
0
 /// <summary>
 /// If "swapped" is true, the loops A and B have been swapped.  This affects
 /// how arguments are passed to the given loop relation, since for example
 /// A.Contains(B) is not the same as B.Contains(A).
 /// </summary>
 public IndexCrosser(S2ShapeIndex a_index, S2ShapeIndex b_index, CrossingType type, EdgePairVisitor visitor, bool swapped)
 {
     a_index_           = a_index;
     b_index_           = b_index;
     visitor_           = visitor;
     min_crossing_sign_ = type == CrossingType.INTERIOR ? 1 : 0;
     swapped_           = swapped;
     b_query_           = new S2CrossingEdgeQuery(b_index_);
 }
Exemplo n.º 6
0
    // Convert the contents of an S2ShapeIndex to the format above.  The index may
    // contain S2Shapes of any type.  Shapes are reordered if necessary so that
    // all point geometry (shapes of dimension 0) are first, followed by all
    // polyline geometry, followed by all polygon geometry.
    public static string ToDebugString(this S2ShapeIndex index)
    {
        StringBuilder sb = new();

        for (var dim = 0; dim < 3; ++dim)
        {
            if (dim > 0)
            {
                sb.Append('#');
            }
            var count = 0;
            foreach (var shape in index)
            {
                if (shape == null || shape.Dimension() != dim)
                {
                    continue;
                }

                sb.Append((count > 0) ? " | " : (dim > 0) ? " " : "");
                for (var i = 0; i < shape.NumChains(); ++i, ++count)
                {
                    if (i > 0)
                    {
                        sb.Append((dim == 2) ? "; " : " | ");
                    }
                    var chain = shape.GetChain(i);
                    if (chain.Length == 0)
                    {
                        System.Diagnostics.Debug.Assert(dim == 2);
                        sb.Append("full");
                    }
                    else
                    {
                        sb.Append(AppendVertex(shape.GetEdge(chain.Start).V0));
                    }
                    var limit = chain.Start + chain.Length;
                    if (dim != 1)
                    {
                        --limit;
                    }
                    for (var e = chain.Start; e < limit; ++e)
                    {
                        sb.Append(", ");
                        sb.Append(AppendVertex(shape.GetEdge(e).V1));
                    }
                }
            }
            // Example output: "# #", "0:0 # #", "# # 0:0, 0:1, 1:0"
            if (dim == 1 || (dim == 0 && count > 0))
            {
                sb.Append(' ');
            }
        }
        return(sb.ToString());
    }
Exemplo n.º 7
0
        private int[] GetContainingShapes(S2MinDistanceTarget target, S2ShapeIndex index, int max_shapes)
        {
            var shape_ids = new SortedSet <Int32>();

            target.VisitContainingShapes(
                index, (S2Shape containing_shape, S2Point target_point) => {
                shape_ids.Add(containing_shape.Id);
                return(shape_ids.Count < max_shapes);
            });
            return(shape_ids.ToArray());
        }
Exemplo n.º 8
0
 /// <summary>
 /// Appends all edges in the given S2ShapeIndexCell to the given vector.
 /// </summary>
 private static void AppendShapeEdges(S2ShapeIndex index, S2ShapeIndexCell cell, ShapeEdgeVector shape_edges)
 {
     for (int s = 0; s < cell.NumClipped(); ++s)
     {
         var clipped   = cell.Clipped(s);
         var shape     = index.Shape(clipped.ShapeId);
         var num_edges = clipped.NumEdges;
         for (int i = 0; i < num_edges; i++)
         {
             shape_edges.Add(new ShapeEdge(shape, clipped.Edge(i)));
         }
     }
 }
Exemplo n.º 9
0
    // Returns the maximum dimension of any shape in the index.  Returns -1 if the
    // index does not contain any shapes.
    //
    // Note that the dimension does *not* depend on whether the shapes in the
    // index contain any points; for example, the dimension of an empty point set
    // is 0, and the dimension of an empty polygon is 2.
    public static int GetDimension(S2ShapeIndex index)
    {
        int dim = -1;

        for (int i = 0; i < index.NumShapeIds(); ++i)
        {
            var shape = index.Shape(i);
            if (shape != null)
            {
                dim = Math.Max(dim, shape.Dimension());
            }
        }
        return(dim);
    }
Exemplo n.º 10
0
    // Returns the number of points (objects of dimension zero) in the index.
    // Note that polyline and polygon vertices are *not* included in this count.
    public static int GetNumPoints(S2ShapeIndex index)
    {
        int count = 0;

        for (int i = 0; i < index.NumShapeIds(); ++i)
        {
            var shape = index.Shape(i);
            if (shape != null && shape.Dimension() == 0)
            {
                count += shape.NumEdges();
            }
        }
        return(count);
    }
Exemplo n.º 11
0
    // Returns the total length of all polylines in the index.  Returns zero if no
    // polylines are present.
    //
    // All edges are modeled as spherical geodesics.  The result can be converted
    // to a distance on the Earth's surface (with a worst-case error of 0.562%
    // near the equator) using the functions in s2earth.h.
    public static S1Angle GetLength(S2ShapeIndex index)
    {
        var length = S1Angle.Zero;

        for (int i = 0; i < index.NumShapeIds(); ++i)
        {
            var shape = index.Shape(i);
            if (shape != null)
            {
                length += S2.GetLength(shape);
            }
        }
        return(length);
    }
Exemplo n.º 12
0
    // Returns the total perimeter of all polygons in the index (including both
    // "shells" and "holes").  Returns zero if no polygons are present.
    //
    // All edges are modeled as spherical geodesics.  The result can be converted
    // to a distance on the Earth's surface (with a worst-case error of 0.562%
    // near the equator) using the functions in s2earth.h.
    public static S1Angle GetPerimeter(S2ShapeIndex index)
    {
        var perimeter = S1Angle.Zero;

        for (int i = 0; i < index.NumShapeIds(); ++i)
        {
            var shape = index.Shape(i);
            if (shape != null)
            {
                perimeter += S2.GetPerimeter(shape);
            }
        }
        return(perimeter);
    }
Exemplo n.º 13
0
    // Returns the total area of all polygons in the index.  Returns zero if no
    // polygons are present.  This method has good relative accuracy for both very
    // large and very small regions.  Note that the result may exceed 4*Pi if the
    // index contains overlapping polygons.
    //
    // All edges are modeled as spherical geodesics.  The result can be converted
    // to an area on the Earth's surface (with a worst-case error of 0.900% near
    // the poles) using the functions in s2earth.h.
    public static double GetArea(S2ShapeIndex index)
    {
        double area = 0;

        for (int i = 0; i < index.NumShapeIds(); ++i)
        {
            var shape = index.Shape(i);
            if (shape != null)
            {
                area += S2.GetArea(shape);
            }
        }
        return(area);
    }
Exemplo n.º 14
0
    // Returns the centroid of all shapes whose dimension is maximal within the
    // index, multiplied by the measure of those shapes.  For example, if the
    // index contains points and polylines, then the result is defined as the
    // centroid of the polylines multiplied by the total length of those
    // polylines.  The points would be ignored when computing the centroid.
    //
    // The measure of a given shape is defined as follows:
    //
    //  - For dimension 0 shapes, the measure is shape.num_edges().
    //  - For dimension 1 shapes, the measure is GetLength(shape).
    //  - For dimension 2 shapes, the measure is GetArea(shape).
    //
    // Note that the centroid is not unit length, so you may need to call
    // Normalize() before passing it to other S2 functions.  Note that this
    // function returns (0, 0, 0) if the index contains no geometry.
    //
    // The centroid is scaled by the total measure of the shapes for two reasons:
    // (1) it is cheaper to compute this way, and (2) this makes it easier to
    // compute the centroid of a collection of shapes (since the individual
    // centroids can simply be summed).
    public static S2Point GetCentroid(S2ShapeIndex index)
    {
        int dim      = GetDimension(index);
        var centroid = S2Point.Empty;

        for (int i = 0; i < index.NumShapeIds(); ++i)
        {
            var shape = index.Shape(i);
            if (shape != null && shape.Dimension() == dim)
            {
                centroid += S2.GetCentroid(shape);
            }
        }
        return(centroid);
    }
Exemplo n.º 15
0
 public VisitIntersectingShapesTest(S2ShapeIndex index)
 {
     index_  = index;
     iter_   = new(index);
     region_ = new(index);
     // Create an S2ShapeIndex for each shape in the original index, so that we
     // can use MayIntersect() and Contains() to determine the status of
     // individual shapes.
     for (int s = 0; s < index_.NumShapeIds(); ++s)
     {
         var shape_index = new MutableS2ShapeIndex();
         shape_index.Add(new S2WrappedShape(index_.Shape(s)));
         shape_indexes_.Add(shape_index);
     }
 }
Exemplo n.º 16
0
    // Like CountEdges(), but stops once "max_edges" edges have been found (in
    // which case the current running total is returned).
    public static int GetCountEdgesUpTo(this S2ShapeIndex index, int max_edges)
    {
        int num_edges = 0;

        foreach (var shape in index)
        {
            if (shape == null)
            {
                continue;
            }
            num_edges += shape.NumEdges();
            if (num_edges >= max_edges)
            {
                break;
            }
        }
        return(num_edges);
    }
Exemplo n.º 17
0
        /// <summary>
        /// Visits all pairs of crossing edges in the given S2ShapeIndex, terminating
        /// early if the given EdgePairVisitor function returns false (in which case
        /// VisitCrossings returns false as well).  "type" indicates whether all
        /// crossings should be visited, or only interior crossings.
        ///
        /// If "need_adjacent" is false, then edge pairs of the form (AB, BC) may
        /// optionally be ignored (even if the two edges belong to different edge
        /// chains).  This option exists for the benefit of FindSelfIntersection(),
        /// which does not need such edge pairs (see below).
        /// </summary>
        private static bool VisitCrossings(S2ShapeIndex index, CrossingType type, bool need_adjacent, EdgePairVisitor visitor)
        {
            // TODO(ericv): Use brute force if the total number of edges is small enough
            // (using a larger threshold if the S2ShapeIndex is not constructed yet).
            var count = index.GetEnumerableCount();

            for (var pos = 0; pos < count; pos++)
            {
                var shape_edges = new ShapeEdgeVector();
                var icell       = index.GetIndexCell(pos);
                var indexCell   = icell.Value.Item2;
                GetShapeEdges(index, indexCell, shape_edges);
                if (!VisitCrossings(shape_edges, type, need_adjacent, visitor))
                {
                    return(false);
                }
            }
            return(true);
        }
Exemplo n.º 18
0
 // Returns the total number of edges in all indexed shapes.  This method takes
 // time linear in the number of shapes.
 public static int GetCountEdges(this S2ShapeIndex index)
 {
     return(GetCountEdgesUpTo(index, int.MaxValue));
 }
Exemplo n.º 19
0
 /// <summary>
 /// Returns a vector containing all edges in the given S2ShapeIndexCell.
 /// (The result is returned as an output parameter so that the same storage can
 /// be reused, rather than allocating a new temporary vector each time.)
 /// </summary>
 private static void GetShapeEdges(S2ShapeIndex index, S2ShapeIndexCell cell, ShapeEdgeVector shape_edges)
 {
     shape_edges.Clear();
     AppendShapeEdges(index, cell, shape_edges);
 }
Exemplo n.º 20
0
 // Finds all polygons in the given "query_index" that completely contain a
 // connected component of the target geometry.  (For example, if the
 // target consists of 10 points, this method finds polygons that contain
 // any of those 10 points.)  For each such polygon, "visitor" is called
 // with the S2Shape of the polygon along with a point of the target
 // geometry that is contained by that polygon.
 //
 // Optionally, any polygon that intersects the target geometry may also be
 // returned.  In other words, this method returns all polygons that
 // contain any connected component of the target, along with an arbitrary
 // subset of the polygons that intersect the target.
 //
 // For example, suppose that "query_index" contains two abutting polygons
 // A and B.  If the target consists of two points "a" contained by A and
 // "b" contained by B, then both A and B are returned.  But if the target
 // consists of the edge "ab", then any subset of {A, B} could be returned
 // (because both polygons intersect the target but neither one contains
 // the edge "ab").
 //
 // If "visitor" returns false, this method terminates early and returns
 // false as well.  Otherwise returns true.
 //
 // NOTE(ericv): This method exists only for the purpose of implementing
 // S2ClosestEdgeQuery::Options::include_interiors() efficiently.  Its API is
 // unlikely to be useful for other purposes.
 //
 // CAVEAT: Containing shapes may be visited more than once.
 public abstract bool VisitContainingShapes(S2ShapeIndex query_index, ShapeVisitor visitor);
Exemplo n.º 21
0
 private EdgeEnumerator(S2ShapeIndex index, int shape_id, int num_edges, int edge_id)
 {
     index_ = index; shape_id_ = shape_id; num_edges_ = num_edges; edge_id_ = edge_id;
 }
Exemplo n.º 22
0
 // Constructs an iterator positioned as specified.  By default iterators
 // are unpositioned, since this avoids an extra seek in this situation
 // where one of the seek methods (such as Locate) is immediately called.
 //
 // If you want to position the iterator at the beginning, e.g. in order to
 // loop through the entire index, do this instead:
 //
 //   for (S2ShapeIndex.Iterator it(out index, S2ShapeIndex.InitialPosition.BEGIN);
 //        !it.done(); it.Next()) { ... }
 public Enumerator(S2ShapeIndex index)
 {
     _it = index.GetNewEnumerator();
 }
Exemplo n.º 23
0
    /// <summary>
    /// Verifies that two S2ShapeIndexes have identical contents (including all the
    /// S2Shapes in both indexes).
    /// </summary>
    public static void ExpectEqual(S2ShapeIndex a, S2ShapeIndex b)
    {
        // Check that both indexes have identical shapes.
        Assert.True(a.NumShapeIds() == b.NumShapeIds());
        for (int shape_id = 0; shape_id < a.NumShapeIds(); ++shape_id)
        {
            var a_shape = a.Shape(shape_id);
            var b_shape = b.Shape(shape_id);
            if (a_shape == null || b_shape == null)
            {
                Assert.True(a_shape == b_shape);
            }
            else
            {
                Assert.True(a_shape.Id == b_shape.Id);
                Assert.True(a_shape == b_shape);
            }
        }

        // Check that both indexes have identical cell contents.
        var a_it     = a.GetNewEnumerator();
        var b_it     = b.GetNewEnumerator();
        var aHasNext = a_it.MoveNext();
        var bHasNext = b_it.MoveNext();

        while (aHasNext && bHasNext)
        {
            Assert.True(a_it.Current.Item1 == b_it.Current.Item1);
            var a_cell = a_it.Current.Item2;
            var b_cell = b_it.Current.Item2;
            Assert.True(a_cell.NumClipped() == b_cell.NumClipped());
            for (var i = 0; i < a_cell.NumClipped(); ++i)
            {
                var a_clipped = a_cell.Clipped(i);
                var b_clipped = b_cell.Clipped(i);
                Assert.True(a_clipped.ShapeId == b_clipped.ShapeId);
                Assert.True(a_clipped.ContainsCenter == b_clipped.ContainsCenter);
                Assert.True(a_clipped.NumEdges == b_clipped.NumEdges);
                for (int j = 0; j < a_clipped.NumEdges; ++j)
                {
                    Assert.True(a_clipped.Edge(j) == b_clipped.Edge(j));
                }
            }
            aHasNext = a_it.MoveNext();
            bHasNext = b_it.MoveNext();
        }
        Assert.True(!bHasNext);

        // Spot-check the other iterator methods.  (We know that both indexes have
        // the same contents, so any differences are due to implementation bugs.)
        a_it.Reset();
        b_it.Reset();
        a_it.MoveNext();
        b_it.MoveNext();
        Assert.True(a_it.Current.Item1 == b_it.Current.Item1);
        aHasNext = a_it.MoveNext();
        bHasNext = b_it.MoveNext();
        if (aHasNext)
        {
            Assert.True(a_it.Current.Item1 == b_it.Current.Item1);
            Assert.True(aHasNext == bHasNext);
            // Assert.True(a_it.MovePrevious());
            // Assert.True(b_it.MovePrevious());
            Assert.True(a_it.Current.Item1 == b_it.Current.Item1);
        }
        // Assert.False(a_it.MovePrevious());
        // Assert.False(b_it.MovePrevious());
        // a_it.Finish();
        // b_it.Finish();
        // Assert.True(a_it.id() == b_it.id());
        // a_it.Seek(a_it.id().Next);
        // b_it.Seek(b_it.id().Next);
        // Assert.True(a_it.id() == b_it.id());
    }
Exemplo n.º 24
0
        /// <summary>
        /// Like the above, but visits all pairs of crossing edges where one edge comes
        /// from each S2ShapeIndex.
        ///
        /// CAVEAT: Crossings may be visited more than once.
        /// </summary>
        public static bool VisitCrossingEdgePairs(S2ShapeIndex a_index, S2ShapeIndex b_index, CrossingType type, EdgePairVisitor visitor)
        {
            // We look for S2CellId ranges where the indexes of A and B overlap, and
            // then test those edges for crossings.

            // TODO(ericv): Use brute force if the total number of edges is small enough
            // (using a larger threshold if the S2ShapeIndex is not constructed yet).
            var ai = new RangeEnumerator(a_index);
            var bi = new RangeEnumerator(b_index);
            var ab = new IndexCrosser(a_index, b_index, type, visitor, false);  // Tests A against B
            var ba = new IndexCrosser(b_index, a_index, type, visitor, true);   // Tests B against A

            while (!ai.Done() || !bi.Done())
            {
                if (ai.RangeMax < bi.RangeMin)
                {
                    // The A and B cells don't overlap, and A precedes B.
                    ai.SeekTo(bi);
                }
                else if (bi.RangeMax < ai.RangeMin)
                {
                    // The A and B cells don't overlap, and B precedes A.
                    bi.SeekTo(ai);
                }
                else
                {
                    // One cell contains the other.  Determine which cell is larger.
                    var ab_relation = ai.Id.LowestOnBit() - bi.Id.LowestOnBit();
                    if (ab_relation > 0)
                    {
                        // A's index cell is larger.
                        if (!ab.VisitCrossings(ai, bi))
                        {
                            return(false);
                        }
                    }
                    else if (ab_relation < 0)
                    {
                        // B's index cell is larger.
                        if (!ba.VisitCrossings(bi, ai))
                        {
                            return(false);
                        }
                    }
                    else
                    {
                        // The A and B cells are the same.
                        if (ai.Cell.NumEdges() > 0 && bi.Cell.NumEdges() > 0)
                        {
                            if (!ab.VisitCellCellCrossings(ai.Cell, bi.Cell))
                            {
                                return(false);
                            }
                        }
                        ai.MoveNext();
                        bi.MoveNext();
                    }
                }
            }
            return(true);
        }
Exemplo n.º 25
0
 // Construct a new RangeIterator positioned at the first cell of the index.
 public RangeEnumerator(S2ShapeIndex index)
 {
     _index = index;
     _it    = new S2ShapeIndex.Enumerator(index);
     //Refresh();
 }
Exemplo n.º 26
0
 // Convenience constructor that accepts an S1Angle for the radius.
 // REQUIRES: radius >= S1Angle.Zero()
 public S2ShapeIndexBufferedRegion(S2ShapeIndex index, S1Angle radius)
     : this(index, new S1ChordAngle(radius))
 {
 }
Exemplo n.º 27
0
        /// <summary>
        /// Visits all pairs of crossing edges in the given S2ShapeIndex, terminating
        /// early if the given EdgePairVisitor function returns false (in which case
        /// VisitCrossings returns false as well).  "type" indicates whether all
        /// crossings should be visited, or only interior crossings.
        ///
        /// CAVEAT: Crossings may be visited more than once.
        /// </summary>
        public static bool VisitCrossingEdgePairs(S2ShapeIndex index, CrossingType type, EdgePairVisitor visitor)
        {
            var needAdjacent = type == CrossingType.ALL;

            return(VisitCrossings(index, type, needAdjacent, visitor));
        }