Skip to content

Commit b64208c

Browse files
improve the robustness of the FIM evaluation with building footprint
1 parent aecb0d1 commit b64208c

File tree

8 files changed

+394
-178
lines changed

8 files changed

+394
-178
lines changed
33.7 KB
Binary file not shown.

dist/fimeval-0.1.46.tar.gz

33.7 KB
Binary file not shown.

poetry.lock

Lines changed: 344 additions & 111 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "fimeval"
3-
version = "0.1.45"
3+
version = "0.1.46"
44
description = "A Framework for Automatic Evaluation of Flood Inundation Mapping Predictions Evaluation"
55
authors = [
66
"Surface Dynamics Modeling Lab",

src/fimeval/BuildingFootprint/evaluationwithBF.py

Lines changed: 26 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,6 @@ def Changeintogpkg(input_path, output_dir, layer_name):
2121
gdf.to_file(output_gpkg, driver="GPKG")
2222
return output_gpkg
2323

24-
25-
def Changeintogpkg(input_path, output_dir, layer_name):
26-
input_path = str(input_path)
27-
if input_path.endswith(".gpkg"):
28-
return input_path
29-
else:
30-
gdf = gpd.read_file(input_path)
31-
output_gpkg = os.path.join(output_dir, f"{layer_name}.gpkg")
32-
gdf.to_file(output_gpkg, driver="GPKG")
33-
return output_gpkg
34-
35-
3624
def GetFloodedBuildingCountInfo(
3725
building_fp_path,
3826
study_area_path,
@@ -51,8 +39,17 @@ def GetFloodedBuildingCountInfo(
5139
building_gdf = gpd.read_file(building_fp_gpkg)
5240
study_area_gdf = gpd.read_file(study_area_path)
5341

54-
if building_gdf.crs != study_area_gdf.crs:
55-
building_gdf = building_gdf.to_crs(study_area_gdf.crs)
42+
with rasterio.open(raster1_path) as src:
43+
target_crs = str(src.crs)
44+
45+
# Reproject all GeoDataFrames to the target CRS
46+
if building_gdf.crs != target_crs:
47+
building_gdf = building_gdf.to_crs(target_crs)
48+
print("reproject building_gdf")
49+
50+
if study_area_gdf.crs != target_crs:
51+
study_area_gdf = study_area_gdf.to_crs(target_crs)
52+
print("reproject study_area_gdf")
5653

5754
clipped_buildings = gpd.overlay(building_gdf, study_area_gdf, how="intersection")
5855
clipped_buildings["centroid"] = clipped_buildings.geometry.centroid
@@ -85,7 +82,7 @@ def count_centroids_in_raster(raster_path, label):
8582
elif pixel_value == 4:
8683
centroid_counts["True Positive"] += 1
8784

88-
if "benchmark" in str(raster1_path).lower():
85+
if "bm" in str(raster1_path).lower():
8986
count_centroids_in_raster(raster1_path, "Benchmark")
9087
count_centroids_in_raster(raster2_path, "Candidate")
9188
elif "candidate" in str(raster2_path).lower():
@@ -220,46 +217,32 @@ def count_centroids_in_raster(raster_path, label):
220217
print(f"Performance metrics chart is saved as PNG at {output_path}")
221218
fig.show()
222219

223-
224220
def process_TIFF(
225221
tif_files, contingency_files, building_footprint, boundary, method_path
226222
):
227223
benchmark_path = None
228-
candidate_path = []
229-
230-
if len(tif_files) == 2:
231-
for tif_file in tif_files:
232-
if "benchmark" in tif_file.name.lower():
233-
benchmark_path = tif_file
234-
else:
235-
candidate_path.append(tif_file)
224+
candidate_paths = []
236225

237-
elif len(tif_files) > 2:
238-
for tif_file in tif_files:
239-
if "benchmark" in tif_file.name.lower():
226+
for tif_file in tif_files:
227+
if "bm" in tif_file.name.lower() or "benchmark" in tif_file.name.lower():
228+
if benchmark_path is None:
240229
benchmark_path = tif_file
241-
print(f"---Benchmark: {tif_file.name}---")
242230
else:
243-
candidate_path.append(tif_file)
244-
245-
if benchmark_path and candidate_path:
246-
for candidate in candidate_path:
231+
candidate_paths.append(tif_file)
232+
else:
233+
candidate_paths.append(tif_file)
247234

235+
if benchmark_path and candidate_paths:
236+
for candidate in candidate_paths:
248237
matching_contingency_map = None
249238
candidate_base_name = candidate.stem.replace("_clipped", "")
250239

251240
for contingency_file in contingency_files:
252241
if candidate_base_name in contingency_file.name:
253242
matching_contingency_map = contingency_file
254-
print(
255-
f"Found matching contingency map for candidate {candidate.name}: {contingency_file.name}"
256-
)
257243
break
258244

259245
if matching_contingency_map:
260-
print(
261-
f"---FIM evaluation with Building Footprint starts for {candidate.name}---"
262-
)
263246
GetFloodedBuildingCountInfo(
264247
building_footprint,
265248
boundary,
@@ -273,8 +256,11 @@ def process_TIFF(
273256
print(
274257
f"No matching contingency map found for candidate {candidate.name}. Skipping..."
275258
)
276-
277-
259+
elif not benchmark_path:
260+
print("Warning: No benchmark file found.")
261+
elif not candidate_paths:
262+
print("Warning: No candidate files found.")
263+
278264
def find_existing_footprint(out_dir):
279265
gpkg_files = list(Path(out_dir).glob("*.gpkg"))
280266
return gpkg_files[0] if gpkg_files else None
@@ -337,7 +323,6 @@ def EvaluationWithBuildingFootprint(
337323
)
338324
else:
339325
building_footprintMS = EX_building_footprint
340-
341326
process_TIFF(
342327
tif_files,
343328
contingency_files,

src/fimeval/ContingencyMap/printcontingency.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ def getContingencyMap(raster_path, method_path):
1818
combined_flood = np.full_like(band1, fill_value=1, dtype=int)
1919

2020
# Map pixel values to colors
21-
combined_flood[band1 == 5] = 0
22-
combined_flood[band1 == 0] = 1
23-
combined_flood[band1 == 1] = 2
24-
combined_flood[band1 == 2] = 3
25-
combined_flood[band1 == 3] = 4
26-
combined_flood[band1 == 4] = 5
21+
combined_flood[band1 == 5] = 5
22+
combined_flood[band1 == 0] = 0
23+
combined_flood[band1 == 1] = 1
24+
combined_flood[band1 == 2] = 2
25+
combined_flood[band1 == 3] = 3
26+
combined_flood[band1 == 4] = 4
2727

2828
# Handle NoData explicitly, mapping it to "No Data" class (1)
2929
if nodata_value is not None:
@@ -42,7 +42,7 @@ def getContingencyMap(raster_path, method_path):
4242
ys_dd = np.array(latitudes).reshape(ys.shape)
4343

4444
# Define the color map and normalization
45-
flood_colors = ["black", "white", "grey", "green", "blue", "red"] # 6 classes
45+
flood_colors = ["white", "grey", "green", "blue", "red", "black"] # 6 classes
4646
flood_cmap = mcolors.ListedColormap(flood_colors)
4747
flood_norm = mcolors.BoundaryNorm(
4848
boundaries=np.arange(-0.5, 6.5, 1), ncolors=len(flood_colors)
@@ -60,6 +60,7 @@ def getContingencyMap(raster_path, method_path):
6060

6161
# Create legend patches
6262
value_labels = {
63+
0: "No data",
6364
1: "True negative",
6465
2: "False positive",
6566
3: "False negative",

src/fimeval/utilis.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ def MakeFIMsUniform(fim_dir, target_crs=None, target_resolution=None):
132132
for src_path in tif_files:
133133
dst_path = processing_folder / src_path.name
134134
reprojectFIMs(str(src_path), str(dst_path), target_crs)
135+
compress_tif_lzw(dst_path)
135136
else:
136137
all_within_conus = all(is_within_conus(bounds_list[i], crs_list[i]) for i in range(len(bounds_list)))
137138

@@ -148,7 +149,6 @@ def MakeFIMsUniform(fim_dir, target_crs=None, target_resolution=None):
148149
for src_path in tif_files:
149150
dst_path = processing_folder / src_path.name
150151
shutil.copy(src_path, dst_path)
151-
compress_tif_lzw(dst_path)
152152

153153
# Resolution check and resampling
154154
processed_tifs = list(processing_folder.glob('*.tif'))
@@ -170,7 +170,6 @@ def MakeFIMsUniform(fim_dir, target_crs=None, target_resolution=None):
170170
for src_path in processed_tifs:
171171
resample_to_resolution(str(src_path), target_resolution, target_resolution)
172172
else:
173-
print("FIMs are in different resolution after projection. \n")
174173
coarser_x = max(res[0] for res in resolutions)
175174
coarser_y = max(res[1] for res in resolutions)
176175
print(f"Using coarser resolution: X={coarser_x}, Y={coarser_y}. Resampling all FIMS to this resolution.")

tests/test_evaluationfim.py

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,51 @@
1-
import fimpef as fp
1+
import fimeval as fe
22
from pathlib import Path
33

44
Main_dir = (
55
# "../docs/sampledata"
6-
'/Users/Supath/Downloads/SDML/FIMeval/FIMS'
76
)
87
PWD_dir = "./path/to/PWB"
9-
# output_dir = "../docs/Output"
10-
output_dir = '/Users/Supath/Downloads/SDML/FIMeval/output'
8+
output_dir = "../docs/Output"
119
target_crs = "EPSG:5070" # Target CRS for reprojecting the FIMs, need to be in EPSG code of Projected CRS
12-
target_resolution = 30 #This will be in meters, if it passes the FIMS will be resampled to this resolution else, it will find the coarser resolution among all FIMS for this case and use that to resample!
10+
target_resolution = 10 #This will be in meters, if it passes the FIMS will be resampled to this resolution else, it will find the coarser resolution among all FIMS for this case and use that to resample!
1311

1412

15-
building_footprint = "./path/to/building_footprint"
13+
building_footprint = "../path/to/building_footprint.shp" # If user is working with user defined building footprint shapefile
1614

1715
# If user is working with user defined shapefile
18-
# AOI = "./path/to/shapefile"
16+
AOI = "path/to/shapefile.shp" # This shapefile should be in projected CRS, if not, it will be reprojected to the target CRS
1917

2018
# Three methods are available to evaluate the FIM,
2119
# 1. Smallest extent
2220
# 2. Convex Hull
2321
# 3. AOI (User defined shapefile)
24-
method_name = "smallest_extent"
22+
method_name = "AOI"
2523

2624
# 3 letter country ISO code
2725
countryISO = "USA"
2826

2927
# Run the evaluation
3028
# It has the Permanent Water Bodies (PWB) dataset as default for United States
31-
fp.EvaluateFIM(Main_dir, method_name, output_dir)
29+
# fe.EvaluateFIM(Main_dir, method_name, output_dir)
3230

3331
# OR, If the Evaluation Study Area is outside the US or, user has their own PWB dataset
34-
# fp.EvaluateFIM(Main_dir, method_name, output_dir, PWB_dir=PWD_dir)
32+
# fe.EvaluateFIM(Main_dir, method_name, output_dir, PWB_dir=PWD_dir)
3533

3634

3735
#If the FIMS are not in projected crs or are in different spatial resolution
38-
fp.EvaluateFIM(Main_dir, method_name, output_dir, PWB_dir=PWD_dir, target_crs=target_crs, target_resolution=target_resolution)
36+
fe.EvaluateFIM(Main_dir, method_name, output_dir, target_crs=target_crs, shapefile_dir = AOI, target_resolution=target_resolution)
3937

4038
# Once the FIM evaluation is done, print the contingency map
41-
fp.PrintContingencyMap(Main_dir, method_name, output_dir)
39+
fe.PrintContingencyMap(Main_dir, method_name, output_dir)
4240

4341

4442
# Plot rhe evaluation metrics after the FIM evaluation
45-
fp.PlotEvaluationMetrics(Main_dir, method_name, output_dir)
43+
fe.PlotEvaluationMetrics(Main_dir, method_name, output_dir)
4644

47-
# FIM Evaluation with Building Footprint (by default, it uses the Microsoft Building Footprint dataset)
48-
fp.EvaluationWithBuildingFootprint(
45+
# # FIM Evaluation with Building Footprint (by default, it uses the Microsoft Building Footprint dataset)
46+
fe.EvaluationWithBuildingFootprint(
4947
Main_dir, method_name, output_dir, country=countryISO
5048
)
5149

5250
# If user have their own building footprint dataset, they can use it as well
53-
# fp.EvaluationWithBuildingFootprint(Main_dir, method_name, output_dir, building_footprint = building_footprint, shapefile_dir=AOI)
51+
fe.EvaluationWithBuildingFootprint(Main_dir, method_name, output_dir, building_footprint = building_footprint, shapefile_dir=AOI)

0 commit comments

Comments
 (0)