pygeoif - Man Page

pygeoif 1.6.0

Overview

PyGeoIf provides a GeoJSON-like protocol <https://gist.github.com/2217756> for geo-spatial (GIS) vector data.

Other Python programs and packages that you may have heard of that implement this protocol:

When you want to write your own geospatial library with support for this protocol you may use pygeoif as a starting point and build your functionality on top of it. It has no requirements outside the Python standard library and is therefore easy to integrate into your project. It is tested on CPython <https://python.org>, PyPy <https://www.pypy.org/> and GraalPy <https://www.graalvm.org/python/>, but it should work on alternative Python implementations (that implement the language specification >=3.9) as well.

You may think of pygeoif as a 'shapely ultralight' which lets you construct geometries and perform very basic operations like reading and writing geometries from/to WKT, constructing line strings out of points, polygons from linear rings, multi polygons from polygons, etc. It was inspired by shapely and implements the geometries in a way that when you are familiar with pygeoif, you will feel right at home with shapely or the other way round. It provides Hypothesis strategies for all geometries for property based testing with Hypothesis <https://hypothesis.works>.

It was written to provide clean and python only geometries for fastkml <http://pypi.python.org/pypi/fastkml/>

[image: Documentation] [image]
<https://pygeoif.readthedocs.io/en/latest/?badge=latest> [image: GitHub Actions] [image]
<https://github.com/cleder/pygeoif/actions/workflows/run-all-tests.yml> [image: Codecov] [image]
<https://codecov.io/gh/cleder/pygeoif> [image: Tested with Hypothesis] [image]
<https://hypothesis.readthedocs.io> [image: Black] [image]
<https://github.com/psf/> [image: Mypy] [image]
<http://mypy-lang.org/> [image: Openhub] [image]
<https://www.openhub.net/p/pygeoif/> [image: CodeFactor] [image]
<https://www.codefactor.io/repository/github/cleder/pygeoif/overview/main> [image: pre-commit] [image]
<https://github.com/pre-commit/pre-commit> [image: Supported Python versions] [image]
<https://pypi.python.org/pypi/pygeoif/> [image: Supported Python implementations] [image]
<https://pypi.python.org/pypi/pygeoif/> [image: Latest Version] [image]
<https://pypi.python.org/pypi/pygeoif/> [image: Conda Version] [image]
<https://anaconda.org/conda-forge/pygeoif> [image: License] [image]
<https://pypi.python.org/pypi/pygeoif/> [image: Downloads] [image]
<https://www.pepy.tech/projects/pygeoif>

Installation

You can install PyGeoIf from pypi using pip:

pip install pygeoif

Example

>>> from pygeoif import geometry
>>> p = geometry.Point(1,1)
>>> p.__geo_interface__
{'type': 'Point', 'bbox': (1, 1, 1, 1), 'coordinates': (1, 1)}
>>> print(p)
POINT (1 1)
>>> p
Point(1, 1)
>>> l = geometry.LineString([(0.0, 0.0), (1.0, 1.0)])
>>> l.bounds
(0.0, 0.0, 1.0, 1.0)
>>> print(l)
LINESTRING (0.0 0.0, 1.0 1.0)

You find more examples in the tests <https://github.com/cleder/pygeoif/tree/main/tests> directory which cover every aspect of pygeoif or in fastkml <http://pypi.python.org/pypi/fastkml/>.

Classes

All classes implement the attribute:

All geometry classes implement the attributes:

For two-dimensional geometries the following methods are implemented:

Point

A zero dimensional geometry

A point has zero length and zero area. A point cannot be empty.

Attributes

x, y, z (float)

Coordinate values

Example

LineString

A one-dimensional figure comprising one or more line segments

A LineString has non-zero length and zero area. It may approximate a curve and need not be straight. Unlike a LinearRing, a LineString is not closed.

Attributes

geoms (sequence)

A sequence of Points

LinearRing

A closed one-dimensional geometry comprising one or more line segments

A LinearRing that crosses itself or touches itself at a single point is invalid and operations on it may fail.

A LinearRing is self closing.

Polygon

A two-dimensional figure bounded by a linear ring

A polygon has a non-zero area. It may have one or more negative-space "holes" which are also bounded by linear rings. If any rings cross each other, the geometry is invalid and operations on it may fail.

Attributes

exterior (LinearRing)

The ring which bounds the positive space of the polygon.

interiors (sequence)

A sequence of rings which bound all existing holes.

MultiPoint

A collection of one or more points.

Attributes

geoms (sequence)

A sequence of Points.

MultiLineString

A collection of one or more line strings.

A MultiLineString has non-zero length and zero area.

Attributes

geoms (sequence)

A sequence of LineStrings

MultiPolygon

A collection of one or more polygons.

Attributes

geoms (sequence)

A sequence of Polygon instances

GeometryCollection

A heterogenous collection of geometries (Points, LineStrings, LinearRings and Polygons).

Attributes

geoms (sequence)

A sequence of geometry instances

Please note: GEOMETRYCOLLECTION isn't supported by the Shapefile or GeoJSON <https://geojson.org/> format. And this sub-class isn't generally supported by ordinary GIS sw (viewers and so on). So it's very rarely used in the real GIS professional world.

Example

>>> from pygeoif import geometry
>>> p = geometry.Point(1.0, -1.0)
>>> p2 = geometry.Point(1.0, -1.0)
>>> geoms = [p, p2]
>>> c = geometry.GeometryCollection(geoms)
>>> [geom for geom in geoms]
[Point(1.0, -1.0), Point(1.0, -1.0)]

