Geometry Helpers#
Pure functions for building and inspecting shapely geometries. These
sit on pyramids.feature.geometry and are re-exposed as
FeatureCollection static methods for convenience (e.g.
FeatureCollection.create_polygon is a thin delegate to
pyramids.feature.geometry.create_polygon).
Factories#
Create shapely geometries from coordinate sequences. ARC-15 split these by return type so each function has a single, unambiguous output:
| Function | Returns |
|---|---|
create_polygon(coords) |
shapely.Polygon |
polygon_wkt(coords) |
WKT str |
create_points(coords) |
list[shapely.Point] |
point_collection(coords, crs) |
geopandas.GeoDataFrame |
pyramids.feature.geometry.create_polygon(coords)
#
Build a :class:shapely.Polygon from a sequence of (x, y) tuples.
the return type is now unconditional — always a
Polygon. For the WKT string form use :func:polygon_wkt.
validates that the ring has at least 3 distinct-capable vertices. Shapely itself accepts 2-vertex input and produces an invalid polygon; raising here surfaces the user error at the point of origin.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coords
|
list[tuple[float, float]]
|
Sequence of |
required |
Returns:
| Name | Type | Description |
|---|---|---|
Polygon |
Polygon
|
A shapely |
Raises:
| Type | Description |
|---|---|
InvalidGeometryError
|
If |
Examples:
- Build a triangle and inspect its WKT form:
- Build a unit square and read back its bounding box and area:
- Fewer than three vertices raises
InvalidGeometryError:
Source code in src/pyramids/feature/geometry.py
pyramids.feature.geometry.polygon_wkt(coords)
#
Build a WKT string for a polygon from (x, y) tuples.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coords
|
list[tuple[float, float]]
|
Ring coordinates. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
Well-Known Text representation of the polygon. |
Examples:
- Build the WKT for a triangle:
- Build the WKT for a unit square and confirm the closing vertex is appended automatically:
Source code in src/pyramids/feature/geometry.py
pyramids.feature.geometry.create_points(coords)
#
Build a list of shapely Point objects from (x, y) tuples.
the return type is now unconditional — always a
list[Point]. For the GeoDataFrame wrapper use
:func:point_collection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coords
|
Iterable[tuple[float, ...]]
|
Iterable of |
required |
Returns:
| Type | Description |
|---|---|
list[Point]
|
list[Point]: The constructed shapely Points. |
Examples:
- Build two points and inspect their WKT forms:
- Build a single point from a generator and read its x/y:
Source code in src/pyramids/feature/geometry.py
pyramids.feature.geometry.point_collection(coords, crs)
#
Build a :class:GeoDataFrame of points with a given CRS (ARC-15).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coords
|
Iterable[tuple[float, ...]]
|
Iterable of |
required |
crs
|
Any
|
A CRS accepted by :class: |
required |
Returns:
| Name | Type | Description |
|---|---|---|
GeoDataFrame |
GeoDataFrame
|
A GeoDataFrame with a single |
Examples:
- Build a GeoDataFrame of two points in WGS84 and inspect the CRS and the first geometry:
- Build an empty-ish frame with a single point and access the x/y of the stored geometry:
Source code in src/pyramids/feature/geometry.py
Coordinate Extraction#
Per-geometry coordinate accessors. get_coords dispatches by geometry
type; the typed helpers (get_point_coords, get_line_coords,
get_poly_coords, get_xy_coords) are lazy — they check coord_type
before touching the geometry's attributes (M2), so an invalid
coord_type raises before a degenerate geometry can trigger a
GEOSException.
pyramids.feature.geometry.get_coords(row, geom_col, coord_type)
#
Return coordinates for a row, dispatching by geometry type.
the previous implementation returned the magic value
-9999 for MultiPolygon rows as a sentinel that the caller
(:meth:FeatureCollection.xy) filtered out afterwards. That
conflated real coordinate values of -9999 (plausible in
several projected CRSes) with the "unhandled geometry" signal and
could silently drop valid rows. The sentinel is gone; callers
must explode MultiPolygon rows before calling this function
(:func:explode_gdf does exactly that and is always invoked from
:meth:FeatureCollection.xy).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
row
|
Series
|
A row of the GeoDataFrame. |
required |
geom_col
|
str
|
Name of the geometry column. |
required |
coord_type
|
str
|
|
required |
Returns:
| Name | Type | Description |
|---|---|---|
Any |
Any
|
Coordinates as |
Raises:
| Type | Description |
|---|---|
InvalidGeometryError
|
If the geometry is a |
Examples:
- Extract the x coordinate of a Point row in a GeoDataFrame:
- Extract the x coordinates of a Polygon exterior ring:
>>> import geopandas as gpd >>> from shapely.geometry import Polygon >>> from pyramids.feature.geometry import get_coords >>> gdf = gpd.GeoDataFrame( ... {"geometry": [Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])]}, ... crs="EPSG:4326", ... ) >>> get_coords(gdf.iloc[0], "geometry", "x") [0.0, 1.0, 1.0, 0.0, 0.0] - A
MultiPolygonrow raisesInvalidGeometryError— use :func:explode_gdffirst:>>> import geopandas as gpd >>> from shapely.geometry import Polygon, MultiPolygon >>> from pyramids.feature.geometry import get_coords >>> mp = MultiPolygon([Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])]) >>> gdf = gpd.GeoDataFrame({"geometry": [mp]}, crs="EPSG:4326") >>> get_coords(gdf.iloc[0], "geometry", "x") # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... pyramids.base._errors.InvalidGeometryError: get_coords does not accept MultiPolygon rows
Source code in src/pyramids/feature/geometry.py
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 | |
pyramids.feature.geometry.get_xy_coords(geometry, coord_type)
#
Return x or y coords from a LineString / Polygon boundary.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
geometry
|
Any
|
Any geometry exposing |
required |
coord_type
|
str
|
|
required |
Returns:
| Name | Type | Description |
|---|---|---|
list |
list
|
Coordinate values. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
- Extract x values from a LineString:
- Extract y values from a Polygon exterior ring:
- An unknown axis label raises
ValueError:
Source code in src/pyramids/feature/geometry.py
pyramids.feature.geometry.get_point_coords(geometry, coord_type)
#
Return the x or y coordinate of a shapely :class:Point.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
geometry
|
Point
|
A shapely Point. |
required |
coord_type
|
str
|
|
required |
Returns:
| Type | Description |
|---|---|
float | int
|
float | int: The requested coordinate. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
- Extract the x coordinate of a Point:
- Extract the y coordinate of a Point:
- An unknown axis label raises
ValueError:
Source code in src/pyramids/feature/geometry.py
pyramids.feature.geometry.get_line_coords(geometry, coord_type)
#
Return x or y coordinates of a :class:LineString.
Thin wrapper around :func:get_xy_coords that documents the
LineString-specific entry point used by
:func:multi_geom_handler and :func:get_coords.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
geometry
|
LineString
|
A shapely |
required |
coord_type
|
str
|
|
required |
Returns:
| Name | Type | Description |
|---|---|---|
list |
list
|
Coordinate values along the requested axis, in vertex |
list
|
order. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
- Extract x values along a two-segment line:
- Extract y values from a straight horizontal line:
Source code in src/pyramids/feature/geometry.py
pyramids.feature.geometry.get_poly_coords(geometry, coord_type)
#
Return x or y coordinates of a :class:Polygon exterior.
Only the exterior ring is considered — interior rings (holes) are ignored. The returned sequence is closed (the first vertex is repeated at the end) because shapely stores polygon rings with an explicit closing vertex.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
geometry
|
Polygon
|
A shapely |
required |
coord_type
|
str
|
|
required |
Returns:
| Name | Type | Description |
|---|---|---|
list |
list
|
Coordinate values of the exterior ring, in ring-traversal |
list
|
order, with the closing vertex repeated. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
- Extract x coordinates of the exterior ring of a unit square:
- Extract y coordinates of a triangle (interior hole is ignored):
Source code in src/pyramids/feature/geometry.py
Multi-geometry Handling#
explode_gdf expands MultiPolygon (or GeometryCollection) rows
into one-geometry-per-row frames; multi_geom_handler and
geometry_collection_coords return per-part coordinate sequences
without exploding.
pyramids.feature.geometry.explode_gdf(gdf, geometry='multipolygon')
#
Explode multi-geometries into per-row single geometries.
Rows whose geometry type matches geometry are expanded so that
each child geometry becomes its own row.
the input gdf is not mutated. Earlier versions
silently dropped the exploded rows from the caller's frame via
inplace=True — a caller that kept a handle to the input saw
its data change underneath them. The function now snapshots the
input up-front and returns a new frame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gdf
|
GeoDataFrame
|
The GeoDataFrame to process. Not mutated. |
required |
geometry
|
str
|
The geometry type to explode
( |
'multipolygon'
|
Returns:
| Name | Type | Description |
|---|---|---|
GeoDataFrame |
GeoDataFrame
|
A new GeoDataFrame with exploded rows first and |
GeoDataFrame
|
the preserved (non-matching) rows after. |
Examples:
- Explode a two-row frame that mixes one MultiPolygon with a
single Polygon and inspect the resulting geometries:
>>> import geopandas as gpd >>> from shapely.geometry import Polygon, MultiPolygon >>> from pyramids.feature.geometry import explode_gdf >>> gdf = gpd.GeoDataFrame( ... { ... "name": ["a", "b"], ... "geometry": [ ... MultiPolygon( ... [ ... Polygon([(0, 0), (2, 0), (2, 2), (0, 2)]), ... Polygon([(5, 5), (7, 5), (7, 7), (5, 7)]), ... ] ... ), ... Polygon([(10, 10), (11, 10), (11, 11), (10, 11)]), ... ], ... }, ... crs="EPSG:4326", ... ) >>> result = explode_gdf(gdf, "multipolygon") >>> len(result) 3 >>> [g.geom_type for g in result.geometry] ['Polygon', 'Polygon', 'Polygon'] >>> list(result["name"]) ['b', 'a', 'a'] - The input frame is not mutated; callers keep their
original data even after the explode call:
>>> import geopandas as gpd >>> from shapely.geometry import Polygon, MultiPolygon >>> from pyramids.feature.geometry import explode_gdf >>> gdf = gpd.GeoDataFrame( ... { ... "geometry": [ ... MultiPolygon( ... [ ... Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]), ... Polygon([(2, 2), (3, 2), (3, 3), (2, 3)]), ... ] ... ), ... ], ... }, ... crs="EPSG:4326", ... ) >>> _ = explode_gdf(gdf, "multipolygon") >>> gdf.iloc[0].geometry.geom_type 'MultiPolygon'
Source code in src/pyramids/feature/geometry.py
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 | |
pyramids.feature.geometry.multi_geom_handler(multi_geometry, coord_type, geom_type)
#
Extract per-part coordinates from a multi-geometry.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
multi_geometry
|
MultiPolygon | MultiPoint | MultiLineString
|
A shapely |
required |
coord_type
|
str
|
|
required |
geom_type
|
str
|
One of |
required |
Returns:
| Name | Type | Description |
|---|---|---|
list |
list
|
A list of per-part coordinate sequences. For |
list
|
|
|
list
|
|
|
list
|
of vertex coordinates for the corresponding part. |
Examples:
- Extract x coordinates from a MultiPoint (one scalar per part):
- Extract x coordinates from a MultiLineString (one list per part):
- Extract y coordinates from a MultiPolygon exterior rings:
>>> from shapely.geometry import Polygon, MultiPolygon >>> from pyramids.feature.geometry import multi_geom_handler >>> mp = MultiPolygon( ... [ ... Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]), ... Polygon([(2, 2), (3, 2), (3, 3), (2, 3)]), ... ] ... ) >>> multi_geom_handler(mp, "y", "multipolygon") [[0.0, 0.0, 1.0, 1.0, 0.0], [2.0, 2.0, 3.0, 3.0, 2.0]]
Source code in src/pyramids/feature/geometry.py
pyramids.feature.geometry.geometry_collection_coords(geom, coord_type)
#
Extract coords from every sub-geometry of a GeometryCollection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
geom
|
Any
|
A shapely |
required |
coord_type
|
str
|
|
required |
Returns:
| Name | Type | Description |
|---|---|---|
list |
list[Any]
|
Merged coordinates from Point / LineString / Polygon |
list[Any]
|
sub-geometries, in iteration order. Point contributions are |
|
list[Any]
|
appended as scalars; LineString and Polygon contributions are |
|
list[Any]
|
extended into the same flat list. |
Examples:
- Merge x coordinates from a Point and a LineString in one collection:
- Merge y coordinates from a Point, a LineString, and a Polygon:
>>> from shapely.geometry import Point, LineString, Polygon, GeometryCollection >>> from pyramids.feature.geometry import geometry_collection_coords >>> gc = GeometryCollection( ... [ ... Point(3, 4), ... LineString([(0, 0), (2, 2)]), ... Polygon([(5, 5), (6, 5), (6, 6), (5, 6)]), ... ] ... ) >>> geometry_collection_coords(gc, "y") [4.0, 0.0, 2.0, 5.0, 5.0, 6.0, 6.0, 5.0]