๐Ÿ“˜ Example: Projections in FloodAdapt

In FloodAdapt, a Projection object is used to describe future climate and socio-economic conditions. These are defined by the two main components of a Projection object: - physical_projection: a PhysicalProjection object that describes the physical changes in the environment, such as sea level rise. - socio_economic_change: a SocioEconomicChange object that describes the socio-economic changes, such as population and economic growth.

If you want to learn more about Projections, you can check the Projections section of the FloodAdapt GUI documentation.

In this notebook, we will look into all the available FloodAdapt methods to create, save, edit and delete Projections.

Import libraries

First the required python libraries for this notebook are imported.

from flood_adapt import FloodAdapt
from flood_adapt.objects.projections.projections import PhysicalProjection, SocioEconomicChange, Projection
from flood_adapt.objects.forcing.unit_system import UnitTypesLength, UnitfulLength, UnitfulLengthRefValue, VerticalReference
from flood_adapt.config.config import Settings
from pathlib import Path
import pandas as pd
import geopandas as gpd
from IPython.display import HTML

๐Ÿš€ Step 1: Reading-in the FloodAdapt database

Then, we need to create a FloodAdapt object, with the example database of Charleston. This object has all the required methods for adding, copying, editing or deleting projections from the database.

# Setup FloodAdapt
STATIC_DATA_DIR = Path("../../_data/examples/static-data/4_Projections").resolve() # for later use
settings = Settings(
    DATABASE_ROOT=Path("../../_data/examples").resolve(),
    DATABASE_NAME="charleston_test"
)
fa = FloodAdapt(database_path=settings.database_path)

๐Ÿ”Ž Step 2: Getting available projections from the database

Using the get_projections() method of the FloodAdapt class, we can get a dictionary of all the projections in the database. The keys of the returned dictionary are the names, descriptions, paths and last modification dates of the projections. We can use this method to check which projections are currently available in the database.

pd.DataFrame(fa.get_projections()) # here we turn the dictionary to a Pandas DataFrame for better visualization
name description path last_modification_date
0 current C:\a\FloodAdapt\FloodAdapt\docs\_data\examples... 2025-06-12 16:39:04.230988

As can be seen above, right now, there is only one projection available in the database named โ€œcurrentโ€. This is a default projection created when the database is created, to describe the current conditions without any future changes.

We can get the Projection object of a projection by using the get_projection() method of the FloodAdapt class. This method takes only the name of the projection as an argument.

fa.get_projection("current")
Projection(name='current', description='', physical_projection=PhysicalProjection(sea_level_rise=UnitfulLength(value=0.0, units=UnitTypesLength.meters), subsidence=UnitfulLength(value=0.0, units=UnitTypesLength.meters), rainfall_multiplier=1.0, storm_frequency_increase=0.0), socio_economic_change=SocioEconomicChange(population_growth_existing=0.0, economic_growth=0.0, population_growth_new=0.0, new_development_elevation=None, new_development_shapefile=None))

As can be seen the โ€œcurrentโ€ projection has default values for the attributes of the physical_projection and socio_economic_change, which essentially means that this projection describes the current conditions without any change.

๐Ÿ“ˆ Step 3: Creating a new Projection object

A Projection object can be created by using the individual FloodAdapt object classes. This can ensure correct type hinting and avoid errors.

๐ŸŒŠ๐ŸŒง๏ธ Physical Projection

First, letโ€™s a create a PhysicalProjection that describes future conditions with 0.5 meters of sea level rise. To do this we can use the sea_level_rise attribute which has a value with a unit, which in FloodAdapt can be defined using a UnitfulLength object with the value and unit fields. The value field is a float and the unit field can be one of the UnitTypesLength.

phys_proj = PhysicalProjection(sea_level_rise=UnitfulLength(value=0.5, units=UnitTypesLength.meters))
phys_proj
PhysicalProjection(sea_level_rise=UnitfulLength(value=0.5, units=UnitTypesLength.meters), subsidence=UnitfulLength(value=0.0, units=UnitTypesLength.meters), rainfall_multiplier=1.0, storm_frequency_increase=0.0)

