Skip to content

Commit

Permalink
Merge branch 'main' into outlet_match_metric
Browse files Browse the repository at this point in the history
  • Loading branch information
Dobson committed Mar 4, 2024
2 parents 1c829e9 + 2ab3a75 commit 74bbfb9
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 142 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pre-commit/action@v3.0.0
- uses: pre-commit/action@v3.0.1

test:
needs: qa
Expand Down
6 changes: 3 additions & 3 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ fiona==1.9.5
# swmmanywhere (pyproject.toml)
fonttools==4.47.2
# via matplotlib
fsspec==2023.12.2
fsspec==2024.2.0
# via fastparquet
geographiclib==2.0
# via geopy
Expand Down Expand Up @@ -195,7 +195,7 @@ platformdirs==3.11.0
# via virtualenv
pluggy==1.4.0
# via pytest
pre-commit==3.6.0
pre-commit==3.6.2
# via swmmanywhere (pyproject.toml)
pyarrow==14.0.2
# via swmmanywhere (pyproject.toml)
Expand Down Expand Up @@ -292,7 +292,7 @@ typing-extensions==4.9.0
# mypy
# pydantic
# pydantic-core
tzdata==2023.4
tzdata==2024.1
# via pandas
urllib3==2.1.0
# via requests
Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ fiona==1.9.5
# swmmanywhere (pyproject.toml)
fonttools==4.47.2
# via matplotlib
fsspec==2023.12.2
fsspec==2024.2.0
# via fastparquet
geographiclib==2.0
# via geopy
Expand Down Expand Up @@ -233,7 +233,7 @@ typing-extensions==4.9.0
# via
# pydantic
# pydantic-core
tzdata==2023.4
tzdata==2024.1
# via pandas
urllib3==2.1.0
# via requests
Expand Down
81 changes: 55 additions & 26 deletions swmmanywhere/geospatial_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -608,8 +608,13 @@ def remove_(mp): return remove_zero_area_subareas(mp, removed_subareas)
def derive_rc(polys_gdf: gpd.GeoDataFrame,
G: nx.Graph,
building_footprints: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
"""Derive the RC of each subcatchment.
"""Derive the Runoff Coefficient (RC) of each subcatchment.
The runoff coefficient is the ratio of impervious area to total area. The
impervious area is calculated by overlaying the subcatchments with building
footprints and all edges in G buffered by their width parameter (i.e., to
calculate road area).
Args:
polys_gdf (gpd.GeoDataFrame): A GeoDataFrame containing polygons that
represent subcatchments with columns: 'geometry', 'area', and 'id'.
Expand Down Expand Up @@ -692,6 +697,49 @@ def calculate_angle(point1: tuple[float,float],

return angle_degrees

def nodes_to_features(G: nx.Graph):
"""Convert a graph to a GeoJSON node feature collection.
Args:
G (nx.Graph): The input graph.
Returns:
dict: A GeoJSON feature collection.
"""
features = []
for node, data in G.nodes(data=True):
feature = {
'type': 'Feature',
'geometry': sgeom.mapping(sgeom.Point(data['x'], data['y'])),
'properties': {'id': node, **data}
}
features.append(feature)
return features

def edges_to_features(G: nx.Graph):
"""Convert a graph to a GeoJSON edge feature collection.
Args:
G (nx.Graph): The input graph.
Returns:
dict: A GeoJSON feature collection.
"""
features = []
for u, v, data in G.edges(data=True):
if 'geometry' not in data:
geom = None
else:
geom = sgeom.mapping(data['geometry'])
del data['geometry']
feature = {
'type': 'Feature',
'geometry': geom,
'properties': {'u': u, 'v': v, **data}
}
features.append(feature)
return features

def graph_to_geojson(graph: nx.Graph,
fid: Path,
crs: str):
Expand All @@ -703,39 +751,20 @@ def graph_to_geojson(graph: nx.Graph,
crs (str): The CRS of the graph.
"""
graph = graph.copy()
for iterable, label in zip([graph.nodes(data=True),
graph.edges(data=True)],
['nodes', 'edges']):
geojson_features = []
for item_ in iterable:
if label == 'nodes':
data = item_[1]
data['geometry'] = sgeom.Point(data['x'], data['y'])
name_data = {'id' : item_[0]}
else:
data = item_[2]
name_data = {'u' : item_[0] ,
'v' : item_[1]}
if 'geometry' in data:
geometry = sgeom.mapping(data['geometry'])
data_ = data.copy()
del data_['geometry']
feature = {
'type': 'Feature',
'geometry': geometry,
'properties': {**data_, **name_data}
}
geojson_features.append(feature)
nodes = nodes_to_features(graph)
edges = edges_to_features(graph)

for iterable, label in zip([nodes, edges], ['nodes', 'edges']):
geojson = {
'type': 'FeatureCollection',
'features': geojson_features,
'features' : iterable,
'crs' : {
'type': 'name',
'properties': {
'name': "urn:ogc:def:crs:{0}".format(crs.replace(':', '::'))
}
}
}
}
fid_ = fid.with_stem(fid.stem + f'_{label}').with_suffix('.geojson')

with fid_.open('w') as output_file:
Expand Down
2 changes: 1 addition & 1 deletion swmmanywhere/graph_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ def __call__(self, G: nx.Graph,
# Derive
subs_gdf = go.derive_subcatchments(G,temp_fid)

# RC
# Calculate runoff coefficient (RC)
if addresses.building.suffix == '.parquet':
buildings = gpd.read_parquet(addresses.building)
else:
Expand Down
80 changes: 42 additions & 38 deletions swmmanywhere/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@

def get_full_parameters():
"""Get the full set of parameters."""
full_parameters = {
return {
"subcatchment_derivation": SubcatchmentDerivation(),
"outlet_derivation": OutletDerivation(),
"topology_derivation": TopologyDerivation(),
"hydraulic_design": HydraulicDesign()
}
return full_parameters

class SubcatchmentDerivation(BaseModel):
"""Parameters for subcatchment derivation."""
Expand Down Expand Up @@ -235,6 +234,42 @@ def _generate_property(self,
getattr(self, location),
property_name)

def _generate_project(self):
return self._generate_path(self.project_name)

def _generate_national(self):
return self._generate_property('national', 'project')

def _generate_national_building(self):
return self._generate_property('building.parquet',
'national')
def _generate_bbox(self):
return self._generate_property(f'bbox_{self.bbox_number}',
'project')
def _generate_model(self):
return self._generate_property(f'model_{self.model_number}',
'bbox')
def _generate_subcatchments(self):
return self._generate_property(f'subcatchments.{self.extension}',
'model')
def _generate_download(self):
return self._generate_property('download',
'bbox')
def _generate_river(self):
return self._generate_property('river.json',
'download')
def _generate_street(self):
return self._generate_property('street.json',
'download')
def _generate_elevation(self):
return self._generate_property('elevation.tif', 'download')
def _generate_building(self):
return self._generate_property(f'building.{self.extension}',
'download')
def _generate_precipitation(self):
return self._generate_property(f'precipitation.{self.extension}',
'download')

def _fetch_address(self, name):
"""Fetch the address.
Expand All @@ -248,39 +283,8 @@ def _fetch_address(self, name):
Returns:
Path: Path to the folder/file.
"""
if name == 'project':
return self._generate_path(self.project_name)
elif name == 'national':
return self._generate_property('national', 'project')
elif name == 'national_building':
return self._generate_property('building.parquet',
'national')
elif name == 'bbox':
return self._generate_property(f'bbox_{self.bbox_number}',
'project')
elif name == 'model':
return self._generate_property(f'model_{self.model_number}',
'bbox')
elif name == 'subcatchments':
return self._generate_property(f'subcatchments.{self.extension}',
'model')
elif name == 'download':
return self._generate_property('download',
'bbox')
elif name == 'river':
return self._generate_property('river.json',
'download')
elif name == 'street':
return self._generate_property('street.json',
'download')
elif name == 'elevation':
return self._generate_property('elevation.tif', 'download')
elif name == 'building':
return self._generate_property(f'building.{self.extension}',
'download')
elif name == 'precipitation':
return self._generate_property(f'precipitation.{self.extension}',
'download')
else:
raise AttributeError(f"Attribute {name} not found")

try:
return getattr(self, f"_generate_{name}")()
except AttributeError:
raise AttributeError(
f"Generate path for '{name}' failed. Attribute not found.")
Loading

0 comments on commit 74bbfb9

Please sign in to comment.