Advanced usage

Using config files

Instead of specifying all inputs using set_input, it is possible to use a config file. A config file can be loaded using read_config_file or it can be read and executed immediately by using run_config_file.

The syntax of the config file is explained more in detail in Niche Configuration file, but is already introduced here because it will be used in the next examples.

If you want to recreate the examples below, the config files can be found under the docs folder, so if you extract all the data the you should be able to run the examples from the notebook.

Comparing Niche classes

Niche models can be compared using a NicheDelta class. This can be used to compare different scenario’s.

In our example, we will compare the results of the running Niche two times, once using a simple model and once using a full model.

[1]:
import niche_vlaanderen as nv
import matplotlib.pyplot as plt

simple = nv.Niche()
simple.run_config_file("simple.yml")

full = nv.Niche()
full.run_config_file("full.yml")

delta = nv.NicheDelta(simple, full)
ax = delta.plot(7)
plt.show()
_images/advanced_usage_2_0.png

It is also possible to show the areas in a dataframe by using the table attribute.

[2]:
delta.table.head()
[2]:
vegetation presence area_ha
0 1 not present in both models 14.44
1 1 present in both models 8.88
2 1 only in model 1 1.00
3 2 not present in both models 11.52
4 2 only in model 1 10.00

Like Niche, NicheDelta also has a write method, which takes a directory as an argument.

[3]:
delta.write("comparison_output", overwrite_files=True)

Creating deviation maps

In many cases, it is not only important to find out which vegetation types are possible given the different input files, but also to find out how much change would be required to mhw or mlw to allow a certain vegetation type.

To create deviation maps, it is necessary to run a model with the deviation option.

[4]:
dev = nv.Niche()
dev.set_input("mhw", "../testcase/zwarte_beek/input/mhw.asc")
dev.set_input("mlw", "../testcase/zwarte_beek/input/mhw.asc")
dev.set_input("soil_code", "../testcase/zwarte_beek/input/soil_code.asc")
dev.run(deviation=True, full_model=False)

The deviation maps can be plotted by specifying either mhw or mlw with the vegetation type, eg mhw_14 (to show the deviation between mhw and the required mhw for vegetation type 14). Positive values indicate that the actual condition is too dry for the vegetation type. Negative values indicate that the actual condition is too wet for the vegetation type.

[5]:
dev.plot("mlw")
dev.plot("mlw_14")

plt.show()
_images/advanced_usage_10_0.png
_images/advanced_usage_10_1.png

Create detailed absence/presence plots

Rather than just showing if a certain vegetation type is present or not present, it is possible to show a more detailed analysis on the reason a vegetation type is present or not present. This follows the steps of the niche analysis: ie if soil type is possible, mxw checks are done and finally other input grids are matched.

[6]:
ax = full.plot(7)
ax = full.plot_detail(7)
_images/advanced_usage_12_0.png
_images/advanced_usage_12_1.png

This detailed version of the map can be exported with:

[7]:
full.write("output", detailed_files=True)

Creating statistics per shape object

Niche also contains a helper function that allows one to calculate the possible vegetation by using a vector dataset, such as a .geojson or .shp file.

The vegetation is returned as a pandas dataframe, where shapes are identified by their id and the area not covered by a shape gets shape_id -1.

[8]:
df = full.zonal_stats("../testcase/zwarte_beek/input/study_area_l72.geojson")
df
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████| 28/28 [00:00<00:00, 52.36it/s]
[8]:
vegetation shape_id presence area_ha
0 1 0 not present 15.44
1 1 0 present 8.88
2 1 0 no data 0.00
3 2 0 not present 21.52
4 2 0 present 2.80
... ... ... ... ...
79 15 -1 present 0.00
80 16 -1 present 0.00
81 20 -1 present 0.00
82 21 -1 present 0.00
83 28 -1 present 0.00

168 rows × 4 columns

Using abiotic grids

In certain cases the intermediary grids of Acidity or NutrientLevel need changes, to compensate for specific circumstances.

In that case it is possible to run a Niche model and make some adjustments to the grid and then using an abiotic grid as an input.

[9]:
import niche_vlaanderen as nv
import matplotlib.pyplot as plt

full = nv.Niche()
full.run_config_file("full.yml")
full.write("output_abiotic", overwrite_files=True)

Now it is possible to adapt the acidity and nutrient_level grids outside niche. For this demo, we will use some Python magic to make all nutrient levels one level lower. Note that there is no need to do this in Python, any other tool could be used as well. So if you don’t understand this code - don’t panic (and ignore the warning)!

[10]:
import rasterio
with rasterio.open("output_abiotic/full_nutrient_level.tif") as src:
    nutrient_level = src.read(1)
    profile = src.profile
    nodata = src.nodatavals[0]

nutrient_level[nutrient_level != nodata] = nutrient_level[nutrient_level != nodata] -1

# we can not have nutrient level 0, so we set all places where this occurs to 1
nutrient_level[nutrient_level ==0 ] = 1

with rasterio.open("output_abiotic/adjusted_nutrient.tif", 'w', **profile) as dst:
    dst.write(nutrient_level, 1)

Next we will create a new niche model using the same options as our previous full models, but we will add the previously calculated nutrient level as input: in this case, this nutrient level grid will be used (and not calculated from the other grids). Note that we use the read_config_file method (and not run_config_file) because we still want to edit the configuration before running.

[11]:
adjusted = nv.Niche()
# set the input layers as usual:
adjusted.read_config_file("full.yml")
# now select a custom layer for the nutrient level:
adjusted.set_input("nutrient_level", "output_abiotic/adjusted_nutrient.tif")
# since we do not define a custom acidity layer to use, acidity will be calculated as usual
adjusted.name = "adjusted_nutrient_level"
adjusted.run()

adjusted.plot(7)
full.plot(7)
plt.show()
_images/advanced_usage_22_0.png
_images/advanced_usage_22_1.png

Overwriting standard code tables

One is free to adapt the standard code tables that are used by NICHE. By specifying the paths to the adapted code tables in a NICHE class object, the standard code tables can be overwritten. In this way, standard model functioning can be tweaked. However, it is strongly advised to use ecological data that is reviewed by experts and to have in-depth knowledge of the model functioning.

The possible code tables that can be adapted and set within a NICHE object are:

ct_acidity, ct_soil_mlw_class, ct_soil_codes, lnk_acidity, ct_seepage, ct_vegetation, ct_management, ct_nutrient_level and ct_mineralisation

After adapting the vegetation code table for type 7 (Caricion gracilis) on peaty soil (V) by randomly altering the maximum mhw and mlw to 5 and 4 cm resp. (i.e. below ground, instead of standard values of -28 and -29 cm) and saving the file to ct_vegetation_adj7.csv, the adjusted model can be built and run.

[12]:
adjusted_ct = nv.Niche(ct_vegetation = "ct_vegetation_adj7.csv")
adjusted_ct.read_config_file("full.yml")
adjusted_ct.run()

Example of changed potential area of Caricion gracilis vegetation type because of the changes set in the vegetation code table:

[13]:
adjusted_ct.plot(7)
full.plot(7)
plt.show()
_images/advanced_usage_27_0.png
_images/advanced_usage_27_1.png

Potential area is shrinking because of the range of grondwater levels that have become more narrow (excluding the wettest places).