Class S2Loop
- java.lang.Object
-
- com.google.common.geometry.S2Loop
-
- All Implemented Interfaces:
S2Region,S2Shape,Serializable,Comparable<S2Loop>
@GwtCompatible(serializable=true) public final class S2Loop extends Object implements S2Region, Comparable<S2Loop>, Serializable, S2Shape
An S2Loop represents a simple spherical polygon. It consists of a single chain of vertices where the first vertex is implicitly connected to the last. All loops are defined to have a CCW orientation, i.e. the interior of the loop is on the left side of the edges. This implies that a clockwise loop enclosing a small area is interpreted to be a CCW loop enclosing a very large area.Loops are not allowed to have any duplicate vertices (whether adjacent or not), and non- adjacent edges are not allowed to intersect. Loops must have at least 3 vertices (except for the "empty" and "full" loops discussed below). Although these restrictions are not enforced in optimized code, you may get unexpected results if they are violated.
There are two special loops: the "empty" loop contains no points, while the "full" loop contains all points. These loops do not have any edges, but to preserve the invariant that every loop can be represented as a vertex chain, they are defined as having exactly one vertex each (
empty()andfull().)Point containment of loops is defined such that if the sphere is subdivided into faces (loops), every point is contained by exactly one face. This implies that loops do not necessarily contain their vertices.
- See Also:
- Serialized Form
-
-
Nested Class Summary
-
Nested classes/interfaces inherited from interface com.google.common.geometry.S2Shape
S2Shape.MutableEdge, S2Shape.ReferencePoint
-
-
Field Summary
Fields Modifier and Type Field Description static doubleMAX_INTERSECTION_ERRORMax angle that intersections can be off by and yet still be considered collinear.
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method Description intcompareBoundary(S2Loop b)Returns +1 if A contains the boundary of B, -1 if A excludes the boundary of B, and 0 if the boundaries of A and B cross.intcompareTo(S2Loop other)Comparator (needed by Comparable interface)booleancontains(S2Cell target)If this method returns true, the region completely contains the given cell.booleancontains(S2Loop b)Return true if the region contained by this loop is a superset of the region contained by the given other loop.booleancontains(S2Point p)Returns true if the point is contained by the loop.booleancontainsNested(S2Loop b)Given two loops of a polygon, return true if A contains B.booleancontainsOrigin()Returns true if this shape containsS2.origin().intdepth()intdimension()Returns the dimension of the geometry represented by this shape.static S2Loopempty()Returns a new loop with one vertex that defines an empty loop (i.e., a loop with no edges that contains no points.)booleanequals(Object obj)booleanfindValidationError(S2Error error)Returns true if this is *not* a valid loop and setserrorappropriately.booleanfindValidationErrorNoIndex(S2Error error)Like findValidationError(), but skips any checks that would require building the S2ShapeIndex (i.e., self-intersection tests).static S2Loopfull()Returns a new loop with one vertex that creates a full loop (i.e., a loop with no edges that contains all points).doublegetArea()Returns the area of the loop interior, i.e.S2AreaCentroidgetAreaAndCentroid()Returns a pair ofgetArea()andgetCentroid(), computed more efficiently than computing them separately.S2CapgetCapBound()Returns a spherical cap that bounds this loop.S2PointgetCentroid()Returns the true centroid of the loop multiplied by the area of the loop, or null if this loop is empty, full, or invalid.voidgetChainEdge(int chainId, int offset, S2Shape.MutableEdge result)Returns the edge for the given chain id and offset inresult.intgetChainLength(int chainId)Returns the number of edge ids corresponding to the edge chain for the given chain id.intgetChainStart(int chainId)Returns the first edge id corresponding to the edge chain for the given chain id.S2PointgetChainVertex(int chainId, int edgeOffset)Returns the start point of the edge that would be returned byS2Shape.getChainEdge(int, int, com.google.common.geometry.S2Shape.MutableEdge), or the endpoint of the last edge ifedgeOffset==getChainLength(chainId).S1AnglegetDistance(S2Point p)Returns the shortest distance from a point P to this loop, given as the angle formed between P, the origin and the nearest point on the loop to P.voidgetEdge(int index, S2Shape.MutableEdge result)Returns the edge for the given index inresult.S2LatLngRectgetRectBound()Returns a fairly tight bounding latitude-longitude rectangle.S2LatLngRectgetSubregionBound()Returns a slightly looser bounding latitude-longitude rectangle than that returned bygetRectBound().doublegetTurningAngle()Returns the sum of the turning angles at each vertex.inthashCode()booleanhasInterior()Returns true if this shape has an interior, i.e.booleanintersects(S2Loop b)Return true if the region contained by this loop intersects the region contained by the given other loop.voidinvert()Reverse the order of the loop vertices, effectively complementing the region represented by the loop.booleanisEmpty()Returns true if this is the special "empty" loop that contains no points.booleanisEmptyOrFull()Returns true if this loop is either "empty" or "full".booleanisFull()Returns true if this is the special "full" loop that contains all points.booleanisHole()Return true if this loop represents a hole in its containing polygon.booleanisNormalized()Return true if the loop is generally a left-turning, aka counter-clockwise loop.booleanisOriginInside()Return true if the S2:origin() is inside this loop.booleanisValid()Returns true if this loop is valid.static booleanisValid(List<S2Point> vertices)Static version of isValid(), to be used only when an S2Loop instance is not available, but validity of the points must be checked.static S2LoopmakeRegularLoop(S2Point center, S1Angle radius, int numVertices)Create a circle of points with a given center, radius, and number of vertices.static List<S2Point>makeRegularVertices(S2Point center, S1Angle radius, int numVertices)AsmakeRegularLoop(S2Point, S1Angle, int), but returns vertices as a list.booleanmayIntersect(S2Cell target)If this method returns false, the region does not intersect the given cell.static S2LoopnewLoopWithTrustedDetails(List<S2Point> vertices, boolean originInside, S2LatLngRect bound)Fast/unsafe loop initialization.voidnormalize()Invert the loop if necessary so that the area enclosed by the loop is at most 2*Pi.intnumChains()Returns the number of contiguous edge chains in the shape.intnumEdges()Returns the number of edges in this shape.intnumVertices()S2PointorientedVertex(int i)Like vertex(), but this method returns vertices in reverse order if the loop represents a polygon hole.List<S2Point>orientedVertices()Returns the vertices oriented such that left is on the inside.voidsetDepth(int depth)The depth of a loop is defined as its nesting level within its containing polygon.intsign()The sign of a loop is -1 if the loop represents a hole in its containing polygon, and +1 otherwise.S2Loopsimplify(S1Angle tolerance, com.google.common.base.Predicate<S2Point> vertexFilter)Returns a simplified loop, which may be self-intersecting, or null if the entire loop was within the tolerance.StringtoString()S2Pointvertex(int i)For convenience, we make two entire copies of the vertex list available: vertex(n..2*n-1) is mapped to vertex(0..n-1), where n == numVertices().List<S2Point>vertices()Returns an unmodifiable view of the vertices of this polyline.-
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
-
Methods inherited from interface com.google.common.geometry.S2Shape
chain, chains, getReferencePoint
-
-
-
-
Field Detail
-
MAX_INTERSECTION_ERROR
public static final double MAX_INTERSECTION_ERROR
Max angle that intersections can be off by and yet still be considered collinear.- See Also:
- Constant Field Values
-
-
Constructor Detail
-
S2Loop
public S2Loop(List<S2Point> vertices)
Initializes a loop with the given vertices. The last vertex is implicitly connected to the first. All points should be unit length. Loops must have at least 3 vertices (except for the "empty" and "full" loops; seeempty()andfull().- Parameters:
vertices- the vertices for this new loop
-
S2Loop
public S2Loop(S2Cell cell)
Initialize a loop corresponding to the given cell.
-
S2Loop
public S2Loop(S2Loop src)
Copy constructor.
-
-
Method Detail
-
newLoopWithTrustedDetails
public static S2Loop newLoopWithTrustedDetails(List<S2Point> vertices, boolean originInside, S2LatLngRect bound)
Fast/unsafe loop initialization.This constructor provides known good values for bounds and the originInside value. This is intended to be a "fast loop creation" when we already know a lot about the loop. It is primarily used in combination with the fast S2Polygon initializer (
S2Polygon.initWithNestedLoops(java.util.Map)). The last vertex is implicitly connected to the first. All points should be unit length. Loops must have at least 3 vertices, except for the empty and full loops (seeempty()andfull().)- Parameters:
vertices- loop verticesoriginInside- true if the S2::origin() is inside the loopbound- the lat/long bounds of the loop- Returns:
- new loop.
-
makeRegularLoop
public static S2Loop makeRegularLoop(S2Point center, S1Angle radius, int numVertices)
Create a circle of points with a given center, radius, and number of vertices.
-
makeRegularVertices
public static List<S2Point> makeRegularVertices(S2Point center, S1Angle radius, int numVertices)
AsmakeRegularLoop(S2Point, S1Angle, int), but returns vertices as a list.
-
empty
public static final S2Loop empty()
Returns a new loop with one vertex that defines an empty loop (i.e., a loop with no edges that contains no points.)
-
full
public static final S2Loop full()
Returns a new loop with one vertex that creates a full loop (i.e., a loop with no edges that contains all points). Seeempty()for further details.
-
depth
public int depth()
-
setDepth
public void setDepth(int depth)
The depth of a loop is defined as its nesting level within its containing polygon. "Outer shell" loops have depth 0, holes within those loops have depth 1, shells within those holes have depth 2, etc. This field is only used by the S2Polygon implementation.- Parameters:
depth-
-
isHole
public boolean isHole()
Return true if this loop represents a hole in its containing polygon.
-
sign
public int sign()
The sign of a loop is -1 if the loop represents a hole in its containing polygon, and +1 otherwise.
-
numVertices
public int numVertices()
-
vertex
public S2Point vertex(int i)
For convenience, we make two entire copies of the vertex list available: vertex(n..2*n-1) is mapped to vertex(0..n-1), where n == numVertices().
-
orientedVertex
public S2Point orientedVertex(int i)
Like vertex(), but this method returns vertices in reverse order if the loop represents a polygon hole. For example, arguments 0, 1, 2 are mapped to vertices n-1, n-2, n-3, where n == numVertices(). This ensures that the interior of the polygon is always to the left of the vertex chain.
-
vertices
public List<S2Point> vertices()
Returns an unmodifiable view of the vertices of this polyline.
-
orientedVertices
public List<S2Point> orientedVertices()
Returns the vertices oriented such that left is on the inside.
-
isEmpty
public boolean isEmpty()
Returns true if this is the special "empty" loop that contains no points.
-
isFull
public boolean isFull()
Returns true if this is the special "full" loop that contains all points.
-
isEmptyOrFull
public boolean isEmptyOrFull()
Returns true if this loop is either "empty" or "full".
-
numEdges
public int numEdges()
Description copied from interface:S2ShapeReturns the number of edges in this shape.
-
getEdge
public void getEdge(int index, S2Shape.MutableEdge result)Description copied from interface:S2ShapeReturns the edge for the given index inresult. Must not return zero-length edges.- Specified by:
getEdgein interfaceS2Shape- Parameters:
index- which edge to set intoresult, from 0 toS2Shape.numEdges()- 1
-
hasInterior
public boolean hasInterior()
Description copied from interface:S2ShapeReturns true if this shape has an interior, i.e. the shape consists of one or more closed non-intersecting loops.- Specified by:
hasInteriorin interfaceS2Shape
-
containsOrigin
public boolean containsOrigin()
Description copied from interface:S2ShapeReturns true if this shape containsS2.origin(). Should return false for shapes that do not have an interior.- Specified by:
containsOriginin interfaceS2Shape
-
numChains
public int numChains()
Description copied from interface:S2ShapeReturns the number of contiguous edge chains in the shape. For example, a shape whose edges are [AB, BC, CD, AE, EF] may consist of two chains [A, B, C, D] and [A, E, F]. Every chain is assigned a chain id numbered sequentially starting from zero.An empty shape has no chains. A full shape (which contains the entire globe) has one chain with no edges. Other shapes should have at least one chain, and the sum of all valid
chain lengthsshould equalS2Shape.numEdges()(that is, edges may only be used by a single chain).Note that it is always acceptable to implement this method by returning
S2Shape.numEdges()(i.e. every chain consists of a single edge), but this may reduce the efficiency of some algorithms.
-
getChainStart
public int getChainStart(int chainId)
Description copied from interface:S2ShapeReturns the first edge id corresponding to the edge chain for the given chain id. The edge chains must form contiguous, non-overlapping ranges that cover the entire range of edge ids.- Specified by:
getChainStartin interfaceS2Shape- Parameters:
chainId- which edge chain to return its start, from 0 toS2Shape.numChains()- 1
-
getChainLength
public int getChainLength(int chainId)
Description copied from interface:S2ShapeReturns the number of edge ids corresponding to the edge chain for the given chain id. The edge chains must form contiguous, non-overlapping ranges that cover the entire range of edge ids.- Specified by:
getChainLengthin interfaceS2Shape- Parameters:
chainId- which edge chain to return its length, from 0 toS2Shape.numChains()- 1
-
getChainEdge
public void getChainEdge(int chainId, int offset, S2Shape.MutableEdge result)Description copied from interface:S2ShapeReturns the edge for the given chain id and offset inresult. Must not return zero-length edges.- Specified by:
getChainEdgein interfaceS2Shape- Parameters:
chainId- which chain contains the edge to return, from 0 toS2Shape.numChains()- 1offset- position from chain start for the edge to return, from 0 toS2Shape.getChainLength(int)- 1
-
getChainVertex
public S2Point getChainVertex(int chainId, int edgeOffset)
Description copied from interface:S2ShapeReturns the start point of the edge that would be returned byS2Shape.getChainEdge(int, int, com.google.common.geometry.S2Shape.MutableEdge), or the endpoint of the last edge ifedgeOffset==getChainLength(chainId).- Specified by:
getChainVertexin interfaceS2Shape
-
dimension
public int dimension()
Description copied from interface:S2ShapeReturns the dimension of the geometry represented by this shape.- 0 - Point geometry. Each point is represented as a degenerate edge.
- 1 - Polyline geometry. Polyline edges may be degenerate. A shape may represent any number of polylines. Polylines edges may intersect.
- 2 - Polygon geometry. Edges should be oriented such that the polygon interior is always on the left. In theory the edges may be returned in any order, but typically the edges are organized as a collection of edge chains where each chain represents one polygon loop. Polygons may have degeneracies, e.g., degenerate edges or sibling pairs consisting of an edge and its corresponding reversed edge. A polygon loop may also be full (containing all points on the sphere); by convention this is represented as a chain with no edges.
Note that this method allows degenerate geometry of different dimensions to be distinguished, e.g., it allows a point to be distinguished from a polyline or polygon that has been simplified to a single point.
-
compareTo
public int compareTo(S2Loop other)
Comparator (needed by Comparable interface)- Specified by:
compareToin interfaceComparable<S2Loop>
-
isNormalized
public boolean isNormalized()
Return true if the loop is generally a left-turning, aka counter-clockwise loop.
-
normalize
public void normalize()
Invert the loop if necessary so that the area enclosed by the loop is at most 2*Pi.
-
invert
public void invert()
Reverse the order of the loop vertices, effectively complementing the region represented by the loop.
-
getArea
public double getArea()
Returns the area of the loop interior, i.e. the region on the left side of the loop regardless of whether it is a shell or a hole. This value is between 0 and 4*Pi, or explicitly 0 if the loop is invalid.
-
getCentroid
public S2Point getCentroid()
Returns the true centroid of the loop multiplied by the area of the loop, or null if this loop is empty, full, or invalid.The result is not unit length, so you may want to normalize it. Also note that in general, the centroid may not be contained by the loop. See
S2for additional centroid details.We prescale by the loop area for two reasons:
- It is cheaper to compute this way, and
- It makes it easier to compute the centroid of more complicated shapes (by splitting them into disjoint regions and summing their centroids).
Note that the return value is not affected by whether this loop is a "hole" or a "shell".
-
getAreaAndCentroid
public S2AreaCentroid getAreaAndCentroid()
Returns a pair ofgetArea()andgetCentroid(), computed more efficiently than computing them separately.
-
getTurningAngle
public double getTurningAngle()
Returns the sum of the turning angles at each vertex. The return value is positive if the loop is counter-clockwise, negative if the loop is clockwise, and zero if the loop is a great circle.Degenerate and nearly-degenerate loops are handled consistently with
S2Predicates.sign(S2Point, S2Point, S2Point).For example, if a loop has zero area (i.e., it is a very small CCW loop) then the turning angle will always be negative.
This quantity is also called the "geodesic curvature" of the loop.
-
contains
public boolean contains(S2Loop b)
Return true if the region contained by this loop is a superset of the region contained by the given other loop.
-
intersects
public boolean intersects(S2Loop b)
Return true if the region contained by this loop intersects the region contained by the given other loop.
-
containsNested
public boolean containsNested(S2Loop b)
Given two loops of a polygon, return true if A contains B. This version of Contains() is cheap because it does not test for edge intersections. The loops must meet all the S2Polygon requirements; for example this implies that their boundaries may not cross or have any shared edges (although they may have shared vertices).
-
compareBoundary
public int compareBoundary(S2Loop b)
Returns +1 if A contains the boundary of B, -1 if A excludes the boundary of B, and 0 if the boundaries of A and B cross.Shared edges are handled as follows: If XY is a shared edge, define reversed(XY) to be true if XY appears in opposite directions in A and B. Then A contains XY if and only if reversed(XY) == B->isHole(). Intuitively, this checks whether A contains a vanishingly small region extending from the boundary of B toward the interior of the polygon to which loop B belongs.
This method is used for testing containment and intersection of multi-loop polygons. Note that this method is not symmetric, since the result depends on the direction of loop A but not on the direction of loop B (in the absence of shared edges).
- Parameters:
b- the loop to compare against this loop; neither loop may be empty, and ifbis full, then it must not be a hole.
-
getCapBound
public S2Cap getCapBound()
Returns a spherical cap that bounds this loop. It may be expanded slightly such that if the loop contains a point P, then the bound contains P also.- Specified by:
getCapBoundin interfaceS2Region
-
getRectBound
public S2LatLngRect getRectBound()
Returns a fairly tight bounding latitude-longitude rectangle. It is not guaranteed to be as tight as possible, to ensure that if the loop contains a point P, then the bound contains P also.- Specified by:
getRectBoundin interfaceS2Region
-
getSubregionBound
public S2LatLngRect getSubregionBound()
Returns a slightly looser bounding latitude-longitude rectangle than that returned bygetRectBound(). It is not guaranteed that if this loop contains a loop X, then the subregion bound will contain X.getRectBound().
-
contains
public boolean contains(S2Cell target)
If this method returns true, the region completely contains the given cell. Otherwise, either the region does not contain the cell or the containment relationship could not be determined.
-
mayIntersect
public boolean mayIntersect(S2Cell target)
If this method returns false, the region does not intersect the given cell. Otherwise, either region intersects the cell, or the intersection relationship could not be determined.- Specified by:
mayIntersectin interfaceS2Region
-
simplify
public S2Loop simplify(S1Angle tolerance, com.google.common.base.Predicate<S2Point> vertexFilter)
Returns a simplified loop, which may be self-intersecting, or null if the entire loop was within the tolerance.If self-intersections could occur and a valid result is needed, instead use
S2Polygon.initToSimplified(S2Polygon, S1Angle, boolean).Always keeps the first vertex from the loop, and if
vertexFilteris not null, also keeps vertices for whichvertexFilter.shouldKeepVertex()is true.
-
contains
public boolean contains(S2Point p)
Returns true if the point is contained by the loop. The containment test is exact, placingparbitrarily within or without the loop depending on orientation of the edges, such that given two loops sharing an edge, and a point on that edge, only one of the loops will contain it. The point does not need to be normalized.
-
getDistance
public S1Angle getDistance(S2Point p)
Returns the shortest distance from a point P to this loop, given as the angle formed between P, the origin and the nearest point on the loop to P. This angle in radians is equivalent to the arclength along the unit sphere.
-
isOriginInside
public boolean isOriginInside()
Return true if the S2:origin() is inside this loop.Primarily used to serialize internal details about a loop for later fast initialization.
-
isValid
public boolean isValid()
Returns true if this loop is valid.
-
isValid
public static boolean isValid(List<S2Point> vertices)
Static version of isValid(), to be used only when an S2Loop instance is not available, but validity of the points must be checked.- Returns:
- true if the given loop is valid. Creates an instance of S2Loop and defers this call to
isValid().
-
findValidationError
public boolean findValidationError(S2Error error)
Returns true if this is *not* a valid loop and setserrorappropriately. Otherwise returns false and leaveserrorunchanged. Requires that error != null.
-
findValidationErrorNoIndex
public boolean findValidationErrorNoIndex(S2Error error)
Like findValidationError(), but skips any checks that would require building the S2ShapeIndex (i.e., self-intersection tests). This will be used by the S2Polygon implementation, which uses its own index to check for loop self-intersection.
-
-