Feature

Aggregates a geometry instance with associated user-defined properties.

Attributes

geometry (object)

A geometry instance

properties (dict)

A dictionary linking field keys with values associated with with geometry instance

Example

>>> from pygeoif import Point, Feature
>>> p = Point(1.0, -1.0)
>>> props = {'Name': 'Sample Point', 'Other': 'Other Data'}
>>> a = Feature(p, props)
>>> a.properties
{'Name': 'Sample Point', 'Other': 'Other Data'}
>>> a.properties['Name']
'Sample Point'

FeatureCollection

A heterogenous collection of Features

Attributes

features: sequence

A sequence of feature instances

Example

>>> from pygeoif import Point, Feature, FeatureCollection
>>> p = Point(1.0, -1.0)
>>> props = {'Name': 'Sample Point', 'Other': 'Other Data'}
>>> a = Feature(p, props)
>>> p2 = Point(1.0, -1.0)
>>> props2 = {'Name': 'Sample Point2', 'Other': 'Other Data2'}
>>> b = Feature(p2, props2)
>>> features = [a, b]
>>> c = FeatureCollection(features)
>>> [feature for feature in c]
[Feature(Point(1.0, -1.0), {'Name': 'Sample Point', 'Other': 'Other Data'},...]

Functions

shape

Create a pygeoif feature from an object that provides the __geo_interface__ or any GeoJSON <https://geojson.org/> compatible dictionary.

>>> from shapely.geometry import Point
>>> from pygeoif import geometry, shape
>>> shape(Point(0,0))
Point(0.0, 0.0)

from_wkt

Create a geometry from its WKT representation

>>> from pygeoif import from_wkt
>>> p = from_wkt('POINT (0 1)')
>>> print(p)
POINT (0.0 1.0)

signed_area

Return the signed area enclosed by a ring. A value >= 0 indicates a counter-clockwise oriented ring.

orient

Returns a copy of a polygon with exteriors and interiors in the right orientation.

if ccw is True than the exterior will be in counterclockwise orientation and the interiors will be in clockwise orientation, or the other way round when ccw is False.

box

Return a rectangular polygon with configurable normal vector.

mapping

Return the __geo_interface__ dictionary.

Development

Clone this repository, create a virtualenv with Python 3.8 or later with python3 -m venv .venv and activate it with source .venv/bin/activate.

Then install the requirements with pip install -e ".[dev]".

pre-commit

Install the pre-commit hook with:

pip install pre-commit
pre-commit install

and check the code with:

pre-commit run --all-files

Testing

Run the unit and static tests with:

pytest tests
pytest --doctest-glob="README.rst"
ruff check pygeoif
ruff fmt pygeoif
mypy pygeoif

Acknowledgments

The tests were improved with mutmut <https://github.com/boxed/mutmut> which discovered some nasty edge cases.

API Reference

pygeoif

Module contents

Submodules

pygeoif.geometry module

Geometries in pure Python.

class pygeoif.geometry.GeometryCollection(geometries: Iterable[Point <#pygeoif.geometry.Point> | LineString <#pygeoif.geometry.LineString> | LinearRing <#pygeoif.geometry.LinearRing> | Polygon <#pygeoif.geometry.Polygon> | MultiPoint <#pygeoif.geometry.MultiPoint> | MultiLineString <#pygeoif.geometry.MultiLineString> | MultiPolygon <#pygeoif.geometry.MultiPolygon> | GeometryCollection <#pygeoif.geometry.GeometryCollection>])

Bases: _MultiGeometry

A heterogenous collection of geometries.

Attributes

geoms (sequence)

A sequence of geometry instances

Please note: GEOMETRYCOLLECTION isn't supported by the Shapefile format. And this sub- class isn't generally supported by ordinary GIS sw (viewers and so on). So it's very rarely used in the real GIS professional world.

Example

Initialize Geometries and construct a GeometryCollection

>>> from pygeoif import geometry
>>> p = geometry.Point(1.0, -1.0)
>>> p2 = geometry.Point(1.0, -1.0)
>>> geoms = [p, p2]
>>> c = geometry.GeometryCollection(geoms)
>>> c.__geo_interface__
{'type': 'GeometryCollection',
'geometries': [{'type': 'Point', 'coordinates': (1.0, -1.0)},
{'type': 'Point', 'coordinates': (1.0, -1.0)}]}
class pygeoif.geometry.LineString(coordinates: Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]])

Bases: _Geometry

A one-dimensional figure comprising one or more line segments.

A LineString has non-zero length and zero area. It may approximate a curve and need not be straight. Unlike a LinearRing, a LineString is not closed.

Attributes

geoms (sequence)

A sequence of Points

property geoms: tuple[Point <#pygeoif.geometry.Point>, ...]

Return the underlying geometries.

property coords: Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]]

Return the geometry coordinates.

property is_empty: bool

Return if this geometry is empty.

A Linestring is considered empty when it has no points.

property has_z: bool | None

Return True if the geometry's coordinate sequence(s) have z values.

classmethod from_coordinates(coordinates: Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]]) -> LineString <#pygeoif.geometry.LineString>

Construct a linestring from coordinates.

classmethod from_points(*args: Point <#pygeoif.geometry.Point>) -> LineString <#pygeoif.geometry.LineString>

Create a linestring from points.

class pygeoif.geometry.LinearRing(coordinates: Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]])

