FireSTARR
Loading...
Searching...
No Matches
Environment.h
1/* Copyright (c) Queen's Printer for Ontario, 2020. */
2/* Copyright (c) His Majesty the King in Right of Canada as represented by the Minister of Natural Resources, 2021-2025. */
3
4/* SPDX-License-Identifier: AGPL-3.0-or-later */
5
6#pragma once
7#include "stdafx.h"
8#include "Cell.h"
9#include "ConstantGrid.h"
10#include "Event.h"
11#include "FuelType.h"
12#include "GridMap.h"
13#include "IntensityMap.h"
14#include "Point.h"
15#include "Settings.h"
16
17namespace fs::topo
18{
19using FuelGrid = data::ConstantGrid<const fuel::FuelType*, FuelSize>;
20using ElevationGrid = data::ConstantGrid<ElevationSize>;
21using CellGrid = data::ConstantGrid<Cell, Topo>;
49{
50public:
60 [[nodiscard]] static Environment loadEnvironment(
61 const string dir_out,
62 const string& path,
63 const Point& point,
64 const string& perimeter,
65 int year);
73 [[nodiscard]] static Environment load(const string dir_out,
74 const Point& point,
75 const string& in_fuel,
76 const string& in_elevation);
84 [[nodiscard]] unique_ptr<Coordinates> findCoordinates(
85 const Point& point,
86 bool flipped) const;
91 Environment(Environment&& rhs) noexcept = default;
96 Environment(const Environment& rhs) noexcept = default;
102 Environment& operator=(Environment&& rhs) noexcept = default;
108 Environment& operator=(const Environment& rhs) noexcept = default;
113 [[nodiscard]] constexpr const string& proj4() const
114 {
115 return cells_->proj4();
116 }
121 [[nodiscard]] constexpr Idx rows() const
122 {
123 return cells_->rows();
124 }
129 [[nodiscard]] constexpr Idx columns() const
130 {
131 return cells_->columns();
132 }
137 [[nodiscard]] constexpr MathSize cellSize() const
138 {
139 return cells_->cellSize();
140 }
145 [[nodiscard]] constexpr ElevationSize elevation() const
146 {
147 return elevation_;
148 }
155 [[nodiscard]]
156#ifdef NDEBUG
157 constexpr
158#endif
159 Cell
160 cell(const Idx row, const Idx column) const
161 {
162 return cells_->at(Location(row, column));
163 }
169 template <class P>
170 [[nodiscard]] constexpr Cell cell(const Position<P>& position) const
171 {
172 return cells_->at(position);
173 }
179 // [[nodiscard]] constexpr Cell cell(const HashSize hash_size) const
180 // {
181 // return cells_->at(hash_size);
182 // }
190 [[nodiscard]]
191#ifdef NDEBUG
192 constexpr
193#endif
194 Cell
195 offset(const sim::Event& event,
196 const Idx row,
197 const Idx column) const
198 {
199 const auto& p = event.cell();
200 // return cell(p.hash() + column + static_cast<HashSize>(MAX_COLUMNS) * row);
201 return cell(Location(p.row() + row, p.column() + column));
202 }
213 [[nodiscard]] sim::ProbabilityMap* makeProbabilityMap(DurationSize time,
214 DurationSize start_time,
215 int min_value,
216 int low_max,
217 int med_max,
218 int max_value) const;
225 template <class Other>
226 [[nodiscard]] unique_ptr<data::GridMap<Other>> makeMap(const Other nodata) const
227 {
228 return make_unique<data::GridMap<Other>>(*cells_, nodata);
229 }
234 [[nodiscard]] unique_ptr<sim::BurnedData> makeBurnedData() const
235 {
236 auto result = make_unique<sim::BurnedData>(*not_burnable_);
237 return result;
238 }
243 void resetBurnedData(sim::BurnedData* data) const noexcept
244 {
245 // setting to {} probably makes a bitset of the same size on the stack?
246 // *data = {};
247 *data = *not_burnable_;
248 }
249protected:
255 [[nodiscard]] static CellGrid* makeCells(
256 const FuelGrid& fuel,
258 {
259 logging::check_equal(fuel.yllcorner(), elevation.yllcorner(), "yllcorner");
260 static Cell nodata{};
261 auto values = vector<Cell>{fuel.data.size()};
262 vector<HashSize> hashes{};
263 // for (HashSize h = 0; h < static_cast<size_t>(MAX_ROWS) * MAX_COLUMNS; ++h)
264 // {
265 // hashes.emplace_back(h);
266 // }
267 for (Idx r = 0; r < fuel.rows(); ++r)
268 {
269 for (Idx c = 0; c < fuel.columns(); ++c)
270 {
271 const auto h = hashes.emplace_back(Location(r, c).hash());
272 // hashes.emplace_back(Location(r, c).hash());
273 // }
274 // }
275 // std::for_each(
276 // std::execution::par_unseq,
277 // hashes.begin(),
278 // hashes.end(),
279 // [&fuel, &values, &elevation](auto&& h)
280 // {
281 const topo::Location loc{r, c, h};
282 // const topo::Location loc{static_cast<Idx>(h / MAX_COLUMNS),
283 // static_cast<Idx>(h % MAX_COLUMNS),
284 // h};
285 // const auto r = loc.row();
286 // const auto c = loc.column();
287 // const auto f = fuel::FuelType::safeCode(fuel.at(h));
288 if (r >= 0 && r < fuel.rows() && c >= 0 && c < fuel.columns())
289 {
290 // NOTE: this needs to translate to internal codes?
291 const auto f = fuel::FuelType::safeCode(fuel.at(loc));
292 auto s = static_cast<SlopeSize>(INVALID_SLOPE);
293 auto a = static_cast<AspectSize>(INVALID_ASPECT);
294 // HACK: don't calculate for outside box of cells
295 if (r > 0 && r < fuel.rows() - 1 && c > 0 && c < fuel.columns() - 1)
296 {
297 MathSize dem[9];
298 bool valid = true;
299 for (int i = -1; i < 2; ++i)
300 {
301 for (int j = -1; j < 2; ++j)
302 {
303 // grid is (0, 0) at bottom left, but want [0] in array to be NW corner
304 auto actual_row = static_cast<Idx>(r - i);
305 auto actual_column = static_cast<Idx>(c + j);
306 auto cur_loc = Location{actual_row, actual_column};
307 // auto cur_h = cur_loc.hash();
308 // dem[3 * (i + 1) + (j + 1)] = 1.0 * elevation.at(cur_h);
309 const auto v = elevation.at(cur_loc);
310 // can't calculate slope & aspect if any surrounding cell is nodata
311 if (elevation.nodataValue() == v)
312 {
313 valid = false;
314 break;
315 }
316 dem[3 * (i + 1) + (j + 1)] = 1.0 * v;
317 }
318 if (!valid)
319 {
320 break;
321 }
322 }
323 if (valid)
324 {
325 // Horn's algorithm
326 const MathSize dx = ((dem[2] + dem[5] + dem[5] + dem[8])
327 - (dem[0] + dem[3] + dem[3] + dem[6]))
328 / elevation.cellSize();
329 const MathSize dy = ((dem[6] + dem[7] + dem[7] + dem[8])
330 - (dem[0] + dem[1] + dem[1] + dem[2]))
331 / elevation.cellSize();
332 const MathSize key = (dx * dx + dy * dy);
333 auto slope_pct = static_cast<float>(100 * (sqrt(key) / 8.0));
334 s = min(static_cast<SlopeSize>(MAX_SLOPE_FOR_DISTANCE), static_cast<SlopeSize>(round(slope_pct)));
335 static_assert(std::numeric_limits<SlopeSize>::max() >= MAX_SLOPE_FOR_DISTANCE);
336 MathSize aspect_azimuth = 0.0;
337
338 if (s > 0 && (dx != 0 || dy != 0))
339 {
340 aspect_azimuth = atan2(dy, -dx) * M_RADIANS_TO_DEGREES;
341 // NOTE: need to change this out of 'math' direction into 'real' direction (i.e. N is 0, not E)
342 aspect_azimuth = (aspect_azimuth > 90.0) ? (450.0 - aspect_azimuth) : (90.0 - aspect_azimuth);
343 if (aspect_azimuth == 360.0)
344 {
345 aspect_azimuth = 0.0;
346 }
347 }
348
349 a = static_cast<AspectSize>(round(aspect_azimuth));
350 }
351 }
352 const auto cell = Cell{h, s, a, f};
353 values.at(h) = cell;
354#ifdef DEBUG_GRIDS
355#ifndef VLD_RPTHOOK_INSTALL
356 logging::check_equal(cell.row(), r, "Cell row");
357 logging::check_equal(cell.column(), c, "Cell column");
358 const auto v = values.at(h);
359 logging::check_equal(v.row(), r, "Row");
360 logging::check_equal(v.column(), c, "Column");
361 if (!(INVALID_SLOPE == cell.slope() || INVALID_ASPECT == cell.aspect() || INVALID_FUEL_CODE == cell.fuelCode()))
362 {
363 logging::check_equal(cell.slope(), s, "Cell slope");
364 logging::check_equal(v.slope(), s, "Slope");
365 if (0 != s)
366 {
367 logging::check_equal(cell.aspect(), a, "Cell aspect");
368 logging::check_equal(v.aspect(), a, "Aspect");
369 }
370 else
371 {
372 logging::check_equal(cell.aspect(), a, "Cell aspect when slope is 0");
373 logging::check_equal(v.aspect(), 0, "Aspect when slope is 0");
374 }
375 logging::check_equal(v.fuelCode(), f, "Fuel");
376 logging::check_equal(cell.fuelCode(), f, "Cell fuel");
377 }
378 else
379 {
380 logging::check_equal(cell.slope(), INVALID_SLOPE, "Invalid Cell slope");
381 logging::check_equal(cell.aspect(), INVALID_ASPECT, "Invalid Cell aspect");
382 logging::check_equal(cell.fuelCode(), INVALID_FUEL_CODE, "Invalid Cell fuel");
383 logging::check_equal(v.slope(), INVALID_SLOPE, "Invalid slope");
384 logging::check_equal(v.aspect(), INVALID_ASPECT, "Invalid aspect");
385 logging::check_equal(v.fuelCode(), INVALID_FUEL_CODE, "Invalid fuel");
386 }
387#endif
388#endif
389 }
390 // });
391 }
392 }
393 return new topo::CellGrid(
394 fuel.cellSize(),
395 fuel.rows(),
396 fuel.columns(),
397 nodata.fullHash(),
398 nodata,
399 fuel.xllcorner(),
400 fuel.yllcorner(),
401 fuel.xurcorner(),
402 fuel.yurcorner(),
403 string(fuel.proj4()),
404 std::move(values));
405 }
409 shared_ptr<sim::BurnedData> initializeNotBurnable(const CellGrid& cells) const
410 {
411 // shared_ptr<sim::BurnedData> result{};
412 // std::fill(not_burnable_.begin(), not_burnable_.end(), false);
413 // make a template we can copy to reset things
414 auto result = make_shared<sim::BurnedData>();
415 for (Idx r = 0; r < rows(); ++r)
416 {
417 for (Idx c = 0; c < columns(); ++c)
418 {
419 const Location location(r, c);
420 // HACK: just mark outside edge as unburnable so we never need to check
421 bool is_outer = 0 == r
422 || 0 == c
423 || (rows() - 1) == r
424 || (columns() - 1) == c;
425 // (*result)[location.hash()] = (nullptr == fuel::fuel_by_code(cells.at(location).fuelCode()));
426 (*result)[location.hash()] = is_outer || fuel::is_null_fuel(cells.at(location));
427 // if (fuel::is_null_fuel(cell(location)))
428 // {
429 // not_burnable_[location.hash()] = true;
430 // }
431 }
432 }
433 return result;
434 }
440 Environment(const string dir_out,
441 CellGrid* cells,
442 const ElevationSize elevation) noexcept
443 : dir_out_(dir_out),
444 cells_(cells),
447 {
448 // try
449 // {
450 // initializeNotBurnable();
451 // }
452 // catch (...)
453 // {
454 // std::terminate();
455 // }
456 }
463 const string dir_out,
464 const FuelGrid& fuel,
466 const Point& point)
467 : Environment(dir_out,
468 makeCells(fuel,
469 elevation),
470 elevation.at(Location(*elevation.findCoordinates(point, false).get())))
471 {
472 // take elevation at point so that if max grid size changes elevation doesn't
473 logging::note("Start elevation is %d", elevation_);
475 {
476 logging::debug("Saving simulation area");
477 const auto lookup = sim::Settings::fuelLookup();
478 auto convert_to_slope =
479 [&elevation](
480 const Cell& v) -> SlopeSize {
481 return v.slope();
482 };
483 auto convert_to_aspect =
484 [&elevation](
485 const Cell& v) -> AspectSize {
486 return v.aspect();
487 };
488 auto convert_to_area =
489 [&elevation](
490 const ElevationSize v) -> ElevationSize {
491 // need to still be nodata if it was
492 return (v == elevation.nodataValue()) ? v : 3;
493 };
494 auto convert_to_fuelcode =
495 [&lookup](
496 const fuel::FuelType* const value) -> FuelSize {
497 return lookup.fuelToCode(value);
498 };
499 fuel.saveToFile<FuelSize>(
500 dir_out_,
501 "fuel",
502 convert_to_fuelcode);
503 elevation.saveToFile(
504 dir_out_,
505 "dem");
506 // save slope & aspect grids
507 cells_->saveToFile<SlopeSize>(
508 dir_out_,
509 "slope",
510 convert_to_slope,
511 static_cast<SlopeSize>(INVALID_SLOPE));
512 cells_->saveToFile<AspectSize>(
513 dir_out_,
514 "aspect",
515 convert_to_aspect,
516 static_cast<AspectSize>(INVALID_ASPECT));
517 // HACK: make a grid with "3" as the value so if we merge max with it it'll cover up anything else
518 elevation.saveToFile<ElevationSize>(
519 dir_out_,
520 "simulation_area",
521 convert_to_area);
522 logging::debug("Done saving fuel grid");
523 }
524 }
525private:
526 const string dir_out_;
534 shared_ptr<const sim::BurnedData> not_burnable_;
538 ElevationSize elevation_;
539};
540}
A GridData<T, V, const vector<T>> that cannot change once initialized.
Definition ConstantGrid.h:27
constexpr T at(const Location &location) const noexcept override
Value for grid at given Location.
Definition ConstantGrid.h:34
constexpr MathSize xurcorner() const noexcept
Upper right corner X coordinate in meters.
Definition Grid.h:116
constexpr const string & proj4() const noexcept
Proj4 string defining coordinate system for this grid. Must be a UTM projection.
Definition Grid.h:132
constexpr MathSize cellSize() const noexcept
Cell size used for GridBase.
Definition Grid.h:72
constexpr MathSize yllcorner() const noexcept
Lower left corner Y coordinate in meters.
Definition Grid.h:108
constexpr MathSize xllcorner() const noexcept
Lower left corner X coordinate in meters.
Definition Grid.h:100
constexpr MathSize yurcorner() const noexcept
Upper right corner Y coordinate in meters.
Definition Grid.h:124
string saveToFile(const string &dir, const string &base_name, std::function< R(T value)> convert, const R no_data) const
Save GridMap contents to file based on settings.
Definition Grid.h:856
D data
Structure that holds data represented by this GridData.
Definition Grid.h:506
constexpr Idx rows() const noexcept
Number of rows in the GridBase.
Definition Grid.h:257
constexpr Idx columns() const noexcept
Number of columns in the GridBase.
Definition Grid.h:265
An FBP fuel type.
Definition FuelType.h:55
static constexpr FuelCodeSize safeCode(const FuelType *fuel)
Convert FuelType to its code, or 0 if nullptr.
Definition FuelType.h:62
A specific Event scheduled in a specific Scenario.
Definition Event.h:19
Map of the percentage of simulations in which a Cell burned in each intensity category.
Definition ProbabilityMap.h:22
static const fuel::FuelLookup & fuelLookup() noexcept
Fuel lookup table.
Definition Settings.cpp:596
static bool saveSimulationArea() noexcept
Whether or not to save simulation area grids.
Definition Settings.cpp:672
A Position with a Slope, Aspect, and Fuel.
Definition Cell.h:20
static constexpr AspectSize aspect(const SpreadKey value) noexcept
Aspect (degrees)
Definition Cell.h:126
static constexpr FuelCodeSize fuelCode(const SpreadKey value) noexcept
Fuel.
Definition Cell.h:136
static constexpr SlopeSize slope(const SpreadKey value) noexcept
Slope (degrees)
Definition Cell.h:145
The area that a Model is run for, with Fuel, Slope, and Aspect grids.
Definition Environment.h:49
ElevationSize elevation_
Elevation at StartPoint.
Definition Environment.h:538
void resetBurnedData(sim::BurnedData *data) const noexcept
Reset with known non-fuel cells.
Definition Environment.h:243
Environment(const string dir_out, const FuelGrid &fuel, const ElevationGrid &elevation, const Point &point)
Load from rasters.
Definition Environment.h:462
Environment & operator=(const Environment &rhs) noexcept=default
Copy assignment.
static Environment load(const string dir_out, const Point &point, const string &in_fuel, const string &in_elevation)
Load from rasters.
Definition Environment.cpp:23
shared_ptr< const sim::BurnedData > not_burnable_
BurnedData of cells that are not burnable.
Definition Environment.h:534
Cell offset(const sim::Event &event, const Idx row, const Idx column) const
Cell at Location with given hash.
Definition Environment.h:195
shared_ptr< sim::BurnedData > initializeNotBurnable(const CellGrid &cells) const
Creates a map of areas that are not burnable either because of fuel or the initial perimeter.
Definition Environment.h:409
constexpr const string & proj4() const
UTM projection that this uses.
Definition Environment.h:113
CellGrid * cells_
Cells representing Environment.
Definition Environment.h:530
unique_ptr< data::GridMap< Other > > makeMap(const Other nodata) const
Create a GridMap<Other> covering this Environment.
Definition Environment.h:226
Environment & operator=(Environment &&rhs) noexcept=default
Move assignment.
Environment(const string dir_out, CellGrid *cells, const ElevationSize elevation) noexcept
Construct from cells and elevation.
Definition Environment.h:440
constexpr Idx columns() const
Number of columns in grid.
Definition Environment.h:129
static Environment loadEnvironment(const string dir_out, const string &path, const Point &point, const string &perimeter, int year)
Load from rasters in folder that have same projection as Perimeter.
Definition Environment.cpp:65
Environment(Environment &&rhs) noexcept=default
Move constructor.
constexpr Cell cell(const Position< P > &position) const
Cell at given Location.
Definition Environment.h:170
static CellGrid * makeCells(const FuelGrid &fuel, const ElevationGrid &elevation)
Combine rasters into ConstantGrid<Cell, Topo>
Definition Environment.h:255
unique_ptr< sim::BurnedData > makeBurnedData() const
Create BurnedData and set burned bits based on Perimeter.
Definition Environment.h:234
sim::ProbabilityMap * makeProbabilityMap(DurationSize time, DurationSize start_time, int min_value, int low_max, int med_max, int max_value) const
Make a ProbabilityMap that covers this Environment.
Definition Environment.cpp:49
Cell cell(const Idx row, const Idx column) const
Cell at given row and column.
Definition Environment.h:160
constexpr MathSize cellSize() const
Cell width and height (m)
Definition Environment.h:137
Environment(const Environment &rhs) noexcept=default
Copy constructor.
constexpr Idx rows() const
Number of rows in grid.
Definition Environment.h:121
unique_ptr< Coordinates > findCoordinates(const Point &point, bool flipped) const
Determine Coordinates in the grid for the Point.
Definition Environment.cpp:164
constexpr ElevationSize elevation() const
Elevation of the origin Point.
Definition Environment.h:145
Definition Location.h:229
A geographic location in lat/long coordinates.
Definition Point.h:13
A Position with a row and column.
Definition Location.h:57
constexpr Idx row() const noexcept
Row.
Definition Location.h:64
constexpr HashSize hash() const noexcept
Hash derived from row and column.
Definition Location.h:80
constexpr Idx column() const noexcept
Column.
Definition Location.h:72
Definition util.h:14