From 00c766f58964d5169b1e454e54eabd9b442a884c Mon Sep 17 00:00:00 2001 From: Angus Date: Fri, 19 Jul 2024 13:01:48 +0100 Subject: [PATCH 1/3] Removed obselete warning --- wntr/network/io.py | 1 - 1 file changed, 1 deletion(-) diff --git a/wntr/network/io.py b/wntr/network/io.py index 83bca2cf0..eca53a44a 100644 --- a/wntr/network/io.py +++ b/wntr/network/io.py @@ -486,7 +486,6 @@ def write_inpfile(wn, filename: str, units=None, version: float = 2.2, """ if wn._inpfile is None: - logger.warning("Writing a minimal INP file without saved non-WNTR options (energy, etc.)") wn._inpfile = wntr.epanet.InpFile() if units is None: units = wn._options.hydraulic.inpfile_units From 31004a99f54fe7cbcace357cee725f78a4eaca40 Mon Sep 17 00:00:00 2001 From: angusmcb <58976795+angusmcb@users.noreply.github.com> Date: Mon, 12 Aug 2024 18:48:08 +0200 Subject: [PATCH 2/3] Fixed inconsistencies in geodataframe index names across io functions and gis. Added new tests to check for consistent index naming. Fix #433 - Gis files written with column title 'name' (#435) --------- Co-authored-by: Angus Co-authored-by: kbonney --- documentation/gis.rst | 41 ++++++++++++++++++++++------------------- wntr/gis/geospatial.py | 5 ++--- wntr/gis/network.py | 7 +++---- wntr/network/io.py | 6 +++--- wntr/tests/test_gis.py | 34 ++++++++++++++++++++++++++++++++-- 5 files changed, 62 insertions(+), 31 deletions(-) diff --git a/documentation/gis.rst b/documentation/gis.rst index bbf5959ca..e57d6886d 100644 --- a/documentation/gis.rst +++ b/documentation/gis.rst @@ -112,12 +112,13 @@ For example, the junctions GeoDataFrame contains the following information: :skipif: gpd is None >>> print(wn_gis.junctions.head()) - node_type elevation initial_quality geometry - 10 Junction 216.408 5.000e-04 POINT (20.00000 70.00000) - 11 Junction 216.408 5.000e-04 POINT (30.00000 70.00000) - 12 Junction 213.360 5.000e-04 POINT (50.00000 70.00000) - 13 Junction 211.836 5.000e-04 POINT (70.00000 70.00000) - 21 Junction 213.360 5.000e-04 POINT (30.00000 40.00000) + node_type elevation initial_quality geometry + name + 10 Junction 216.408 5.000e-04 POINT (20.00000 70.00000) + 11 Junction 216.408 5.000e-04 POINT (30.00000 70.00000) + 12 Junction 213.360 5.000e-04 POINT (50.00000 70.00000) + 13 Junction 211.836 5.000e-04 POINT (70.00000 70.00000) + 21 Junction 213.360 5.000e-04 POINT (30.00000 40.00000) Each GeoDataFrame contains attributes and geometry: @@ -333,21 +334,23 @@ and then translates the GeoDataFrames coordinates to EPSG:3857. >>> wn_gis = wntr.network.to_gis(wn, crs='EPSG:4326') >>> print(wn_gis.junctions.head()) - node_type elevation initial_quality geometry - 10 Junction 216.408 5.000e-04 POINT (20.00000 70.00000) - 11 Junction 216.408 5.000e-04 POINT (30.00000 70.00000) - 12 Junction 213.360 5.000e-04 POINT (50.00000 70.00000) - 13 Junction 211.836 5.000e-04 POINT (70.00000 70.00000) - 21 Junction 213.360 5.000e-04 POINT (30.00000 40.00000) - + node_type elevation initial_quality geometry + name + 10 Junction 216.408 5.000e-04 POINT (20.00000 70.00000) + 11 Junction 216.408 5.000e-04 POINT (30.00000 70.00000) + 12 Junction 213.360 5.000e-04 POINT (50.00000 70.00000) + 13 Junction 211.836 5.000e-04 POINT (70.00000 70.00000) + 21 Junction 213.360 5.000e-04 POINT (30.00000 40.00000) + >>> wn_gis.to_crs('EPSG:3857') >>> print(wn_gis.junctions.head()) - node_type elevation initial_quality geometry - 10 Junction 216.408 5.000e-04 POINT (2226389.816 11068715.659) - 11 Junction 216.408 5.000e-04 POINT (3339584.724 11068715.659) - 12 Junction 213.360 5.000e-04 POINT (5565974.540 11068715.659) - 13 Junction 211.836 5.000e-04 POINT (7792364.356 11068715.659) - 21 Junction 213.360 5.000e-04 POINT (3339584.724 4865942.280) + node_type elevation initial_quality geometry + name + 10 Junction 216.408 5.000e-04 POINT (2226389.816 11068715.659) + 11 Junction 216.408 5.000e-04 POINT (3339584.724 11068715.659) + 12 Junction 213.360 5.000e-04 POINT (5565974.540 11068715.659) + 13 Junction 211.836 5.000e-04 POINT (7792364.356 11068715.659) + 21 Junction 213.360 5.000e-04 POINT (3339584.724 4865942.280) Snap point geometries to the nearest point or line ---------------------------------------------------- diff --git a/wntr/gis/geospatial.py b/wntr/gis/geospatial.py index 860516fee..332b7af37 100644 --- a/wntr/gis/geospatial.py +++ b/wntr/gis/geospatial.py @@ -64,8 +64,7 @@ def snap(A, B, tolerance): assert A.crs == B.crs # Modify B to include "indexB" as a separate column - B = B.reset_index() - B.rename(columns={'index':'indexB'}, inplace=True) + B = B.reset_index(names='indexB') # Define the coordinate reference system, based on B crs = B.crs @@ -228,7 +227,7 @@ def intersect(A, B, B_value=None, include_background=False, background_value=0): n = intersects.groupby('_tmp_index_name')['geometry'].count() B_indices = intersects.groupby('_tmp_index_name')['index_right'].apply(list) - stats = pd.DataFrame(index=A.index, data={'intersections': B_indices, + stats = pd.DataFrame(index=A.index.copy(), data={'intersections': B_indices, 'n': n,}) stats['n'] = stats['n'].fillna(0) stats['n'] = stats['n'].apply(int) diff --git a/wntr/gis/network.py b/wntr/gis/network.py index a6ea7e7a4..d9c4e9d6b 100644 --- a/wntr/gis/network.py +++ b/wntr/gis/network.py @@ -133,7 +133,6 @@ def _extract_geodataframe(df, crs=None, links_as_points=False): # Set index if len(df) > 0: df.set_index('name', inplace=True) - df.index.name = None df = gpd.GeoDataFrame(df, crs=crs, geometry=geom) else: @@ -300,7 +299,7 @@ def add_link_attributes(self, values, name): self.pumps[name] = np.nan self.pumps.loc[link_name, name] = value - def _read(self, files, index_col='index'): + def _read(self, files, index_col='name'): if 'junctions' in files.keys(): data = gpd.read_file(files['junctions']).set_index(index_col) @@ -321,7 +320,7 @@ def _read(self, files, index_col='index'): data = gpd.read_file(files['valves']).set_index(index_col) self.valves = pd.concat([self.valves, data]) - def read_geojson(self, files, index_col='index'): + def read_geojson(self, files, index_col='name'): """ Append information from GeoJSON files to a WaterNetworkGIS object @@ -336,7 +335,7 @@ def read_geojson(self, files, index_col='index'): """ self._read(files, index_col) - def read_shapefile(self, files, index_col='index'): + def read_shapefile(self, files, index_col='name'): """ Append information from Esri Shapefiles to a WaterNetworkGIS object diff --git a/wntr/network/io.py b/wntr/network/io.py index eca53a44a..e4ceac8de 100644 --- a/wntr/network/io.py +++ b/wntr/network/io.py @@ -550,7 +550,7 @@ def write_geojson(wn, prefix: str, crs=None, pumps_as_points=True, wn_gis.write_geojson(prefix=prefix) -def read_geojson(files, index_col='index', append=None): +def read_geojson(files, index_col='name', append=None): """ Create or append a WaterNetworkModel from GeoJSON files @@ -611,7 +611,7 @@ def write_shapefile(wn, prefix: str, crs=None, pumps_as_points=True, valves_as_points=valves_as_points) wn_gis.write_shapefile(prefix=prefix) -def read_shapefile(files, index_col='index', append=None): +def read_shapefile(files, index_col='name', append=None): """ Create or append a WaterNetworkModel from Esri Shapefiles @@ -634,7 +634,7 @@ def read_shapefile(files, index_col='index', append=None): """ gis_data = WaterNetworkGIS() - gis_data.read_shapefile(files, index_col='index') + gis_data.read_shapefile(files,index_col=index_col) wn = gis_data._create_wn(append=append) return wn diff --git a/wntr/tests/test_gis.py b/wntr/tests/test_gis.py index 6c3bd8565..31513f265 100644 --- a/wntr/tests/test_gis.py +++ b/wntr/tests/test_gis.py @@ -77,11 +77,36 @@ def tearDownClass(self): def test_gis_index(self): # Tests that WN can be made using dataframes with customized index names wn_gis = self.wn.to_gis() + + # check that index name of geodataframes is "name" + assert wn_gis.junctions.index.name == "name" + assert wn_gis.tanks.index.name == "name" + assert wn_gis.reservoirs.index.name == "name" + assert wn_gis.pipes.index.name == "name" + assert wn_gis.pumps.index.name == "name" + + # check that index names can be changed and still be read back into a wn wn_gis.junctions.index.name = "my_index" wn_gis.pipes.index.name = "my_index" wn2 = wntr.network.from_gis(wn_gis) - self.wn == wn2 - + + assert self.wn.pipe_name_list == wn2.pipe_name_list + assert self.wn.junction_name_list == wn2.junction_name_list + + # test snap and intersect functionality with alternate index names + result = wntr.gis.snap(self.points, wn_gis.junctions, tolerance=5.0) + assert len(result) > 0 + result = wntr.gis.snap(wn_gis.junctions, self.points, tolerance=5.0) + assert len(result) > 0 + result = wntr.gis.intersect(wn_gis.junctions, self.polygons) + assert len(result) > 0 + result = wntr.gis.intersect(self.polygons, wn_gis.pipes) + assert len(result) > 0 + + # check that custom index name persists after running snap/intersect + assert wn_gis.junctions.index.name == "my_index" + assert wn_gis.pipes.index.name == "my_index" + def test_wn_to_gis(self): # Check type isinstance(self.gis_data.junctions, gpd.GeoDataFrame) @@ -256,8 +281,13 @@ def test_write_geojson(self): for component in components: if component == 'valves': continue # Net1 has no valves + # check file exists filename = abspath(join(testdir, prefix+'_'+component+'.geojson')) self.assertTrue(isfile(filename)) + + # check for "name" column + gdf = gpd.read_file(filename) + assert "name" in gdf.columns def test_snap_points_to_points(self): From 9edc23d5d4072995bcf6d022499df393dd37ddb7 Mon Sep 17 00:00:00 2001 From: David Hart Date: Thu, 19 Sep 2024 08:08:13 -0600 Subject: [PATCH 3/3] This commit upgrades the runners for release and adds Python 3.12 support which shoudl fix #441 --- .github/workflows/release.yml | 54 +++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0e4de3db7..a976f3e41 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,48 +10,72 @@ on: jobs: wheels: + name: Build distribution 📦 on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: matrix: - os: [windows-2019, macOS-13, ubuntu-20.04] + os: [windows-latest, macOS-13, macos-13, ubuntu-latest] steps: - - uses: actions/checkout@v3 + - name: Harden Runner + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + with: + egress-policy: audit + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Build wheels - uses: pypa/cibuildwheel@v2.11.1 + uses: pypa/cibuildwheel@79b0dd328794e1180a7268444d46cdf12e1abd01 # v2.21.0 env: CIBW_ENVIRONMENT: BUILD_WNTR_EXTENSIONS='true' - CIBW_BUILD: cp37-* cp38-* cp39-* cp310-* cp311-* + CIBW_BUILD: cp38-* cp39-* cp310-* cp311-* cp312-* CIBW_SKIP: "*-win32 *-manylinux_i686 pp* *-musllinux*" - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: + name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} path: ./wheelhouse/*.whl source: + name: Make SDist artifact 📦 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - name: Harden Runner + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + with: + egress-policy: audit + + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/setup-python@v5 with: python-version: '3.11' - - name: build the sdist - run: | - python -m pip install --upgrade build - python -m build --sdist - - uses: actions/upload-artifact@v3 + + - name: Build SDist + run: pipx run build --sdist + + - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: + name: cibw-sdist path: dist/*.tar.gz publish-to-pypi: + name: Publish Python 🐍 distribution 📦 to PyPI needs: [wheels, source] runs-on: ubuntu-latest + environment: + name: release + permissions: + id-token: write if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') steps: - - uses: actions/download-artifact@v3 + - name: Harden Runner + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + with: + egress-policy: audit + + - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: - name: artifact + pattern: cibw-* path: dist + merge-multiple: true - - uses: pypa/gh-action-pypi-publish@release/v1 + - uses: pypa/gh-action-pypi-publish@0ab0b79471669eb3a4d647e625009c62f9f3b241 # release/v1 with: user: __token__ password: ${{ secrets.PYPI_WNTR_API_TOKEN }}