๐Ÿ‘ฅ๐Ÿ’ฐ Socio-Economic Change

Then we create a SocioEconomicChange object that describes future conditions with a population growth of 10% and an economic growth of 5% for the existing areas. To do this we can use the population_growth and economic_growth attributes which are both floats given in percentages.

se_change = SocioEconomicChange(population_growth_existing=10,
                          economic_growth=5)
se_change
SocioEconomicChange(population_growth_existing=10.0, economic_growth=5.0, population_growth_new=0.0, new_development_elevation=None, new_development_shapefile=None)

Now, we can create a Projection object, giving it a unique name (which cannot contain any spaces or special characters), and the previously created PhysicalProjection and SocioEconomicChange objects. The description field is optional, and can be used to provide a more extensive description of the projection.

future_1 = Projection(
    name="future_1", 
    description=r"0.5 m sea level rise, 10% population growth, 5% economic growth", 
    physical_projection=phys_proj, 
    socio_economic_change=se_change
)
future_1
Projection(name='future_1', description='0.5 m sea level rise, 10% population growth, 5% economic growth', physical_projection=PhysicalProjection(sea_level_rise=UnitfulLength(value=0.5, units=UnitTypesLength.meters), subsidence=UnitfulLength(value=0.0, units=UnitTypesLength.meters), rainfall_multiplier=1.0, storm_frequency_increase=0.0), socio_economic_change=SocioEconomicChange(population_growth_existing=10.0, economic_growth=5.0, population_growth_new=0.0, new_development_elevation=None, new_development_shapefile=None))

Alternatively, a new projection can be created by using the create_projection() method of the FloodAdapt class. This method takes a single argument which is a dictionary containing the required projection parameters. Letโ€™s create the exact same projection as before, but now using this method.

# Create dictionary
future_1_dict = {
    "name": "slr_50cm",
    "description": "0.5 m sea level rise",
    "physical_projection": {"sea_level_rise": {"value": 0.5, "units": "meters"}},
    "socio_economic_change": {"population_growth_existing": 10, "economic_growth": 5}
}
# Create Projection object from dictionary
future_1_from_dict = fa.create_projection(future_1_dict)

We can now verify that the two projection objects are identical:

# Check if the two objects are equal
future_1_from_dict == future_1
True

๐Ÿ’พ Step 4: Saving a new Projection to the database

In the previous step we have created a Projection object, but we have not yet saved it in our database. The save_projection() method of the FloodAdapt class can be used to achieve that. This method takes a single argument which is a Projection object. If a projection with the same name already exists in the database, an error will be raised. Letโ€™s save the projection we just created to the database.

fa.save_projection(future_1)

Using the get_projections() method of the FloodAdapt class, we can check that the projection has been saved to the database.

pd.DataFrame(fa.get_projections())
name description path last_modification_date
0 current C:\a\FloodAdapt\FloodAdapt\docs\_data\examples... 2025-06-12 16:39:04.230988
1 future_1 0.5 m sea level rise, 10% population growth, 5... C:\a\FloodAdapt\FloodAdapt\docs\_data\examples... 2025-06-12 16:56:25.050519

โœ๏ธ Step 5: Copying and Editing a Projection in the database

If we want to edit small parts of a projection, it is easier to copy an existing projection and edit the copy. This way we do not have to create a new projection from scratch.

A projection can be copied in the database by using the copy_projection() method of the FloodAdapt class. This method takes three arguments: the name of the projection to be copied and the name and description of the new projection. Letโ€™s copy the projection we just created, which we would like to adjust, to represent 1 meter of sea level rise.

fa.copy_projection(
    old_name="future_1", 
    new_name="future_2", 
    new_description=r"1 m sea level rise, 10% population growth, 5% economic growth"
)

We can see that now a new projection with name โ€œfuture_2โ€ has been created in the database. However, the actual attributes of the projection are still the same as the original projection.

future_2 = fa.get_projection("future_2")
future_2.physical_projection.sea_level_rise
UnitfulLength(value=0.5, units=UnitTypesLength.meters)

We can directly edit the relevant attributes of the projection object. In this case, we want to change the sea level rise to 1 meter.

future_2.physical_projection.sea_level_rise.value = 1.0

While we have now edited the projection object, we have not yet saved the changes to the database. We can do this by using the save_projection() method with the argument overwrite=True, since we want to edit an existing projection.

fa.save_projection(future_2, overwrite=True)

Now we can verify that the projection has been updated in the database. The sea level rise is now 1 meter.

future_2 = fa.get_projection("future_2")
future_2.physical_projection.sea_level_rise
UnitfulLength(value=1.0, units=UnitTypesLength.meters)

โŒ Step 6: Deleting a Projection from the database

We now have 3 projections in the database.

pd.DataFrame(fa.get_projections())
name description path last_modification_date
0 current C:\a\FloodAdapt\FloodAdapt\docs\_data\examples... 2025-06-12 16:39:04.230988
1 future_1 0.5 m sea level rise, 10% population growth, 5... C:\a\FloodAdapt\FloodAdapt\docs\_data\examples... 2025-06-12 16:56:25.050519
2 future_2 1 m sea level rise, 10% population growth, 5% ... C:\a\FloodAdapt\FloodAdapt\docs\_data\examples... 2025-06-12 16:56:25.130099

If we want to delete a projection from the database, we can use the delete_projection() method of the FloodAdapt class. This method takes a single argument which is the name of the projection to be deleted. Letโ€™s delete the projection we just created.

fa.delete_projection("future_2")

We can check that the projection has been indeed deleted from the database by using the get_projections() method of the FloodAdapt class. The projection with name โ€œfuture_2โ€ is no longer in the database.

pd.DataFrame(fa.get_projections())
name description path last_modification_date
0 current C:\a\FloodAdapt\FloodAdapt\docs\_data\examples... 2025-06-12 16:39:04.230988
1 future_1 0.5 m sea level rise, 10% population growth, 5... C:\a\FloodAdapt\FloodAdapt\docs\_data\examples... 2025-06-12 16:56:25.050519

๐ŸŒ Step 7: Use of Sea Level Rise Scenarios

A FloodAdapt database can include sea level rise scenarios, describing a timeline of future sea level rise relative to a reference year. If these scenarios are available in the database the get_slr_scn_names() method will return a list of the available scenarios names.

โ„น๏ธ Adding sea level rise scenarios to your database
If you want to learn more about how to add sea level rise scenarios to your FloodAdapt database during system setup, you can check the Sea level rise (SLR) scenarios section of the FloodAdapt Setup Guide.

fa.get_slr_scn_names()
['ssp119', 'ssp126', 'ssp245', 'ssp370', 'ssp585']

The plot_slr_scenarios will create a temporary plot in html format, and will return the path of the html file. This allows to visualize the different scenarios in time.

# Get the path of the html
html_path = fa.plot_slr_scenarios()
# Show the 
HTML(filename=html_path)  # Adjust width and height as needed

Then the interp_slr() method can be used to interpolate the sea level rise for a given year. This method takes two arguments: the name of the scenario and the year for which we want to interpolate the sea level rise. The method returns a float value representing the interpolated sea level rise in the default length units of the database.

fa.interp_slr(slr_scenario="ssp585", year=2050)
0.6

๐Ÿ—๏ธ Step 7: Population Growth in New Developments Areas

In Step 3 we created a Projection with a population growth of 10% to the existing exposure area. This area is essentially describing the buildings in the static impact model.

Using the get_building_geometries() method of the FloodAdapt class, we can get a geopandas GeoDataFrame of the centroids of the buildings in the database, along with their attributes.

fa.get_building_geometries().explore() # we use the explore method to make the interactive map
Make this Notebook Trusted to load map: File -> Trust Notebook

