Skip to content

Commit

Permalink
Basic time-based mitigation overlay
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelthreet committed May 16, 2024
1 parent 5bb4b7f commit b3c2511
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 20 deletions.
69 changes: 49 additions & 20 deletions simfire/utils/layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -809,8 +809,10 @@ def __init__(
self.height = height
self.width = width

self.screen_size: Tuple[int, int] = screen_size
self.screen_size = screen_size

# Format to convert BurnMD timestamp to datetime object
self.strptime_fmt = "%Y/%m/%d %H:%M:%S.%f"
# set the path
self.fire_path = f"{self.state.title()}/{self.year}/fires/{self.fire.title()}"
# get available geopandas dataframes
Expand All @@ -819,13 +821,14 @@ def __init__(
self.latitude, self.longitude = self._get_fire_init_pos()
# get the bounds of screen
self.points = self._get_bounds_of_screen()
self.lat_lon_box = LandFireLatLongBox(self.points, year=self.year, height=self.height, width=self.width)
self.lat_lon_box = LandFireLatLongBox(
self.points, year=self.year, height=self.height, width=self.width
)
self.lat_lon_array = self.lat_lon_box.create_lat_lon_array()
self.mitigation_array = self._make_mitigations()
self.start_time = self.polygons_df.iloc[0]["DateStart"]
self.end_time = self.polygons_df.iloc[0]["DateContai"]
# get the duraton of fire specified
self.duration = self._calc_time_elapsed(
self.polygons_df.iloc[0]["DateStart"], self.polygons_df.iloc[0]["DateContai"]
)
self.duration = self._calc_time_elapsed(self.start_time, self.end_time)

def _get_historical_data(self) -> None:
"""Collect geopandas dataframes availbale for the specified fire"""
Expand All @@ -847,6 +850,19 @@ def _get_historical_data(self) -> None:
lines_df = geopandas.GeoDataFrame()
print("There is no mitigation data for this wildfire.")

# Add a column with a datetime object for ease of future computation
polygons_df.insert(
2,
"DateTime",
list(map(self.convert_to_datetime, polygons_df.CreateDate.to_list())),
True,
)
lines_df.insert(
2,
"DateTime",
list(map(self.convert_to_datetime, lines_df.CreateDate.to_list())),
True,
)
self.polygons_df = polygons_df
self.lines_df = lines_df

Expand Down Expand Up @@ -899,16 +915,18 @@ def _get_fire_init_pos(self):

return latitutde, longitude

def _make_mitigations(self) -> np.ndarray:
def make_mitigations(
self, start_time: datetime.datetime, end_time: datetime.datetime
) -> np.ndarray:
"""Method to add mitigation locations to the firemap at the start of the
simulation.
Return an array of the mitigation type as an enum that gets passed into the
sprites.
Arguments:
screen_size (h, w): Numpy instantiates array as (column, row)
lat_lon_array (w, h, 2): Array of tuples (lat, lon)
start_time: The start time to grab mitigations for
end_time: The end time to grab mitigations for
NOTE: This will not use the time-series aspect of the mitigations
Expand All @@ -923,12 +941,22 @@ def _make_mitigations(self) -> np.ndarray:

# only going to use `Completed` mitigations
# we do not care about 'MitigationTimestamps'
geo_completed_dozer_mitigations = self.lines_df.loc[
self.lines_df["FeatureCat"] == "Completed Dozer Line", :
]
geo_completed_hand_mitigations = self.lines_df.loc[
self.lines_df["FeatureCat"] == "Completed Hand Line", :
]
dozer_idxs = np.logical_and.reduce(
(
(self.lines_df["FeatureCat"] == "Completed Dozer Line").to_numpy(),
(self.lines_df["DateTime"] > start_time).to_numpy(),
(self.lines_df["DateTime"] < end_time).to_numpy(),
)
)
hand_idxs = np.logical_and.reduce(
(
(self.lines_df["FeatureCat"] == "Completed Hand Line").to_numpy(),
(self.lines_df["DateTime"] > start_time).to_numpy(),
(self.lines_df["DateTime"] < end_time).to_numpy(),
)
)
geo_completed_dozer_mitigations = self.lines_df.loc[dozer_idxs, :]
geo_completed_hand_mitigations = self.lines_df.loc[hand_idxs, :]

def _get_geometry(df):
xy = df.geometry.xy
Expand Down Expand Up @@ -981,11 +1009,9 @@ def _calc_time_elapsed(self, start_time: str, end_time: str) -> str:
Returns
A string of `<>h <>m <>s`
"""
datetimeFormat = "%Y/%m/%d %H:%M:%S.%f"

time_dif = datetime.datetime.strptime(
end_time, datetimeFormat
) - datetime.datetime.strptime(start_time, datetimeFormat)
time_dif = self.convert_to_datetime(end_time) - self.convert_to_datetime(
start_time
)

# convert to days, hours, minutes, seconds
days = f"{time_dif.days}d"
Expand All @@ -996,6 +1022,9 @@ def _calc_time_elapsed(self, start_time: str, end_time: str) -> str:

return days + " " + hours + " " + minutes + " " + seconds

def convert_to_datetime(self, bmd_time: str) -> datetime.datetime:
return datetime.datetime.strptime(bmd_time, self.strptime_fmt)

def _make_perimeters_image(self) -> np.ndarray:
"""
Create an array of the historical perimeter data
Expand Down
13 changes: 13 additions & 0 deletions tests/historical_test.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from datetime import datetime, timedelta
from simfire.sim.simulation import FireSimulation
from simfire.utils.config import Config
from simfire.utils.layers import HistoricalLayer
Expand All @@ -17,6 +18,18 @@

# Get hist_layer start time
# Set udpate duration to 1m, 1h, whatever makes sense
update_minutes = 12 * 60
update_interval = f"{update_minutes}m"
update_interval_datetime = timedelta(minutes=update_minutes)
current_time = hist_layer.convert_to_datetime(hist_layer.start_time)
end_time = hist_layer.convert_to_datetime(hist_layer.end_time)
while current_time < end_time:
mitigations = hist_layer.make_mitigations(
current_time, current_time + update_interval_datetime
)
sim.fire_map[mitigations != 0] = mitigations[mitigations != 0]
sim.run(update_interval)
current_time += update_interval_datetime
# Loop through simulation
# Update Historical Layer to get mitigations for specifc times (can filter the data frame)

Expand Down

0 comments on commit b3c2511

Please sign in to comment.