Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate use of feature and geometry dicts #1116

Merged
merged 2 commits into from
Jun 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,21 @@ All issue numbers are relative to https://github.com/Toblerity/Fiona/issues.
1.9a2 (TBD)
-----------

Deprecations:

- Fiona's API methods will accept feature and geometry dicts in 1.9.0, but this
usage is deprecated. Instances of Feature and Geometry will be required in
2.0.
- The precision keyword argument of fiona.transform.transform_geom is
deprecated and will be removed in version 2.0.
- Deprecated usage has been eliminated in the project. Fiona's tests pass when
run with a -Werror::DeprecationWarning filter.

Changes:

- Fiona's FionaDeprecationWarning now sub-classes DeprecationWarning.
- Some test modules have beeen re-formatted using black.

New features:

- Fiona Collections now carry a context exit stack into which we can push fiona
Expand Down
43 changes: 22 additions & 21 deletions fiona/_geometry.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ from __future__ import absolute_import
import logging

from fiona.errors import UnsupportedGeometryTypeError
from fiona.model import GEOMETRY_TYPES, Geometry
from fiona.model import _guard_model_object, GEOMETRY_TYPES, Geometry

from fiona._err cimport exc_wrap_int

Expand Down Expand Up @@ -100,16 +100,16 @@ cdef class GeomBuilder:
values.append(OGR_G_GetZ(geom, i))
coords.append(tuple(values))
return coords

cpdef _buildPoint(self):
return {'type': 'Point', 'coordinates': self._buildCoords(self.geom)[0]}

cpdef _buildLineString(self):
return {'type': 'LineString', 'coordinates': self._buildCoords(self.geom)}

cpdef _buildLinearRing(self):
return {'type': 'LinearRing', 'coordinates': self._buildCoords(self.geom)}

cdef _buildParts(self, void *geom):
cdef int j
cdef void *part
Expand All @@ -120,27 +120,27 @@ cdef class GeomBuilder:
part = OGR_G_GetGeometryRef(geom, j)
parts.append(GeomBuilder().build(part))
return parts

cpdef _buildPolygon(self):
coordinates = [p['coordinates'] for p in self._buildParts(self.geom)]
return {'type': 'Polygon', 'coordinates': coordinates}

cpdef _buildMultiPoint(self):
coordinates = [p['coordinates'] for p in self._buildParts(self.geom)]
return {'type': 'MultiPoint', 'coordinates': coordinates}

cpdef _buildMultiLineString(self):
coordinates = [p['coordinates'] for p in self._buildParts(self.geom)]
return {'type': 'MultiLineString', 'coordinates': coordinates}

cpdef _buildMultiPolygon(self):
coordinates = [p['coordinates'] for p in self._buildParts(self.geom)]
return {'type': 'MultiPolygon', 'coordinates': coordinates}

cpdef _buildGeometryCollection(self):
parts = self._buildParts(self.geom)
return {'type': 'GeometryCollection', 'geometries': parts}

cdef build(self, void *geom):
# The only method anyone needs to call
if geom == NULL:
Expand All @@ -157,7 +157,7 @@ cdef class GeomBuilder:
self.ndims = OGR_G_GetCoordinateDimension(geom)
self.geom = geom
built = getattr(self, '_build' + self.geomtypename)()
return Geometry(**built)
return Geometry.from_dict(**built)

cpdef build_wkb(self, object wkb):
# The only other method anyone needs to call
Expand Down Expand Up @@ -189,20 +189,20 @@ cdef class OGRGeomBuilder:
cdef void *cogr_geometry = self._createOgrGeometry(GEOJSON2OGR_GEOMETRY_TYPES['Point'])
self._addPointToGeometry(cogr_geometry, coordinates)
return cogr_geometry

cdef void * _buildLineString(self, object coordinates) except NULL:
cdef void *cogr_geometry = self._createOgrGeometry(GEOJSON2OGR_GEOMETRY_TYPES['LineString'])
for coordinate in coordinates:
self._addPointToGeometry(cogr_geometry, coordinate)
return cogr_geometry

