pygplates.PolygonOnSphere¶
-
class
pygplates.
PolygonOnSphere
¶ Bases:
pygplates.GeometryOnSphere
Represents a polygon on the surface of the unit length sphere. Polygons are equality (
==
,!=
) comparable (but not hashable - cannot be used as a key in adict
). SeePointOnSphere
for an overview of equality in the presence of limited floating-point precision.A polygon instance is both:
- a sequence of
points
- seeget_points()
, and - a sequence of
segments
(between adjacent points) - seeget_segments()
.
In addition a polygon instance is directly iterable over its points (without having to use
get_points()
):polygon = pygplates.PolygonOnSphere(points) for point in polygon: ...
…and so the following operations for accessing the points are supported:
Operation Result len(polygon)
number of vertices in polygon for p in polygon
iterates over the vertices p of polygon p in polygon
True
if p is equal to a vertex of polygonp not in polygon
False
if p is equal to a vertex of polygonpolygon[i]
the vertex of polygon at index i polygon[i:j]
slice of polygon from i to j polygon[i:j:k]
slice of polygon from i to j with step k Note
if p in polygon
does not test whether a pointp
is inside the the interior of a polygon - useis_point_in_polygon()
for that instead.Since a PolygonOnSphere is immutable it contains no operations or methods that modify its state (such as adding or removing points). This is similar to other immutable types in python such asstr
.So instead of modifying an existing polygon you will need to create a newPolygonOnSphere
instance as the following example demonstrates:# Get a list of points from an existing 'polygon'. points = list(polygon) # Modify the points list somehow. points[0] = pygplates.PointOnSphere(...) points.append(pygplates.PointOnSphere(...)) # 'polygon' now references a new PolygonOnSphere instance. polygon = pygplates.PolygonOnSphere(points)
The following example demonstrates creating a
PolygonOnSphere
from aPolylineOnSphere
:polygon = pygplates.PolygonOnSphere(polyline)
Note
A polygon closes the loop between its last and first points so there’s no need to make the first and last points equal.
-
__init__
(...)¶ A PolygonOnSphere object can be constructed in more than one way…
- __init__(points)
Create a polygon from a sequence of (x,y,z) or (latitude,longitude) points.
param points: A sequence of (x,y,z) points, or (latitude,longitude) points (in degrees). type points: Any sequence of PointOnSphere
orLatLonPoint
or tuple (float,float,float) or tuple (float,float)raises: InvalidLatLonError if any latitude or longitude is invalid raises: ViolatedUnitVectorInvariantError if any (x,y,z) is not unit magnitude raises: InvalidPointsForPolygonConstructionError if sequence has less than three points or if any two points (adjacent in the points sequence) are antipodal to each other (on opposite sides of the globe) Note
The sequence must contain at least three points in order to be a valid polygon, otherwise InvalidPointsForPolygonConstructionError will be raised.
During creation, a
GreatCircleArc
is created between each adjacent pair of of points in points - seeget_segments()
. The last arc is created between the last and first points to close the loop of the polygon. For this reason you do not need to ensure that the first and last points have the same position (although it’s not an error if this is the case because the final arc will then just have a zero length).It is not an error for adjacent points in the sequence to be coincident. In this case each
GreatCircleArc
between two such adjacent points will have zero length (GreatCircleArc.is_zero_length()
will returnTrue
) and will have no rotation axis (GreatCircleArc.get_rotation_axis()
will raise an error).The following example shows a few different ways to create a
polygon
:points = [] points.append(pygplates.PointOnSphere(...)) points.append(pygplates.PointOnSphere(...)) points.append(pygplates.PointOnSphere(...)) polygon = pygplates.PolygonOnSphere(points) points = [] points.append((lat1,lon1)) points.append((lat2,lon2)) points.append((lat3,lon3)) polygon = pygplates.PolygonOnSphere(points) points = [] points.append([x1,y1,z1]) points.append([x2,y2,z2]) points.append([x3,y3,z3]) polygon = pygplates.PolygonOnSphere(points)
If you have latitude/longitude values but they are not a sequence of tuples or if the latitude/longitude order is swapped then the following examples demonstrate how you could restructure them:
# Flat lat/lon array. points = numpy.array([lat1, lon1, lat2, lon2, lat3, lon3]) polygon = pygplates.PolygonOnSphere(zip(points[::2],points[1::2])) # Flat lon/lat list (ie, different latitude/longitude order). points = [lon1, lat1, lon2, lat2, lon3, lat3] polygon = pygplates.PolygonOnSphere(zip(points[1::2],points[::2])) # Separate lat/lon arrays. lats = numpy.array([lat1, lat2, lat3]) lons = numpy.array([lon1, lon2, lon3]) polygon = pygplates.PolygonOnSphere(zip(lats,lons)) # Lon/lat list of tuples (ie, different latitude/longitude order). points = [(lon1, lat1), (lon2, lat2), (lon3, lat3)] polygon = pygplates.PolygonOnSphere([(lat,lon) for lon, lat in points])
- __init__(geometry, [allow_one_or_two_points=True])
Create a polygon from a
GeometryOnSphere
.param geometry: The point, multi-point, polyline or polygon geometry to convert from. type geometry: GeometryOnSphere
param allow_one_or_two_points: Whether geometry is allowed to be a PointOnSphere
or aMultiPointOnSphere
containing only one or two points - if allowed then one of those points is duplicated since a PolygonOnSphere requires at least three points - default isTrue
.type allow_one_or_two_points: bool raises: InvalidPointsForPolygonConstructionError if geometry is a PointOnSphere
, or aMultiPointOnSphere
with one or two points (and allow_one_or_two_points isFalse
), or if any two consecutive points in aMultiPointOnSphere
are antipodal to each other (on opposite sides of the globe)If allow_one_or_two_points is
True
then geometry can bePointOnSphere
,MultiPointOnSphere
,PolylineOnSphere
orPolygonOnSphere
. However if allow_one_or_two_points isFalse
then geometry must be aPolygonOnSphere
, or aMultiPointOnSphere
orPolylineOnSphere
containing at least three points to avoid raising InvalidPointsForPolygonConstructionError.During creation, a
GreatCircleArc
is created between each adjacent pair of geometry points - seeget_segments()
.It is not an error for adjacent points in a geometry sequence to be coincident. In this case each
GreatCircleArc
between two such adjacent points will have zero length (GreatCircleArc.is_zero_length()
will returnTrue
) and will have no rotation axis (GreatCircleArc.get_rotation_axis()
will raise an error). However if two such adjacent points are antipodal (on opposite sides of the globe) then InvalidPointsForPolygonConstructionError will be raisedTo create a PolygonOnSphere from any geometry type:
polygon = pygplates.PolygonOnSphere(geometry)
To create a PolygonOnSphere from any geometry containing at least three points:
try: polygon = pygplates.PolygonOnSphere(geometry, allow_one_or_two_points=False) except pygplates.InvalidPointsForPolygonConstructionError: ... # Handle failure to convert 'geometry' to a PolygonOnSphere.
Methods
__init__
(…)A PolygonOnSphere object can be constructed in more than one way… clone
()Create a duplicate of this geometry (derived) instance. distance
(geometry1, geometry2, …)[staticmethod] Returns the (minimum) distance between two geometries (in radians). get_arc_length
()Returns the total arc length of this polygon (in radians). get_area
()Returns the area of this polygon (on a sphere of unit radius). get_boundary_centroid
()Returns the boundary centroid of this polygon. get_interior_centroid
()Returns the interior centroid of this polygon. get_orientation
()Returns whether this polygon is clockwise or counter-clockwise. get_points
()Returns a read-only sequence of points
in this geometry.get_segments
()Returns a read-only sequence of segments
in this polygon.get_signed_area
()Returns the signed area of this polygon (on a sphere of unit radius). is_point_in_polygon
(point)Determines whether the specified point lies within the interior of this polygon. partition
(geometry, …)Partition a geometry into optional inside/outside lists of partitioned geometry pieces. to_lat_lon_array
()Returns the sequence of points, in this geometry, as a numpy array of (latitude,longitude) pairs (in degrees). to_lat_lon_list
()Returns the sequence of points, in this geometry, as (latitude,longitude) tuples (in degrees). to_lat_lon_point_list
()Returns the sequence of points, in this geometry, as lat lon points
.to_tessellated
(tessellate_radians)Returns a new polygon that is tessellated version of this polygon. to_xyz_array
()Returns the sequence of points, in this geometry, as a numpy array of (x,y,z) triplets. to_xyz_list
()Returns the sequence of points, in this geometry, as (x,y,z) cartesian coordinate tuples. -
class
Orientation
¶ Bases:
Boost.Python.enum
-
class
PartitionResult
¶ Bases:
Boost.Python.enum
-
get_arc_length
()¶ Returns the total arc length of this polygon (in radians).
Return type: float
-
get_area
()¶ Returns the area of this polygon (on a sphere of unit radius).
Return type: float The area is essentially the absolute value of the
signed area
.To convert to area on the Earth’s surface, multiply the result by the Earth radius squared (see
Earth
).
-
get_boundary_centroid
()¶ Returns the boundary centroid of this polygon.
Return type: PointOnSphere
The boundary centroid is calculated as a weighted average of the mid-points of the
great circle arcs
of this polygon with weighting proportional to the individual arc lengths.Note that if you want a centroid closer to the centre-of-mass of the polygon interior then use
get_interior_centroid()
instead.
-
get_interior_centroid
()¶ Returns the interior centroid of this polygon.
Return type: PointOnSphere
The interior centroid is calculated as a weighted average of the centroids of spherical triangles formed by the
great circle arcs
of this polygon and its (boundary) centroid with weighting proportional to the signed area of each individual spherical triangle. The three vertices of each spherical triangle consist of the polygon (boundary) centroid and the two end points of a great circle arc.This centroid is useful when the centre-of-mass of the polygon interior is desired. For example, the interior centroid of a bottom-heavy, pear-shaped polygon will be closer to the bottom of the polygon. This centroid is not exactly at the centre-of-mass, but it will be a lot closer to the real centre-of-mass than
get_boundary_centroid()
.
-
get_orientation
()¶ Returns whether this polygon is clockwise or counter-clockwise.
Return type: PolygonOnSphere.Orientation If this polygon is clockwise (when viewed from above the surface of the sphere) then PolygonOnSphere.Orientation.clockwise is returned, otherwise PolygonOnSphere.Orientation.counter_clockwise is returned.
if polygon.get_orientation() == pygplates.PolygonOnSphere.Orientation.clockwise: print 'Orientation is clockwise' else: print 'Orientation is counter-clockwise'
-
get_segments
()¶ Returns a read-only sequence of
segments
in this polygon.Return type: a read-only sequence of GreatCircleArc
The following operations for accessing the great circle arcs in the returned read-only sequence are supported:
Operation Result len(seq)
number of segments of the polygon for s in seq
iterates over the segments s of the polygon s in seq
True
if s is an segment of the polygons not in seq
False
if s is an segment of the polygonseq[i]
the segment of the polygon at index i seq[i:j]
slice of segments of the polygon from i to j seq[i:j:k]
slice of segments of the polygon from i to j with step k Note
Between each adjacent pair of
points
there is ansegment
such that the number of points equals the number of segments.The following example demonstrates some uses of the above operations:
polygon = pygplates.PolygonOnSphere(points) ... segments = polygon.get_segments() for segment in segments: if not segment.is_zero_length(): segment_midpoint_direction = segment.get_arc_direction(0.5) first_segment = segments[0] last_segment = segments[-1]
Note
The
end point
of the last segment is equal to thestart point
of the first segment.Note
The returned sequence is read-only and cannot be modified.
Note
If you want a modifiable sequence consider wrapping the returned sequence in a
list
using something likesegments = list(polygon.get_segments())
but note that modifying thelist
(eg, appending a new segment) will not modify the original polygon.
-
get_signed_area
()¶ Returns the signed area of this polygon (on a sphere of unit radius).
Return type: float If this polygon is clockwise (when viewed from above the surface of the sphere) then the returned area will be negative, otherwise it will be positive. However if you only want to determine the orientation of this polygon then
get_orientation()
is more efficient than comparing the sign of the area.To convert to signed area on the Earth’s surface, multiply the result by the Earth radius squared (see
Earth
).See also
-
is_point_in_polygon
(point)¶ Determines whether the specified point lies within the interior of this polygon.
Parameters: point ( PointOnSphere
orLatLonPoint
or (latitude,longitude), in degrees, or (x,y,z)) – the point to be testedReturn type: bool Test if a (latitude, longitude) point is inside a polygon:
if polygon.is_point_in_polygon((latitude, longitude)): ...
-
partition
(geometry[, partitioned_geometries_inside][, partitioned_geometries_outside])¶ Partition a geometry into optional inside/outside lists of partitioned geometry pieces.
Parameters: - geometry (
GeometryOnSphere
) – the geometry to be partitioned - partitioned_geometries_inside (
list
ofGeometryOnSphere
, or None) – optional list of geometries partitioned inside this polygon (note that the list is not cleared first) - partitioned_geometries_outside (
list
ofGeometryOnSphere
, or None) – optional list of geometries partitioned outside this polygon (note that the list is not cleared first)
Return type: The returned result is:
- PolygonOnSphere.PartitionResult.inside: if geometry is entirely inside this polygon, or
- PolygonOnSphere.PartitionResult.outside: if geometry is entirely outside this polygon, or
- PolygonOnSphere.PartitionResult.intersecting: if geometry intersects this polygon.
If partitioned_geometries_inside is specified then it must be a
list
and any part of geometry inside this polygon is added to it. So if PolygonOnSphere.PartitionResult.inside is returned this means geometry is added and if PolygonOnSphere.PartitionResult.intersecting is returned this means the partitioned parts of geometry inside this polygon are added.If partitioned_geometries_outside is specified then if must be a
list
and any part of geometry outside this polygon is added to it. So if PolygonOnSphere.PartitionResult.outside is returned this means geometry is added and if PolygonOnSphere.PartitionResult.intersecting is returned this means the partitioned parts of geometry outside this polygon are added.Note
Partitioning
point
geometries returns only PolygonOnSphere.PartitionResult.inside or PolygonOnSphere.PartitionResult.outside.If a partitioned
multi-point
contains points both inside and outside this polygon then PolygonOnSphere.PartitionResult.intersecting is returned. In this case the points inside are added as a singleMultiPointOnSphere
to partitioned_geometries_inside (if specified) and the points outside are added as a singleMultiPointOnSphere
to partitioned_geometries_outside (if specified).Warning
Support for partitioning apolygon
geometry is partial.If a polygon geometry is entirely inside or entirely outside this polygon then it will get added as a polygon as expected (to partitioned_geometries_inside or partitioned_geometries_outside respectively if specified).But if a polygon geometry intersects this polygon, then partitioned polylines (not polygons) are added (to the optional inside/outside lists).This is also how it is in the Assign Plate IDs dialog in GPlates.In a future release this will be fixed to always return polygons.Test if a polyline is entirely inside a polygon:
if polygon.partition(polyline) == pygplates.PolygonOnSphere.PartitionResult.inside: ...
Find the bits of a polyline that are outside a group of continental polygons:
# Start with the original polyline to partition. oceanic_polylines = [polyline] for continental_polygon in continental_polygons: # Iterate over the polylines that are outside the continental polygons processed so far. current_oceanic_polylines = oceanic_polylines # The new list of polylines will also be outside the current continental polygon. oceanic_polylines = [] for current_oceanic_polyline in current_oceanic_polylines: continental_polygon.partition(current_oceanic_polyline, partitioned_geometries_outside=oceanic_polylines) # The final result is in 'oceanic_polylines'.
- geometry (
-
to_tessellated
(tessellate_radians)¶ Returns a new polygon that is tessellated version of this polygon.
Parameters: tessellate_radians (float) – maximum tessellation angle (in radians) Return type: PolygonOnSphere
Adjacent points (in the returned tessellated polygon) are separated by no more than tessellate_radians on the globe.
Create a polygon tessellated to 2 degrees:
tessellated_polygon = polygon.to_tessellated(math.radians(2))
Note
Since a PolygonOnSphere is immutable it cannot be modified. Which is why a new (tessellated) PolygonOnSphere is returned.
Note
The distance between adjacent points (in the tessellated polygon) will not be exactly uniform. This is because each
segment
in the original polygon is tessellated to the nearest integer number of points (that keeps that segment under the threshold) and hence each original segment will have a slightly different tessellation angle.See also
- a sequence of