Impact function writing tutorial - mf4dl1/jaksafe GitHub Wiki
This is a simple tutorial for writing of InaSAFE impact functions.
Note
This is an old text that needs to be verified, but it may be helpful and can serve as a starting point for additional documentation.
InaSAFE contains a plugin system that allows complex impact functions to be implemented in Python (http://www.python.org) whilst (ideally) minimizing the need to understand all the complexity of the handling the hazard and exposure layers. Features of the impact function system are:
- Auto registration of new impact functions after restart.
- Derivation of more complex impact functions from simpler ones.
- Auto hiding for impact functions that aren't appropriate (depending on the requirements).
- Allow for additional functionality to be added easily.
- Provide up-to-date documentation on impact functions functionality.
This section provides a beginners tutorial on writing a simple earthquake impact function from scratch. You will need to be familiar with the basics of Python to be able to write and debug impact functions. If you are new to Python the standard Python tutorial is a great place to start see http://docs.python.org/tutorial/.
For this impact function we want to calculate a simple impact by using the following function of the severity of hazard (i.e. the amount of ground shaking - H) by the exposure (i.e. the number of people in that area - P). e.g.:
Impact = 10 ** (a * H - b) * P
where
H: Raster layer of MMI ground shaking
P: Raster layer of population data on the same grid as H
a,b: Parameters that were tuned from real world data
As the first step we need to define the impact function class.:
class SimpleImpactEarthquakeFunction(FunctionProvider)
Every impact function must be subclassed from FunctionProvider. This is the method of registration for the impact function and allows the InaSAFE Plugin Manager to know what impact functions are available.
Each impact function needs to be used in the correct context. Using a flood impact function for earthquakes will likely yield misleading results at best! As such plugins may have a variety of conditions that need to be met before they can be run. Such conditions may include:
- The type of hazard
- The type of exposure
- The form of the layer data (raster or vector)
- The measure or unit type of a layer
- Any other meta data defined in the layer
In the future impact functions may also support filtering by: * The geographic location * The available layer meta data
InaSAFE will try to show users only those impact functions that can be validly run.
These parameters required to run the impact function, and indeed all specific parameters, are defined in the doc string of the class:
class SimpleImpactEarthquakeFunction(FunctionProvider):
"""Simple impact function for earthquakes
:author Allen
:rating 1
:param requires category=='hazard' and \
subcategory=='earthquake' and \
layer_type=='raster'
:param requires category=='exposure' and \
subcategory=='population' and \
layer_type=='raster'
"""
title = tr('Test Simple Earthquake impact function')
parameters = OrderedDict([('a', 0.97429), ('b', 11.037)])
This tells InaSAFE that this impact function requires at a minimum inputs of:
- category of 'hazard', with a layer subcategory of 'earthquake' and it must be a layerType of 'Raster'
- category of 'exposure', with a layer subcategory of 'earthquake' and it must be a layerType of 'Raster'
title: tag specifies the title of the impact function as displayed in the InaSAFE user interface.
parameters: dictionary of parameters that can be configured from the user interface. In this case two parameters a and b with their default values.
The require expression can be any arbitrary python expression that can be evaluated.
Note
- Lines can be broken using the line continuation character '\' at the end of a line.
- If any one of the conditions is not met the plugin will not be visible from the impact selection box.
Each impact function must then define a run method which contains the execution code:
def run(self, input):
The parameters are user configurable with default values defined in the Class. The keywords parameter needs to have at least the impact_summary entry. The impact_summary entry usually contains an HTML table describing the analysis results. For printing purposes, InaSAFE needs several other entries in the keywords section such as map_title, legend_notes, legend_units, legend_title, impact_table. Please refer to :ref:`writing_impact_functions` for examples on how to use the keywords:
def run(self, layers)
"""Risk plugin for earthquake fatalities
Input
layers: List of layers expected to contain
H: Raster layer of MMI ground shaking
P: Raster layer of population data on the same grid as H
"""
# Identify input layers
intensity = layers[0]
population = layers[1]
# Extract data
H = intensity.get_data(nan=0)
P = population.get_data(nan=0)
# Parameters
a = self.parameters['a']
b = self.parameters['b']
# Calculate impact
F = 10 ** (a * H - b) * P
# Create new layer and return
R = Raster(F,
projection=population.get_projection(),
geotransform=population.get_geotransform(),
keywords={'impact_summary': '</table>'})
return R
At the end of the function the calculated impact layer R is returned. This return layer in our example is a Raster layer. The correct projection for this layer is ensured by passing the input layer projections.
The whole impact function file will now read:
from safe.common.utilities import OrderedDict
from safe.impact_functions.core import (
FunctionProvider,
get_hazard_layer,
get_exposure_layer)
from safe.storage.raster import Raster
from safe.common.utilities import (
ugettext as tr)
class SimpleImpactEarthquakeFunction(FunctionProvider):
"""Simple plugin for earthquake damage
:author Allen
:rating 1
:param requires category=='hazard' and \
subcategory=='earthquake' and \
layertype=='raster
:param requires category=='exposure' and \
subcategory=='population' and \
layertype=='raster'
"""
title = tr('Test Simple Earthquake impact function')
parameters = OrderedDict([('a', 0.97429), ('b', 11.037)])
def run(self, layers):
"""Risk plugin for earthquake fatalities
Input
layers: List of layers expected to contain
H: Raster layer of MMI ground shaking
P: Raster layer of population data on the same grid as H
"""
# Extract input layers
intensity = get_hazard_layer(layers)
population = get_exposure_layer(layers)
# Extract data
H = intensity.get_data(nan=0)
P = population.get_data(nan=0, scaling=True)
a = self.parameters['a']
b = self.parameters['b']
# Calculate impact
F = 10 ** (a * H - b) * P
# Create new layer and return
R = Raster(F,
projection=population.get_projection(),
geotransform=population.get_geotransform(),
keywords={'impact_summary': ''</table>'})
return R
Save this as SimpleImpactEarthquakeFunction.py into into the following directory:
[root |project_name| dir]/safe/impact_functions/earthquake
Then start QGis and activate InaSAFE.
Load the following data
- Earthquake ground shaking
- Glp10ag (Population for Indonesia)
- You can also use the qgis project indonesia_earthquake_scenarios inside the InaSAFE data directory
Using the indonesia_earthquake_scenarios
- Select an earth quake layer and the population data
- Your new function should be available for execution.