mirror of
https://github.com/wassname/geopandas.git
synced 2026-09-13 12:30:29 +08:00
ENH: First commit at common class for geo ops
Moves the GeoSeries geo-aware ops into a base GeoPandas class. And gets all tests passing
This commit is contained in:
@@ -0,0 +1,433 @@
|
||||
from warnings import warn
|
||||
|
||||
from shapely.geometry import MultiPoint, MultiLineString, MultiPolygon
|
||||
from shapely.geometry.base import BaseGeometry
|
||||
from shapely.ops import cascaded_union, unary_union
|
||||
import shapely.affinity as affinity
|
||||
|
||||
import numpy as np
|
||||
from pandas import Series, DataFrame
|
||||
|
||||
import geopandas as gpd
|
||||
|
||||
|
||||
def _geo_op(this, other, op):
|
||||
"""Operation that returns a GeoSeries"""
|
||||
if isinstance(other, gpd.GeoSeries):
|
||||
crs = this.crs
|
||||
if crs != other.crs:
|
||||
warn('GeoSeries crs mismatch: {0} and {1}'.format(this.crs,
|
||||
other.crs))
|
||||
this, other = this.align(other)
|
||||
return gpd.GeoSeries([getattr(this_elem, op)(other_elem)
|
||||
for this_elem, other_elem in zip(this, other)],
|
||||
index=this.index, crs=crs)
|
||||
else:
|
||||
return gpd.GeoSeries([getattr(s, op)(other) for s in this],
|
||||
index=this.index, crs=this.crs)
|
||||
|
||||
|
||||
# TODO: think about merging with _geo_op
|
||||
def _series_op(this, other, op, **kwargs):
|
||||
"""Geometric operation that returns a pandas Series"""
|
||||
if isinstance(other, gpd.GeoSeries):
|
||||
this, other = this.align(other)
|
||||
return Series([getattr(this_elem, op)(other_elem, **kwargs)
|
||||
for this_elem, other_elem in zip(this, other)],
|
||||
index=this.index)
|
||||
else:
|
||||
return Series([getattr(s, op)(other, **kwargs) for s in this],
|
||||
index=this.index)
|
||||
|
||||
def _geo_unary_op(this, op):
|
||||
"""Unary operation that returns a GeoSeries"""
|
||||
return gpd.GeoSeries([getattr(geom, op) for geom in this],
|
||||
index=this.index, crs=this.crs)
|
||||
|
||||
def _series_unary_op(this, op):
|
||||
"""Unary operation that returns a Series"""
|
||||
return Series([getattr(geom, op) for geom in this],
|
||||
index=this.index)
|
||||
|
||||
|
||||
class GeoPandasBase(object):
|
||||
@property
|
||||
def area(self):
|
||||
"""Return the area of each geometry in the GeoSeries"""
|
||||
return _series_unary_op(self, 'area')
|
||||
|
||||
@property
|
||||
def geom_type(self):
|
||||
"""Return the geometry type of each geometry in the GeoSeries"""
|
||||
return _series_unary_op(self, 'geom_type')
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
"""Return the geometry type of each geometry in the GeoSeries"""
|
||||
return self.geom_type
|
||||
|
||||
@property
|
||||
def length(self):
|
||||
"""Return the length of each geometry in the GeoSeries"""
|
||||
return _series_unary_op(self, 'length')
|
||||
|
||||
@property
|
||||
def is_valid(self):
|
||||
"""Return True for each valid geometry, else False"""
|
||||
return _series_unary_op(self, 'is_valid')
|
||||
|
||||
@property
|
||||
def is_empty(self):
|
||||
"""Return True for each empty geometry, False for non-empty"""
|
||||
return _series_unary_op(self, 'is_empty')
|
||||
|
||||
@property
|
||||
def is_simple(self):
|
||||
"""Return True for each simple geometry, else False"""
|
||||
return _series_unary_op(self, 'is_simple')
|
||||
|
||||
@property
|
||||
def is_ring(self):
|
||||
"""Return True for each geometry that is a closed ring, else False"""
|
||||
# operates on the exterior, so can't use _series_unary_op()
|
||||
return Series([geom.exterior.is_ring for geom in self.geometry],
|
||||
index=self.index)
|
||||
|
||||
#
|
||||
# Unary operations that return a GeoSeries
|
||||
#
|
||||
|
||||
@property
|
||||
def boundary(self):
|
||||
"""Return the bounding geometry for each geometry"""
|
||||
return _geo_unary_op(self, 'boundary')
|
||||
|
||||
@property
|
||||
def centroid(self):
|
||||
"""Return the centroid of each geometry in the GeoSeries"""
|
||||
return _geo_unary_op(self, 'centroid')
|
||||
|
||||
@property
|
||||
def convex_hull(self):
|
||||
"""Return the convex hull of each geometry"""
|
||||
return _geo_unary_op(self, 'convex_hull')
|
||||
|
||||
@property
|
||||
def envelope(self):
|
||||
"""Return a bounding rectangle for each geometry"""
|
||||
return _geo_unary_op(self, 'envelope')
|
||||
|
||||
@property
|
||||
def exterior(self):
|
||||
"""Return the outer boundary of each polygon"""
|
||||
# TODO: return empty geometry for non-polygons
|
||||
return _geo_unary_op(self, 'exterior')
|
||||
|
||||
@property
|
||||
def interiors(self):
|
||||
"""Return the interior rings of each polygon"""
|
||||
# TODO: return empty list or None for non-polygons
|
||||
return _geo_unary_op(self, 'interiors')
|
||||
|
||||
def representative_point(self):
|
||||
"""Return a GeoSeries of points guaranteed to be in each geometry"""
|
||||
return gpd.GeoSeries([geom.representative_point()
|
||||
for geom in self.geometry],
|
||||
index=self.index)
|
||||
|
||||
#
|
||||
# Reduction operations that return a Shapely geometry
|
||||
#
|
||||
|
||||
@property
|
||||
def cascaded_union(self):
|
||||
"""Deprecated: Return the unary_union of all geometries"""
|
||||
return cascaded_union(self.values)
|
||||
|
||||
@property
|
||||
def unary_union(self):
|
||||
"""Return the union of all geometries"""
|
||||
return unary_union(self.values)
|
||||
|
||||
#
|
||||
# Binary operations that return a pandas Series
|
||||
#
|
||||
|
||||
def contains(self, other):
|
||||
"""Return True for all geometries that contain *other*, else False"""
|
||||
return _series_op(self, other, 'contains')
|
||||
|
||||
def equals(self, other):
|
||||
"""Return True for all geometries that equal *other*, else False"""
|
||||
return _series_op(self, other, 'equals')
|
||||
|
||||
def almost_equals(self, other, decimal=6):
|
||||
"""Return True for all geometries that is approximately equal to *other*, else False"""
|
||||
# TODO: pass precision argument
|
||||
return _series_op(self, other, 'almost_equals', decimal=decimal)
|
||||
|
||||
def equals_exact(self, other, tolerance):
|
||||
"""Return True for all geometries that equal *other* to a given tolerance, else False"""
|
||||
# TODO: pass tolerance argument.
|
||||
return _series_op(self, other, 'equals_exact', tolerance=tolerance)
|
||||
|
||||
def crosses(self, other):
|
||||
"""Return True for all geometries that cross *other*, else False"""
|
||||
return _series_op(self, other, 'crosses')
|
||||
|
||||
def disjoint(self, other):
|
||||
"""Return True for all geometries that are disjoint with *other*, else False"""
|
||||
return _series_op(self, other, 'disjoint')
|
||||
|
||||
def intersects(self, other):
|
||||
"""Return True for all geometries that intersect *other*, else False"""
|
||||
return _series_op(self, other, 'intersects')
|
||||
|
||||
def overlaps(self, other):
|
||||
"""Return True for all geometries that overlap *other*, else False"""
|
||||
return _series_op(self, other, 'overlaps')
|
||||
|
||||
def touches(self, other):
|
||||
"""Return True for all geometries that touch *other*, else False"""
|
||||
return _series_op(self, other, 'touches')
|
||||
|
||||
def within(self, other):
|
||||
"""Return True for all geometries that are within *other*, else False"""
|
||||
return _series_op(self, other, 'within')
|
||||
|
||||
def distance(self, other):
|
||||
"""Return distance of each geometry to *other*"""
|
||||
return _series_op(self, other, 'distance')
|
||||
|
||||
#
|
||||
# Binary operations that return a GeoSeries
|
||||
#
|
||||
|
||||
def difference(self, other):
|
||||
"""Return the set-theoretic difference of each geometry with *other*"""
|
||||
return _geo_op(self, other, 'difference')
|
||||
|
||||
def symmetric_difference(self, other):
|
||||
"""Return the symmetric difference of each geometry with *other*"""
|
||||
return _geo_op(self, other, 'symmetric_difference')
|
||||
|
||||
def union(self, other):
|
||||
"""Return the set-theoretic union of each geometry with *other*"""
|
||||
return _geo_op(self, other, 'union')
|
||||
|
||||
def intersection(self, other):
|
||||
"""Return the set-theoretic intersection of each geometry with *other*"""
|
||||
return _geo_op(self, other, 'intersection')
|
||||
|
||||
#
|
||||
# Other operations
|
||||
#
|
||||
|
||||
@property
|
||||
def bounds(self):
|
||||
"""Return a DataFrame of minx, miny, maxx, maxy values of geometry objects"""
|
||||
bounds = np.array([geom.bounds for geom in self.geometry])
|
||||
return DataFrame(bounds,
|
||||
columns=['minx', 'miny', 'maxx', 'maxy'],
|
||||
index=self.index)
|
||||
|
||||
@property
|
||||
def total_bounds(self):
|
||||
"""Return a single bounding box (minx, miny, maxx, maxy) for all geometries
|
||||
|
||||
This is a shortcut for calculating the min/max x and y bounds individually.
|
||||
"""
|
||||
|
||||
b = self.bounds
|
||||
return (b['minx'].min(),
|
||||
b['miny'].min(),
|
||||
b['maxx'].max(),
|
||||
b['maxy'].max())
|
||||
|
||||
def buffer(self, distance, resolution=16):
|
||||
return gpd.GeoSeries([geom.buffer(distance, resolution)
|
||||
for geom in self.geometry],
|
||||
index=self.index, crs=self.crs)
|
||||
|
||||
def simplify(self, *args, **kwargs):
|
||||
return gpd.GeoSeries([geom.simplify(*args, **kwargs)
|
||||
for geom in self.geometry],
|
||||
index=self.index, crs=self.crs)
|
||||
|
||||
def relate(self, other):
|
||||
raise NotImplementedError
|
||||
|
||||
def project(self, other, normalized=False):
|
||||
"""
|
||||
Return the distance along each geometry nearest to *other*
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : BaseGeometry or GeoSeries
|
||||
The *other* geometry to computed projected point from.
|
||||
normalized : boolean
|
||||
If normalized is True, return the distance normalized to
|
||||
the length of the object.
|
||||
|
||||
The project method is the inverse of interpolate.
|
||||
"""
|
||||
|
||||
return _series_op(self, other, 'project', normalized=normalized)
|
||||
|
||||
def interpolate(self, distance, normalized=False):
|
||||
"""
|
||||
Return a point at the specified distance along each geometry
|
||||
|
||||
Parameters
|
||||
----------
|
||||
distance : float or Series of floats
|
||||
Distance(s) along the geometries at which a point should be returned
|
||||
normalized : boolean
|
||||
If normalized is True, distance will be interpreted as a fraction
|
||||
of the geometric object's length.
|
||||
"""
|
||||
|
||||
return gpd.GeoSeries([s.interpolate(distance, normalized)
|
||||
for s in self.geometry],
|
||||
index=self.index, crs=self.crs)
|
||||
|
||||
def translate(self, xoff=0.0, yoff=0.0, zoff=0.0):
|
||||
"""
|
||||
Shift the coordinates of the GeoSeries.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
xoff, yoff, zoff : float, float, float
|
||||
Amount of offset along each dimension.
|
||||
xoff, yoff, and zoff for translation along the x, y, and z
|
||||
dimensions respectively.
|
||||
|
||||
See shapely manual for more information:
|
||||
http://toblerity.org/shapely/manual.html#affine-transformations
|
||||
"""
|
||||
|
||||
return gpd.GeoSeries([affinity.translate(s, xoff, yoff, zoff)
|
||||
for s in self.geometry],
|
||||
index=self.index, crs=self.crs)
|
||||
|
||||
# Shift is simply an alias for translate
|
||||
shift = translate
|
||||
|
||||
def rotate(self, angle, origin='center', use_radians=False):
|
||||
"""
|
||||
Rotate the coordinates of the GeoSeries.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
angle : float
|
||||
The angle of rotation can be specified in either degrees (default)
|
||||
or radians by setting use_radians=True. Positive angles are
|
||||
counter-clockwise and negative are clockwise rotations.
|
||||
origin : string, Point, or tuple (x, y)
|
||||
The point of origin can be a keyword 'center' for the bounding box
|
||||
center (default), 'centroid' for the geometry's centroid, a Point
|
||||
object or a coordinate tuple (x, y).
|
||||
use_radians : boolean
|
||||
Whether to interpret the angle of rotation as degrees or radians
|
||||
|
||||
See shapely manual for more information:
|
||||
http://toblerity.org/shapely/manual.html#affine-transformations
|
||||
"""
|
||||
|
||||
return gpd.GeoSeries([affinity.rotate(s, angle, origin=origin,
|
||||
use_radians=use_radians) for s in self.geometry],
|
||||
index=self.index, crs=self.crs)
|
||||
|
||||
def scale(self, xfact=1.0, yfact=1.0, zfact=1.0, origin='center'):
|
||||
"""
|
||||
Scale the geometries of the GeoSeries along each (x, y, z) dimension.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
xfact, yfact, zfact : float, float, float
|
||||
Scaling factors for the x, y, and z dimensions respectively.
|
||||
origin : string, Point, or tuple
|
||||
The point of origin can be a keyword 'center' for the 2D bounding
|
||||
box center (default), 'centroid' for the geometry's 2D centroid, a
|
||||
Point object or a coordinate tuple (x, y, z).
|
||||
|
||||
Note: Negative scale factors will mirror or reflect coordinates.
|
||||
|
||||
See shapely manual for more information:
|
||||
http://toblerity.org/shapely/manual.html#affine-transformations
|
||||
"""
|
||||
|
||||
return gpd.GeoSeries([affinity.scale(s, xfact, yfact, zfact,
|
||||
origin=origin) for s in self.geometry], index=self.index,
|
||||
crs=self.crs)
|
||||
|
||||
def skew(self, xs=0.0, ys=0.0, origin='center', use_radians=False):
|
||||
"""
|
||||
Shear/Skew the geometries of the GeoSeries by angles along x and y dimensions.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
xs, ys : float, float
|
||||
The shear angle(s) for the x and y axes respectively. These can be
|
||||
specified in either degrees (default) or radians by setting
|
||||
use_radians=True.
|
||||
origin : string, Point, or tuple (x, y)
|
||||
The point of origin can be a keyword 'center' for the bounding box
|
||||
center (default), 'centroid' for the geometry's centroid, a Point
|
||||
object or a coordinate tuple (x, y).
|
||||
use_radians : boolean
|
||||
Whether to interpret the shear angle(s) as degrees or radians
|
||||
|
||||
See shapely manual for more information:
|
||||
http://toblerity.org/shapely/manual.html#affine-transformations
|
||||
"""
|
||||
|
||||
return gpd.GeoSeries([affinity.skew(s, xs, ys, origin=origin,
|
||||
use_radians=use_radians) for s in self.geometry],
|
||||
index=self.index, crs=self.crs)
|
||||
|
||||
|
||||
#
|
||||
# Implement standard operators for GeoSeries
|
||||
#
|
||||
|
||||
def __contains__(self, other):
|
||||
"""Allow tests of the form "geom in s"
|
||||
|
||||
Tests whether a GeoSeries contains a geometry.
|
||||
|
||||
Note: This is not the same as the geometric method "contains".
|
||||
"""
|
||||
if isinstance(other, BaseGeometry):
|
||||
return np.any(self.equals(other))
|
||||
else:
|
||||
return False
|
||||
|
||||
def __xor__(self, other):
|
||||
"""Implement ^ operator as for builtin set type"""
|
||||
return self.symmetric_difference(other)
|
||||
|
||||
def __or__(self, other):
|
||||
"""Implement | operator as for builtin set type"""
|
||||
return self.union(other)
|
||||
|
||||
def __and__(self, other):
|
||||
"""Implement & operator as for builtin set type"""
|
||||
return self.intersection(other)
|
||||
|
||||
def __sub__(self, other):
|
||||
"""Implement - operator as for builtin set type"""
|
||||
return self.difference(other)
|
||||
|
||||
|
||||
def _array_input(arr):
|
||||
if isinstance(arr, (MultiPoint, MultiLineString, MultiPolygon)):
|
||||
# Prevent against improper length detection when input is a
|
||||
# Multi*
|
||||
geom = arr
|
||||
arr = np.empty(1, dtype=object)
|
||||
arr[0] = geom
|
||||
|
||||
return arr
|
||||
|
||||
|
||||
+3
-411
@@ -7,10 +7,10 @@ import pyproj
|
||||
from shapely.geometry import shape, Polygon, Point
|
||||
from shapely.geometry.collection import GeometryCollection
|
||||
from shapely.geometry.base import BaseGeometry
|
||||
from shapely.ops import cascaded_union, unary_union, transform
|
||||
import shapely.affinity as affinity
|
||||
from shapely.ops import transform
|
||||
|
||||
from geopandas.plotting import plot_series
|
||||
from geopandas.base import GeoPandasBase
|
||||
|
||||
OLD_PANDAS = issubclass(Series, np.ndarray)
|
||||
|
||||
@@ -30,7 +30,7 @@ def _convert_array_args(args):
|
||||
args = ([args[0]],)
|
||||
return args
|
||||
|
||||
class GeoSeries(Series):
|
||||
class GeoSeries(GeoPandasBase, Series):
|
||||
"""A Series object designed to store shapely geometry objects."""
|
||||
_metadata = ['name', 'crs']
|
||||
|
||||
@@ -90,414 +90,6 @@ class GeoSeries(Series):
|
||||
data.crs = self.crs
|
||||
data.to_file(filename, driver, **kwargs)
|
||||
|
||||
#
|
||||
# Internal methods
|
||||
#
|
||||
|
||||
def _geo_op(self, other, op):
|
||||
"""Operation that returns a GeoSeries"""
|
||||
if isinstance(other, GeoSeries):
|
||||
if self.crs != other.crs:
|
||||
warn('GeoSeries crs mismatch: {0} and {1}'.format(self.crs, other.crs))
|
||||
this, other = self.align(other)
|
||||
return GeoSeries([getattr(this_elem, op)(other_elem)
|
||||
for this_elem, other_elem in zip(this, other)],
|
||||
index=this.index, crs=self.crs)
|
||||
else:
|
||||
return GeoSeries([getattr(s, op)(other) for s in self],
|
||||
index=self.index, crs=self.crs)
|
||||
|
||||
# TODO: think about merging with _geo_op
|
||||
def _series_op(self, other, op, **kwargs):
|
||||
"""Geometric operation that returns a pandas Series"""
|
||||
if isinstance(other, GeoSeries):
|
||||
this, other = self.align(other)
|
||||
return Series([getattr(this_elem, op)(other_elem, **kwargs) for
|
||||
this_elem, other_elem in zip(this, other)],
|
||||
index=this.index)
|
||||
else:
|
||||
return Series([getattr(s, op)(other, **kwargs) for s in self],
|
||||
index=self.index)
|
||||
|
||||
def _geo_unary_op(self, op):
|
||||
"""Unary operation that returns a GeoSeries"""
|
||||
return GeoSeries([getattr(geom, op) for geom in self],
|
||||
index=self.index, crs=self.crs)
|
||||
|
||||
def _series_unary_op(self, op):
|
||||
"""Unary operation that returns a Series"""
|
||||
return Series([getattr(geom, op) for geom in self],
|
||||
index=self.index)
|
||||
|
||||
#
|
||||
# Implementation of Shapely methods
|
||||
#
|
||||
|
||||
#
|
||||
# Unary operations that return a Series
|
||||
#
|
||||
|
||||
@property
|
||||
def area(self):
|
||||
"""Return the area of each geometry in the GeoSeries"""
|
||||
return self._series_unary_op('area')
|
||||
|
||||
@property
|
||||
def geom_type(self):
|
||||
"""Return the geometry type of each geometry in the GeoSeries"""
|
||||
return self._series_unary_op('geom_type')
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
"""Return the geometry type of each geometry in the GeoSeries"""
|
||||
return self.geom_type
|
||||
|
||||
@property
|
||||
def length(self):
|
||||
"""Return the length of each geometry in the GeoSeries"""
|
||||
return self._series_unary_op('length')
|
||||
|
||||
@property
|
||||
def is_valid(self):
|
||||
"""Return True for each valid geometry, else False"""
|
||||
return self._series_unary_op('is_valid')
|
||||
|
||||
@property
|
||||
def is_empty(self):
|
||||
"""Return True for each empty geometry, False for non-empty"""
|
||||
return self._series_unary_op('is_empty')
|
||||
|
||||
@property
|
||||
def is_simple(self):
|
||||
"""Return True for each simple geometry, else False"""
|
||||
return self._series_unary_op('is_simple')
|
||||
|
||||
@property
|
||||
def is_ring(self):
|
||||
"""Return True for each geometry that is a closed ring, else False"""
|
||||
# operates on the exterior, so can't use _series_unary_op()
|
||||
return Series([geom.exterior.is_ring for geom in self],
|
||||
index=self.index)
|
||||
|
||||
#
|
||||
# Unary operations that return a GeoSeries
|
||||
#
|
||||
|
||||
@property
|
||||
def boundary(self):
|
||||
"""Return the bounding geometry for each geometry"""
|
||||
return self._geo_unary_op('boundary')
|
||||
|
||||
@property
|
||||
def centroid(self):
|
||||
"""Return the centroid of each geometry in the GeoSeries"""
|
||||
return self._geo_unary_op('centroid')
|
||||
|
||||
@property
|
||||
def convex_hull(self):
|
||||
"""Return the convex hull of each geometry"""
|
||||
return self._geo_unary_op('convex_hull')
|
||||
|
||||
@property
|
||||
def envelope(self):
|
||||
"""Return a bounding rectangle for each geometry"""
|
||||
return self._geo_unary_op('envelope')
|
||||
|
||||
@property
|
||||
def exterior(self):
|
||||
"""Return the outer boundary of each polygon"""
|
||||
# TODO: return empty geometry for non-polygons
|
||||
return self._geo_unary_op('exterior')
|
||||
|
||||
@property
|
||||
def interiors(self):
|
||||
"""Return the interior rings of each polygon"""
|
||||
# TODO: return empty list or None for non-polygons
|
||||
return self._geo_unary_op('interiors')
|
||||
|
||||
def representative_point(self):
|
||||
"""Return a GeoSeries of points guaranteed to be in each geometry"""
|
||||
return GeoSeries([geom.representative_point() for geom in self],
|
||||
index=self.index)
|
||||
|
||||
#
|
||||
# Reduction operations that return a Shapely geometry
|
||||
#
|
||||
|
||||
@property
|
||||
def cascaded_union(self):
|
||||
"""Deprecated: Return the unary_union of all geometries"""
|
||||
return cascaded_union(self.values)
|
||||
|
||||
@property
|
||||
def unary_union(self):
|
||||
"""Return the union of all geometries"""
|
||||
return unary_union(self.values)
|
||||
|
||||
#
|
||||
# Binary operations that return a GeoSeries
|
||||
#
|
||||
|
||||
def difference(self, other):
|
||||
"""Return the set-theoretic difference of each geometry with *other*"""
|
||||
return self._geo_op(other, 'difference')
|
||||
|
||||
def symmetric_difference(self, other):
|
||||
"""Return the symmetric difference of each geometry with *other*"""
|
||||
return self._geo_op(other, 'symmetric_difference')
|
||||
|
||||
def union(self, other):
|
||||
"""Return the set-theoretic union of each geometry with *other*"""
|
||||
return self._geo_op(other, 'union')
|
||||
|
||||
def intersection(self, other):
|
||||
"""Return the set-theoretic intersection of each geometry with *other*"""
|
||||
return self._geo_op(other, 'intersection')
|
||||
|
||||
#
|
||||
# Binary operations that return a pandas Series
|
||||
#
|
||||
|
||||
def contains(self, other):
|
||||
"""Return True for all geometries that contain *other*, else False"""
|
||||
return self._series_op(other, 'contains')
|
||||
|
||||
def equals(self, other):
|
||||
"""Return True for all geometries that equal *other*, else False"""
|
||||
return self._series_op(other, 'equals')
|
||||
|
||||
def almost_equals(self, other, decimal=6):
|
||||
"""Return True for all geometries that is approximately equal to *other*, else False"""
|
||||
# TODO: pass precision argument
|
||||
return self._series_op(other, 'almost_equals', decimal=decimal)
|
||||
|
||||
def equals_exact(self, other, tolerance):
|
||||
"""Return True for all geometries that equal *other* to a given tolerance, else False"""
|
||||
# TODO: pass tolerance argument.
|
||||
return self._series_op(other, 'equals_exact', tolerance=tolerance)
|
||||
|
||||
def crosses(self, other):
|
||||
"""Return True for all geometries that cross *other*, else False"""
|
||||
return self._series_op(other, 'crosses')
|
||||
|
||||
def disjoint(self, other):
|
||||
"""Return True for all geometries that are disjoint with *other*, else False"""
|
||||
return self._series_op(other, 'disjoint')
|
||||
|
||||
def intersects(self, other):
|
||||
"""Return True for all geometries that intersect *other*, else False"""
|
||||
return self._series_op(other, 'intersects')
|
||||
|
||||
def overlaps(self, other):
|
||||
"""Return True for all geometries that overlap *other*, else False"""
|
||||
return self._series_op(other, 'overlaps')
|
||||
|
||||
def touches(self, other):
|
||||
"""Return True for all geometries that touch *other*, else False"""
|
||||
return self._series_op(other, 'touches')
|
||||
|
||||
def within(self, other):
|
||||
"""Return True for all geometries that are within *other*, else False"""
|
||||
return self._series_op(other, 'within')
|
||||
|
||||
def distance(self, other):
|
||||
"""Return distance of each geometry to *other*"""
|
||||
return self._series_op(other, 'distance')
|
||||
|
||||
#
|
||||
# Other operations
|
||||
#
|
||||
|
||||
@property
|
||||
def bounds(self):
|
||||
"""Return a DataFrame of minx, miny, maxx, maxy values of geometry objects"""
|
||||
bounds = np.array([geom.bounds for geom in self])
|
||||
return DataFrame(bounds,
|
||||
columns=['minx', 'miny', 'maxx', 'maxy'],
|
||||
index=self.index)
|
||||
|
||||
@property
|
||||
def total_bounds(self):
|
||||
"""Return a single bounding box (minx, miny, maxx, maxy) for all geometries
|
||||
|
||||
This is a shortcut for calculating the min/max x and y bounds individually.
|
||||
"""
|
||||
|
||||
b = self.bounds
|
||||
return (b['minx'].min(),
|
||||
b['miny'].min(),
|
||||
b['maxx'].max(),
|
||||
b['maxy'].max())
|
||||
|
||||
def buffer(self, distance, resolution=16):
|
||||
return GeoSeries([geom.buffer(distance, resolution) for geom in self],
|
||||
index=self.index, crs=self.crs)
|
||||
|
||||
def simplify(self, *args, **kwargs):
|
||||
return GeoSeries([geom.simplify(*args, **kwargs) for geom in self],
|
||||
index=self.index, crs=self.crs)
|
||||
|
||||
def relate(self, other):
|
||||
raise NotImplementedError
|
||||
|
||||
def project(self, other, normalized=False):
|
||||
"""
|
||||
Return the distance along each geometry nearest to *other*
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : BaseGeometry or GeoSeries
|
||||
The *other* geometry to computed projected point from.
|
||||
normalized : boolean
|
||||
If normalized is True, return the distance normalized to
|
||||
the length of the object.
|
||||
|
||||
The project method is the inverse of interpolate.
|
||||
"""
|
||||
|
||||
return self._series_op(other, 'project', normalized=normalized)
|
||||
|
||||
def interpolate(self, distance, normalized=False):
|
||||
"""
|
||||
Return a point at the specified distance along each geometry
|
||||
|
||||
Parameters
|
||||
----------
|
||||
distance : float or Series of floats
|
||||
Distance(s) along the geometries at which a point should be returned
|
||||
normalized : boolean
|
||||
If normalized is True, distance will be interpreted as a fraction
|
||||
of the geometric object's length.
|
||||
"""
|
||||
|
||||
return GeoSeries([s.interpolate(distance, normalized) for s in self],
|
||||
index=self.index, crs=self.crs)
|
||||
|
||||
def translate(self, xoff=0.0, yoff=0.0, zoff=0.0):
|
||||
"""
|
||||
Shift the coordinates of the GeoSeries.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
xoff, yoff, zoff : float, float, float
|
||||
Amount of offset along each dimension.
|
||||
xoff, yoff, and zoff for translation along the x, y, and z
|
||||
dimensions respectively.
|
||||
|
||||
See shapely manual for more information:
|
||||
http://toblerity.org/shapely/manual.html#affine-transformations
|
||||
"""
|
||||
|
||||
return GeoSeries([affinity.translate(s, xoff, yoff, zoff) for s in self],
|
||||
index=self.index, crs=self.crs)
|
||||
|
||||
# Shift is simply an alias for translate
|
||||
shift = translate
|
||||
|
||||
def rotate(self, angle, origin='center', use_radians=False):
|
||||
"""
|
||||
Rotate the coordinates of the GeoSeries.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
angle : float
|
||||
The angle of rotation can be specified in either degrees (default)
|
||||
or radians by setting use_radians=True. Positive angles are
|
||||
counter-clockwise and negative are clockwise rotations.
|
||||
origin : string, Point, or tuple (x, y)
|
||||
The point of origin can be a keyword 'center' for the bounding box
|
||||
center (default), 'centroid' for the geometry's centroid, a Point
|
||||
object or a coordinate tuple (x, y).
|
||||
use_radians : boolean
|
||||
Whether to interpret the angle of rotation as degrees or radians
|
||||
|
||||
See shapely manual for more information:
|
||||
http://toblerity.org/shapely/manual.html#affine-transformations
|
||||
"""
|
||||
|
||||
return GeoSeries([affinity.rotate(s, angle, origin=origin,
|
||||
use_radians=use_radians) for s in self], index=self.index,
|
||||
crs=self.crs)
|
||||
|
||||
def scale(self, xfact=1.0, yfact=1.0, zfact=1.0, origin='center'):
|
||||
"""
|
||||
Scale the geometries of the GeoSeries along each (x, y, z) dimension.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
xfact, yfact, zfact : float, float, float
|
||||
Scaling factors for the x, y, and z dimensions respectively.
|
||||
origin : string, Point, or tuple
|
||||
The point of origin can be a keyword 'center' for the 2D bounding
|
||||
box center (default), 'centroid' for the geometry's 2D centroid, a
|
||||
Point object or a coordinate tuple (x, y, z).
|
||||
|
||||
Note: Negative scale factors will mirror or reflect coordinates.
|
||||
|
||||
See shapely manual for more information:
|
||||
http://toblerity.org/shapely/manual.html#affine-transformations
|
||||
"""
|
||||
|
||||
return GeoSeries([affinity.scale(s, xfact, yfact, zfact,
|
||||
origin=origin) for s in self], index=self.index, crs=self.crs)
|
||||
|
||||
def skew(self, xs=0.0, ys=0.0, origin='center', use_radians=False):
|
||||
"""
|
||||
Shear/Skew the geometries of the GeoSeries by angles along x and y dimensions.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
xs, ys : float, float
|
||||
The shear angle(s) for the x and y axes respectively. These can be
|
||||
specified in either degrees (default) or radians by setting
|
||||
use_radians=True.
|
||||
origin : string, Point, or tuple (x, y)
|
||||
The point of origin can be a keyword 'center' for the bounding box
|
||||
center (default), 'centroid' for the geometry's centroid, a Point
|
||||
object or a coordinate tuple (x, y).
|
||||
use_radians : boolean
|
||||
Whether to interpret the shear angle(s) as degrees or radians
|
||||
|
||||
See shapely manual for more information:
|
||||
http://toblerity.org/shapely/manual.html#affine-transformations
|
||||
"""
|
||||
|
||||
return GeoSeries([affinity.skew(s, xs, ys, origin=origin,
|
||||
use_radians=use_radians) for s in self], index=self.index,
|
||||
crs=self.crs)
|
||||
|
||||
#
|
||||
# Implement standard operators for GeoSeries
|
||||
#
|
||||
|
||||
def __contains__(self, other):
|
||||
"""Allow tests of the form "geom in s"
|
||||
|
||||
Tests whether a GeoSeries contains a geometry.
|
||||
|
||||
Note: This is not the same as the geometric method "contains".
|
||||
"""
|
||||
if isinstance(other, BaseGeometry):
|
||||
return np.any(self.equals(other))
|
||||
else:
|
||||
return False
|
||||
|
||||
def __xor__(self, other):
|
||||
"""Implement ^ operator as for builtin set type"""
|
||||
return self.symmetric_difference(other)
|
||||
|
||||
def __or__(self, other):
|
||||
"""Implement | operator as for builtin set type"""
|
||||
return self.union(other)
|
||||
|
||||
def __and__(self, other):
|
||||
"""Implement & operator as for builtin set type"""
|
||||
return self.intersection(other)
|
||||
|
||||
def __sub__(self, other):
|
||||
"""Implement - operator as for builtin set type"""
|
||||
return self.difference(other)
|
||||
|
||||
#
|
||||
# Implement pandas methods
|
||||
#
|
||||
|
||||
Reference in New Issue
Block a user