How does the is_exposed function work

I have a vector data set of address points and a raster layer showing flood inundation


Not all address points are within the flood extent but if I use is_exposed on these 2 layers it returns True for all address points.

pipeline_test.txt

input(relation: 'Address_Points_Clipped', name: 'exposure') as exposures_input

-> select({*, is_exposed(exposure.geom, bookmark('max_extent_clipped'))}) as hazard_layer
  -> save('hazard_ouptut.csv', format:'csv')
[Address_Points_Clipped]
location = Data\Exposure\clipped-addresses.csv
set-attribute.geom = create_point("gd2000_ycoord","gd2000_xcoord")
crs-name = EPSG:4326

[max_extent_clipped]
location = Data\Hazard\NZ_ARI1000_200-clipped.tif
format = geotiff
crs-name = EPSG:2193

I was expecting that address points that fell within the “No Data” areas of NZ_ARI1000_200-clipped.tif would be assigned a 0/False in the is_exposed column.

Hi John,

The problem is this line in your pipeline:

You’re passing the bookmark/coverage directly to is_exposed(), whereas it expects to get the hazard intensity value sampled from the coverage. i.e. you want something more like this:

-> select({*, is_exposed(exposure.geom, sample_one(exposure, bookmark('max_extent_clipped')))})

It might be slightly clearer to split this over 2 steps (which also lets you see the hazard intensity value that was sampled).

-> select({*, sample_one(exposure.geom, bookmark('max_extent_clipped')) as hazard})
-> select({*, is_exposed(exposure.geom, hazard)})

A few more follow-up notes:

is_exposed() returns 1 if the hazard argument is present (i.e. not null) and 0 if not. It’s basically the same behaviour as:

if_then_else(is_null(hazard), 0, 1) as is_exposed

is_exposed() relies on the sampling operation returning null if the hazard does not intersect the element-at-risk. Some GeoTIFF coverages may return zero instead of null for the unexposed regions, in which case you’d want a custom function that is more like:

if_then_else(hazard > 0, 1, 0) as is_exposed

One final gotcha when writing a pipeline manually from scratch is do not pass the return value from sample() directly to is_exposed(). sample() returns a list of hazard intensities, and so the list will always be non-null (using the wizard to build your pipeline will avoid this pitfall). Instead, you can used sample_one() or sample_centroid(), or do something like this:

if_then_else(length(hazard) > 0, 1, 0) as is_exposed

Cheers Tim, this is working now. I ended up just using sample_one and then filtering based on whether or not the hazard column is empty

-> select({*, sample_one(exposure.geom, bookmark('max_extent')) as hazard})
-> filter("hazard" != '')

OK, that will work, but the reasons why it works are a bit convoluted. That check is essentially the same as:

riskscape expr eval "    '' != null_of('floating')    "
<nothing>

So although you are comparing a text string and a number there, because one is null the overall result is also null (or <nothing>). In the filter() step, null/nothing gets treated as false, so those unexposed buildings get removed.

It’s probably slightly nicer to explicitly check against null in the filter step, e.g.

filter(is_not_null(hazard))
1 Like