Bases: LineString

A closed one-dimensional geometry comprising one or more line segments.

A LinearRing that crosses itself or touches itself at a single point is invalid and operations on it may fail.

A Linear Ring is self closing

property centroid: Point <#pygeoif.geometry.Point> | None

Return the centroid of the ring.

property is_ccw: bool

Return True if the ring is oriented counter clock-wise.

class pygeoif.geometry.MultiLineString(lines: Sequence[Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]]], unique: bool = False)

Bases: _MultiGeometry

A collection of one or more line strings.

A MultiLineString has non-zero length and zero area.

Attributes

geoms (sequence)

A sequence of LineStrings

property geoms: Iterator[LineString <#pygeoif.geometry.LineString>]

Iterate over the points.

classmethod from_linestrings(*args: LineString <#pygeoif.geometry.LineString>, unique: bool = False) -> MultiLineString <#pygeoif.geometry.MultiLineString>

Create a MultiLineString from LineStrings.

class pygeoif.geometry.MultiPoint(points: Sequence[tuple[float, float] | tuple[float, float, float]], unique: bool = False)

Bases: _MultiGeometry

A collection of one or more points.

Attributes

geoms (sequence)

A sequence of Points

property geoms: Iterator[Point <#pygeoif.geometry.Point>]

Iterate over the points.

classmethod from_points(*args: Point <#pygeoif.geometry.Point>, unique: bool = False) -> MultiPoint <#pygeoif.geometry.MultiPoint>

Create a MultiPoint from Points.

class pygeoif.geometry.MultiPolygon(polygons: Sequence[tuple[Sequence[tuple[float, float]], Sequence[Sequence[tuple[float, float]]]] | tuple[Sequence[tuple[float, float]]] | tuple[Sequence[tuple[float, float, float]], Sequence[Sequence[tuple[float, float, float]]]] | tuple[Sequence[tuple[float, float, float]]]], unique: bool = False)

Bases: _MultiGeometry

A collection of one or more polygons.

If component polygons overlap the collection is invalid and some operations on it may fail.

Attributes

geoms (sequence)

A sequence of Polygon instances

property geoms: Iterator[Polygon <#pygeoif.geometry.Polygon>]

Iterate over the points.

classmethod from_polygons(*args: Polygon <#pygeoif.geometry.Polygon>, unique: bool = False) -> MultiPolygon <#pygeoif.geometry.MultiPolygon>

Create a MultiPolygon from Polygons.

class pygeoif.geometry.Point(x: float, y: float, z: float | None = None)

Bases: _Geometry

A zero dimensional geometry.

A point has zero length and zero area.

Attributes

x, y, z (float)

Coordinate values

Example

>>> p = Point(1.0, -1.0)
>>> print p
POINT (1.0 -1.0)
>>> p.y
-1.0
>>> p.x
1.0
property is_empty: bool

Return if this geometry is empty.

A Point is considered empty when it has no valid coordinates.

property x: float

Return x coordinate.

property y: float

Return y coordinate.

property z: float | None

Return z coordinate.

property coords: tuple[tuple[float, float] | tuple[float, float, float]] | tuple[()]

Return the geometry coordinates.

property has_z: bool

Return True if the geometry's coordinate sequence(s) have z values.

classmethod from_coordinates(coordinates: Sequence[tuple[float, float] | tuple[float, float, float]]) -> Point <#pygeoif.geometry.Point>

Construct a point from coordinates.

class pygeoif.geometry.Polygon(shell: Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]], holes: Sequence[Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]]] | None = None)

Bases: _Geometry

A two-dimensional figure bounded by a linear ring.

A polygon has a non-zero area. It may have one or more negative-space "holes" which are also bounded by linear rings. If any rings cross each other, the geometry is invalid and operations on it may fail.

Attributes

exterior (LinearRing)

The ring which bounds the positive space of the polygon.

interiors (sequence)

A sequence of rings which bound all existing holes.

property exterior: LinearRing <#pygeoif.geometry.LinearRing>

Return the exterior Linear Ring of the polygon.

property interiors: Iterator[LinearRing <#pygeoif.geometry.LinearRing>]

Interiors (Holes) of the polygon.

property is_empty: bool

Return if this geometry is empty.

A polygon is empty when it does not have an exterior.

property coords: tuple[Sequence[tuple[float, float]], Sequence[Sequence[tuple[float, float]]]] | tuple[Sequence[tuple[float, float]]] | tuple[Sequence[tuple[float, float, float]], Sequence[Sequence[tuple[float, float, float]]]] | tuple[Sequence[tuple[float, float, float]]]

Return Coordinates of the Polygon.

Note that this is not implemented in Shapely.

property has_z: bool | None

Return True if the geometry's coordinate sequence(s) have z values.

classmethod from_coordinates(coordinates: tuple[Sequence[tuple[float, float]], Sequence[Sequence[tuple[float, float]]]] | tuple[Sequence[tuple[float, float]]] | tuple[Sequence[tuple[float, float, float]], Sequence[Sequence[tuple[float, float, float]]]] | tuple[Sequence[tuple[float, float, float]]]) -> Polygon <#pygeoif.geometry.Polygon>

Construct a linestring from coordinates.

classmethod from_linear_rings(shell: LinearRing <#pygeoif.geometry.LinearRing>, *args: LinearRing <#pygeoif.geometry.LinearRing>) -> Polygon <#pygeoif.geometry.Polygon>

Construct a Polygon from linear rings.

pygeoif.feature module

Features.

class pygeoif.feature.Feature(geometry: Point <#pygeoif.geometry.Point> | LineString <#pygeoif.geometry.LineString> | LinearRing <#pygeoif.geometry.LinearRing> | Polygon <#pygeoif.geometry.Polygon> | MultiPoint <#pygeoif.geometry.MultiPoint> | MultiLineString <#pygeoif.geometry.MultiLineString> | MultiPolygon <#pygeoif.geometry.MultiPolygon>, properties: dict[str, Any] | None = None, feature_id: str | int | None = None)

Bases: object

Aggregates a geometry instance with associated user-defined properties.

Attributes

geometry (object)

A geometry instance

properties (dict)

A dictionary linking field keys with values associated with geometry instance

Example

>>> p = Point(1.0, -1.0)
>>> props = {'Name': 'Sample Point', 'Other': 'Other Data'}
>>> a = Feature(p, props)
>>> a.properties
{'Name': 'Sample Point', 'Other': 'Other Data'}
 >>> a.properties['Name']
'Sample Point'
property id: str | int | None

Return the id of the feature.

property geometry: Point <#pygeoif.geometry.Point> | LineString <#pygeoif.geometry.LineString> | LinearRing <#pygeoif.geometry.LinearRing> | Polygon <#pygeoif.geometry.Polygon> | MultiPoint <#pygeoif.geometry.MultiPoint> | MultiLineString <#pygeoif.geometry.MultiLineString> | MultiPolygon <#pygeoif.geometry.MultiPolygon>

Return the geometry of the feature.

property properties: dict[str, Any]

Return a dictionary of properties.

class pygeoif.feature.FeatureCollection(features: Sequence[Feature <#pygeoif.feature.Feature>])

Bases: object

A heterogenous collection of Features.

Attributes

features (sequence)

A sequence of feature instances

Example

>>> from pygeoif import geometry
>>> p = geometry.Point(1.0, -1.0)
>>> props = {'Name': 'Sample Point', 'Other': 'Other Data'}
>>> a = geometry.Feature(p, props)
>>> p2 = geometry.Point(1.0, -1.0)
>>> props2 = {'Name': 'Sample Point2', 'Other': 'Other Data2'}
>>> b = geometry.Feature(p2, props2)
>>> features = [a, b]
>>> c = geometry.FeatureCollection(features)
>>> c.__geo_interface__
{'type': 'FeatureCollection',
 'features': [{'geometry': {'type': 'Point', 'coordinates': (1.0, -1.0)},
 'type': 'Feature',
 'properties': {'Other': 'Other Data', 'Name': 'Sample Point'}},
{'geometry': {'type': 'Point', 'coordinates': (1.0, -1.0)},
 'type': 'Feature',
 'properties': {'Other': 'Other Data2', 'Name': 'Sample Point2'}}]}
property features: Generator[Feature <#pygeoif.feature.Feature>, None, None]

Iterate over the features of the collection.

property bounds: tuple[float, float, float, float]

Return the X-Y bounding box.

pygeoif.factories module

Geometry Factories.

pygeoif.factories.box(minx: float, miny: float, maxx: float, maxy: float, ccw: bool = True) -> Polygon <#pygeoif.geometry.Polygon>

Return a rectangular polygon with configurable normal vector.

pygeoif.factories.force_2d(context: GeoType <#pygeoif.types.GeoType> | GeoCollectionType <#pygeoif.types.GeoCollectionType>) -> Point <#pygeoif.geometry.Point> | LineString <#pygeoif.geometry.LineString> | LinearRing <#pygeoif.geometry.LinearRing> | Polygon <#pygeoif.geometry.Polygon> | MultiPoint <#pygeoif.geometry.MultiPoint> | MultiLineString <#pygeoif.geometry.MultiLineString> | MultiPolygon <#pygeoif.geometry.MultiPolygon> | GeometryCollection <#pygeoif.geometry.GeometryCollection>

Force the dimensionality of a geometry to 2D.

>>> force_2d(Point(0, 0, 1))
Point(0, 0)
>>> force_2d(Point(0, 0))
Point(0, 0)
>>> force_2d(LineString([(0, 0, 0), (0, 1, 1), (1, 1, 2)]))
LineString(((0, 0), (0, 1), (1, 1)))
pygeoif.factories.force_3d(context: GeoType <#pygeoif.types.GeoType> | GeoCollectionType <#pygeoif.types.GeoCollectionType>, z: float = 0) -> Point <#pygeoif.geometry.Point> | LineString <#pygeoif.geometry.LineString> | LinearRing <#pygeoif.geometry.LinearRing> | Polygon <#pygeoif.geometry.Polygon> | MultiPoint <#pygeoif.geometry.MultiPoint> | MultiLineString <#pygeoif.geometry.MultiLineString> | MultiPolygon <#pygeoif.geometry.MultiPolygon> | GeometryCollection <#pygeoif.geometry.GeometryCollection>

Force the dimensionality of a geometry to 3D.

>>> force_3d(Point(0, 0))
Point(0, 0, 0)
>>> force_3d(Point(0, 0), 1)
Point(0, 0, 1)
>>> force_3d(Point(0, 0, 0))
Point(0, 0, 0)
>>> force_3d(LineString([(0, 0), (0, 1), (1, 1)]))
LineString(((0, 0, 0), (0, 1, 0), (1, 1, 0)))
pygeoif.factories.from_wkt(geo_str: str) -> Point <#pygeoif.geometry.Point> | LineString <#pygeoif.geometry.LineString> | LinearRing <#pygeoif.geometry.LinearRing> | Polygon <#pygeoif.geometry.Polygon> | MultiPoint <#pygeoif.geometry.MultiPoint> | MultiLineString <#pygeoif.geometry.MultiLineString> | MultiPolygon <#pygeoif.geometry.MultiPolygon> | GeometryCollection <#pygeoif.geometry.GeometryCollection> | None

Create a geometry from its WKT representation.

pygeoif.factories.mapping(ob: GeoType <#pygeoif.types.GeoType> | GeoCollectionType <#pygeoif.types.GeoCollectionType>) -> GeoCollectionInterface <#pygeoif.types.GeoCollectionInterface> | GeoInterface <#pygeoif.types.GeoInterface>

Return a GeoJSON-like mapping.

Parameters

ob :

An object which implements __geo_interface__.

Returns

dict Example ------- >>> pt = Point(0, 0) >>> mapping(pt) {'type': 'Point', 'bbox': (0, 0, 0, 0), 'coordinates': (0, 0)}

pygeoif.factories.orient(polygon: Polygon <#pygeoif.geometry.Polygon>, ccw: bool = True) -> Polygon <#pygeoif.geometry.Polygon>

Return a polygon with exteriors and interiors in the right orientation.

if ccw is True than the exterior will be in counterclockwise orientation and the interiors will be in clockwise orientation, or the other way round when ccw is False.

pygeoif.factories.shape(context: GeoType <#pygeoif.types.GeoType> | GeoCollectionType <#pygeoif.types.GeoCollectionType> | GeoInterface <#pygeoif.types.GeoInterface> | GeoCollectionInterface <#pygeoif.types.GeoCollectionInterface>) -> Point <#pygeoif.geometry.Point> | LineString <#pygeoif.geometry.LineString> | LinearRing <#pygeoif.geometry.LinearRing> | Polygon <#pygeoif.geometry.Polygon> | MultiPoint <#pygeoif.geometry.MultiPoint> | MultiLineString <#pygeoif.geometry.MultiLineString> | MultiPolygon <#pygeoif.geometry.MultiPolygon> | GeometryCollection <#pygeoif.geometry.GeometryCollection>

Return a new geometry with coordinates copied from the context.

Changes to the original context will not be reflected in the geometry object.

Parameters

context :

a GeoJSON-like dict, which provides a "type" member describing the type of the geometry and "coordinates" member providing a list of coordinates, or an object which implements __geo_interface__.

Returns

Geometry object Example ------- Create a Point from GeoJSON, and then create a copy using __geo_interface__. >>> context = {'type': 'Point', 'coordinates': [0, 1]} >>> geom = shape(context) >>> geom.geom_type == 'Point' True >>> geom.wkt 'Point (0 1)' >>> geom2 = shape(geom) >>> geom == geom2 True

pygeoif.functions module

Functions for geometries.

pygeoif.functions.centroid(coords: Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]]) -> tuple[tuple[float, float], float]

Calculate the coordinates of the centroid and the area of a LineString.

pygeoif.functions.compare_coordinates(coords: float | tuple[float, float] | tuple[float, float, float] | Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]] | Sequence[Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]]] | Sequence[tuple[float, float] | tuple[float, float, float] | Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]] | Sequence[Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]]]], other: float | tuple[float, float] | tuple[float, float, float] | Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]] | Sequence[Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]]] | Sequence[tuple[float, float] | tuple[float, float, float] | Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]] | Sequence[Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]]]]) -> bool

