road_flood_risk_map module¶
Main module.
RoadFloodRiskMap (Map)
¶
A class to represent a road flood risk map.
Source code in road_flood_risk_map/road_flood_risk_map.py
class RoadFloodRiskMap(geemap.Map):
"""A class to represent a road flood risk map."""
def __init__(
self,
basemap="SATELLITE",
center=[40, -100],
zoom=4,
height="600px",
add_google_map=False,
verbose=False,
):
"""
Initialize the RoadFloodRiskMap with map data.
Args:
basemap (str): The basemap to use. Default is "SATELLITE". Other options include "ROADMAP", "TERRAIN", "HYBRID", etc.
center (list): The center of the map as a list of [latitude, longitude]. Default is [40, -100].
zoom (int): The initial zoom level of the map. Default is 4.
height (str): The height of the map in CSS units. Default is "600px".
add_google_map (bool): Whether to add Google Maps basemap. Default is False.
verbose (bool): Whether to print verbose output. Default is False.
"""
geemap.ee_initialize() # Initialize Earth Engine
super().__init__(
basemap=basemap,
center=center,
zoom=zoom,
height=height,
add_google_map=add_google_map,
)
self.wbe = WbEnvironment()
self.wbe.verbose = verbose
self.wbe.working_directory = (
os.getcwd()
) # Set the working directory to the current directory
def retrieve_alos_palsar_data_clip(
self,
region_of_interest: ee.Geometry,
output_file_name: str | None = None,
scale: int = 30,
):
"""
Retrieve ALOS PALSAR data clipped to a region of interest. If `output_file_name` is provided, the data will be saved to a file.
Args:
region_of_interest (Geometry.BBox): The region to clip the ALOS PALSAR data to. It follows the following format: `ee.Geometry.BBox(west, south, east, north)`. **west** The westernmost enclosed longitude. Will be adjusted to lie in the range -180° to 180°. **south** The southernmost enclosed latitude. If less than -90° (south pole), will be treated as -90°. **east** The easternmost enclosed longitude. **north** The northernmost enclosed latitude. If greater than +90° (north pole), will be treated as +90°.
output_file_name (str | None): The name of the output file to save the data. If None, the data will not be saved to a file.
scale (int): The scale in meters at which to export the image. Default is 30.
Returns:
image: The ALOS PALSAR data clipped to the region of interest.
"""
# Placeholder for actual data retrieval logic
sarHh = (
ee.ImageCollection("JAXA/ALOS/PALSAR/YEARLY/SAR_EPOCH")
.filter(ee.Filter.date("2017-01-01", "2018-01-01"))
.select("HH")
)
if output_file_name != None or output_file_name != "":
try:
geemap.ee_export_image(
sarHh.mean(),
filename=output_file_name + ".tif",
region=region_of_interest,
scale=scale,
)
except Exception as e:
print(f"Error exporting image: {e}")
return sarHh.mean().clip(region_of_interest)
def retrieve_sentinel_1_data_clip(
self,
region_of_interest: ee.Geometry,
output_file_name: str | None = None,
scale: int = 30,
):
"""
Retrieve Sentinel-1 data clipped to a region of interest. If `output_file_name` is provided, the data will be saved to a file.
Args:
region_of_interest (Geometry.BBox): The region to clip the Sentinel-1 data to. It follows the following format: `ee.Geometry.BBox(west, south, east, north)`. **west** The westernmost enclosed longitude. Will be adjusted to lie in the range -180° to 180°. **south** The southernmost enclosed latitude. If less than -90° (south pole), will be treated as -90°. **east** The easternmost enclosed longitude. **north** The northernmost enclosed latitude. If greater than +90° (north pole), will be treated as +90°.
output_file_name (str | None): The name of the output file to save the data. If None, the data will not be saved to a file.
scale (int): The scale in meters at which to export the image. Default is 30.
Returns:
image: The Sentinel-1 data clipped to the region of interest.
"""
# Placeholder for actual data retrieval logic
sentinel1 = (
ee.ImageCollection("COPERNICUS/S1_GRD")
.filter(ee.Filter.listContains("transmitterReceiverPolarisation", "VH"))
.filter(ee.Filter.date("2024-06-01", "2025-06-01"))
.filter(ee.Filter.eq("resolution_meters", scale))
.select("VH")
)
if output_file_name != None and output_file_name != "":
try:
geemap.ee_export_image(
sentinel1.mean(),
filename=output_file_name + ".tif",
region=region_of_interest,
scale=scale,
)
except Exception as e:
print(f"Error exporting image: {e}")
return sentinel1.mean().clip(region_of_interest)
def perform_hydrological_analysis(self, input_dem_file: str):
"""
Perform a hydrological analysis on the region of interest. If `output_file_name` is provided, the results will be saved to a file.
Args:
input_dem_file (str): The path to the input DEM file.
output_file_name (str): The name of the output file to save the results. If None, the results will not be saved to a file.
Returns:
filled_file_name (str): The filled DEM raster file name.
d8_ptr_file_name (str): The D8 flow pointer raster file name.
flow_accum_file_name (str): The flow accumulation raster file name.
"""
# Retrieve DEM data
dem = self.wbe.read_raster(input_dem_file)
# Resolves all of the depressions in a DEM, outputting a breached DEM, an aspect-aligned non-divergent flow pointer, and a flow accumulation raster.
filled, d8_ptr, flow_accum = self.wbe.flow_accum_full_workflow(
dem=dem,
out_type="sca",
log_transform=True,
)
base_file = os.path.basename(input_dem_file)
filled_file_name = f"filled_{base_file}"
d8_ptr_file_name = f"d8_flow_{base_file}"
flow_accum_file_name = f"flow_{base_file}"
self.wbe.write_raster(filled, f"{filled_file_name}")
self.wbe.write_raster(d8_ptr, f"{d8_ptr_file_name}")
self.wbe.write_raster(flow_accum, f"{flow_accum_file_name}")
return filled_file_name, d8_ptr_file_name, flow_accum_file_name
def add_analyse_roi_widget(self):
"""
Function to be run by widget to analyse the region of interest (ROI) for flood risk.
Args:
location (str): The location to check.
Returns:
None
"""
from ipywidgets import Dropdown, VBox, HBox, Button, Layout, Text, Output
from . import common
data_source = [
"Sentinel-1",
"ALOS PALSAR",
]
debug_view = Output(
layout={
"border": "1px solid black",
"overflow_x": "auto",
"width": "300px",
"height": "150px",
}
)
debug_view.add_class("wrap-output")
display(
HTML(
"""
<style>
.wrap-output {
width: 300px;
height: 150px;
overflow-x: hidden; /* disable horizontal scroll */
overflow-y: auto; /* enable vertical scroll */
}
.wrap-output pre {
white-space: pre-wrap !important;
word-wrap: break-word !important;
}
</style>
"""
)
)
self.roi_name = Text(
value="",
placeholder="Enter file name",
description="ROI Name:",
disabled=True,
)
self.file_name = Text(
value="",
description="DEM File:",
disabled=False,
)
self.dropdown = Dropdown(
options=data_source,
description="Data Source:",
value="Sentinel-1",
layout=Layout(width="auto", display="block"),
disabled=True,
)
self.submit_button = Button(
description="Submit",
button_style="success",
layout=Layout(width="auto", display="block"),
disabled=True,
)
self.analyze_button = Button(
description="Analyze DEM",
button_style="success",
layout=Layout(width="auto", display="block"),
disabled=True,
)
@debug_view.capture(clear_output=True)
def handle_submit(b):
print("Submit button clicked.")
print(f"Selected data source: {self.dropdown.value}")
temp_file = f"temp_{self.roi_name.value}"
img = None
if self.dropdown.value == "Sentinel-1":
img = self.retrieve_sentinel_1_data_clip(
self.user_roi, output_file_name=temp_file, scale=10
)
elif self.dropdown.value == "ALOS PALSAR":
img = self.retrieve_alos_palsar_data_clip(
self.user_roi, output_file_name=temp_file, scale=30
)
self.add_ee_layer(img, name=f"{self.dropdown.value} Image")
temp_file = f"{os.path.join(os.getcwd(),temp_file)}.tif"
if temp_file != "" and os.path.isfile(temp_file):
# Fix for unsupported compression method when opening raster file in WBE
print("Reinstating compression method for the file...")
self.file_name.value = common.fix_raster_metadata(
temp_file, self.roi_name.value
)
self.file_name.disabled = False
self.analyze_button.disabled = False
print(f"Done. File ready for analysis: {self.file_name.value}")
@debug_view.capture(clear_output=True)
def handle_analyze(b):
print("Analyze button clicked.")
print(f"Processing File: {self.file_name.value}")
filled, d8_ptr, flow_accum = self.perform_hydrological_analysis(
self.file_name.value
)
print("Hydrological analysis completed.")
# Display results on the map
self.add_raster(filled, layer_name="Filled DEM")
self.add_raster(d8_ptr, layer_name="D8 Flow Pointer")
self.add_raster(
flow_accum, colormap="viridis", layer_name="Flow Accumulation"
)
def disable_widget(roi_count):
if roi_count == 1:
self.dropdown.disabled = False
self.roi_name.disabled = False
# self.submit_button.disabled = False
print(
"Region of interest defined. You can now select a data source and enter a file name."
)
else:
self.dropdown.disabled = True
self.roi_name.disabled = True
# self.submit_button.disabled = True
print("Please define a single region of interest first.")
def handle_roi_name_change(change):
if change["new"] != "":
self.submit_button.disabled = False
else:
self.submit_button.disabled = True
def handle_file_name_change(change):
if change["new"] != "" and os.path.isfile(self.file_name.value):
self.analyze_button.disabled = False
else:
self.analyze_button.disabled = True
@debug_view.capture(clear_output=True)
def check_aoi(_, action, geo_json):
print("Checking region of interest...")
print(f"Action: {action}")
roi_count = self.user_rois.size().getInfo()
if action == "created":
disable_widget(roi_count)
elif action == "deleted":
disable_widget(roi_count)
def toggle_dropdown(b):
if widget_vbox.layout.display == "none":
widget_vbox.layout.display = "flex"
btn.icon = "times"
else:
widget_vbox.layout.display = "none"
btn.icon = "chevron-left"
self.submit_button.on_click(handle_submit)
self.analyze_button.on_click(handle_analyze)
self.roi_name.observe(handle_roi_name_change, names="value")
self.file_name.observe(handle_file_name_change, names="value")
widget_vbox = VBox(
[
self.roi_name,
self.dropdown,
self.submit_button,
debug_view,
self.file_name,
self.analyze_button,
],
Layout={"overflow_x": "auto", "width": "300px", "height": "150px"},
)
btn = Button(
icon="times",
button_style="primary",
layout=Layout(width="35px", height="35px"),
)
btn.on_click(toggle_dropdown)
widget_hbox = HBox([widget_vbox, btn], layout=Layout(width="auto"))
self.add_widget(widget_hbox)
self.draw_control.on_draw(check_aoi)
__init__(self, basemap='SATELLITE', center=[40, -100], zoom=4, height='600px', add_google_map=False, verbose=False)
special
¶
Initialize the RoadFloodRiskMap with map data.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
basemap |
str |
The basemap to use. Default is "SATELLITE". Other options include "ROADMAP", "TERRAIN", "HYBRID", etc. |
'SATELLITE' |
center |
list |
The center of the map as a list of [latitude, longitude]. Default is [40, -100]. |
[40, -100] |
zoom |
int |
The initial zoom level of the map. Default is 4. |
4 |
height |
str |
The height of the map in CSS units. Default is "600px". |
'600px' |
add_google_map |
bool |
Whether to add Google Maps basemap. Default is False. |
False |
verbose |
bool |
Whether to print verbose output. Default is False. |
False |
Source code in road_flood_risk_map/road_flood_risk_map.py
def __init__(
self,
basemap="SATELLITE",
center=[40, -100],
zoom=4,
height="600px",
add_google_map=False,
verbose=False,
):
"""
Initialize the RoadFloodRiskMap with map data.
Args:
basemap (str): The basemap to use. Default is "SATELLITE". Other options include "ROADMAP", "TERRAIN", "HYBRID", etc.
center (list): The center of the map as a list of [latitude, longitude]. Default is [40, -100].
zoom (int): The initial zoom level of the map. Default is 4.
height (str): The height of the map in CSS units. Default is "600px".
add_google_map (bool): Whether to add Google Maps basemap. Default is False.
verbose (bool): Whether to print verbose output. Default is False.
"""
geemap.ee_initialize() # Initialize Earth Engine
super().__init__(
basemap=basemap,
center=center,
zoom=zoom,
height=height,
add_google_map=add_google_map,
)
self.wbe = WbEnvironment()
self.wbe.verbose = verbose
self.wbe.working_directory = (
os.getcwd()
) # Set the working directory to the current directory
add_analyse_roi_widget(self)
¶
Function to be run by widget to analyse the region of interest (ROI) for flood risk.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
location |
str |
The location to check. |
required |
Returns:
Type | Description |
---|---|
None |
Source code in road_flood_risk_map/road_flood_risk_map.py
def add_analyse_roi_widget(self):
"""
Function to be run by widget to analyse the region of interest (ROI) for flood risk.
Args:
location (str): The location to check.
Returns:
None
"""
from ipywidgets import Dropdown, VBox, HBox, Button, Layout, Text, Output
from . import common
data_source = [
"Sentinel-1",
"ALOS PALSAR",
]
debug_view = Output(
layout={
"border": "1px solid black",
"overflow_x": "auto",
"width": "300px",
"height": "150px",
}
)
debug_view.add_class("wrap-output")
display(
HTML(
"""
<style>
.wrap-output {
width: 300px;
height: 150px;
overflow-x: hidden; /* disable horizontal scroll */
overflow-y: auto; /* enable vertical scroll */
}
.wrap-output pre {
white-space: pre-wrap !important;
word-wrap: break-word !important;
}
</style>
"""
)
)
self.roi_name = Text(
value="",
placeholder="Enter file name",
description="ROI Name:",
disabled=True,
)
self.file_name = Text(
value="",
description="DEM File:",
disabled=False,
)
self.dropdown = Dropdown(
options=data_source,
description="Data Source:",
value="Sentinel-1",
layout=Layout(width="auto", display="block"),
disabled=True,
)
self.submit_button = Button(
description="Submit",
button_style="success",
layout=Layout(width="auto", display="block"),
disabled=True,
)
self.analyze_button = Button(
description="Analyze DEM",
button_style="success",
layout=Layout(width="auto", display="block"),
disabled=True,
)
@debug_view.capture(clear_output=True)
def handle_submit(b):
print("Submit button clicked.")
print(f"Selected data source: {self.dropdown.value}")
temp_file = f"temp_{self.roi_name.value}"
img = None
if self.dropdown.value == "Sentinel-1":
img = self.retrieve_sentinel_1_data_clip(
self.user_roi, output_file_name=temp_file, scale=10
)
elif self.dropdown.value == "ALOS PALSAR":
img = self.retrieve_alos_palsar_data_clip(
self.user_roi, output_file_name=temp_file, scale=30
)
self.add_ee_layer(img, name=f"{self.dropdown.value} Image")
temp_file = f"{os.path.join(os.getcwd(),temp_file)}.tif"
if temp_file != "" and os.path.isfile(temp_file):
# Fix for unsupported compression method when opening raster file in WBE
print("Reinstating compression method for the file...")
self.file_name.value = common.fix_raster_metadata(
temp_file, self.roi_name.value
)
self.file_name.disabled = False
self.analyze_button.disabled = False
print(f"Done. File ready for analysis: {self.file_name.value}")
@debug_view.capture(clear_output=True)
def handle_analyze(b):
print("Analyze button clicked.")
print(f"Processing File: {self.file_name.value}")
filled, d8_ptr, flow_accum = self.perform_hydrological_analysis(
self.file_name.value
)
print("Hydrological analysis completed.")
# Display results on the map
self.add_raster(filled, layer_name="Filled DEM")
self.add_raster(d8_ptr, layer_name="D8 Flow Pointer")
self.add_raster(
flow_accum, colormap="viridis", layer_name="Flow Accumulation"
)
def disable_widget(roi_count):
if roi_count == 1:
self.dropdown.disabled = False
self.roi_name.disabled = False
# self.submit_button.disabled = False
print(
"Region of interest defined. You can now select a data source and enter a file name."
)
else:
self.dropdown.disabled = True
self.roi_name.disabled = True
# self.submit_button.disabled = True
print("Please define a single region of interest first.")
def handle_roi_name_change(change):
if change["new"] != "":
self.submit_button.disabled = False
else:
self.submit_button.disabled = True
def handle_file_name_change(change):
if change["new"] != "" and os.path.isfile(self.file_name.value):
self.analyze_button.disabled = False
else:
self.analyze_button.disabled = True
@debug_view.capture(clear_output=True)
def check_aoi(_, action, geo_json):
print("Checking region of interest...")
print(f"Action: {action}")
roi_count = self.user_rois.size().getInfo()
if action == "created":
disable_widget(roi_count)
elif action == "deleted":
disable_widget(roi_count)
def toggle_dropdown(b):
if widget_vbox.layout.display == "none":
widget_vbox.layout.display = "flex"
btn.icon = "times"
else:
widget_vbox.layout.display = "none"
btn.icon = "chevron-left"
self.submit_button.on_click(handle_submit)
self.analyze_button.on_click(handle_analyze)
self.roi_name.observe(handle_roi_name_change, names="value")
self.file_name.observe(handle_file_name_change, names="value")
widget_vbox = VBox(
[
self.roi_name,
self.dropdown,
self.submit_button,
debug_view,
self.file_name,
self.analyze_button,
],
Layout={"overflow_x": "auto", "width": "300px", "height": "150px"},
)
btn = Button(
icon="times",
button_style="primary",
layout=Layout(width="35px", height="35px"),
)
btn.on_click(toggle_dropdown)
widget_hbox = HBox([widget_vbox, btn], layout=Layout(width="auto"))
self.add_widget(widget_hbox)
self.draw_control.on_draw(check_aoi)
perform_hydrological_analysis(self, input_dem_file)
¶
Perform a hydrological analysis on the region of interest. If output_file_name
is provided, the results will be saved to a file.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
input_dem_file |
str |
The path to the input DEM file. |
required |
output_file_name |
str |
The name of the output file to save the results. If None, the results will not be saved to a file. |
required |
Returns:
Type | Description |
---|---|
filled_file_name (str) |
The filled DEM raster file name. d8_ptr_file_name (str): The D8 flow pointer raster file name. flow_accum_file_name (str): The flow accumulation raster file name. |
Source code in road_flood_risk_map/road_flood_risk_map.py
def perform_hydrological_analysis(self, input_dem_file: str):
"""
Perform a hydrological analysis on the region of interest. If `output_file_name` is provided, the results will be saved to a file.
Args:
input_dem_file (str): The path to the input DEM file.
output_file_name (str): The name of the output file to save the results. If None, the results will not be saved to a file.
Returns:
filled_file_name (str): The filled DEM raster file name.
d8_ptr_file_name (str): The D8 flow pointer raster file name.
flow_accum_file_name (str): The flow accumulation raster file name.
"""
# Retrieve DEM data
dem = self.wbe.read_raster(input_dem_file)
# Resolves all of the depressions in a DEM, outputting a breached DEM, an aspect-aligned non-divergent flow pointer, and a flow accumulation raster.
filled, d8_ptr, flow_accum = self.wbe.flow_accum_full_workflow(
dem=dem,
out_type="sca",
log_transform=True,
)
base_file = os.path.basename(input_dem_file)
filled_file_name = f"filled_{base_file}"
d8_ptr_file_name = f"d8_flow_{base_file}"
flow_accum_file_name = f"flow_{base_file}"
self.wbe.write_raster(filled, f"{filled_file_name}")
self.wbe.write_raster(d8_ptr, f"{d8_ptr_file_name}")
self.wbe.write_raster(flow_accum, f"{flow_accum_file_name}")
return filled_file_name, d8_ptr_file_name, flow_accum_file_name
retrieve_alos_palsar_data_clip(self, region_of_interest, output_file_name=None, scale=30)
¶
Retrieve ALOS PALSAR data clipped to a region of interest. If output_file_name
is provided, the data will be saved to a file.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
region_of_interest |
Geometry.BBox |
The region to clip the ALOS PALSAR data to. It follows the following format: |
required |
output_file_name |
str | None |
The name of the output file to save the data. If None, the data will not be saved to a file. |
None |
scale |
int |
The scale in meters at which to export the image. Default is 30. |
30 |
Returns:
Type | Description |
---|---|
image |
The ALOS PALSAR data clipped to the region of interest. |
Source code in road_flood_risk_map/road_flood_risk_map.py
def retrieve_alos_palsar_data_clip(
self,
region_of_interest: ee.Geometry,
output_file_name: str | None = None,
scale: int = 30,
):
"""
Retrieve ALOS PALSAR data clipped to a region of interest. If `output_file_name` is provided, the data will be saved to a file.
Args:
region_of_interest (Geometry.BBox): The region to clip the ALOS PALSAR data to. It follows the following format: `ee.Geometry.BBox(west, south, east, north)`. **west** The westernmost enclosed longitude. Will be adjusted to lie in the range -180° to 180°. **south** The southernmost enclosed latitude. If less than -90° (south pole), will be treated as -90°. **east** The easternmost enclosed longitude. **north** The northernmost enclosed latitude. If greater than +90° (north pole), will be treated as +90°.
output_file_name (str | None): The name of the output file to save the data. If None, the data will not be saved to a file.
scale (int): The scale in meters at which to export the image. Default is 30.
Returns:
image: The ALOS PALSAR data clipped to the region of interest.
"""
# Placeholder for actual data retrieval logic
sarHh = (
ee.ImageCollection("JAXA/ALOS/PALSAR/YEARLY/SAR_EPOCH")
.filter(ee.Filter.date("2017-01-01", "2018-01-01"))
.select("HH")
)
if output_file_name != None or output_file_name != "":
try:
geemap.ee_export_image(
sarHh.mean(),
filename=output_file_name + ".tif",
region=region_of_interest,
scale=scale,
)
except Exception as e:
print(f"Error exporting image: {e}")
return sarHh.mean().clip(region_of_interest)
retrieve_sentinel_1_data_clip(self, region_of_interest, output_file_name=None, scale=30)
¶
Retrieve Sentinel-1 data clipped to a region of interest. If output_file_name
is provided, the data will be saved to a file.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
region_of_interest |
Geometry.BBox |
The region to clip the Sentinel-1 data to. It follows the following format: |
required |
output_file_name |
str | None |
The name of the output file to save the data. If None, the data will not be saved to a file. |
None |
scale |
int |
The scale in meters at which to export the image. Default is 30. |
30 |
Returns:
Type | Description |
---|---|
image |
The Sentinel-1 data clipped to the region of interest. |
Source code in road_flood_risk_map/road_flood_risk_map.py
def retrieve_sentinel_1_data_clip(
self,
region_of_interest: ee.Geometry,
output_file_name: str | None = None,
scale: int = 30,
):
"""
Retrieve Sentinel-1 data clipped to a region of interest. If `output_file_name` is provided, the data will be saved to a file.
Args:
region_of_interest (Geometry.BBox): The region to clip the Sentinel-1 data to. It follows the following format: `ee.Geometry.BBox(west, south, east, north)`. **west** The westernmost enclosed longitude. Will be adjusted to lie in the range -180° to 180°. **south** The southernmost enclosed latitude. If less than -90° (south pole), will be treated as -90°. **east** The easternmost enclosed longitude. **north** The northernmost enclosed latitude. If greater than +90° (north pole), will be treated as +90°.
output_file_name (str | None): The name of the output file to save the data. If None, the data will not be saved to a file.
scale (int): The scale in meters at which to export the image. Default is 30.
Returns:
image: The Sentinel-1 data clipped to the region of interest.
"""
# Placeholder for actual data retrieval logic
sentinel1 = (
ee.ImageCollection("COPERNICUS/S1_GRD")
.filter(ee.Filter.listContains("transmitterReceiverPolarisation", "VH"))
.filter(ee.Filter.date("2024-06-01", "2025-06-01"))
.filter(ee.Filter.eq("resolution_meters", scale))
.select("VH")
)
if output_file_name != None and output_file_name != "":
try:
geemap.ee_export_image(
sentinel1.mean(),
filename=output_file_name + ".tif",
region=region_of_interest,
scale=scale,
)
except Exception as e:
print(f"Error exporting image: {e}")
return sentinel1.mean().clip(region_of_interest)