cdef void * _buildLinearRing(self, object coordinates) except NULL:
cdef void *cogr_geometry = self._createOgrGeometry(GEOJSON2OGR_GEOMETRY_TYPES['LinearRing'])
for coordinate in coordinates:
self._addPointToGeometry(cogr_geometry, coordinate)
OGR_G_CloseRings(cogr_geometry)
return cogr_geometry

cdef void * _buildPolygon(self, object coordinates) except NULL:
cdef void *cogr_ring
cdef void *cogr_geometry = self._createOgrGeometry(GEOJSON2OGR_GEOMETRY_TYPES['Polygon'])
Expand Down Expand Up @@ -235,17 +235,18 @@ cdef class OGRGeomBuilder:
exc_wrap_int(OGR_G_AddGeometryDirectly(cogr_geometry, cogr_part))
return cogr_geometry

cdef void * _buildGeometryCollection(self, object coordinates) except NULL:
cdef void * _buildGeometryCollection(self, object geometries) except NULL:
cdef void *cogr_part
cdef void *cogr_geometry = self._createOgrGeometry(GEOJSON2OGR_GEOMETRY_TYPES['GeometryCollection'])
for part in coordinates:
for part in geometries:
cogr_part = OGRGeomBuilder().build(part)
exc_wrap_int(OGR_G_AddGeometryDirectly(cogr_geometry, cogr_part))
return cogr_geometry

cdef void * build(self, object geometry) except NULL:
cdef object typename = geometry['type']
cdef object coordinates = geometry.get('coordinates')
cdef object typename = geometry.type
cdef object coordinates = geometry.coordinates
cdef object geometries = geometry.geometries
if typename == 'Point':
return self._buildPoint(coordinates)
elif typename == 'LineString':
Expand All @@ -261,14 +262,14 @@ cdef class OGRGeomBuilder:
elif typename == 'MultiPolygon':
return self._buildMultiPolygon(coordinates)
elif typename == 'GeometryCollection':
coordinates = geometry.get('geometries')
return self._buildGeometryCollection(coordinates)
return self._buildGeometryCollection(geometries)
else:
raise UnsupportedGeometryTypeError("Unsupported geometry type %s" % typename)


def geometryRT(geometry):
def geometryRT(geom):
# For testing purposes only, leaks the JSON data
geometry = _guard_model_object(geom)
cdef void *cogr_geometry = OGRGeomBuilder().build(geometry)
result = GeomBuilder().build(cogr_geometry)
_deleteOgrGeom(cogr_geometry)
Expand Down
115 changes: 24 additions & 91 deletions fiona/_transform.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,10 @@ cdef object _transform_single_geom(
OGRGeometryFactory *factory,
void *transform,
char **options,
object precision
):
"""Transform a single geometry."""
cdef void *src_ogr_geom = NULL
cdef void *dst_ogr_geom = NULL
cdef int i

if not isinstance(single_geom, Geometry):
single_geom = Geometry.from_dict(**single_geom)