Compare two coordinate sequences.

pygeoif.functions.compare_geo_interface(first: GeoInterface <#pygeoif.types.GeoInterface> | GeoCollectionInterface <#pygeoif.types.GeoCollectionInterface>, second: GeoInterface <#pygeoif.types.GeoInterface> | GeoCollectionInterface <#pygeoif.types.GeoCollectionInterface>) -> bool

Compare two geo interfaces.

pygeoif.functions.convex_hull(points: Iterable[tuple[float, float]]) -> Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]]

Return the convex hull of a set of points using Andrew's monotone chain algorithm.

Args

points (Iterable[Point2D]): A collection of 2D points.

Returns

LineType: The convex hull, represented as a list of points.

pygeoif.functions.dedupe(coords: Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]]) -> Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]]

Remove duplicate Points from a LineString.

pygeoif.functions.move_coordinate(coordinate: tuple[float, float] | tuple[float, float, float], move_by: tuple[float, float] | tuple[float, float, float]) -> tuple[float, float] | tuple[float, float, float]

Move the coordinate by the given vector.

This forcefully changes the dimensions of the coordinate to match the latter. >>> move_coordinate((0, 0), (-1, 1)) (-1, 1) >>> move_coordinate((0, 0, 0), (-1, 1)) (-1, 1) >>> move_coordinate((0, 0), (-1, 1, 0)) (-1, 1, 0)