In addition to the population growth in the existing exposure area, we can also add new development areas to a projection. This is done by using the population_growth_new attribute of the SocioEconomicChange object. This attribute describes the % of the current population that will be distributed in these new areas.

When a population_growth_new is defined (i.e., the value is not zero), then the new_development_shapefile should be provided, which should be a path to a geospatial file that describes the area(s) where the new development will take place.

The new_development_elevation field is a UnitfulLengthRefValue object which describes the elevation of the new development area, relative to a reference.

โ„น๏ธ Population growth in new development areas
If you want to learn more about how the population growth in new development areas works in FloodAdapt, you can check the Population growth - new development areas section of the FloodAdapt User Guide.

The areas can be defined by any geospatial file format supported by geopandas, such as shapefiles, geojson, etc., and should include Polygon geometries. For example, here we will use a shapefile with 3 Polygon areas defined randomly just for this example.

new_dev_path = str(STATIC_DATA_DIR / "new_dev_areas.geojson")
new_dev = gpd.read_file(new_dev_path)
new_dev.explore()
Make this Notebook Trusted to load map: File -> Trust Notebook

Letโ€™s make a new SocioEconomicChange object with a population growth of 10% in the existing exposure area and 5% in the new development areas. The new development area will be a shapefile with a path to the file, and the elevation of the new development area will be 1 meter above datum.

Then we can create the SocioEconomicChange object accordingly.

โ„น๏ธ New Development Elevation Reference
When specifying the elevation of the new development area, a reference needs to be provided. This can either be relative to โ€œdatumโ€ which describes the local Datum, or relative to โ€œfloodmapโ€ which describes the base flood elevation (BFE).

se_change = SocioEconomicChange(
    population_growth_existing=10,
    economic_growth=5,
    population_growth_new=5,
    new_development_shapefile=new_dev_path,
    new_development_elevation=UnitfulLengthRefValue(
        value=0.5,
        units=UnitTypesLength.meters,
        type=VerticalReference.datum)
    )
se_change
SocioEconomicChange(population_growth_existing=10.0, economic_growth=5.0, population_growth_new=5.0, new_development_elevation=UnitfulLengthRefValue(value=0.5, units=UnitTypesLength.meters), new_development_shapefile='C:\\a\\FloodAdapt\\FloodAdapt\\docs\\_data\\examples\\static-data\\4_Projections\\new_dev_areas.geojson')

We can create a new Projection object with the new SocioEconomicChange and adding a Physical Projections with a sea level rise for the SSP585 scenario of 2050.

proj_2050 = Projection(
    name="proj_2050",
    description="2050 projection", 
    physical_projection=PhysicalProjection(
        sea_level_rise=UnitfulLength(
            value=fa.interp_slr(slr_scenario="ssp585", year=2050), 
            units=UnitTypesLength.meters)
        ),
    socio_economic_change=se_change
    )
proj_2050
Projection(name='proj_2050', description='2050 projection', physical_projection=PhysicalProjection(sea_level_rise=UnitfulLength(value=0.6, units=UnitTypesLength.meters), subsidence=UnitfulLength(value=0.0, units=UnitTypesLength.meters), rainfall_multiplier=1.0, storm_frequency_increase=0.0), socio_economic_change=SocioEconomicChange(population_growth_existing=10.0, economic_growth=5.0, population_growth_new=5.0, new_development_elevation=UnitfulLengthRefValue(value=0.5, units=UnitTypesLength.meters), new_development_shapefile='C:\\a\\FloodAdapt\\FloodAdapt\\docs\\_data\\examples\\static-data\\4_Projections\\new_dev_areas.geojson'))

And now we can save the projection to the database.

fa.save_projection(proj_2050)

When we get the new projection from the database, we can see that the new development shapefile has been added as part of the projection.

fa.get_projection("proj_2050").socio_economic_change.new_development_shapefile
'new_dev_areas.geojson'
Back to top