Skip to content

Part IV - Edit the DEM file for a simulation

Download the jupyter notebook

DEM Edit💡

In this notebook we will go through the steps of editing the DEM file of your threedimodel for a simulation.

Let's first import all required modules for the notebook and provide our credentials to connect to the threedi-api.

from getpass import getpass
from datetime import datetime
from threedi_api_client.openapi import ApiException
from threedi_api_client.openapi.models import RasterEdit
from threedi_api_client.api import ThreediApi
from threedi_api_client.versions import V3Api

API_HOST = "https://api.3di.live"
PERSONAL_API_KEY = getpass("Personal API token")  # https://management.3di.live/personal_api_keys

config = {
    "THREEDI_API_HOST": API_HOST,
    "THREEDI_API_PERSONAL_API_TOKEN": PERSONAL_API_KEY
}

api_client: V3Api = ThreediApi(config=config)

Check if we can connect to the threedi-api with the provided credentials:

try:
    user = api_client.auth_profile_list()
except ApiException as e:
    print("Oops, something went wrong. Maybe you made a typo?")
else:
    print(f"Successfully logged in as {user.username}!")

To run a simulation we'll need a threedimodel. Let's see which threedimodels are available:

models = api_client.threedimodels_list(limit=5)  # limit to the first 5 results
for model in models.results:
    print(f"{model.name}")

If we already know the name of a specific model, we can also look it up with the following api call:

my_model = api_client.threedimodels_list(name__icontains='v2_bergermeer')
print(my_model)

my_model = my_model.results[0]

Now that we have a threedimodel we are almost ready to create the simulation. But first we'll need to get an organisation under which name we will run the simulation.

Let's see which organisations are available:

organisations = api_client.organisations_list()
for organisation in organisations.results:
    print(f"{organisation.name}: {organisation.unique_id}")


# Here I use the following organisation, you should select from the list below
organisation_uuid = "a1993f6e13564e9687ae03a3604463f9"

# Retrieve first simulation template
simulation_templates = api_client.simulation_templates_list(simulation__threedimodel__id=my_model.id)
assert simulation_templates.count > 0, f"No simulation templates found for threedimodel {my_model.name}"
simulation_template_id = simulation_templates.results[0].id

#Now we got all the pieces to create a simulation
my_simulation = api_client.simulations_from_template(
    data={
        "template": simulation_template_id,
        "name": "my first simulation",        
        "organisation": organisation_uuid,
        "start_datetime": datetime.now(),
        "duration": 3600  # in seconds, so we simulate for 1 hour
    }
)


# The simulation has now been created but is not yet running. We can see the status of
# the simulation with the following call:

status = api_client.simulations_status_list(my_simulation.id)
print(status)

We will now initialize the simulation and add our DEM edits. First we need to find our model's DEM. The model has a list of rasters:

my_model_rasters = api_client.threedimodels_rasters_list(my_model.id)
my_model_rasters.results

From this list, we need the id of the DEM:

for raster in my_model_rasters.results:
    if raster.type == 'dem_file':
        raster_id = raster.id
        break
print(f'id of dem raster: {raster_id}')

Now we have what we need to make the raster edit API call:

data = {
  "offset": 0, # time in seconds since start of simulation
  "value": 2.5, # new height in mMSL
  "raster": raster_id,
  "polygon": {
    "type": "Polygon",
    "coordinates": [
      [
        [
          4.699366986751557,
          52.64294137346387
        ],
        [
          4.699687510728837,
          52.64336289692115
        ],
        [
          4.700147509574891,
          52.642926725857535
        ],
        [
          4.699698239564897,
          52.64304716158594
        ],
        [
          4.699366986751557,
          52.64294137346387
        ]
      ]
    ]
  }
}

raster_edit = RasterEdit(**data)


# POST the edit to the API
raster_edit_result = api_client.simulations_events_raster_edits_create(my_simulation.id, raster_edit)
raster_edit_result

Use a shapefile for the edit💡

To make it a bit easier, we can also use a polygon shapefile as input for the raster edits. In this case, a shapefile with the elevation and (time) offset stored in the attribute table:

import ogr
import osr

polygons_fn = "dem_edits.shp"
datasource = ogr.Open(polygons_fn)
layer = datasource.GetLayer()

# Create a CoordinateTransformation to WGS84, as required by the API
source_srs = layer.GetSpatialRef()
target_srs = osr.SpatialReference()
target_srs.ImportFromEPSG(4326)
transform = osr.CoordinateTransformation(source_srs, target_srs)

for feature in layer:
    geom = feature.GetGeometryRef()
    geom.Transform(transform)
    wkt = geom.ExportToWkt()
    data = {
      "offset": feature['offset'], # time in seconds since start of simulation
      "value": feature['elevation'], # new height in mMSL
      "raster": raster_id,
      "polygon": wkt
    }

    raster_edit = RasterEdit(**data)


    # POST the edit to the API
    raster_edit_result = api_client.simulations_events_raster_edits_create(my_simulation.id, raster_edit)
To run this simulation we have to tell it to start:

api_client.simulations_actions_create(my_simulation.id, data={"name": "start", "max_rate": 10})

# Eventually the simulation will finish. You can periodically check its progress by calling
# for the status again:

api_client.simulations_status_list(my_simulation.id)


# Eventually you should see something like this::

    {
        'created': datetime.datetime(2020, 7, 27, 14, 7, 6, 654905, tzinfo=tzutc()),
        'id': 15866,
        'name': 'finished',
        'paused': False,
        'time': 3600.0
     }

Congratulations, you just made your first simulation with a DEM edit via the threedi-api-client!