pygeoif.functions.move_coordinates(coordinates: tuple[float, float] | tuple[float, float, float] | Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]] | Sequence[Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]]], move_by: tuple[float, float] | tuple[float, float, float]) -> tuple[float, float] | tuple[float, float, float] | Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]] | Sequence[Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]]]

Move the coordinates recursively by the given vector.

This forcefully changes the dimension of each of the coordinate to match the dimensionality of the vector. >>> move_coordinates(((0, 0), (-1, 1)), (-1, 1)) ((-1, 1), (-2, 2)) >>> move_coordinates(((0, 0, 0), (-1, 1, 0)), (-1, 1)) ((-1, 1), (-2, 2)) >>> move_coordinates(((0, 0), (-1, 1)), (-1, 1, 0)) ((-1, 1, 0), (-2, 2, 0))

pygeoif.functions.signed_area(coords: Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]]) -> float

Return the signed area enclosed by a ring.

Linear time algorithm: <http://www.cgafaq.info/wiki/Polygon_Area>. A value >= 0 indicates a counter-clockwise oriented ring.

pygeoif.exceptions module

Exceptions for pygeoif.

exception pygeoif.exceptions.DimensionError

Bases: ValueError

Geometries must have 2 or 3 dimensions.

exception pygeoif.exceptions.InvalidGeometryError

