Skip to content

Commit

Permalink
Added Topographic Position Index processor.
Browse files Browse the repository at this point in the history
  • Loading branch information
metasim committed Oct 25, 2023
1 parent cd72939 commit 8377428
Show file tree
Hide file tree
Showing 8 changed files with 270 additions and 42 deletions.
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

- Added support for digital elevation model raster processing.

- <>

- **Breaking**: `CslStringListIterator` returns a `CslStringListEntry` instead of `(String, String)` in order to differentiate between `key=value` entries vs `flag` entries.
- **Breaking**: `CslStringList::fetch_name_value` returns `Option<String>` instead of `Result<Option<String>>`, better reflecting the semantics of GDAL C API.
- Added `CslStringList::get_field`, `CslStringList::find_string`, `CslStringList::partial_find_string`, `CslStringList::find_string_case_sensitive`, `CslStringList::into_ptr`
Expand Down
28 changes: 14 additions & 14 deletions src/raster/processing/dem/aspect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::path::{Path, PathBuf};

use crate::cpl::CslStringList;
use crate::errors::*;
use crate::raster::processing::dem::{DEMSlopeAlg, GdalDemProcessor};
use crate::raster::processing::dem::{DemSlopeAlg, GdalDemProcessor};
use crate::raster::processing::{merge, GdalProcessor};
use crate::Dataset;

