From d39b64cb5c65076a0a3365502d0558b06e1d9f61 Mon Sep 17 00:00:00 2001 From: jlarsen Date: Wed, 17 Jan 2024 13:15:18 -0800 Subject: [PATCH] update(GeoSpatialCollection): add support for geopandas GeoDataFrame objects * update test_geospatial_util.py to include GeoDataFrame tests * add geopandas as optional dependency --- autotest/test_geospatial_util.py | 14 +++++++++----- flopy/utils/geospatial_utils.py | 32 ++++++++++++++++++++++++++++++++ pyproject.toml | 1 + 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/autotest/test_geospatial_util.py b/autotest/test_geospatial_util.py index 3ac9f42a9a..2d0508c1ed 100644 --- a/autotest/test_geospatial_util.py +++ b/autotest/test_geospatial_util.py @@ -360,7 +360,7 @@ def test_multilinestring(multilinestring): assert gi1 == gi2, "GeoSpatialUtil multilinestring conversion error" -@requires_pkg("shapely", "geojson") +@requires_pkg("shapely", "geojson", "geopandas") def test_polygon_collection(polygon, poly_w_hole, multipolygon): col = [ Shape.from_geojson(polygon), @@ -377,8 +377,9 @@ def test_polygon_collection(polygon, poly_w_hole, multipolygon): points = gc1.points geojson = gc1.geojson fp_geo = gc1.flopy_geometry + gdf = gc1.geo_dataframe - collections = [shp, shply, points, geojson, fp_geo] + collections = [shp, shply, points, geojson, fp_geo, gdf] for col in collections: gc2 = GeoSpatialCollection(col, shapetype) @@ -410,8 +411,9 @@ def test_point_collection(point, multipoint): points = gc1.points geojson = gc1.geojson fp_geo = gc1.flopy_geometry + gdf = gc1.geo_dataframe - collections = [shp, shply, points, geojson, fp_geo] + collections = [shp, shply, points, geojson, fp_geo, gdf] for col in collections: gc2 = GeoSpatialCollection(col, shapetype) gi2 = [i.flopy_geometry.__geo_interface__ for i in gc2] @@ -439,8 +441,9 @@ def test_linestring_collection(linestring, multilinestring): points = gc1.points geojson = gc1.geojson fp_geo = gc1.flopy_geometry + gdf = gc1.geo_dataframe - collections = [shp, shply, points, geojson, fp_geo] + collections = [shp, shply, points, geojson, fp_geo, gdf] for col in collections: gc2 = GeoSpatialCollection(col, shapetype) gi2 = [i.flopy_geometry.__geo_interface__ for i in gc2] @@ -485,8 +488,9 @@ def test_mixed_collection( points = gc1.points geojson = gc1.geojson fp_geo = gc1.flopy_geometry + gdf = gc1.geo_dataframe - collections = [shp, shply, lshply, points, geojson, fp_geo] + collections = [shp, shply, lshply, points, geojson, fp_geo, gdf] for col in collections: gc2 = GeoSpatialCollection(col, shapetype) diff --git a/flopy/utils/geospatial_utils.py b/flopy/utils/geospatial_utils.py index 4cd5620a8b..2f88a0cfb3 100644 --- a/flopy/utils/geospatial_utils.py +++ b/flopy/utils/geospatial_utils.py @@ -263,6 +263,10 @@ def __init__(self, obj, shapetype=None): self.__shapefile = import_optional_dependency( "shapefile", errors="silent" ) + gpd = import_optional_dependency( + "geopandas", errors="silent" + ) + shapely_geo = import_optional_dependency( "shapely.geometry", errors="silent" ) @@ -275,6 +279,7 @@ def __init__(self, obj, shapetype=None): self._flopy_geometry = None self._points = None self.__shapetype = None + self.__attributes = None if isinstance(obj, Collection): for shape in obj: @@ -361,6 +366,16 @@ def __init__(self, obj, shapetype=None): for geom in obj.geoms: self.__collection.append(GeoSpatialUtil(geom)) + if gpd is not None: + if isinstance(obj, gpd.GeoDataFrame): + self.__attributes = {} + for geom in obj.geometry.values: + self.__collection.append(GeoSpatialUtil(geom)) + + for k in list(obj): + if k != "geometry": + self.__attributes[k] = obj[k].values + if not self.__collection: raise AssertionError( f"Reader is not installed for collection type: {type(obj)}" @@ -419,6 +434,23 @@ def shapely(self): ) return self._shapely + @property + def geo_dataframe(self): + """ + Property that returns a geopandas DataFrame + + Returns + ------- + geopandas.GeoDataFrame + """ + gpd = import_optional_dependency("geopandas") + data = {"geometry": self.shapely.geoms} + if self.__attributes is not None: + for k, v in self.__attributes.items(): + data[k] = v + gdf = gpd.GeoDataFrame(data) + return gdf + @property def geojson(self): """ diff --git a/pyproject.toml b/pyproject.toml index bb5ebb487b..7858e63c54 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,6 +64,7 @@ optional = [ "descartes", "fiona", "geojson", + "geopandas", "imageio", "netcdf4", "pymetis ; platform_system != 'Windows'",