From 999a7d58cc1cbd718793b5edd2cbfe686d59a399 Mon Sep 17 00:00:00 2001 From: Jacob Bates Date: Fri, 26 Apr 2024 13:53:14 -0500 Subject: [PATCH 01/13] add bc_lines with test --- src/rashdf/geom.py | 39 +++++++++++++++++++++++++++++++++-- tests/data/json/bc_lines.json | 1 + tests/test_geom.py | 8 ++++++- 3 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 tests/data/json/bc_lines.json diff --git a/src/rashdf/geom.py b/src/rashdf/geom.py index 5209655..58171b5 100644 --- a/src/rashdf/geom.py +++ b/src/rashdf/geom.py @@ -5,7 +5,7 @@ import pandas as pd from geopandas import GeoDataFrame from pyproj import CRS -from shapely import Polygon, Point, LineString, polygonize +from shapely import Polygon, Point, LineString, MultiLineString, polygonize from typing import Optional @@ -142,7 +142,42 @@ def mesh_cell_faces(self) -> GeoDataFrame: return GeoDataFrame(face_dict, geometry="geometry", crs=self.projection()) def bc_lines(self) -> GeoDataFrame: - raise NotImplementedError + """Return the 2D mesh area boundary condition lines. + + Returns + ------- + GeoDataFrame + A GeoDataFrame containing the 2D mesh area boundary condition lines if they exist. + """ + if "/Geometry/Boundary Condition Lines" not in self: + return GeoDataFrame() + bc_line_data = self["/Geometry/Boundary Condition Lines"] + bc_line_ids = range(bc_line_data["Attributes"][()].shape[0]) + v_conv_str = np.vectorize(convert_ras_hdf_string) + names = v_conv_str(bc_line_data["Attributes"][()]["Name"]) + mesh_names = v_conv_str(bc_line_data["Attributes"][()]["SA-2D"]) + types = v_conv_str(bc_line_data["Attributes"][()]["Type"]) + multi_lines = list() + for i, polyline_info in enumerate(bc_line_data["Polyline Info"][()]): + pnt_start, pnt_cnt, part_start, part_cnt = polyline_info + points = bc_line_data["Polyline Points"][()][pnt_start:pnt_start+pnt_cnt] + parts = bc_line_data["Polyline Parts"][()][part_start:part_start+part_cnt] + multi_lines.append( + MultiLineString( + list(points[part_pnt_start:part_pnt_start+part_pnt_cnt] for part_pnt_start, part_pnt_cnt in parts) + ) + ) + return GeoDataFrame( + { + "bc_line_id":bc_line_ids, + "name":names, + "mesh_name":mesh_names, + "type":types, + "geometry":multi_lines + }, + geometry="geometry", + crs=self.projection() + ) def breaklines(self) -> GeoDataFrame: raise NotImplementedError diff --git a/tests/data/json/bc_lines.json b/tests/data/json/bc_lines.json new file mode 100644 index 0000000..aa15afe --- /dev/null +++ b/tests/data/json/bc_lines.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "features": [{"id": "0", "type": "Feature", "properties": {"bc_line_id": 0, "name": "2d_out", "mesh_name": "2D Interior Area", "type": "External"}, "geometry": {"type": "MultiLineString", "coordinates": [[[406023.42, 1801556.29], [406029.95, 1801947.71]]]}}, {"id": "1", "type": "Feature", "properties": {"bc_line_id": 1, "name": "NW_out", "mesh_name": "Perimeter_NW", "type": "External"}, "geometry": {"type": "MultiLineString", "coordinates": [[[403925.27, 1802019.54], [403620.03, 1802568.0]]]}}], "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG::2965"}}} \ No newline at end of file diff --git a/tests/test_geom.py b/tests/test_geom.py index 13342b2..f573ada 100644 --- a/tests/test_geom.py +++ b/tests/test_geom.py @@ -43,4 +43,10 @@ def test_mesh_cell_polygons(): geom = TEST_DATA / "ras/Muncie.g05.hdf" with RasGeomHdf(geom) as ghdf: with open(TEST_DATA / "json/mesh_cell_polygons.json") as json: - assert ghdf.mesh_cell_polygons().to_json() == json.read() \ No newline at end of file + assert ghdf.mesh_cell_polygons().to_json() == json.read() + +def test_bc_lines(): + geom = TEST_DATA / "ras/Muncie.g05.hdf" + with RasGeomHdf(geom) as ghdf: + with open(TEST_DATA / "json/bc_lines.json") as json: + assert ghdf.bc_lines().to_json() == json.read() \ No newline at end of file From 47dd94c2133bca31ea14ddc8d6cc2600a59595bc Mon Sep 17 00:00:00 2001 From: Jacob Bates Date: Fri, 26 Apr 2024 14:30:12 -0500 Subject: [PATCH 02/13] add breaklines with test --- src/rashdf/geom.py | 32 +++++++++++++++++++++++++++++++- tests/data/json/breaklines.json | 1 + tests/test_geom.py | 8 +++++++- 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 tests/data/json/breaklines.json diff --git a/src/rashdf/geom.py b/src/rashdf/geom.py index 58171b5..69267ff 100644 --- a/src/rashdf/geom.py +++ b/src/rashdf/geom.py @@ -180,7 +180,37 @@ def bc_lines(self) -> GeoDataFrame: ) def breaklines(self) -> GeoDataFrame: - raise NotImplementedError + """Return the 2D mesh area breaklines. + + Returns + ------- + GeoDataFrame + A GeoDataFrame containing the 2D mesh area breaklines if they exist. + """ + if "/Geometry/2D Flow Area Break Lines" not in self: + return GeoDataFrame() + bl_line_data = self["/Geometry/2D Flow Area Break Lines"] + bl_line_ids = range(bl_line_data["Attributes"][()].shape[0]) + names = np.vectorize(convert_ras_hdf_string)(bl_line_data["Attributes"][()]["Name"]) + multi_lines = list() + for i, polyline_info in enumerate(bl_line_data["Polyline Info"][()]): + pnt_start, pnt_cnt, part_start, part_cnt = polyline_info + points = bl_line_data["Polyline Points"][()][pnt_start:pnt_start+pnt_cnt] + parts = bl_line_data["Polyline Parts"][()][part_start:part_start+part_cnt] + multi_lines.append( + MultiLineString( + list(points[part_pnt_start:part_pnt_start+part_pnt_cnt] for part_pnt_start, part_pnt_cnt in parts) + ) + ) + return GeoDataFrame( + { + "bl_id":bl_line_ids, + "name":names, + "geometry":multi_lines + }, + geometry="geometry", + crs=self.projection() + ) def refinement_regions(self) -> GeoDataFrame: raise NotImplementedError diff --git a/tests/data/json/breaklines.json b/tests/data/json/breaklines.json new file mode 100644 index 0000000..72bd742 --- /dev/null +++ b/tests/data/json/breaklines.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "features": [{"id": "0", "type": "Feature", "properties": {"bl_id": 0, "name": "Road 1"}, "geometry": {"type": "MultiLineString", "coordinates": [[[409074.78, 1802876.36], [409026.14, 1802817.66], [408942.28, 1802769.02], [408870.17, 1802745.54], [408828.24, 1802740.51], [408806.44, 1802720.39], [408774.57, 1802686.84], [408719.22, 1802626.47], [408695.74, 1802596.28], [408695.74, 1802564.41], [408727.61, 1802530.87], [408781.28, 1802463.78], [408819.85, 1802436.95], [408943.96, 1802418.5], [409019.43, 1802416.82], [409051.3, 1802396.7], [409026.14, 1802322.9], [409006.02, 1802249.11], [409019.43, 1802116.61], [409007.69, 1802026.05], [409002.66, 1801870.07]]]}}, {"id": "1", "type": "Feature", "properties": {"bl_id": 1, "name": "HighGround 1"}, "geometry": {"type": "MultiLineString", "coordinates": [[[410305.8, 1801586.64], [410290.71, 1801675.53], [410240.39, 1801740.93], [410134.73, 1801789.57], [410015.65, 1801878.46], [409901.61, 1801963.99], [409871.42, 1802052.88], [409849.62, 1802158.54], [409837.88, 1802265.88], [409804.33, 1802336.32], [409737.25, 1802374.89], [409650.04, 1802391.67], [409594.69, 1802405.08], [409537.67, 1802405.08], [409484.0, 1802395.02], [409430.33, 1802401.73], [409374.99, 1802423.53], [409334.74, 1802426.89], [409271.0, 1802416.82], [409232.43, 1802406.76], [409123.42, 1802410.11], [409063.04, 1802410.11]]]}}, {"id": "2", "type": "Feature", "properties": {"bl_id": 2, "name": "Breakline 1"}, "geometry": {"type": "MultiLineString", "coordinates": [[[410388.19, 1802731.41], [410547.77, 1802837.8], [410735.99, 1802907.36], [410891.48, 1802911.45], [411267.92, 1802874.62]]]}}, {"id": "3", "type": "Feature", "properties": {"bl_id": 3, "name": "Breakline 2"}, "geometry": {"type": "MultiLineString", "coordinates": [[[402838.91, 1804637.9], [402791.97, 1804500.22], [402879.58, 1804431.38], [402820.13, 1804362.54], [402557.28, 1804346.89], [402375.79, 1804315.6], [402350.76, 1804231.11], [402375.79, 1804059.01], [402463.41, 1804002.69], [402641.77, 1803987.04], [402782.58, 1804005.82], [402798.23, 1803918.2]]]}}], "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG::2965"}}} \ No newline at end of file diff --git a/tests/test_geom.py b/tests/test_geom.py index f573ada..423a689 100644 --- a/tests/test_geom.py +++ b/tests/test_geom.py @@ -49,4 +49,10 @@ def test_bc_lines(): geom = TEST_DATA / "ras/Muncie.g05.hdf" with RasGeomHdf(geom) as ghdf: with open(TEST_DATA / "json/bc_lines.json") as json: - assert ghdf.bc_lines().to_json() == json.read() \ No newline at end of file + assert ghdf.bc_lines().to_json() == json.read() + +def test_breaklines(): + geom = TEST_DATA / "ras/Muncie.g05.hdf" + with RasGeomHdf(geom) as ghdf: + with open(TEST_DATA / "json/breaklines.json") as json: + assert ghdf.breaklines().to_json() == json.read() \ No newline at end of file From f65410bdfcc5ec6131cafe7cbf133d19c7d69b3a Mon Sep 17 00:00:00 2001 From: Jacob Bates Date: Fri, 26 Apr 2024 16:22:41 -0500 Subject: [PATCH 03/13] add refinement_regions with test --- src/rashdf/geom.py | 34 +++++++++++++++++++++++-- tests/data/json/refinement_regions.json | 1 + tests/test_geom.py | 8 +++++- 3 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 tests/data/json/refinement_regions.json diff --git a/src/rashdf/geom.py b/src/rashdf/geom.py index 69267ff..0b19d4e 100644 --- a/src/rashdf/geom.py +++ b/src/rashdf/geom.py @@ -5,7 +5,7 @@ import pandas as pd from geopandas import GeoDataFrame from pyproj import CRS -from shapely import Polygon, Point, LineString, MultiLineString, polygonize +from shapely import Polygon, Point, LineString, MultiLineString, MultiPolygon, polygonize from typing import Optional @@ -213,7 +213,37 @@ def breaklines(self) -> GeoDataFrame: ) def refinement_regions(self) -> GeoDataFrame: - raise NotImplementedError + """Return the 2D mesh area refinement regions. + + Returns + ------- + GeoDataFrame + A GeoDataFrame containing the 2D mesh area refinement regions if they exist. + """ + if "/Geometry/2D Flow Area Refinement Regions" not in self: + return GeoDataFrame() + rr_data = self["/Geometry/2D Flow Area Refinement Regions"] + rr_ids = range(rr_data["Attributes"][()].shape[0]) + names = np.vectorize(convert_ras_hdf_string)(rr_data["Attributes"][()]["Name"]) + multi_polygons = list() + for i, polygon_info in enumerate(rr_data["Polygon Info"][()]): + pnt_start, pnt_cnt, part_start, part_cnt = polygon_info + points = rr_data["Polygon Points"][()][pnt_start:pnt_start+pnt_cnt] + parts = rr_data["Polygon Parts"][()][part_start:part_start+part_cnt] + multi_polygons.append( + MultiLineString( + list(points[part_pnt_start:part_pnt_start+part_pnt_cnt] for part_pnt_start, part_pnt_cnt in parts) + ) + ) + return GeoDataFrame( + { + "rr_id":rr_ids, + "name":names, + "geometry":multi_polygons + }, + geometry="geometry", + crs=self.projection() + ) def connections(self) -> GeoDataFrame: raise NotImplementedError diff --git a/tests/data/json/refinement_regions.json b/tests/data/json/refinement_regions.json new file mode 100644 index 0000000..5aabf5a --- /dev/null +++ b/tests/data/json/refinement_regions.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "features": [{"id": "0", "type": "Feature", "properties": {"rr_id": 0, "name": "Region 1"}, "geometry": {"type": "MultiLineString", "coordinates": [[[403466.64123541984, 1804195.9021819197], [403449.7976758183, 1804409.253936872], [403607.00423209905, 1804515.9298143482], [403865.27214598877, 1804549.6169335512], [403955.1044638635, 1804392.4103772705], [403932.6463843948, 1804201.5167017868], [403792.2833877156, 1804083.6117845762], [403646.3058711692, 1804061.1537051075], [403539.62999369303, 1804094.8408243107], [403466.64123541984, 1804195.9021819197]]]}}, {"id": "1", "type": "Feature", "properties": {"rr_id": 1, "name": "Region 2"}, "geometry": {"type": "MultiLineString", "coordinates": [[[408105.7632410168, 1803317.9615429805], [408201.9074582713, 1803374.3219461986], [408404.14184628957, 1803251.6551862531], [408397.51121061685, 1803065.9973874167], [408500.2860635441, 1802926.7540382894], [408599.74559863505, 1802837.2404567075], [408719.09704074415, 1802777.564735653], [408735.673629926, 1802731.1502859439], [408652.7906840169, 1802648.267340035], [408503.6013813805, 1802628.3754330166], [408288.10572201683, 1802717.8890145984], [408155.49300856225, 1802850.501728053], [408049.4028377986, 1802946.6459453076], [407979.7811632349, 1803029.5288912167], [407963.2045740531, 1803158.826286835], [408022.88029510767, 1803251.6551862531], [408105.7632410168, 1803317.9615429805]]]}}, {"id": "2", "type": "Feature", "properties": {"rr_id": 2, "name": "Region 3"}, "geometry": {"type": "MultiLineString", "coordinates": [[[410254.0891989809, 1802329.9968277444], [410366.81000541727, 1802356.5193704353], [410615.4588431446, 1802366.4653239443], [410761.3328279446, 1802313.4202385624], [410874.053634381, 1802220.5913391444], [410814.37791332643, 1802071.4020365078], [410648.6120215082, 1802005.0956797807], [410532.5758972355, 1802011.7263154534], [410356.86405190814, 1802078.0326721806], [410277.2964238354, 1802187.4381607806], [410254.0891989809, 1802329.9968277444]]]}}], "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG::2965"}}} \ No newline at end of file diff --git a/tests/test_geom.py b/tests/test_geom.py index 423a689..964095e 100644 --- a/tests/test_geom.py +++ b/tests/test_geom.py @@ -55,4 +55,10 @@ def test_breaklines(): geom = TEST_DATA / "ras/Muncie.g05.hdf" with RasGeomHdf(geom) as ghdf: with open(TEST_DATA / "json/breaklines.json") as json: - assert ghdf.breaklines().to_json() == json.read() \ No newline at end of file + assert ghdf.breaklines().to_json() == json.read() + +def test_refinement_regions(): + geom = TEST_DATA / "ras/Muncie.g05.hdf" + with RasGeomHdf(geom) as ghdf: + with open(TEST_DATA / "json/refinement_regions.json") as json: + assert ghdf.refinement_regions().to_json() == json.read() \ No newline at end of file From 7ee254aa96c337915bb30cb2afb9f6e1354ca66b Mon Sep 17 00:00:00 2001 From: Jacob Bates Date: Fri, 26 Apr 2024 16:25:25 -0500 Subject: [PATCH 04/13] remove uneeded enumerate objects --- src/rashdf/geom.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/rashdf/geom.py b/src/rashdf/geom.py index 0b19d4e..4038f3f 100644 --- a/src/rashdf/geom.py +++ b/src/rashdf/geom.py @@ -158,8 +158,7 @@ def bc_lines(self) -> GeoDataFrame: mesh_names = v_conv_str(bc_line_data["Attributes"][()]["SA-2D"]) types = v_conv_str(bc_line_data["Attributes"][()]["Type"]) multi_lines = list() - for i, polyline_info in enumerate(bc_line_data["Polyline Info"][()]): - pnt_start, pnt_cnt, part_start, part_cnt = polyline_info + for pnt_start, pnt_cnt, part_start, part_cnt in bc_line_data["Polyline Info"][()]: points = bc_line_data["Polyline Points"][()][pnt_start:pnt_start+pnt_cnt] parts = bc_line_data["Polyline Parts"][()][part_start:part_start+part_cnt] multi_lines.append( @@ -193,8 +192,7 @@ def breaklines(self) -> GeoDataFrame: bl_line_ids = range(bl_line_data["Attributes"][()].shape[0]) names = np.vectorize(convert_ras_hdf_string)(bl_line_data["Attributes"][()]["Name"]) multi_lines = list() - for i, polyline_info in enumerate(bl_line_data["Polyline Info"][()]): - pnt_start, pnt_cnt, part_start, part_cnt = polyline_info + for pnt_start, pnt_cnt, part_start, part_cnt in bl_line_data["Polyline Info"][()]: points = bl_line_data["Polyline Points"][()][pnt_start:pnt_start+pnt_cnt] parts = bl_line_data["Polyline Parts"][()][part_start:part_start+part_cnt] multi_lines.append( @@ -226,8 +224,7 @@ def refinement_regions(self) -> GeoDataFrame: rr_ids = range(rr_data["Attributes"][()].shape[0]) names = np.vectorize(convert_ras_hdf_string)(rr_data["Attributes"][()]["Name"]) multi_polygons = list() - for i, polygon_info in enumerate(rr_data["Polygon Info"][()]): - pnt_start, pnt_cnt, part_start, part_cnt = polygon_info + for pnt_start, pnt_cnt, part_start, part_cnt in rr_data["Polygon Info"][()]: points = rr_data["Polygon Points"][()][pnt_start:pnt_start+pnt_cnt] parts = rr_data["Polygon Parts"][()][part_start:part_start+part_cnt] multi_polygons.append( From abe9b560c8f06e54a9638bd6cc622535ccac701b Mon Sep 17 00:00:00 2001 From: Jacob Bates Date: Tue, 30 Apr 2024 13:19:11 -0500 Subject: [PATCH 05/13] make geometries singlepart when possible --- src/rashdf/geom.py | 51 +++++++++++++++---------- tests/data/json/bc_lines.json | 2 +- tests/data/json/breaklines.json | 2 +- tests/data/json/refinement_regions.json | 2 +- 4 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/rashdf/geom.py b/src/rashdf/geom.py index 4038f3f..725b6cd 100644 --- a/src/rashdf/geom.py +++ b/src/rashdf/geom.py @@ -157,22 +157,25 @@ def bc_lines(self) -> GeoDataFrame: names = v_conv_str(bc_line_data["Attributes"][()]["Name"]) mesh_names = v_conv_str(bc_line_data["Attributes"][()]["SA-2D"]) types = v_conv_str(bc_line_data["Attributes"][()]["Type"]) - multi_lines = list() + geoms = list() for pnt_start, pnt_cnt, part_start, part_cnt in bc_line_data["Polyline Info"][()]: points = bc_line_data["Polyline Points"][()][pnt_start:pnt_start+pnt_cnt] - parts = bc_line_data["Polyline Parts"][()][part_start:part_start+part_cnt] - multi_lines.append( - MultiLineString( - list(points[part_pnt_start:part_pnt_start+part_pnt_cnt] for part_pnt_start, part_pnt_cnt in parts) + if part_cnt == 1: + geoms.append(LineString(points)) + else: + parts = bc_line_data["Polyline Parts"][()][part_start:part_start+part_cnt] + geoms.append( + MultiLineString( + list(points[part_pnt_start:part_pnt_start+part_pnt_cnt] for part_pnt_start, part_pnt_cnt in parts) + ) ) - ) return GeoDataFrame( { "bc_line_id":bc_line_ids, "name":names, "mesh_name":mesh_names, "type":types, - "geometry":multi_lines + "geometry":geoms }, geometry="geometry", crs=self.projection() @@ -191,20 +194,23 @@ def breaklines(self) -> GeoDataFrame: bl_line_data = self["/Geometry/2D Flow Area Break Lines"] bl_line_ids = range(bl_line_data["Attributes"][()].shape[0]) names = np.vectorize(convert_ras_hdf_string)(bl_line_data["Attributes"][()]["Name"]) - multi_lines = list() + geoms = list() for pnt_start, pnt_cnt, part_start, part_cnt in bl_line_data["Polyline Info"][()]: points = bl_line_data["Polyline Points"][()][pnt_start:pnt_start+pnt_cnt] - parts = bl_line_data["Polyline Parts"][()][part_start:part_start+part_cnt] - multi_lines.append( - MultiLineString( - list(points[part_pnt_start:part_pnt_start+part_pnt_cnt] for part_pnt_start, part_pnt_cnt in parts) + if part_cnt == 1: + geoms.append(LineString(points)) + else: + parts = bl_line_data["Polyline Parts"][()][part_start:part_start+part_cnt] + geoms.append( + MultiLineString( + list(points[part_pnt_start:part_pnt_start+part_pnt_cnt] for part_pnt_start, part_pnt_cnt in parts) + ) ) - ) return GeoDataFrame( { "bl_id":bl_line_ids, "name":names, - "geometry":multi_lines + "geometry":geoms }, geometry="geometry", crs=self.projection() @@ -223,20 +229,23 @@ def refinement_regions(self) -> GeoDataFrame: rr_data = self["/Geometry/2D Flow Area Refinement Regions"] rr_ids = range(rr_data["Attributes"][()].shape[0]) names = np.vectorize(convert_ras_hdf_string)(rr_data["Attributes"][()]["Name"]) - multi_polygons = list() + geoms = list() for pnt_start, pnt_cnt, part_start, part_cnt in rr_data["Polygon Info"][()]: points = rr_data["Polygon Points"][()][pnt_start:pnt_start+pnt_cnt] - parts = rr_data["Polygon Parts"][()][part_start:part_start+part_cnt] - multi_polygons.append( - MultiLineString( - list(points[part_pnt_start:part_pnt_start+part_pnt_cnt] for part_pnt_start, part_pnt_cnt in parts) + if part_cnt == 1: + geoms.append(Polygon(points)) + else: + parts = rr_data["Polygon Parts"][()][part_start:part_start+part_cnt] + geoms.append( + MultiPolygon( + list(points[part_pnt_start:part_pnt_start+part_pnt_cnt] for part_pnt_start, part_pnt_cnt in parts) + ) ) - ) return GeoDataFrame( { "rr_id":rr_ids, "name":names, - "geometry":multi_polygons + "geometry":geoms }, geometry="geometry", crs=self.projection() diff --git a/tests/data/json/bc_lines.json b/tests/data/json/bc_lines.json index aa15afe..9bc1388 100644 --- a/tests/data/json/bc_lines.json +++ b/tests/data/json/bc_lines.json @@ -1 +1 @@ -{"type": "FeatureCollection", "features": [{"id": "0", "type": "Feature", "properties": {"bc_line_id": 0, "name": "2d_out", "mesh_name": "2D Interior Area", "type": "External"}, "geometry": {"type": "MultiLineString", "coordinates": [[[406023.42, 1801556.29], [406029.95, 1801947.71]]]}}, {"id": "1", "type": "Feature", "properties": {"bc_line_id": 1, "name": "NW_out", "mesh_name": "Perimeter_NW", "type": "External"}, "geometry": {"type": "MultiLineString", "coordinates": [[[403925.27, 1802019.54], [403620.03, 1802568.0]]]}}], "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG::2965"}}} \ No newline at end of file +{"type": "FeatureCollection", "features": [{"id": "0", "type": "Feature", "properties": {"bc_line_id": 0, "name": "2d_out", "mesh_name": "2D Interior Area", "type": "External"}, "geometry": {"type": "LineString", "coordinates": [[406023.42, 1801556.29], [406029.95, 1801947.71]]}}, {"id": "1", "type": "Feature", "properties": {"bc_line_id": 1, "name": "NW_out", "mesh_name": "Perimeter_NW", "type": "External"}, "geometry": {"type": "LineString", "coordinates": [[403925.27, 1802019.54], [403620.03, 1802568.0]]}}], "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG::2965"}}} \ No newline at end of file diff --git a/tests/data/json/breaklines.json b/tests/data/json/breaklines.json index 72bd742..31c718c 100644 --- a/tests/data/json/breaklines.json +++ b/tests/data/json/breaklines.json @@ -1 +1 @@ -{"type": "FeatureCollection", "features": [{"id": "0", "type": "Feature", "properties": {"bl_id": 0, "name": "Road 1"}, "geometry": {"type": "MultiLineString", "coordinates": [[[409074.78, 1802876.36], [409026.14, 1802817.66], [408942.28, 1802769.02], [408870.17, 1802745.54], [408828.24, 1802740.51], [408806.44, 1802720.39], [408774.57, 1802686.84], [408719.22, 1802626.47], [408695.74, 1802596.28], [408695.74, 1802564.41], [408727.61, 1802530.87], [408781.28, 1802463.78], [408819.85, 1802436.95], [408943.96, 1802418.5], [409019.43, 1802416.82], [409051.3, 1802396.7], [409026.14, 1802322.9], [409006.02, 1802249.11], [409019.43, 1802116.61], [409007.69, 1802026.05], [409002.66, 1801870.07]]]}}, {"id": "1", "type": "Feature", "properties": {"bl_id": 1, "name": "HighGround 1"}, "geometry": {"type": "MultiLineString", "coordinates": [[[410305.8, 1801586.64], [410290.71, 1801675.53], [410240.39, 1801740.93], [410134.73, 1801789.57], [410015.65, 1801878.46], [409901.61, 1801963.99], [409871.42, 1802052.88], [409849.62, 1802158.54], [409837.88, 1802265.88], [409804.33, 1802336.32], [409737.25, 1802374.89], [409650.04, 1802391.67], [409594.69, 1802405.08], [409537.67, 1802405.08], [409484.0, 1802395.02], [409430.33, 1802401.73], [409374.99, 1802423.53], [409334.74, 1802426.89], [409271.0, 1802416.82], [409232.43, 1802406.76], [409123.42, 1802410.11], [409063.04, 1802410.11]]]}}, {"id": "2", "type": "Feature", "properties": {"bl_id": 2, "name": "Breakline 1"}, "geometry": {"type": "MultiLineString", "coordinates": [[[410388.19, 1802731.41], [410547.77, 1802837.8], [410735.99, 1802907.36], [410891.48, 1802911.45], [411267.92, 1802874.62]]]}}, {"id": "3", "type": "Feature", "properties": {"bl_id": 3, "name": "Breakline 2"}, "geometry": {"type": "MultiLineString", "coordinates": [[[402838.91, 1804637.9], [402791.97, 1804500.22], [402879.58, 1804431.38], [402820.13, 1804362.54], [402557.28, 1804346.89], [402375.79, 1804315.6], [402350.76, 1804231.11], [402375.79, 1804059.01], [402463.41, 1804002.69], [402641.77, 1803987.04], [402782.58, 1804005.82], [402798.23, 1803918.2]]]}}], "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG::2965"}}} \ No newline at end of file +{"type": "FeatureCollection", "features": [{"id": "0", "type": "Feature", "properties": {"bl_id": 0, "name": "Road 1"}, "geometry": {"type": "LineString", "coordinates": [[409074.78, 1802876.36], [409026.14, 1802817.66], [408942.28, 1802769.02], [408870.17, 1802745.54], [408828.24, 1802740.51], [408806.44, 1802720.39], [408774.57, 1802686.84], [408719.22, 1802626.47], [408695.74, 1802596.28], [408695.74, 1802564.41], [408727.61, 1802530.87], [408781.28, 1802463.78], [408819.85, 1802436.95], [408943.96, 1802418.5], [409019.43, 1802416.82], [409051.3, 1802396.7], [409026.14, 1802322.9], [409006.02, 1802249.11], [409019.43, 1802116.61], [409007.69, 1802026.05], [409002.66, 1801870.07]]}}, {"id": "1", "type": "Feature", "properties": {"bl_id": 1, "name": "HighGround 1"}, "geometry": {"type": "LineString", "coordinates": [[410305.8, 1801586.64], [410290.71, 1801675.53], [410240.39, 1801740.93], [410134.73, 1801789.57], [410015.65, 1801878.46], [409901.61, 1801963.99], [409871.42, 1802052.88], [409849.62, 1802158.54], [409837.88, 1802265.88], [409804.33, 1802336.32], [409737.25, 1802374.89], [409650.04, 1802391.67], [409594.69, 1802405.08], [409537.67, 1802405.08], [409484.0, 1802395.02], [409430.33, 1802401.73], [409374.99, 1802423.53], [409334.74, 1802426.89], [409271.0, 1802416.82], [409232.43, 1802406.76], [409123.42, 1802410.11], [409063.04, 1802410.11]]}}, {"id": "2", "type": "Feature", "properties": {"bl_id": 2, "name": "Breakline 1"}, "geometry": {"type": "LineString", "coordinates": [[410388.19, 1802731.41], [410547.77, 1802837.8], [410735.99, 1802907.36], [410891.48, 1802911.45], [411267.92, 1802874.62]]}}, {"id": "3", "type": "Feature", "properties": {"bl_id": 3, "name": "Breakline 2"}, "geometry": {"type": "LineString", "coordinates": [[402838.91, 1804637.9], [402791.97, 1804500.22], [402879.58, 1804431.38], [402820.13, 1804362.54], [402557.28, 1804346.89], [402375.79, 1804315.6], [402350.76, 1804231.11], [402375.79, 1804059.01], [402463.41, 1804002.69], [402641.77, 1803987.04], [402782.58, 1804005.82], [402798.23, 1803918.2]]}}], "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG::2965"}}} \ No newline at end of file diff --git a/tests/data/json/refinement_regions.json b/tests/data/json/refinement_regions.json index 5aabf5a..b91337f 100644 --- a/tests/data/json/refinement_regions.json +++ b/tests/data/json/refinement_regions.json @@ -1 +1 @@ -{"type": "FeatureCollection", "features": [{"id": "0", "type": "Feature", "properties": {"rr_id": 0, "name": "Region 1"}, "geometry": {"type": "MultiLineString", "coordinates": [[[403466.64123541984, 1804195.9021819197], [403449.7976758183, 1804409.253936872], [403607.00423209905, 1804515.9298143482], [403865.27214598877, 1804549.6169335512], [403955.1044638635, 1804392.4103772705], [403932.6463843948, 1804201.5167017868], [403792.2833877156, 1804083.6117845762], [403646.3058711692, 1804061.1537051075], [403539.62999369303, 1804094.8408243107], [403466.64123541984, 1804195.9021819197]]]}}, {"id": "1", "type": "Feature", "properties": {"rr_id": 1, "name": "Region 2"}, "geometry": {"type": "MultiLineString", "coordinates": [[[408105.7632410168, 1803317.9615429805], [408201.9074582713, 1803374.3219461986], [408404.14184628957, 1803251.6551862531], [408397.51121061685, 1803065.9973874167], [408500.2860635441, 1802926.7540382894], [408599.74559863505, 1802837.2404567075], [408719.09704074415, 1802777.564735653], [408735.673629926, 1802731.1502859439], [408652.7906840169, 1802648.267340035], [408503.6013813805, 1802628.3754330166], [408288.10572201683, 1802717.8890145984], [408155.49300856225, 1802850.501728053], [408049.4028377986, 1802946.6459453076], [407979.7811632349, 1803029.5288912167], [407963.2045740531, 1803158.826286835], [408022.88029510767, 1803251.6551862531], [408105.7632410168, 1803317.9615429805]]]}}, {"id": "2", "type": "Feature", "properties": {"rr_id": 2, "name": "Region 3"}, "geometry": {"type": "MultiLineString", "coordinates": [[[410254.0891989809, 1802329.9968277444], [410366.81000541727, 1802356.5193704353], [410615.4588431446, 1802366.4653239443], [410761.3328279446, 1802313.4202385624], [410874.053634381, 1802220.5913391444], [410814.37791332643, 1802071.4020365078], [410648.6120215082, 1802005.0956797807], [410532.5758972355, 1802011.7263154534], [410356.86405190814, 1802078.0326721806], [410277.2964238354, 1802187.4381607806], [410254.0891989809, 1802329.9968277444]]]}}], "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG::2965"}}} \ No newline at end of file +{"type": "FeatureCollection", "features": [{"id": "0", "type": "Feature", "properties": {"rr_id": 0, "name": "Region 1"}, "geometry": {"type": "Polygon", "coordinates": [[[403466.64123541984, 1804195.9021819197], [403449.7976758183, 1804409.253936872], [403607.00423209905, 1804515.9298143482], [403865.27214598877, 1804549.6169335512], [403955.1044638635, 1804392.4103772705], [403932.6463843948, 1804201.5167017868], [403792.2833877156, 1804083.6117845762], [403646.3058711692, 1804061.1537051075], [403539.62999369303, 1804094.8408243107], [403466.64123541984, 1804195.9021819197]]]}}, {"id": "1", "type": "Feature", "properties": {"rr_id": 1, "name": "Region 2"}, "geometry": {"type": "Polygon", "coordinates": [[[408105.7632410168, 1803317.9615429805], [408201.9074582713, 1803374.3219461986], [408404.14184628957, 1803251.6551862531], [408397.51121061685, 1803065.9973874167], [408500.2860635441, 1802926.7540382894], [408599.74559863505, 1802837.2404567075], [408719.09704074415, 1802777.564735653], [408735.673629926, 1802731.1502859439], [408652.7906840169, 1802648.267340035], [408503.6013813805, 1802628.3754330166], [408288.10572201683, 1802717.8890145984], [408155.49300856225, 1802850.501728053], [408049.4028377986, 1802946.6459453076], [407979.7811632349, 1803029.5288912167], [407963.2045740531, 1803158.826286835], [408022.88029510767, 1803251.6551862531], [408105.7632410168, 1803317.9615429805]]]}}, {"id": "2", "type": "Feature", "properties": {"rr_id": 2, "name": "Region 3"}, "geometry": {"type": "Polygon", "coordinates": [[[410254.0891989809, 1802329.9968277444], [410366.81000541727, 1802356.5193704353], [410615.4588431446, 1802366.4653239443], [410761.3328279446, 1802313.4202385624], [410874.053634381, 1802220.5913391444], [410814.37791332643, 1802071.4020365078], [410648.6120215082, 1802005.0956797807], [410532.5758972355, 1802011.7263154534], [410356.86405190814, 1802078.0326721806], [410277.2964238354, 1802187.4381607806], [410254.0891989809, 1802329.9968277444]]]}}], "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG::2965"}}} \ No newline at end of file From f7c6d0c276ae686ab4d4babc588cfa37bd3326f2 Mon Sep 17 00:00:00 2001 From: Thomas Williams Date: Tue, 30 Apr 2024 11:55:02 -0400 Subject: [PATCH 06/13] pre-commit with ruff --- .pre-commit-config.yaml | 9 +++++++++ README.md | 12 +++++++++++- pyproject.toml | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..dad7062 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,9 @@ +repos: +- repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.4.2 + hooks: + # Run the linter. + - id: ruff + # Run the formatter. + - id: ruff-format \ No newline at end of file diff --git a/README.md b/README.md index 7e7bd49..caf6ca7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # rashdf Read data from HEC-RAS HDF files. -## Setup +## Developer Setup Create a virtual environment in the project directory: ``` $ python -m venv venv-rashdf @@ -13,6 +13,16 @@ $ source ./venv/bin/activate (venv-rashdf) $ ``` +Install dev dependencies: +``` +(venv-rashdf) $ pip install ".[dev]" +``` + +Install git hook scripts (used for automatic liniting/formatting) +``` +(venv-rashdf) $ pre-commit install +``` + With the virtual environment activated, run the tests: ``` (venv-rashdf) $ pytest diff --git a/pyproject.toml b/pyproject.toml index 053bf48..bd64e0d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ version = "0.1.0-beta.1" dependencies = ["h5py", "geopandas"] [project.optional-dependencies] -dev = ["pytest"] +dev = ["pre-commit", "ruff", "pytest"] [project.urls] repository = "https://github.com/fema-ffrd/rashdf" From c9977adf22c3f3db35bd24e223c9e2ceda0e7a6a Mon Sep 17 00:00:00 2001 From: Thomas Williams Date: Tue, 30 Apr 2024 13:40:18 -0400 Subject: [PATCH 07/13] fix ruff linting/formatting issues --- .github/workflows/continuous-integration.yml | 5 + src/rashdf/__init__.py | 2 + src/rashdf/base.py | 9 +- src/rashdf/geom.py | 115 +++++++++++++------ src/rashdf/plan.py | 1 - src/rashdf/utils.py | 4 +- tests/test_geom.py | 8 +- tests/test_utils.py | 8 +- 8 files changed, 107 insertions(+), 45 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index a93f864..b4ccdbb 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -40,6 +40,11 @@ jobs: python -m pip install --upgrade pip pip install ".[dev]" + - name: Lint (ruff) + run: | + ruff + ruff-format --check + - name: Test with pytest run: | pytest diff --git a/src/rashdf/__init__.py b/src/rashdf/__init__.py index 8f03a64..21b0045 100644 --- a/src/rashdf/__init__.py +++ b/src/rashdf/__init__.py @@ -1,3 +1,5 @@ from .base import RasHdf from .geom import RasGeomHdf from .plan import RasPlanHdf + +__all__ = ["RasHdf", "RasGeomHdf", "RasPlanHdf"] diff --git a/src/rashdf/base.py b/src/rashdf/base.py index 4b1aaab..7e97232 100644 --- a/src/rashdf/base.py +++ b/src/rashdf/base.py @@ -6,7 +6,7 @@ class RasHdf(h5py.File): def __init__(self, name: str, **kwargs): """Open a HEC-RAS HDF file. - + Parameters ---------- name : str @@ -17,7 +17,9 @@ def __init__(self, name: str, **kwargs): super().__init__(name, mode="r", **kwargs) @classmethod - def open_uri(cls, uri: str, fsspec_kwargs: dict = {}, h5py_kwargs: dict = {}) -> 'RasHdf': + def open_uri( + cls, uri: str, fsspec_kwargs: dict = {}, h5py_kwargs: dict = {} + ) -> "RasHdf": """Open a HEC-RAS HDF file from a URI. Parameters @@ -41,5 +43,6 @@ def open_uri(cls, uri: str, fsspec_kwargs: dict = {}, h5py_kwargs: dict = {}) -> >>> results_hdf = RasHdf.open_uri("s3://my-bucket/results.hdf") """ import fsspec + remote_file = fsspec.open(uri, mode="rb", **fsspec_kwargs) - return cls(remote_file.open(), **h5py_kwargs) \ No newline at end of file + return cls(remote_file.open(), **h5py_kwargs) diff --git a/src/rashdf/geom.py b/src/rashdf/geom.py index 725b6cd..464272f 100644 --- a/src/rashdf/geom.py +++ b/src/rashdf/geom.py @@ -2,20 +2,18 @@ from .utils import convert_ras_hdf_string import numpy as np -import pandas as pd from geopandas import GeoDataFrame from pyproj import CRS from shapely import Polygon, Point, LineString, MultiLineString, MultiPolygon, polygonize -from typing import Optional +from typing import List, Optional class RasGeomHdf(RasHdf): - def projection(self) -> Optional[CRS]: """Return the projection of the RAS geometry as a pyproj.CRS object. - + Returns ------- CRS @@ -24,14 +22,14 @@ def projection(self) -> Optional[CRS]: proj_wkt = self.attrs.get("Projection") if proj_wkt is None: return None - if type(proj_wkt) == bytes or type(proj_wkt) == np.bytes_: + if isinstance(proj_wkt, bytes) or isinstance(proj_wkt, np.bytes_): proj_wkt = proj_wkt.decode("utf-8") return CRS.from_wkt(proj_wkt) - - def mesh_area_names(self) -> list: - """Return a list of the 2D mesh area names of + + def mesh_area_names(self) -> List[str]: + """Return a list of the 2D mesh area names of the RAS geometry. - + Returns ------- list @@ -39,11 +37,16 @@ def mesh_area_names(self) -> list: """ if "/Geometry/2D Flow Areas" not in self: return list() - return list([convert_ras_hdf_string(n) for n in self["/Geometry/2D Flow Areas/Attributes"][()]["Name"]]) + return list( + [ + convert_ras_hdf_string(n) + for n in self["/Geometry/2D Flow Areas/Attributes"][()]["Name"] + ] + ) def mesh_areas(self) -> GeoDataFrame: """Return 2D flow area perimeter polygons. - + Returns ------- GeoDataFrame @@ -52,12 +55,19 @@ def mesh_areas(self) -> GeoDataFrame: mesh_area_names = self.mesh_area_names() if not mesh_area_names: return GeoDataFrame() - mesh_area_polygons = [Polygon(self[f"/Geometry/2D Flow Areas/{n}/Perimeter"][()]) for n in mesh_area_names] - return GeoDataFrame({"mesh_name" : mesh_area_names, "geometry" : mesh_area_polygons}, geometry="geometry", crs=self.projection()) + mesh_area_polygons = [ + Polygon(self[f"/Geometry/2D Flow Areas/{n}/Perimeter"][()]) + for n in mesh_area_names + ] + return GeoDataFrame( + {"mesh_name": mesh_area_names, "geometry": mesh_area_polygons}, + geometry="geometry", + crs=self.projection(), + ) def mesh_cell_polygons(self) -> GeoDataFrame: """Return the 2D flow mesh cell polygons. - + Returns ------- GeoDataFrame @@ -69,30 +79,49 @@ def mesh_cell_polygons(self) -> GeoDataFrame: face_gdf = self.mesh_cell_faces() - cell_dict = {"mesh_name":[], "cell_id":[], "geometry":[]} + cell_dict = {"mesh_name": [], "cell_id": [], "geometry": []} for i, mesh_name in enumerate(mesh_area_names): cell_cnt = self["/Geometry/2D Flow Areas/Cell Info"][()][i][1] cell_ids = list(range(cell_cnt)) - cell_face_info = self[f"/Geometry/2D Flow Areas/{mesh_name}/Cells Face and Orientation Info"][()] - cell_face_values = self[f"/Geometry/2D Flow Areas/{mesh_name}/Cells Face and Orientation Values"][()][:,0] + cell_face_info = self[ + f"/Geometry/2D Flow Areas/{mesh_name}/Cells Face and Orientation Info" + ][()] + cell_face_values = self[ + f"/Geometry/2D Flow Areas/{mesh_name}/Cells Face and Orientation Values" + ][()][:, 0] face_id_lists = list( np.vectorize( - lambda cell_id: str(cell_face_values[cell_face_info[cell_id][0]:cell_face_info[cell_id][0]+cell_face_info[cell_id][1]]) + lambda cell_id: str( + cell_face_values[ + cell_face_info[cell_id][0] : cell_face_info[cell_id][0] + + cell_face_info[cell_id][1] + ] + ) )(cell_ids) ) - mesh_faces = face_gdf[face_gdf.mesh_name == mesh_name][["face_id", "geometry"]].set_index("face_id").to_numpy() - cell_dict["mesh_name"] += [mesh_name]*cell_cnt + mesh_faces = ( + face_gdf[face_gdf.mesh_name == mesh_name][["face_id", "geometry"]] + .set_index("face_id") + .to_numpy() + ) + cell_dict["mesh_name"] += [mesh_name] * cell_cnt cell_dict["cell_id"] += cell_ids cell_dict["geometry"] += list( np.vectorize( - lambda face_id_list: polygonize(np.ravel(mesh_faces[np.array(face_id_list.strip("[]").split()).astype(int)])).geoms[0] + lambda face_id_list: polygonize( + np.ravel( + mesh_faces[ + np.array(face_id_list.strip("[]").split()).astype(int) + ] + ) + ).geoms[0] )(face_id_lists) ) return GeoDataFrame(cell_dict, geometry="geometry", crs=self.projection()) def mesh_cell_points(self) -> GeoDataFrame: """Return the 2D flow mesh cell points. - + Returns ------- GeoDataFrame @@ -101,18 +130,24 @@ def mesh_cell_points(self) -> GeoDataFrame: mesh_area_names = self.mesh_area_names() if not mesh_area_names: return GeoDataFrame() - pnt_dict = {"mesh_name":[], "cell_id":[], "geometry":[]} + pnt_dict = {"mesh_name": [], "cell_id": [], "geometry": []} for i, mesh_name in enumerate(mesh_area_names): starting_row, count = self["/Geometry/2D Flow Areas/Cell Info"][()][i] - cell_pnt_coords = self["/Geometry/2D Flow Areas/Cell Points"][()][starting_row:starting_row+count] - pnt_dict["mesh_name"] += [mesh_name]*cell_pnt_coords.shape[0] + cell_pnt_coords = self["/Geometry/2D Flow Areas/Cell Points"][()][ + starting_row : starting_row + count + ] + pnt_dict["mesh_name"] += [mesh_name] * cell_pnt_coords.shape[0] pnt_dict["cell_id"] += range(count) - pnt_dict["geometry"] += list(np.vectorize(lambda coords: Point(coords), signature="(n)->()")(cell_pnt_coords)) + pnt_dict["geometry"] += list( + np.vectorize(lambda coords: Point(coords), signature="(n)->()")( + cell_pnt_coords + ) + ) return GeoDataFrame(pnt_dict, geometry="geometry", crs=self.projection()) def mesh_cell_faces(self) -> GeoDataFrame: """Return the 2D flow mesh cell faces. - + Returns ------- GeoDataFrame @@ -121,22 +156,32 @@ def mesh_cell_faces(self) -> GeoDataFrame: mesh_area_names = self.mesh_area_names() if not mesh_area_names: return GeoDataFrame() - face_dict = {"mesh_name":[], "face_id":[], "geometry":[]} + face_dict = {"mesh_name": [], "face_id": [], "geometry": []} for mesh_name in mesh_area_names: - facepoints_index = self[f"/Geometry/2D Flow Areas/{mesh_name}/Faces FacePoint Indexes"][()] - facepoints_coordinates = self[f"/Geometry/2D Flow Areas/{mesh_name}/FacePoints Coordinate"][()] - faces_perimeter_info = self[f"/Geometry/2D Flow Areas/{mesh_name}/Faces Perimeter Info"][()] - faces_perimeter_values = self[f"/Geometry/2D Flow Areas/{mesh_name}/Faces Perimeter Values"][()] + facepoints_index = self[ + f"/Geometry/2D Flow Areas/{mesh_name}/Faces FacePoint Indexes" + ][()] + facepoints_coordinates = self[ + f"/Geometry/2D Flow Areas/{mesh_name}/FacePoints Coordinate" + ][()] + faces_perimeter_info = self[ + f"/Geometry/2D Flow Areas/{mesh_name}/Faces Perimeter Info" + ][()] + faces_perimeter_values = self[ + f"/Geometry/2D Flow Areas/{mesh_name}/Faces Perimeter Values" + ][()] face_id = -1 for pnt_a_index, pnt_b_index in facepoints_index: - face_id+=1 + face_id += 1 face_dict["mesh_name"].append(mesh_name) face_dict["face_id"].append(face_id) coordinates = list() coordinates.append(facepoints_coordinates[pnt_a_index]) starting_row, count = faces_perimeter_info[face_id] if count > 0: - coordinates += list(faces_perimeter_values[starting_row:starting_row+count]) + coordinates += list( + faces_perimeter_values[starting_row : starting_row + count] + ) coordinates.append(facepoints_coordinates[pnt_b_index]) face_dict["geometry"].append(LineString(coordinates)) return GeoDataFrame(face_dict, geometry="geometry", crs=self.projection()) @@ -289,7 +334,7 @@ def flowpaths(self) -> GeoDataFrame: def bank_points(self) -> GeoDataFrame: raise NotImplementedError - + def bank_lines(self) -> GeoDataFrame: raise NotImplementedError diff --git a/src/rashdf/plan.py b/src/rashdf/plan.py index 3e007ed..a64ec57 100644 --- a/src/rashdf/plan.py +++ b/src/rashdf/plan.py @@ -4,6 +4,5 @@ class RasPlanHdf(RasGeomHdf): - def enroachment_points(self) -> GeoDataFrame: raise NotImplementedError diff --git a/src/rashdf/utils.py b/src/rashdf/utils.py index 218ed28..0d6dc81 100644 --- a/src/rashdf/utils.py +++ b/src/rashdf/utils.py @@ -40,7 +40,7 @@ def parse_ras_simulation_window_datetime(datetime_str) -> datetime: def parse_run_time_window(window: str) -> Tuple[datetime, datetime]: """ Parse a run time window string into a tuple of datetime objects. - + Parameters ---------- window (str): The run time window string to be parsed. @@ -167,4 +167,4 @@ def convert_ras_hdf_value( # Convert all other types to string else: - return str(value) \ No newline at end of file + return str(value) diff --git a/tests/test_geom.py b/tests/test_geom.py index 964095e..2fa3809 100644 --- a/tests/test_geom.py +++ b/tests/test_geom.py @@ -6,6 +6,7 @@ TEST_DATA = Path("./tests/data") + def test_projection(tmp_path): wkt = 'PROJCS["Albers_Conic_Equal_Area",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Albers"],PARAMETER["false_easting",0.0],PARAMETER["false_northing",0.0],PARAMETER["central_meridian",-96.0],PARAMETER["standard_parallel_1",29.5],PARAMETER["standard_parallel_2",45.5],PARAMETER["latitude_of_origin",37.5],UNIT["Foot_US",0.3048006096012192]]' # Create a dummy HDF file @@ -16,29 +17,34 @@ def test_projection(tmp_path): # Test the projection assert ras_hdf.projection() == CRS.from_wkt(wkt) + def test_mesh_area_names(): geom = TEST_DATA / "ras/Muncie.g05.hdf" with RasGeomHdf(geom) as ghdf: assert ghdf.mesh_area_names() == ["2D Interior Area", "Perimeter_NW"] + def test_mesh_areas(): geom = TEST_DATA / "ras/Muncie.g05.hdf" with RasGeomHdf(geom) as ghdf: with open(TEST_DATA / "json/mesh_areas.json") as json: assert ghdf.mesh_areas().to_json() == json.read() + def test_mesh_cell_faces(): geom = TEST_DATA / "ras/Muncie.g05.hdf" with RasGeomHdf(geom) as ghdf: with open(TEST_DATA / "json/mesh_cell_faces.json") as json: assert ghdf.mesh_cell_faces().to_json() == json.read() + def test_mesh_cell_points(): geom = TEST_DATA / "ras/Muncie.g05.hdf" with RasGeomHdf(geom) as ghdf: with open(TEST_DATA / "json/mesh_cell_points.json") as json: assert ghdf.mesh_cell_points().to_json() == json.read() + def test_mesh_cell_polygons(): geom = TEST_DATA / "ras/Muncie.g05.hdf" with RasGeomHdf(geom) as ghdf: @@ -61,4 +67,4 @@ def test_refinement_regions(): geom = TEST_DATA / "ras/Muncie.g05.hdf" with RasGeomHdf(geom) as ghdf: with open(TEST_DATA / "json/refinement_regions.json") as json: - assert ghdf.refinement_regions().to_json() == json.read() \ No newline at end of file + assert ghdf.refinement_regions().to_json() == json.read() diff --git a/tests/test_utils.py b/tests/test_utils.py index 3dee3d0..14f820b 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -7,11 +7,13 @@ def test_convert_ras_hdf_value(): - assert utils.convert_ras_hdf_value(b"True") == True - assert utils.convert_ras_hdf_value(b"False") == False + assert utils.convert_ras_hdf_value(b"True") is True + assert utils.convert_ras_hdf_value(b"False") is False assert utils.convert_ras_hdf_value(np.float32(1.23)) == pytest.approx(1.23) assert utils.convert_ras_hdf_value(np.int32(123)) == 123 - assert utils.convert_ras_hdf_value(b"15Mar2024 16:39:01") == datetime(2024, 3, 15, 16, 39, 1) + assert utils.convert_ras_hdf_value(b"15Mar2024 16:39:01") == datetime( + 2024, 3, 15, 16, 39, 1 + ) assert utils.convert_ras_hdf_value(b"15Mar2024 16:39:01 to 16Mar2024 16:39:01") == [ datetime(2024, 3, 15, 16, 39, 1), datetime(2024, 3, 16, 16, 39, 1), From 84c6721f7caed1bec0e17bb702ccca1918b5d180 Mon Sep 17 00:00:00 2001 From: Thomas Williams Date: Tue, 30 Apr 2024 14:14:06 -0400 Subject: [PATCH 08/13] CI workflow badge --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index caf6ca7..b3d5777 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,15 @@ # rashdf +[![CI](https://github.com/fema-ffrd/rashdf/actions/workflows/continuous-integration.yml/badge.svg?branch=main)](https://github.com/fema-ffrd/rashdf/actions/workflows/continuous-integration.yml) +[![Release](https://github.com/fema-ffrd/rashdf/actions/workflows/release.yml/badge.svg)](https://github.com/fema-ffrd/rashdf/actions/workflows/release.yml) +[![PyPI version](https://badge.fury.io/py/rashdf.svg)](https://badge.fury.io/py/rashdf) + Read data from HEC-RAS HDF files. +## Install +```bash +$ pip install rashdf=0.1.0b1 +``` + ## Developer Setup Create a virtual environment in the project directory: ``` From bc0a2560ab55610399a804d250a33f3cd593f47c Mon Sep 17 00:00:00 2001 From: Thomas Williams Date: Tue, 30 Apr 2024 15:00:12 -0400 Subject: [PATCH 09/13] refactor geom tests --- tests/test_geom.py | 60 +++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/tests/test_geom.py b/tests/test_geom.py index 2fa3809..a29b08e 100644 --- a/tests/test_geom.py +++ b/tests/test_geom.py @@ -1,10 +1,13 @@ from src.rashdf import RasGeomHdf import h5py +from geopandas import GeoDataFrame from pyproj import CRS from pathlib import Path TEST_DATA = Path("./tests/data") +MUNCIE_G05 = TEST_DATA / "ras/Muncie.g05.hdf" +TEST_JSON = TEST_DATA / "json" def test_projection(tmp_path): @@ -18,53 +21,50 @@ def test_projection(tmp_path): assert ras_hdf.projection() == CRS.from_wkt(wkt) +def _gdf_matches_json(gdf: GeoDataFrame, json_file: Path) -> bool: + with open(json_file) as j: + return gdf.to_json() == j.read() + + def test_mesh_area_names(): - geom = TEST_DATA / "ras/Muncie.g05.hdf" - with RasGeomHdf(geom) as ghdf: + with RasGeomHdf(MUNCIE_G05) as ghdf: assert ghdf.mesh_area_names() == ["2D Interior Area", "Perimeter_NW"] def test_mesh_areas(): - geom = TEST_DATA / "ras/Muncie.g05.hdf" - with RasGeomHdf(geom) as ghdf: - with open(TEST_DATA / "json/mesh_areas.json") as json: - assert ghdf.mesh_areas().to_json() == json.read() + mesh_areas_json = TEST_JSON / "mesh_areas.json" + with RasGeomHdf(MUNCIE_G05) as ghdf: + assert _gdf_matches_json(ghdf.mesh_areas(), mesh_areas_json) def test_mesh_cell_faces(): - geom = TEST_DATA / "ras/Muncie.g05.hdf" - with RasGeomHdf(geom) as ghdf: - with open(TEST_DATA / "json/mesh_cell_faces.json") as json: - assert ghdf.mesh_cell_faces().to_json() == json.read() + mesh_cell_faces_json = TEST_JSON / "mesh_cell_faces.json" + with RasGeomHdf(MUNCIE_G05) as ghdf: + assert _gdf_matches_json(ghdf.mesh_cell_faces(), mesh_cell_faces_json) def test_mesh_cell_points(): - geom = TEST_DATA / "ras/Muncie.g05.hdf" - with RasGeomHdf(geom) as ghdf: - with open(TEST_DATA / "json/mesh_cell_points.json") as json: - assert ghdf.mesh_cell_points().to_json() == json.read() + mesh_cell_points_json = TEST_JSON / "mesh_cell_points.json" + with RasGeomHdf(MUNCIE_G05) as ghdf: + assert _gdf_matches_json(ghdf.mesh_cell_points(), mesh_cell_points_json) def test_mesh_cell_polygons(): - geom = TEST_DATA / "ras/Muncie.g05.hdf" - with RasGeomHdf(geom) as ghdf: - with open(TEST_DATA / "json/mesh_cell_polygons.json") as json: - assert ghdf.mesh_cell_polygons().to_json() == json.read() + mesh_cell_polygons_json = TEST_JSON / "mesh_cell_polygons.json" + with RasGeomHdf(MUNCIE_G05) as ghdf: + assert _gdf_matches_json(ghdf.mesh_cell_polygons(), mesh_cell_polygons_json) def test_bc_lines(): - geom = TEST_DATA / "ras/Muncie.g05.hdf" - with RasGeomHdf(geom) as ghdf: - with open(TEST_DATA / "json/bc_lines.json") as json: - assert ghdf.bc_lines().to_json() == json.read() + bc_lines_json = TEST_JSON / "bc_lines.json" + with RasGeomHdf(MUNCIE_G05) as ghdf: + assert _gdf_matches_json(ghdf.bc_lines(), bc_lines_json) def test_breaklines(): - geom = TEST_DATA / "ras/Muncie.g05.hdf" - with RasGeomHdf(geom) as ghdf: - with open(TEST_DATA / "json/breaklines.json") as json: - assert ghdf.breaklines().to_json() == json.read() + breaklines_json = TEST_JSON / "breaklines.json" + with RasGeomHdf(MUNCIE_G05) as ghdf: + assert _gdf_matches_json(ghdf.breaklines(), breaklines_json) def test_refinement_regions(): - geom = TEST_DATA / "ras/Muncie.g05.hdf" - with RasGeomHdf(geom) as ghdf: - with open(TEST_DATA / "json/refinement_regions.json") as json: - assert ghdf.refinement_regions().to_json() == json.read() + rr_json = TEST_JSON / "refinement_regions.json" + with RasGeomHdf(MUNCIE_G05) as ghdf: + assert _gdf_matches_json(ghdf.refinement_regions(), rr_json) From 41576b1b414fcc2cc03cbfceb696319a4e89983c Mon Sep 17 00:00:00 2001 From: Thomas Williams Date: Tue, 30 Apr 2024 15:00:36 -0400 Subject: [PATCH 10/13] ruff settings for vscode --- .vscode/settings.json | 12 +++--------- src/rashdf/base.py | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 8108b2b..b1828e7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,16 +2,10 @@ "python.testing.pytestArgs": [ "." ], - "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, - "python.formatting.provider": "none", - "editor.formatOnSave": true, - "python.formatting.blackArgs": [ - "--line-length=120" - ], - "python.languageServer": "Pylance", - "python.linting.lintOnSave": true, + "python.testing.unittestEnabled": false, "[python]": { - "editor.defaultFormatter": "ms-python.black-formatter" + "editor.defaultFormatter": "charliermarsh.ruff", + "editor.formatOnSave": true } } \ No newline at end of file diff --git a/src/rashdf/base.py b/src/rashdf/base.py index 7e97232..c13b9cc 100644 --- a/src/rashdf/base.py +++ b/src/rashdf/base.py @@ -42,7 +42,7 @@ def open_uri( -------- >>> results_hdf = RasHdf.open_uri("s3://my-bucket/results.hdf") """ - import fsspec + import fsspec # type: ignore remote_file = fsspec.open(uri, mode="rb", **fsspec_kwargs) return cls(remote_file.open(), **h5py_kwargs) From d56b2c9bd704953726cc79a81f388ef802fed2b7 Mon Sep 17 00:00:00 2001 From: Thomas Williams Date: Tue, 30 Apr 2024 15:01:15 -0400 Subject: [PATCH 11/13] remove unused .devcontainer stuff for now --- .devcontainer/Dockerfile | 4 --- .devcontainer/README.md | 10 ------- .devcontainer/devcontainer.json | 47 --------------------------------- .devcontainer/env.yaml | 11 -------- README.md | 3 +++ 5 files changed, 3 insertions(+), 72 deletions(-) delete mode 100644 .devcontainer/Dockerfile delete mode 100644 .devcontainer/README.md delete mode 100644 .devcontainer/devcontainer.json delete mode 100644 .devcontainer/env.yaml diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index 9b87b6a..0000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -FROM mambaorg/micromamba:1.4.2 -COPY --chown=$MAMBA_USER:$MAMBA_USER env.yaml /tmp/env.yaml -RUN micromamba install -y -n base -f /tmp/env.yaml && \ - micromamba clean --all --yes \ No newline at end of file diff --git a/.devcontainer/README.md b/.devcontainer/README.md deleted file mode 100644 index 59d21b5..0000000 --- a/.devcontainer/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# FFRD Devcontainer Template -Devcontainers enable interactive development within a Docker container using VSCode. - -This devcontainer sets up a reproducible environment for Python projects using micromamba environments (a faster and more robust version of Conda). - -When you open this repository in VSCode, you might receive a prompt to re-open the project in a devcontainer. Alternatively, you can access this option through the View menu by selecting Command Palette and then choosing DevContainers: Reopen in Container. - -Additional requirements: -1. An environment file (env.yaml) is required in the .devcontainer folder. -2. Make sure you have a Docker engine installed locally (such as Docker Desktop). diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index 15e8d4a..0000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "Ubuntu", - // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile - "build": { - "dockerfile": "Dockerfile" - }, - "features": { - "ghcr.io/devcontainers/features/git:1": {}, - "ghcr.io/devcontainers/features/aws-cli:1": {} - }, - // mount aws credentials folder to dev container - "mounts": [ - "source=${localEnv:HOME}${localEnv:USERPROFILE}/.aws,target=/home/mambauser/.aws,type=bind,consistency=cached" - ], - // uncomment to enable gpu in the container - // "runArgs": [ - // "--gpus=all" - // ], - // Configure tool-specific properties. - "customizations": { - "settings": { - "python.defaultInterpreterPath": "/opt/conda/bin/python", - "python.linting.enabled": true, - "python.linting.pylintEnabled": true, - "editor.defaultFormatter": "ms-python.python", - "python.formatting.provider": "black", - "python.formatting.blackPath": "/opt/conda/bin/black", - "python.linting.pylintPath": "/opt/conda/bin/pylint", - "python.editor.defaultFormatter": "ms-python.black-formatter", - "editor.formatOnSave": true, - "python.languageServer": "Pylance", - "python.linting.lintOnSave": true, - "python.analysis.extraPaths": [ - "${workspaceFolder}/src" - ] - }, - "vscode": { - "extensions": [ - "ms-python.python", - "njpwerner.autodocstring", - "ms-python.pylint", - "github.copilot", - "ms-python.python" - ] - } - } -} \ No newline at end of file diff --git a/.devcontainer/env.yaml b/.devcontainer/env.yaml deleted file mode 100644 index 205d218..0000000 --- a/.devcontainer/env.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# base packages that can be added to. Updated by micromamba env export --from-history > env.yaml -name: base -channels: -- conda-forge -dependencies: - - black - - click - - pip - - pylint - - pytest - - python \ No newline at end of file diff --git a/README.md b/README.md index b3d5777..d16cf73 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,10 @@ Read data from HEC-RAS HDF files. + + ## Install +A prerelease version of `rashdf` is available from PyPI: ```bash $ pip install rashdf=0.1.0b1 ``` From a1c0a0515a69a350ca0cb7cbfc771c0513b161dd Mon Sep 17 00:00:00 2001 From: Thomas Williams Date: Tue, 30 Apr 2024 15:11:22 -0400 Subject: [PATCH 12/13] README tweaks --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d16cf73..ab02cf7 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,9 @@ [![Release](https://github.com/fema-ffrd/rashdf/actions/workflows/release.yml/badge.svg)](https://github.com/fema-ffrd/rashdf/actions/workflows/release.yml) [![PyPI version](https://badge.fury.io/py/rashdf.svg)](https://badge.fury.io/py/rashdf) -Read data from HEC-RAS HDF files. - +Read data from [HEC-RAS](https://www.hec.usace.army.mil/software/hec-ras/) [HDF](https://github.com/HDFGroup/hdf5) files. +*Pronunciation: `raz·aitch·dee·eff`* ## Install A prerelease version of `rashdf` is available from PyPI: From 02408cda5ef5baa969c576296bbad456c04af57e Mon Sep 17 00:00:00 2001 From: Thomas Williams Date: Tue, 30 Apr 2024 15:26:17 -0400 Subject: [PATCH 13/13] apply ruff formatting after rebase --- .github/workflows/continuous-integration.yml | 4 +- src/rashdf/geom.py | 90 ++++++++++++-------- tests/test_geom.py | 3 + 3 files changed, 61 insertions(+), 36 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index b4ccdbb..a56e6a5 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -42,8 +42,8 @@ jobs: - name: Lint (ruff) run: | - ruff - ruff-format --check + ruff check + ruff format --check - name: Test with pytest run: | diff --git a/src/rashdf/geom.py b/src/rashdf/geom.py index 464272f..8447342 100644 --- a/src/rashdf/geom.py +++ b/src/rashdf/geom.py @@ -4,7 +4,14 @@ import numpy as np from geopandas import GeoDataFrame from pyproj import CRS -from shapely import Polygon, Point, LineString, MultiLineString, MultiPolygon, polygonize +from shapely import ( + Polygon, + Point, + LineString, + MultiLineString, + MultiPolygon, + polygonize, +) from typing import List, Optional @@ -188,7 +195,7 @@ def mesh_cell_faces(self) -> GeoDataFrame: def bc_lines(self) -> GeoDataFrame: """Return the 2D mesh area boundary condition lines. - + Returns ------- GeoDataFrame @@ -203,32 +210,41 @@ def bc_lines(self) -> GeoDataFrame: mesh_names = v_conv_str(bc_line_data["Attributes"][()]["SA-2D"]) types = v_conv_str(bc_line_data["Attributes"][()]["Type"]) geoms = list() - for pnt_start, pnt_cnt, part_start, part_cnt in bc_line_data["Polyline Info"][()]: - points = bc_line_data["Polyline Points"][()][pnt_start:pnt_start+pnt_cnt] + for pnt_start, pnt_cnt, part_start, part_cnt in bc_line_data["Polyline Info"][ + () + ]: + points = bc_line_data["Polyline Points"][()][ + pnt_start : pnt_start + pnt_cnt + ] if part_cnt == 1: geoms.append(LineString(points)) else: - parts = bc_line_data["Polyline Parts"][()][part_start:part_start+part_cnt] + parts = bc_line_data["Polyline Parts"][()][ + part_start : part_start + part_cnt + ] geoms.append( MultiLineString( - list(points[part_pnt_start:part_pnt_start+part_pnt_cnt] for part_pnt_start, part_pnt_cnt in parts) + list( + points[part_pnt_start : part_pnt_start + part_pnt_cnt] + for part_pnt_start, part_pnt_cnt in parts + ) ) ) return GeoDataFrame( { - "bc_line_id":bc_line_ids, - "name":names, - "mesh_name":mesh_names, - "type":types, - "geometry":geoms + "bc_line_id": bc_line_ids, + "name": names, + "mesh_name": mesh_names, + "type": types, + "geometry": geoms, }, geometry="geometry", - crs=self.projection() + crs=self.projection(), ) def breaklines(self) -> GeoDataFrame: """Return the 2D mesh area breaklines. - + Returns ------- GeoDataFrame @@ -238,32 +254,39 @@ def breaklines(self) -> GeoDataFrame: return GeoDataFrame() bl_line_data = self["/Geometry/2D Flow Area Break Lines"] bl_line_ids = range(bl_line_data["Attributes"][()].shape[0]) - names = np.vectorize(convert_ras_hdf_string)(bl_line_data["Attributes"][()]["Name"]) + names = np.vectorize(convert_ras_hdf_string)( + bl_line_data["Attributes"][()]["Name"] + ) geoms = list() - for pnt_start, pnt_cnt, part_start, part_cnt in bl_line_data["Polyline Info"][()]: - points = bl_line_data["Polyline Points"][()][pnt_start:pnt_start+pnt_cnt] + for pnt_start, pnt_cnt, part_start, part_cnt in bl_line_data["Polyline Info"][ + () + ]: + points = bl_line_data["Polyline Points"][()][ + pnt_start : pnt_start + pnt_cnt + ] if part_cnt == 1: geoms.append(LineString(points)) else: - parts = bl_line_data["Polyline Parts"][()][part_start:part_start+part_cnt] + parts = bl_line_data["Polyline Parts"][()][ + part_start : part_start + part_cnt + ] geoms.append( MultiLineString( - list(points[part_pnt_start:part_pnt_start+part_pnt_cnt] for part_pnt_start, part_pnt_cnt in parts) + list( + points[part_pnt_start : part_pnt_start + part_pnt_cnt] + for part_pnt_start, part_pnt_cnt in parts + ) ) ) return GeoDataFrame( - { - "bl_id":bl_line_ids, - "name":names, - "geometry":geoms - }, + {"bl_id": bl_line_ids, "name": names, "geometry": geoms}, geometry="geometry", - crs=self.projection() + crs=self.projection(), ) def refinement_regions(self) -> GeoDataFrame: """Return the 2D mesh area refinement regions. - + Returns ------- GeoDataFrame @@ -276,24 +299,23 @@ def refinement_regions(self) -> GeoDataFrame: names = np.vectorize(convert_ras_hdf_string)(rr_data["Attributes"][()]["Name"]) geoms = list() for pnt_start, pnt_cnt, part_start, part_cnt in rr_data["Polygon Info"][()]: - points = rr_data["Polygon Points"][()][pnt_start:pnt_start+pnt_cnt] + points = rr_data["Polygon Points"][()][pnt_start : pnt_start + pnt_cnt] if part_cnt == 1: geoms.append(Polygon(points)) else: - parts = rr_data["Polygon Parts"][()][part_start:part_start+part_cnt] + parts = rr_data["Polygon Parts"][()][part_start : part_start + part_cnt] geoms.append( MultiPolygon( - list(points[part_pnt_start:part_pnt_start+part_pnt_cnt] for part_pnt_start, part_pnt_cnt in parts) + list( + points[part_pnt_start : part_pnt_start + part_pnt_cnt] + for part_pnt_start, part_pnt_cnt in parts + ) ) ) return GeoDataFrame( - { - "rr_id":rr_ids, - "name":names, - "geometry":geoms - }, + {"rr_id": rr_ids, "name": names, "geometry": geoms}, geometry="geometry", - crs=self.projection() + crs=self.projection(), ) def connections(self) -> GeoDataFrame: diff --git a/tests/test_geom.py b/tests/test_geom.py index a29b08e..8673727 100644 --- a/tests/test_geom.py +++ b/tests/test_geom.py @@ -54,16 +54,19 @@ def test_mesh_cell_polygons(): with RasGeomHdf(MUNCIE_G05) as ghdf: assert _gdf_matches_json(ghdf.mesh_cell_polygons(), mesh_cell_polygons_json) + def test_bc_lines(): bc_lines_json = TEST_JSON / "bc_lines.json" with RasGeomHdf(MUNCIE_G05) as ghdf: assert _gdf_matches_json(ghdf.bc_lines(), bc_lines_json) + def test_breaklines(): breaklines_json = TEST_JSON / "breaklines.json" with RasGeomHdf(MUNCIE_G05) as ghdf: assert _gdf_matches_json(ghdf.breaklines(), breaklines_json) + def test_refinement_regions(): rr_json = TEST_JSON / "refinement_regions.json" with RasGeomHdf(MUNCIE_G05) as ghdf: