flood-adapt 1.1.5__tar.gz → 2.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/PKG-INFO +2 -2
  2. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/__init__.py +1 -1
  3. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/adapter/fiat_adapter.py +57 -29
  4. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/adapter/sfincs_adapter.py +164 -93
  5. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/config/hazard.py +1 -0
  6. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/database_builder.py +41 -46
  7. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/dbs_classes/database.py +57 -76
  8. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/dbs_classes/dbs_scenario.py +1 -2
  9. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/dbs_classes/interface/database.py +8 -31
  10. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/events/events.py +0 -57
  11. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/forcing/forcing.py +1 -46
  12. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt.egg-info/PKG-INFO +2 -2
  13. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt.egg-info/requires.txt +1 -1
  14. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/pyproject.toml +19 -19
  15. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/LICENSE +0 -0
  16. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/README.md +0 -0
  17. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/adapter/__init__.py +0 -0
  18. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/adapter/interface/__init__.py +0 -0
  19. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/adapter/interface/hazard_adapter.py +0 -0
  20. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/adapter/interface/impact_adapter.py +0 -0
  21. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/adapter/interface/model_adapter.py +0 -0
  22. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/adapter/interface/offshore.py +0 -0
  23. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/adapter/sfincs_offshore.py +0 -0
  24. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/config/__init__.py +0 -0
  25. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/config/config.py +0 -0
  26. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/config/fiat.py +0 -0
  27. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/config/gui.py +0 -0
  28. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/config/impacts.py +0 -0
  29. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/config/sfincs.py +0 -0
  30. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/config/site.py +0 -0
  31. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/__init__.py +0 -0
  32. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/metrics_utils.py +0 -0
  33. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/default_units/imperial.toml +0 -0
  34. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/default_units/metric.toml +0 -0
  35. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/green_infra_table/green_infra_lookup_table.csv +0 -0
  36. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/icons/black_down_48x48.png +0 -0
  37. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/icons/black_left_48x48.png +0 -0
  38. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/icons/black_right_48x48.png +0 -0
  39. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/icons/black_up_48x48.png +0 -0
  40. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-16_white_down.png +0 -0
  41. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-16_white_left.png +0 -0
  42. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-16_white_right.png +0 -0
  43. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-16_white_up.png +0 -0
  44. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-24_black_down.png +0 -0
  45. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-24_black_left.png +0 -0
  46. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-24_black_right.png +0 -0
  47. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-24_black_up.png +0 -0
  48. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-24_white_left.png +0 -0
  49. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/icons/icons8-triangle-arrow-24_white_right.png +0 -0
  50. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/icons/white_down_48x48.png +0 -0
  51. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/icons/white_left_48x48.png +0 -0
  52. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/icons/white_right_48x48.png +0 -0
  53. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/icons/white_up_48x48.png +0 -0
  54. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/infographics/images/ambulance.png +0 -0
  55. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/infographics/images/car.png +0 -0
  56. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/infographics/images/cart.png +0 -0
  57. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/infographics/images/firetruck.png +0 -0
  58. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/infographics/images/hospital.png +0 -0
  59. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/infographics/images/house.png +0 -0
  60. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/infographics/images/info.png +0 -0
  61. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/infographics/images/money.png +0 -0
  62. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/infographics/images/person.png +0 -0
  63. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/infographics/images/school.png +0 -0
  64. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/infographics/images/truck.png +0 -0
  65. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/infographics/images/walking_person.png +0 -0
  66. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/infographics/styles.css +0 -0
  67. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/database_builder/templates/output_layers/bin_colors.toml +0 -0
  68. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/dbs_classes/__init__.py +0 -0
  69. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/dbs_classes/dbs_benefit.py +0 -0
  70. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/dbs_classes/dbs_event.py +0 -0
  71. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/dbs_classes/dbs_measure.py +0 -0
  72. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/dbs_classes/dbs_projection.py +0 -0
  73. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/dbs_classes/dbs_static.py +0 -0
  74. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/dbs_classes/dbs_strategy.py +0 -0
  75. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/dbs_classes/dbs_template.py +0 -0
  76. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/dbs_classes/interface/element.py +0 -0
  77. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/dbs_classes/interface/static.py +0 -0
  78. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/flood_adapt.py +0 -0
  79. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/misc/__init__.py +0 -0
  80. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/misc/database_user.py +0 -0
  81. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/misc/debug_timer.py +0 -0
  82. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/misc/exceptions.py +0 -0
  83. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/misc/log.py +0 -0
  84. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/misc/path_builder.py +0 -0
  85. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/misc/utils.py +0 -0
  86. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/__init__.py +0 -0
  87. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/benefits/__init__.py +0 -0
  88. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/benefits/benefits.py +0 -0
  89. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/events/__init__.py +0 -0
  90. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/events/event_factory.py +0 -0
  91. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/events/event_set.py +0 -0
  92. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/events/historical.py +0 -0
  93. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/events/hurricane.py +0 -0
  94. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/events/synthetic.py +0 -0
  95. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/forcing/__init__.py +0 -0
  96. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/forcing/csv.py +0 -0
  97. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/forcing/discharge.py +0 -0
  98. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/forcing/forcing_factory.py +0 -0
  99. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/forcing/meteo_handler.py +0 -0
  100. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/forcing/netcdf.py +0 -0
  101. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/forcing/plotting.py +0 -0
  102. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/forcing/rainfall.py +0 -0
  103. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/forcing/tide_gauge.py +0 -0
  104. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/forcing/time_frame.py +0 -0
  105. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/forcing/timeseries.py +0 -0
  106. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/forcing/unit_system.py +0 -0
  107. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/forcing/waterlevels.py +0 -0
  108. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/forcing/wind.py +0 -0
  109. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/measures/__init__.py +0 -0
  110. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/measures/measure_factory.py +0 -0
  111. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/measures/measures.py +0 -0
  112. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/object_model.py +0 -0
  113. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/output/floodmap.py +0 -0
  114. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/projections/__init__.py +0 -0
  115. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/projections/projections.py +0 -0
  116. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/scenarios/__init__.py +0 -0
  117. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/scenarios/scenarios.py +0 -0
  118. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/strategies/__init__.py +0 -0
  119. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/objects/strategies/strategies.py +0 -0
  120. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/workflows/__init__.py +0 -0
  121. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/workflows/benefit_runner.py +0 -0
  122. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt/workflows/scenario_runner.py +0 -0
  123. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt.egg-info/SOURCES.txt +0 -0
  124. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt.egg-info/dependency_links.txt +0 -0
  125. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt.egg-info/not-zip-safe +0 -0
  126. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/flood_adapt.egg-info/top_level.txt +0 -0
  127. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/setup.cfg +0 -0
  128. {flood_adapt-1.1.5 → flood_adapt-2.0.0}/tests/test_flood_adapt.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: flood-adapt
3
- Version: 1.1.5
3
+ Version: 2.0.0
4
4
  Summary: A software package support system which can be used to assess the benefits and costs of flood resilience measures
5
5
  Author-email: Gundula Winter <Gundula.Winter@deltares.nl>, Panos Athanasiou <Panos.Athanasiou@deltares.nl>, Frederique de Groen <Frederique.deGroen@deltares.nl>, Tim de Wilde <Tim.deWilde@deltares.nl>, Julian Hofer <Julian.Hofer@deltares.nl>, Daley Adrichem <Daley.Adrichem@deltares.nl>, Luuk Blom <Luuk.Blom@deltares.nl>
6
6
  License: ====================================================
@@ -717,7 +717,7 @@ Requires-Dist: fiona<2.0,>=1.0
717
717
  Requires-Dist: geojson<4.0,>=3.0
718
718
  Requires-Dist: geopandas<2.0,>=1.0
719
719
  Requires-Dist: hydromt-fiat<1.0,>=0.5.9
720
- Requires-Dist: hydromt-sfincs<2.0,>=1.2.2
720
+ Requires-Dist: hydromt-sfincs[quadtree]<2.0,>=1.2.2
721
721
  Requires-Dist: numpy<2.0,>=1.0
722
722
  Requires-Dist: numpy-financial<2.0,>=1.0
723
723
  Requires-Dist: pandas<3.0,>=2.0
@@ -1,5 +1,5 @@
1
1
  # has to be here at the start to avoid circular imports
2
- __version__ = "1.1.5"
2
+ __version__ = "2.0.0"
3
3
 
4
4
  from flood_adapt import adapter, database_builder, dbs_classes, objects
5
5
  from flood_adapt.config.config import Settings
@@ -258,7 +258,7 @@ class FiatAdapter(IImpactAdapter):
258
258
  map_type=floodmap.map_type,
259
259
  var=var,
260
260
  is_risk=is_risk,
261
- units=us.UnitTypesLength.meters,
261
+ units=self.database.site.sfincs.config.floodmap_units,
262
262
  )
263
263
 
264
264
  # Save any changes made to disk as well
@@ -489,7 +489,10 @@ class FiatAdapter(IImpactAdapter):
489
489
  fiat_results_path = impacts_output_path.joinpath(
490
490
  f"Impacts_detailed_{scenario.name}.csv"
491
491
  )
492
- self.outputs["table"].to_csv(fiat_results_path, index=False)
492
+ try:
493
+ self.outputs["table"].to_csv(fiat_results_path, index=False)
494
+ except Exception as e:
495
+ logger.error(f"Error while saving detailed impacts file csv: {e}")
493
496
 
494
497
  # Add exceedance probabilities if needed (only for risk)
495
498
  if mode == Mode.risk:
@@ -500,13 +503,16 @@ class FiatAdapter(IImpactAdapter):
500
503
  )
501
504
  with open(config_path, mode="rb") as fp:
502
505
  config = tomli.load(fp)["flood_exceedance"]
503
- self.add_exceedance_probability(
504
- column=config[
505
- "column"
506
- ], # TODO check how to the correct version of column
507
- threshold=config["threshold"],
508
- period=config["period"],
509
- )
506
+ try:
507
+ self.add_exceedance_probability(
508
+ column=config[
509
+ "column"
510
+ ], # TODO check how to the correct version of column
511
+ threshold=config["threshold"],
512
+ period=config["period"],
513
+ )
514
+ except Exception as e:
515
+ logger.error(f"Error while adding exceedance probabilities: {e}")
510
516
 
511
517
  # Create the infometrics files
512
518
  if mode == Mode.risk:
@@ -531,7 +537,10 @@ class FiatAdapter(IImpactAdapter):
531
537
  metrics_outputs_path = impacts_output_path.parent.joinpath(
532
538
  f"Infometrics_{scenario.name}.csv"
533
539
  )
534
- self.create_infometrics(metric_config_paths, metrics_outputs_path)
540
+ try:
541
+ self.create_infometrics(metric_config_paths, metrics_outputs_path)
542
+ except Exception as e:
543
+ logger.error(f"Error while creating infometrics: {e}")
535
544
 
536
545
  # Get paths of created aggregated infometrics
537
546
  aggr_metrics_paths = list(
@@ -543,20 +552,28 @@ class FiatAdapter(IImpactAdapter):
543
552
  config_base_path = self.database.static_path.joinpath(
544
553
  "templates", "Infographics"
545
554
  )
546
- self.create_infographics(
547
- name=scenario.name,
548
- output_base_path=impacts_output_path.parent,
549
- config_base_path=config_base_path,
550
- metrics_path=metrics_outputs_path,
551
- mode=mode,
552
- )
555
+ try:
556
+ self.create_infographics(
557
+ name=scenario.name,
558
+ output_base_path=impacts_output_path.parent,
559
+ config_base_path=config_base_path,
560
+ metrics_path=metrics_outputs_path,
561
+ mode=mode,
562
+ )
563
+ except Exception as e:
564
+ logger.error(f"Error while creating infographics: {e}")
553
565
 
554
566
  # Calculate equity based damages
555
567
  if mode == Mode.risk:
556
568
  for file in aggr_metrics_paths:
557
569
  # Load metrics
558
570
  aggr_label = file.stem.split(f"{metrics_outputs_path.stem}_")[-1]
559
- self.add_equity(aggr_label=aggr_label, metrics_path=file)
571
+ try:
572
+ self.add_equity(aggr_label=aggr_label, metrics_path=file)
573
+ except Exception as e:
574
+ logger.error(
575
+ f"Error while calculating equity metrics for {aggr_label}: {e}"
576
+ )
560
577
 
561
578
  # Save aggregated metrics to shapefiles
562
579
  for file in aggr_metrics_paths:
@@ -564,24 +581,35 @@ class FiatAdapter(IImpactAdapter):
564
581
  output_path = impacts_output_path.joinpath(
565
582
  f"Impacts_aggregated_{scenario.name}_{aggr_label}.gpkg"
566
583
  )
567
- self.save_aggregation_spatial(
568
- aggr_label=aggr_label, metrics_path=file, output_path=output_path
569
- )
584
+ try:
585
+ self.save_aggregation_spatial(
586
+ aggr_label=aggr_label, metrics_path=file, output_path=output_path
587
+ )
588
+ except Exception as e:
589
+ logger.error(
590
+ f"Error while saving aggregation spatial for {aggr_label}: {e}"
591
+ )
570
592
 
571
593
  # Merge points data to building footprints
572
- self.save_building_footprints(
573
- output_path=impacts_output_path.joinpath(
574
- f"Impacts_building_footprints_{scenario.name}.gpkg"
594
+ try:
595
+ self.save_building_footprints(
596
+ output_path=impacts_output_path.joinpath(
597
+ f"Impacts_building_footprints_{scenario.name}.gpkg"
598
+ )
575
599
  )
576
- )
600
+ except Exception as e:
601
+ logger.error(f"Error while saving building footprints: {e}")
577
602
 
578
603
  # Create a roads spatial file
579
604
  if self.config.roads_file_name:
580
- self.save_roads(
581
- output_path=impacts_output_path.joinpath(
582
- f"Impacts_roads_{scenario.name}.gpkg"
605
+ try:
606
+ self.save_roads(
607
+ output_path=impacts_output_path.joinpath(
608
+ f"Impacts_roads_{scenario.name}.gpkg"
609
+ )
583
610
  )
584
- )
611
+ except Exception as e:
612
+ logger.error(f"Error while saving roads spatial file: {e}")
585
613
 
586
614
  logger.info("Delft-FIAT post-processing complete!")
587
615
 
@@ -15,12 +15,12 @@ import plotly.express as px
15
15
  import pyproj
16
16
  import shapely
17
17
  import xarray as xr
18
+ import xugrid as xu
18
19
  from cht_cyclones.tropical_cyclone import TropicalCyclone
19
20
  from cht_tide.read_bca import SfincsBoundary
20
21
  from cht_tide.tide_predict import predict
21
22
  from hydromt_sfincs import SfincsModel as HydromtSfincsModel
22
23
  from hydromt_sfincs.quadtree import QuadtreeGrid
23
- from numpy import matlib
24
24
  from shapely.affinity import translate
25
25
 
26
26
  from flood_adapt.adapter.interface.hazard_adapter import IHazardAdapter
@@ -308,9 +308,9 @@ class SfincsAdapter(IHazardAdapter):
308
308
  ):
309
309
  raise RuntimeError("SFINCS was not run successfully!")
310
310
 
311
+ self.write_water_level_map(scenario)
311
312
  self.write_floodmap_geotiff(scenario)
312
313
  self.plot_wl_obs(scenario)
313
- self.write_water_level_map(scenario)
314
314
 
315
315
  def set_timing(self, time: TimeFrame):
316
316
  """Set model reference times."""
@@ -386,20 +386,27 @@ class SfincsAdapter(IHazardAdapter):
386
386
  def get_model_root(self) -> Path:
387
387
  return Path(self._model.root)
388
388
 
389
- def get_mask(self):
389
+ def get_mask(self) -> xr.DataArray:
390
390
  """Get mask with inactive cells from model."""
391
- mask = self._model.grid["msk"]
391
+ mask = self._model.mask
392
392
  return mask
393
393
 
394
- def get_bedlevel(self):
394
+ def get_bedlevel(self) -> xr.DataArray:
395
395
  """Get bed level from model."""
396
396
  self._model.read_results()
397
397
  zb = self._model.results["zb"]
398
+ # Convert bed level from meters to floodmap units
399
+ conversion = us.UnitfulLength(
400
+ value=1.0, units=us.UnitTypesLength.meters
401
+ ).convert(self.settings.config.floodmap_units)
402
+ zb = zb * conversion
403
+ zb.attrs["units"] = self.settings.config.floodmap_units.value
398
404
  return zb
399
405
 
400
406
  def get_model_boundary(self) -> gpd.GeoDataFrame:
401
407
  """Get bounding box from model."""
402
- return self._model.region
408
+ boundary = self._model.region[["geometry"]]
409
+ return boundary
403
410
 
404
411
  def get_model_grid(self) -> QuadtreeGrid:
405
412
  """Get grid from model.
@@ -411,6 +418,15 @@ class SfincsAdapter(IHazardAdapter):
411
418
  """
412
419
  return self._model.quadtree
413
420
 
421
+ def get_finest_res(self) -> float:
422
+ """Get the finest resolution of the model grid."""
423
+ if self._model.grid_type == "quadtree":
424
+ res0 = self._model.quadtree.dx
425
+ res = res0 / 2**self._model.quadtree.nr_refinement_levels
426
+ else:
427
+ res = self._model.res[0]
428
+ return res
429
+
414
430
  # Forcing properties
415
431
  @property
416
432
  def waterlevels(self) -> xr.Dataset | xr.DataArray | None:
@@ -543,7 +559,7 @@ class SfincsAdapter(IHazardAdapter):
543
559
  sim_path : Path, optional
544
560
  Path to the simulation folder, by default None.
545
561
  """
546
- logger.info("Writing flood maps to geotiff")
562
+ logger.info("Writing flood maps to geotiff.")
547
563
  results_path = self._get_result_path(scenario)
548
564
  sim_path = sim_path or self._get_simulation_path(scenario)
549
565
  demfile = self.database.static_path / "dem" / self.settings.dem.filename
@@ -560,13 +576,8 @@ class SfincsAdapter(IHazardAdapter):
560
576
 
561
577
  floodmap_fn = results_path / f"FloodMap_{scenario.name}.tif"
562
578
 
563
- # convert zsmax from meters to floodmap units
564
- floodmap_conversion = us.UnitfulLength(
565
- value=1.0, units=us.UnitTypesLength.meters
566
- ).convert(self.settings.config.floodmap_units)
567
-
568
579
  utils.downscale_floodmap(
569
- zsmax=floodmap_conversion * zsmax,
580
+ zsmax=zsmax,
570
581
  dep=dem_conversion * dem,
571
582
  hmin=0.01,
572
583
  floodmap_fn=floodmap_fn.as_posix(),
@@ -582,7 +593,23 @@ class SfincsAdapter(IHazardAdapter):
582
593
 
583
594
  with SfincsAdapter(model_root=sim_path) as model:
584
595
  zsmax = model._get_zsmax()
585
- zsmax.to_netcdf(results_path / "max_water_level_map.nc")
596
+ if hasattr(zsmax, "ugrid"):
597
+ # First write netcdf with quadtree water levels
598
+ zsmax.to_netcdf(results_path / "max_water_level_map_qt.nc")
599
+ # Rasterize to regular grid with the finest resolution
600
+ zsmax = self._rasterize_quadtree(zsmax)
601
+ # Add CRS to the rasterized xarray
602
+ zsmax = zsmax.rio.write_crs(model._model.config["epsg"])
603
+ # Save as a Cloud Optimized GeoTIFF (COG)
604
+ zsmax.rio.to_raster(
605
+ results_path / "max_water_level_map.tif",
606
+ driver="COG",
607
+ compress="deflate",
608
+ dtype="float32",
609
+ nodata=np.nan,
610
+ OVERVIEW_RESAMPLING="nearest",
611
+ tags={"units": self.settings.config.floodmap_units.value},
612
+ )
586
613
 
587
614
  def plot_wl_obs(
588
615
  self,
@@ -727,83 +754,117 @@ class SfincsAdapter(IHazardAdapter):
727
754
 
728
755
  TODO: make this robust and more efficient for bigger datasets.
729
756
  """
757
+ # Check if the scenario is a risk scenario
730
758
  event: EventSet = self.database.events.get(scenario.event, load_all=True)
731
759
  if not isinstance(event, EventSet):
732
760
  raise ValueError("This function is only available for risk scenarios.")
761
+ logger.info("Calculating flood risk maps, this may take some time.")
733
762
 
763
+ # Get the simulation paths and result path
734
764
  result_path = self._get_result_path(scenario)
735
765
  sim_paths = [
736
766
  self._get_simulation_path(scenario, sub_event=sub_event)
737
767
  for sub_event in event._events
738
768
  ]
739
769
 
770
+ # Get the required return periods for flood maps
771
+ floodmap_rp = self.database.site.fiat.risk.return_periods
772
+
773
+ # Get the frequencies for each sub-event
774
+ frequencies = [sub_event.frequency for sub_event in event.sub_events]
740
775
  phys_proj = self.database.projections.get(
741
776
  scenario.projection
742
777
  ).physical_projection
743
778
 
744
- floodmap_rp = self.database.site.fiat.risk.return_periods
745
- frequencies = [sub_event.frequency for sub_event in event.sub_events]
746
-
747
- # adjust storm frequency for hurricane events
779
+ # adjust storm frequency for hurricane events if provided in the projection
748
780
  if not math.isclose(phys_proj.storm_frequency_increase, 0, abs_tol=1e-9):
749
781
  storminess_increase = phys_proj.storm_frequency_increase / 100.0
750
782
  for ii, event in enumerate(event._events):
751
783
  if event.template == Template.Hurricane:
752
784
  frequencies[ii] = frequencies[ii] * (1 + storminess_increase)
753
785
 
786
+ # Read static mask and bed level from the first simulation path
754
787
  with SfincsAdapter(model_root=sim_paths[0]) as dummymodel:
755
788
  # read mask and bed level
756
789
  mask = dummymodel.get_mask()
757
790
  zb = dummymodel.get_bedlevel()
758
791
 
792
+ # Read the max water level maps from each simulation path
759
793
  zs_maps = []
760
794
  for simulation_path in sim_paths:
761
795
  # read zsmax data from overland sfincs model
762
796
  with SfincsAdapter(model_root=simulation_path) as sim:
763
797
  zsmax = sim._get_zsmax().load()
764
- zs_maps.append(zsmax)
798
+ zs_maps.append(zsmax.values)
765
799
 
766
- # Create RP flood maps
767
- logger.info("Calculating flood risk maps, this may take some time")
800
+ # Calculate return period flood maps
801
+ # All units are in the floodmap units
768
802
  rp_flood_maps = self.calc_rp_maps(
769
803
  floodmaps=zs_maps,
770
804
  frequencies=frequencies,
771
- zb=zb,
772
- mask=mask,
805
+ zb=zb.values,
806
+ mask=mask.values,
773
807
  return_periods=floodmap_rp,
774
808
  )
775
809
 
810
+ # For each return period, save water level and flood depth maps
776
811
  for ii, rp in enumerate(floodmap_rp):
777
- zs_rp_single = rp_flood_maps[ii]
778
- zs_rp_single = zs_rp_single.rio.write_crs(
779
- zsmax.raster.crs
780
- ) # , inplace=True)
781
- zs_rp_single = zs_rp_single.to_dataset(name="risk_map").transpose()
782
- fn_rp = result_path / f"RP_{rp:04d}_maps.nc"
783
- zs_rp_single.to_netcdf(fn_rp)
784
-
785
- # write geotiff
812
+ # Prepare data array for the return period flood map
813
+ zs_rp_single = xr.DataArray(
814
+ rp_flood_maps[ii],
815
+ name="zsmax",
816
+ attrs={"units": self.settings.config.floodmap_units.value},
817
+ )
818
+ # If model is quadtree, write the quadtree netcdf with water levels since it is needed for visualizations
819
+ if self._model.grid_type == "quadtree":
820
+ # Use mask grid
821
+ zs_rp_single = xu.UgridDataArray.from_data(
822
+ zs_rp_single, grid=mask.grid, facet="face"
823
+ )
824
+ # Save to netcdf
825
+ zs_rp_single.to_netcdf(
826
+ result_path / f"RP_{rp:04d}_max_water_level_map_qt.nc"
827
+ )
828
+ # Rasterize to regular grid with the finest resolution
829
+ zs_rp_single = self._rasterize_quadtree(zs_rp_single)
830
+ # Prepare regular grid water level map
831
+ elif self._model.grid_type == "regular":
832
+ # Create a DataArray with the mask coordinates
833
+ zs_rp_single = xr.DataArray(
834
+ zs_rp_single,
835
+ dims=("y", "x"),
836
+ coords={"y": mask.y, "x": mask.x},
837
+ name="zsmax",
838
+ )
839
+ else:
840
+ raise ValueError("unsupported sfincs model type")
841
+ # Write COG geotiff with water levels
842
+ zs_rp_single = zs_rp_single.rio.write_crs(self._model.crs)
843
+ fn_rp = result_path / f"RP_{rp:04d}_max_water_level_map.tif"
844
+ zs_rp_single.transpose("y", "x").rio.to_raster(
845
+ fn_rp,
846
+ driver="COG",
847
+ compress="deflate",
848
+ dtype="float32",
849
+ nodata=np.nan,
850
+ OVERVIEW_RESAMPLING="nearest",
851
+ tags={"units": self.settings.config.floodmap_units.value},
852
+ )
853
+
786
854
  # dem file for high resolution flood depth map
787
855
  demfile = self.database.static_path / "dem" / self.settings.dem.filename
856
+ # convert dem from dem units to floodmap units
857
+ dem_conversion = us.UnitfulLength(
858
+ value=1.0, units=self.settings.dem.units
859
+ ).convert(self.settings.config.floodmap_units)
788
860
 
789
861
  # writing the geotiff to the scenario results folder
790
862
  with SfincsAdapter(model_root=sim_paths[0]) as dummymodel:
791
863
  dem = dummymodel._model.data_catalog.get_rasterdataset(demfile)
792
- zsmax = zs_rp_single.to_array().squeeze().transpose()
793
- floodmap_fn = fn_rp.with_suffix(".tif")
794
-
795
- # convert dem from dem units to floodmap units
796
- dem_conversion = us.UnitfulLength(
797
- value=1.0, units=self.settings.dem.units
798
- ).convert(self.settings.config.floodmap_units)
799
-
800
- # convert zsmax from meters to floodmap units
801
- floodmap_conversion = us.UnitfulLength(
802
- value=1.0, units=us.UnitTypesLength.meters
803
- ).convert(self.settings.config.floodmap_units)
864
+ floodmap_fn = result_path / f"RP_{rp:04d}_FloodMap.tif"
804
865
 
805
866
  utils.downscale_floodmap(
806
- zsmax=floodmap_conversion * zsmax,
867
+ zsmax=zs_rp_single,
807
868
  dep=dem_conversion * dem,
808
869
  hmin=0.01,
809
870
  floodmap_fn=floodmap_fn.as_posix(),
@@ -1525,9 +1586,9 @@ class SfincsAdapter(IHazardAdapter):
1525
1586
  if isinstance(event, EventSet):
1526
1587
  map_fn = []
1527
1588
  for rp in self.database.site.fiat.risk.return_periods:
1528
- map_fn.append(results_path / f"RP_{rp:04d}_maps.nc")
1589
+ map_fn.append(results_path / f"RP_{rp:04d}_max_water_level_map.tif")
1529
1590
  elif isinstance(event, Event):
1530
- map_fn = [results_path / "max_water_level_map.nc"]
1591
+ map_fn = [results_path / "max_water_level_map.tif"]
1531
1592
  else:
1532
1593
  raise ValueError(f"Unsupported mode: {event.mode}")
1533
1594
 
@@ -1541,7 +1602,14 @@ class SfincsAdapter(IHazardAdapter):
1541
1602
  """Read zsmax file and return absolute maximum water level over entire simulation."""
1542
1603
  self._model.read_results()
1543
1604
  zsmax = self._model.results["zsmax"].max(dim="timemax")
1544
- zsmax.attrs["units"] = "m"
1605
+
1606
+ # Convert from meters to floodmap units
1607
+ floodmap_conversion = us.UnitfulLength(
1608
+ value=1.0, units=us.UnitTypesLength.meters
1609
+ ).convert(self.settings.config.floodmap_units)
1610
+ zsmax = zsmax * floodmap_conversion
1611
+ zsmax.attrs["units"] = self.settings.config.floodmap_units.value
1612
+
1545
1613
  return zsmax
1546
1614
 
1547
1615
  def _get_zs_points(self):
@@ -1571,6 +1639,21 @@ class SfincsAdapter(IHazardAdapter):
1571
1639
  )
1572
1640
  return df, gdf
1573
1641
 
1642
+ def _rasterize_quadtree(self, zsmax: xu.UgridDataArray) -> xr.DataArray:
1643
+ """Rasterize the zsmax UgridDataArray to a regular grid."""
1644
+ xmin, ymin, xmax, ymax = zsmax.ugrid.bounds["mesh2d"]
1645
+ d = self.get_finest_res()
1646
+ x = np.arange(xmin + 0.5 * d, xmax, d)
1647
+ y = np.arange(ymax - 0.5 * d, ymin, -d)
1648
+ # Create a template DataArray with the desired x and y coordinates
1649
+ template = xr.DataArray(
1650
+ np.zeros((len(y), len(x))),
1651
+ coords={"y": y, "x": x},
1652
+ dims=("y", "x"),
1653
+ )
1654
+ zsmax = zsmax.ugrid.rasterize_like(template)
1655
+ return zsmax
1656
+
1574
1657
  def _create_spw_file_from_track(
1575
1658
  self,
1576
1659
  track_forcing: Union[RainfallTrack, WindTrack],
@@ -1810,12 +1893,12 @@ class SfincsAdapter(IHazardAdapter):
1810
1893
 
1811
1894
  @staticmethod
1812
1895
  def calc_rp_maps(
1813
- floodmaps: list[xr.DataArray],
1896
+ floodmaps: list[np.ndarray],
1814
1897
  frequencies: list[float],
1815
- zb: xr.DataArray,
1816
- mask: xr.DataArray,
1898
+ zb: np.ndarray,
1899
+ mask: np.ndarray,
1817
1900
  return_periods: list[float],
1818
- ) -> list[xr.DataArray]:
1901
+ ) -> list[np.ndarray]:
1819
1902
  """
1820
1903
  Calculate return period (RP) flood maps from a set of flood simulation results.
1821
1904
 
@@ -1824,26 +1907,25 @@ class SfincsAdapter(IHazardAdapter):
1824
1907
  using exceedance probabilities and handles masked or dry cells appropriately.
1825
1908
 
1826
1909
  Args:
1827
- floodmaps (list[xr.DataArray]): List of water level maps (xarray DataArrays), one for each simulation.
1910
+ floodmaps (list[np.ndarray]): List of water level maps (NumPy arrays), one for each simulation.
1828
1911
  frequencies (list[float]): List of frequencies (probabilities of occurrence) corresponding to each floodmap.
1829
1912
  zb (np.ndarray): Array of bed elevations for each grid cell.
1830
- mask (xr.DataArray): Mask indicating valid (1) and invalid (0) grid cells.
1913
+ mask (np.ndarray): Mask indicating valid (1) and invalid (0) grid cells.
1831
1914
  return_periods (list[float]): List of return periods (in years) for which to generate hazard maps.
1832
1915
 
1833
1916
  Returns
1834
1917
  -------
1835
- list[xr.DataArray]: List of xarray DataArrays, each representing the hazard map for a given return period.
1836
- Each DataArray contains water levels (meters) for the corresponding return period.
1918
+ list[np.ndarray]: List of NumPy arrays, each representing the hazard map for a given return period.
1919
+ Each array contains water levels (meters) for the corresponding return period.
1837
1920
  """
1838
- floodmaps = floodmaps.copy() # avoid modifying the original list
1839
- # Check that all floodmaps have the same shape and dimensions
1921
+ floodmaps = [np.asarray(fm) for fm in floodmaps]
1922
+ # Check that all floodmaps have the same shape
1840
1923
  first_shape = floodmaps[0].shape
1841
- first_dims = floodmaps[0].dims
1842
1924
  for i, floodmap in enumerate(floodmaps):
1843
- if floodmap.shape != first_shape or floodmap.dims != first_dims:
1925
+ if floodmap.shape != first_shape:
1844
1926
  raise ValueError(
1845
- f"Floodmap at index {i} does not match the shape or dimensions of the first floodmap. "
1846
- f"Expected shape {first_shape} and dims {first_dims}, got shape {floodmap.shape} and dims {floodmap.dims}."
1927
+ f"Floodmap at index {i} does not match the shape of the first floodmap. "
1928
+ f"Expected shape {first_shape}, got shape {floodmap.shape}."
1847
1929
  )
1848
1930
 
1849
1931
  # Check that zb and mask have the same shape
@@ -1863,22 +1945,21 @@ class SfincsAdapter(IHazardAdapter):
1863
1945
  f"Floodmap shape: {first_shape}, zb shape: {zb.shape}, mask shape: {mask.shape}."
1864
1946
  )
1865
1947
 
1866
- # stack dimensions if floodmaps are 2D
1867
- if len(floodmaps[0].shape) > 1:
1868
- stacking = True
1869
- for i, floodmap in enumerate(floodmaps):
1870
- floodmaps[i] = floodmap.stack(z=("x", "y"))
1871
- zb = zb.stack(z=("x", "y"))
1872
- mask = mask.stack(z=("x", "y"))
1873
- else:
1874
- stacking = False
1875
-
1948
+ # If input is 2D, reshape to 1D for processing and then reshape back to 2D
1949
+ reshape_needed = False
1950
+ if zb.ndim == 2:
1951
+ reshape_needed = True
1952
+ shape_orig = zb.shape
1953
+ n_cells = zb.size
1954
+ floodmaps = [fm.reshape(n_cells) for fm in floodmaps]
1955
+ zb = zb.reshape(n_cells)
1956
+ mask = mask.reshape(n_cells)
1876
1957
  # 1a: make a table of all water levels and associated frequencies
1877
- zs = xr.concat(floodmaps, pd.Index(frequencies, name="frequency"))
1958
+ zs = np.stack(floodmaps, axis=0)
1878
1959
  # Get the indices of columns with all NaN values
1879
1960
  nan_cells = np.where(np.all(np.isnan(zs), axis=0))[0]
1880
1961
  # fill nan values with minimum bed levels in each grid cell, np.interp cannot ignore nan values
1881
- zs = xr.where(np.isnan(zs), np.tile(zb, (zs.shape[0], 1)), zs)
1962
+ zs = np.where(np.isnan(zs), np.tile(zb, (zs.shape[0], 1)), zs)
1882
1963
  # Get table of frequencies
1883
1964
  freq = np.tile(frequencies, (zs.shape[1], 1)).transpose()
1884
1965
 
@@ -1886,7 +1967,7 @@ class SfincsAdapter(IHazardAdapter):
1886
1967
  # (i.e. each h-value should be linked to the same p-values as in step 1a)
1887
1968
  sort_index = zs.argsort(axis=0)
1888
1969
  sorted_prob = np.flipud(np.take_along_axis(freq, sort_index, axis=0))
1889
- sorted_zs = np.flipud(np.take_along_axis(zs.values, sort_index, axis=0))
1970
+ sorted_zs = np.flipud(np.take_along_axis(zs, sort_index, axis=0))
1890
1971
 
1891
1972
  # 1c: Compute exceedance probabilities of water depths
1892
1973
  # Method: accumulate probabilities from top to bottom
@@ -1902,23 +1983,19 @@ class SfincsAdapter(IHazardAdapter):
1902
1983
  # h(T) = interp1 (log(T*), h*, log(T))
1903
1984
  # in which t* and h* are the values from the table and T is the return period (T) of interest
1904
1985
  # The resulting T-year water depths for all grids combined form the T-year hazard map
1905
- rp_da = xr.DataArray(rp_zs, dims=zs.dims)
1906
-
1907
- # no_data_value = -999 # in SFINCS
1908
- # sorted_zs = xr.where(sorted_zs == no_data_value, np.nan, sorted_zs)
1909
1986
 
1910
1987
  valid_cells = np.where(mask == 1)[
1911
1988
  0
1912
1989
  ] # only loop over cells where model is not masked
1913
- h = matlib.repmat(
1914
- np.copy(zb), len(return_periods), 1
1990
+ h = np.tile(
1991
+ zb, (len(return_periods), 1)
1915
1992
  ) # if not flooded (i.e. not in valid_cells) revert to bed_level, read from SFINCS results so it is the minimum bed level in a grid cell
1916
1993
 
1917
1994
  for jj in valid_cells: # looping over all non-masked cells.
1918
1995
  # linear interpolation for all return periods to evaluate
1919
1996
  h[:, jj] = np.interp(
1920
1997
  np.log10(return_periods),
1921
- np.log10(rp_da[::-1, jj]),
1998
+ np.log10(rp_zs[::-1, jj]),
1922
1999
  sorted_zs[::-1, jj],
1923
2000
  left=0,
1924
2001
  )
@@ -1935,16 +2012,10 @@ class SfincsAdapter(IHazardAdapter):
1935
2012
 
1936
2013
  rp_maps = []
1937
2014
  for ii, rp in enumerate(return_periods):
1938
- da = xr.DataArray(
1939
- data=h[ii, :], coords={"z": zs["z"]}, attrs={"units": "meters"}
1940
- )
1941
- if stacking:
1942
- # Ensure unstacking creates (y, x) dimensions in the correct order
1943
- da = da.unstack()
1944
- # Reorder dimensions if needed
1945
- if set(da.dims) == {"y", "x"} and da.dims != ("y", "x"):
1946
- da = da.transpose("y", "x")
1947
- # #create single nc
1948
- rp_maps.append(da)
2015
+ rp_maps.append(h[ii, :])
2016
+
2017
+ if reshape_needed:
2018
+ # Reshape back to 2D if needed
2019
+ rp_maps = [rp_map.reshape(shape_orig) for rp_map in rp_maps]
1949
2020
 
1950
2021
  return rp_maps
@@ -116,6 +116,7 @@ class DemModel(BaseModel):
116
116
 
117
117
  filename: str
118
118
  units: us.UnitTypesLength
119
+ index_filename: str = "index.tif"
119
120
 
120
121
 
121
122
  class DatumModel(BaseModel):