Bases: ValueError

Geometry is not valid.

exception pygeoif.exceptions.WKTParserError

Bases: AttributeError

WKT not supported or cannot be parsed.

pygeoif.types module

Types for geometries.

class pygeoif.types.GeoCollectionInterface

Bases: TypedDict

Geometry Collection Interface.

type: Literal['GeometryCollection']

geometries: Sequence[GeoInterface <#pygeoif.types.GeoInterface> | GeoCollectionInterface <#pygeoif.types.GeoCollectionInterface>]

bbox: NotRequired[tuple[float, float, float, float]]

class pygeoif.types.GeoCollectionType(*args, **kwargs)

Bases: Protocol

Any compatible type that implements the __geo_interface__.

class pygeoif.types.GeoFeatureCollectionInterface

Bases: TypedDict

Bbox and id are optional keys for the GeoFeatureCollectionInterface.

type: Literal['FeatureCollection']

features: Sequence[GeoFeatureInterface <#pygeoif.types.GeoFeatureInterface>]

bbox: NotRequired[tuple[float, float, float, float]]

id: NotRequired[str | int]

class pygeoif.types.GeoFeatureInterface

Bases: TypedDict

The GeoFeatureInterface has optional keys.

type: Literal['Feature']

bbox: NotRequired[tuple[float, float, float, float]]

properties: NotRequired[dict[str, Any]]

id: NotRequired[str | int]

geometry: GeoInterface <#pygeoif.types.GeoInterface>

class pygeoif.types.GeoInterface

Bases: TypedDict

Required keys for the GeoInterface.

type: Literal['Point', 'LineString', 'LinearRing', 'Polygon', 'MultiPoint', 'MultiLineString', 'MultiPolygon']

coordinates: tuple[float, float] | tuple[float, float, float] | Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]] | Sequence[Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]]] | Sequence[tuple[float, float] | tuple[float, float, float] | Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]] | Sequence[Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]]]]

bbox: NotRequired[tuple[float, float, float, float]]

class pygeoif.types.GeoType(*args, **kwargs)

Bases: Protocol

Any compatible type that implements the __geo_interface__.

pygeoif.about module

About pygeoif.

The only purpose of this module is to provide a version number for the package.

pygeoif.hypothesis.strategies module

Data-generating strategies for property-based testing.

Coordinates are limited to 32 bit floats to avoid precision issues.

class pygeoif.hypothesis.strategies.Srs(name: str, min_xyz: tuple[float | None, float | None, float | None], max_xyz: tuple[float | None, float | None, float | None])

Represents a spatial reference system (SRS).

Attributes

name (str): The name of the SRS. min_xyz (Tuple[Optional[float], Optional[float], Optional[float]]): - The minimum x, y, and z values of the SRS. max_xyz (Tuple[Optional[float], Optional[float], Optional[float]]): - The maximum x, y, and z values of the SRS.

name: str

min_xyz: tuple[float | None, float | None, float | None]

max_xyz: tuple[float | None, float | None, float | None]

longitudes() -> SearchStrategy[float]

Generate a search strategy for generating longitudes.

Returns a search strategy that generates floats within the longitude bounds of the SRS.

latitudes() -> SearchStrategy[float]

Generate a search strategy for generating latitudes.

Returns a search strategy that generates floats within the latitude bounds of the SRS.

elevations() -> SearchStrategy[float]

Generate a search strategy for generating elevations.

Returns a search strategy that generates floats within the elevation bounds of the SRS.

