# How does the is\_exposed function work

**URL:** <https://community.riskscape.org.nz/t/how-does-the-is-exposed-function-work/144>\
**Category:** Community\
**Created:** [8 September 2023 04:02 UTC](https://community.riskscape.org.nz/t/how-does-the-is-exposed-function-work/144 "2023-09-08T04:02:35Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![JPowell](https://avatars.discourse-cdn.com/v4/letter/j/bbce88/32.png) [@JPowell](https://community.riskscape.org.nz/u/JPowell)\
**Post date:** [8 September 2023 04:02 UTC](https://community.riskscape.org.nz/t/how-does-the-is-exposed-function-work/144/1 "2023-09-08T04:02:35Z")

</div>

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

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/riskscape/original/1X/a5328ddfc056a742a02dfb60d7da840f4da6391f.jpeg)  
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

```auto
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')

```

```auto
[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.

---

<div class="post-metadata">

**Author:** ![timbeale](https://avatars.discourse-cdn.com/v4/letter/t/57b2e6/32.png) [@timbeale](https://community.riskscape.org.nz/u/timbeale)\
**Post date:** [10 September 2023 20:56 UTC](https://community.riskscape.org.nz/t/how-does-the-is-exposed-function-work/144/2 "2023-09-10T20:56:00Z")

</div>

Hi John,

The problem is this line in your pipeline:

> [@JPowell](#):
>
> ```auto
> -> select({*, is_exposed(exposure.geom, bookmark('max_extent_clipped'))})
> 
> ```

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:

```auto
-> 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).

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

```

---

<div class="post-metadata">

**Author:** ![timbeale](https://avatars.discourse-cdn.com/v4/letter/t/57b2e6/32.png) [@timbeale](https://community.riskscape.org.nz/u/timbeale)\
**Post date:** [10 September 2023 21:09 UTC](https://community.riskscape.org.nz/t/how-does-the-is-exposed-function-work/144/3 "2023-09-10T21:09:13Z")

</div>

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:

```auto
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:

```auto
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:

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

```

---

<div class="post-metadata">

**Author:** ![JPowell](https://avatars.discourse-cdn.com/v4/letter/j/bbce88/32.png) [@JPowell](https://community.riskscape.org.nz/u/JPowell)\
**Post date:** [10 September 2023 23:54 UTC](https://community.riskscape.org.nz/t/how-does-the-is-exposed-function-work/144/4 "2023-09-10T23:54:12Z")

</div>

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

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

```

---

<div class="post-metadata">

**Author:** ![timbeale](https://avatars.discourse-cdn.com/v4/letter/t/57b2e6/32.png) [@timbeale](https://community.riskscape.org.nz/u/timbeale)\
**Post date:** [11 September 2023 00:15 UTC](https://community.riskscape.org.nz/t/how-does-the-is-exposed-function-work/144/5 "2023-09-11T00:15:48Z")

</div>

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

```auto
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.

```auto
filter(is_not_null(hazard))

```
