Files
california-equity-git/.venv/lib/python3.12/site-packages/geopandas/base.py
2024-09-28 22:56:00 -07:00

6203 lines
209 KiB
Python

import warnings
from warnings import warn
import numpy as np
import pandas as pd
from pandas import DataFrame, Series
import shapely
from shapely.geometry import MultiPoint, box
from shapely.geometry.base import BaseGeometry
from . import _compat as compat
from .array import GeometryArray, GeometryDtype, points_from_xy
def is_geometry_type(data):
"""
Check if the data is of geometry dtype.
Does not include object array of shapely scalars.
"""
if isinstance(getattr(data, "dtype", None), GeometryDtype):
# GeometryArray, GeoSeries and Series[GeometryArray]
return True
else:
return False
def _delegate_binary_method(op, this, other, align, *args, **kwargs):
# type: (str, GeoSeries, GeoSeries) -> GeoSeries/Series
if align is None:
align = True
maybe_warn = True
else:
maybe_warn = False
this = this.geometry
if isinstance(other, GeoPandasBase):
if align and not this.index.equals(other.index):
if maybe_warn:
warn(
"The indices of the left and right GeoSeries' are not equal, and "
"therefore they will be aligned (reordering and/or introducing "
"missing values) before executing the operation. If this alignment "
"is the desired behaviour, you can silence this warning by passing "
"'align=True'. If you don't want alignment and protect yourself of "
"accidentally aligning, you can pass 'align=False'.",
stacklevel=4,
)
this, other = this.align(other.geometry)
else:
other = other.geometry
a_this = GeometryArray(this.values)
other = GeometryArray(other.values)
elif isinstance(other, BaseGeometry):
a_this = GeometryArray(this.values)
else:
raise TypeError(type(this), type(other))
data = getattr(a_this, op)(other, *args, **kwargs)
return data, this.index
def _binary_geo(op, this, other, align, *args, **kwargs):
# type: (str, GeoSeries, GeoSeries) -> GeoSeries
"""Binary operation on GeoSeries objects that returns a GeoSeries"""
from .geoseries import GeoSeries
geoms, index = _delegate_binary_method(op, this, other, align, *args, **kwargs)
return GeoSeries(geoms, index=index, crs=this.crs)
def _binary_op(op, this, other, align, *args, **kwargs):
# type: (str, GeoSeries, GeoSeries, args/kwargs) -> Series[bool/float]
"""Binary operation on GeoSeries objects that returns a Series"""
data, index = _delegate_binary_method(op, this, other, align, *args, **kwargs)
return Series(data, index=index)
def _delegate_property(op, this):
# type: (str, GeoSeries) -> GeoSeries/Series
a_this = GeometryArray(this.geometry.values)
data = getattr(a_this, op)
if isinstance(data, GeometryArray):
from .geoseries import GeoSeries
return GeoSeries(data, index=this.index, crs=this.crs)
else:
return Series(data, index=this.index)
def _delegate_geo_method(op, this, **kwargs):
# type: (str, GeoSeries) -> GeoSeries
"""Unary operation that returns a GeoSeries"""
from .geodataframe import GeoDataFrame
from .geoseries import GeoSeries
if isinstance(this, GeoSeries):
klass, var_name = "GeoSeries", "gs"
elif isinstance(this, GeoDataFrame):
klass, var_name = "GeoDataFrame", "gdf"
else:
klass, var_name = this.__class__.__name__, "this"
for key, val in kwargs.items():
if isinstance(val, pd.Series):
if not val.index.equals(this.index):
raise ValueError(
f"Index of the Series passed as '{key}' does not match index of "
f"the {klass}. If you want both Series to be aligned, align them "
f"before passing them to this method as "
f"`{var_name}, {key} = {var_name}.align({key})`. If "
f"you want to ignore the index, pass the underlying array as "
f"'{key}' using `{key}.values`."
)
kwargs[key] = np.asarray(val)
a_this = GeometryArray(this.geometry.values)
data = getattr(a_this, op)(**kwargs)
return GeoSeries(data, index=this.index, crs=this.crs)
class GeoPandasBase(object):
@property
def area(self):
"""Returns a ``Series`` containing the area of each geometry in the
``GeoSeries`` expressed in the units of the CRS.
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... Polygon([(10, 0), (10, 5), (0, 0)]),
... Polygon([(0, 0), (2, 2), (2, 0)]),
... LineString([(0, 0), (1, 1), (0, 1)]),
... Point(0, 1)
... ]
... )
>>> s
0 POLYGON ((0 0, 1 1, 0 1, 0 0))
1 POLYGON ((10 0, 10 5, 0 0, 10 0))
2 POLYGON ((0 0, 2 2, 2 0, 0 0))
3 LINESTRING (0 0, 1 1, 0 1)
4 POINT (0 1)
dtype: geometry
>>> s.area
0 0.5
1 25.0
2 2.0
3 0.0
4 0.0
dtype: float64
See also
--------
GeoSeries.length : measure length
Notes
-----
Area may be invalid for a geographic CRS using degrees as units;
use :meth:`GeoSeries.to_crs` to project geometries to a planar
CRS before using this function.
Every operation in GeoPandas is planar, i.e. the potential third
dimension is not taken into account.
"""
return _delegate_property("area", self)
@property
def crs(self):
"""
The Coordinate Reference System (CRS) represented as a ``pyproj.CRS``
object.
Returns None if the CRS is not set, and to set the value it
:getter: Returns a ``pyproj.CRS`` or None. When setting, the value
can be anything accepted by
:meth:`pyproj.CRS.from_user_input() <pyproj.crs.CRS.from_user_input>`,
such as an authority string (eg "EPSG:4326") or a WKT string.
Examples
--------
>>> s.crs # doctest: +SKIP
<Geographic 2D CRS: EPSG:4326>
Name: WGS 84
Axis Info [ellipsoidal]:
- Lat[north]: Geodetic latitude (degree)
- Lon[east]: Geodetic longitude (degree)
Area of Use:
- name: World
- bounds: (-180.0, -90.0, 180.0, 90.0)
Datum: World Geodetic System 1984
- Ellipsoid: WGS 84
- Prime Meridian: Greenwich
See also
--------
GeoSeries.set_crs : assign CRS
GeoSeries.to_crs : re-project to another CRS
"""
return self.geometry.values.crs
@crs.setter
def crs(self, value):
"""Sets the value of the crs"""
self.geometry.values.crs = value
@property
def geom_type(self):
"""
Returns a ``Series`` of strings specifying the `Geometry Type` of each
object.
Examples
--------
>>> from shapely.geometry import Point, Polygon, LineString
>>> d = {'geometry': [Point(2, 1), Polygon([(0, 0), (1, 1), (1, 0)]),
... LineString([(0, 0), (1, 1)])]}
>>> gdf = geopandas.GeoDataFrame(d, crs="EPSG:4326")
>>> gdf.geom_type
0 Point
1 Polygon
2 LineString
dtype: object
"""
return _delegate_property("geom_type", self)
@property
def type(self):
"""Return the geometry type of each geometry in the GeoSeries"""
return self.geom_type
@property
def length(self):
"""Returns a ``Series`` containing the length of each geometry
expressed in the units of the CRS.
In the case of a (Multi)Polygon it measures the length
of its exterior (i.e. perimeter).
Examples
--------
>>> from shapely.geometry import Polygon, LineString, MultiLineString, Point, \
GeometryCollection
>>> s = geopandas.GeoSeries(
... [
... LineString([(0, 0), (1, 1), (0, 1)]),
... LineString([(10, 0), (10, 5), (0, 0)]),
... MultiLineString([((0, 0), (1, 0)), ((-1, 0), (1, 0))]),
... Polygon([(0, 0), (1, 1), (0, 1)]),
... Point(0, 1),
... GeometryCollection([Point(1, 0), LineString([(10, 0), (10, 5), (0,\
0)])])
... ]
... )
>>> s
0 LINESTRING (0 0, 1 1, 0 1)
1 LINESTRING (10 0, 10 5, 0 0)
2 MULTILINESTRING ((0 0, 1 0), (-1 0, 1 0))
3 POLYGON ((0 0, 1 1, 0 1, 0 0))
4 POINT (0 1)
5 GEOMETRYCOLLECTION (POINT (1 0), LINESTRING (1...
dtype: geometry
>>> s.length
0 2.414214
1 16.180340
2 3.000000
3 3.414214
4 0.000000
5 16.180340
dtype: float64
See also
--------
GeoSeries.area : measure area of a polygon
Notes
-----
Length may be invalid for a geographic CRS using degrees as units;
use :meth:`GeoSeries.to_crs` to project geometries to a planar
CRS before using this function.
Every operation in GeoPandas is planar, i.e. the potential third
dimension is not taken into account.
"""
return _delegate_property("length", self)
@property
def is_valid(self):
"""Returns a ``Series`` of ``dtype('bool')`` with value ``True`` for
geometries that are valid.
Examples
--------
An example with one invalid polygon (a bowtie geometry crossing itself)
and one missing geometry:
>>> from shapely.geometry import Polygon
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... Polygon([(0,0), (1, 1), (1, 0), (0, 1)]), # bowtie geometry
... Polygon([(0, 0), (2, 2), (2, 0)]),
... None
... ]
... )
>>> s
0 POLYGON ((0 0, 1 1, 0 1, 0 0))
1 POLYGON ((0 0, 1 1, 1 0, 0 1, 0 0))
2 POLYGON ((0 0, 2 2, 2 0, 0 0))
3 None
dtype: geometry
>>> s.is_valid
0 True
1 False
2 True
3 False
dtype: bool
See also
--------
GeoSeries.is_valid_reason : reason for invalidity
"""
return _delegate_property("is_valid", self)
def is_valid_reason(self):
"""Returns a ``Series`` of strings with the reason for invalidity of
each geometry.
Examples
--------
An example with one invalid polygon (a bowtie geometry crossing itself)
and one missing geometry:
>>> from shapely.geometry import Polygon
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... Polygon([(0,0), (1, 1), (1, 0), (0, 1)]), # bowtie geometry
... Polygon([(0, 0), (2, 2), (2, 0)]),
... None
... ]
... )
>>> s
0 POLYGON ((0 0, 1 1, 0 1, 0 0))
1 POLYGON ((0 0, 1 1, 1 0, 0 1, 0 0))
2 POLYGON ((0 0, 2 2, 2 0, 0 0))
3 None
dtype: geometry
>>> s.is_valid_reason()
0 Valid Geometry
1 Self-intersection[0.5 0.5]
2 Valid Geometry
3 None
dtype: object
See also
--------
GeoSeries.is_valid : detect invalid geometries
GeoSeries.make_valid : fix invalid geometries
"""
return Series(self.geometry.values.is_valid_reason(), index=self.index)
@property
def is_empty(self):
"""
Returns a ``Series`` of ``dtype('bool')`` with value ``True`` for
empty geometries.
Examples
--------
An example of a GeoDataFrame with one empty point, one point and one missing
value:
>>> from shapely.geometry import Point
>>> d = {'geometry': [Point(), Point(2, 1), None]}
>>> gdf = geopandas.GeoDataFrame(d, crs="EPSG:4326")
>>> gdf
geometry
0 POINT EMPTY
1 POINT (2 1)
2 None
>>> gdf.is_empty
0 True
1 False
2 False
dtype: bool
See Also
--------
GeoSeries.isna : detect missing values
"""
return _delegate_property("is_empty", self)
def count_coordinates(self):
"""
Returns a ``Series`` containing the count of the number of coordinate pairs
in each geometry.
Examples
--------
An example of a GeoDataFrame with two line strings, one point and one None
value:
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... LineString([(0, 0), (1, 1), (1, -1), (0, 1)]),
... LineString([(0, 0), (1, 1), (1, -1)]),
... Point(0, 0),
... Polygon([(10, 10), (10, 20), (20, 20), (20, 10), (10, 10)]),
... None
... ]
... )
>>> s
0 LINESTRING (0 0, 1 1, 1 -1, 0 1)
1 LINESTRING (0 0, 1 1, 1 -1)
2 POINT (0 0)
3 POLYGON ((10 10, 10 20, 20 20, 20 10, 10 10))
4 None
dtype: geometry
>>> s.count_coordinates()
0 4
1 3
2 1
3 5
4 0
dtype: int32
See also
--------
GeoSeries.get_coordinates : extract coordinates as a :class:`~pandas.DataFrame`
GoSeries.count_geometries : count the number of geometries in a collection
"""
return Series(self.geometry.values.count_coordinates(), index=self.index)
def count_geometries(self):
"""
Returns a ``Series`` containing the count of geometries in each multi-part
geometry.
For single-part geometry objects, this is always 1. For multi-part geometries,
like ``MultiPoint`` or ``MultiLineString``, it is the number of parts in the
geometry. For ``GeometryCollection``, it is the number of geometries direct
parts of the collection (the method does not recurse into collections within
collections).
Examples
--------
>>> from shapely.geometry import Point, MultiPoint, LineString, MultiLineString
>>> s = geopandas.GeoSeries(
... [
... MultiPoint([(0, 0), (1, 1), (1, -1), (0, 1)]),
... MultiLineString([((0, 0), (1, 1)), ((-1, 0), (1, 0))]),
... LineString([(0, 0), (1, 1), (1, -1)]),
... Point(0, 0),
... ]
... )
>>> s
0 MULTIPOINT ((0 0), (1 1), (1 -1), (0 1))
1 MULTILINESTRING ((0 0, 1 1), (-1 0, 1 0))
2 LINESTRING (0 0, 1 1, 1 -1)
3 POINT (0 0)
dtype: geometry
>>> s.count_geometries()
0 4
1 2
2 1
3 1
dtype: int32
See also
--------
GeoSeries.count_coordinates : count the number of coordinates in a geometry
GeoSeries.count_interior_rings : count the number of interior rings
"""
return Series(self.geometry.values.count_geometries(), index=self.index)
def count_interior_rings(self):
"""
Returns a ``Series`` containing the count of the number of interior rings
in a polygonal geometry.
For non-polygonal geometries, this is always 0.
Examples
--------
>>> from shapely.geometry import Polygon, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon(
... [(0, 0), (0, 5), (5, 5), (5, 0)],
... [[(1, 1), (1, 4), (4, 4), (4, 1)]],
... ),
... Polygon(
... [(0, 0), (0, 5), (5, 5), (5, 0)],
... [
... [(1, 1), (1, 2), (2, 2), (2, 1)],
... [(3, 2), (3, 3), (4, 3), (4, 2)],
... ],
... ),
... Point(0, 1),
... ]
... )
>>> s
0 POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0), (1 1, 1 4,...
1 POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0), (1 1, 1 2,...
2 POINT (0 1)
dtype: geometry
>>> s.count_interior_rings()
0 1
1 2
2 0
dtype: int32
See also
--------
GeoSeries.count_coordinates : count the number of coordinates in a geometry
GeoSeries.count_geometries : count the number of geometries in a collection
"""
return Series(self.geometry.values.count_interior_rings(), index=self.index)
@property
def is_simple(self):
"""Returns a ``Series`` of ``dtype('bool')`` with value ``True`` for
geometries that do not cross themselves.
This is meaningful only for `LineStrings` and `LinearRings`.
Examples
--------
>>> from shapely.geometry import LineString
>>> s = geopandas.GeoSeries(
... [
... LineString([(0, 0), (1, 1), (1, -1), (0, 1)]),
... LineString([(0, 0), (1, 1), (1, -1)]),
... ]
... )
>>> s
0 LINESTRING (0 0, 1 1, 1 -1, 0 1)
1 LINESTRING (0 0, 1 1, 1 -1)
dtype: geometry
>>> s.is_simple
0 False
1 True
dtype: bool
"""
return _delegate_property("is_simple", self)
@property
def is_ring(self):
"""Returns a ``Series`` of ``dtype('bool')`` with value ``True`` for
features that are closed.
When constructing a LinearRing, the sequence of coordinates may be
explicitly closed by passing identical values in the first and last indices.
Otherwise, the sequence will be implicitly closed by copying the first tuple
to the last index.
Examples
--------
>>> from shapely.geometry import LineString, LinearRing
>>> s = geopandas.GeoSeries(
... [
... LineString([(0, 0), (1, 1), (1, -1)]),
... LineString([(0, 0), (1, 1), (1, -1), (0, 0)]),
... LinearRing([(0, 0), (1, 1), (1, -1)]),
... ]
... )
>>> s
0 LINESTRING (0 0, 1 1, 1 -1)
1 LINESTRING (0 0, 1 1, 1 -1, 0 0)
2 LINEARRING (0 0, 1 1, 1 -1, 0 0)
dtype: geometry
>>> s.is_ring
0 False
1 True
2 True
dtype: bool
"""
return _delegate_property("is_ring", self)
@property
def is_ccw(self):
"""Returns a ``Series`` of ``dtype('bool')`` with value ``True``
if a LineString or LinearRing is counterclockwise.
Note that there are no checks on whether lines are actually
closed and not self-intersecting, while this is a requirement
for ``is_ccw``. The recommended usage of this property for
LineStrings is ``GeoSeries.is_ccw & GeoSeries.is_simple`` and for
LinearRings ``GeoSeries.is_ccw & GeoSeries.is_valid``.
This property will return False for non-linear geometries and for
lines with fewer than 4 points (including the closing point).
Examples
--------
>>> from shapely.geometry import LineString, LinearRing, Point
>>> s = geopandas.GeoSeries(
... [
... LinearRing([(0, 0), (0, 1), (1, 1), (0, 0)]),
... LinearRing([(0, 0), (1, 1), (0, 1), (0, 0)]),
... LineString([(0, 0), (1, 1), (0, 1)]),
... Point(3, 3)
... ]
... )
>>> s
0 LINEARRING (0 0, 0 1, 1 1, 0 0)
1 LINEARRING (0 0, 1 1, 0 1, 0 0)
2 LINESTRING (0 0, 1 1, 0 1)
3 POINT (3 3)
dtype: geometry
>>> s.is_ccw
0 False
1 True
2 False
3 False
dtype: bool
"""
return _delegate_property("is_ccw", self)
@property
def is_closed(self):
"""Returns a ``Series`` of ``dtype('bool')`` with value ``True``
if a LineString's or LinearRing's first and last points are equal.
Returns False for any other geometry type.
Examples
--------
>>> from shapely.geometry import LineString, Point, Polygon
>>> s = geopandas.GeoSeries(
... [
... LineString([(0, 0), (1, 1), (0, 1), (0, 0)]),
... LineString([(0, 0), (1, 1), (0, 1)]),
... Polygon([(0, 0), (0, 1), (1, 1), (0, 0)]),
... Point(3, 3)
... ]
... )
>>> s
0 LINESTRING (0 0, 1 1, 0 1, 0 0)
1 LINESTRING (0 0, 1 1, 0 1)
2 POLYGON ((0 0, 0 1, 1 1, 0 0))
3 POINT (3 3)
dtype: geometry
>>> s.is_closed
0 True
1 False
2 False
3 False
dtype: bool
"""
return _delegate_property("is_closed", self)
@property
def has_z(self):
"""Returns a ``Series`` of ``dtype('bool')`` with value ``True`` for
features that have a z-component.
Notes
-----
Every operation in GeoPandas is planar, i.e. the potential third
dimension is not taken into account.
Examples
--------
>>> from shapely.geometry import Point
>>> s = geopandas.GeoSeries(
... [
... Point(0, 1),
... Point(0, 1, 2),
... ]
... )
>>> s
0 POINT (0 1)
1 POINT Z (0 1 2)
dtype: geometry
>>> s.has_z
0 False
1 True
dtype: bool
"""
return _delegate_property("has_z", self)
def get_precision(self):
"""Returns a ``Series`` of the precision of each geometry.
If a precision has not been previously set, it will be 0, indicating regular
double precision coordinates are in use. Otherwise, it will return the precision
grid size that was set on a geometry.
Returns NaN for not-a-geometry values.
Examples
--------
>>> from shapely.geometry import Point
>>> s = geopandas.GeoSeries(
... [
... Point(0, 1),
... Point(0, 1, 2),
... Point(0, 1.5, 2),
... ]
... )
>>> s
0 POINT (0 1)
1 POINT Z (0 1 2)
2 POINT Z (0 1.5 2)
dtype: geometry
>>> s.get_precision()
0 0.0
1 0.0
2 0.0
dtype: float64
>>> s1 = s.set_precision(1)
>>> s1
0 POINT (0 1)
1 POINT Z (0 1 2)
2 POINT Z (0 2 2)
dtype: geometry
>>> s1.get_precision()
0 1.0
1 1.0
2 1.0
dtype: float64
See also
--------
GeoSeries.set_precision : set precision grid size
"""
return Series(self.geometry.values.get_precision(), index=self.index)
def get_geometry(self, index):
"""Returns the n-th geometry from a collection of geometries.
Parameters
----------
index : int or array_like
Position of a geometry to be retrieved within its collection
Returns
-------
GeoSeries
Notes
-----
Simple geometries act as collections of length 1. Any out-of-range index value
returns None.
Examples
--------
>>> from shapely.geometry import Point, MultiPoint, GeometryCollection
>>> s = geopandas.GeoSeries(
... [
... Point(0, 0),
... MultiPoint([(0, 0), (1, 1), (0, 1), (1, 0)]),
... GeometryCollection(
... [MultiPoint([(0, 0), (1, 1), (0, 1), (1, 0)]), Point(0, 1)]
... ),
... ]
... )
>>> s
0 POINT (0 0)
1 MULTIPOINT ((0 0), (1 1), (0 1), (1 0))
2 GEOMETRYCOLLECTION (MULTIPOINT ((0 0), (1 1), ...
dtype: geometry
>>> s.get_geometry(0)
0 POINT (0 0)
1 POINT (0 0)
2 MULTIPOINT ((0 0), (1 1), (0 1), (1 0))
dtype: geometry
>>> s.get_geometry(1)
0 None
1 POINT (1 1)
2 POINT (0 1)
dtype: geometry
>>> s.get_geometry(-1)
0 POINT (0 0)
1 POINT (1 0)
2 POINT (0 1)
dtype: geometry
"""
return _delegate_geo_method("get_geometry", self, index=index)
#
# Unary operations that return a GeoSeries
#
@property
def boundary(self):
"""Returns a ``GeoSeries`` of lower dimensional objects representing
each geometry's set-theoretic `boundary`.
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... LineString([(0, 0), (1, 1), (1, 0)]),
... Point(0, 0),
... ]
... )
>>> s
0 POLYGON ((0 0, 1 1, 0 1, 0 0))
1 LINESTRING (0 0, 1 1, 1 0)
2 POINT (0 0)
dtype: geometry
>>> s.boundary
0 LINESTRING (0 0, 1 1, 0 1, 0 0)
1 MULTIPOINT ((0 0), (1 0))
2 GEOMETRYCOLLECTION EMPTY
dtype: geometry
See also
--------
GeoSeries.exterior : outer boundary (without interior rings)
"""
return _delegate_property("boundary", self)
@property
def centroid(self):
"""Returns a ``GeoSeries`` of points representing the centroid of each
geometry.
Note that centroid does not have to be on or within original geometry.
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... LineString([(0, 0), (1, 1), (1, 0)]),
... Point(0, 0),
... ]
... )
>>> s
0 POLYGON ((0 0, 1 1, 0 1, 0 0))
1 LINESTRING (0 0, 1 1, 1 0)
2 POINT (0 0)
dtype: geometry
>>> s.centroid
0 POINT (0.33333 0.66667)
1 POINT (0.70711 0.5)
2 POINT (0 0)
dtype: geometry
See also
--------
GeoSeries.representative_point : point guaranteed to be within each geometry
"""
return _delegate_property("centroid", self)
def concave_hull(self, ratio=0.0, allow_holes=False):
"""Returns a ``GeoSeries`` of geometries representing the concave hull
of each geometry.
The concave hull of a geometry is the smallest concave `Polygon`
containing all the points in each geometry, unless the number of points
in the geometric object is less than three. For two points, the concave
hull collapses to a `LineString`; for 1, a `Point`.
The hull is constructed by removing border triangles of the Delaunay
Triangulation of the points as long as their "size" is larger than the
maximum edge length ratio and optionally allowing holes. The edge length factor
is a fraction of the length difference between the longest and shortest edges
in the Delaunay Triangulation of the input points. For further information
on the algorithm used, see
https://libgeos.org/doxygen/classgeos_1_1algorithm_1_1hull_1_1ConcaveHull.html
Parameters
----------
ratio : float, (optional, default 0.0)
Number in the range [0, 1]. Higher numbers will include fewer vertices
in the hull.
allow_holes : bool, (optional, default False)
If set to True, the concave hull may have holes.
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point, MultiPoint
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... LineString([(0, 0), (1, 1), (1, 0)]),
... MultiPoint([(0, 0), (1, 1), (0, 1), (1, 0), (0.5, 0.5)]),
... MultiPoint([(0, 0), (1, 1)]),
... Point(0, 0),
... ],
... crs=3857
... )
>>> s
0 POLYGON ((0 0, 1 1, 0 1, 0 0))
1 LINESTRING (0 0, 1 1, 1 0)
2 MULTIPOINT ((0 0), (1 1), (0 1), (1 0), (0.5 0...
3 MULTIPOINT ((0 0), (1 1))
4 POINT (0 0)
dtype: geometry
>>> s.concave_hull()
0 POLYGON ((0 1, 1 1, 0 0, 0 1))
1 POLYGON ((0 0, 1 1, 1 0, 0 0))
2 POLYGON ((0.5 0.5, 0 1, 1 1, 1 0, 0 0, 0.5 0.5))
3 LINESTRING (0 0, 1 1)
4 POINT (0 0)
dtype: geometry
See also
--------
GeoSeries.convex_hull : convex hull geometry
"""
return _delegate_geo_method(
"concave_hull", self, ratio=ratio, allow_holes=allow_holes
)
@property
def convex_hull(self):
"""Returns a ``GeoSeries`` of geometries representing the convex hull
of each geometry.
The convex hull of a geometry is the smallest convex `Polygon`
containing all the points in each geometry, unless the number of points
in the geometric object is less than three. For two points, the convex
hull collapses to a `LineString`; for 1, a `Point`.
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point, MultiPoint
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... LineString([(0, 0), (1, 1), (1, 0)]),
... MultiPoint([(0, 0), (1, 1), (0, 1), (1, 0), (0.5, 0.5)]),
... MultiPoint([(0, 0), (1, 1)]),
... Point(0, 0),
... ]
... )
>>> s
0 POLYGON ((0 0, 1 1, 0 1, 0 0))
1 LINESTRING (0 0, 1 1, 1 0)
2 MULTIPOINT ((0 0), (1 1), (0 1), (1 0), (0.5 0...
3 MULTIPOINT ((0 0), (1 1))
4 POINT (0 0)
dtype: geometry
>>> s.convex_hull
0 POLYGON ((0 0, 0 1, 1 1, 0 0))
1 POLYGON ((0 0, 1 1, 1 0, 0 0))
2 POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))
3 LINESTRING (0 0, 1 1)
4 POINT (0 0)
dtype: geometry
See also
--------
GeoSeries.concave_hull : concave hull geometry
GeoSeries.envelope : bounding rectangle geometry
"""
return _delegate_property("convex_hull", self)
def delaunay_triangles(self, tolerance=0.0, only_edges=False):
"""Returns a ``GeoSeries`` consisting of objects representing
the computed Delaunay triangulation between the vertices of
an input geometry.
All geometries within the GeoSeries are considered together within a single
Delaunay triangulation. The resulting geometries therefore do not map 1:1
to input geometries. Note that each vertex of a geometry is considered a site
for the triangulation, so the triangles will be constructed between the vertices
of each geometry.
Notes
-----
If you want to generate Delaunay triangles for each geometry separately, use
:func:`shapely.delaunay_triangles` instead.
Parameters
----------
tolerance : float, default 0.0
Snap input vertices together if their distance is less than this value.
only_edges : bool (optional, default False)
If set to True, the triangulation will return linestrings instead of
polygons.
Examples
--------
>>> from shapely import LineString, MultiPoint, Point, Polygon
>>> s = geopandas.GeoSeries(
... [
... Point(1, 1),
... Point(2, 2),
... Point(1, 3),
... Point(0, 2),
... ]
... )
>>> s
0 POINT (1 1)
1 POINT (2 2)
2 POINT (1 3)
3 POINT (0 2)
dtype: geometry
>>> s.delaunay_triangles()
0 POLYGON ((0 2, 1 1, 1 3, 0 2))
1 POLYGON ((1 3, 1 1, 2 2, 1 3))
dtype: geometry
>>> s.delaunay_triangles(only_edges=True)
0 LINESTRING (1 3, 2 2)
1 LINESTRING (0 2, 1 3)
2 LINESTRING (0 2, 1 1)
3 LINESTRING (1 1, 2 2)
4 LINESTRING (1 1, 1 3)
dtype: geometry
The method supports any geometry type but keep in mind that the underlying
algorithm is based on the vertices of the input geometries only and does not
consider edge segments between vertices.
>>> s2 = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... LineString([(1, 0), (2, 1), (1, 2)]),
... MultiPoint([(2, 3), (2, 0), (3, 1)]),
... ]
... )
>>> s2
0 POLYGON ((0 0, 1 1, 0 1, 0 0))
1 LINESTRING (1 0, 2 1, 1 2)
2 MULTIPOINT ((2 3), (2 0), (3 1))
dtype: geometry
>>> s2.delaunay_triangles()
0 POLYGON ((0 1, 0 0, 1 0, 0 1))
1 POLYGON ((0 1, 1 0, 1 1, 0 1))
2 POLYGON ((0 1, 1 1, 1 2, 0 1))
3 POLYGON ((1 2, 1 1, 2 1, 1 2))
4 POLYGON ((1 2, 2 1, 2 3, 1 2))
5 POLYGON ((2 3, 2 1, 3 1, 2 3))
6 POLYGON ((3 1, 2 1, 2 0, 3 1))
7 POLYGON ((2 0, 2 1, 1 1, 2 0))
8 POLYGON ((2 0, 1 1, 1 0, 2 0))
dtype: geometry
See also
--------
GeoSeries.voronoi_polygons : Voronoi diagram around vertices
"""
from .geoseries import GeoSeries
geometry_input = shapely.geometrycollections(self.geometry.values._data)
delaunay = shapely.delaunay_triangles(
geometry_input,
tolerance=tolerance,
only_edges=only_edges,
)
return GeoSeries(delaunay, crs=self.crs).explode(ignore_index=True)
def voronoi_polygons(self, tolerance=0.0, extend_to=None, only_edges=False):
"""Returns a ``GeoSeries`` consisting of objects representing
the computed Voronoi diagram around the vertices of an input geometry.
All geometries within the GeoSeries are considered together within a single
Voronoi diagram. The resulting geometries therefore do not necessarily map 1:1
to input geometries. Note that each vertex of a geometry is considered a site
for the Voronoi diagram, so the diagram will be constructed around the vertices
of each geometry.
Notes
-----
The order of polygons in the output currently does not correspond to the order
of input vertices.
If you want to generate a Voronoi diagram for each geometry separately, use
:func:`shapely.voronoi_polygons` instead.
Parameters
----------
tolerance : float, default 0.0
Snap input vertices together if their distance is less than this value.
extend_to : shapely.Geometry, default None
If set, the Voronoi diagram will be extended to cover the
envelope of this geometry (unless this envelope is smaller than the input
geometry).
only_edges : bool (optional, default False)
If set to True, the diagram will return LineStrings instead
of Polygons.
Examples
--------
The most common use case is to generate polygons representing the Voronoi
diagram around a set of points:
>>> from shapely import LineString, MultiPoint, Point, Polygon
>>> s = geopandas.GeoSeries(
... [
... Point(1, 1),
... Point(2, 2),
... Point(1, 3),
... Point(0, 2),
... ]
... )
>>> s
0 POINT (1 1)
1 POINT (2 2)
2 POINT (1 3)
3 POINT (0 2)
dtype: geometry
By default, you get back a GeoSeries of polygons:
>>> s.voronoi_polygons()
0 POLYGON ((-2 5, 1 2, -2 -1, -2 5))
1 POLYGON ((4 5, 1 2, -2 5, 4 5))
2 POLYGON ((-2 -1, 1 2, 4 -1, -2 -1))
3 POLYGON ((4 -1, 1 2, 4 5, 4 -1))
dtype: geometry
If you set only_edges to True, you get back LineStrings representing the
edges of the Voronoi diagram:
>>> s.voronoi_polygons(only_edges=True)
0 LINESTRING (-2 5, 1 2)
1 LINESTRING (1 2, -2 -1)
2 LINESTRING (4 5, 1 2)
3 LINESTRING (1 2, 4 -1)
dtype: geometry
You can also extend each diagram to a given geometry:
>>> limit = Polygon([(-10, -10), (0, 15), (15, 15), (15, 0)])
>>> s.voronoi_polygons(extend_to=limit)
0 POLYGON ((-10 13, 1 2, -10 -9, -10 13))
1 POLYGON ((15 15, 15 -10, 13 -10, 1 2, 14 15, 1...
2 POLYGON ((-10 -10, -10 -9, 1 2, 13 -10, -10 -10))
3 POLYGON ((-10 15, 14 15, 1 2, -10 13, -10 15))
dtype: geometry
The method supports any geometry type but keep in mind that the underlying
algorithm is based on the vertices of the input geometries only and does not
consider edge segments between vertices.
>>> s2 = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... LineString([(1, 0), (2, 1), (1, 2)]),
... MultiPoint([(2, 3), (2, 0), (3, 1)]),
... ]
... )
>>> s2
0 POLYGON ((0 0, 1 1, 0 1, 0 0))
1 LINESTRING (1 0, 2 1, 1 2)
2 MULTIPOINT ((2 3), (2 0), (3 1))
dtype: geometry
>>> s2.voronoi_polygons()
0 POLYGON ((1.5 1.5, 1.5 0.5, 0.5 0.5, 0.5 1.5, ...
1 POLYGON ((1.5 0.5, 1.5 1.5, 2 2, 2.5 2, 2.5 0....
2 POLYGON ((-3 -3, -3 0.5, 0.5 0.5, 0.5 -3, -3 -3))
3 POLYGON ((0.5 -3, 0.5 0.5, 1.5 0.5, 1.5 -3, 0....
4 POLYGON ((-3 5, 0.5 1.5, 0.5 0.5, -3 0.5, -3 5))
5 POLYGON ((-3 6, -2 6, 2 2, 1.5 1.5, 0.5 1.5, -...
6 POLYGON ((1.5 -3, 1.5 0.5, 2.5 0.5, 6 -3, 1.5 ...
7 POLYGON ((6 6, 6 3.75, 2.5 2, 2 2, -2 6, 6 6))
8 POLYGON ((6 -3, 2.5 0.5, 2.5 2, 6 3.75, 6 -3))
dtype: geometry
See also
--------
GeoSeries.delaunay_triangles : Delaunay triangulation around vertices
"""
from .geoseries import GeoSeries
geometry_input = shapely.geometrycollections(self.geometry.values._data)
voronoi = shapely.voronoi_polygons(
geometry_input,
tolerance=tolerance,
extend_to=extend_to,
only_edges=only_edges,
)
return GeoSeries(voronoi, crs=self.crs).explode(ignore_index=True)
@property
def envelope(self):
"""Returns a ``GeoSeries`` of geometries representing the envelope of
each geometry.
The envelope of a geometry is the bounding rectangle. That is, the
point or smallest rectangular polygon (with sides parallel to the
coordinate axes) that contains the geometry.
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point, MultiPoint
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... LineString([(0, 0), (1, 1), (1, 0)]),
... MultiPoint([(0, 0), (1, 1)]),
... Point(0, 0),
... ]
... )
>>> s
0 POLYGON ((0 0, 1 1, 0 1, 0 0))
1 LINESTRING (0 0, 1 1, 1 0)
2 MULTIPOINT ((0 0), (1 1))
3 POINT (0 0)
dtype: geometry
>>> s.envelope
0 POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))
1 POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))
2 POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))
3 POINT (0 0)
dtype: geometry
See also
--------
GeoSeries.convex_hull : convex hull geometry
"""
return _delegate_property("envelope", self)
def minimum_rotated_rectangle(self):
"""Returns a ``GeoSeries`` of the general minimum bounding rectangle
that contains the object.
Unlike envelope this rectangle is not constrained to be parallel
to the coordinate axes. If the convex hull of the object is a
degenerate (line or point) this degenerate is returned.
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point, MultiPoint
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... LineString([(0, 0), (1, 1), (1, 0)]),
... MultiPoint([(0, 0), (1, 1)]),
... Point(0, 0),
... ]
... )
>>> s
0 POLYGON ((0 0, 1 1, 0 1, 0 0))
1 LINESTRING (0 0, 1 1, 1 0)
2 MULTIPOINT ((0 0), (1 1))
3 POINT (0 0)
dtype: geometry
>>> s.minimum_rotated_rectangle()
0 POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))
1 POLYGON ((1 1, 1 0, 0 0, 0 1, 1 1))
2 LINESTRING (0 0, 1 1)
3 POINT (0 0)
dtype: geometry
See also
--------
GeoSeries.envelope : bounding rectangle
"""
return _delegate_geo_method("minimum_rotated_rectangle", self)
@property
def exterior(self):
"""Returns a ``GeoSeries`` of LinearRings representing the outer
boundary of each polygon in the GeoSeries.
Applies to GeoSeries containing only Polygons. Returns ``None``` for
other geometry types.
Examples
--------
>>> from shapely.geometry import Polygon, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... Polygon([(1, 0), (2, 1), (0, 0)]),
... Point(0, 1)
... ]
... )
>>> s
0 POLYGON ((0 0, 1 1, 0 1, 0 0))
1 POLYGON ((1 0, 2 1, 0 0, 1 0))
2 POINT (0 1)
dtype: geometry
>>> s.exterior
0 LINEARRING (0 0, 1 1, 0 1, 0 0)
1 LINEARRING (1 0, 2 1, 0 0, 1 0)
2 None
dtype: geometry
See also
--------
GeoSeries.boundary : complete set-theoretic boundary
GeoSeries.interiors : list of inner rings of each polygon
"""
# TODO: return empty geometry for non-polygons
return _delegate_property("exterior", self)
def extract_unique_points(self):
"""Returns a ``GeoSeries`` of MultiPoints representing all
distinct vertices of an input geometry.
Examples
--------
>>> from shapely import LineString, Polygon
>>> s = geopandas.GeoSeries(
... [
... LineString([(0, 0), (0, 0), (1, 1), (1, 1)]),
... Polygon([(0, 0), (0, 0), (1, 1), (1, 1)])
... ],
... )
>>> s
0 LINESTRING (0 0, 0 0, 1 1, 1 1)
1 POLYGON ((0 0, 0 0, 1 1, 1 1, 0 0))
dtype: geometry
>>> s.extract_unique_points()
0 MULTIPOINT ((0 0), (1 1))
1 MULTIPOINT ((0 0), (1 1))
dtype: geometry
See also
--------
GeoSeries.get_coordinates : extract coordinates as a :class:`~pandas.DataFrame`
"""
return _delegate_geo_method("extract_unique_points", self)
def offset_curve(self, distance, quad_segs=8, join_style="round", mitre_limit=5.0):
"""Returns a ``LineString`` or ``MultiLineString`` geometry at a
distance from the object on its right or its left side.
Parameters
----------
distance : float | array-like
Specifies the offset distance from the input geometry. Negative
for right side offset, positive for left side offset.
quad_segs : int (optional, default 8)
Specifies the number of linear segments in a quarter circle in the
approximation of circular arcs.
join_style : {'round', 'bevel', 'mitre'}, (optional, default 'round')
Specifies the shape of outside corners. 'round' results in
rounded shapes. 'bevel' results in a beveled edge that touches the
original vertex. 'mitre' results in a single vertex that is beveled
depending on the ``mitre_limit`` parameter.
mitre_limit : float (optional, default 5.0)
Crops of 'mitre'-style joins if the point is displaced from the
buffered vertex by more than this limit.
See http://shapely.readthedocs.io/en/latest/manual.html#object.offset_curve
for details.
Examples
--------
>>> from shapely.geometry import LineString
>>> s = geopandas.GeoSeries(
... [
... LineString([(0, 0), (0, 1), (1, 1)]),
... ],
... crs=3857
... )
>>> s
0 LINESTRING (0 0, 0 1, 1 1)
dtype: geometry
>>> s.offset_curve(1)
0 LINESTRING (-1 0, -1 1, -0.981 1.195, -0.924 1...
dtype: geometry
"""
return _delegate_geo_method(
"offset_curve",
self,
distance=distance,
quad_segs=quad_segs,
join_style=join_style,
mitre_limit=mitre_limit,
)
@property
def interiors(self):
"""Returns a ``Series`` of List representing the
inner rings of each polygon in the GeoSeries.
Applies to GeoSeries containing only Polygons.
Returns
-------
inner_rings: Series of List
Inner rings of each polygon in the GeoSeries.
Examples
--------
>>> from shapely.geometry import Polygon
>>> s = geopandas.GeoSeries(
... [
... Polygon(
... [(0, 0), (0, 5), (5, 5), (5, 0)],
... [[(1, 1), (2, 1), (1, 2)], [(1, 4), (2, 4), (2, 3)]],
... ),
... Polygon([(1, 0), (2, 1), (0, 0)]),
... ]
... )
>>> s
0 POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0), (1 1, 2 1,...
1 POLYGON ((1 0, 2 1, 0 0, 1 0))
dtype: geometry
>>> s.interiors
0 [LINEARRING (1 1, 2 1, 1 2, 1 1), LINEARRING (...
1 []
dtype: object
See also
--------
GeoSeries.exterior : outer boundary
"""
return _delegate_property("interiors", self)
def remove_repeated_points(self, tolerance=0.0):
"""Returns a ``GeoSeries`` containing a copy of the input geometry
with repeated points removed.
From the start of the coordinate sequence, each next point within the
tolerance is removed.
Removing repeated points with a non-zero tolerance may result in an invalid
geometry being returned.
Parameters
----------
tolerance : float, default 0.0
Remove all points within this distance of each other. Use 0.0
to remove only exactly repeated points (the default).
Examples
--------
>>> from shapely import LineString, Polygon
>>> s = geopandas.GeoSeries(
... [
... LineString([(0, 0), (0, 0), (1, 0)]),
... Polygon([(0, 0), (0, 0.5), (0, 1), (0.5, 1), (0,0)]),
... ],
... )
>>> s
0 LINESTRING (0 0, 0 0, 1 0)
1 POLYGON ((0 0, 0 0.5, 0 1, 0.5 1, 0 0))
dtype: geometry
>>> s.remove_repeated_points(tolerance=0.0)
0 LINESTRING (0 0, 1 0)
1 POLYGON ((0 0, 0 0.5, 0 1, 0.5 1, 0 0))
dtype: geometry
"""
return _delegate_geo_method("remove_repeated_points", self, tolerance=tolerance)
def set_precision(self, grid_size, mode="valid_output"):
"""Returns a ``GeoSeries`` with the precision set to a precision grid size.
By default, geometries use double precision coordinates (``grid_size=0``).
Coordinates will be rounded if a precision grid is less precise than the input
geometry. Duplicated vertices will be dropped from lines and polygons for grid
sizes greater than 0. Line and polygon geometries may collapse to empty
geometries if all vertices are closer together than ``grid_size``. Spikes or
sections in Polygons narrower than ``grid_size`` after rounding the vertices
will be removed, which can lead to MultiPolygons or empty geometries. Z values,
if present, will not be modified.
Parameters
----------
grid_size : float
Precision grid size. If 0, will use double precision (will not modify
geometry if precision grid size was not previously set). If this value is
more precise than input geometry, the input geometry will not be modified.
mode : {'valid_output', 'pointwise', 'keep_collapsed'}, default 'valid_output'
This parameter determines the way a precision reduction is applied on the
geometry. There are three modes:
* ``'valid_output'`` (default): The output is always valid. Collapsed
geometry elements (including both polygons and lines) are removed.
Duplicate vertices are removed.
* ``'pointwise'``: Precision reduction is performed pointwise. Output
geometry may be invalid due to collapse or self-intersection. Duplicate
vertices are not removed.
* ``'keep_collapsed'``: Like the default mode, except that collapsed linear
geometry elements are preserved. Collapsed polygonal input elements are
removed. Duplicate vertices are removed.
Examples
--------
>>> from shapely import LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Point(0.9, 0.9),
... Point(0.9, 0.9, 0.9),
... LineString([(0, 0), (0, 0.1), (0, 1), (1, 1)]),
... LineString([(0, 0), (0, 0.1), (0.1, 0.1)])
... ],
... )
>>> s
0 POINT (0.9 0.9)
1 POINT Z (0.9 0.9 0.9)
2 LINESTRING (0 0, 0 0.1, 0 1, 1 1)
3 LINESTRING (0 0, 0 0.1, 0.1 0.1)
dtype: geometry
>>> s.set_precision(1)
0 POINT (1 1)
1 POINT Z (1 1 0.9)
2 LINESTRING (0 0, 0 1, 1 1)
3 LINESTRING Z EMPTY
dtype: geometry
>>> s.set_precision(1, mode="pointwise")
0 POINT (1 1)
1 POINT Z (1 1 0.9)
2 LINESTRING (0 0, 0 0, 0 1, 1 1)
3 LINESTRING (0 0, 0 0, 0 0)
dtype: geometry
>>> s.set_precision(1, mode="keep_collapsed")
0 POINT (1 1)
1 POINT Z (1 1 0.9)
2 LINESTRING (0 0, 0 1, 1 1)
3 LINESTRING (0 0, 0 0)
dtype: geometry
Notes
-----
Subsequent operations will always be performed in the precision of the geometry
with higher precision (smaller ``grid_size``). That same precision will be
attached to the operation outputs.
Input geometries should be geometrically valid; unexpected results may occur if
input geometries are not. You can check the validity with
:meth:`~GeoSeries.is_valid` and fix invalid geometries with
:meth:`~GeoSeries.make_valid` methods.
"""
return _delegate_geo_method(
"set_precision", self, grid_size=grid_size, mode=mode
)
def representative_point(self):
"""Returns a ``GeoSeries`` of (cheaply computed) points that are
guaranteed to be within each geometry.
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... LineString([(0, 0), (1, 1), (1, 0)]),
... Point(0, 0),
... ]
... )
>>> s
0 POLYGON ((0 0, 1 1, 0 1, 0 0))
1 LINESTRING (0 0, 1 1, 1 0)
2 POINT (0 0)
dtype: geometry
>>> s.representative_point()
0 POINT (0.25 0.5)
1 POINT (1 1)
2 POINT (0 0)
dtype: geometry
See also
--------
GeoSeries.centroid : geometric centroid
"""
return _delegate_geo_method("representative_point", self)
def minimum_bounding_circle(self):
"""Returns a ``GeoSeries`` of geometries representing the minimum bounding
circle that encloses each geometry.
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1), (0, 0)]),
... LineString([(0, 0), (1, 1), (1, 0)]),
... Point(0, 0),
... ]
... )
>>> s
0 POLYGON ((0 0, 1 1, 0 1, 0 0))
1 LINESTRING (0 0, 1 1, 1 0)
2 POINT (0 0)
dtype: geometry
>>> s.minimum_bounding_circle()
0 POLYGON ((1.20711 0.5, 1.19352 0.36205, 1.1532...
1 POLYGON ((1.20711 0.5, 1.19352 0.36205, 1.1532...
2 POINT (0 0)
dtype: geometry
See also
--------
GeoSeries.convex_hull : convex hull geometry
"""
return _delegate_geo_method("minimum_bounding_circle", self)
def minimum_bounding_radius(self):
"""Returns a `Series` of the radii of the minimum bounding circles
that enclose each geometry.
Examples
--------
>>> from shapely.geometry import Point, LineString, Polygon
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1), (0, 0)]),
... LineString([(0, 0), (1, 1), (1, 0)]),
... Point(0,0),
... ]
... )
>>> s
0 POLYGON ((0 0, 1 1, 0 1, 0 0))
1 LINESTRING (0 0, 1 1, 1 0)
2 POINT (0 0)
dtype: geometry
>>> s.minimum_bounding_radius()
0 0.707107
1 0.707107
2 0.000000
dtype: float64
See also
--------
GeoSeries.minumum_bounding_circle : minimum bounding circle (geometry)
"""
return Series(self.geometry.values.minimum_bounding_radius(), index=self.index)
def minimum_clearance(self):
"""Returns a ``Series`` containing the minimum clearance distance,
which is the smallest distance by which a vertex of the geometry
could be moved to produce an invalid geometry.
If no minimum clearance exists for a geometry (for example,
a single point, or an empty geometry), infinity is returned.
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1), (0, 0)]),
... LineString([(0, 0), (1, 1), (3, 2)]),
... Point(0, 0),
... ]
... )
>>> s
0 POLYGON ((0 0, 1 1, 0 1, 0 0))
1 LINESTRING (0 0, 1 1, 3 2)
2 POINT (0 0)
dtype: geometry
>>> s.minimum_clearance()
0 0.707107
1 1.414214
2 inf
dtype: float64
"""
return Series(self.geometry.values.minimum_clearance(), index=self.index)
def normalize(self):
"""Returns a ``GeoSeries`` of normalized
geometries to normal form (or canonical form).
This method orders the coordinates, rings of a polygon and parts of
multi geometries consistently. Typically useful for testing purposes
(for example in combination with `equals_exact`).
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... LineString([(0, 0), (1, 1), (1, 0)]),
... Point(0, 0),
... ],
... )
>>> s
0 POLYGON ((0 0, 1 1, 0 1, 0 0))
1 LINESTRING (0 0, 1 1, 1 0)
2 POINT (0 0)
dtype: geometry
>>> s.normalize()
0 POLYGON ((0 0, 0 1, 1 1, 0 0))
1 LINESTRING (0 0, 1 1, 1 0)
2 POINT (0 0)
dtype: geometry
"""
return _delegate_geo_method("normalize", self)
def make_valid(self):
"""
Repairs invalid geometries.
Returns a ``GeoSeries`` with valid geometries.
If the input geometry is already valid, then it will be preserved.
In many cases, in order to create a valid geometry, the input
geometry must be split into multiple parts or multiple geometries.
If the geometry must be split into multiple parts of the same type
to be made valid, then a multi-part geometry will be returned
(e.g. a MultiPolygon).
If the geometry must be split into multiple parts of different types
to be made valid, then a GeometryCollection will be returned.
Examples
--------
>>> from shapely.geometry import MultiPolygon, Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (0, 2), (1, 1), (2, 2), (2, 0), (1, 1), (0, 0)]),
... Polygon([(0, 2), (0, 1), (2, 0), (0, 0), (0, 2)]),
... LineString([(0, 0), (1, 1), (1, 0)]),
... ],
... )
>>> s
0 POLYGON ((0 0, 0 2, 1 1, 2 2, 2 0, 1 1, 0 0))
1 POLYGON ((0 2, 0 1, 2 0, 0 0, 0 2))
2 LINESTRING (0 0, 1 1, 1 0)
dtype: geometry
>>> s.make_valid()
0 MULTIPOLYGON (((1 1, 0 0, 0 2, 1 1)), ((2 0, 1...
1 GEOMETRYCOLLECTION (POLYGON ((2 0, 0 0, 0 1, 2...
2 LINESTRING (0 0, 1 1, 1 0)
dtype: geometry
"""
return _delegate_geo_method("make_valid", self)
def reverse(self):
"""Returns a ``GeoSeries`` with the order of coordinates reversed.
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... LineString([(0, 0), (1, 1), (1, 0)]),
... Point(0, 0),
... ]
... )
>>> s
0 POLYGON ((0 0, 1 1, 0 1, 0 0))
1 LINESTRING (0 0, 1 1, 1 0)
2 POINT (0 0)
dtype: geometry
>>> s.reverse()
0 POLYGON ((0 0, 0 1, 1 1, 0 0))
1 LINESTRING (1 0, 1 1, 0 0)
2 POINT (0 0)
dtype: geometry
See also
--------
GeoSeries.normalize : normalize order of coordinates
"""
return _delegate_geo_method("reverse", self)
def segmentize(self, max_segment_length):
"""Returns a ``GeoSeries`` with vertices added to line segments based on
maximum segment length.
Additional vertices will be added to every line segment in an input geometry so
that segments are no longer than the provided maximum segment length. New
vertices will evenly subdivide each segment. Only linear components of input
geometries are densified; other geometries are returned unmodified.
Parameters
----------
max_segment_length : float | array-like
Additional vertices will be added so that all line segments are no longer
than this value. Must be greater than 0.
Returns
-------
GeoSeries
Examples
--------
>>> from shapely.geometry import Polygon, LineString
>>> s = geopandas.GeoSeries(
... [
... LineString([(0, 0), (0, 10)]),
... Polygon([(0, 0), (10, 0), (10, 10), (0, 10), (0, 0)]),
... ],
... )
>>> s
0 LINESTRING (0 0, 0 10)
1 POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))
dtype: geometry
>>> s.segmentize(max_segment_length=5)
0 LINESTRING (0 0, 0 5, 0 10)
1 POLYGON ((0 0, 5 0, 10 0, 10 5, 10 10, 5 10, 0...
dtype: geometry
"""
return _delegate_geo_method(
"segmentize", self, max_segment_length=max_segment_length
)
def transform(self, transformation, include_z=False):
"""Returns a ``GeoSeries`` with the transformation function
applied to the geometry coordinates.
Parameters
----------
transformation : Callable
A function that transforms a (N, 2) or (N, 3) ndarray of float64
to another (N,2) or (N, 3) ndarray of float64
include_z : bool, default False
If True include the third dimension in the coordinates array that
is passed to the ``transformation`` function. If a geometry has no third
dimension, the z-coordinates passed to the function will be NaN.
Returns
-------
GeoSeries
Examples
--------
>>> from shapely import Point, Polygon
>>> s = geopandas.GeoSeries([Point(0, 0)])
>>> s.transform(lambda x: x + 1)
0 POINT (1 1)
dtype: geometry
>>> s = geopandas.GeoSeries([Polygon([(0, 0), (1, 1), (0, 1)])])
>>> s.transform(lambda x: x * [2, 3])
0 POLYGON ((0 0, 2 3, 0 3, 0 0))
dtype: geometry
By default the third dimension is ignored and you need explicitly include it:
>>> s = geopandas.GeoSeries([Point(0, 0, 0)])
>>> s.transform(lambda x: x + 1, include_z=True)
0 POINT Z (1 1 1)
dtype: geometry
"""
return _delegate_geo_method(
"transform", self, transformation=transformation, include_z=include_z
)
def force_2d(self):
"""Forces the dimensionality of a geometry to 2D.
Removes the additional Z coordinate dimension from all geometries.
Returns
-------
GeoSeries
Examples
--------
>>> from shapely import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Point(0.5, 2.5, 0),
... LineString([(1, 1, 1), (0, 1, 3), (1, 0, 2)]),
... Polygon([(0, 0, 0), (0, 10, 0), (10, 10, 0)]),
... ],
... )
>>> s
0 POINT Z (0.5 2.5 0)
1 LINESTRING Z (1 1 1, 0 1 3, 1 0 2)
2 POLYGON Z ((0 0 0, 0 10 0, 10 10 0, 0 0 0))
dtype: geometry
>>> s.force_2d()
0 POINT (0.5 2.5)
1 LINESTRING (1 1, 0 1, 1 0)
2 POLYGON ((0 0, 0 10, 10 10, 0 0))
dtype: geometry
"""
return _delegate_geo_method("force_2d", self)
def force_3d(self, z=0):
"""Forces the dimensionality of a geometry to 3D.
2D geometries will get the provided Z coordinate; 3D geometries
are unchanged (unless their Z coordinate is ``np.nan``).
Note that for empty geometries, 3D is only supported since GEOS 3.9 and then
still only for simple geometries (non-collections).
Parameters
----------
z : float | array_like (default 0)
Z coordinate to be assigned
Returns
-------
GeoSeries
Examples
--------
>>> from shapely import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Point(1, 2),
... Point(0.5, 2.5, 2),
... LineString([(1, 1), (0, 1), (1, 0)]),
... Polygon([(0, 0), (0, 10), (10, 10)]),
... ],
... )
>>> s
0 POINT (1 2)
1 POINT Z (0.5 2.5 2)
2 LINESTRING (1 1, 0 1, 1 0)
3 POLYGON ((0 0, 0 10, 10 10, 0 0))
dtype: geometry
>>> s.force_3d()
0 POINT Z (1 2 0)
1 POINT Z (0.5 2.5 2)
2 LINESTRING Z (1 1 0, 0 1 0, 1 0 0)
3 POLYGON Z ((0 0 0, 0 10 0, 10 10 0, 0 0 0))
dtype: geometry
Z coordinate can be specified as scalar:
>>> s.force_3d(4)
0 POINT Z (1 2 4)
1 POINT Z (0.5 2.5 2)
2 LINESTRING Z (1 1 4, 0 1 4, 1 0 4)
3 POLYGON Z ((0 0 4, 0 10 4, 10 10 4, 0 0 4))
dtype: geometry
Or as an array-like (one value per geometry):
>>> s.force_3d(range(4))
0 POINT Z (1 2 0)
1 POINT Z (0.5 2.5 2)
2 LINESTRING Z (1 1 2, 0 1 2, 1 0 2)
3 POLYGON Z ((0 0 3, 0 10 3, 10 10 3, 0 0 3))
dtype: geometry
"""
return _delegate_geo_method("force_3d", self, z=z)
def line_merge(self, directed=False):
"""Returns (Multi)LineStrings formed by combining the lines in a
MultiLineString.
Lines are joined together at their endpoints in case two lines are intersecting.
Lines are not joined when 3 or more lines are intersecting at the endpoints.
Line elements that cannot be joined are kept as is in the resulting
MultiLineString.
The direction of each merged LineString will be that of the majority of the
LineStrings from which it was derived. Except if ``directed=True`` is specified,
then the operation will not change the order of points within lines and so only
lines which can be joined with no change in direction are merged.
Non-linear geometeries result in an empty GeometryCollection.
Parameters
----------
directed : bool, default False
Only combine lines if possible without changing point order.
Requires GEOS >= 3.11.0
Returns
-------
GeoSeries
Examples
--------
>>> from shapely.geometry import MultiLineString, Point
>>> s = geopandas.GeoSeries(
... [
... MultiLineString([[(0, 2), (0, 10)], [(0, 10), (5, 10)]]),
... MultiLineString([[(0, 2), (0, 10)], [(0, 11), (5, 10)]]),
... MultiLineString(),
... MultiLineString([[(0, 0), (1, 0)], [(0, 0), (3, 0)]]),
... Point(0, 0),
... ]
... )
>>> s
0 MULTILINESTRING ((0 2, 0 10), (0 10, 5 10))
1 MULTILINESTRING ((0 2, 0 10), (0 11, 5 10))
2 MULTILINESTRING EMPTY
3 MULTILINESTRING ((0 0, 1 0), (0 0, 3 0))
4 POINT (0 0)
dtype: geometry
>>> s.line_merge()
0 LINESTRING (0 2, 0 10, 5 10)
1 MULTILINESTRING ((0 2, 0 10), (0 11, 5 10))
2 GEOMETRYCOLLECTION EMPTY
3 LINESTRING (1 0, 0 0, 3 0)
4 GEOMETRYCOLLECTION EMPTY
dtype: geometry
With ``directed=True``, you can avoid changing the order of points within lines
and merge only lines where no change of direction is required:
>>> s.line_merge(directed=True)
0 LINESTRING (0 2, 0 10, 5 10)
1 MULTILINESTRING ((0 2, 0 10), (0 11, 5 10))
2 GEOMETRYCOLLECTION EMPTY
3 MULTILINESTRING ((0 0, 1 0), (0 0, 3 0))
4 GEOMETRYCOLLECTION EMPTY
dtype: geometry
"""
return _delegate_geo_method("line_merge", self, directed=directed)
#
# Reduction operations that return a Shapely geometry
#
@property
def unary_union(self):
"""Returns a geometry containing the union of all geometries in the
``GeoSeries``.
The ``unary_union`` attribute is deprecated. Use :meth:`union_all`
instead.
Examples
--------
>>> from shapely.geometry import box
>>> s = geopandas.GeoSeries([box(0,0,1,1), box(0,0,2,2)])
>>> s
0 POLYGON ((1 0, 1 1, 0 1, 0 0, 1 0))
1 POLYGON ((2 0, 2 2, 0 2, 0 0, 2 0))
dtype: geometry
>>> union = s.unary_union
>>> print(union)
POLYGON ((0 1, 0 2, 2 2, 2 0, 1 0, 0 0, 0 1))
See also
--------
GeoSeries.union_all
"""
warn(
"The 'unary_union' attribute is deprecated, "
"use the 'union_all()' method instead.",
DeprecationWarning,
stacklevel=2,
)
return self.geometry.values.union_all()
def union_all(self, method="unary"):
"""Returns a geometry containing the union of all geometries in the
``GeoSeries``.
By default, the unary union algorithm is used. If the geometries are
non-overlapping (forming a coverage), GeoPandas can use a significantly faster
algorithm to perform the union using the ``method="coverage"`` option.
Parameters
----------
method : str (default ``"unary"``)
The method to use for the union. Options are:
* ``"unary"``: use the unary union algorithm. This option is the most robust
but can be slow for large numbers of geometries (default).
* ``"coverage"``: use the coverage union algorithm. This option is optimized
for non-overlapping polygons and can be significantly faster than the
unary union algorithm. However, it can produce invalid geometries if the
polygons overlap.
Examples
--------
>>> from shapely.geometry import box
>>> s = geopandas.GeoSeries([box(0, 0, 1, 1), box(0, 0, 2, 2)])
>>> s
0 POLYGON ((1 0, 1 1, 0 1, 0 0, 1 0))
1 POLYGON ((2 0, 2 2, 0 2, 0 0, 2 0))
dtype: geometry
>>> s.union_all()
<POLYGON ((0 1, 0 2, 2 2, 2 0, 1 0, 0 0, 0 1))>
"""
return self.geometry.values.union_all(method=method)
def intersection_all(self):
"""Returns a geometry containing the intersection of all geometries in
the ``GeoSeries``.
This method ignores None values when other geometries are present.
If all elements of the GeoSeries are None, an empty GeometryCollection is
returned.
Examples
--------
>>> from shapely.geometry import box
>>> s = geopandas.GeoSeries(
... [box(0, 0, 2, 2), box(1, 1, 3, 3), box(0, 0, 1.5, 1.5)]
... )
>>> s
0 POLYGON ((2 0, 2 2, 0 2, 0 0, 2 0))
1 POLYGON ((3 1, 3 3, 1 3, 1 1, 3 1))
2 POLYGON ((1.5 0, 1.5 1.5, 0 1.5, 0 0, 1.5 0))
dtype: geometry
>>> s.intersection_all()
<POLYGON ((1 1, 1 1.5, 1.5 1.5, 1.5 1, 1 1))>
"""
return self.geometry.values.intersection_all()
#
# Binary operations that return a pandas Series
#
def contains(self, other, align=None):
"""Returns a ``Series`` of ``dtype('bool')`` with value ``True`` for
each aligned geometry that contains `other`.
An object is said to contain `other` if at least one point of `other` lies in
the interior and no points of `other` lie in the exterior of the object.
(Therefore, any given polygon does not contain its own boundary - there is not
any point that lies in the interior.)
If either object is empty, this operation returns ``False``.
This is the inverse of :meth:`within` in the sense that the expression
``a.contains(b) == b.within(a)`` always evaluates to ``True``.
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : GeoSeries or geometric object
The GeoSeries (elementwise) or geometric object to test if it
is contained.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
Series (bool)
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... LineString([(0, 0), (0, 2)]),
... LineString([(0, 0), (0, 1)]),
... Point(0, 1),
... ],
... index=range(0, 4),
... )
>>> s2 = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (2, 2), (0, 2)]),
... Polygon([(0, 0), (1, 2), (0, 2)]),
... LineString([(0, 0), (0, 2)]),
... Point(0, 1),
... ],
... index=range(1, 5),
... )
>>> s
0 POLYGON ((0 0, 1 1, 0 1, 0 0))
1 LINESTRING (0 0, 0 2)
2 LINESTRING (0 0, 0 1)
3 POINT (0 1)
dtype: geometry
>>> s2
1 POLYGON ((0 0, 2 2, 0 2, 0 0))
2 POLYGON ((0 0, 1 2, 0 2, 0 0))
3 LINESTRING (0 0, 0 2)
4 POINT (0 1)
dtype: geometry
We can check if each geometry of GeoSeries contains a single
geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> point = Point(0, 1)
>>> s.contains(point)
0 False
1 True
2 False
3 True
dtype: bool
We can also check two GeoSeries against each other, row by row.
The GeoSeries above have different indices. We can either align both GeoSeries
based on index values and compare elements with the same index using
``align=True`` or ignore index and compare elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s2.contains(s, align=True)
0 False
1 False
2 False
3 True
4 False
dtype: bool
>>> s2.contains(s, align=False)
1 True
2 False
3 True
4 True
dtype: bool
Notes
-----
This method works in a row-wise manner. It does not check if an element
of one GeoSeries ``contains`` *any* element of the other one.
See also
--------
GeoSeries.contains_properly
GeoSeries.within
"""
return _binary_op("contains", self, other, align)
def contains_properly(self, other, align=None):
"""Returns a ``Series`` of ``dtype('bool')`` with value ``True`` for
each aligned geometry that is completely inside ``other``, with no common
boundary points.
Geometry A contains geometry B properly if B intersects the interior of A but
not the boundary (or exterior). This means that a geometry A does not “contain
properly” itself, which contrasts with the :meth:`~GeoSeries.contains` method,
where common points on the boundary are allowed.
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : GeoSeries or geometric object
The GeoSeries (elementwise) or geometric object to test if it
is contained.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
Series (bool)
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... LineString([(0, 0), (0, 2)]),
... LineString([(0, 0), (0, 1)]),
... Point(0, 1),
... ],
... index=range(0, 4),
... )
>>> s2 = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (2, 2), (0, 2)]),
... Polygon([(0, 0), (1, 2), (0, 2)]),
... LineString([(0, 0), (0, 2)]),
... Point(0, 1),
... ],
... index=range(1, 5),
... )
>>> s
0 POLYGON ((0 0, 1 1, 0 1, 0 0))
1 LINESTRING (0 0, 0 2)
2 LINESTRING (0 0, 0 1)
3 POINT (0 1)
dtype: geometry
>>> s2
1 POLYGON ((0 0, 2 2, 0 2, 0 0))
2 POLYGON ((0 0, 1 2, 0 2, 0 0))
3 LINESTRING (0 0, 0 2)
4 POINT (0 1)
dtype: geometry
We can check if each geometry of GeoSeries contains a single
geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> point = Point(0, 1)
>>> s.contains_properly(point)
0 False
1 True
2 False
3 True
dtype: bool
We can also check two GeoSeries against each other, row by row.
The GeoSeries above have different indices. We can either align both GeoSeries
based on index values and compare elements with the same index using
``align=True`` or ignore index and compare elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s2.contains_properly(s, align=True)
0 False
1 False
2 False
3 True
4 False
dtype: bool
>>> s2.contains_properly(s, align=False)
1 False
2 False
3 False
4 True
dtype: bool
Compare it to the result of :meth:`~GeoSeries.contains`:
>>> s2.contains(s, align=False)
1 True
2 False
3 True
4 True
dtype: bool
Notes
-----
This method works in a row-wise manner. It does not check if an element
of one GeoSeries ``contains_properly`` *any* element of the other one.
See also
--------
GeoSeries.contains
"""
return _binary_op("contains_properly", self, other, align)
def dwithin(self, other, distance, align=None):
"""Returns a ``Series`` of ``dtype('bool')`` with value ``True`` for
each aligned geometry that is within a set distance from ``other``.
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : GeoSeries or geometric object
The GeoSeries (elementwise) or geometric object to test for
equality.
distance : float, np.array, pd.Series
Distance(s) to test if each geometry is within. A scalar distance will be
applied to all geometries. An array or Series will be applied elementwise.
If np.array or pd.Series are used then it must have same length as the
GeoSeries.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
Series (bool)
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... LineString([(0, 0), (0, 2)]),
... LineString([(0, 0), (0, 1)]),
... Point(0, 1),
... ],
... index=range(0, 4),
... )
>>> s2 = geopandas.GeoSeries(
... [
... Polygon([(1, 0), (4, 2), (2, 2)]),
... Polygon([(2, 0), (3, 2), (2, 2)]),
... LineString([(2, 0), (2, 2)]),
... Point(1, 1),
... ],
... index=range(1, 5),
... )
>>> s
0 POLYGON ((0 0, 1 1, 0 1, 0 0))
1 LINESTRING (0 0, 0 2)
2 LINESTRING (0 0, 0 1)
3 POINT (0 1)
dtype: geometry
>>> s2
1 POLYGON ((1 0, 4 2, 2 2, 1 0))
2 POLYGON ((2 0, 3 2, 2 2, 2 0))
3 LINESTRING (2 0, 2 2)
4 POINT (1 1)
dtype: geometry
We can check if each geometry of GeoSeries contains a single
geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> point = Point(0, 1)
>>> s2.dwithin(point, 1.8)
1 True
2 False
3 False
4 True
dtype: bool
We can also check two GeoSeries against each other, row by row.
The GeoSeries above have different indices. We can either align both GeoSeries
based on index values and compare elements with the same index using
``align=True`` or ignore index and compare elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s.dwithin(s2, distance=1, align=True)
0 False
1 True
2 False
3 False
4 False
dtype: bool
>>> s.dwithin(s2, distance=1, align=False)
0 True
1 False
2 False
3 True
dtype: bool
Notes
-----
This method works in a row-wise manner. It does not check if an element
of one GeoSeries is within the set distance of *any* element of the other one.
See also
--------
GeoSeries.within
"""
return _binary_op("dwithin", self, other, distance=distance, align=align)
def geom_equals(self, other, align=None):
"""Returns a ``Series`` of ``dtype('bool')`` with value ``True`` for
each aligned geometry equal to `other`.
An object is said to be equal to `other` if its set-theoretic
`boundary`, `interior`, and `exterior` coincides with those of the
other.
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : GeoSeries or geometric object
The GeoSeries (elementwise) or geometric object to test for
equality.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
Series (bool)
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (2, 2), (0, 2)]),
... Polygon([(0, 0), (1, 2), (0, 2)]),
... LineString([(0, 0), (0, 2)]),
... Point(0, 1),
... ],
... )
>>> s2 = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (2, 2), (0, 2)]),
... Polygon([(0, 0), (1, 2), (0, 2)]),
... Point(0, 1),
... LineString([(0, 0), (0, 2)]),
... ],
... index=range(1, 5),
... )
>>> s
0 POLYGON ((0 0, 2 2, 0 2, 0 0))
1 POLYGON ((0 0, 1 2, 0 2, 0 0))
2 LINESTRING (0 0, 0 2)
3 POINT (0 1)
dtype: geometry
>>> s2
1 POLYGON ((0 0, 2 2, 0 2, 0 0))
2 POLYGON ((0 0, 1 2, 0 2, 0 0))
3 POINT (0 1)
4 LINESTRING (0 0, 0 2)
dtype: geometry
We can check if each geometry of GeoSeries contains a single
geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> polygon = Polygon([(0, 0), (2, 2), (0, 2)])
>>> s.geom_equals(polygon)
0 True
1 False
2 False
3 False
dtype: bool
We can also check two GeoSeries against each other, row by row.
The GeoSeries above have different indices. We can either align both GeoSeries
based on index values and compare elements with the same index using
``align=True`` or ignore index and compare elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s.geom_equals(s2)
0 False
1 False
2 False
3 True
4 False
dtype: bool
>>> s.geom_equals(s2, align=False)
0 True
1 True
2 False
3 False
dtype: bool
Notes
-----
This method works in a row-wise manner. It does not check if an element
of one GeoSeries is equal to *any* element of the other one.
See also
--------
GeoSeries.geom_equals_exact
"""
return _binary_op("geom_equals", self, other, align)
def geom_almost_equals(self, other, decimal=6, align=None):
"""Returns a ``Series`` of ``dtype('bool')`` with value ``True`` if
each aligned geometry is approximately equal to `other`.
Approximate equality is tested at all points to the specified `decimal`
place precision.
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : GeoSeries or geometric object
The GeoSeries (elementwise) or geometric object to compare to.
decimal : int
Decimal place precision used when testing for approximate equality.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
Series (bool)
Examples
--------
>>> from shapely.geometry import Point
>>> s = geopandas.GeoSeries(
... [
... Point(0, 1.1),
... Point(0, 1.01),
... Point(0, 1.001),
... ],
... )
>>> s
0 POINT (0 1.1)
1 POINT (0 1.01)
2 POINT (0 1.001)
dtype: geometry
>>> s.geom_almost_equals(Point(0, 1), decimal=2)
0 False
1 False
2 True
dtype: bool
>>> s.geom_almost_equals(Point(0, 1), decimal=1)
0 False
1 True
2 True
dtype: bool
Notes
-----
This method works in a row-wise manner. It does not check if an element
of one GeoSeries is equal to *any* element of the other one.
See also
--------
GeoSeries.geom_equals
GeoSeries.geom_equals_exact
"""
warnings.warn(
"The 'geom_almost_equals()' method is deprecated because the name is "
"confusing. The 'geom_equals_exact()' method should be used instead.",
FutureWarning,
stacklevel=2,
)
tolerance = 0.5 * 10 ** (-decimal)
return _binary_op(
"geom_equals_exact", self, other, tolerance=tolerance, align=align
)
def geom_equals_exact(self, other, tolerance, align=None):
"""Return True for all geometries that equal aligned *other* to a given
tolerance, else False.
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : GeoSeries or geometric object
The GeoSeries (elementwise) or geometric object to compare to.
tolerance : float
Decimal place precision used when testing for approximate equality.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
Series (bool)
Examples
--------
>>> from shapely.geometry import Point
>>> s = geopandas.GeoSeries(
... [
... Point(0, 1.1),
... Point(0, 1.0),
... Point(0, 1.2),
... ]
... )
>>> s
0 POINT (0 1.1)
1 POINT (0 1)
2 POINT (0 1.2)
dtype: geometry
>>> s.geom_equals_exact(Point(0, 1), tolerance=0.1)
0 False
1 True
2 False
dtype: bool
>>> s.geom_equals_exact(Point(0, 1), tolerance=0.15)
0 True
1 True
2 False
dtype: bool
Notes
-----
This method works in a row-wise manner. It does not check if an element
of one GeoSeries is equal to *any* element of the other one.
See also
--------
GeoSeries.geom_equals
"""
return _binary_op(
"geom_equals_exact", self, other, tolerance=tolerance, align=align
)
def crosses(self, other, align=None):
"""Returns a ``Series`` of ``dtype('bool')`` with value ``True`` for
each aligned geometry that cross `other`.
An object is said to cross `other` if its `interior` intersects the
`interior` of the other but does not contain it, and the dimension of
the intersection is less than the dimension of the one or the other.
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : GeoSeries or geometric object
The GeoSeries (elementwise) or geometric object to test if is
crossed.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
Series (bool)
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (2, 2), (0, 2)]),
... LineString([(0, 0), (2, 2)]),
... LineString([(2, 0), (0, 2)]),
... Point(0, 1),
... ],
... )
>>> s2 = geopandas.GeoSeries(
... [
... LineString([(1, 0), (1, 3)]),
... LineString([(2, 0), (0, 2)]),
... Point(1, 1),
... Point(0, 1),
... ],
... index=range(1, 5),
... )
>>> s
0 POLYGON ((0 0, 2 2, 0 2, 0 0))
1 LINESTRING (0 0, 2 2)
2 LINESTRING (2 0, 0 2)
3 POINT (0 1)
dtype: geometry
>>> s2
1 LINESTRING (1 0, 1 3)
2 LINESTRING (2 0, 0 2)
3 POINT (1 1)
4 POINT (0 1)
dtype: geometry
We can check if each geometry of GeoSeries crosses a single
geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> line = LineString([(-1, 1), (3, 1)])
>>> s.crosses(line)
0 True
1 True
2 True
3 False
dtype: bool
We can also check two GeoSeries against each other, row by row.
The GeoSeries above have different indices. We can either align both GeoSeries
based on index values and compare elements with the same index using
``align=True`` or ignore index and compare elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s.crosses(s2, align=True)
0 False
1 True
2 False
3 False
4 False
dtype: bool
>>> s.crosses(s2, align=False)
0 True
1 True
2 False
3 False
dtype: bool
Notice that a line does not cross a point that it contains.
Notes
-----
This method works in a row-wise manner. It does not check if an element
of one GeoSeries ``crosses`` *any* element of the other one.
See also
--------
GeoSeries.disjoint
GeoSeries.intersects
"""
return _binary_op("crosses", self, other, align)
def disjoint(self, other, align=None):
"""Returns a ``Series`` of ``dtype('bool')`` with value ``True`` for
each aligned geometry disjoint to `other`.
An object is said to be disjoint to `other` if its `boundary` and
`interior` does not intersect at all with those of the other.
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : GeoSeries or geometric object
The GeoSeries (elementwise) or geometric object to test if is
disjoint.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
Series (bool)
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (2, 2), (0, 2)]),
... LineString([(0, 0), (2, 2)]),
... LineString([(2, 0), (0, 2)]),
... Point(0, 1),
... ],
... )
>>> s2 = geopandas.GeoSeries(
... [
... Polygon([(-1, 0), (-1, 2), (0, -2)]),
... LineString([(0, 0), (0, 1)]),
... Point(1, 1),
... Point(0, 0),
... ],
... )
>>> s
0 POLYGON ((0 0, 2 2, 0 2, 0 0))
1 LINESTRING (0 0, 2 2)
2 LINESTRING (2 0, 0 2)
3 POINT (0 1)
dtype: geometry
>>> s2
0 POLYGON ((-1 0, -1 2, 0 -2, -1 0))
1 LINESTRING (0 0, 0 1)
2 POINT (1 1)
3 POINT (0 0)
dtype: geometry
We can check each geometry of GeoSeries to a single
geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> line = LineString([(0, 0), (2, 0)])
>>> s.disjoint(line)
0 False
1 False
2 False
3 True
dtype: bool
We can also check two GeoSeries against each other, row by row.
We can either align both GeoSeries
based on index values and compare elements with the same index using
``align=True`` or ignore index and compare elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s.disjoint(s2)
0 True
1 False
2 False
3 True
dtype: bool
Notes
-----
This method works in a row-wise manner. It does not check if an element
of one GeoSeries is equal to *any* element of the other one.
See also
--------
GeoSeries.intersects
GeoSeries.touches
"""
return _binary_op("disjoint", self, other, align)
def intersects(self, other, align=None):
"""Returns a ``Series`` of ``dtype('bool')`` with value ``True`` for
each aligned geometry that intersects `other`.
An object is said to intersect `other` if its `boundary` and `interior`
intersects in any way with those of the other.
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : GeoSeries or geometric object
The GeoSeries (elementwise) or geometric object to test if is
intersected.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
Series (bool)
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (2, 2), (0, 2)]),
... LineString([(0, 0), (2, 2)]),
... LineString([(2, 0), (0, 2)]),
... Point(0, 1),
... ],
... )
>>> s2 = geopandas.GeoSeries(
... [
... LineString([(1, 0), (1, 3)]),
... LineString([(2, 0), (0, 2)]),
... Point(1, 1),
... Point(0, 1),
... ],
... index=range(1, 5),
... )
>>> s
0 POLYGON ((0 0, 2 2, 0 2, 0 0))
1 LINESTRING (0 0, 2 2)
2 LINESTRING (2 0, 0 2)
3 POINT (0 1)
dtype: geometry
>>> s2
1 LINESTRING (1 0, 1 3)
2 LINESTRING (2 0, 0 2)
3 POINT (1 1)
4 POINT (0 1)
dtype: geometry
We can check if each geometry of GeoSeries crosses a single
geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> line = LineString([(-1, 1), (3, 1)])
>>> s.intersects(line)
0 True
1 True
2 True
3 True
dtype: bool
We can also check two GeoSeries against each other, row by row.
The GeoSeries above have different indices. We can either align both GeoSeries
based on index values and compare elements with the same index using
``align=True`` or ignore index and compare elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s.intersects(s2, align=True)
0 False
1 True
2 True
3 False
4 False
dtype: bool
>>> s.intersects(s2, align=False)
0 True
1 True
2 True
3 True
dtype: bool
Notes
-----
This method works in a row-wise manner. It does not check if an element
of one GeoSeries ``crosses`` *any* element of the other one.
See also
--------
GeoSeries.disjoint
GeoSeries.crosses
GeoSeries.touches
GeoSeries.intersection
"""
return _binary_op("intersects", self, other, align)
def overlaps(self, other, align=None):
"""Returns True for all aligned geometries that overlap *other*, else False.
Geometries overlaps if they have more than one but not all
points in common, have the same dimension, and the intersection of the
interiors of the geometries has the same dimension as the geometries
themselves.
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : GeoSeries or geometric object
The GeoSeries (elementwise) or geometric object to test if
overlaps.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
Series (bool)
Examples
--------
>>> from shapely.geometry import Polygon, LineString, MultiPoint, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (2, 2), (0, 2)]),
... Polygon([(0, 0), (2, 2), (0, 2)]),
... LineString([(0, 0), (2, 2)]),
... MultiPoint([(0, 0), (0, 1)]),
... ],
... )
>>> s2 = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (2, 0), (0, 2)]),
... LineString([(0, 1), (1, 1)]),
... LineString([(1, 1), (3, 3)]),
... Point(0, 1),
... ],
... index=range(1, 5),
... )
>>> s
0 POLYGON ((0 0, 2 2, 0 2, 0 0))
1 POLYGON ((0 0, 2 2, 0 2, 0 0))
2 LINESTRING (0 0, 2 2)
3 MULTIPOINT ((0 0), (0 1))
dtype: geometry
>>> s2
1 POLYGON ((0 0, 2 0, 0 2, 0 0))
2 LINESTRING (0 1, 1 1)
3 LINESTRING (1 1, 3 3)
4 POINT (0 1)
dtype: geometry
We can check if each geometry of GeoSeries overlaps a single
geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
>>> s.overlaps(polygon)
0 True
1 True
2 False
3 False
dtype: bool
We can also check two GeoSeries against each other, row by row.
The GeoSeries above have different indices. We can either align both GeoSeries
based on index values and compare elements with the same index using
``align=True`` or ignore index and compare elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s.overlaps(s2)
0 False
1 True
2 False
3 False
4 False
dtype: bool
>>> s.overlaps(s2, align=False)
0 True
1 False
2 True
3 False
dtype: bool
Notes
-----
This method works in a row-wise manner. It does not check if an element
of one GeoSeries ``overlaps`` *any* element of the other one.
See also
--------
GeoSeries.crosses
GeoSeries.intersects
"""
return _binary_op("overlaps", self, other, align)
def touches(self, other, align=None):
"""Returns a ``Series`` of ``dtype('bool')`` with value ``True`` for
each aligned geometry that touches `other`.
An object is said to touch `other` if it has at least one point in
common with `other` and its interior does not intersect with any part
of the other. Overlapping features therefore do not touch.
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : GeoSeries or geometric object
The GeoSeries (elementwise) or geometric object to test if is
touched.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
Series (bool)
Examples
--------
>>> from shapely.geometry import Polygon, LineString, MultiPoint, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (2, 2), (0, 2)]),
... Polygon([(0, 0), (2, 2), (0, 2)]),
... LineString([(0, 0), (2, 2)]),
... MultiPoint([(0, 0), (0, 1)]),
... ],
... )
>>> s2 = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (-2, 0), (0, -2)]),
... LineString([(0, 1), (1, 1)]),
... LineString([(1, 1), (3, 0)]),
... Point(0, 1),
... ],
... index=range(1, 5),
... )
>>> s
0 POLYGON ((0 0, 2 2, 0 2, 0 0))
1 POLYGON ((0 0, 2 2, 0 2, 0 0))
2 LINESTRING (0 0, 2 2)
3 MULTIPOINT ((0 0), (0 1))
dtype: geometry
>>> s2
1 POLYGON ((0 0, -2 0, 0 -2, 0 0))
2 LINESTRING (0 1, 1 1)
3 LINESTRING (1 1, 3 0)
4 POINT (0 1)
dtype: geometry
We can check if each geometry of GeoSeries touches a single
geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> line = LineString([(0, 0), (-1, -2)])
>>> s.touches(line)
0 True
1 True
2 True
3 True
dtype: bool
We can also check two GeoSeries against each other, row by row.
The GeoSeries above have different indices. We can either align both GeoSeries
based on index values and compare elements with the same index using
``align=True`` or ignore index and compare elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s.touches(s2, align=True)
0 False
1 True
2 True
3 False
4 False
dtype: bool
>>> s.touches(s2, align=False)
0 True
1 False
2 True
3 False
dtype: bool
Notes
-----
This method works in a row-wise manner. It does not check if an element
of one GeoSeries ``touches`` *any* element of the other one.
See also
--------
GeoSeries.overlaps
GeoSeries.intersects
"""
return _binary_op("touches", self, other, align)
def within(self, other, align=None):
"""Returns a ``Series`` of ``dtype('bool')`` with value ``True`` for
each aligned geometry that is within `other`.
An object is said to be within `other` if at least one of its points is located
in the `interior` and no points are located in the `exterior` of the other.
If either object is empty, this operation returns ``False``.
This is the inverse of :meth:`contains` in the sense that the
expression ``a.within(b) == b.contains(a)`` always evaluates to
``True``.
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : GeoSeries or geometric object
The GeoSeries (elementwise) or geometric object to test if each
geometry is within.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
Series (bool)
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (2, 2), (0, 2)]),
... Polygon([(0, 0), (1, 2), (0, 2)]),
... LineString([(0, 0), (0, 2)]),
... Point(0, 1),
... ],
... )
>>> s2 = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... LineString([(0, 0), (0, 2)]),
... LineString([(0, 0), (0, 1)]),
... Point(0, 1),
... ],
... index=range(1, 5),
... )
>>> s
0 POLYGON ((0 0, 2 2, 0 2, 0 0))
1 POLYGON ((0 0, 1 2, 0 2, 0 0))
2 LINESTRING (0 0, 0 2)
3 POINT (0 1)
dtype: geometry
>>> s2
1 POLYGON ((0 0, 1 1, 0 1, 0 0))
2 LINESTRING (0 0, 0 2)
3 LINESTRING (0 0, 0 1)
4 POINT (0 1)
dtype: geometry
We can check if each geometry of GeoSeries is within a single
geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> polygon = Polygon([(0, 0), (2, 2), (0, 2)])
>>> s.within(polygon)
0 True
1 True
2 False
3 False
dtype: bool
We can also check two GeoSeries against each other, row by row.
The GeoSeries above have different indices. We can either align both GeoSeries
based on index values and compare elements with the same index using
``align=True`` or ignore index and compare elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s2.within(s)
0 False
1 False
2 True
3 False
4 False
dtype: bool
>>> s2.within(s, align=False)
1 True
2 False
3 True
4 True
dtype: bool
Notes
-----
This method works in a row-wise manner. It does not check if an element
of one GeoSeries is ``within`` *any* element of the other one.
See also
--------
GeoSeries.contains
"""
return _binary_op("within", self, other, align)
def covers(self, other, align=None):
"""
Returns a ``Series`` of ``dtype('bool')`` with value ``True`` for
each aligned geometry that is entirely covering `other`.
An object A is said to cover another object B if no points of B lie
in the exterior of A.
If either object is empty, this operation returns ``False``.
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
See
https://lin-ear-th-inking.blogspot.com/2007/06/subtleties-of-ogc-covers-spatial.html
for reference.
Parameters
----------
other : Geoseries or geometric object
The Geoseries (elementwise) or geometric object to check is being covered.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
Series (bool)
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (2, 0), (2, 2), (0, 2)]),
... Polygon([(0, 0), (2, 2), (0, 2)]),
... LineString([(0, 0), (2, 2)]),
... Point(0, 0),
... ],
... )
>>> s2 = geopandas.GeoSeries(
... [
... Polygon([(0.5, 0.5), (1.5, 0.5), (1.5, 1.5), (0.5, 1.5)]),
... Polygon([(0, 0), (2, 0), (2, 2), (0, 2)]),
... LineString([(1, 1), (1.5, 1.5)]),
... Point(0, 0),
... ],
... index=range(1, 5),
... )
>>> s
0 POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0))
1 POLYGON ((0 0, 2 2, 0 2, 0 0))
2 LINESTRING (0 0, 2 2)
3 POINT (0 0)
dtype: geometry
>>> s2
1 POLYGON ((0.5 0.5, 1.5 0.5, 1.5 1.5, 0.5 1.5, ...
2 POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0))
3 LINESTRING (1 1, 1.5 1.5)
4 POINT (0 0)
dtype: geometry
We can check if each geometry of GeoSeries covers a single
geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> poly = Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])
>>> s.covers(poly)
0 True
1 False
2 False
3 False
dtype: bool
We can also check two GeoSeries against each other, row by row.
The GeoSeries above have different indices. We can either align both GeoSeries
based on index values and compare elements with the same index using
``align=True`` or ignore index and compare elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s.covers(s2, align=True)
0 False
1 False
2 False
3 False
4 False
dtype: bool
>>> s.covers(s2, align=False)
0 True
1 False
2 True
3 True
dtype: bool
Notes
-----
This method works in a row-wise manner. It does not check if an element
of one GeoSeries ``covers`` *any* element of the other one.
See also
--------
GeoSeries.covered_by
GeoSeries.overlaps
"""
return _binary_op("covers", self, other, align)
def covered_by(self, other, align=None):
"""
Returns a ``Series`` of ``dtype('bool')`` with value ``True`` for
each aligned geometry that is entirely covered by `other`.
An object A is said to cover another object B if no points of B lie
in the exterior of A.
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
See
https://lin-ear-th-inking.blogspot.com/2007/06/subtleties-of-ogc-covers-spatial.html
for reference.
Parameters
----------
other : Geoseries or geometric object
The Geoseries (elementwise) or geometric object to check is being covered.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
Series (bool)
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0.5, 0.5), (1.5, 0.5), (1.5, 1.5), (0.5, 1.5)]),
... Polygon([(0, 0), (2, 0), (2, 2), (0, 2)]),
... LineString([(1, 1), (1.5, 1.5)]),
... Point(0, 0),
... ],
... )
>>> s2 = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (2, 0), (2, 2), (0, 2)]),
... Polygon([(0, 0), (2, 2), (0, 2)]),
... LineString([(0, 0), (2, 2)]),
... Point(0, 0),
... ],
... index=range(1, 5),
... )
>>> s
0 POLYGON ((0.5 0.5, 1.5 0.5, 1.5 1.5, 0.5 1.5, ...
1 POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0))
2 LINESTRING (1 1, 1.5 1.5)
3 POINT (0 0)
dtype: geometry
>>>
>>> s2
1 POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0))
2 POLYGON ((0 0, 2 2, 0 2, 0 0))
3 LINESTRING (0 0, 2 2)
4 POINT (0 0)
dtype: geometry
We can check if each geometry of GeoSeries is covered by a single
geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> poly = Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])
>>> s.covered_by(poly)
0 True
1 True
2 True
3 True
dtype: bool
We can also check two GeoSeries against each other, row by row.
The GeoSeries above have different indices. We can either align both GeoSeries
based on index values and compare elements with the same index using
``align=True`` or ignore index and compare elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s.covered_by(s2, align=True)
0 False
1 True
2 True
3 True
4 False
dtype: bool
>>> s.covered_by(s2, align=False)
0 True
1 False
2 True
3 True
dtype: bool
Notes
-----
This method works in a row-wise manner. It does not check if an element
of one GeoSeries is ``covered_by`` *any* element of the other one.
See also
--------
GeoSeries.covers
GeoSeries.overlaps
"""
return _binary_op("covered_by", self, other, align)
def distance(self, other, align=None):
"""Returns a ``Series`` containing the distance to aligned `other`.
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : Geoseries or geometric object
The Geoseries (elementwise) or geometric object to find the
distance to.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
Series (float)
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 0), (1, 1)]),
... Polygon([(0, 0), (-1, 0), (-1, 1)]),
... LineString([(1, 1), (0, 0)]),
... Point(0, 0),
... ],
... )
>>> s2 = geopandas.GeoSeries(
... [
... Polygon([(0.5, 0.5), (1.5, 0.5), (1.5, 1.5), (0.5, 1.5)]),
... Point(3, 1),
... LineString([(1, 0), (2, 0)]),
... Point(0, 1),
... ],
... index=range(1, 5),
... )
>>> s
0 POLYGON ((0 0, 1 0, 1 1, 0 0))
1 POLYGON ((0 0, -1 0, -1 1, 0 0))
2 LINESTRING (1 1, 0 0)
3 POINT (0 0)
dtype: geometry
>>> s2
1 POLYGON ((0.5 0.5, 1.5 0.5, 1.5 1.5, 0.5 1.5, ...
2 POINT (3 1)
3 LINESTRING (1 0, 2 0)
4 POINT (0 1)
dtype: geometry
We can check the distance of each geometry of GeoSeries to a single
geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> point = Point(-1, 0)
>>> s.distance(point)
0 1.0
1 0.0
2 1.0
3 1.0
dtype: float64
We can also check two GeoSeries against each other, row by row.
The GeoSeries above have different indices. We can either align both GeoSeries
based on index values and use elements with the same index using
``align=True`` or ignore index and use elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s.distance(s2, align=True)
0 NaN
1 0.707107
2 2.000000
3 1.000000
4 NaN
dtype: float64
>>> s.distance(s2, align=False)
0 0.000000
1 3.162278
2 0.707107
3 1.000000
dtype: float64
"""
return _binary_op("distance", self, other, align)
def hausdorff_distance(self, other, align=None, densify=None):
"""Returns a ``Series`` containing the Hausdorff distance to aligned `other`.
The Hausdorff distance is the largest distance consisting of any point in `self`
with the nearest point in `other`.
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : GeoSeries or geometric object
The Geoseries (elementwise) or geometric object to find the
distance to.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
densify : float (default None)
A value between 0 and 1, that splits each subsegment of a line string
into equal length segments, making the approximation less coarse.
A densify value of 0.5 will add a point halfway between each pair of
points. A densify value of 0.25 will add a point a quarter of the way
between each pair of points.
Returns
-------
Series (float)
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 0), (1, 1)]),
... Polygon([(0, 0), (-1, 0), (-1, 1)]),
... LineString([(1, 1), (0, 0)]),
... Point(0, 0),
... ],
... )
>>> s2 = geopandas.GeoSeries(
... [
... Polygon([(0.5, 0.5), (1.5, 0.5), (1.5, 1.5), (0.5, 1.5)]),
... Point(3, 1),
... LineString([(1, 0), (2, 0)]),
... Point(0, 1),
... ],
... index=range(1, 5),
... )
>>> s
0 POLYGON ((0 0, 1 0, 1 1, 0 0))
1 POLYGON ((0 0, -1 0, -1 1, 0 0))
2 LINESTRING (1 1, 0 0)
3 POINT (0 0)
dtype: geometry
>>> s2
1 POLYGON ((0.5 0.5, 1.5 0.5, 1.5 1.5, 0.5 1.5, ...
2 POINT (3 1)
3 LINESTRING (1 0, 2 0)
4 POINT (0 1)
dtype: geometry
We can check the hausdorff distance of each geometry of GeoSeries
to a single geometry:
>>> point = Point(-1, 0)
>>> s.hausdorff_distance(point)
0 2.236068
1 1.000000
2 2.236068
3 1.000000
dtype: float64
We can also check two GeoSeries against each other, row by row.
The GeoSeries above have different indices. We can either align both GeoSeries
based on index values and use elements with the same index using
``align=True`` or ignore index and use elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s.hausdorff_distance(s2, align=True)
0 NaN
1 2.121320
2 3.162278
3 2.000000
4 NaN
dtype: float64
>>> s.hausdorff_distance(s2, align=False)
0 0.707107
1 4.123106
2 1.414214
3 1.000000
dtype: float64
We can also set a densify value, which is a float between 0 and 1 and
signifies the fraction of the distance between each pair of points that will
be used as the distance between the points when densifying.
>>> l1 = geopandas.GeoSeries([LineString([(130, 0), (0, 0), (0, 150)])])
>>> l2 = geopandas.GeoSeries([LineString([(10, 10), (10, 150), (130, 10)])])
>>> l1.hausdorff_distance(l2)
0 14.142136
dtype: float64
>>> l1.hausdorff_distance(l2, densify=0.25)
0 70.0
dtype: float64
"""
return _binary_op("hausdorff_distance", self, other, align, densify=densify)
def frechet_distance(self, other, align=None, densify=None):
"""Returns a ``Series`` containing the Frechet distance to aligned `other`.
The Fréchet distance is a measure of similarity: it is the greatest distance
between any point in A and the closest point in B. The discrete distance is an
approximation of this metric: only vertices are considered. The parameter
``densify`` makes this approximation less coarse by splitting the line segments
between vertices before computing the distance.
Fréchet distance sweep continuously along their respective curves and the
direction of curves is significant. This makes it a better measure of similarity
than Hausdorff distance for curve or surface matching.
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : GeoSeries or geometric object
The Geoseries (elementwise) or geometric object to find the
distance to.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
densify : float (default None)
A value between 0 and 1, that splits each subsegment of a line string
into equal length segments, making the approximation less coarse.
A densify value of 0.5 will add a point halfway between each pair of
points. A densify value of 0.25 will add a point every quarter of the way
between each pair of points.
Returns
-------
Series (float)
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 0), (1, 1)]),
... Polygon([(0, 0), (-1, 0), (-1, 1)]),
... LineString([(1, 1), (0, 0)]),
... Point(0, 0),
... ],
... )
>>> s2 = geopandas.GeoSeries(
... [
... Polygon([(0.5, 0.5), (1.5, 0.5), (1.5, 1.5), (0.5, 1.5)]),
... Point(3, 1),
... LineString([(1, 0), (2, 0)]),
... Point(0, 1),
... ],
... index=range(1, 5),
... )
>>> s
0 POLYGON ((0 0, 1 0, 1 1, 0 0))
1 POLYGON ((0 0, -1 0, -1 1, 0 0))
2 LINESTRING (1 1, 0 0)
3 POINT (0 0)
dtype: geometry
>>> s2
1 POLYGON ((0.5 0.5, 1.5 0.5, 1.5 1.5, 0.5 1.5, ...
2 POINT (3 1)
3 LINESTRING (1 0, 2 0)
4 POINT (0 1)
dtype: geometry
We can check the frechet distance of each geometry of GeoSeries
to a single geometry:
>>> point = Point(-1, 0)
>>> s.frechet_distance(point)
0 2.236068
1 1.000000
2 2.236068
3 1.000000
dtype: float64
We can also check two GeoSeries against each other, row by row.
The GeoSeries above have different indices. We can either align both GeoSeries
based on index values and use elements with the same index using
``align=True`` or ignore index and use elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s.frechet_distance(s2, align=True)
0 NaN
1 2.121320
2 3.162278
3 2.000000
4 NaN
dtype: float64
>>> s.frechet_distance(s2, align=False)
0 0.707107
1 4.123106
2 2.000000
3 1.000000
dtype: float64
We can also set a ``densify`` value, which is a float between 0 and 1 and
signifies the fraction of the distance between each pair of points that will
be used as the distance between the points when densifying.
>>> l1 = geopandas.GeoSeries([LineString([(0, 0), (10, 0), (0, 15)])])
>>> l2 = geopandas.GeoSeries([LineString([(0, 0), (20, 15), (9, 11)])])
>>> l1.frechet_distance(l2)
0 18.027756
dtype: float64
>>> l1.frechet_distance(l2, densify=0.25)
0 16.77051
dtype: float64
"""
return _binary_op("frechet_distance", self, other, align, densify=densify)
#
# Binary operations that return a GeoSeries
#
def difference(self, other, align=None):
"""Returns a ``GeoSeries`` of the points in each aligned geometry that
are not in `other`.
.. image:: ../../../_static/binary_geo-difference.svg
:align: center
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : Geoseries or geometric object
The Geoseries (elementwise) or geometric object to find the
difference to.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
GeoSeries
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (2, 2), (0, 2)]),
... Polygon([(0, 0), (2, 2), (0, 2)]),
... LineString([(0, 0), (2, 2)]),
... LineString([(2, 0), (0, 2)]),
... Point(0, 1),
... ],
... )
>>> s2 = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... LineString([(1, 0), (1, 3)]),
... LineString([(2, 0), (0, 2)]),
... Point(1, 1),
... Point(0, 1),
... ],
... index=range(1, 6),
... )
>>> s
0 POLYGON ((0 0, 2 2, 0 2, 0 0))
1 POLYGON ((0 0, 2 2, 0 2, 0 0))
2 LINESTRING (0 0, 2 2)
3 LINESTRING (2 0, 0 2)
4 POINT (0 1)
dtype: geometry
>>> s2
1 POLYGON ((0 0, 1 1, 0 1, 0 0))
2 LINESTRING (1 0, 1 3)
3 LINESTRING (2 0, 0 2)
4 POINT (1 1)
5 POINT (0 1)
dtype: geometry
We can do difference of each geometry and a single
shapely geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> s.difference(Polygon([(0, 0), (1, 1), (0, 1)]))
0 POLYGON ((0 2, 2 2, 1 1, 0 1, 0 2))
1 POLYGON ((0 2, 2 2, 1 1, 0 1, 0 2))
2 LINESTRING (1 1, 2 2)
3 MULTILINESTRING ((2 0, 1 1), (1 1, 0 2))
4 POINT EMPTY
dtype: geometry
We can also check two GeoSeries against each other, row by row.
The GeoSeries above have different indices. We can either align both GeoSeries
based on index values and compare elements with the same index using
``align=True`` or ignore index and compare elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s.difference(s2, align=True)
0 None
1 POLYGON ((0 2, 2 2, 1 1, 0 1, 0 2))
2 MULTILINESTRING ((0 0, 1 1), (1 1, 2 2))
3 LINESTRING EMPTY
4 POINT (0 1)
5 None
dtype: geometry
>>> s.difference(s2, align=False)
0 POLYGON ((0 2, 2 2, 1 1, 0 1, 0 2))
1 POLYGON ((0 0, 0 2, 1 2, 2 2, 1 1, 0 0))
2 MULTILINESTRING ((0 0, 1 1), (1 1, 2 2))
3 LINESTRING (2 0, 0 2)
4 POINT EMPTY
dtype: geometry
See Also
--------
GeoSeries.symmetric_difference
GeoSeries.union
GeoSeries.intersection
"""
return _binary_geo("difference", self, other, align)
def symmetric_difference(self, other, align=None):
"""Returns a ``GeoSeries`` of the symmetric difference of points in
each aligned geometry with `other`.
For each geometry, the symmetric difference consists of points in the
geometry not in `other`, and points in `other` not in the geometry.
.. image:: ../../../_static/binary_geo-symm_diff.svg
:align: center
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : Geoseries or geometric object
The Geoseries (elementwise) or geometric object to find the
symmetric difference to.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
GeoSeries
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (2, 2), (0, 2)]),
... Polygon([(0, 0), (2, 2), (0, 2)]),
... LineString([(0, 0), (2, 2)]),
... LineString([(2, 0), (0, 2)]),
... Point(0, 1),
... ],
... )
>>> s2 = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... LineString([(1, 0), (1, 3)]),
... LineString([(2, 0), (0, 2)]),
... Point(1, 1),
... Point(0, 1),
... ],
... index=range(1, 6),
... )
>>> s
0 POLYGON ((0 0, 2 2, 0 2, 0 0))
1 POLYGON ((0 0, 2 2, 0 2, 0 0))
2 LINESTRING (0 0, 2 2)
3 LINESTRING (2 0, 0 2)
4 POINT (0 1)
dtype: geometry
>>> s2
1 POLYGON ((0 0, 1 1, 0 1, 0 0))
2 LINESTRING (1 0, 1 3)
3 LINESTRING (2 0, 0 2)
4 POINT (1 1)
5 POINT (0 1)
dtype: geometry
We can do symmetric difference of each geometry and a single
shapely geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> s.symmetric_difference(Polygon([(0, 0), (1, 1), (0, 1)]))
0 POLYGON ((0 2, 2 2, 1 1, 0 1, 0 2))
1 POLYGON ((0 2, 2 2, 1 1, 0 1, 0 2))
2 GEOMETRYCOLLECTION (POLYGON ((0 0, 0 1, 1 1, 0...
3 GEOMETRYCOLLECTION (POLYGON ((0 0, 0 1, 1 1, 0...
4 POLYGON ((0 1, 1 1, 0 0, 0 1))
dtype: geometry
We can also check two GeoSeries against each other, row by row.
The GeoSeries above have different indices. We can either align both GeoSeries
based on index values and compare elements with the same index using
``align=True`` or ignore index and compare elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s.symmetric_difference(s2, align=True)
0 None
1 POLYGON ((0 2, 2 2, 1 1, 0 1, 0 2))
2 MULTILINESTRING ((0 0, 1 1), (1 1, 2 2), (1 0,...
3 LINESTRING EMPTY
4 MULTIPOINT ((0 1), (1 1))
5 None
dtype: geometry
>>> s.symmetric_difference(s2, align=False)
0 POLYGON ((0 2, 2 2, 1 1, 0 1, 0 2))
1 GEOMETRYCOLLECTION (POLYGON ((0 0, 0 2, 1 2, 2...
2 MULTILINESTRING ((0 0, 1 1), (1 1, 2 2), (2 0,...
3 LINESTRING (2 0, 0 2)
4 POINT EMPTY
dtype: geometry
See Also
--------
GeoSeries.difference
GeoSeries.union
GeoSeries.intersection
"""
return _binary_geo("symmetric_difference", self, other, align)
def union(self, other, align=None):
"""Returns a ``GeoSeries`` of the union of points in each aligned geometry with
`other`.
.. image:: ../../../_static/binary_geo-union.svg
:align: center
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : Geoseries or geometric object
The Geoseries (elementwise) or geometric object to find the union
with.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
GeoSeries
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (2, 2), (0, 2)]),
... Polygon([(0, 0), (2, 2), (0, 2)]),
... LineString([(0, 0), (2, 2)]),
... LineString([(2, 0), (0, 2)]),
... Point(0, 1),
... ],
... )
>>> s2 = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... LineString([(1, 0), (1, 3)]),
... LineString([(2, 0), (0, 2)]),
... Point(1, 1),
... Point(0, 1),
... ],
... index=range(1, 6),
... )
>>> s
0 POLYGON ((0 0, 2 2, 0 2, 0 0))
1 POLYGON ((0 0, 2 2, 0 2, 0 0))
2 LINESTRING (0 0, 2 2)
3 LINESTRING (2 0, 0 2)
4 POINT (0 1)
dtype: geometry
>>>
>>> s2
1 POLYGON ((0 0, 1 1, 0 1, 0 0))
2 LINESTRING (1 0, 1 3)
3 LINESTRING (2 0, 0 2)
4 POINT (1 1)
5 POINT (0 1)
dtype: geometry
We can do union of each geometry and a single
shapely geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> s.union(Polygon([(0, 0), (1, 1), (0, 1)]))
0 POLYGON ((0 0, 0 1, 0 2, 2 2, 1 1, 0 0))
1 POLYGON ((0 0, 0 1, 0 2, 2 2, 1 1, 0 0))
2 GEOMETRYCOLLECTION (POLYGON ((0 0, 0 1, 1 1, 0...
3 GEOMETRYCOLLECTION (POLYGON ((0 0, 0 1, 1 1, 0...
4 POLYGON ((0 1, 1 1, 0 0, 0 1))
dtype: geometry
We can also check two GeoSeries against each other, row by row.
The GeoSeries above have different indices. We can either align both GeoSeries
based on index values and compare elements with the same index using
``align=True`` or ignore index and compare elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s.union(s2, align=True)
0 None
1 POLYGON ((0 0, 0 1, 0 2, 2 2, 1 1, 0 0))
2 MULTILINESTRING ((0 0, 1 1), (1 1, 2 2), (1 0,...
3 LINESTRING (2 0, 0 2)
4 MULTIPOINT ((0 1), (1 1))
5 None
dtype: geometry
>>> s.union(s2, align=False)
0 POLYGON ((0 0, 0 1, 0 2, 2 2, 1 1, 0 0))
1 GEOMETRYCOLLECTION (POLYGON ((0 0, 0 2, 1 2, 2...
2 MULTILINESTRING ((0 0, 1 1), (1 1, 2 2), (2 0,...
3 LINESTRING (2 0, 0 2)
4 POINT (0 1)
dtype: geometry
See Also
--------
GeoSeries.symmetric_difference
GeoSeries.difference
GeoSeries.intersection
"""
return _binary_geo("union", self, other, align)
def intersection(self, other, align=None):
"""Returns a ``GeoSeries`` of the intersection of points in each
aligned geometry with `other`.
.. image:: ../../../_static/binary_geo-intersection.svg
:align: center
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : Geoseries or geometric object
The Geoseries (elementwise) or geometric object to find the
intersection with.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
GeoSeries
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (2, 2), (0, 2)]),
... Polygon([(0, 0), (2, 2), (0, 2)]),
... LineString([(0, 0), (2, 2)]),
... LineString([(2, 0), (0, 2)]),
... Point(0, 1),
... ],
... )
>>> s2 = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... LineString([(1, 0), (1, 3)]),
... LineString([(2, 0), (0, 2)]),
... Point(1, 1),
... Point(0, 1),
... ],
... index=range(1, 6),
... )
>>> s
0 POLYGON ((0 0, 2 2, 0 2, 0 0))
1 POLYGON ((0 0, 2 2, 0 2, 0 0))
2 LINESTRING (0 0, 2 2)
3 LINESTRING (2 0, 0 2)
4 POINT (0 1)
dtype: geometry
>>> s2
1 POLYGON ((0 0, 1 1, 0 1, 0 0))
2 LINESTRING (1 0, 1 3)
3 LINESTRING (2 0, 0 2)
4 POINT (1 1)
5 POINT (0 1)
dtype: geometry
We can also do intersection of each geometry and a single
shapely geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> s.intersection(Polygon([(0, 0), (1, 1), (0, 1)]))
0 POLYGON ((0 0, 0 1, 1 1, 0 0))
1 POLYGON ((0 0, 0 1, 1 1, 0 0))
2 LINESTRING (0 0, 1 1)
3 POINT (1 1)
4 POINT (0 1)
dtype: geometry
We can also check two GeoSeries against each other, row by row.
The GeoSeries above have different indices. We can either align both GeoSeries
based on index values and compare elements with the same index using
``align=True`` or ignore index and compare elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s.intersection(s2, align=True)
0 None
1 POLYGON ((0 0, 0 1, 1 1, 0 0))
2 POINT (1 1)
3 LINESTRING (2 0, 0 2)
4 POINT EMPTY
5 None
dtype: geometry
>>> s.intersection(s2, align=False)
0 POLYGON ((0 0, 0 1, 1 1, 0 0))
1 LINESTRING (1 1, 1 2)
2 POINT (1 1)
3 POINT (1 1)
4 POINT (0 1)
dtype: geometry
See Also
--------
GeoSeries.difference
GeoSeries.symmetric_difference
GeoSeries.union
"""
return _binary_geo("intersection", self, other, align)
def clip_by_rect(self, xmin, ymin, xmax, ymax):
"""Returns a ``GeoSeries`` of the portions of geometry within the given
rectangle.
Note that the results are not exactly equal to
:meth:`~GeoSeries.intersection()`. E.g. in edge cases,
:meth:`~GeoSeries.clip_by_rect()` will not return a point just touching the
rectangle. Check the examples section below for some of these exceptions.
The geometry is clipped in a fast but possibly dirty way. The output is not
guaranteed to be valid. No exceptions will be raised for topological errors.
Note: empty geometries or geometries that do not overlap with the specified
bounds will result in ``GEOMETRYCOLLECTION EMPTY``.
Parameters
----------
xmin: float
Minimum x value of the rectangle
ymin: float
Minimum y value of the rectangle
xmax: float
Maximum x value of the rectangle
ymax: float
Maximum y value of the rectangle
Returns
-------
GeoSeries
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (2, 2), (0, 2)]),
... Polygon([(0, 0), (2, 2), (0, 2)]),
... LineString([(0, 0), (2, 2)]),
... LineString([(2, 0), (0, 2)]),
... Point(0, 1),
... ],
... )
>>> bounds = (0, 0, 1, 1)
>>> s
0 POLYGON ((0 0, 2 2, 0 2, 0 0))
1 POLYGON ((0 0, 2 2, 0 2, 0 0))
2 LINESTRING (0 0, 2 2)
3 LINESTRING (2 0, 0 2)
4 POINT (0 1)
dtype: geometry
>>> s.clip_by_rect(*bounds)
0 POLYGON ((0 0, 0 1, 1 1, 0 0))
1 POLYGON ((0 0, 0 1, 1 1, 0 0))
2 LINESTRING (0 0, 1 1)
3 GEOMETRYCOLLECTION EMPTY
4 GEOMETRYCOLLECTION EMPTY
dtype: geometry
See also
--------
GeoSeries.intersection
"""
from .geoseries import GeoSeries
geometry_array = GeometryArray(self.geometry.values)
clipped_geometry = geometry_array.clip_by_rect(xmin, ymin, xmax, ymax)
return GeoSeries(clipped_geometry, index=self.index, crs=self.crs)
def shortest_line(self, other, align=None):
"""
Returns the shortest two-point line between two geometries.
The resulting line consists of two points, representing the nearest points
between the geometry pair. The line always starts in the first geometry a
and ends in he second geometry b. The endpoints of the line will not
necessarily be existing vertices of the input geometries a and b, but
can also be a point along a line segment.
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : Geoseries or geometric object
The Geoseries (elementwise) or geometric object to find the
shortest line with.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
GeoSeries
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (2, 2), (0, 2)]),
... Polygon([(0, 0), (2, 2), (0, 2)]),
... LineString([(0, 0), (2, 2)]),
... LineString([(2, 0), (0, 2)]),
... Point(0, 1),
... ],
... )
>>> s
0 POLYGON ((0 0, 2 2, 0 2, 0 0))
1 POLYGON ((0 0, 2 2, 0 2, 0 0))
2 LINESTRING (0 0, 2 2)
3 LINESTRING (2 0, 0 2)
4 POINT (0 1)
dtype: geometry
We can also do intersection of each geometry and a single
shapely geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> p = Point(3, 3)
>>> s.shortest_line(p)
0 LINESTRING (2 2, 3 3)
1 LINESTRING (2 2, 3 3)
2 LINESTRING (2 2, 3 3)
3 LINESTRING (1 1, 3 3)
4 LINESTRING (0 1, 3 3)
dtype: geometry
We can also check two GeoSeries against each other, row by row.
The GeoSeries above have different indices than the one below. We can either
align both GeoSeries based on index values and compare elements with the same
index using ``align=True`` or ignore index and compare elements based on their
matching order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s2 = geopandas.GeoSeries(
... [
... Polygon([(0.5, 0.5), (1.5, 0.5), (1.5, 1.5), (0.5, 1.5)]),
... Point(3, 1),
... LineString([(1, 0), (2, 0)]),
... Point(10, 15),
... Point(0, 1),
... ],
... index=range(1, 6),
... )
>>> s.shortest_line(s2, align=True)
0 None
1 LINESTRING (0.5 0.5, 0.5 0.5)
2 LINESTRING (2 2, 3 1)
3 LINESTRING (2 0, 2 0)
4 LINESTRING (0 1, 10 15)
5 None
dtype: geometry
>>>
>>> s.shortest_line(s2, align=False)
0 LINESTRING (0.5 0.5, 0.5 0.5)
1 LINESTRING (2 2, 3 1)
2 LINESTRING (0.5 0.5, 1 0)
3 LINESTRING (0 2, 10 15)
4 LINESTRING (0 1, 0 1)
dtype: geometry
"""
return _binary_geo("shortest_line", self, other, align)
def snap(self, other, tolerance, align=None):
"""Snaps an input geometry to reference geometry's vertices.
Vertices of the first geometry are snapped to vertices of the second. geometry,
returning a new geometry; the input geometries are not modified. The result
geometry is the input geometry with the vertices snapped. If no snapping occurs
then the input geometry is returned unchanged. The tolerance is used to control
where snapping is performed.
Where possible, this operation tries to avoid creating invalid geometries;
however, it does not guarantee that output geometries will be valid. It is the
responsibility of the caller to check for and handle invalid geometries.
Because too much snapping can result in invalid geometries being created,
heuristics are used to determine the number and location of snapped vertices
that are likely safe to snap. These heuristics may omit some potential snaps
that are otherwise within the tolerance.
The operation works in a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : GeoSeries or geometric object
The Geoseries (elementwise) or geometric object to snap to.
tolerance : float or array like
Maximum distance between vertices that shall be snapped
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
GeoSeries
Examples
--------
>>> from shapely import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Point(0.5, 2.5),
... LineString([(0.1, 0.1), (0.49, 0.51), (1.01, 0.89)]),
... Polygon([(0, 0), (0, 10), (10, 10), (10, 0), (0, 0)]),
... ],
... )
>>> s
0 POINT (0.5 2.5)
1 LINESTRING (0.1 0.1, 0.49 0.51, 1.01 0.89)
2 POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))
dtype: geometry
>>> s2 = geopandas.GeoSeries(
... [
... Point(0, 2),
... LineString([(0, 0), (0.5, 0.5), (1.0, 1.0)]),
... Point(8, 10),
... ],
... index=range(1, 4),
... )
>>> s2
1 POINT (0 2)
2 LINESTRING (0 0, 0.5 0.5, 1 1)
3 POINT (8 10)
dtype: geometry
We can snap each geometry to a single shapely geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> s.snap(Point(0, 2), tolerance=1)
0 POINT (0 2)
1 LINESTRING (0.1 0.1, 0.49 0.51, 1.01 0.89)
2 POLYGON ((0 0, 0 2, 0 10, 10 10, 10 0, 0 0))
dtype: geometry
We can also snap two GeoSeries to each other, row by row.
The GeoSeries above have different indices. We can either align both GeoSeries
based on index values and snap elements with the same index using
``align=True`` or ignore index and snap elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s.snap(s2, tolerance=1, align=True)
0 None
1 LINESTRING (0.1 0.1, 0.49 0.51, 1.01 0.89)
2 POLYGON ((0.5 0.5, 1 1, 0 10, 10 10, 10 0, 0.5...
3 None
dtype: geometry
>>> s.snap(s2, tolerance=1, align=False)
0 POINT (0 2)
1 LINESTRING (0 0, 0.5 0.5, 1 1)
2 POLYGON ((0 0, 0 10, 8 10, 10 10, 10 0, 0 0))
dtype: geometry
"""
return _binary_geo("snap", self, other, align, tolerance=tolerance)
def shared_paths(self, other, align=None):
"""
Returns the shared paths between two geometries.
Geometries within the GeoSeries should be only (Multi)LineStrings or
LinearRings. A GeoSeries of GeometryCollections is returned with two elements
in each GeometryCollection. The first element is a MultiLineString containing
shared paths with the same direction for both inputs. The second element is a
MultiLineString containing shared paths with the opposite direction for the two
inputs.
You can extract individual geometries of the resulting GeometryCollection using
the :meth:`GeoSeries.get_geometry` method.
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : Geoseries or geometric object
The Geoseries (elementwise) or geometric object to find the shared paths
with. Has to contain only (Multi)LineString or LinearRing geometry types.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
GeoSeries
Examples
--------
>>> from shapely.geometry import LineString, MultiLineString
>>> s = geopandas.GeoSeries(
... [
... LineString([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)]),
... LineString([(1, 0), (2, 0), (2, 1), (1, 1), (1, 0)]),
... MultiLineString([[(1, 0), (2, 0)], [(2, 1), (1, 1), (1, 0)]]),
... ],
... )
>>> s
0 LINESTRING (0 0, 1 0, 1 1, 0 1, 0 0)
1 LINESTRING (1 0, 2 0, 2 1, 1 1, 1 0)
2 MULTILINESTRING ((1 0, 2 0), (2 1, 1 1, 1 0))
dtype: geometry
We can find the shared paths between each geometry and a single shapely
geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> l = LineString([(1, 1), (0, 1)])
>>> s.shared_paths(l)
0 GEOMETRYCOLLECTION (MULTILINESTRING ((1 1, 0 1...
1 GEOMETRYCOLLECTION (MULTILINESTRING EMPTY, MUL...
2 GEOMETRYCOLLECTION (MULTILINESTRING EMPTY, MUL...
dtype: geometry
We can also check two GeoSeries against each other, row by row. The GeoSeries
above have different indices than the one below. We can either align both
GeoSeries based on index values and compare elements with the same index using
``align=True`` or ignore index and compare elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s2 = geopandas.GeoSeries(
... [
... LineString([(1, 1), (0, 1)]),
... LineString([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)]),
... LineString([(1, 0), (2, 0), (2, 1), (1, 1), (1, 0)]),
... ],
... index=[1, 2, 3]
... )
>>> s.shared_paths(s2, align=True)
0 None
1 GEOMETRYCOLLECTION (MULTILINESTRING EMPTY, MUL...
2 GEOMETRYCOLLECTION (MULTILINESTRING EMPTY, MUL...
3 None
dtype: geometry
>>>
>>> s.shared_paths(s2, align=False)
0 GEOMETRYCOLLECTION (MULTILINESTRING ((1 1, 0 1...
1 GEOMETRYCOLLECTION (MULTILINESTRING EMPTY, MUL...
2 GEOMETRYCOLLECTION (MULTILINESTRING ((1 0, 2 0...
dtype: geometry
See Also
--------
GeoSeries.get_geometry
"""
return _binary_geo("shared_paths", self, other, align)
#
# Other operations
#
@property
def bounds(self):
"""Returns a ``DataFrame`` with columns ``minx``, ``miny``, ``maxx``,
``maxy`` values containing the bounds for each geometry.
See ``GeoSeries.total_bounds`` for the limits of the entire series.
Examples
--------
>>> from shapely.geometry import Point, Polygon, LineString
>>> d = {'geometry': [Point(2, 1), Polygon([(0, 0), (1, 1), (1, 0)]),
... LineString([(0, 1), (1, 2)])]}
>>> gdf = geopandas.GeoDataFrame(d, crs="EPSG:4326")
>>> gdf.bounds
minx miny maxx maxy
0 2.0 1.0 2.0 1.0
1 0.0 0.0 1.0 1.0
2 0.0 1.0 1.0 2.0
You can assign the bounds to the ``GeoDataFrame`` as:
>>> import pandas as pd
>>> gdf = pd.concat([gdf, gdf.bounds], axis=1)
>>> gdf
geometry minx miny maxx maxy
0 POINT (2 1) 2.0 1.0 2.0 1.0
1 POLYGON ((0 0, 1 1, 1 0, 0 0)) 0.0 0.0 1.0 1.0
2 LINESTRING (0 1, 1 2) 0.0 1.0 1.0 2.0
"""
bounds = GeometryArray(self.geometry.values).bounds
return DataFrame(
bounds, columns=["minx", "miny", "maxx", "maxy"], index=self.index
)
@property
def total_bounds(self):
"""Returns a tuple containing ``minx``, ``miny``, ``maxx``, ``maxy``
values for the bounds of the series as a whole.
See ``GeoSeries.bounds`` for the bounds of the geometries contained in
the series.
Examples
--------
>>> from shapely.geometry import Point, Polygon, LineString
>>> d = {'geometry': [Point(3, -1), Polygon([(0, 0), (1, 1), (1, 0)]),
... LineString([(0, 1), (1, 2)])]}
>>> gdf = geopandas.GeoDataFrame(d, crs="EPSG:4326")
>>> gdf.total_bounds
array([ 0., -1., 3., 2.])
"""
return GeometryArray(self.geometry.values).total_bounds
@property
def sindex(self):
"""Generate the spatial index
Creates R-tree spatial index based on ``shapely.STRtree``.
Note that the spatial index may not be fully
initialized until the first use.
Examples
--------
>>> from shapely.geometry import box
>>> s = geopandas.GeoSeries(geopandas.points_from_xy(range(5), range(5)))
>>> s
0 POINT (0 0)
1 POINT (1 1)
2 POINT (2 2)
3 POINT (3 3)
4 POINT (4 4)
dtype: geometry
Query the spatial index with a single geometry based on the bounding box:
>>> s.sindex.query(box(1, 1, 3, 3))
array([1, 2, 3])
Query the spatial index with a single geometry based on the predicate:
>>> s.sindex.query(box(1, 1, 3, 3), predicate="contains")
array([2])
Query the spatial index with an array of geometries based on the bounding
box:
>>> s2 = geopandas.GeoSeries([box(1, 1, 3, 3), box(4, 4, 5, 5)])
>>> s2
0 POLYGON ((3 1, 3 3, 1 3, 1 1, 3 1))
1 POLYGON ((5 4, 5 5, 4 5, 4 4, 5 4))
dtype: geometry
>>> s.sindex.query(s2)
array([[0, 0, 0, 1],
[1, 2, 3, 4]])
Query the spatial index with an array of geometries based on the predicate:
>>> s.sindex.query(s2, predicate="contains")
array([[0],
[2]])
"""
return self.geometry.values.sindex
@property
def has_sindex(self):
"""Check the existence of the spatial index without generating it.
Use the `.sindex` attribute on a GeoDataFrame or GeoSeries
to generate a spatial index if it does not yet exist,
which may take considerable time based on the underlying index
implementation.
Note that the underlying spatial index may not be fully
initialized until the first use.
Examples
--------
>>> from shapely.geometry import Point
>>> d = {'geometry': [Point(1, 2), Point(2, 1)]}
>>> gdf = geopandas.GeoDataFrame(d)
>>> gdf.has_sindex
False
>>> index = gdf.sindex
>>> gdf.has_sindex
True
Returns
-------
bool
`True` if the spatial index has been generated or
`False` if not.
"""
return self.geometry.values.has_sindex
def buffer(
self,
distance,
resolution=16,
cap_style="round",
join_style="round",
mitre_limit=5.0,
single_sided=False,
**kwargs,
):
"""Returns a ``GeoSeries`` of geometries representing all points within
a given ``distance`` of each geometric object.
Computes the buffer of a geometry for positive and negative buffer distance.
The buffer of a geometry is defined as the Minkowski sum (or difference, for
negative distance) of the geometry with a circle with radius equal to the
absolute value of the buffer distance.
The buffer operation always returns a polygonal result. The negative or
zero-distance buffer of lines and points is always empty.
Parameters
----------
distance : float, np.array, pd.Series
The radius of the buffer in the Minkowski sum (or difference). If np.array
or pd.Series are used then it must have same length as the GeoSeries.
resolution : int (optional, default 16)
The resolution of the buffer around each vertex. Specifies the number of
linear segments in a quarter circle in the approximation of circular arcs.
cap_style : {'round', 'square', 'flat'}, default 'round'
Specifies the shape of buffered line endings. ``'round'`` results in
circular line endings (see ``resolution``). Both ``'square'`` and ``'flat'``
result in rectangular line endings, ``'flat'`` will end at the original
vertex, while ``'square'`` involves adding the buffer width.
join_style : {'round', 'mitre', 'bevel'}, default 'round'
Specifies the shape of buffered line midpoints. ``'round'`` results in
rounded shapes. ``'bevel'`` results in a beveled edge that touches the
original vertex. ``'mitre'`` results in a single vertex that is beveled
depending on the ``mitre_limit`` parameter.
mitre_limit : float, default 5.0
Crops of ``'mitre'``-style joins if the point is displaced from the
buffered vertex by more than this limit.
single_sided : bool, default False
Only buffer at one side of the geometry.
Examples
--------
>>> from shapely.geometry import Point, LineString, Polygon
>>> s = geopandas.GeoSeries(
... [
... Point(0, 0),
... LineString([(1, -1), (1, 0), (2, 0), (2, 1)]),
... Polygon([(3, -1), (4, 0), (3, 1)]),
... ]
... )
>>> s
0 POINT (0 0)
1 LINESTRING (1 -1, 1 0, 2 0, 2 1)
2 POLYGON ((3 -1, 4 0, 3 1, 3 -1))
dtype: geometry
>>> s.buffer(0.2)
0 POLYGON ((0.2 0, 0.19904 -0.0196, 0.19616 -0.0...
1 POLYGON ((0.8 0, 0.80096 0.0196, 0.80384 0.039...
2 POLYGON ((2.8 -1, 2.8 1, 2.80096 1.0196, 2.803...
dtype: geometry
``Further specification as ``join_style`` and ``cap_style`` are shown in the
following illustration:
.. plot:: _static/code/buffer.py
"""
return _delegate_geo_method(
"buffer",
self,
distance=distance,
resolution=resolution,
cap_style=cap_style,
join_style=join_style,
mitre_limit=mitre_limit,
single_sided=single_sided,
**kwargs,
)
def simplify(self, tolerance, preserve_topology=True):
"""Returns a ``GeoSeries`` containing a simplified representation of
each geometry.
The algorithm (Douglas-Peucker) recursively splits the original line
into smaller parts and connects these parts' endpoints
by a straight line. Then, it removes all points whose distance
to the straight line is smaller than `tolerance`. It does not
move any points and it always preserves endpoints of
the original line or polygon.
See http://shapely.readthedocs.io/en/latest/manual.html#object.simplify
for details
Parameters
----------
tolerance : float
All parts of a simplified geometry will be no more than
`tolerance` distance from the original. It has the same units
as the coordinate reference system of the GeoSeries.
For example, using `tolerance=100` in a projected CRS with meters
as units means a distance of 100 meters in reality.
preserve_topology: bool (default True)
False uses a quicker algorithm, but may produce self-intersecting
or otherwise invalid geometries.
Notes
-----
Invalid geometric objects may result from simplification that does not
preserve topology and simplification may be sensitive to the order of
coordinates: two geometries differing only in order of coordinates may be
simplified differently.
Examples
--------
>>> from shapely.geometry import Point, LineString
>>> s = geopandas.GeoSeries(
... [Point(0, 0).buffer(1), LineString([(0, 0), (1, 10), (0, 20)])]
... )
>>> s
0 POLYGON ((1 0, 0.99518 -0.09802, 0.98079 -0.19...
1 LINESTRING (0 0, 1 10, 0 20)
dtype: geometry
>>> s.simplify(1)
0 POLYGON ((0 1, 0 -1, -1 0, 0 1))
1 LINESTRING (0 0, 0 20)
dtype: geometry
"""
return _delegate_geo_method(
"simplify", self, tolerance=tolerance, preserve_topology=preserve_topology
)
def relate(self, other, align=None):
"""
Returns the DE-9IM intersection matrices for the geometries
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : BaseGeometry or GeoSeries
The other geometry to computed
the DE-9IM intersection matrices from.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
spatial_relations: Series of strings
The DE-9IM intersection matrices which describe
the spatial relations of the other geometry.
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (2, 2), (0, 2)]),
... Polygon([(0, 0), (2, 2), (0, 2)]),
... LineString([(0, 0), (2, 2)]),
... LineString([(2, 0), (0, 2)]),
... Point(0, 1),
... ],
... )
>>> s2 = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... LineString([(1, 0), (1, 3)]),
... LineString([(2, 0), (0, 2)]),
... Point(1, 1),
... Point(0, 1),
... ],
... index=range(1, 6),
... )
>>> s
0 POLYGON ((0 0, 2 2, 0 2, 0 0))
1 POLYGON ((0 0, 2 2, 0 2, 0 0))
2 LINESTRING (0 0, 2 2)
3 LINESTRING (2 0, 0 2)
4 POINT (0 1)
dtype: geometry
>>> s2
1 POLYGON ((0 0, 1 1, 0 1, 0 0))
2 LINESTRING (1 0, 1 3)
3 LINESTRING (2 0, 0 2)
4 POINT (1 1)
5 POINT (0 1)
dtype: geometry
We can relate each geometry and a single
shapely geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> s.relate(Polygon([(0, 0), (1, 1), (0, 1)]))
0 212F11FF2
1 212F11FF2
2 F11F00212
3 F01FF0212
4 F0FFFF212
dtype: object
We can also check two GeoSeries against each other, row by row.
The GeoSeries above have different indices. We can either align both GeoSeries
based on index values and compare elements with the same index using
``align=True`` or ignore index and compare elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s.relate(s2, align=True)
0 None
1 212F11FF2
2 0F1FF0102
3 1FFF0FFF2
4 FF0FFF0F2
5 None
dtype: object
>>> s.relate(s2, align=False)
0 212F11FF2
1 1F20F1102
2 0F1FF0102
3 0F1FF0FF2
4 0FFFFFFF2
dtype: object
"""
return _binary_op("relate", self, other, align)
def relate_pattern(self, other, pattern, align=None):
"""
Returns True if the DE-9IM string code for the relationship between
the geometries satisfies the pattern, else False.
This function compares the DE-9IM code string for two geometries
against a specified pattern. If the string matches the pattern then
``True`` is returned, otherwise ``False``. The pattern specified can
be an exact match (``0``, ``1`` or ``2``), a boolean match
(uppercase ``T`` or ``F``), or a wildcard (``*``). For example,
the pattern for the ``within`` predicate is ``'T*F**F***'``
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
Parameters
----------
other : BaseGeometry or GeoSeries
The other geometry to be tested agains the pattern.
pattern : str
The DE-9IM pattern to test against.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
Series
Examples
--------
>>> from shapely.geometry import Polygon, LineString, Point
>>> s = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (2, 2), (0, 2)]),
... Polygon([(0, 0), (2, 2), (0, 2)]),
... LineString([(0, 0), (2, 2)]),
... LineString([(2, 0), (0, 2)]),
... Point(0, 1),
... ],
... )
>>> s2 = geopandas.GeoSeries(
... [
... Polygon([(0, 0), (1, 1), (0, 1)]),
... LineString([(1, 0), (1, 3)]),
... LineString([(2, 0), (0, 2)]),
... Point(1, 1),
... Point(0, 1),
... ],
... index=range(1, 6),
... )
>>> s
0 POLYGON ((0 0, 2 2, 0 2, 0 0))
1 POLYGON ((0 0, 2 2, 0 2, 0 0))
2 LINESTRING (0 0, 2 2)
3 LINESTRING (2 0, 0 2)
4 POINT (0 1)
dtype: geometry
>>> s2
1 POLYGON ((0 0, 1 1, 0 1, 0 0))
2 LINESTRING (1 0, 1 3)
3 LINESTRING (2 0, 0 2)
4 POINT (1 1)
5 POINT (0 1)
dtype: geometry
We can check the relate pattern of each geometry and a single
shapely geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> s.relate_pattern(Polygon([(0, 0), (1, 1), (0, 1)]), "2*T***F**")
0 True
1 True
2 False
3 False
4 False
dtype: bool
We can also check two GeoSeries against each other, row by row.
The GeoSeries above have different indices. We can either align both GeoSeries
based on index values and compare elements with the same index using
``align=True`` or ignore index and compare elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s.relate_pattern(s2, "TF******T", align=True)
0 False
1 False
2 True
3 True
4 False
5 False
dtype: bool
>>> s.relate_pattern(s2, "TF******T", align=False)
0 False
1 True
2 True
3 True
4 True
dtype: bool
"""
return _binary_op("relate_pattern", self, other, pattern=pattern, align=align)
def project(self, other, normalized=False, align=None):
"""
Return the distance along each geometry nearest to *other*
The operation works on a 1-to-1 row-wise manner:
.. image:: ../../../_static/binary_op-01.svg
:align: center
The project method is the inverse of interpolate.
In shapely, this is equal to ``line_locate_point``.
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.
align : bool | None (default None)
If True, automatically aligns GeoSeries based on their indices.
If False, the order of elements is preserved. None defaults to True.
Returns
-------
Series
Examples
--------
>>> from shapely.geometry import LineString, Point
>>> s = geopandas.GeoSeries(
... [
... LineString([(0, 0), (2, 0), (0, 2)]),
... LineString([(0, 0), (2, 2)]),
... LineString([(2, 0), (0, 2)]),
... ],
... )
>>> s2 = geopandas.GeoSeries(
... [
... Point(1, 0),
... Point(1, 0),
... Point(2, 1),
... ],
... index=range(1, 4),
... )
>>> s
0 LINESTRING (0 0, 2 0, 0 2)
1 LINESTRING (0 0, 2 2)
2 LINESTRING (2 0, 0 2)
dtype: geometry
>>> s2
1 POINT (1 0)
2 POINT (1 0)
3 POINT (2 1)
dtype: geometry
We can project each geometry on a single
shapely geometry:
.. image:: ../../../_static/binary_op-03.svg
:align: center
>>> s.project(Point(1, 0))
0 1.000000
1 0.707107
2 0.707107
dtype: float64
We can also check two GeoSeries against each other, row by row.
The GeoSeries above have different indices. We can either align both GeoSeries
based on index values and project elements with the same index using
``align=True`` or ignore index and project elements based on their matching
order using ``align=False``:
.. image:: ../../../_static/binary_op-02.svg
>>> s.project(s2, align=True)
0 NaN
1 0.707107
2 0.707107
3 NaN
dtype: float64
>>> s.project(s2, align=False)
0 1.000000
1 0.707107
2 0.707107
dtype: float64
See also
--------
GeoSeries.interpolate
"""
return _binary_op("project", self, other, normalized=normalized, align=align)
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. If np.array or pd.Series are used then it must have
same length as the GeoSeries.
normalized : boolean
If normalized is True, distance will be interpreted as a fraction
of the geometric object's length.
Examples
--------
>>> from shapely.geometry import LineString, Point
>>> s = geopandas.GeoSeries(
... [
... LineString([(0, 0), (2, 0), (0, 2)]),
... LineString([(0, 0), (2, 2)]),
... LineString([(2, 0), (0, 2)]),
... ],
... )
>>> s
0 LINESTRING (0 0, 2 0, 0 2)
1 LINESTRING (0 0, 2 2)
2 LINESTRING (2 0, 0 2)
dtype: geometry
>>> s.interpolate(1)
0 POINT (1 0)
1 POINT (0.70711 0.70711)
2 POINT (1.29289 0.70711)
dtype: geometry
>>> s.interpolate([1, 2, 3])
0 POINT (1 0)
1 POINT (1.41421 1.41421)
2 POINT (0 2)
dtype: geometry
"""
return _delegate_geo_method(
"interpolate", self, distance=distance, normalized=normalized
)
def affine_transform(self, matrix):
"""Return a ``GeoSeries`` with translated geometries.
See http://shapely.readthedocs.io/en/stable/manual.html#shapely.affinity.affine_transform
for details.
Parameters
----------
matrix: List or tuple
6 or 12 items for 2D or 3D transformations respectively.
For 2D affine transformations,
the 6 parameter matrix is ``[a, b, d, e, xoff, yoff]``
For 3D affine transformations,
the 12 parameter matrix is ``[a, b, c, d, e, f, g, h, i, xoff, yoff, zoff]``
Examples
--------
>>> from shapely.geometry import Point, LineString, Polygon
>>> s = geopandas.GeoSeries(
... [
... Point(1, 1),
... LineString([(1, -1), (1, 0)]),
... Polygon([(3, -1), (4, 0), (3, 1)]),
... ]
... )
>>> s
0 POINT (1 1)
1 LINESTRING (1 -1, 1 0)
2 POLYGON ((3 -1, 4 0, 3 1, 3 -1))
dtype: geometry
>>> s.affine_transform([2, 3, 2, 4, 5, 2])
0 POINT (10 8)
1 LINESTRING (4 0, 7 4)
2 POLYGON ((8 4, 13 10, 14 12, 8 4))
dtype: geometry
""" # (E501 link is longer than max line length)
return _delegate_geo_method("affine_transform", self, matrix=matrix)
def translate(self, xoff=0.0, yoff=0.0, zoff=0.0):
"""Returns a ``GeoSeries`` with translated geometries.
See http://shapely.readthedocs.io/en/latest/manual.html#shapely.affinity.translate
for details.
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.
Examples
--------
>>> from shapely.geometry import Point, LineString, Polygon
>>> s = geopandas.GeoSeries(
... [
... Point(1, 1),
... LineString([(1, -1), (1, 0)]),
... Polygon([(3, -1), (4, 0), (3, 1)]),
... ]
... )
>>> s
0 POINT (1 1)
1 LINESTRING (1 -1, 1 0)
2 POLYGON ((3 -1, 4 0, 3 1, 3 -1))
dtype: geometry
>>> s.translate(2, 3)
0 POINT (3 4)
1 LINESTRING (3 2, 3 3)
2 POLYGON ((5 2, 6 3, 5 4, 5 2))
dtype: geometry
""" # (E501 link is longer than max line length)
return _delegate_geo_method("translate", self, xoff=xoff, yoff=yoff, zoff=zoff)
def rotate(self, angle, origin="center", use_radians=False):
"""Returns a ``GeoSeries`` with rotated geometries.
See http://shapely.readthedocs.io/en/latest/manual.html#shapely.affinity.rotate
for details.
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
Examples
--------
>>> from shapely.geometry import Point, LineString, Polygon
>>> s = geopandas.GeoSeries(
... [
... Point(1, 1),
... LineString([(1, -1), (1, 0)]),
... Polygon([(3, -1), (4, 0), (3, 1)]),
... ]
... )
>>> s
0 POINT (1 1)
1 LINESTRING (1 -1, 1 0)
2 POLYGON ((3 -1, 4 0, 3 1, 3 -1))
dtype: geometry
>>> s.rotate(90)
0 POINT (1 1)
1 LINESTRING (1.5 -0.5, 0.5 -0.5)
2 POLYGON ((4.5 -0.5, 3.5 0.5, 2.5 -0.5, 4.5 -0.5))
dtype: geometry
>>> s.rotate(90, origin=(0, 0))
0 POINT (-1 1)
1 LINESTRING (1 1, 0 1)
2 POLYGON ((1 3, 0 4, -1 3, 1 3))
dtype: geometry
"""
return _delegate_geo_method(
"rotate", self, angle=angle, origin=origin, use_radians=use_radians
)
def scale(self, xfact=1.0, yfact=1.0, zfact=1.0, origin="center"):
"""Returns a ``GeoSeries`` with scaled geometries.
The geometries can be scaled by different factors along each
dimension. Negative scale factors will mirror or reflect coordinates.
See http://shapely.readthedocs.io/en/latest/manual.html#shapely.affinity.scale
for details.
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).
Examples
--------
>>> from shapely.geometry import Point, LineString, Polygon
>>> s = geopandas.GeoSeries(
... [
... Point(1, 1),
... LineString([(1, -1), (1, 0)]),
... Polygon([(3, -1), (4, 0), (3, 1)]),
... ]
... )
>>> s
0 POINT (1 1)
1 LINESTRING (1 -1, 1 0)
2 POLYGON ((3 -1, 4 0, 3 1, 3 -1))
dtype: geometry
>>> s.scale(2, 3)
0 POINT (1 1)
1 LINESTRING (1 -2, 1 1)
2 POLYGON ((2.5 -3, 4.5 0, 2.5 3, 2.5 -3))
dtype: geometry
>>> s.scale(2, 3, origin=(0, 0))
0 POINT (2 3)
1 LINESTRING (2 -3, 2 0)
2 POLYGON ((6 -3, 8 0, 6 3, 6 -3))
dtype: geometry
"""
return _delegate_geo_method(
"scale", self, xfact=xfact, yfact=yfact, zfact=zfact, origin=origin
)
def skew(self, xs=0.0, ys=0.0, origin="center", use_radians=False):
"""Returns a ``GeoSeries`` with skewed geometries.
The geometries are sheared by angles along the x and y dimensions.
See http://shapely.readthedocs.io/en/latest/manual.html#shapely.affinity.skew
for details.
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
Examples
--------
>>> from shapely.geometry import Point, LineString, Polygon
>>> s = geopandas.GeoSeries(
... [
... Point(1, 1),
... LineString([(1, -1), (1, 0)]),
... Polygon([(3, -1), (4, 0), (3, 1)]),
... ]
... )
>>> s
0 POINT (1 1)
1 LINESTRING (1 -1, 1 0)
2 POLYGON ((3 -1, 4 0, 3 1, 3 -1))
dtype: geometry
>>> s.skew(45, 30)
0 POINT (1 1)
1 LINESTRING (0.5 -1, 1.5 0)
2 POLYGON ((2 -1.28868, 4 0.28868, 4 0.71132, 2 ...
dtype: geometry
>>> s.skew(45, 30, origin=(0, 0))
0 POINT (2 1.57735)
1 LINESTRING (0 -0.42265, 1 0.57735)
2 POLYGON ((2 0.73205, 4 2.3094, 4 2.73205, 2 0....
dtype: geometry
"""
return _delegate_geo_method(
"skew", self, xs=xs, ys=ys, origin=origin, use_radians=use_radians
)
@property
def cx(self):
"""
Coordinate based indexer to select by intersection with bounding box.
Format of input should be ``.cx[xmin:xmax, ymin:ymax]``. Any of
``xmin``, ``xmax``, ``ymin``, and ``ymax`` can be provided, but input
must include a comma separating x and y slices. That is, ``.cx[:, :]``
will return the full series/frame, but ``.cx[:]`` is not implemented.
Examples
--------
>>> from shapely.geometry import LineString, Point
>>> s = geopandas.GeoSeries(
... [Point(0, 0), Point(1, 2), Point(3, 3), LineString([(0, 0), (3, 3)])]
... )
>>> s
0 POINT (0 0)
1 POINT (1 2)
2 POINT (3 3)
3 LINESTRING (0 0, 3 3)
dtype: geometry
>>> s.cx[0:1, 0:1]
0 POINT (0 0)
3 LINESTRING (0 0, 3 3)
dtype: geometry
>>> s.cx[:, 1:]
1 POINT (1 2)
2 POINT (3 3)
3 LINESTRING (0 0, 3 3)
dtype: geometry
"""
return _CoordinateIndexer(self)
def get_coordinates(self, include_z=False, ignore_index=False, index_parts=False):
"""Gets coordinates from a :class:`GeoSeries` as a :class:`~pandas.DataFrame` of
floats.
The shape of the returned :class:`~pandas.DataFrame` is (N, 2), with N being the
number of coordinate pairs. With the default of ``include_z=False``,
three-dimensional data is ignored. When specifying ``include_z=True``, the shape
of the returned :class:`~pandas.DataFrame` is (N, 3).
Parameters
----------
include_z : bool, default False
Include Z coordinates
ignore_index : bool, default False
If True, the resulting index will be labelled 0, 1, …, n - 1, ignoring
``index_parts``.
index_parts : bool, default False
If True, the resulting index will be a :class:`~pandas.MultiIndex` (original
index with an additional level indicating the ordering of the coordinate
pairs: a new zero-based index for each geometry in the original GeoSeries).
Returns
-------
pandas.DataFrame
Examples
--------
>>> from shapely.geometry import Point, LineString, Polygon
>>> s = geopandas.GeoSeries(
... [
... Point(1, 1),
... LineString([(1, -1), (1, 0)]),
... Polygon([(3, -1), (4, 0), (3, 1)]),
... ]
... )
>>> s
0 POINT (1 1)
1 LINESTRING (1 -1, 1 0)
2 POLYGON ((3 -1, 4 0, 3 1, 3 -1))
dtype: geometry
>>> s.get_coordinates()
x y
0 1.0 1.0
1 1.0 -1.0
1 1.0 0.0
2 3.0 -1.0
2 4.0 0.0
2 3.0 1.0
2 3.0 -1.0
>>> s.get_coordinates(ignore_index=True)
x y
0 1.0 1.0
1 1.0 -1.0
2 1.0 0.0
3 3.0 -1.0
4 4.0 0.0
5 3.0 1.0
6 3.0 -1.0
>>> s.get_coordinates(index_parts=True)
x y
0 0 1.0 1.0
1 0 1.0 -1.0
1 1.0 0.0
2 0 3.0 -1.0
1 4.0 0.0
2 3.0 1.0
3 3.0 -1.0
"""
coords, outer_idx = shapely.get_coordinates(
self.geometry.values._data, include_z=include_z, return_index=True
)
column_names = ["x", "y"]
if include_z:
column_names.append("z")
index = _get_index_for_parts(
self.index,
outer_idx,
ignore_index=ignore_index,
index_parts=index_parts,
)
return pd.DataFrame(coords, index=index, columns=column_names)
def hilbert_distance(self, total_bounds=None, level=16):
"""
Calculate the distance along a Hilbert curve.
The distances are calculated for the midpoints of the geometries in the
GeoDataFrame, and using the total bounds of the GeoDataFrame.
The Hilbert distance can be used to spatially sort GeoPandas
objects, by mapping two dimensional geometries along the Hilbert curve.
Parameters
----------
total_bounds : 4-element array, optional
The spatial extent in which the curve is constructed (used to
rescale the geometry midpoints). By default, the total bounds
of the full GeoDataFrame or GeoSeries will be computed. If known,
you can pass the total bounds to avoid this extra computation.
level : int (1 - 16), default 16
Determines the precision of the curve (points on the curve will
have coordinates in the range [0, 2^level - 1]).
Returns
-------
Series
Series containing distance along the curve for geometry
"""
from geopandas.tools.hilbert_curve import _hilbert_distance
distances = _hilbert_distance(
self.geometry.values, total_bounds=total_bounds, level=level
)
return pd.Series(distances, index=self.index, name="hilbert_distance")
def sample_points(self, size, method="uniform", seed=None, rng=None, **kwargs):
"""
Sample points from each geometry.
Generate a MultiPoint per each geometry containing points sampled from the
geometry. You can either sample randomly from a uniform distribution or use an
advanced sampling algorithm from the ``pointpats`` package.
For polygons, this samples within the area of the polygon. For lines,
this samples along the length of the linestring. For multi-part
geometries, the weights of each part are selected according to their relevant
attribute (area for Polygons, length for LineStrings), and then points are
sampled from each part.
Any other geometry type (e.g. Point, GeometryCollection) is ignored, and an
empty MultiPoint geometry is returned.
Parameters
----------
size : int | array-like
The size of the sample requested. Indicates the number of samples to draw
from each geometry. If an array of the same length as a GeoSeries is
passed, it denotes the size of a sample per geometry.
method : str, default "uniform"
The sampling method. ``uniform`` samples uniformly at random from a
geometry using ``numpy.random.uniform``. Other allowed strings
(e.g. ``"cluster_poisson"``) denote sampling function name from the
``pointpats.random`` module (see
http://pysal.org/pointpats/api.html#random-distributions). Pointpats methods
are implemented for (Multi)Polygons only and will return an empty MultiPoint
for other geometry types.
rng : {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional
A random generator or seed to initialize the numpy BitGenerator. If None, then fresh,
unpredictable entropy will be pulled from the OS.
**kwargs : dict
Options for the pointpats sampling algorithms.
Returns
-------
GeoSeries
Points sampled within (or along) each geometry.
Examples
--------
>>> from shapely.geometry import Polygon
>>> s = geopandas.GeoSeries(
... [
... Polygon([(1, -1), (1, 0), (0, 0)]),
... Polygon([(3, -1), (4, 0), (3, 1)]),
... ]
... )
>>> s.sample_points(size=10) # doctest: +SKIP
0 MULTIPOINT ((0.1045 -0.10294), (0.35249 -0.264...
1 MULTIPOINT ((3.03261 -0.43069), (3.10068 0.114...
Name: sampled_points, dtype: geometry
""" # noqa: E501
from .geoseries import GeoSeries
from .tools._random import uniform
if seed is not None:
warn(
"The 'seed' keyword is deprecated. Use 'rng' instead.",
FutureWarning,
stacklevel=2,
)
rng = seed
if method == "uniform":
if pd.api.types.is_list_like(size):
result = [uniform(geom, s, rng) for geom, s in zip(self.geometry, size)]
else:
result = self.geometry.apply(uniform, size=size, rng=rng)
else:
pointpats = compat.import_optional_dependency(
"pointpats",
f"For complex sampling methods, the pointpats module is required. "
f"Your requested method, '{method}' was not a supported option "
f"and the pointpats package was not able to be imported.",
)
if not hasattr(pointpats.random, method):
raise AttributeError(
f"pointpats.random module has no sampling method {method}."
f"Consult the pointpats.random module documentation for"
f" available random sampling methods."
)
sample_function = getattr(pointpats.random, method)
result = self.geometry.apply(
lambda x: (
points_from_xy(
*sample_function(x, size=size, **kwargs).T
).union_all()
if not (x.is_empty or x is None or "Polygon" not in x.geom_type)
else MultiPoint()
),
)
return GeoSeries(result, name="sampled_points", crs=self.crs, index=self.index)
def build_area(self, node=True):
"""Creates an areal geometry formed by the constituent linework.
Builds areas from the GeoSeries that contain linework which represents the edges
of a planar graph. Any geometry type may be provided as input; only the
constituent lines and rings will be used to create the output polygons. All
geometries within the GeoSeries are considered together and the resulting
polygons therefore do not map 1:1 to input geometries.
This function converts inner rings into holes. To turn inner rings into polygons
as well, use polygonize.
Unless you know that the input GeoSeries represents a planar graph with a clean
topology (e.g. there is a node on both lines where they intersect), it is
recommended to use ``node=True`` which performs noding prior to building areal
geometry. Using ``node=False`` will provide performance benefits but may result
in incorrect polygons if the input is not of the proper topology.
If the input linework crosses, this function may produce invalid polygons. Use
:meth:`GeoSeries.make_valid` to ensure valid geometries.
Parameters
----------
node : bool, default True
Perform noding prior to building the areas, by default True.
Returns
-------
GeoSeries
GeoSeries with polygons
Examples
--------
>>> from shapely.geometry import LineString, Polygon
>>> s = geopandas.GeoSeries([
... LineString([(18, 4), (4, 2), (2, 9)]),
... LineString([(18, 4), (16, 16)]),
... LineString([(16, 16), (8, 19), (8, 12), (2, 9)]),
... LineString([(8, 6), (12, 13), (15, 8)]),
... LineString([(8, 6), (15, 8)]),
... LineString([(0, 0), (0, 3), (3, 3), (3, 0), (0, 0)]),
... Polygon([(1, 1), (2, 2), (1, 2), (1, 1)]),
... ])
>>> s.build_area()
0 POLYGON ((0 3, 3 3, 3 0, 0 0, 0 3), (1 1, 2 2,...
1 POLYGON ((4 2, 2 9, 8 12, 8 19, 16 16, 18 4, 4...
Name: polygons, dtype: geometry
"""
from .geoseries import GeoSeries
if node:
geometry_input = self.geometry.union_all()
else:
geometry_input = shapely.geometrycollections(self.geometry.values._data)
polygons = shapely.build_area(geometry_input)
return GeoSeries(polygons, crs=self.crs, name="polygons").explode(
ignore_index=True
)
def polygonize(self, node=True, full=False):
"""Creates polygons formed from the linework of a GeoSeries.
Polygonizes the GeoSeries that contain linework which represents the
edges of a planar graph. Any geometry type may be provided as input; only the
constituent lines and rings will be used to create the output polygons.
Lines or rings that when combined do not completely close a polygon will be
ignored. Duplicate segments are ignored.
Unless you know that the input GeoSeries represents a planar graph with a clean
topology (e.g. there is a node on both lines where they intersect), it is
recommended to use ``node=True`` which performs noding prior to polygonization.
Using ``node=False`` will provide performance benefits but may result in
incorrect polygons if the input is not of the proper topology.
When ``full=True``, the return value is a 4-tuple containing output polygons,
along with lines which could not be converted to polygons. The return value
consists of 4 elements or varying lenghts:
- GeoSeries of the valid polygons (same as with ``full=False``)
- GeoSeries of cut edges: edges connected on both ends but not part of
polygonal output
- GeoSeries of dangles: edges connected on one end but not part of polygonal
output
- GeoSeries of invalid rings: polygons that are formed but are not valid
(bowties, etc)
Parameters
----------
node : bool, default True
Perform noding prior to polygonization, by default True.
full : bool, default False
Return the full output composed of a tuple of GeoSeries, by default False.
Returns
-------
GeoSeries | tuple(GeoSeries, GeoSeries, GeoSeries, GeoSeries)
GeoSeries with the polygons or a tuple of four GeoSeries as
``(polygons, cuts, dangles, invalid)``
Examples
--------
>>> from shapely.geometry import LineString
>>> s = geopandas.GeoSeries([
... LineString([(0, 0), (1, 1)]),
... LineString([(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)]),
... LineString([(0.5, 0.2), (0.5, 0.8)]),
... ])
>>> s.polygonize()
0 POLYGON ((0 0, 0.5 0.5, 1 1, 1 0, 0 0))
1 POLYGON ((0.5 0.5, 0 0, 0 1, 1 1, 0.5 0.5))
Name: polygons, dtype: geometry
>>> polygons, cuts, dangles, invalid = s.polygonize(full=True)
"""
from .geoseries import GeoSeries
if node:
geometry_input = [self.geometry.union_all()]
else:
geometry_input = self.geometry.values
if full:
polygons, cuts, dangles, invalid = shapely.polygonize_full(geometry_input)
cuts = GeoSeries(cuts, crs=self.crs, name="cut_edges").explode(
ignore_index=True
)
dangles = GeoSeries(dangles, crs=self.crs, name="dangles").explode(
ignore_index=True
)
invalid = GeoSeries(invalid, crs=self.crs, name="invalid_rings").explode(
ignore_index=True
)
polygons = GeoSeries(polygons, crs=self.crs, name="polygons").explode(
ignore_index=True
)
return (polygons, cuts, dangles, invalid)
polygons = shapely.polygonize(geometry_input)
return GeoSeries(polygons, crs=self.crs, name="polygons").explode(
ignore_index=True
)
def _get_index_for_parts(orig_idx, outer_idx, ignore_index, index_parts):
"""Helper to handle index when geometries get exploded to parts.
Used in get_coordinates and explode.
Parameters
----------
orig_idx : pandas.Index
original index
outer_idx : array
the index of each returned geometry as a separate ndarray of integers
ignore_index : bool
index_parts : bool
Returns
-------
pandas.Index
index or multiindex
"""
if ignore_index:
return None
else:
if len(outer_idx):
# Generate inner index as a range per value of outer_idx
# 1. identify the start of each run of values in outer_idx
# 2. count number of values per run
# 3. use cumulative sums to create an incremental range
# starting at 0 in each run
run_start = np.r_[True, outer_idx[:-1] != outer_idx[1:]]
counts = np.diff(np.r_[np.nonzero(run_start)[0], len(outer_idx)])
inner_index = (~run_start).cumsum(dtype=outer_idx.dtype)
inner_index -= np.repeat(inner_index[run_start], counts)
else:
inner_index = []
# extract original index values based on integer index
outer_index = orig_idx.take(outer_idx)
if index_parts:
nlevels = outer_index.nlevels
index_arrays = [outer_index.get_level_values(lvl) for lvl in range(nlevels)]
index_arrays.append(inner_index)
index = pd.MultiIndex.from_arrays(
index_arrays, names=list(orig_idx.names) + [None]
)
else:
index = outer_index
return index
class _CoordinateIndexer(object):
# see docstring GeoPandasBase.cx property above
def __init__(self, obj):
self.obj = obj
def __getitem__(self, key):
obj = self.obj
xs, ys = key
# handle numeric values as x and/or y coordinate index
if type(xs) is not slice:
xs = slice(xs, xs)
if type(ys) is not slice:
ys = slice(ys, ys)
# don't know how to handle step; should this raise?
if xs.step is not None or ys.step is not None:
warn(
"Ignoring step - full interval is used.",
stacklevel=2,
)
if xs.start is None or xs.stop is None or ys.start is None or ys.stop is None:
xmin, ymin, xmax, ymax = obj.total_bounds
bbox = box(
xs.start if xs.start is not None else xmin,
ys.start if ys.start is not None else ymin,
xs.stop if xs.stop is not None else xmax,
ys.stop if ys.stop is not None else ymax,
)
idx = obj.intersects(bbox)
return obj[idx]