pygeoif.hypothesis.strategies.geometry_collections(*, min_geoms: int = 1, max_geoms: int = 5, max_points: int = 20, min_interiors: int = 0, max_interiors: int = 5, max_leaves: int = 3, srs: Srs <#pygeoif.hypothesis.strategies.Srs> | None = None, has_z: bool | None = None) -> SearchStrategy[GeometryCollection <#pygeoif.geometry.GeometryCollection>]

Generate a random GeometryCollection object.

Args

draw (st.DrawFn): The Hypothesis draw function. min_geoms (int): The minimum number of geometries in the collection. max_geoms (int): The maximum number of geometries in the collection. max_points (int): The maximum number of points in each geometry. min_interiors (int): The minimum number of interiors in each polygon. max_interiors (int): The maximum number of interiors in each polygon. srs (Optional[Srs], optional): The spatial reference system of the geometries. has_z (Optional[bool], optional): Whether the geometries have Z coordinates. max_leaves (int): The maximum recursion depth of the collection.

Returns

GeometryCollection: A randomly generated GeometryCollection object, that may

contain any of the following geometries: Point, LineString, LinearRing, Polygon, MultiPoint, MultiLineString, MultiPolygon, GeometryCollection.

pygeoif.hypothesis.strategies.line_coords(*, min_points: int, max_points: int | None = None, srs: Srs <#pygeoif.hypothesis.strategies.Srs> | None = None, has_z: bool | None = None, unique: bool = False) -> SearchStrategy[Sequence[tuple[float, float]] | Sequence[tuple[float, float, float]]]

Generate a random line in either 2D or 3D space.

Args

draw: The draw function from the hypothesis library. min_points: Minimum number of points in the line max_points: Maximum number of points in the line srs: An optional parameter specifying the spatial reference system. has_z: An optional parameter specifying whether to generate 2D or 3D points. unique: Optional flag to generate unique points (default False).

Returns

A list of point coordinates representing the line in either 2D or 3D space.

pygeoif.hypothesis.strategies.line_strings(*, max_points: int | None = None, srs: Srs <#pygeoif.hypothesis.strategies.Srs> | None = None, has_z: bool | None = None) -> SearchStrategy[LineString <#pygeoif.geometry.LineString>]

Generate a random linestring in either 2D or 3D space.

Args

draw: The draw function from the hypothesis library. max_points: Maximum number of points in the line (must be greater than 1) srs: An optional parameter specifying the spatial reference system. has_z: An optional parameter specifying whether to generate 2D or 3D lines.

Returns

A LineString representing the randomly generated linestring in either 2D or 3D space.

pygeoif.hypothesis.strategies.linear_rings(*, max_points: int | None = None, srs: Srs <#pygeoif.hypothesis.strategies.Srs> | None = None, has_z: bool | None = None) -> SearchStrategy[LinearRing <#pygeoif.geometry.LinearRing>]

Generate a linear ring using the provided draw function.

Args

draw (st.DrawFn): The draw function used to generate the coordinates. max_points (Optional[int]): The maximum number of points in the linear ring. If not specified, there is no limit. srs (Optional[Srs]): The spatial reference system of the linear ring. If not specified, the default SRS is used. has_z (Optional[bool]): Whether the linear ring has z-coordinates. If not specified, 2D or 3D coordinates are generated.

Returns

LinearRing: The generated linear ring.

Raises

ValueError: If max_points is less than 4.

pygeoif.hypothesis.strategies.multi_line_strings(*, min_lines: int = 1, max_lines: int = 5, max_points: int = 10, srs: Srs <#pygeoif.hypothesis.strategies.Srs> | None = None, has_z: bool | None = None) -> SearchStrategy[MultiLineString <#pygeoif.geometry.MultiLineString>]

Generate a random MultiLineString object.

Args

draw (st.DrawFn): The Hypothesis draw function. min_lines (int, optional): The minimum number of lines in the MultiLineString. max_lines (int, optional): The maximum number of lines in the MultiLineString. max_points (int, optional): The maximum number of points in each line. srs (Srs, optional): The spatial reference system of the MultiLineString. has_z (bool, optional): Whether the MultiLineString has z-coordinates.

Returns

MultiLineString: The generated MultiLineString object.

pygeoif.hypothesis.strategies.multi_points(*, min_points: int = 1, max_points: int | None = None, srs: Srs <#pygeoif.hypothesis.strategies.Srs> | None = None, has_z: bool | None = None) -> SearchStrategy[MultiPoint <#pygeoif.geometry.MultiPoint>]

Generate a MultiPoint geometry object with random coordinates.

Args

draw (st.DrawFn): The draw function from the hypothesis library. min_points (int): The minimum number of points in the MultiPoint. Default is 1. max_points (Optional[int]): The maximum number of points in the MultiPoint. srs (Optional[Srs]): The spatial reference system of the coordinates. has_z (Optional[bool]): Whether the coordinates have a Z component. if not specified, 2D and 3D coordinates will be generated randomly.

Returns

MultiPoint: The generated MultiPoint geometry object.

pygeoif.hypothesis.strategies.multi_polygons(*, min_polygons: int = 1, max_polygons: int = 3, max_points: int = 10, min_interiors: int = 0, max_interiors: int = 2, srs: Srs <#pygeoif.hypothesis.strategies.Srs> | None = None, has_z: bool | None = None) -> SearchStrategy[MultiPolygon <#pygeoif.geometry.MultiPolygon>]

Generate a random MultiPolygon object.

Args

draw (st.DrawFn): The Hypothesis draw function. min_polygons (int, optional): The min number of polygons in the MultiPolygon. max_polygons (int, optional): The max number of polygons in the MultiPolygon. max_points (int, optional): The maximum number of points in each polygon. min_interiors (int, optional): The minimum number of interiors in each polygon. max_interiors (int, optional): The maximum number of interiors in each polygon. srs (Optional[Srs], optional): The spatial reference system of the MultiPolygon. has_z (Optional[bool], optional): Whether the MultiPolygon has z-coordinates.

Returns

MultiPolygon: The generated MultiPolygon object.

pygeoif.hypothesis.strategies.point_coords(*, srs: Srs <#pygeoif.hypothesis.strategies.Srs> | None = None, has_z: bool | None = None) -> SearchStrategy[tuple[float, float] | tuple[float, float, float]]

Generate a random point in either 2D or 3D space.

Args

draw: The draw function from the hypothesis library. srs: An optional parameter specifying the spatial reference system. has_z: An optional parameter specifying whether to generate 2D or 3D points.

Returns

A tuple representing the point in either 2D or 3D space.

pygeoif.hypothesis.strategies.points(*, srs: Srs <#pygeoif.hypothesis.strategies.Srs> | None = None, has_z: bool | None = None) -> SearchStrategy[Point <#pygeoif.geometry.Point>]

Generate a random point in either 2D or 3D space.

Args

draw: The draw function from the hypothesis library. srs: An optional parameter specifying the spatial reference system. has_z: An optional parameter specifying whether to generate 2D or 3D points.

Returns

A randomly generated point in either 2D or 3D space.

pygeoif.hypothesis.strategies.polygons(*, max_points: int | None = None, min_interiors: int = 0, max_interiors: int = 5, srs: Srs <#pygeoif.hypothesis.strategies.Srs> | None = None, has_z: bool | None = None) -> SearchStrategy[Polygon <#pygeoif.geometry.Polygon>]

Generate a random polygon using the given strategies.

Args

draw (st.DrawFn): The draw function used to generate random values. max_points (Optional[int]): The maximum number of points in the polygon. If not specified, there is no limit. min_interiors (Optional[int]): The minimum number of interior rings (holes) in the polygon. Defaults to 0. max_interiors (Optional[int]): The maximum number of interior rings (holes) in the polygon. If not specified, there is no limit. srs (Optional[Srs]): The spatial reference system of the polygon. Defaults to None. has_z (Optional[bool]): Whether the polygon has z-coordinates. If not specified, a random boolean value will be used.

Returns

Polygon: The generated polygon.

Raises

ValueError: If max_points is specified and is less than 4.

Changelog

1.6.0 (unreleased)

  • drop Python 3.8 support
  • add GraalPy to test matrix

1.5.1 (2024/12/05)

  • expose force_2d and force_3d factories in the top-level module.
  • fix WKT parsing for nested GeometryCollections.
  • fix WKT output for MultiPoints.
  • make hypothesis strategy for GeometryCollections recursive, so that it can generate nested collections.
  • add Python 3.14 to test matrix.

1.5.0 (2024/05/11)

  • fix handling of empty geometries.
  • more hypothesis tests

1.4.0 (2024/03/25)

  • add Hypothesis tests [Ben Shaver, Christian Ledermann]
  • fix convex_hull edge-cases discovered by Hypothesis tests
  • fix from_wkt to include multi geometries in GeometryCollections
  • drop Python 3.7 support

1.3.0 (2024/02/05)

  • add Python 3.13 to supported versions
  • remove maybe_valid methods
  • GeoType and GeoCollectionType protocols to use a property instead of an attribute

1.2.0 (2023/11/27)

  • Geometries are now immutable (but not hashable)
  • add force_2d and force_3d factories [Alex Svetkin]

1.1.1 (2023/10/27)

  • Use pyproject.toml, remove setup.py and MANIFEST.in

1.1 (2023/10/13)

  • Fix nested MultiGeometries
  • Improve type annotations
  • Add Python 3.12 to supported versions
  • Last version to support Python 3.7

1.0 (2022/09/29)

  • Add type annotations
  • refactor
  • changes to keep functionality and interface close to shapely
  • remove support for python 2
  • minimum python version is 3.7
  • rename as_shape to shape
  • add box factory
  • format with black
  • reconstruct objects from their representation
  • Parse WKT that is not in upper case.
  • Centroid for LinearRings
  • Convex Hull
  • implement equality __eq__ operator (==)
  • is_empty and bool
  • drop duplicate points when creating LineStrings

0.7 (2017/05/04)

  • fix broken multipolygon [mindflayer]
  • add "bbox" to __geo_interface__ output [jzmiller1]

0.6 (2015/08/04)

  • Add id to feature [jzmiller1]

0.5 (2015/07/13)

  • Add __iter__ method to FeatureCollection and GeometryCollection [jzmiller1].
  • add pypy and pypy3 and python 3.4 to travis.
  • Add tox configuration for performing local testing [Ian Lee].
  • Add Travis continuous deployment.

0.4 (2013/10/25)

  • after a year in production promote it to Development Status :: 5 - Production/Stable
  • MultiPolygons return tuples as the __geo_interface__

0.3.1 (2012/11/15)

  • specify minor python versions tested with Travis CI
  • fix for signed area

0.3 (2012/11/14)

  • add GeometryCollection
  • len(Multi*) and len(GeometryCollection) returns the number of contained Geometries
  • add orient function to get clockwise or counterclockwise oriented polygons
  • add signed_area function
  • add _set_orientation method to lineStrings, Polygons and MultiPolygons

0.2.1 (2012/08/02)

  • as_shape also accepts an object that is neither a dictionary nor has a __geo_interface__ but can be converted into a __geo_interface__ compliant dictionary

0.2 (2012/08/01)

  • change license to LGPL
  • add wkt as a property
  • as_shape also accepts a __geo_interface__ compliant dictionary
  • test with python3

0.1 (2012/07/27)

  • initial release

Contributors

License

Copyright (C) 2012 - 2023  Christian Ledermann

This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL <https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html>) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License (LGPL <https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html>) for more details.

You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

Author

Christian Ledermann

Info

Feb 26, 2026