src_ogr_geom = _geometry.OGRGeomBuilder().build(single_geom)
dst_ogr_geom = factory.transformWithOptions(
Expand All @@ -120,10 +116,7 @@ cdef object _transform_single_geom(

if dst_ogr_geom == NULL:
warnings.warn(
"Full reprojection failed, but partial is possible. To enable partial "
"reprojection wrap the transform_geom call like so:\n"
"with fiona.Env(OGR_ENABLE_PARTIAL_REPROJECTION=True):\n"
" transform_geom(...)"
"Full reprojection failed. To enable partial reprojection set OGR_ENABLE_PARTIAL_REPROJECTION=True"
)
return None
else:
Expand All @@ -133,89 +126,11 @@ cdef object _transform_single_geom(
if src_ogr_geom != NULL:
_geometry.OGR_G_DestroyGeometry(src_ogr_geom)

if precision >= 0:

def round_point(g):
coords = list(g['coordinates'])
x, y = coords[:2]
x = round(x, precision)
y = round(y, precision)
new_coords = [x, y]
if len(coords) == 3:
z = coords[2]
new_coords.append(round(z, precision))
return new_coords

def round_linestring(g):
coords = list(zip(*g['coordinates']))
xp, yp = coords[:2]
xp = [round(v, precision) for v in xp]
yp = [round(v, precision) for v in yp]
if len(coords) == 3:
zp = coords[2]
zp = [round(v, precision) for v in zp]
new_coords = list(zip(xp, yp, zp))
else:
new_coords = list(zip(xp, yp))
return new_coords

def round_polygon(g):
new_coords = []
for piece in out_geom['coordinates']:
coords = list(zip(*piece))
xp, yp = coords[:2]
xp = [round(v, precision) for v in xp]
yp = [round(v, precision) for v in yp]
if len(coords) == 3:
zp = coords[2]
zp = [round(v, precision) for v in zp]
new_coords.append(list(zip(xp, yp, zp)))
else:
new_coords.append(list(zip(xp, yp)))
return new_coords

def round_multipolygon(g):
parts = g['coordinates']
new_coords = []
for part in parts:
inner_coords = []
for ring in part:
coords = list(zip(*ring))
xp, yp = coords[:2]
xp = [round(v, precision) for v in xp]
yp = [round(v, precision) for v in yp]
if len(coords) == 3:
zp = coords[2]
zp = [round(v, precision) for v in zp]
inner_coords.append(list(zip(xp, yp, zp)))
else:
inner_coords.append(list(zip(xp, yp)))
new_coords.append(inner_coords)
return new_coords

def round_geometry(g):
if g['type'] == 'Point':
g['coordinates'] = round_point(g)
elif g['type'] in ['LineString', 'MultiPoint']:
g['coordinates'] = round_linestring(g)
elif g['type'] in ['Polygon', 'MultiLineString']:
g['coordinates'] = round_polygon(g)
elif g['type'] == 'MultiPolygon':
g['coordinates'] = round_multipolygon(g)
else:
raise RuntimeError("Unsupported geometry type: {}".format(g['type']))

if out_geom['type'] == 'GeometryCollection':
for _g in out_geom['geometries']:
round_geometry(_g)
else:
round_geometry(out_geom)

return out_geom


def _transform_geom(src_crs, dst_crs, geom, antimeridian_cutting, antimeridian_offset, precision):
"""Return a transformed geometry.
"""Return transformed geometries.

"""
cdef char *proj_c = NULL
Expand Down Expand Up @@ -246,11 +161,15 @@ def _transform_geom(src_crs, dst_crs, geom, antimeridian_cutting, antimeridian_o

factory = new OGRGeometryFactory()

if isinstance(geom, DICT_TYPES):
out_geom = _transform_single_geom(geom, factory, transform, options, precision)
if isinstance(geom, Geometry):
out_geom = recursive_round(
_transform_single_geom(geom, factory, transform, options), precision)
else:
out_geom = [
_transform_single_geom(single_geom, factory, transform, options, precision)
recursive_round(
_transform_single_geom(single_geom, factory, transform, options),
precision,
)
for single_geom in geom
]

Expand All @@ -263,3 +182,17 @@ def _transform_geom(src_crs, dst_crs, geom, antimeridian_cutting, antimeridian_o
OSRRelease(dst)

return out_geom


def recursive_round(obj, precision):
"""Recursively round coordinates."""
if precision < 0:
return obj
if getattr(obj, 'geometries', None):
return Geometry(geometries=[recursive_round(part, precision) for part in obj.geometries])
elif getattr(obj, 'coordinates', None):
return Geometry(coordinates=[recursive_round(part, precision) for part in obj.coordinates])
if isinstance(obj, (int, float)):
return round(obj, precision)
else:
return [recursive_round(part, precision) for part in obj]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaces the previous rounding functions.

Loading