Expand All @@ -18,23 +18,23 @@ use crate::Dataset;
/// * 270° it's facing the West (provided that the top of your input raster is north oriented).
///
/// By default, the aspect value `-9999` is used as the no-data value to indicate undefined aspect in flat
/// areas with slope=0. See [`DEMAspectProcessor::with_zero_for_flat`] for alternative.
/// areas with slope=0. See [`DemAspectProcessor::with_zero_for_flat`] for alternative.
///
/// Note: Results are nonsensical if the underlying [`Dataset`] does not contain digital elevation data.
///
/// See: [`gdaldem aspect`](https://gdal.org/programs/gdaldem.html#aspect) for details,
#[derive(Debug, Clone)]
pub struct DEMAspectProcessor {
pub struct DemAspectProcessor {
input_band: Option<NonZeroUsize>,
algorithm: Option<DEMSlopeAlg>,
algorithm: Option<DemSlopeAlg>,
destination: Option<PathBuf>,
compute_edges: Option<bool>,
zero_for_flat: Option<bool>,
trigonometric: Option<bool>,
extra_options: Option<CslStringList>,
}

impl DEMAspectProcessor {
impl DemAspectProcessor {
/// Create a aspect-from-DEM processor
pub fn new() -> Self {
Self {
Expand All @@ -59,7 +59,7 @@ impl DEMAspectProcessor {
}

/// Specify the slope computation algorithm.
pub fn with_algorithm(self, algorithm: DEMSlopeAlg) -> Self {
pub fn with_algorithm(self, algorithm: DemSlopeAlg) -> Self {
Self {
algorithm: Some(algorithm),
..self
Expand Down Expand Up @@ -114,13 +114,13 @@ impl DEMAspectProcessor {
}
}

impl Default for DEMAspectProcessor {
impl Default for DemAspectProcessor {
fn default() -> Self {
Self::new()
}
}

impl GdalProcessor for DEMAspectProcessor {
impl GdalProcessor for DemAspectProcessor {
fn to_options(&self) -> CslStringList {
let mut opts = CslStringList::new();

Expand Down Expand Up @@ -158,7 +158,7 @@ impl GdalProcessor for DEMAspectProcessor {
}
}

impl GdalDemProcessor for DEMAspectProcessor {
impl GdalDemProcessor for DemAspectProcessor {
fn mode(&self) -> &'static str {
"aspect"
}
Expand All @@ -179,9 +179,9 @@ mod tests {

#[test]
fn options() -> Result<()> {
let dsp = DEMAspectProcessor::new()
let dsp = DemAspectProcessor::new()
.with_input_band(2.try_into().unwrap())
.with_algorithm(DEMSlopeAlg::ZevenbergenThorne)
.with_algorithm(DemSlopeAlg::ZevenbergenThorne)
.with_compute_edges(true)
.with_zero_for_flat(true)
.with_trigonometric_angles(true)
Expand All @@ -197,13 +197,13 @@ mod tests {

#[test]
fn aspect() -> Result<()> {
let dsp = DEMAspectProcessor::new()
.with_algorithm(DEMSlopeAlg::Horn)
let proc = DemAspectProcessor::new()
.with_algorithm(DemSlopeAlg::Horn)
.with_zero_for_flat(true);

let ds = Dataset::open(fixture("dem-hills.tiff"))?;

let slope = dsp.eval(&ds)?;
let slope = proc.eval(&ds)?;

let stats = slope.rasterband(1)?.get_statistics(true, false)?.unwrap();

Expand Down
10 changes: 6 additions & 4 deletions src/raster/processing/dem/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
mod aspect;
mod roughness;
mod slope;
mod tpi;

pub use aspect::*;
pub use roughness::*;
pub use slope::*;
pub use tpi::*;

use crate::cpl::CslStringList;
use crate::errors::Result;
Expand Down Expand Up @@ -64,16 +66,16 @@ pub(crate) trait GdalDemProcessor: GdalProcessor {
/// The literature suggests `ZevenbergenThorne` to be more suited to smooth landscapes,
/// whereas `Horn` performs better on rougher terrain.
#[derive(Debug, Clone, Copy)]
pub enum DEMSlopeAlg {
pub enum DemSlopeAlg {
Horn,
ZevenbergenThorne,
}

impl Display for DEMSlopeAlg {
impl Display for DemSlopeAlg {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
DEMSlopeAlg::Horn => f.write_str("Horn"),
DEMSlopeAlg::ZevenbergenThorne => f.write_str("ZevenbergenThorne"),
DemSlopeAlg::Horn => f.write_str("Horn"),
DemSlopeAlg::ZevenbergenThorne => f.write_str("ZevenbergenThorne"),
}
}
}
Expand Down
18 changes: 9 additions & 9 deletions src/raster/processing/dem/roughness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ use crate::Dataset;
///
/// See: [`gdaldem roughness`](https://gdal.org/programs/gdaldem.html#roughness) for details.
#[derive(Debug, Clone)]
pub struct DEMRoughnessProcessor {
pub struct DemRoughnessProcessor {
input_band: Option<NonZeroUsize>,
destination: Option<PathBuf>,
compute_edges: Option<bool>,
extra_options: Option<CslStringList>,
}

impl DEMRoughnessProcessor {
impl DemRoughnessProcessor {
/// Create a aspect-from-DEM processor
pub fn new() -> Self {
Self {
Expand Down Expand Up @@ -77,13 +77,13 @@ impl DEMRoughnessProcessor {
}
}

impl Default for DEMRoughnessProcessor {
impl Default for DemRoughnessProcessor {
fn default() -> Self {
Self::new()
}
}

impl GdalProcessor for DEMRoughnessProcessor {
impl GdalProcessor for DemRoughnessProcessor {
fn to_options(&self) -> CslStringList {
let mut opts = CslStringList::new();

Expand All @@ -108,7 +108,7 @@ impl GdalProcessor for DEMRoughnessProcessor {
}
}

impl GdalDemProcessor for DEMRoughnessProcessor {
impl GdalDemProcessor for DemRoughnessProcessor {
fn mode(&self) -> &'static str {
"roughness"
}
Expand All @@ -129,7 +129,7 @@ mod tests {

#[test]
fn options() -> Result<()> {
let dsp = DEMRoughnessProcessor::new()
let dsp = DemRoughnessProcessor::new()
.with_input_band(2.try_into().unwrap())
.with_compute_edges(true)
.with_options("-of GTiff".parse()?);
Expand All @@ -141,12 +141,12 @@ mod tests {
}

#[test]
fn aspect() -> Result<()> {
let dsp = DEMRoughnessProcessor::new();
fn roughness() -> Result<()> {
let proc = DemRoughnessProcessor::new();

let ds = Dataset::open(fixture("dem-hills.tiff"))?;

let slope = dsp.eval(&ds)?;
let slope = proc.eval(&ds)?;

let stats = slope.rasterband(1)?.get_statistics(true, false)?.unwrap();

Expand Down
30 changes: 15 additions & 15 deletions src/raster/processing/dem/slope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,36 @@ use std::path::{Path, PathBuf};

use crate::cpl::CslStringList;
use crate::errors::*;
use crate::raster::processing::dem::{DEMSlopeAlg, GdalDemProcessor};
use crate::raster::processing::dem::{DemSlopeAlg, GdalDemProcessor};
use crate::raster::processing::{merge, GdalProcessor};
use crate::Dataset;

/// Slope processor for DEM datasets.
///
/// This processor will take a DEM raster and output a 32-bit float raster with slope values.
/// You have the option of specifying the type of slope value you want:
/// [degrees or percent slope](DEMSlopeProcessor::with_percentage_results).
/// [degrees or percent slope](DemSlopeProcessor::with_percentage_results).
///
/// In cases where the horizontal units differ from the vertical units, you can also supply
/// a [scaling factor](DEMSlopeProcessor::with_scale).
/// a [scaling factor](DemSlopeProcessor::with_scale).
///
/// The value `-9999` is used as the output no-data value.
///
/// Note: Results are nonsensical if the underlying [`Dataset`] does not contain digital elevation data.
///
/// See: [`gdaldem slope`](https://gdal.org/programs/gdaldem.html#slope) for details,
#[derive(Debug, Clone)]
pub struct DEMSlopeProcessor {
pub struct DemSlopeProcessor {
input_band: Option<NonZeroUsize>,
algorithm: Option<DEMSlopeAlg>,
algorithm: Option<DemSlopeAlg>,
scale: Option<f64>,
destination: Option<PathBuf>,
compute_edges: Option<bool>,
percentage_results: Option<bool>,
extra_options: Option<CslStringList>,
}

impl DEMSlopeProcessor {
impl DemSlopeProcessor {
/// Create a slope-from-DEM processor
pub fn new() -> Self {
Self {
Expand All @@ -57,7 +57,7 @@ impl DEMSlopeProcessor {
}

/// Specify the slope computation algorithm.
pub fn with_algorithm(self, algorithm: DEMSlopeAlg) -> Self {
pub fn with_algorithm(self, algorithm: DemSlopeAlg) -> Self {
Self {
algorithm: Some(algorithm),
..self
Expand Down Expand Up @@ -122,13 +122,13 @@ impl DEMSlopeProcessor {
}
}

impl Default for DEMSlopeProcessor {
impl Default for DemSlopeProcessor {
fn default() -> Self {
Self::new()
}
}

impl GdalProcessor for DEMSlopeProcessor {
impl GdalProcessor for DemSlopeProcessor {
fn to_options(&self) -> CslStringList {
let mut opts = CslStringList::new();

Expand Down Expand Up @@ -167,7 +167,7 @@ impl GdalProcessor for DEMSlopeProcessor {
}
}

impl GdalDemProcessor for DEMSlopeProcessor {
impl GdalDemProcessor for DemSlopeProcessor {
fn mode(&self) -> &'static str {
"slope"
}
Expand All @@ -188,9 +188,9 @@ mod tests {

#[test]
fn options() -> Result<()> {
let dsp = DEMSlopeProcessor::new()
let dsp = DemSlopeProcessor::new()
.with_input_band(2.try_into().unwrap())
.with_algorithm(DEMSlopeAlg::ZevenbergenThorne)
.with_algorithm(DemSlopeAlg::ZevenbergenThorne)
.with_scale(37.0)
.with_compute_edges(true)
.with_percentage_results(true)
Expand All @@ -205,14 +205,14 @@ mod tests {

#[test]
fn slope() -> Result<()> {
let dsp = DEMSlopeProcessor::new()
.with_algorithm(DEMSlopeAlg::Horn)
let proc = DemSlopeProcessor::new()
.with_algorithm(DemSlopeAlg::Horn)
.with_percentage_results(true)
.with_scale(111120.0);

let ds = Dataset::open(fixture("dem-hills.tiff"))?;

let slope = dsp.eval(&ds)?;
let slope = proc.eval(&ds)?;

let stats = slope.rasterband(1)?.get_statistics(true, false)?.unwrap();

Expand Down
Loading

0 comments on commit 8377428

Please sign in to comment.