Built-in types
This guide provides a comprehensive overview of utilizing SpectralIndices.jl with Julia's built-in types and data structures. By exploring these foundational elements, you'll gain valuable insights into the package's functionality and its application in calculating spectral indices like NDVI and SAVI.
Introduction to Indices Calculation
Let's begin with an example involving two data points representing the near-infrared (NIR) and red reflectances of vegetation, stored as Int values:
nir = 6723
red = 12431243Our goal is to calculate the Normalized Difference Vegetation Index (NDVI). NDVI is a widely used spectral index for monitoring vegetation health, calculated using NIR and red reflectances. The formula for NDVI is:
Direct Calculation with NDVI Struct
SpectralIndices.jl provides a straightforward method for computing NDVI:
using SpectralIndices
NDVINDVI: Normalized Difference Vegetation Index
* Application Domain: vegetation
* Bands/Parameters: Any["N", "R"]
* Formula: (N-R)/(N+R)
* Reference: https://ntrs.nasa.gov/citations/19740022614This outputs the NDVI struct, containing all necessary information. The struct can also be used as a function to compute NDVI:
NDVI(Float64, nir, red)0.6879236756213909This method is direct but not the recommended approach for computing indices. When using this method, ensure the parameter order matches the bands field of the SpectralIndex:
NDVI.bands2-element Vector{Any}:
"N"
"R"Using compute_index
A more flexible way to calculate indices is through the compute_index function. This function accepts the SpectralIndex struct and parameters as either a dictionary or keyword arguments:
params = Dict(
"N" => nir,
"R" => red
)
ndvi = compute_index(NDVI, params)0.6879236756213909Warning
Please ensure dictionary keys match the band names specified in the bands field.
Additionally you can pass the values as kwargs as follows:
ndvi = compute_index(NDVI; N=nir, R=red)0.6879236756213909Order of keyword arguments does not affect the outcome:
ndvi1 = compute_index(NDVI; N=nir, R=red)
ndvi2 = compute_index(NDVI; R=red, N=nir)
ndvi1 == ndvi2trueAdditionally, you can also pass the indexas a String:
params = Dict(
"N" => nir,
"R" => red
)
ndvi = compute_index("NDVI", params)0.6879236756213909Or, using the kwargs:
ndvi = compute_index("NDVI"; N=nir, R=red)0.6879236756213909Handling Floats
For Floats the procedure is similar. We will illustrate the example with the SAVI index
SAVISAVI: Soil-Adjusted Vegetation Index
* Application Domain: vegetation
* Bands/Parameters: Any["L", "N", "R"]
* Formula: (1.0+L)*(N-R)/(N+R+L)
* Reference: https://doi.org/10.1016/0034-4257(88)90106-XThis index needs the following bands:
SAVI.bands3-element Vector{Any}:
"L"
"N"
"R"The L parameter is new in this example. Thankfully, SpectralIndices.jl provides a list of constant values handy that we can leverage in this situation:
constants["L"]L: Canopy background adjustment
* Description: Canopy background adjustment
* Standard: L
* Default value: 1.0
* Current value: 1.0So now that we know what L is or does, we can use it in our calculation of the SAVI index. But first we are going to redefine the values to be Floats since we want to showcase some properties of SpectralIndices.jl with that data type. Additionally, SAVI needs imput values to be between -1 and 1:
nir /= 10000
red /= 100000.1243Now we can proceed as before. Either using a Dict to built our parameters:
params = Dict(
"N" => nir,
"R" => red,
"L" => 0.5
)
savi = compute(SAVI, params)0.6339657565941694or by passing them as kwargs:
savi = compute(SAVI; N=nir, R=red, L=0.5)0.6339657565941694And the same holds true for compute_index as well:
params = Dict(
"N" => nir,
"R" => red,
"L" => 0.5
)
savi = compute_index("SAVI", params)0.6339657565941694savi = compute_index("SAVI"; N=nir, R=red, L=0.5)0.6339657565941694Float32, Float16
The package can compute indices at custom precision
T = Float32
savi = compute_index("SAVI"; N=T(nir), R=T(red), L=T(0.5))0.63396573f0T = Float16
savi = compute_index("SAVI"; N=T(nir), R=T(red), L=T(0.5))Float16(0.634)Computing Multiple Indices
Now that we have added more indices we can explore how to compute multiple indices at the same time. All is needed is to pass a Vector of Strings to the compute_index function with the chosen spectral indices inside, as well as the chosen parameters of course:
params = Dict(
"N" => nir,
"R" => red,
"L" => 0.5
)
ndvi, savi = compute_index(["NDVI", "SAVI"], params)2-element Vector{Any}:
0.687923675621391
0.6339657565941694Alternatively, using kwargs:
ndvi, savi = compute_index(["NDVI", "SAVI"]; N=nir, R=red, L=0.5)2-element Vector{Any}:
0.687923675621391
0.6339657565941694Extension to Vectors
The extension to Vectors is relatively straightforward. We follow the same procedure as before, defining our parameters, only this time they are arrays:
params = Dict(
"N" => fill(nir, 10),
"R" => fill(red, 10),
"L" => fill(0.5, 10)
)Dict{String, Vector{Float64}} with 3 entries:
"N" => [0.6723, 0.6723, 0.6723, 0.6723, 0.6723, 0.6723, 0.6723, 0.6723, 0.672…
"L" => [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]
"R" => [0.1243, 0.1243, 0.1243, 0.1243, 0.1243, 0.1243, 0.1243, 0.1243, 0.124…After that we can compute either one, or both indices:
ndvi, savi = compute_index(["NDVI", "SAVI"], params)2-element Vector{Any}:
[0.687923675621391, 0.687923675621391, 0.687923675621391, 0.687923675621391, 0.687923675621391, 0.687923675621391, 0.687923675621391, 0.687923675621391, 0.687923675621391, 0.687923675621391]
[0.6339657565941694, 0.6339657565941694, 0.6339657565941694, 0.6339657565941694, 0.6339657565941694, 0.6339657565941694, 0.6339657565941694, 0.6339657565941694, 0.6339657565941694, 0.6339657565941694]We can use the same params to calculate single indices. The additional bands are just going to be ignored:
ndvi = compute_index("NDVI", params)10-element Vector{Float64}:
0.687923675621391
0.687923675621391
0.687923675621391
0.687923675621391
0.687923675621391
0.687923675621391
0.687923675621391
0.687923675621391
0.687923675621391
0.687923675621391savi = compute_index("SAVI", params)10-element Vector{Float64}:
0.6339657565941694
0.6339657565941694
0.6339657565941694
0.6339657565941694
0.6339657565941694
0.6339657565941694
0.6339657565941694
0.6339657565941694
0.6339657565941694
0.6339657565941694And as always, we can also pass them as kwargs:
ndvi, savi = compute_index(["NDVI", "SAVI"];
N=fill(nir, 10),
R=fill(red, 10),
L=fill(0.5, 10))2-element Vector{Any}:
[0.687923675621391, 0.687923675621391, 0.687923675621391, 0.687923675621391, 0.687923675621391, 0.687923675621391, 0.687923675621391, 0.687923675621391, 0.687923675621391, 0.687923675621391]
[0.6339657565941694, 0.6339657565941694, 0.6339657565941694, 0.6339657565941694, 0.6339657565941694, 0.6339657565941694, 0.6339657565941694, 0.6339657565941694, 0.6339657565941694, 0.6339657565941694]ndvi = compute_index("NDVI";
N=fill(nir, 10),
R=fill(red, 10),
L=fill(0.5, 10))10-element Vector{Float64}:
0.687923675621391
0.687923675621391
0.687923675621391
0.687923675621391
0.687923675621391
0.687923675621391
0.687923675621391
0.687923675621391
0.687923675621391
0.687923675621391savi = compute_index("SAVI";
N=fill(nir, 10),
R=fill(red, 10),
L=fill(0.5, 10))10-element Vector{Float64}:
0.6339657565941694
0.6339657565941694
0.6339657565941694
0.6339657565941694
0.6339657565941694
0.6339657565941694
0.6339657565941694
0.6339657565941694
0.6339657565941694
0.6339657565941694Extension to NamedTuples
SpectralIndices.jl allows you to also create indices from NamedTuples:
params = (N=fill(0.2, 10), R=fill(0.1, 10), L=fill(0.5, 10))
compute_index("NDVI", params)(NDVI = [0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333],)compute_index(["NDVI", "SAVI"], params)(NDVI = [0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333], SAVI = [0.18750000000000003, 0.18750000000000003, 0.18750000000000003, 0.18750000000000003, 0.18750000000000003, 0.18750000000000003, 0.18750000000000003, 0.18750000000000003, 0.18750000000000003, 0.18750000000000003])You can also pass the NamedTuple as kwargs splatting them, but the output will not be a NamedTuple
compute_index("NDVI"; params...)10-element Vector{Float64}:
0.3333333333333333
0.3333333333333333
0.3333333333333333
0.3333333333333333
0.3333333333333333
0.3333333333333333
0.3333333333333333
0.3333333333333333
0.3333333333333333
0.3333333333333333compute_index(["NDVI", "SAVI"]; params...)2-element Vector{Any}:
[0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333, 0.3333333333333333]
[0.18750000000000003, 0.18750000000000003, 0.18750000000000003, 0.18750000000000003, 0.18750000000000003, 0.18750000000000003, 0.18750000000000003, 0.18750000000000003, 0.18750000000000003, 0.18750000000000003]