more libraries
This commit is contained in:
316
.venv/lib/python3.12/site-packages/fiona/features.py
Normal file
316
.venv/lib/python3.12/site-packages/fiona/features.py
Normal file
@@ -0,0 +1,316 @@
|
||||
"""Operations on GeoJSON feature and geometry objects."""
|
||||
|
||||
from collections import UserDict
|
||||
from functools import wraps
|
||||
import itertools
|
||||
from typing import Generator, Iterable, Mapping, Union
|
||||
|
||||
from fiona.transform import transform_geom # type: ignore
|
||||
import shapely # type: ignore
|
||||
import shapely.ops # type: ignore
|
||||
from shapely.geometry import mapping, shape # type: ignore
|
||||
from shapely.geometry.base import BaseGeometry, BaseMultipartGeometry # type: ignore
|
||||
|
||||
from .errors import ReduceError
|
||||
from ._vendor import snuggs
|
||||
|
||||
# Patch snuggs's func_map, extending it with Python builtins, geometry
|
||||
# methods and attributes, and functions exported in the shapely module
|
||||
# (such as set_precision).
|
||||
|
||||
|
||||
class FuncMapper(UserDict, Mapping):
|
||||
"""Resolves functions from names in pipeline expressions."""
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Get a function by its name."""
|
||||
if key in self.data:
|
||||
return self.data[key]
|
||||
elif key in __builtins__ and not key.startswith("__"):
|
||||
return __builtins__[key]
|
||||
elif key in dir(shapely):
|
||||
return lambda g, *args, **kwargs: getattr(shapely, key)(g, *args, **kwargs)
|
||||
elif key in dir(shapely.ops):
|
||||
return lambda g, *args, **kwargs: getattr(shapely.ops, key)(
|
||||
g, *args, **kwargs
|
||||
)
|
||||
else:
|
||||
return (
|
||||
lambda g, *args, **kwargs: getattr(g, key)(*args, **kwargs)
|
||||
if callable(getattr(g, key))
|
||||
else getattr(g, key)
|
||||
)
|
||||
|
||||
|
||||
def collect(geoms: Iterable) -> object:
|
||||
"""Turn a sequence of geometries into a single GeometryCollection.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
geoms : Iterable
|
||||
A sequence of geometry objects.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Geometry
|
||||
|
||||
"""
|
||||
return shapely.GeometryCollection(list(geoms))
|
||||
|
||||
|
||||
def dump(geom: Union[BaseGeometry, BaseMultipartGeometry]) -> Generator:
|
||||
"""Get the individual parts of a geometry object.
|
||||
|
||||
If the given geometry object has a single part, e.g., is an
|
||||
instance of LineString, Point, or Polygon, this function yields a
|
||||
single result, the geometry itself.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
geom : a shapely geometry object.
|
||||
|
||||
Yields
|
||||
------
|
||||
A shapely geometry object.
|
||||
|
||||
"""
|
||||
if hasattr(geom, "geoms"):
|
||||
parts = geom.geoms
|
||||
else:
|
||||
parts = [geom]
|
||||
for part in parts:
|
||||
yield part
|
||||
|
||||
|
||||
def identity(obj: object) -> object:
|
||||
"""Get back the given argument.
|
||||
|
||||
To help in making expression lists, where the first item must be a
|
||||
callable object.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
obj : objeect
|
||||
|
||||
Returns
|
||||
-------
|
||||
obj
|
||||
|
||||
"""
|
||||
return obj
|
||||
|
||||
|
||||
def vertex_count(obj: object) -> int:
|
||||
"""Count the vertices of a GeoJSON-like geometry object.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
obj: object
|
||||
A GeoJSON-like mapping or an object that provides
|
||||
__geo_interface__.
|
||||
|
||||
Returns
|
||||
-------
|
||||
int
|
||||
|
||||
"""
|
||||
shp = shape(obj)
|
||||
if hasattr(shp, "geoms"):
|
||||
return sum(vertex_count(part) for part in shp.geoms)
|
||||
elif hasattr(shp, "exterior"):
|
||||
return vertex_count(shp.exterior) + sum(
|
||||
vertex_count(ring) for ring in shp.interiors
|
||||
)
|
||||
else:
|
||||
return len(shp.coords)
|
||||
|
||||
|
||||
def binary_projectable_property_wrapper(func):
|
||||
"""Project func's geometry args before computing a property.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
func : callable
|
||||
Signature is func(geom1, geom2, *args, **kwargs)
|
||||
|
||||
Returns
|
||||
-------
|
||||
callable
|
||||
Signature is func(geom1, geom2, projected=True, *args, **kwargs)
|
||||
|
||||
"""
|
||||
|
||||
@wraps(func)
|
||||
def wrapper(geom1, geom2, *args, projected=True, **kwargs):
|
||||
if projected:
|
||||
geom1 = shape(transform_geom("OGC:CRS84", "EPSG:6933", mapping(geom1)))
|
||||
geom2 = shape(transform_geom("OGC:CRS84", "EPSG:6933", mapping(geom2)))
|
||||
|
||||
return func(geom1, geom2, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def unary_projectable_property_wrapper(func):
|
||||
"""Project func's geometry arg before computing a property.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
func : callable
|
||||
Signature is func(geom1, *args, **kwargs)
|
||||
|
||||
Returns
|
||||
-------
|
||||
callable
|
||||
Signature is func(geom1, projected=True, *args, **kwargs)
|
||||
|
||||
"""
|
||||
|
||||
@wraps(func)
|
||||
def wrapper(geom, *args, projected=True, **kwargs):
|
||||
if projected:
|
||||
geom = shape(transform_geom("OGC:CRS84", "EPSG:6933", mapping(geom)))
|
||||
|
||||
return func(geom, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def unary_projectable_constructive_wrapper(func):
|
||||
"""Project func's geometry arg before constructing a new geometry.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
func : callable
|
||||
Signature is func(geom1, *args, **kwargs)
|
||||
|
||||
Returns
|
||||
-------
|
||||
callable
|
||||
Signature is func(geom1, projected=True, *args, **kwargs)
|
||||
|
||||
"""
|
||||
|
||||
@wraps(func)
|
||||
def wrapper(geom, *args, projected=True, **kwargs):
|
||||
if projected:
|
||||
geom = shape(transform_geom("OGC:CRS84", "EPSG:6933", mapping(geom)))
|
||||
product = func(geom, *args, **kwargs)
|
||||
return shape(transform_geom("EPSG:6933", "OGC:CRS84", mapping(product)))
|
||||
else:
|
||||
return func(geom, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
area = unary_projectable_property_wrapper(shapely.area)
|
||||
buffer = unary_projectable_constructive_wrapper(shapely.buffer)
|
||||
distance = binary_projectable_property_wrapper(shapely.distance)
|
||||
set_precision = unary_projectable_constructive_wrapper(shapely.set_precision)
|
||||
simplify = unary_projectable_constructive_wrapper(shapely.simplify)
|
||||
length = unary_projectable_property_wrapper(shapely.length)
|
||||
|
||||
snuggs.func_map = FuncMapper(
|
||||
area=area,
|
||||
buffer=buffer,
|
||||
collect=collect,
|
||||
distance=distance,
|
||||
dump=dump,
|
||||
identity=identity,
|
||||
length=length,
|
||||
simplify=simplify,
|
||||
set_precision=set_precision,
|
||||
vertex_count=vertex_count,
|
||||
**{
|
||||
k: getattr(itertools, k)
|
||||
for k in dir(itertools)
|
||||
if not k.startswith("_") and callable(getattr(itertools, k))
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def map_feature(
|
||||
expression: str, feature: Mapping, dump_parts: bool = False
|
||||
) -> Generator:
|
||||
"""Map a pipeline expression to a feature.
|
||||
|
||||
Yields one or more values.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
expression : str
|
||||
A snuggs expression. The outermost parentheses are optional.
|
||||
feature : dict
|
||||
A Fiona feature object.
|
||||
dump_parts : bool, optional (default: False)
|
||||
If True, the parts of the feature's geometry are turned into
|
||||
new features.
|
||||
|
||||
Yields
|
||||
------
|
||||
object
|
||||
|
||||
"""
|
||||
if not (expression.startswith("(") and expression.endswith(")")):
|
||||
expression = f"({expression})"
|
||||
|
||||
try:
|
||||
geom = shape(feature.get("geometry", None))
|
||||
if dump_parts and hasattr(geom, "geoms"):
|
||||
parts = geom.geoms
|
||||
else:
|
||||
parts = [geom]
|
||||
except (AttributeError, KeyError):
|
||||
parts = [None]
|
||||
|
||||
for part in parts:
|
||||
result = snuggs.eval(expression, g=part, f=feature)
|
||||
if isinstance(result, (str, float, int, Mapping)):
|
||||
yield result
|
||||
elif isinstance(result, (BaseGeometry, BaseMultipartGeometry)):
|
||||
yield mapping(result)
|
||||
else:
|
||||
try:
|
||||
for item in result:
|
||||
if isinstance(item, (BaseGeometry, BaseMultipartGeometry)):
|
||||
item = mapping(item)
|
||||
yield item
|
||||
except TypeError:
|
||||
yield result
|
||||
|
||||
|
||||
def reduce_features(expression: str, features: Iterable[Mapping]) -> Generator:
|
||||
"""Reduce a collection of features to a single value.
|
||||
|
||||
The pipeline is a string that, when evaluated by snuggs, produces
|
||||
a new value. The name of the input feature collection in the
|
||||
context of the pipeline is "c".
|
||||
|
||||
Parameters
|
||||
----------
|
||||
pipeline : str
|
||||
Geometry operation pipeline such as "(unary_union c)".
|
||||
features : iterable
|
||||
A sequence of Fiona feature objects.
|
||||
|
||||
Yields
|
||||
------
|
||||
object
|
||||
|
||||
Raises
|
||||
------
|
||||
ReduceError
|
||||
|
||||
"""
|
||||
if not (expression.startswith("(") and expression.endswith(")")):
|
||||
expression = f"({expression})"
|
||||
|
||||
collection = (shape(feat["geometry"]) for feat in features)
|
||||
result = snuggs.eval(expression, c=collection)
|
||||
|
||||
if isinstance(result, (str, float, int, Mapping)):
|
||||
yield result
|
||||
elif isinstance(result, (BaseGeometry, BaseMultipartGeometry)):
|
||||
yield mapping(result)
|
||||
else:
|
||||
raise ReduceError("Expression failed to reduce to a single value.")
|
||||
Reference in New Issue
Block a user