.. _combining_layers:

# Combining multiple layers together

RiskScape :ref:`models` are designed to combine layers.
However, there are several different ways you may wish to combine your input data layers.
This page clarifies what can be done in a standard wizard model, and what requires more advanced pipeline techniques.

## The basics

The exposure-layer contains your elements-at-risk.
Simple wizard models let you combine the exposure-layer with:

- A single hazard-layer, in either vector or raster form.
- An area-layer, which contains regional boundaries of interest.
- A resource-layer, which contains supplementary information, such as soil type.

.. _combine_hazards:

## Combining multiple hazard-layers

Wizard models also let you combine multiple hazard layers into one.

When the exposure-layer contains point geometry, or centroid sampling is used, the sampling operation is simple.
The given point is simply evaluated for each hazard-layer, and the combined results are returned.

For example, if one GeoTIFF contains a depth of 0.8m for a given point, and a second GeoTIFF contains a velocity of 0.1m/s
at the same point, then the sampling operation will return value like `{ depth: 0.8, velocity: 0.1 }`.
The return value here is called a *struct*.

When the exposure-layer contains line-string or polygon geometry, the sampling operation is a bit more complicated.
RiskScape does the following:
- Cuts the polygon or line-string by a common grid resolution.
- Takes the *centroid* of each cut segment and evaluates that point against each hazard-layer.

Ideally the grid resolution you use should be the pixel/cell size of the smallest resolution hazard-layer.
With raster data, evaluating any point within the same pixel/cell should always produce the same result.

.. note::
    If one or more hazard-layer contains vector data, then evaluating the centroid of each cut segment
    may not detect some geometry intersections between the exposure-layer and the hazard-layer.
    You could try either sampling the vector data on its own separately, or rasterize the vector data.

The following diagram shows conceptually what happens when two coverage layers are sampled.

.. image:: ../../diagrams/combined_coverages.svg
    :target: ../../_images/combined_coverages.svg
    :width: 75%
    :alt: Diagram showing the same building outline being sampled against two different coverages

This diagram shows a single building footprint polygon that overlaps with two different coverages (one *blue*, one *checkerboard* pattern).
Where the building intersects both hazard-layers, *two* hazard intensity measures will be returned (i.e. both *blue* and *checkerboard*).
In other places, the building only intersects one of the layers, so only a *blue* result, or only a *checkerboard* result is returned.

.. tip::
    Advanced users can also combine hazard-layers in a pipeline model using the ``combine_coverages()`` function.
    For a worked example of a pipeline using ``combine_coverages()``, refer to  the :ref:`devora`.

.. _union_step:

## Combining exposure-layers

Sometimes you may have related elements-at-risk that are stored in different datasets.
For example, you might have a water network that is comprised of polygon-based pump stations in one shapefile
and linestring-based pipes in another shapefile.

Advanced users can build a single RiskScape pipeline model that combines elements-at-risk
from different data sources using the `union` pipeline step.
After the `union` step, most pipeline operations will then work normally with the combined data.

The union step is similar to a `join` step, in that it combines two (or more) pipeline branches together.
A simple example of combining two different exposure-layers together might look like this:

```none
input('pump-stations')
-> union() as all_exposures
# -> model continues from here...

input('pipes') -> all_exposures
```

The `union` step will combine the attributes from each input layer.
If an attribute is only present in one input layer, then it will become nullable.
This means the attribute may have the value *null*, or `None` when it is passed to your Python function.

.. tip::
    For best results, we recommend ensuring that the attributes in both layers have the same names and same data types.
    You can use bookmark features, such as ``set-attribute`` or ``type``/``map-attribute`` to help with this.

You can use the `union` step anywhere in the pipeline.
For example, instead of performing the union immediately after the `input` steps, you could calculate the
losses for the different asset types separately, and then use a `union` step to combine the losses
into a single *Event Impact Table*.

### Saving combined geometry

When you save the raw results from a pipeline model to shapefile, typically it is the exposure-layer geometry being saved.

Because the exposure-layer can now come from several different input sources that have been combined via a `union` step,
it can mean that the geometry being saved is no longer homogeneous - the data may contain a mixture of geometry types (e.g. polygon, line-string, point)
or different Coordinate Reference Systems (CRS).
Shapefile is the default geospatial format that results are saved in, but it has some limitations when mixing different CRSs or geometry types.

.. tip::
    You should always be able to save the raw results using the GeoJSON or KML formats.
    These formats can handle a mixture geometry types, and always save geometry in the WGS84 CRS.

The limitations with saving data to shapefile are:
- All geometry must have the same CRS. If not, RiskScape will automatically reproject the geometry to a common CRS.
  Note that reprojection has some limitations - see :ref:`geom_reprojection` for more details.
- All geometry must be the same type (i.e. polygon, line-string, or point). Otherwise, you may get an error like:

  ```none
  java.lang.ClassCastException: org.locationtech.jts.geom.MultiPolygon cannot be cast to org.locationtech.jts.geom.MultiLineString
  ```

  One work-around is to *buffer* the geometry slightly, so that everything becomes a polygon, e.g.
  just before the `save` step, you could add the following step to buffer everything by 1cm:

  ```none
  select({ buffer(exposure, 0.01) as the_geom, * })`
  ```

