From 266c939ad236c16503b088d2faa4500362912fef Mon Sep 17 00:00:00 2001
From: John Lindsay
Date: Wed, 6 Feb 2019 18:13:58 -0500
Subject: [PATCH] Numerous bug fixes and documentation improvements
Numerous bug fixes and documentation improvements
---
.../extract_raster_values_at_points.rs | 174 +++------
src/tools/gis_analysis/highest_pos.rs | 16 +-
src/tools/gis_analysis/lowest_pos.rs | 16 +-
src/tools/gis_analysis/max_abs_overlay.rs | 12 +-
src/tools/gis_analysis/max_overlay.rs | 11 +-
src/tools/gis_analysis/min_abs_overlay.rs | 12 +-
src/tools/gis_analysis/min_overlay.rs | 11 +-
src/tools/gis_analysis/percent_equal_to.rs | 18 +-
.../gis_analysis/percent_greater_than.rs | 18 +-
src/tools/gis_analysis/percent_less_than.rs | 18 +-
src/tools/gis_analysis/sum_overlay.rs | 6 +-
.../balance_contrast_enhancement.rs | 2 +-
.../image_analysis/change_vector_analysis.rs | 8 +-
.../image_analysis/create_colour_composite.rs | 2 +-
.../direct_decorrelation_stretch.rs | 2 +-
.../edge_preserving_mean_filter.rs | 19 +-
src/tools/image_analysis/gamma_correction.rs | 45 +--
.../image_analysis/histogram_equalization.rs | 2 +-
.../image_analysis/histogram_matching.rs | 2 +-
.../histogram_matching_two_images.rs | 2 +-
src/tools/image_analysis/ihs_to_rgb.rs | 327 ++++++++++++-----
.../image_analysis/image_stack_profile.rs | 16 +-
src/tools/image_analysis/integral_image.rs | 13 +-
src/tools/image_analysis/line_thin.rs | 2 +-
src/tools/image_analysis/mean_filter.rs | 63 ++--
.../min_max_contrast_stretch.rs | 2 +-
.../modified_k_means_clustering.rs | 4 +-
src/tools/image_analysis/mosaic.rs | 4 +-
.../percentage_contrast_stretch.rs | 4 +-
src/tools/image_analysis/resample.rs | 2 +-
src/tools/image_analysis/rgb_to_ihs.rs | 339 ++++++++++++------
.../sigmoidal_contrast_stretch.rs | 24 +-
src/tools/image_analysis/sobel_filter.rs | 4 +-
.../image_analysis/split_colour_composite.rs | 36 +-
.../image_analysis/stdev_contrast_stretch.rs | 18 +-
src/tools/image_analysis/thicken_line.rs | 18 +-
.../write_func_memory_insertion.rs | 23 +-
.../lidar_analysis/clip_lidar_to_polygon.rs | 11 +
.../erase_polygon_from_lidar.rs | 11 +
src/tools/lidar_analysis/lidar_join.rs | 8 +-
src/tools/lidar_analysis/lidar_tile.rs | 30 +-
.../lidar_analysis/lidar_tile_footprint.rs | 13 +-
.../principal_component_analysis.rs | 82 ++++-
.../stream_network_analysis/horton_order.rs | 2 +-
.../stream_network_analysis/long_profile.rs | 26 +-
.../long_profile_from_points.rs | 2 +-
.../raster_streams_to_vector.rs | 3 +
.../rasterize_streams.rs | 2 +-
.../circular_variance_of_aspect.rs | 12 +-
.../drainage_preserving_smoothing.rs | 51 +--
src/tools/terrain_analysis/elev_percentile.rs | 2 +-
.../feature_preserving_denoise.rs | 55 +--
.../terrain_analysis/percent_elev_range.rs | 4 +-
src/tools/terrain_analysis/profile.rs | 13 +-
src/tools/terrain_analysis/relative_aspect.rs | 17 +
.../relative_topographic_position.rs | 31 +-
whitebox_plugin_generator.py | 3 +-
whitebox_tools.py | 70 ++--
58 files changed, 1190 insertions(+), 553 deletions(-)
diff --git a/src/tools/gis_analysis/extract_raster_values_at_points.rs b/src/tools/gis_analysis/extract_raster_values_at_points.rs
index b416f3fa5..8b5be9256 100644
--- a/src/tools/gis_analysis/extract_raster_values_at_points.rs
+++ b/src/tools/gis_analysis/extract_raster_values_at_points.rs
@@ -2,7 +2,7 @@
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
Created: 17/06/2018
-Last Modified: 17/06/2018
+Last Modified: 30/01/2019
License: MIT
*/
@@ -14,7 +14,17 @@ use std::f64;
use std::io::{Error, ErrorKind};
use std::path;
-/// Extracts the values of raster(s) at vector point locations.
+/// This tool can be used to extract the values of one or more rasters (`--inputs`) at the sites of a set of vector points.
+/// By default, the data is output to the attribute table of the input points (`--points`) vector; however,
+/// if the `--out_text` parameter is specified, the tool will additionally output point values as text data
+/// to standard output (*stdout*). Attribute fields will be added to the table of the points file, with field
+/// names, *VALUE1*, *VALUE2*, *VALUE3*, etc. each corresponding to the order of input rasters.
+///
+/// If you need to plot a chart of values from a raster stack at a set of points, the `ImageStackProfile` may be
+/// more suitable for this application.
+///
+/// # See Also
+/// `ImageStackProfile`, `FindLowestOrHighestPoints`
pub struct ExtractRasterValuesAtPoints {
name: String,
description: String,
@@ -51,6 +61,17 @@ impl ExtractRasterValuesAtPoints {
optional: false,
});
+ parameters.push(ToolParameter {
+ name: "Output text?".to_owned(),
+ flags: vec!["--out_text".to_owned()],
+ description:
+ "Output point values as text? Otherwise, the only output is to to the points file's attribute table."
+ .to_owned(),
+ parameter_type: ParameterType::Boolean,
+ default_value: Some("false".to_string()),
+ optional: true,
+ });
+
let sep: String = path::MAIN_SEPARATOR.to_string();
let p = format!("{}", env::current_dir().unwrap().display());
let e = format!("{}", env::current_exe().unwrap().display());
@@ -117,6 +138,7 @@ impl WhiteboxTool for ExtractRasterValuesAtPoints {
) -> Result<(), Error> {
let mut input_files = String::new();
let mut points_file = String::new();
+ let mut output_text = false;
if args.len() == 0 {
return Err(Error::new(
@@ -134,24 +156,23 @@ impl WhiteboxTool for ExtractRasterValuesAtPoints {
keyval = true;
}
let flag_val = vec[0].to_lowercase().replace("--", "-");
- if vec[0].to_lowercase() == "-i" || vec[0].to_lowercase() == "--inputs" {
- if keyval {
- input_files = vec[1].to_string();
+ if flag_val == "-i" || flag_val == "-inputs" {
+ input_files = if keyval {
+ vec[1].to_string()
} else {
- input_files = args[i + 1].to_string();
- }
+ args[i + 1].to_string()
+ };
} else if flag_val == "-points" {
points_file = if keyval {
vec[1].to_string()
} else {
args[i + 1].to_string()
};
+ } else if flag_val.contains("-out_text") {
+ output_text = true;
}
}
- // let mut progress: usize;
- // let mut old_progress: usize = 1;
-
if verbose {
println!("***************{}", "*".repeat(self.get_tool_name().len()));
println!("* Welcome to {} *", self.get_tool_name());
@@ -163,12 +184,12 @@ impl WhiteboxTool for ExtractRasterValuesAtPoints {
let start = Instant::now();
let mut cmd = input_files.split(";");
- let mut vec = cmd.collect::>();
- if vec.len() == 1 {
+ let mut v = cmd.collect::>();
+ if v.len() == 1 {
cmd = input_files.split(",");
- vec = cmd.collect::>();
+ v = cmd.collect::>();
}
- let num_files = vec.len();
+ let num_files = v.len();
if num_files < 1 {
return Err(Error::new(ErrorKind::InvalidInput,
"There is something incorrect about the input files. At least one input is required to operate this tool."));
@@ -187,8 +208,9 @@ impl WhiteboxTool for ExtractRasterValuesAtPoints {
}
let (mut row, mut col): (isize, isize);
- let mut x_vals = vec![];
- let mut y_vals = vec![];
+ let mut x_vals = Vec::with_capacity(num_records);
+ let mut y_vals = Vec::with_capacity(num_records);
+ let mut raster_values = vec![vec![0f64; num_files]; num_records];
for record_num in 0..num_records {
let record = points.get_record(record_num);
y_vals.push(record.points[0].y);
@@ -196,8 +218,8 @@ impl WhiteboxTool for ExtractRasterValuesAtPoints {
}
// add the attributes for each raster
- for i in 0..vec.len() {
- if !vec[i].trim().is_empty() {
+ for i in 0..num_files {
+ if !v[i].trim().is_empty() {
let val =
AttributeField::new(&format!("VALUE{}", i + 1), FieldDataType::Real, 12u8, 4u8);
points.attributes.add_field(&val);
@@ -206,7 +228,7 @@ impl WhiteboxTool for ExtractRasterValuesAtPoints {
let mut z: f64;
let mut i = 1;
- for value in vec {
+ for value in v {
if !value.trim().is_empty() {
if verbose {
println!("Reading data...")
@@ -227,114 +249,22 @@ impl WhiteboxTool for ExtractRasterValuesAtPoints {
&format!("VALUE{}", i),
FieldData::Real(z),
);
+
+ if output_text {
+ raster_values[record_num][i - 1] = z;
+ }
}
i += 1;
}
}
- // drop(attributes);
-
- // let start = time::now();
- // let rows = input.configs.rows as isize;
- // let columns = input.configs.columns as isize;
- // let nodata = input.configs.nodata;
-
- // // loop through the raster, locating the min/max
- // let rows_completed = Arc::new(Mutex::new(0..rows));
- // let old_progress = Arc::new(Mutex::new(1));
- // let num_procs = num_cpus::get() as isize;
- // let (tx, rx) = mpsc::channel();
- // for tid in 0..num_procs {
- // let input = input.clone();
- // let rows_completed = rows_completed.clone();
- // let old_progress = old_progress.clone();
- // let tx = tx.clone();
- // thread::spawn(move || {
- // let mut low_z = f64::INFINITY;
- // let mut low_row = 0isize;
- // let mut low_col = 0isize;
- // let mut high_z = f64::NEG_INFINITY;
- // let mut high_row = 0isize;
- // let mut high_col = 0isize;
- // let mut progress: usize;
- // // let mut old_progress: usize = 1;
- // for row in (0..rows).filter(|r| r % num_procs == tid) {
- // for col in 0..columns {
- // z = input.get_value(row, col);
- // if z != nodata {
- // if z < low_z {
- // low_z = z;
- // low_col = col;
- // low_row = row;
- // }
- // if z > high_z {
- // high_z = z;
- // high_col = col;
- // high_row = row;
- // }
- // }
- // }
- // let r = match rows_completed.lock().unwrap().next() {
- // Some(val) => val,
- // None => 0, // There are no more tiles to interpolate
- // };
- // if verbose {
- // progress = (100.0_f64 * r as f64 / (rows - 1) as f64) as usize;
- // let mut p = old_progress.lock().unwrap();
- // if progress != *p {
- // println!("Progress: {}%", progress);
- // *p = progress;
- // }
- // }
- // }
- // tx.send((low_z, low_col, low_row, high_z, high_col, high_row))
- // .unwrap();
- // });
- // }
-
- // let mut low_z = f64::INFINITY;
- // let mut low_row = 0isize;
- // let mut low_col = 0isize;
- // let mut high_z = f64::NEG_INFINITY;
- // let mut high_row = 0isize;
- // let mut high_col = 0isize;
- // for _ in 0..num_procs {
- // let data = rx.recv().unwrap();
- // if data.0 < low_z {
- // low_z = data.0;
- // low_col = data.1;
- // low_row = data.2;
- // }
- // if data.3 > high_z {
- // high_z = data.3;
- // high_col = data.4;
- // high_row = data.5;
- // }
- // }
-
- // // add the vector record(s)
- // let mut rec_num = 1i32;
- // if out_type == "lowest" || out_type == "both" {
- // output.add_point_record(
- // input.get_x_from_column(low_col),
- // input.get_y_from_row(low_row),
- // );
- // output
- // .attributes
- // .add_record(vec![FieldData::Int(rec_num), FieldData::Real(low_z)], false);
- // rec_num += 1i32;
- // }
-
- // if out_type == "highest" || out_type == "both" {
- // output.add_point_record(
- // input.get_x_from_column(high_col),
- // input.get_y_from_row(high_row),
- // );
- // output.attributes.add_record(
- // vec![FieldData::Int(rec_num), FieldData::Real(high_z)],
- // false,
- // );
- // }
+
+ if output_text {
+ println!("Point values:");
+ for record_num in 0..num_records {
+ println!("Point {} values: {:?}", record_num+1, raster_values[record_num]);
+ }
+ }
let elapsed_time = get_formatted_elapsed_time(start);
diff --git a/src/tools/gis_analysis/highest_pos.rs b/src/tools/gis_analysis/highest_pos.rs
index c7fbb7aef..28936ea3a 100644
--- a/src/tools/gis_analysis/highest_pos.rs
+++ b/src/tools/gis_analysis/highest_pos.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: June 22 2017
+Created: 22/06/2017
Last Modified: 13/10/2018
License: MIT
*/
@@ -14,6 +14,20 @@ use std::f64;
use std::io::{Error, ErrorKind};
use std::path;
+/// This tool identifies the stack position (index) of the maximum value within a raster stack on a cell-by-cell
+/// basis. For example, if five raster images (`--inputs`) are input to the tool, the output raster (`--output`)
+/// would show which of the five input rasters contained the highest value for each grid cell. The index value in
+/// the output raster is the zero-order number of the raster stack, i.e. if the highest value in the stack is
+/// contained in the first image, the output value would be 0; if the highest stack value were the second image,
+/// the output value would be 1, and so on. If any of the cell values within the stack is NoData, the output raster
+/// will contain the NoData value for the corresponding grid cell. The index value is related to the order of the
+/// input images.
+///
+/// # Warning
+/// Each of the input rasters must have the same spatial extent and number of rows and columns.
+///
+/// # See Also
+/// `LowestPosition`, `PickFromList`
pub struct HighestPosition {
name: String,
description: String,
diff --git a/src/tools/gis_analysis/lowest_pos.rs b/src/tools/gis_analysis/lowest_pos.rs
index 4b76d3e8e..58b53c9db 100644
--- a/src/tools/gis_analysis/lowest_pos.rs
+++ b/src/tools/gis_analysis/lowest_pos.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: July 4, 2017
+Created: 04/07/2017
Last Modified: 13/10/2018
License: MIT
*/
@@ -14,6 +14,20 @@ use std::f64;
use std::io::{Error, ErrorKind};
use std::path;
+/// This tool identifies the stack position (index) of the minimum value within a raster stack on a cell-by-cell
+/// basis. For example, if five raster images (`--inputs`) are input to the tool, the output raster (`--output`)
+/// would show which of the five input rasters contained the lowest value for each grid cell. The index value in
+/// the output raster is the zero-order number of the raster stack, i.e. if the lowest value in the stack is
+/// contained in the first image, the output value would be 0; if the lowest stack value were the second image,
+/// the output value would be 1, and so on. If any of the cell values within the stack is NoData, the output raster
+/// will contain the NoData value for the corresponding grid cell. The index value is related to the order of the
+/// input images.
+///
+/// # Warning
+/// Each of the input rasters must have the same spatial extent and number of rows and columns.
+///
+/// # See Also
+/// `HighestPosition`, `PickFromList`
pub struct LowestPosition {
name: String,
description: String,
diff --git a/src/tools/gis_analysis/max_abs_overlay.rs b/src/tools/gis_analysis/max_abs_overlay.rs
index 28ab6ce2a..31edd0410 100644
--- a/src/tools/gis_analysis/max_abs_overlay.rs
+++ b/src/tools/gis_analysis/max_abs_overlay.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: June 22 2017
+Created: 22/06/2017
Last Modified: 13/10/2018
License: MIT
*/
@@ -13,6 +13,16 @@ use std::f64;
use std::io::{Error, ErrorKind};
use std::path;
+/// This tool can be used to find the maximum absolute (non-negative) value in each cell of a grid from a set of
+/// input images (`--inputs`). NoData values in any of the input images will result in a NoData pixel in the output
+/// image.
+///
+/// # Warning
+/// Each of the input rasters must have the same spatial extent and number of rows
+/// and columns.
+///
+/// # See Also
+/// `MaxOverlay`, `MinAbsoluteOverlay`, `MinOverlay`
pub struct MaxAbsoluteOverlay {
name: String,
description: String,
diff --git a/src/tools/gis_analysis/max_overlay.rs b/src/tools/gis_analysis/max_overlay.rs
index 1cc113589..e46848751 100644
--- a/src/tools/gis_analysis/max_overlay.rs
+++ b/src/tools/gis_analysis/max_overlay.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: June 22 2017
+Created: 22/06/2017
Last Modified: 13/10/2018
License: MIT
*/
@@ -13,6 +13,15 @@ use std::f64;
use std::io::{Error, ErrorKind};
use std::path;
+/// This tool can be used to find the maximum value in each cell of a grid from a set of input images (`--inputs`).
+/// NoData values in any of the input images will result in a NoData pixel in the output image (`--output`). It is
+/// similar to the `Max` mathematical tool, except that it will accept more than two input images.
+///
+/// # Warning
+/// Each of the input rasters must have the same spatial extent and number of rows and columns.
+///
+/// # See Also
+/// `MinOverlay`, `MaxAbsoluteOverlay`, `MinAbsoluteOverlay`, `Max`
pub struct MaxOverlay {
name: String,
description: String,
diff --git a/src/tools/gis_analysis/min_abs_overlay.rs b/src/tools/gis_analysis/min_abs_overlay.rs
index d76c422ed..874bcc97c 100644
--- a/src/tools/gis_analysis/min_abs_overlay.rs
+++ b/src/tools/gis_analysis/min_abs_overlay.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: June 22 2017
+Created: 22/06/2017
Last Modified: 13/10/2018
License: MIT
*/
@@ -13,6 +13,16 @@ use std::f64;
use std::io::{Error, ErrorKind};
use std::path;
+/// This tool can be used to find the minimum absolute (non-negative) value in each cell of a grid from a set of
+/// input images (`--inputs`). NoData values in any of the input images will result in a NoData pixel in the output
+/// image.
+///
+/// # Warning
+/// Each of the input rasters must have the same spatial extent and number of rows
+/// and columns.
+///
+/// # See Also
+/// `MinOverlay`, `MaxAbsoluteOverlay`, `MaxOverlay`
pub struct MinAbsoluteOverlay {
name: String,
description: String,
diff --git a/src/tools/gis_analysis/min_overlay.rs b/src/tools/gis_analysis/min_overlay.rs
index b38e556f2..7030ab90a 100644
--- a/src/tools/gis_analysis/min_overlay.rs
+++ b/src/tools/gis_analysis/min_overlay.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: June 22 2017
+Created: 22/06/2017
Last Modified: 13/10/2018
License: MIT
*/
@@ -13,6 +13,15 @@ use std::f64;
use std::io::{Error, ErrorKind};
use std::path;
+/// This tool can be used to find the minimum value in each cell of a grid from a set of input images (`--inputs`).
+/// NoData values in any of the input images will result in a NoData pixel in the output image (`--output`). It is
+/// similar to the `Min` mathematical tool, except that it will accept more than two input images.
+///
+/// # Warning
+/// Each of the input rasters must have the same spatial extent and number of rows and columns.
+///
+/// # See Also
+/// `MaxOverlay`, `MaxAbsoluteOverlay`, `MinAbsoluteOverlay`, `Min`
pub struct MinOverlay {
name: String,
description: String,
diff --git a/src/tools/gis_analysis/percent_equal_to.rs b/src/tools/gis_analysis/percent_equal_to.rs
index b46ec4a2b..5b2a48389 100644
--- a/src/tools/gis_analysis/percent_equal_to.rs
+++ b/src/tools/gis_analysis/percent_equal_to.rs
@@ -1,8 +1,8 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: June 22 2017
-Last Modified: 13/10/2018
+Created: 22/06/2017
+Last Modified: 31/01/2019
License: MIT
*/
@@ -14,6 +14,19 @@ use std::f64;
use std::io::{Error, ErrorKind};
use std::path;
+/// This tool calculates the percentage of a raster stack (`--inputs`) that have cell values equal to an input *comparison*
+/// raster. The user must specify the name of the value raster (`--comparison`), the names of the raster files contained
+/// in the stack, and an output raster file name (`--output`). The tool, working on a cell-by-cell basis, will count the
+/// number of rasters within the stack that have the same grid cell value as the corresponding grid cell in the *comparison*
+/// raster. This count is then expressed as a percentage of the number of rasters contained within the stack and output.
+/// If any of the rasters within the stack contain the NoData value, the corresponding grid cell in the output raster will
+/// be assigned NoData.
+///
+/// # Warning
+/// Each of the input rasters must have the same spatial extent and number of rows and columns.
+///
+/// # See Also
+/// `PercentGreaterThan`, `PercentLessThan`
pub struct PercentEqualTo {
name: String,
description: String,
@@ -194,6 +207,7 @@ impl WhiteboxTool for PercentEqualTo {
let nodata = comparison.configs.nodata;
let mut output = Raster::initialize_using_file(&output_file, &comparison);
+ output.configs.data_type = DataType::F32;
let mut n_images: Array2D = Array2D::new(rows, columns, 0, -1)?;
let mut in_nodata: f64;
diff --git a/src/tools/gis_analysis/percent_greater_than.rs b/src/tools/gis_analysis/percent_greater_than.rs
index 2cd5ebb5f..cb5234bfc 100644
--- a/src/tools/gis_analysis/percent_greater_than.rs
+++ b/src/tools/gis_analysis/percent_greater_than.rs
@@ -1,8 +1,8 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: June 22 2017
-Last Modified: 13/10/2018
+Created: 22/06/2017
+Last Modified: 31/01/2019
License: MIT
*/
@@ -14,6 +14,19 @@ use std::f64;
use std::io::{Error, ErrorKind};
use std::path;
+/// This tool calculates the percentage of a raster stack (`--inputs`) that have cell values greater than an input *comparison*
+/// raster. The user must specify the name of the value raster (`--comparison`), the names of the raster files contained
+/// in the stack, and an output raster file name (`--output`). The tool, working on a cell-by-cell basis, will count the
+/// number of rasters within the stack with larger grid cell values greater than the corresponding grid cell in the *comparison*
+/// raster. This count is then expressed as a percentage of the number of rasters contained within the stack and output.
+/// If any of the rasters within the stack contain the NoData value, the corresponding grid cell in the output raster will
+/// be assigned NoData.
+///
+/// # Warning
+/// Each of the input rasters must have the same spatial extent and number of rows and columns.
+///
+/// # See Also
+/// `PercentLessThan`, `PercentEqualTo`
pub struct PercentGreaterThan {
name: String,
description: String,
@@ -194,6 +207,7 @@ impl WhiteboxTool for PercentGreaterThan {
let nodata = comparison.configs.nodata;
let mut output = Raster::initialize_using_file(&output_file, &comparison);
+ output.configs.data_type = DataType::F32;
let mut n_images: Array2D = Array2D::new(rows, columns, 0, -1)?;
let mut in_nodata: f64;
diff --git a/src/tools/gis_analysis/percent_less_than.rs b/src/tools/gis_analysis/percent_less_than.rs
index 7603e06dd..e932e8116 100644
--- a/src/tools/gis_analysis/percent_less_than.rs
+++ b/src/tools/gis_analysis/percent_less_than.rs
@@ -1,8 +1,8 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: June 22 2017
-Last Modified: 13/10/2018
+Created: 22/06/2017
+Last Modified: 31/01/2019
License: MIT
*/
@@ -14,6 +14,19 @@ use std::f64;
use std::io::{Error, ErrorKind};
use std::path;
+/// This tool calculates the percentage of a raster stack (`--inputs`) that have cell values less than an input *comparison*
+/// raster. The user must specify the name of the value raster (`--comparison`), the names of the raster files contained
+/// in the stack, and an output raster file name (`--output`). The tool, working on a cell-by-cell basis, will count the
+/// number of rasters within the stack with larger grid cell values less than the corresponding grid cell in the *comparison*
+/// raster. This count is then expressed as a percentage of the number of rasters contained within the stack and output.
+/// If any of the rasters within the stack contain the NoData value, the corresponding grid cell in the output raster will
+/// be assigned NoData.
+///
+/// # Warning
+/// Each of the input rasters must have the same spatial extent and number of rows and columns.
+///
+/// # See Also
+/// `PercentGreaterThan`, `PercentEqualTo`
pub struct PercentLessThan {
name: String,
description: String,
@@ -194,6 +207,7 @@ impl WhiteboxTool for PercentLessThan {
let nodata = comparison.configs.nodata;
let mut output = Raster::initialize_using_file(&output_file, &comparison);
+ output.configs.data_type = DataType::F32;
let mut n_images: Array2D = Array2D::new(rows, columns, 0, -1)?;
let mut in_nodata: f64;
diff --git a/src/tools/gis_analysis/sum_overlay.rs b/src/tools/gis_analysis/sum_overlay.rs
index a3391bdb0..f77ad4fbc 100644
--- a/src/tools/gis_analysis/sum_overlay.rs
+++ b/src/tools/gis_analysis/sum_overlay.rs
@@ -13,11 +13,11 @@ use std::f64;
use std::io::{Error, ErrorKind};
use std::path;
-/// This tool calculates the sum for each grid cell from a group of raster images.
+/// This tool calculates the sum for each grid cell from a group of raster images (`--inputs`). NoData values in any of the input
+/// images will result in a NoData pixel in the output image (`--output`).
///
/// # Warning
-/// Each of the input rasters must have the same spatial extent and number of rows
-/// and columns.
+/// Each of the input rasters must have the same spatial extent and number of rows and columns.
///
/// # See Also
/// `WeightedSum`
diff --git a/src/tools/image_analysis/balance_contrast_enhancement.rs b/src/tools/image_analysis/balance_contrast_enhancement.rs
index bf00a0d01..6d7f3b909 100644
--- a/src/tools/image_analysis/balance_contrast_enhancement.rs
+++ b/src/tools/image_analysis/balance_contrast_enhancement.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: July 19, 2017
+Created: 19/07/2017
Last Modified: 13/10/2018
License: MIT
*/
diff --git a/src/tools/image_analysis/change_vector_analysis.rs b/src/tools/image_analysis/change_vector_analysis.rs
index 46b0f71ba..ad89ce31a 100644
--- a/src/tools/image_analysis/change_vector_analysis.rs
+++ b/src/tools/image_analysis/change_vector_analysis.rs
@@ -37,7 +37,13 @@ use std::path;
/// It is common to apply a simple thresholding operation on the magnitude data to
/// determine 'actual' change (i.e. change above some assumed level of error). The type
/// of change (qualitatively) is then defined according to the corresponding sector code.
-/// Jensen (2005) provides a useful description of this approach to change detection.
+/// Jensen (2015) provides a useful description of this approach to change detection.
+///
+/// # Reference
+/// Jensen, J. R. (2015). Introductory Digital Image Processing: A Remote Sensing Perspective.
+///
+/// # See Also
+/// `WriteFunctionMemoryInsertion`
pub struct ChangeVectorAnalysis {
name: String,
description: String,
diff --git a/src/tools/image_analysis/create_colour_composite.rs b/src/tools/image_analysis/create_colour_composite.rs
index 848de2a3f..fe06d8660 100644
--- a/src/tools/image_analysis/create_colour_composite.rs
+++ b/src/tools/image_analysis/create_colour_composite.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: July 19, 2017
+Created: 19/07/2017
Last Modified: 06/01/2019
License: MIT
*/
diff --git a/src/tools/image_analysis/direct_decorrelation_stretch.rs b/src/tools/image_analysis/direct_decorrelation_stretch.rs
index 84d776289..f0bd89dab 100644
--- a/src/tools/image_analysis/direct_decorrelation_stretch.rs
+++ b/src/tools/image_analysis/direct_decorrelation_stretch.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: July 21, 2017
+Created: 21/07/2017
Last Modified: 13/10/2018
License: MIT
*/
diff --git a/src/tools/image_analysis/edge_preserving_mean_filter.rs b/src/tools/image_analysis/edge_preserving_mean_filter.rs
index 7335a5e9b..5f28b2d6e 100644
--- a/src/tools/image_analysis/edge_preserving_mean_filter.rs
+++ b/src/tools/image_analysis/edge_preserving_mean_filter.rs
@@ -17,6 +17,23 @@ use std::path;
use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
+
+/// This tool performs a type of edge-preserving mean filter operation on an input image (`--input`). The filter, a
+/// type of low-pass filter, can be used to emphasize the longer-range variability in an image, effectively acting to
+/// smooth the image and to reduce noise in the image. The algorithm calculates the average value in a moving window
+/// centred on each grid cell, including in the averaging only the set of neighbouring values for which the absolute
+/// value difference with the centre value is less than a specified threshold value (`--threshold`). It is, therefore,
+/// similar to the `BilateralFilter`, except all neighbours within the threshold difference are equally weighted and
+/// neighbour distance is not accounted for. Filter kernels are always square, and filter size, is specified using
+/// the `--filter` parameter. This dimensions should be odd, positive integer values, e.g. 3, 5, 7, 9...
+///
+/// This tool works with both greyscale and red-green-blue (RGB) input images. RGB images are decomposed into
+/// intensity-hue-saturation (IHS) and the filter is applied to the intensity channel. If an RGB image is input, the
+/// threshold value must be in the range 0.0-1.0 (more likely less than 0.15), where a value of 1.0 would result in an ordinary mean filter
+/// (`MeanFilter`). NoData values in the input image are ignored during filtering.
+///
+/// # See Also
+/// `MeanFilter`, `BilateralFilter`, `EdgePreservingMeanFilter`, `GaussianFilter`, `MedianFilter`, `RgbToIhs`
pub struct EdgePreservingMeanFilter {
name: String,
description: String,
@@ -268,7 +285,7 @@ impl WhiteboxTool for EdgePreservingMeanFilter {
let mut dx = vec![0isize; num_pixels_in_filter];
let mut dy = vec![0isize; num_pixels_in_filter];
- // fill the filter d_x and d_y values and the distance-weights
+ // fill the filter d_x and d_y values
let midpoint: isize = (filter_size as f64 / 2f64).floor() as isize;
let mut a = 0;
for row in 0..filter_size {
diff --git a/src/tools/image_analysis/gamma_correction.rs b/src/tools/image_analysis/gamma_correction.rs
index d3cb365d8..78125cda1 100644
--- a/src/tools/image_analysis/gamma_correction.rs
+++ b/src/tools/image_analysis/gamma_correction.rs
@@ -1,12 +1,9 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: July 13, 2017
+Created: 13/07/2017
Last Modified: 13/10/2018
License: MIT
-
-NOTES: 1. The tool should be updated to take multiple file inputs.
- 2. Unlike the original Whitebox GAT tool that this is based on,
*/
use crate::raster::*;
@@ -21,6 +18,13 @@ use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
+/// This tool performs a gamma colour correction transform on an input image (`--input`), such that each
+/// input pixel value (zin) is mapped to the corresponding output value (zout) as:
+///
+/// > zout = zin`gamma`
+///
+/// The user must specify the value of the `gamma` parameter. The input image may be of either a greyscale or RGB colour
+/// composite data type.
pub struct GammaCorrection {
name: String,
description: String,
@@ -34,7 +38,7 @@ impl GammaCorrection {
// public constructor
let name = "GammaCorrection".to_string();
let toolbox = "Image Processing Tools/Image Enhancement".to_string();
- let description = "Performs a sigmoidal contrast stretch on input images.".to_string();
+ let description = "Performs a gamma correction on an input images.".to_string();
let mut parameters = vec![];
parameters.push(ToolParameter {
@@ -144,24 +148,25 @@ impl WhiteboxTool for GammaCorrection {
if vec.len() > 1 {
keyval = true;
}
- if vec[0].to_lowercase() == "-i" || vec[0].to_lowercase() == "--input" {
- if keyval {
- input_file = vec[1].to_string();
+ let flag_val = vec[0].to_lowercase().replace("--", "-");
+ if flag_val == "-i" || flag_val == "-input" {
+ input_file = if keyval {
+ vec[1].to_string()
} else {
- input_file = args[i + 1].to_string();
- }
- } else if vec[0].to_lowercase() == "-o" || vec[0].to_lowercase() == "--output" {
- if keyval {
- output_file = vec[1].to_string();
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-o" || flag_val == "-output" {
+ output_file = if keyval {
+ vec[1].to_string()
} else {
- output_file = args[i + 1].to_string();
- }
- } else if vec[0].to_lowercase() == "-gamma" || vec[0].to_lowercase() == "--gamma" {
- if keyval {
- gamma = vec[1].to_string().parse::().unwrap();
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-gamma" {
+ gamma = if keyval {
+ vec[1].to_string().parse::().unwrap()
} else {
- gamma = args[i + 1].to_string().parse::().unwrap();
- }
+ args[i + 1].to_string().parse::().unwrap()
+ };
}
}
diff --git a/src/tools/image_analysis/histogram_equalization.rs b/src/tools/image_analysis/histogram_equalization.rs
index eeeebd763..1579e02aa 100644
--- a/src/tools/image_analysis/histogram_equalization.rs
+++ b/src/tools/image_analysis/histogram_equalization.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: August 26, 2017
+Created: 26/08/2017
Last Modified: 13/10/2018
License: MIT
diff --git a/src/tools/image_analysis/histogram_matching.rs b/src/tools/image_analysis/histogram_matching.rs
index 3154dde45..86c855589 100644
--- a/src/tools/image_analysis/histogram_matching.rs
+++ b/src/tools/image_analysis/histogram_matching.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: September 14, 2017
+Created: 14/09/2017
Last Modified: 13/10/2018
License: MIT
*/
diff --git a/src/tools/image_analysis/histogram_matching_two_images.rs b/src/tools/image_analysis/histogram_matching_two_images.rs
index 715778e80..30689f892 100644
--- a/src/tools/image_analysis/histogram_matching_two_images.rs
+++ b/src/tools/image_analysis/histogram_matching_two_images.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: August 31, 2017
+Created: 31/08/2017
Last Modified: 13/10/2018
License: MIT
*/
diff --git a/src/tools/image_analysis/ihs_to_rgb.rs b/src/tools/image_analysis/ihs_to_rgb.rs
index 5b809f913..5b73fd511 100644
--- a/src/tools/image_analysis/ihs_to_rgb.rs
+++ b/src/tools/image_analysis/ihs_to_rgb.rs
@@ -1,8 +1,8 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: July 25, 2017
-Last Modified: 13/10/2018
+Created: 25/07/2017
+Last Modified: 05/02/2019
License: MIT
*/
@@ -11,13 +11,43 @@ use crate::tools::*;
use num_cpus;
use std::env;
use std::f64;
+use std::f64::consts::PI;
use std::io::{Error, ErrorKind};
use std::path;
use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
-/// Converts intensity, hue, and saturation (IHS) images into red, green, and blue (RGB) images.
+/// This tool transforms three intensity, hue, and saturation (IHS; sometimes HSI or HIS) raster images into three
+/// equivalent multispectral images corresponding with the red, green, and blue channels of an RGB composite. Intensity
+/// refers to the brightness of a color, hue is related to the dominant wavelength of light and is perceived as color,
+/// and saturation is the purity of the color (Koutsias et al., 2000). There are numerous algorithms for performing a
+/// red-green-blue (RGB) to IHS transformation. This tool uses the transformation described by Haydn (1982). Note that,
+/// based on this transformation, the input IHS values must follow the ranges:
+///
+/// > 0 < I < 1
+/// >
+/// > 0 < H < 2PI
+/// >
+/// > 0 < S < 1
+///
+/// The output red, green, and blue images will have values ranging from 0 to 255. The user must specify the names of the
+/// intensity, hue, and saturation images (`--intensity`, `--hue`, `--saturation`). These images will generally be created using
+/// the `RgbToIhs` tool. The user must also specify the names of the output red, green, and blue images (`--red`, `--green`,
+/// `--blue`). Image enhancements, such as contrast stretching, are often performed on the individual IHS components, which are
+/// then inverse transformed back in RGB components using this tool. The output RGB components can then be used to create an
+/// improved color composite image.
+///
+/// # References
+/// Haydn, R., Dalke, G.W. and Henkel, J. (1982) Application of the IHS color transform to the processing of multisensor
+/// data and image enhancement. Proc. of the Inter- national Symposium on Remote Sensing of Arid and Semiarid Lands,
+/// Cairo, 599-616.
+///
+/// Koutsias, N., Karteris, M., and Chuvico, E. (2000). The use of intensity-hue-saturation transformation of Landsat-5 Thematic
+/// Mapper data for burned land mapping. Photogrammetric Engineering and Remote Sensing, 66(7), 829-840.
+///
+/// # See Also
+/// `RgbToIhs`, `BalanceContrastEnhancement`, `DirectDecorrelationStretch`
pub struct IhsToRgb {
name: String,
description: String,
@@ -190,61 +220,44 @@ impl WhiteboxTool for IhsToRgb {
if vec.len() > 1 {
keyval = true;
}
- if vec[0].to_lowercase() == "-red" || vec[0].to_lowercase() == "--red" {
+ let flag_val = vec[0].to_lowercase().replace("--", "-");
+ if flag_val == "-r" || flag_val == "-red" {
if keyval {
red_file = vec[1].to_string();
} else {
red_file = args[i + 1].to_string();
}
- } else if vec[0].to_lowercase() == "-g"
- || vec[0].to_lowercase() == "-green"
- || vec[0].to_lowercase() == "--green"
- {
+ } else if flag_val == "-g" || flag_val == "-green" {
if keyval {
green_file = vec[1].to_string();
} else {
green_file = args[i + 1].to_string();
}
- } else if vec[0].to_lowercase() == "-b"
- || vec[0].to_lowercase() == "-blue"
- || vec[0].to_lowercase() == "--blue"
- {
+ } else if flag_val == "-b" || flag_val == "-blue" {
if keyval {
blue_file = vec[1].to_string();
} else {
blue_file = args[i + 1].to_string();
}
- } else if vec[0].to_lowercase() == "-i"
- || vec[0].to_lowercase() == "-intensity"
- || vec[0].to_lowercase() == "--intensity"
- {
+ } else if flag_val == "-i" || flag_val == "-intensity" {
if keyval {
intensity_file = vec[1].to_string();
} else {
intensity_file = args[i + 1].to_string();
}
- } else if vec[0].to_lowercase() == "-h"
- || vec[0].to_lowercase() == "-hue"
- || vec[0].to_lowercase() == "--hue"
- {
+ } else if flag_val == "-h" || flag_val == "-hue" {
if keyval {
hue_file = vec[1].to_string();
} else {
hue_file = args[i + 1].to_string();
}
- } else if vec[0].to_lowercase() == "-s"
- || vec[0].to_lowercase() == "-saturation"
- || vec[0].to_lowercase() == "--saturation"
- {
+ } else if flag_val == "-s" || flag_val == "-saturation" {
if keyval {
saturation_file = vec[1].to_string();
} else {
saturation_file = args[i + 1].to_string();
}
- } else if vec[0].to_lowercase() == "-o"
- || vec[0].to_lowercase() == "-composite"
- || vec[0].to_lowercase() == "--composite"
- {
+ } else if flag_val == "-o" || flag_val == "-composite" || flag_val == "-output" {
if keyval {
composite_file = vec[1].to_string();
} else {
@@ -265,14 +278,20 @@ impl WhiteboxTool for IhsToRgb {
let mut progress: usize;
let mut old_progress: usize = 1;
- if !red_file.contains(&sep) && !red_file.contains("/") {
- red_file = format!("{}{}", working_directory, red_file);
- }
- if !green_file.contains(&sep) && !green_file.contains("/") {
- green_file = format!("{}{}", working_directory, green_file);
- }
- if !blue_file.contains(&sep) && !blue_file.contains("/") {
- blue_file = format!("{}{}", working_directory, blue_file);
+ if !use_composite {
+ if !red_file.contains(&sep) && !red_file.contains("/") {
+ red_file = format!("{}{}", working_directory, red_file);
+ }
+ if !green_file.contains(&sep) && !green_file.contains("/") {
+ green_file = format!("{}{}", working_directory, green_file);
+ }
+ if !blue_file.contains(&sep) && !blue_file.contains("/") {
+ blue_file = format!("{}{}", working_directory, blue_file);
+ }
+ } else {
+ if !composite_file.contains(&sep) && !composite_file.contains("/") {
+ composite_file = format!("{}{}", working_directory, composite_file);
+ }
}
if !intensity_file.contains(&sep) && !intensity_file.contains("/") {
intensity_file = format!("{}{}", working_directory, intensity_file);
@@ -324,50 +343,37 @@ impl WhiteboxTool for IhsToRgb {
}
let num_procs = num_cpus::get() as isize;
- let (tx, rx) = mpsc::channel();
- for tid in 0..num_procs {
- let input_i = input_i.clone();
- let input_h = input_h.clone();
- let input_s = input_s.clone();
- let tx = tx.clone();
- thread::spawn(move || {
- let (mut r, mut g, mut b): (f64, f64, f64);
- let (mut i, mut h, mut s): (f64, f64, f64);
- for row in (0..rows).filter(|r| r % num_procs == tid) {
- let mut red_data = vec![nodata_i; columns as usize];
- let mut green_data = vec![nodata_i; columns as usize];
- let mut blue_data = vec![nodata_i; columns as usize];
- for col in 0..columns {
- i = input_i[(row, col)];
- h = input_h[(row, col)];
- s = input_s[(row, col)];
- if i != nodata_i && h != nodata_h && s != nodata_s {
- if h <= 1f64 {
- r = i * (1f64 + 2f64 * s - 3f64 * s * h) / 3f64;
- g = i * (1f64 - s + 3f64 * s * h) / 3f64;
- b = i * (1f64 - s) / 3f64;
- } else if h <= 2f64 {
- r = i * (1f64 - s) / 3f64;
- g = i * (1f64 + 2f64 * s - 3f64 * s * (h - 1f64)) / 3f64;
- b = i * (1f64 - s + 3f64 * s * (h - 1f64)) / 3f64;
- } else {
- // h <= 3
- r = i * (1f64 - s + 3f64 * s * (h - 2f64)) / 3f64;
- g = i * (1f64 - s) / 3f64;
- b = i * (1f64 + 2f64 * s - 3f64 * s * (h - 2f64)) / 3f64;
+ if !use_composite {
+ let (tx, rx) = mpsc::channel();
+ for tid in 0..num_procs {
+ let input_i = input_i.clone();
+ let input_h = input_h.clone();
+ let input_s = input_s.clone();
+ let tx = tx.clone();
+ thread::spawn(move || {
+ // let (mut r, mut g, mut b): (f64, f64, f64);
+ let (mut i, mut h, mut s): (f64, f64, f64);
+ for row in (0..rows).filter(|r| r % num_procs == tid) {
+ let mut red_data = vec![nodata_i; columns as usize];
+ let mut green_data = vec![nodata_i; columns as usize];
+ let mut blue_data = vec![nodata_i; columns as usize];
+ for col in 0..columns {
+ i = input_i[(row, col)];
+ h = input_h[(row, col)];
+ s = input_s[(row, col)];
+ if i != nodata_i && h != nodata_h && s != nodata_s {
+ let (r, g, b) = hsi2rgb(h, s, i);
+
+ red_data[col as usize] = r as f64;
+ green_data[col as usize] = g as f64;
+ blue_data[col as usize] = b as f64;
}
-
- red_data[col as usize] = r;
- green_data[col as usize] = g;
- blue_data[col as usize] = b;
}
+ tx.send((row, red_data, green_data, blue_data)).unwrap();
}
- tx.send((row, red_data, green_data, blue_data)).unwrap();
- }
- });
- }
+ });
+ }
- if !use_composite {
let mut output_r = Raster::initialize_using_file(&red_file, &input_i);
output_r.configs.photometric_interp = PhotometricInterpretation::Continuous;
output_r.configs.data_type = DataType::F32;
@@ -466,27 +472,48 @@ impl WhiteboxTool for IhsToRgb {
Err(e) => return Err(e),
};
- println!(
- "{}",
- &format!("Elapsed Time (excluding I/O): {}", elapsed_time).replace("PT", "")
- );
+ if verbose {
+ println!(
+ "{}",
+ &format!("Elapsed Time (excluding I/O): {}", elapsed_time).replace("PT", "")
+ );
+ }
} else {
+ if verbose {
+ println!("Creating a colour composite output...");
+ }
+ let (tx, rx) = mpsc::channel();
+ for tid in 0..num_procs {
+ let input_i = input_i.clone();
+ let input_h = input_h.clone();
+ let input_s = input_s.clone();
+ let tx = tx.clone();
+ thread::spawn(move || {
+ let (mut i, mut h, mut s): (f64, f64, f64);
+ let mut value: f64;
+ for row in (0..rows).filter(|r| r % num_procs == tid) {
+ let mut data = vec![0f64; columns as usize];
+ for col in 0..columns {
+ i = input_i[(row, col)];
+ h = input_h[(row, col)];
+ s = input_s[(row, col)];
+ if i != nodata_i && h != nodata_h && s != nodata_s {
+ value = hsi2value(h, s, i);
+ data[col as usize] = value;
+ }
+ }
+ tx.send((row, data)).unwrap();
+ }
+ });
+ }
+
let mut output = Raster::initialize_using_file(&composite_file, &input_i);
output.configs.photometric_interp = PhotometricInterpretation::RGB;
- output.configs.data_type = DataType::RGB24;
- let out_nodata = 0f64;
- let (mut r, mut g, mut b): (u32, u32, u32);
- let alpha_mask = (255 << 24) as u32;
+ output.configs.nodata = 0f64;
+ output.configs.data_type = DataType::RGBA32;
for row in 0..rows {
let data = rx.recv().unwrap();
- let mut out_data = vec![out_nodata; columns as usize];
- for col in 0..columns {
- r = data.1[col as usize] as u32;
- g = data.2[col as usize] as u32;
- b = data.3[col as usize] as u32;
- out_data[col as usize] = (alpha_mask | (b << 16) | (g << 8) | r) as f64;
- }
- output.set_row_data(data.0, out_data);
+ output.set_row_data(data.0, data.1);
if verbose {
progress = (100.0_f64 * row as f64 / (rows - 1) as f64) as usize;
if progress != old_progress {
@@ -508,7 +535,7 @@ impl WhiteboxTool for IhsToRgb {
output.add_metadata_entry(format!("Elapsed Time (excluding I/O): {}", elapsed_time));
if verbose {
- println!("Saving red data...")
+ println!("Saving data...")
};
let _ = match output.write() {
Ok(_) => {
@@ -529,3 +556,117 @@ impl WhiteboxTool for IhsToRgb {
Ok(())
}
}
+
+// #[inline]
+// fn value2hsi(value: f64) -> (f64, f64, f64) {
+// let r = (value as u32 & 0xFF) as f64 / 255f64;
+// let g = ((value as u32 >> 8) & 0xFF) as f64 / 255f64;
+// let b = ((value as u32 >> 16) & 0xFF) as f64 / 255f64;
+
+// let i = (r + g + b) / 3f64;
+
+// let rn = r / (r + g + b);
+// let gn = g / (r + g + b);
+// let bn = b / (r + g + b);
+
+// let mut h = if rn != gn || rn != bn {
+// ((0.5 * ((rn - gn) + (rn - bn))) / ((rn - gn) * (rn - gn) + (rn - bn) * (gn - bn)).sqrt())
+// .acos()
+// } else {
+// 0f64
+// };
+// if b > g {
+// h = 2f64 * PI - h;
+// }
+
+// let s = 1f64 - 3f64 * rn.min(gn).min(bn);
+
+// (h, s, i)
+// }
+
+#[inline]
+fn hsi2value(h: f64, s: f64, i: f64) -> f64 {
+ let mut r: u32;
+ let mut g: u32;
+ let mut b: u32;
+
+ let x = i * (1f64 - s);
+
+ if h < 2f64 * PI / 3f64 {
+ let y = i * (1f64 + (s * h.cos()) / ((PI / 3f64 - h).cos()));
+ let z = 3f64 * i - (x + y);
+ r = (y * 255f64).round() as u32;
+ g = (z * 255f64).round() as u32;
+ b = (x * 255f64).round() as u32;
+ } else if h < 4f64 * PI / 3f64 {
+ let h = h - 2f64 * PI / 3f64;
+ let y = i * (1f64 + (s * h.cos()) / ((PI / 3f64 - h).cos()));
+ let z = 3f64 * i - (x + y);
+ r = (x * 255f64).round() as u32;
+ g = (y * 255f64).round() as u32;
+ b = (z * 255f64).round() as u32;
+ } else {
+ let h = h - 4f64 * PI / 3f64;
+ let y = i * (1f64 + (s * h.cos()) / ((PI / 3f64 - h).cos()));
+ let z = 3f64 * i - (x + y);
+ r = (z * 255f64).round() as u32;
+ g = (x * 255f64).round() as u32;
+ b = (y * 255f64).round() as u32;
+ }
+
+ if r > 255u32 {
+ r = 255u32;
+ }
+ if g > 255u32 {
+ g = 255u32;
+ }
+ if b > 255u32 {
+ b = 255u32;
+ }
+
+ ((255 << 24) | (b << 16) | (g << 8) | r) as f64
+}
+
+
+#[inline]
+fn hsi2rgb(h: f64, s: f64, i: f64) -> (u32, u32, u32) {
+ let mut r: u32;
+ let mut g: u32;
+ let mut b: u32;
+
+ let x = i * (1f64 - s);
+
+ if h < 2f64 * PI / 3f64 {
+ let y = i * (1f64 + (s * h.cos()) / ((PI / 3f64 - h).cos()));
+ let z = 3f64 * i - (x + y);
+ r = (y * 255f64).round() as u32;
+ g = (z * 255f64).round() as u32;
+ b = (x * 255f64).round() as u32;
+ } else if h < 4f64 * PI / 3f64 {
+ let h = h - 2f64 * PI / 3f64;
+ let y = i * (1f64 + (s * h.cos()) / ((PI / 3f64 - h).cos()));
+ let z = 3f64 * i - (x + y);
+ r = (x * 255f64).round() as u32;
+ g = (y * 255f64).round() as u32;
+ b = (z * 255f64).round() as u32;
+ } else {
+ let h = h - 4f64 * PI / 3f64;
+ let y = i * (1f64 + (s * h.cos()) / ((PI / 3f64 - h).cos()));
+ let z = 3f64 * i - (x + y);
+ r = (z * 255f64).round() as u32;
+ g = (x * 255f64).round() as u32;
+ b = (y * 255f64).round() as u32;
+ }
+
+ if r > 255u32 {
+ r = 255u32;
+ }
+ if g > 255u32 {
+ g = 255u32;
+ }
+ if b > 255u32 {
+ b = 255u32;
+ }
+
+ (r, g, b)
+}
\ No newline at end of file
diff --git a/src/tools/image_analysis/image_stack_profile.rs b/src/tools/image_analysis/image_stack_profile.rs
index 9cf007a79..c906011d9 100644
--- a/src/tools/image_analysis/image_stack_profile.rs
+++ b/src/tools/image_analysis/image_stack_profile.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: March 15, 2018
+Created: 15/03/2018
Last Modified: 13/10/2018
License: MIT
*/
@@ -20,6 +20,20 @@ use std::io::{Error, ErrorKind};
use std::path;
use std::process::Command;
+/// This tool can be used to plot an image stack profile (i.e. a signature) for a set of points (`--points`) and
+/// a multispectral image stack (`--inputs`). The tool outputs an interactive SVG line graph embedded in an
+/// HTML document (`--output`). If the input points vector contains multiple points, each input point will
+/// be associated with a single line in the output plot. The order of vertices in each signature line is
+/// determined by the order of images specified in the `--inputs` parameter. At least two input images are
+/// required to run this operation. Note that this tool does not require multispectral images as
+/// inputs; other types of data may also be used as the image stack. Also note that the input images should be
+/// single-band, continuous greytone rasters. RGB colour images are not good candidates for this tool.
+///
+/// If you require the raster values to be saved in the vector points file's attribute table, or if you need
+/// the raster values to be output as text, you may use the `ExtractRasterValuesAtPoints` tool instead.
+///
+/// # See Also
+/// `ExtractRasterValuesAtPoints`
pub struct ImageStackProfile {
name: String,
description: String,
diff --git a/src/tools/image_analysis/integral_image.rs b/src/tools/image_analysis/integral_image.rs
index 22bb07c6c..c4fabbdee 100644
--- a/src/tools/image_analysis/integral_image.rs
+++ b/src/tools/image_analysis/integral_image.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: June 26, 2017
+Created: 26/06/2017
Last Modified: 13/10/2018
License: MIT
*/
@@ -13,6 +13,17 @@ use std::f64;
use std::io::{Error, ErrorKind};
use std::path;
+/// This tool transforms an input raster image into an integral image, or summed area table. Integral images are
+/// the two-dimensional equivalent to a cumulative distribution function. Each pixel contains the sum of all
+/// pixels contained within the enclosing rectangle above and to the left of a pixel. Images with a very large
+/// number of grid cells will likely experience numerical overflow errors when converted to an integral image.
+/// Integral images are used in a wide variety of computer vision and digital image processing applications,
+/// including texture mapping. They allow for the efficient calculation of very large filters and are the
+/// basis of several of *WhiteboxTools*'s image filters.
+///
+/// # Reference
+/// Crow, F. C. (1984, January). Summed-area tables for texture mapping. In ACM SIGGRAPH computer graphics
+/// (Vol. 18, No. 3, pp. 207-212). ACM.
pub struct IntegralImage {
name: String,
description: String,
diff --git a/src/tools/image_analysis/line_thin.rs b/src/tools/image_analysis/line_thin.rs
index 80b1fc8cf..29a668aac 100644
--- a/src/tools/image_analysis/line_thin.rs
+++ b/src/tools/image_analysis/line_thin.rs
@@ -30,7 +30,7 @@ use std::thread;
/// the output raster must be read and written to during the same loop.
///
/// # See Also
-/// `RemoveSpurs`
+/// `RemoveSpurs`, `ThickenRasterLine`
pub struct LineThinning {
name: String,
description: String,
diff --git a/src/tools/image_analysis/mean_filter.rs b/src/tools/image_analysis/mean_filter.rs
index 8b1c59fb2..ae0dc34ed 100644
--- a/src/tools/image_analysis/mean_filter.rs
+++ b/src/tools/image_analysis/mean_filter.rs
@@ -1,8 +1,8 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: June 25, 2017
-Last Modified: 13/10/2018
+Created: 25/06/2017
+Last Modified: 06/02/2019
License: MIT
*/
@@ -28,7 +28,7 @@ use crate::tools::*;
/// useful for reducing the noise in an image. This tool utilizes an integral image approach (Crow, 1984) to ensure highly
/// efficient filtering that is invariant to filter size. The algorithm operates by calculating the average value
/// in a moving window centred on each grid cell. Neighbourhood size, or filter size, is specified in the x and y
-/// dimensions using the `--filterx` and `--filtery`flags. These dimensions should be odd, positive integer values,
+/// dimensions using the `--filterx` and `--filtery` flags. These dimensions should be odd, positive integer values,
/// e.g. 3, 5, 7, 9... If the kernel filter size is the same in the x and y dimensions, the silent `--filter` flag
/// may be used instead (command-line interface only).
///
@@ -37,11 +37,15 @@ use crate::tools::*;
/// filters such as the edge-preserving smoothing filters including the `BilateralFilter`, `MedianFilter`, `OlympicFilter`,
/// `EdgePreservingMeanFilter` and even `GaussianFilter`.
///
-/// `GaussianFilter` works with both greyscale and red-green-blue (RGB) images. RGB images are
+/// This tool works with both greyscale and red-green-blue (RGB) images. RGB images are
/// decomposed into intensity-hue-saturation (IHS) and the filter is applied to the intensity
/// channel. NoData values in the input image are ignored during filtering. NoData values are assigned to all sites beyond
/// the raster.
///
+/// # Reference
+/// Crow, F. C. (1984, January). Summed-area tables for texture mapping. In ACM SIGGRAPH computer graphics (Vol. 18, No.
+/// 3, pp. 207-212). ACM.
+///
/// # See Also
/// `BilateralFilter`, `EdgePreservingMeanFilter`, `GaussianFilter`, `MedianFilter`, `RgbToIhs`
pub struct MeanFilter {
@@ -172,37 +176,38 @@ impl WhiteboxTool for MeanFilter {
if vec.len() > 1 {
keyval = true;
}
- if vec[0].to_lowercase() == "-i" || vec[0].to_lowercase() == "--input" {
- if keyval {
- input_file = vec[1].to_string();
+ let flag_val = vec[0].to_lowercase().replace("--", "-");
+ if flag_val == "-i" || flag_val == "-input" {
+ input_file = if keyval {
+ vec[1].to_string()
} else {
- input_file = args[i + 1].to_string();
- }
- } else if vec[0].to_lowercase() == "-o" || vec[0].to_lowercase() == "--output" {
- if keyval {
- output_file = vec[1].to_string();
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-o" || flag_val == "-output" {
+ output_file = if keyval {
+ vec[1].to_string()
} else {
- output_file = args[i + 1].to_string();
- }
- } else if vec[0].to_lowercase() == "-filter" || vec[0].to_lowercase() == "--filter" {
- if keyval {
- filter_size_x = vec[1].to_string().parse::().unwrap() as usize;
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-filter" {
+ filter_size_x = if keyval {
+ vec[1].to_string().parse::().unwrap() as usize
} else {
- filter_size_x = args[i + 1].to_string().parse::().unwrap() as usize;
- }
+ args[i + 1].to_string().parse::().unwrap() as usize
+ };
filter_size_y = filter_size_x;
- } else if vec[0].to_lowercase() == "-filterx" || vec[0].to_lowercase() == "--filterx" {
- if keyval {
- filter_size_x = vec[1].to_string().parse::().unwrap() as usize;
+ } else if flag_val == "-filterx" {
+ filter_size_x = if keyval {
+ vec[1].to_string().parse::().unwrap() as usize
} else {
- filter_size_x = args[i + 1].to_string().parse::().unwrap() as usize;
- }
- } else if vec[0].to_lowercase() == "-filtery" || vec[0].to_lowercase() == "--filtery" {
- if keyval {
- filter_size_y = vec[1].to_string().parse::().unwrap() as usize;
+ args[i + 1].to_string().parse::().unwrap() as usize
+ };
+ } else if flag_val == "-filtery" {
+ filter_size_y = if keyval {
+ vec[1].to_string().parse::().unwrap() as usize
} else {
- filter_size_y = args[i + 1].to_string().parse::().unwrap() as usize;
- }
+ args[i + 1].to_string().parse::().unwrap() as usize
+ };
}
}
diff --git a/src/tools/image_analysis/min_max_contrast_stretch.rs b/src/tools/image_analysis/min_max_contrast_stretch.rs
index e8973d84b..8d409ef50 100644
--- a/src/tools/image_analysis/min_max_contrast_stretch.rs
+++ b/src/tools/image_analysis/min_max_contrast_stretch.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: July 13, 2017
+Created: 13/07/2017
Last Modified: 13/10/2018
License: MIT
diff --git a/src/tools/image_analysis/modified_k_means_clustering.rs b/src/tools/image_analysis/modified_k_means_clustering.rs
index 5ccf92137..84f9288cd 100644
--- a/src/tools/image_analysis/modified_k_means_clustering.rs
+++ b/src/tools/image_analysis/modified_k_means_clustering.rs
@@ -1,8 +1,8 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: Dec. 30, 2017
-Last Modified: Dec. 30, 2017
+Created: 30/12/2017
+Last Modified: 30/12/2017
License: MIT
*/
diff --git a/src/tools/image_analysis/mosaic.rs b/src/tools/image_analysis/mosaic.rs
index 23006274b..13b0b7e26 100644
--- a/src/tools/image_analysis/mosaic.rs
+++ b/src/tools/image_analysis/mosaic.rs
@@ -1,8 +1,8 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: January 2 2018
-Last Modified: January 2, 2018
+Created: 02/01/2018
+Last Modified: 02/01/2018
License: MIT
*/
diff --git a/src/tools/image_analysis/percentage_contrast_stretch.rs b/src/tools/image_analysis/percentage_contrast_stretch.rs
index b823dbdd8..2a5e81c08 100644
--- a/src/tools/image_analysis/percentage_contrast_stretch.rs
+++ b/src/tools/image_analysis/percentage_contrast_stretch.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: July 13, 2017
+Created: 13/07/2017
Last Modified: 13/10/2018
License: MIT
@@ -83,7 +83,7 @@ impl PercentageContrastStretch {
description: "Optional amount to clip the distribution tails by, in percent."
.to_owned(),
parameter_type: ParameterType::Float,
- default_value: Some("0.0".to_owned()),
+ default_value: Some("1.0".to_owned()),
optional: true,
});
diff --git a/src/tools/image_analysis/resample.rs b/src/tools/image_analysis/resample.rs
index 0dd594755..64109f1bf 100644
--- a/src/tools/image_analysis/resample.rs
+++ b/src/tools/image_analysis/resample.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: January 1 2018
+Created: 01/01/2018
Last Modified: 13/10/2018
License: MIT
*/
diff --git a/src/tools/image_analysis/rgb_to_ihs.rs b/src/tools/image_analysis/rgb_to_ihs.rs
index 21e1ca678..d527146c2 100644
--- a/src/tools/image_analysis/rgb_to_ihs.rs
+++ b/src/tools/image_analysis/rgb_to_ihs.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: July 25, 2017
+Created: 25/07/2017
Last Modified: 13/10/2018
License: MIT
*/
@@ -11,13 +11,43 @@ use crate::tools::*;
use num_cpus;
use std::env;
use std::f64;
+use std::f64::consts::PI;
use std::io::{Error, ErrorKind};
use std::path;
use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
-/// Converts red, green, and blue (RGB) images into intensity, hue, and saturation (IHS) images.
+/// This tool transforms three raster images of multispectral data (red, green, and blue channels) into their equivalent
+/// intensity, hue, and saturation (IHS; sometimes HSI or HIS) images. Intensity refers to the brightness of a color, hue
+/// is related to the dominant wavelength of light and is perceived as color, and saturation is the purity of the color
+/// (Koutsias et al., 2000). There are numerous algorithms for performing a red-green-blue (RGB) to IHS transformation.
+/// This tool uses the transformation described by Haydn (1982). Note that, based on this transformation, the output
+/// IHS values follow the ranges:
+///
+/// > 0 < I < 1
+/// >
+/// > 0 < H < 2PI
+/// >
+/// > 0 < S < 1
+///
+/// The user must specify the names of the red, green, and blue images (`--red`, `--green`, `--blue`). Importantly, these
+/// images need not necessarily correspond with the specific regions of the electromagnetic spectrum that are red, green,
+/// and blue. Rather, the input images are three multispectral images that could be used to create a RGB color composite.
+/// The user must also specify the names of the output intensity, hue, and saturation images (`--intensity`, `--hue`,
+/// `--saturation`). Image enhancements, such as contrast stretching, are often performed on the IHS components, which are
+/// then inverse transformed back in RGB components to then create an improved color composite image.
+///
+/// # References
+/// Haydn, R., Dalke, G.W. and Henkel, J. (1982) Application of the IHS color transform to the processing of multisensor
+/// data and image enhancement. Proc. of the Inter- national Symposium on Remote Sensing of Arid and Semiarid Lands,
+/// Cairo, 599-616.
+///
+/// Koutsias, N., Karteris, M., and Chuvico, E. (2000). The use of intensity-hue-saturation transformation of Landsat-5 Thematic
+/// Mapper data for burned land mapping. Photogrammetric Engineering and Remote Sensing, 66(7), 829-840.
+///
+/// # See Also
+/// `IhsToRgb`, `BalanceContrastEnhancement`, `DirectDecorrelationStretch`
pub struct RgbToIhs {
name: String,
description: String,
@@ -311,9 +341,9 @@ impl WhiteboxTool for RgbToIhs {
let red_max = input_r.configs.display_max;
let green_max = input_g.configs.display_max;
let blue_max = input_b.configs.display_max;
- let overall_min = red_min.min(green_min.min(blue_min));
- let overall_max = red_max.max(green_max.max(blue_max));
- let range = overall_max - overall_min;
+ // let overall_min = red_min.min(green_min.min(blue_min));
+ // let overall_max = red_max.max(green_max.max(blue_max));
+ // let range = overall_max - overall_min;
let start = Instant::now();
@@ -338,68 +368,45 @@ impl WhiteboxTool for RgbToIhs {
let input_b = input_b.clone();
let tx = tx.clone();
thread::spawn(move || {
- let (mut r, mut g, mut b): (f64, f64, f64);
- let (mut i, mut h, mut s, mut m): (f64, f64, f64, f64);
+ // let (mut r, mut g, mut b): (u32, u32, u32);
+ let (mut red, mut green, mut blue): (f64, f64, f64);
+ // let (mut i, mut h, mut s, mut m): (f64, f64, f64, f64);
+ // let mut value: f64;
+ let red_range = red_max - red_min;
+ let green_range = green_max - green_min;
+ let blue_range = blue_max - blue_min;
for row in (0..rows).filter(|r| r % num_procs == tid) {
let mut intensity_data = vec![nodata_r; columns as usize];
let mut hue_data = vec![nodata_r; columns as usize];
let mut saturation_data = vec![nodata_r; columns as usize];
for col in 0..columns {
- r = input_r[(row, col)];
- g = input_g[(row, col)];
- b = input_b[(row, col)];
- if r != nodata_r && g != nodata_g && b != nodata_b {
- r = (r - overall_min) / range;
- if r < 0f64 {
- r = 0f64;
- }
- if r > 1f64 {
- r = 1f64;
- }
-
- g = (g - overall_min) / range;
- if g < 0f64 {
- g = 0f64;
- }
- if g > 1f64 {
- g = 1f64;
- }
-
- b = (b - overall_min) / range;
- if b < 0f64 {
- b = 0f64;
- }
- if b > 1f64 {
- b = 1f64;
- }
-
- m = r.min(g.min(b));
-
- i = r + g + b;
+ red = input_r[(row, col)];
+ green = input_g[(row, col)];
+ blue = input_b[(row, col)];
+ if red != nodata_r && green != nodata_g && blue != nodata_b {
+ // r = ((red - red_min) / (red_max - red_min) * 255f64) as u32;
+ // if r > 255u32 {
+ // r = 255u32;
+ // }
+
+ // g = ((green - green_min) / (green_max - green_min) * 255f64) as u32;
+ // if g > 255u32 {
+ // g = 255u32;
+ // }
+
+ // b = ((blue - blue_min) / (blue_max - blue_min) * 255f64) as u32;
+ // if b > 255u32 {
+ // b = 255u32;
+ // }
+
+ red = (red - red_min) / red_range;
+ green = (green - green_min) / green_range;
+ blue = (blue - blue_min) / blue_range;
+ let (h, s, i) = rgb2hsi(red, green, blue);
- if i == 3f64 {
- h = 0f64;
- } else if m == b {
- h = (g - b) / (i - 3f64 * b);
- } else if m == r {
- h = (b - r) / (i - 3f64 * r) + 1f64;
- } else {
- // m == g
- h = (r - g) / (i - 3f64 * g) + 2f64;
- }
-
- if h <= 1f64 {
- s = (i - 3f64 * b) / i;
- } else if h <= 2f64 {
- s = (i - 3f64 * r) / i;
- } else {
- // H <= 3
- s = (i - 3f64 * g) / i;
- }
-
- intensity_data[col as usize] = i;
hue_data[col as usize] = h;
saturation_data[col as usize] = s;
+ intensity_data[col as usize] = i;
}
}
tx.send((row, intensity_data, hue_data, saturation_data))
@@ -495,10 +502,12 @@ impl WhiteboxTool for RgbToIhs {
Err(e) => return Err(e),
};
- println!(
- "{}",
- &format!("Elapsed Time (excluding I/O): {}", elapsed_time).replace("PT", "")
- );
+ if verbose {
+ println!(
+ "{}",
+ &format!("Elapsed Time (excluding I/O): {}", elapsed_time).replace("PT", "")
+ );
+ }
} else {
if verbose {
println!("Reading image data...")
@@ -574,15 +583,16 @@ impl WhiteboxTool for RgbToIhs {
}
}
- let range = overall_max - overall_min;
+ // let range = overall_max - overall_min;
let (tx, rx) = mpsc::channel();
for tid in 0..num_procs {
let input = input.clone();
let tx = tx.clone();
thread::spawn(move || {
- let (mut r, mut g, mut b): (f64, f64, f64);
- let (mut i, mut h, mut s, mut m): (f64, f64, f64, f64);
+ // let (mut r, mut g, mut b): (f64, f64, f64);
+ // let (mut i, mut h, mut s, mut m): (f64, f64, f64, f64);
+ // let (mut i, mut h, mut s): (f64, f64, f64);
let mut z: f64;
for row in (0..rows).filter(|r| r % num_procs == tid) {
let mut intensity_data = vec![nodata; columns as usize];
@@ -591,57 +601,59 @@ impl WhiteboxTool for RgbToIhs {
for col in 0..columns {
z = input[(row, col)];
if z != nodata {
- r = (z as u32 & 0xFF) as f64;
- g = ((z as u32 >> 8) & 0xFF) as f64;
- b = ((z as u32 >> 16) & 0xFF) as f64;
-
- r = (r - overall_min) / range;
- if r < 0f64 {
- r = 0f64;
- }
- if r > 1f64 {
- r = 1f64;
- }
-
- g = (g - overall_min) / range;
- if g < 0f64 {
- g = 0f64;
- }
- if g > 1f64 {
- g = 1f64;
- }
-
- b = (b - overall_min) / range;
- if b < 0f64 {
- b = 0f64;
- }
- if b > 1f64 {
- b = 1f64;
- }
-
- m = r.min(g.min(b));
-
- i = r + g + b;
-
- if i == 3f64 {
- h = 0f64;
- } else if m == b {
- h = (g - b) / (i - 3f64 * b);
- } else if m == r {
- h = (b - r) / (i - 3f64 * r) + 1f64;
- } else {
- // m == g
- h = (r - g) / (i - 3f64 * g) + 2f64;
- }
-
- if h <= 1f64 {
- s = (i - 3f64 * b) / i;
- } else if h <= 2f64 {
- s = (i - 3f64 * r) / i;
- } else {
- // H <= 3
- s = (i - 3f64 * g) / i;
- }
+ // r = (z as u32 & 0xFF) as f64;
+ // g = ((z as u32 >> 8) & 0xFF) as f64;
+ // b = ((z as u32 >> 16) & 0xFF) as f64;
+
+ // r = (r - overall_min) / range;
+ // if r < 0f64 {
+ // r = 0f64;
+ // }
+ // if r > 1f64 {
+ // r = 1f64;
+ // }
+
+ // g = (g - overall_min) / range;
+ // if g < 0f64 {
+ // g = 0f64;
+ // }
+ // if g > 1f64 {
+ // g = 1f64;
+ // }
+
+ // b = (b - overall_min) / range;
+ // if b < 0f64 {
+ // b = 0f64;
+ // }
+ // if b > 1f64 {
+ // b = 1f64;
+ // }
+
+ // m = r.min(g.min(b));
+
+ // i = r + g + b;
+
+ // if i == 3f64 {
+ // h = 0f64;
+ // } else if m == b {
+ // h = (g - b) / (i - 3f64 * b);
+ // } else if m == r {
+ // h = (b - r) / (i - 3f64 * r) + 1f64;
+ // } else {
+ // // m == g
+ // h = (r - g) / (i - 3f64 * g) + 2f64;
+ // }
+
+ // if h <= 1f64 {
+ // s = (i - 3f64 * b) / i;
+ // } else if h <= 2f64 {
+ // s = (i - 3f64 * r) / i;
+ // } else {
+ // // H <= 3
+ // s = (i - 3f64 * g) / i;
+ // }
+
+ let (h, s, i) = value2hsi(z);
intensity_data[col as usize] = i;
hue_data[col as usize] = h;
@@ -755,3 +767,96 @@ impl WhiteboxTool for RgbToIhs {
Ok(())
}
}
+
+#[inline]
+fn value2hsi(value: f64) -> (f64, f64, f64) {
+ let r = (value as u32 & 0xFF) as f64 / 255f64;
+ let g = ((value as u32 >> 8) & 0xFF) as f64 / 255f64;
+ let b = ((value as u32 >> 16) & 0xFF) as f64 / 255f64;
+
+ let i = (r + g + b) / 3f64;
+
+ let rn = r / (r + g + b);
+ let gn = g / (r + g + b);
+ let bn = b / (r + g + b);
+
+ let mut h = if rn != gn || rn != bn {
+ ((0.5 * ((rn - gn) + (rn - bn))) / ((rn - gn) * (rn - gn) + (rn - bn) * (gn - bn)).sqrt())
+ .acos()
+ } else {
+ 0f64
+ };
+ if b > g {
+ h = 2f64 * PI - h;
+ }
+
+ let s = 1f64 - 3f64 * rn.min(gn).min(bn);
+
+ (h, s, i)
+}
+
+/// RGB values should be 0-1
+fn rgb2hsi(r: f64, g: f64, b: f64) -> (f64, f64, f64) {
+ let i = (r + g + b) / 3f64;
+
+ let rn = r / (r + g + b);
+ let gn = g / (r + g + b);
+ let bn = b / (r + g + b);
+
+ let mut h = if rn != gn || rn != bn {
+ ((0.5 * ((rn - gn) + (rn - bn))) / ((rn - gn) * (rn - gn) + (rn - bn) * (gn - bn)).sqrt())
+ .acos()
+ } else {
+ 0f64
+ };
+ if b > g {
+ h = 2f64 * PI - h;
+ }
+
+ let s = 1f64 - 3f64 * rn.min(gn).min(bn);
+
+ (h, s, i)
+}
+
+// #[inline]
+// fn hsi2value(h: f64, s: f64, i: f64) -> f64 {
+// let mut r: u32;
+// let mut g: u32;
+// let mut b: u32;
+
+// let x = i * (1f64 - s);
+
+// if h < 2f64 * PI / 3f64 {
+// let y = i * (1f64 + (s * h.cos()) / ((PI / 3f64 - h).cos()));
+// let z = 3f64 * i - (x + y);
+// r = (y * 255f64).round() as u32;
+// g = (z * 255f64).round() as u32;
+// b = (x * 255f64).round() as u32;
+// } else if h < 4f64 * PI / 3f64 {
+// let h = h - 2f64 * PI / 3f64;
+// let y = i * (1f64 + (s * h.cos()) / ((PI / 3f64 - h).cos()));
+// let z = 3f64 * i - (x + y);
+// r = (x * 255f64).round() as u32;
+// g = (y * 255f64).round() as u32;
+// b = (z * 255f64).round() as u32;
+// } else {
+// let h = h - 4f64 * PI / 3f64;
+// let y = i * (1f64 + (s * h.cos()) / ((PI / 3f64 - h).cos()));
+// let z = 3f64 * i - (x + y);
+// r = (z * 255f64).round() as u32;
+// g = (x * 255f64).round() as u32;
+// b = (y * 255f64).round() as u32;
+// }
+
+// if r > 255u32 {
+// r = 255u32;
+// }
+// if g > 255u32 {
+// g = 255u32;
+// }
+// if b > 255u32 {
+// b = 255u32;
+// }
+
+// ((255 << 24) | (b << 16) | (g << 8) | r) as f64
+// }
diff --git a/src/tools/image_analysis/sigmoidal_contrast_stretch.rs b/src/tools/image_analysis/sigmoidal_contrast_stretch.rs
index e590b4e16..7c8c6731c 100644
--- a/src/tools/image_analysis/sigmoidal_contrast_stretch.rs
+++ b/src/tools/image_analysis/sigmoidal_contrast_stretch.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: July 13, 2017
+Created: 13/07/2017
Last Modified: 13/10/2018
License: MIT
@@ -22,6 +22,28 @@ use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
+/// This tool performs a sigmoidal stretch on a raster image. This is a transformation where the input image value for a
+/// grid cell (zin) is transformed to an output value zout such that:
+///
+/// > zout = (1.0 / (1.0 + exp(*gain*(*cutoff* - z))) - *a* ) / *b* x *num_tones*
+///
+/// where,
+///
+/// > z = (zin - *MIN*) / *RANGE*,
+///
+/// > *a* = 1.0 / (1.0 + exp(*gain* x *cutoff*)),
+///
+/// > *b* = 1.0 / (1.0 + exp(*gain* x (*cutoff* - 1.0))) - 1.0 / (1.0 + exp(*gain* x *cutoff*)),
+///
+/// *MIN* and *RANGE* are the minimum value and data range in the input image respectively and *gain* and *cutoff* are
+/// user specified parameters (`--gain`, `--cutoff`).
+///
+/// Like all of *WhiteboxTools*'s contrast enhancement tools, this operation will work on either greyscale or RGB input
+/// images.
+///
+/// # See Also
+/// `GaussianContrastStretch`, `HistogramEqualization`, `MinMaxContrastStretch`, `PercentageContrastStretch`,
+/// `StandardDeviationContrastStretch`
pub struct SigmoidalContrastStretch {
name: String,
description: String,
diff --git a/src/tools/image_analysis/sobel_filter.rs b/src/tools/image_analysis/sobel_filter.rs
index 053ef38a9..6737ebd1d 100644
--- a/src/tools/image_analysis/sobel_filter.rs
+++ b/src/tools/image_analysis/sobel_filter.rs
@@ -341,7 +341,9 @@ impl WhiteboxTool for SobelFilter {
}
if clip_amount > 0.0 {
- println!("Clipping output...");
+ if verbose {
+ println!("Clipping output...");
+ }
output.clip_min_and_max_by_percent(clip_amount);
}
diff --git a/src/tools/image_analysis/split_colour_composite.rs b/src/tools/image_analysis/split_colour_composite.rs
index 0f041509f..18ee90144 100644
--- a/src/tools/image_analysis/split_colour_composite.rs
+++ b/src/tools/image_analysis/split_colour_composite.rs
@@ -1,8 +1,8 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: July 15, 2017
-Last Modified: 13/10/2018
+Created: 15/07/2017
+Last Modified: 06/02/2019
License: MIT
*/
@@ -12,11 +12,18 @@ use num_cpus;
use std::env;
use std::io::{Error, ErrorKind};
use std::path;
+use std::path::Path;
use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
-/// This tool splits an RGB colour composite image into seperate multispectral images.
+/// This tool can be used to split a red-green-blue (RGB) colour-composite image into three separate bands of
+/// multi-spectral imagery. The user must specify the input image (`--input`) and output image (`--output`).
+/// The tool creates three output images, each based on the `--output` parameter and with the `_r`, `_g`, and `_b`
+/// suffixes appended.
+///
+/// # See Also
+/// `CreateColourComposite`
pub struct SplitColourComposite {
name: String,
description: String,
@@ -47,7 +54,7 @@ impl SplitColourComposite {
parameters.push(ToolParameter {
name: "Output File".to_owned(),
flags: vec!["-o".to_owned(), "--output".to_owned()],
- description: "Output raster file (suffixes of '_r', '_g', and '_b' will be appended)."
+ description: "Output raster file (suffixes of _r, _g, and _b will be appended)."
.to_owned(),
parameter_type: ParameterType::NewFile(ParameterFileType::Raster),
default_value: None,
@@ -183,6 +190,7 @@ impl WhiteboxTool for SplitColourComposite {
let rows = input.configs.rows as isize;
let columns = input.configs.columns as isize;
let nodata = input.configs.nodata;
+ let output_nodata = -32768f64;
let num_procs = num_cpus::get() as isize;
let (tx, rx) = mpsc::channel();
@@ -194,9 +202,9 @@ impl WhiteboxTool for SplitColourComposite {
let mut val: u32;
let (mut red, mut green, mut blue): (u32, u32, u32);
for row in (0..rows).filter(|r| r % num_procs == tid) {
- let mut data_r = vec![nodata; columns as usize];
- let mut data_g = vec![nodata; columns as usize];
- let mut data_b = vec![nodata; columns as usize];
+ let mut data_r = vec![output_nodata; columns as usize];
+ let mut data_g = vec![output_nodata; columns as usize];
+ let mut data_b = vec![output_nodata; columns as usize];
for col in 0..columns {
in_val = input.get_value(row, col);
if in_val != nodata {
@@ -214,20 +222,28 @@ impl WhiteboxTool for SplitColourComposite {
});
}
+ let extension: String = match Path::new(&output_file).extension().unwrap().to_str() {
+ Some(n) => format!(".{}", n.to_string()),
+ None => "".to_string(),
+ };
+
let mut output_r =
- Raster::initialize_using_file(&output_file.replace(".dep", "_red.dep"), &input);
+ Raster::initialize_using_file(&output_file.replace(&extension, &format!("_r{}", extension)), &input);
output_r.configs.photometric_interp = PhotometricInterpretation::Continuous;
output_r.configs.data_type = DataType::F32;
+ output_r.configs.nodata = output_nodata;
let mut output_g =
- Raster::initialize_using_file(&output_file.replace(".dep", "_green.dep"), &input);
+ Raster::initialize_using_file(&output_file.replace(&extension, &format!("_g{}", extension)), &input);
output_g.configs.photometric_interp = PhotometricInterpretation::Continuous;
output_g.configs.data_type = DataType::F32;
+ output_g.configs.nodata = output_nodata;
let mut output_b =
- Raster::initialize_using_file(&output_file.replace(".dep", "_blue.dep"), &input);
+ Raster::initialize_using_file(&output_file.replace(&extension, &format!("_b{}", extension)), &input);
output_b.configs.photometric_interp = PhotometricInterpretation::Continuous;
output_b.configs.data_type = DataType::F32;
+ output_b.configs.nodata = output_nodata;
for row in 0..rows {
let data = rx.recv().unwrap();
diff --git a/src/tools/image_analysis/stdev_contrast_stretch.rs b/src/tools/image_analysis/stdev_contrast_stretch.rs
index 42214d642..7d5ed115e 100644
--- a/src/tools/image_analysis/stdev_contrast_stretch.rs
+++ b/src/tools/image_analysis/stdev_contrast_stretch.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: July 13, 2017
+Created: 13/07/2017
Last Modified: 13/10/2018
License: MIT
@@ -22,6 +22,22 @@ use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
+/// This tool performs a standard deviation contrast stretch on a raster image. This operation maps each grid cell value
+/// in the input raster image (zin) onto a new scale that ranges from a lower-tail clip value (`min_val`)
+/// to the upper-tail clip value (`max_val`), with the user-specified number of tonal values (`num_tones`), such that:
+///
+/// > zout = ((zin – min_val)/(max_val – min_val)) x num_tones
+///
+/// where zout is the output value. The values of `min_val` and `max_val` are determined based on the image
+/// mean and standard deviation. Specifically, the user must specify the number of standard deviations (`--clip` or
+/// `--stdev`) to be used in determining the min and max clip values. The tool will then calculate the input image mean
+/// and standard deviation and estimate the clip values from these statistics.
+///
+/// This is the same kind of stretch that is used to display raster type data on the fly in many GIS software packages.
+///
+/// # See Also
+/// `GaussianContrastStretch`, `HistogramEqualization`, `MinMaxContrastStretch`, `PercentageContrastStretch`,
+/// `SigmoidalContrastStretch`
pub struct StandardDeviationContrastStretch {
name: String,
description: String,
diff --git a/src/tools/image_analysis/thicken_line.rs b/src/tools/image_analysis/thicken_line.rs
index 3d92e0f48..e08f67f34 100644
--- a/src/tools/image_analysis/thicken_line.rs
+++ b/src/tools/image_analysis/thicken_line.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: July 4, 2017
+Created: 04/07/2017
Last Modified: 13/10/2018
License: MIT
@@ -16,6 +16,22 @@ use std::f64;
use std::io::{Error, ErrorKind};
use std::path;
+/// This image processing tool can be used to thicken single-cell wide lines within a raster file along diagonal
+/// sections of the lines. Because of the limitation of the raster data format, single-cell wide raster lines can
+/// be traversed along diaganol sections without passing through a line grid cell. This causes problems for various
+/// raster analysis functions for which lines are intended to be barriers. This tool will thicken raster lines,
+/// such that it is impossible to cross a line without passing through a line grid cell. While this can also be
+/// achieved using a maximum filter, unlike the filter approach, this tool will result in the smallest possible
+/// thickening to achieve the desired result.
+///
+/// All non-zero, positive values are considered to be foreground pixels while all zero valued cells or NoData cells
+/// are considered background pixels.
+///
+/// Note: Unlike other filter-based operations in *WhiteboxTools*, this algorithm can't easily be parallelized because
+/// the output raster must be read and written to during the same loop.
+///
+/// # See Also
+/// `LineThinning`
pub struct ThickenRasterLine {
name: String,
description: String,
diff --git a/src/tools/image_analysis/write_func_memory_insertion.rs b/src/tools/image_analysis/write_func_memory_insertion.rs
index d82a691e9..c400af4a7 100644
--- a/src/tools/image_analysis/write_func_memory_insertion.rs
+++ b/src/tools/image_analysis/write_func_memory_insertion.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: July 18, 2017
+Created: 18/07/2017
Last Modified: 13/10/2018
License: MIT
*/
@@ -17,7 +17,26 @@ use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
-/// Performs a write function memory insertion for single-band multi-date change detection.
+/// Jensen (2015) describes write function memory (WFM) insertion as a simple yet effective method of visualizing
+/// land-cover change between two or three dates. WFM insertion may be used to qualitatively inspect change in any
+/// type of registered, multi-date imagery. The technique operates by creating a red-green-blue (RGB) colour composite
+/// image based on co-registered imagery from two or three dates. If two dates are input, the first date image will be
+/// put into the red channel, while the second date image will be put into both the green and blue channels. The result
+/// is an image where the areas of change are displayed as red (date 1 is brighter than date 2) and cyan (date 1 is
+/// darker than date 2), and areas of little change are represented in grey-tones. The larger the change in pixel
+/// brightness between dates, the more intense the resulting colour will be.
+///
+/// If images from three dates are input, the resulting composite can contain many distinct colours. Again, more
+/// intense the colours are indicative of areas of greater land-cover change among the dates, while areas of little
+/// change are represented in grey-tones. Interpreting the direction of change is more difficult when three dates are
+/// used. Note that for multi-spectral imagery, only one band from each date can be used for creating a WFM insertion
+/// image.
+///
+/// # Reference
+/// Jensen, J. R. (2015). Introductory Digital Image Processing: A Remote Sensing Perspective.
+///
+/// # See Also
+/// `CreateColourComposite`, `ChangeVectorAnalysis`
pub struct WriteFunctionMemoryInsertion {
name: String,
description: String,
diff --git a/src/tools/lidar_analysis/clip_lidar_to_polygon.rs b/src/tools/lidar_analysis/clip_lidar_to_polygon.rs
index 1db1ba038..530373041 100644
--- a/src/tools/lidar_analysis/clip_lidar_to_polygon.rs
+++ b/src/tools/lidar_analysis/clip_lidar_to_polygon.rs
@@ -15,6 +15,17 @@ use std::env;
use std::io::{Error, ErrorKind};
use std::path;
+/// This tool can be used to isolate, or clip, all of the LiDAR points in a LAS file (`--input`) contained within
+/// one or more vector polygon features. The user must specify the name of the input clip file (--polygons), wich
+/// must be a vector of a Polygon base shape type. The clip file may contain multiple polygon features and polygon hole
+/// parts will be respected during clipping, i.e. LiDAR points within polygon holes will be removed from the output LAS
+/// file.
+///
+/// Use the `ErasePolygonFromLidar` tool to perform the complementary operation of removing points from a LAS file
+/// that are contained within a set of polygons.
+///
+/// # See Also
+/// `ErasePolygonFromLidar`, `Clip`, `ClipRasterToPolygon`
pub struct ClipLidarToPolygon {
name: String,
description: String,
diff --git a/src/tools/lidar_analysis/erase_polygon_from_lidar.rs b/src/tools/lidar_analysis/erase_polygon_from_lidar.rs
index 3de70b4ac..8679485cb 100644
--- a/src/tools/lidar_analysis/erase_polygon_from_lidar.rs
+++ b/src/tools/lidar_analysis/erase_polygon_from_lidar.rs
@@ -15,6 +15,17 @@ use std::env;
use std::io::{Error, ErrorKind};
use std::path;
+/// This tool can be used to remove, or erase, all of the LiDAR points in a LAS file (`--input`) contained within
+/// one or more vector polygon features. The user must specify the name of the input clip file (--polygons), wich
+/// must be a vector of a Polygon base shape type. The clip file may contain multiple polygon features and polygon hole
+/// parts will be respected during clipping, i.e. LiDAR points within polygon holes will be remain in the output LAS
+/// file.
+///
+/// Use the `ClipLidarToPolygon` tool to perform the complementary operation of clipping (isolating) points from a LAS file
+/// that are contained within a set of polygons, while removing points that lie outside the input polygons.
+///
+/// # See Also
+/// `ClipLidarToPolygon`, `Clip`, `ClipRasterToPolygon`
pub struct ErasePolygonFromLidar {
name: String,
description: String,
diff --git a/src/tools/lidar_analysis/lidar_join.rs b/src/tools/lidar_analysis/lidar_join.rs
index 2edb3b9d1..f9d7276a7 100644
--- a/src/tools/lidar_analysis/lidar_join.rs
+++ b/src/tools/lidar_analysis/lidar_join.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: June 21, 2017
+Created: 21/06/2017
Last Modified: 29/08/2018
License: MIT
*/
@@ -13,6 +13,12 @@ use std::env;
use std::io::{Error, ErrorKind};
use std::path;
+/// This tool can be used to merge multiple LiDAR LAS files into a single output LAS file. Due to their large size,
+/// LiDAR data sets are often tiled into smaller, non-overlapping tiles. Sometimes it is more convenient to combine
+/// multiple tiles together for data processing and `LidarJoin` can be used for this purpose.
+///
+/// # See Also
+/// `LidarTile`
pub struct LidarJoin {
name: String,
description: String,
diff --git a/src/tools/lidar_analysis/lidar_tile.rs b/src/tools/lidar_analysis/lidar_tile.rs
index e81c10b1d..063d31f4d 100644
--- a/src/tools/lidar_analysis/lidar_tile.rs
+++ b/src/tools/lidar_analysis/lidar_tile.rs
@@ -1,8 +1,8 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: June 26, 2017
-Last Modified: 29/08/2018
+Created: 26/06/2017
+Last Modified: 05/02/2019
License: MIT
*/
use crate::lidar::*;
@@ -14,7 +14,15 @@ use std::io::{Error, ErrorKind};
use std::path;
use std::path::Path;
-/// Tiles a LiDAR LAS file into multiple LAS files.
+/// This tool can be used to break a LiDAR LAS file into multiple, non-overlapping tiles, each saved as a
+/// single LAS file. The user must specify the parameter of the tile grid, including its origin (`--origin_x` and
+/// `--origin_y`) and the tile width and height (`--width` and `--height`). Tiles containing fewer points than
+/// specified in the `--min_points` parameter will not be output. This can be useful when tiling terrestrial LiDAR
+/// datasets because the low point density at the edges of the point cloud (i.e. most distant from the scan
+/// station) can result in poorly populated tiles containing relatively few points.
+///
+/// # See Also
+/// `LidarJoin`, `LidarTileFootprint`
pub struct LidarTile {
name: String,
description: String,
@@ -41,8 +49,8 @@ impl LidarTile {
});
parameters.push(ToolParameter {
- name: "Tile Width in X Dimension".to_owned(),
- flags: vec!["--width_x".to_owned()],
+ name: "Tile Width".to_owned(),
+ flags: vec!["--width".to_owned()],
description: "Width of tiles in the X dimension; default 1000.0.".to_owned(),
parameter_type: ParameterType::Float,
default_value: Some("1000.0".to_owned()),
@@ -50,9 +58,9 @@ impl LidarTile {
});
parameters.push(ToolParameter {
- name: "Tile Width in Y Dimension".to_owned(),
- flags: vec!["--width_y".to_owned()],
- description: "Width of tiles in the Y dimension.".to_owned(),
+ name: "Tile Height".to_owned(),
+ flags: vec!["--height".to_owned()],
+ description: "Height of tiles in the Y dimension.".to_owned(),
parameter_type: ParameterType::Float,
default_value: Some("1000.0".to_owned()),
optional: true,
@@ -97,7 +105,7 @@ impl LidarTile {
if e.contains(".exe") {
short_exe += ".exe";
}
- let usage = format!(">>.*{0} -r={1} -v -i=*path*to*data*input.las --width_x=1000.0 --width_y=2500.0 -=min_points=100", short_exe, name).replace("*", &sep);
+ let usage = format!(">>.*{0} -r={1} -v -i=*path*to*data*input.las --width=1000.0 --height=2500.0 -=min_points=100", short_exe, name).replace("*", &sep);
LidarTile {
name: name,
@@ -180,13 +188,13 @@ impl WhiteboxTool for LidarTile {
} else {
input_file = args[i + 1].to_string();
}
- } else if flag_val == "-width_x" {
+ } else if flag_val == "-width_x" || flag_val == "-width" {
if keyval {
width_x = vec[1].to_string().parse::().unwrap();
} else {
width_x = args[i + 1].to_string().parse::().unwrap();
}
- } else if flag_val == "-width_y" {
+ } else if flag_val == "-width_y" || flag_val == "-height" {
if keyval {
width_y = vec[1].to_string().parse::().unwrap();
} else {
diff --git a/src/tools/lidar_analysis/lidar_tile_footprint.rs b/src/tools/lidar_analysis/lidar_tile_footprint.rs
index ec3f42497..17127864d 100644
--- a/src/tools/lidar_analysis/lidar_tile_footprint.rs
+++ b/src/tools/lidar_analysis/lidar_tile_footprint.rs
@@ -21,8 +21,17 @@ use std::sync::mpsc::channel;
use std::sync::{Arc, Mutex};
use std::thread;
-/// Creates a vector polygon of the convex hull of a LiDAR point cloud. When the input/output parameters
-/// are not specified, the tool works with all LAS files contained within the working directory.
+/// This tool can be used to create a vector polygon of the convex hull of a LiDAR point cloud (i.e. LAS file).
+/// If the user specified an input file (`--input`) and output file (`--output`), the tool will calculate the convex
+/// hull, containing all of the data points, and output this feature to a vector polygon file. If the `input` and
+/// `output` parameters are left unspecified, the tool will calculate the hulls of every LAS file contained within the
+/// working directory and output these features to a single vector polygon file. If this is the desired mode of
+/// operation, it is important to specify the working directory (`--wd`) containing the group of LAS files; do not
+/// specify the optional `--input` and `--output` parameters in this case. Each polygon in the output vector will contain
+/// a `LAS_NM` field, specifying the source LAS file name, and a `NUM_PNTS` field, containing the number of points
+/// within the source file. This output can therefore be useful to create an index map of a large tiled LiDAR dataset.
+///
+/// `LidarTile`, `LayerFootprint`
pub struct LidarTileFootprint {
name: String,
description: String,
diff --git a/src/tools/math_stat_analysis/principal_component_analysis.rs b/src/tools/math_stat_analysis/principal_component_analysis.rs
index c5ee2b9c3..182667c5f 100644
--- a/src/tools/math_stat_analysis/principal_component_analysis.rs
+++ b/src/tools/math_stat_analysis/principal_component_analysis.rs
@@ -2,12 +2,8 @@
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
Created: 15/03/2018
-Last Modified: 13/10/2018
+Last Modified: 05/02/2019
License: MIT
-
-Note: The current implementation reads every raster into memory at one time. This is
-because of the calculation of the co-variances. As such, if the entire image stack can't
-fit in memory, the tool will not work.
*/
use crate::na::DMatrix;
@@ -24,6 +20,54 @@ use std::io::{Error, ErrorKind};
use std::path;
use std::process::Command;
+/// Principal component analysis (PCA) is a common data reduction technique that is used to reduce the dimensionality of
+/// multi-dimensional space. In the field of remote sensing, PCA is often used to reduce the number of bands of
+/// multi-spectral, or hyper-spectral, imagery. Image correlation analysis often reveals a substantial level of correlation
+/// among bands of multi-spectral imagery. This correlation represents data redundancy, i.e. fewer images than the number
+/// of bands are required to represent the same information, where the information is related to variation within the imagery.
+/// PCA transforms the original data set of *n* bands into *n* 'component' images, where each component image is uncorrelated
+/// with all other components. The technique works by transforming the axes of the multi-spectral space such that it coincides
+/// with the directions of greatest correlation. Each of these new axes are orthogonal to one another, i.e. they are at right
+/// angles. PCA is therefore a type of coordinate system transformation. The PCA component images are arranged such that the
+/// greatest amount of variance (or information) within the original data set, is contained within the first component and the
+/// amount of variance decreases with each component. It is often the case that the majority of the information contained in a
+/// multi-spectral data set can be represented by the first three or four PCA components. The higher-order components are often
+/// associated with noise in the original data set.
+///
+/// The user must specify the names of the multiple input images (`--inputs`). Additionally, the user must specify whether to
+/// perform a standardized PCA (`--standardized`) and the number of output components (`--num_comp`) to generate (all components
+/// will be output unless otherwise specified). A standardized PCA is performed using the correlation matrix rather than the
+/// variance-covariance matrix. This is appropriate when the variances in the input images differ substantially, such as would be
+/// the case if they contained values that were recorded in different units (e.g. feet and meters) or on different scales (e.g.
+/// 8-bit vs. 16 bit).
+///
+/// Several outputs will be generated when the tool has completed. The PCA report will be embeded within an output (`--output`)
+/// HTML file, which should be automatically displayed after the tool has completed. This report contains useful data summarizing
+/// the results of the PCA, including the explained variances of each factor, the Eigenvalues and Eigenvectors associated with
+/// factors, the factor loadings, and a scree plot. The first table that is in the PCA report lists the amount of explained
+/// variance (in non-cumulative and cumulative form), the Eigenvalue, and the Eigenvector for each component. Each of the PCA
+/// components refer to the newly created, transformed images that are created by running the tool. The amount of explained
+/// variance associated with each component can be thought of as a measure of how much information content within the original
+/// multi-spectral data set that a component has. The higher this value is, the more important the component is. This same
+/// information is presented in graphical form in the *scree plot*, found at the bottom of the PCA report. The Eigenvalue is
+/// another measure of the information content of a component and the eigenvector describes the mathematical transformation
+/// (rotation coordinates) that correspond to a particular component image.
+///
+/// Factor loadings are also output in a table within the PCA text report (second table). These loading values describe the
+/// correlation (i.e. *r* values) between each of the PCA components (columns) and the original images (rows). These values
+/// show you how the information contained in an image is spread among the components. An analysis of factor loadings can be
+/// reveal useful information about the data set. For example, it can help to identify groups of similar images.
+///
+/// PCA is used to reduce the number of band images necessary for classification (i.e. as a data reduction technique), for
+/// noise reduction, and for change detection applications. When used as a change detection technique, the major PCA components
+/// tend to be associated with stable elements of the data set while variance due to land-cover change tend to manifest in the
+/// high-order, 'change components'. When used as a noise reduction technique, an inverse PCA is generally performed, leaving
+/// out one or more of the high-order PCA components, which account for noise variance.
+///
+/// Note: the current implementation reads every raster into memory at one time. This is because of the calculation of the
+/// co-variances. As such, if the entire image stack cannot fit in memory, the tool will likely experience an out-ofs-memory error.
+/// This tool should be run using the `--wd` flag to spectify the working directory into which the component images will be
+/// written.
pub struct PrincipalComponentAnalysis {
name: String,
description: String,
@@ -53,7 +97,7 @@ impl PrincipalComponentAnalysis {
parameters.push(ToolParameter {
name: "Output HTML Report File".to_owned(),
- flags: vec!["--out_html".to_owned()],
+ flags: vec!["--out_html".to_owned(), "--output".to_owned()],
description: "Output HTML report file.".to_owned(),
parameter_type: ParameterType::NewFile(ParameterFileType::Html),
default_value: None,
@@ -89,7 +133,7 @@ impl PrincipalComponentAnalysis {
if e.contains(".exe") {
short_exe += ".exe";
}
- let usage = format!(">>.*{} -r={} -v --wd='*path*to*data*' -i='image1.tif;image2.tif;image3.tif' --out_html=report.html --num_comp=3 --standardized", short_exe, name).replace("*", &sep);
+ let usage = format!(">>.*{} -r={} -v --wd='*path*to*data*' -i='image1.tif;image2.tif;image3.tif' --output=report.html --num_comp=3 --standardized", short_exe, name).replace("*", &sep);
PrincipalComponentAnalysis {
name: name,
@@ -138,6 +182,7 @@ impl WhiteboxTool for PrincipalComponentAnalysis {
let mut input_files_str = String::new();
let mut output_html_file = String::new();
let mut num_comp = 0usize;
+ let mut num_comp_set = false;
let mut standardized = false;
if args.len() == 0 {
@@ -162,7 +207,7 @@ impl WhiteboxTool for PrincipalComponentAnalysis {
} else {
args[i + 1].to_string()
};
- } else if flag_val == "-out_html" {
+ } else if flag_val == "-out_html" || flag_val == "-output" {
output_html_file = if keyval {
vec[1].to_string()
} else {
@@ -174,6 +219,7 @@ impl WhiteboxTool for PrincipalComponentAnalysis {
} else {
args[i + 1].to_string().parse::().unwrap() as usize
};
+ num_comp_set = true;
} else if flag_val == "-standardized" {
standardized = true;
}
@@ -219,9 +265,10 @@ impl WhiteboxTool for PrincipalComponentAnalysis {
let mut average = vec![0f64; num_files];
let mut num_cells = vec![0f64; num_files];
let mut input_raster: Vec = Vec::with_capacity(num_files);
- let mut wd = "".to_string();
let mut file_names = vec![];
- println!("Calculating image means...");
+ if verbose {
+ println!("Calculating image means...");
+ }
for i in 0..num_files {
if !input_files[i].trim().is_empty() {
// quality control on the image file name.
@@ -244,8 +291,6 @@ impl WhiteboxTool for PrincipalComponentAnalysis {
if rows == -1 || columns == -1 {
rows = input_raster[i].configs.rows as isize;
columns = input_raster[i].configs.columns as isize;
- wd = input_file
- .replace(&format!("{}.dep", input_raster[i].get_short_filename()), "");
} else {
if input_raster[i].configs.rows as isize != rows
|| input_raster[i].configs.columns as isize != columns
@@ -420,9 +465,9 @@ impl WhiteboxTool for PrincipalComponentAnalysis {
}
writer.write_all("
".as_bytes())?;
- /////////////////////////////////////////
+ ////////////////////////////
// Factor Loadings Report //
- /////////////////////////////////////////
+ ////////////////////////////
writer.write_all("".as_bytes())?;
writer.write_all("Factor Loadings".as_bytes())?;
writer.write_all("Image | ".as_bytes())?;
@@ -432,7 +477,7 @@ impl WhiteboxTool for PrincipalComponentAnalysis {
writer.write_all("
".as_bytes())?;
if !standardized {
for j in 0..num_files {
- let mut s = format!("{} | ", (j + 1));
+ let mut s = format!(" | {} | ", (j + 1));
for k in 0..num_files {
pc = component_order[k];
let loading = (eigenvectors[pc * num_files + j] * eigenvalues[pc].sqrt())
@@ -507,10 +552,15 @@ impl WhiteboxTool for PrincipalComponentAnalysis {
}
// Output the component images
+ if num_comp == 0 && !num_comp_set {
+ // if it's not set, then output all the components.
+ num_comp = num_files;
+ }
for a in 0..num_comp {
pc = component_order[a];
- let out_file = format!("{}PCA_component{}.dep", wd, (a + 1));
+ let out_file = format!("{}PCA_component{}.tif", working_directory, (a + 1));
let mut output = Raster::initialize_using_file(&out_file, &input_raster[0]);
+ output.configs.data_type = DataType::F32;
for row in 0..rows {
for col in 0..columns {
z1 = input_raster[0].get_value(row, col);
diff --git a/src/tools/stream_network_analysis/horton_order.rs b/src/tools/stream_network_analysis/horton_order.rs
index ac2352785..fa257c068 100644
--- a/src/tools/stream_network_analysis/horton_order.rs
+++ b/src/tools/stream_network_analysis/horton_order.rs
@@ -28,7 +28,7 @@ use std::path;
/// The user must specify the names of a streams raster image (`--streams`) and D8 pointer image (`--d8_pntr`). Stream cells
/// are designated in the streams image as all positive, nonzero values. Thus all non-stream or background grid cells are
/// commonly assigned either zeros or NoData values. The pointer image is used to traverse the stream network and should only
-/// be created using the D8 algorithm. Background cells will be assigned the NoData value in the output image, unless the
+/// be created using the D8 algorithm (`D8Pointer`). Background cells will be assigned the NoData value in the output image, unless the
/// `--zero_background` parameter is used, in which case non-stream cells will be assinged zero values in the output.
///
/// By default, the pointer raster is assumed to use the clockwise indexing method used by WhiteboxTools.
diff --git a/src/tools/stream_network_analysis/long_profile.rs b/src/tools/stream_network_analysis/long_profile.rs
index f8e34f440..647195182 100644
--- a/src/tools/stream_network_analysis/long_profile.rs
+++ b/src/tools/stream_network_analysis/long_profile.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: February 20, 2018
+Created: 20/02/2018
Last Modified: 12/10/2018
License: MIT
*/
@@ -24,7 +24,29 @@ use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
-/// Plots the stream longitudinal profiles for one or more rivers.
+/// This tool can be used to create a [longitudinal profile](http://www.fao.org/docrep/003/X6841E/X6841E02.HTM) plot.
+/// A longitudinal stream profile is a plot of elevation against downstream distance. Most long profiles use distance
+/// from channel head as the distance measure. This tool, however, uses the distance to the stream network outlet cell,
+/// or mouth, as the distance measure. The reason for this difference is that while for any one location within a stream
+/// network there is only ever one downstream outlet, there is usually many upstream channel heads. Thus plotted using
+/// the traditional downstream-distance method, the same point within a network will plot in many different long profile
+/// locations, whereas it will always plot on one unique location in the distance-to-mouth method. One consequence of
+/// this difference is that the long profile will be oriented from right-to-left rather than left-to-right, as would
+/// traditionally be the case.
+///
+/// The tool outputs an interactive SVG line graph embedded in an HTML document (`--output`). The user must specify the
+/// names of a D8 pointer (`--d8_pntr`) image (flow direction), a streams raster image
+/// (`--streams`), and a digital elevation model (`--dem`). Stream cells are designated in the streams image as all
+/// positive, nonzero values. Thus all non-stream or background grid cells are commonly assigned either zeros or NoData
+/// values. The pointer image is used to traverse the stream network and should only be created using the D8 algorithm
+/// (`D8Pointer`). The streams image should be derived using a flow accumulation based stream network extraction
+/// algorithm, also based on the D8 flow algorithm.
+///
+/// By default, the pointer raster is assumed to use the clockwise indexing method used by WhiteboxTools.
+/// If the pointer file contains ESRI flow direction values instead, the `--esri_pntr` parameter must be specified.
+///
+/// # See Also
+/// `LongProfileFromPoints`, `Profile`, `D8Pointer`
pub struct LongProfile {
name: String,
description: String,
diff --git a/src/tools/stream_network_analysis/long_profile_from_points.rs b/src/tools/stream_network_analysis/long_profile_from_points.rs
index 6a5b6317d..1f3803120 100644
--- a/src/tools/stream_network_analysis/long_profile_from_points.rs
+++ b/src/tools/stream_network_analysis/long_profile_from_points.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: February 21, 2018
+Created: 21/02/2018
Last Modified: 12/10/2018
License: MIT
*/
diff --git a/src/tools/stream_network_analysis/raster_streams_to_vector.rs b/src/tools/stream_network_analysis/raster_streams_to_vector.rs
index 414d681e4..1808bf1f3 100644
--- a/src/tools/stream_network_analysis/raster_streams_to_vector.rs
+++ b/src/tools/stream_network_analysis/raster_streams_to_vector.rs
@@ -25,6 +25,9 @@ use std::path;
/// link in the stream network. The flow pointer file must be calculated from a DEM with
/// all topographic depressions and flat areas removed and must be calculated using the
/// D8 flow pointer algorithm. The output vector will contain PolyLine features.
+///
+/// # See Also
+/// `RasterizeStreams`, `RasterToVectorLines`
pub struct RasterStreamsToVector {
name: String,
description: String,
diff --git a/src/tools/stream_network_analysis/rasterize_streams.rs b/src/tools/stream_network_analysis/rasterize_streams.rs
index 5eb4db211..3d9bc5dbb 100644
--- a/src/tools/stream_network_analysis/rasterize_streams.rs
+++ b/src/tools/stream_network_analysis/rasterize_streams.rs
@@ -24,7 +24,7 @@ use std::path;
/// 41(5): 658–668. DOI: 10.1002/esp.3888
///
/// # See Also
-/// `RasterizeStreams`
+/// `RasterStreamsToVector`
pub struct RasterizeStreams {
name: String,
description: String,
diff --git a/src/tools/terrain_analysis/circular_variance_of_aspect.rs b/src/tools/terrain_analysis/circular_variance_of_aspect.rs
index 68e15a126..52b4372a2 100644
--- a/src/tools/terrain_analysis/circular_variance_of_aspect.rs
+++ b/src/tools/terrain_analysis/circular_variance_of_aspect.rs
@@ -228,7 +228,9 @@ impl WhiteboxTool for CircularVarianceOfAspect {
// Smooth the DEM
let mut smoothed_dem = input.get_data_as_array2d();
- println!("Smoothing the input DEM...");
+ if verbose {
+ println!("Smoothing the input DEM...");
+ }
let sigma = (midpoint as f64 - 0.5) / 3f64;
if sigma < 1.8 && filter_size > 3 {
let recip_root_2_pi_times_sigma_d = 1.0 / ((2.0 * f64::consts::PI).sqrt() * sigma);
@@ -537,7 +539,9 @@ impl WhiteboxTool for CircularVarianceOfAspect {
for col in 0..columns {
sumx += xc.get_value(row, col);
sumy += yc.get_value(row, col);
- if smoothed_dem.get_value(row, col) == nodata {
+ if smoothed_dem.get_value(row, col) == nodata ||
+ (xc.get_value(row, col) == 0f64 && yc.get_value(row, col) == 0f64) {
+ // it's either nodata or a flag cell in the DEM.
i_n.decrement(row, col, 1);
}
sumn += i_n.get_value(row, col);
@@ -553,7 +557,9 @@ impl WhiteboxTool for CircularVarianceOfAspect {
xc.increment(row, col, xc.get_value(row, col-1));
yc.increment(row, col, yc.get_value(row, col-1));
i_n.increment(row, col, i_n.get_value(row, col-1));
- if smoothed_dem.get_value(row, col) == nodata {
+ if smoothed_dem.get_value(row, col) == nodata ||
+ (xc.get_value(row, col) == 0f64 && yc.get_value(row, col) == 0f64) {
+ // it's either nodata or a flag cell in the DEM.
i_n.decrement(row, col, 1);
}
}
diff --git a/src/tools/terrain_analysis/drainage_preserving_smoothing.rs b/src/tools/terrain_analysis/drainage_preserving_smoothing.rs
index 21496c53f..acf5b234b 100644
--- a/src/tools/terrain_analysis/drainage_preserving_smoothing.rs
+++ b/src/tools/terrain_analysis/drainage_preserving_smoothing.rs
@@ -2,7 +2,7 @@
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay and Anthony Francioni
Created: 06/09/2018
-Last Modified: 12/10/2018
+Last Modified: 31/01/2019
License: MIT
*/
@@ -88,7 +88,7 @@ impl DrainagePreservingSmoothing {
flags: vec!["--num_iter".to_owned()],
description: "Number of iterations.".to_owned(),
parameter_type: ParameterType::Integer,
- default_value: Some("10".to_owned()),
+ default_value: Some("3".to_owned()),
optional: true,
});
@@ -97,7 +97,7 @@ impl DrainagePreservingSmoothing {
flags: vec!["--max_diff".to_owned()],
description: "Maximum allowable absolute elevation change (optional).".to_owned(),
parameter_type: ParameterType::Float,
- default_value: Some("2.0".to_owned()),
+ default_value: Some("0.5".to_owned()),
optional: true,
});
@@ -205,7 +205,7 @@ impl WhiteboxTool for DrainagePreservingSmoothing {
let mut output_file = String::new();
let mut filter_size = 11usize;
let mut max_norm_diff = 8f64;
- let mut num_iter = 5;
+ let mut num_iter = 3;
let mut reduction = 80f64;
let mut dfm_threshold = 0.15;
let mut z_factor = 1f64;
@@ -329,7 +329,7 @@ impl WhiteboxTool for DrainagePreservingSmoothing {
if verbose {
println!("Reading data...")
- };
+ }
let input = Arc::new(Raster::new(&input_file, "r")?);
@@ -609,13 +609,15 @@ impl WhiteboxTool for DrainagePreservingSmoothing {
}
let t1 = Instant::now();
- println!(
- "{}",
- format!(
- "Calculating normal vectors: {}",
- get_formatted_elapsed_time(start)
- )
- );
+ if verbose {
+ println!(
+ "{}",
+ format!(
+ "Calculating normal vectors: {}",
+ get_formatted_elapsed_time(start)
+ )
+ );
+ }
//////////////////////////////////////////////////////////
// Smooth the normal vector field of the fitted planes. //
@@ -720,13 +722,15 @@ impl WhiteboxTool for DrainagePreservingSmoothing {
}
}
- println!(
- "{}",
- format!(
- "Smoothing normal vectors: {}",
- get_formatted_elapsed_time(t1)
- )
- );
+ if verbose {
+ println!(
+ "{}",
+ format!(
+ "Smoothing normal vectors: {}",
+ get_formatted_elapsed_time(t1)
+ )
+ );
+ }
///////////////////////////////////////////////////////////////////////////
// Update the elevations of the DEM based on the smoothed normal vectors //
@@ -747,9 +751,13 @@ impl WhiteboxTool for DrainagePreservingSmoothing {
let mut zn: f64;
let mut output = Raster::initialize_using_file(&output_file, &input);
output.set_data_from_raster(&input)?;
- println!("Updating elevations...");
+ if verbose {
+ println!("Updating elevations...");
+ }
for loop_num in 0..num_iter {
- println!("Iteration {} of {}...", loop_num + 1, num_iter);
+ if verbose {
+ println!("Iteration {} of {}...", loop_num + 1, num_iter);
+ }
for row in 0..rows {
for col in 0..columns {
@@ -830,6 +838,7 @@ impl WhiteboxTool for DrainagePreservingSmoothing {
output.add_metadata_entry(format!("Iterations: {}", num_iter));
output.add_metadata_entry(format!("Reduction factor: {}", reduction * 100f64));
output.add_metadata_entry(format!("DFM threhsold: {}", dfm_threshold));
+ output.add_metadata_entry(format!("Max. z difference: {}", max_z_diff));
output.add_metadata_entry(format!("Z-factor: {}", z_factor));
output.add_metadata_entry(format!("Elapsed Time (excluding I/O): {}", elapsed_time));
diff --git a/src/tools/terrain_analysis/elev_percentile.rs b/src/tools/terrain_analysis/elev_percentile.rs
index 253185484..b30bbcad5 100644
--- a/src/tools/terrain_analysis/elev_percentile.rs
+++ b/src/tools/terrain_analysis/elev_percentile.rs
@@ -50,7 +50,7 @@ use std::thread;
/// These dimensions should be odd, positive integer values (e.g. 3, 5, 7, 9, etc.).
///
/// # References
-/// Newman, D. R., Lindsay, J. B., & Cockburn, J. M. H. (2018). Evaluating metrics of local topographic position
+/// Newman, D. R., Lindsay, J. B., and Cockburn, J. M. H. (2018). Evaluating metrics of local topographic position
/// for multiscale geomorphometric analysis. Geomorphology, 312, 40-50.
///
/// Huang, T., Yang, G.J.T.G.Y. and Tang, G., 1979. A fast two-dimensional median filtering algorithm. IEEE
diff --git a/src/tools/terrain_analysis/feature_preserving_denoise.rs b/src/tools/terrain_analysis/feature_preserving_denoise.rs
index 91c317ac5..8fc0e80cd 100644
--- a/src/tools/terrain_analysis/feature_preserving_denoise.rs
+++ b/src/tools/terrain_analysis/feature_preserving_denoise.rs
@@ -1,8 +1,8 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: November 23, 2017
-Last Modified: 12/10/2018
+Created: 23/11/2017
+Last Modified: 31/01/2019
License: MIT
*/
@@ -104,7 +104,7 @@ impl FeaturePreservingDenoise {
flags: vec!["--num_iter".to_owned()],
description: "Number of iterations.".to_owned(),
parameter_type: ParameterType::Integer,
- default_value: Some("10".to_owned()),
+ default_value: Some("3".to_owned()),
optional: true,
});
@@ -113,7 +113,7 @@ impl FeaturePreservingDenoise {
flags: vec!["--max_diff".to_owned()],
description: "Maximum allowable absolute elevation change (optional).".to_owned(),
parameter_type: ParameterType::Float,
- default_value: Some("2.0".to_owned()),
+ default_value: Some("0.5".to_owned()),
optional: true,
});
@@ -199,7 +199,7 @@ impl WhiteboxTool for FeaturePreservingDenoise {
let mut output_file = String::new();
let mut filter_size = 11usize;
let mut max_norm_diff = 8f64;
- let mut num_iter = 5;
+ let mut num_iter = 3;
let mut z_factor = 1f64;
let mut max_z_diff = f64::INFINITY;
@@ -295,7 +295,7 @@ impl WhiteboxTool for FeaturePreservingDenoise {
if verbose {
println!("Reading data...")
- };
+ }
let input = Arc::new(Raster::new(&input_file, "r")?);
@@ -391,13 +391,15 @@ impl WhiteboxTool for FeaturePreservingDenoise {
}
let t1 = Instant::now();
- println!(
- "{}",
- format!(
- "Calculating normal vectors: {}",
- get_formatted_elapsed_time(start)
- )
- );
+ if verbose {
+ println!(
+ "{}",
+ format!(
+ "Calculating normal vectors: {}",
+ get_formatted_elapsed_time(start)
+ )
+ );
+ }
//////////////////////////////////////////////////////////
// Smooth the normal vector field of the fitted planes. //
@@ -503,13 +505,15 @@ impl WhiteboxTool for FeaturePreservingDenoise {
}
}
- println!(
- "{}",
- format!(
- "Smoothing normal vectors: {}",
- get_formatted_elapsed_time(t1)
- )
- );
+ if verbose {
+ println!(
+ "{}",
+ format!(
+ "Smoothing normal vectors: {}",
+ get_formatted_elapsed_time(t1)
+ )
+ );
+ }
///////////////////////////////////////////////////////////////////////////
// Update the elevations of the DEM based on the smoothed normal vectors //
@@ -528,9 +532,13 @@ impl WhiteboxTool for FeaturePreservingDenoise {
let mut zn: f64;
let mut output = Raster::initialize_using_file(&output_file, &input);
output.set_data_from_raster(&input)?;
- println!("Updating elevations...");
+ if verbose {
+ println!("Updating elevations...");
+ }
for loop_num in 0..num_iter {
- println!("Iteration {} of {}...", loop_num + 1, num_iter);
+ if verbose {
+ println!("Iteration {} of {}...", loop_num + 1, num_iter);
+ }
for row in 0..rows {
for col in 0..columns {
@@ -598,12 +606,13 @@ impl WhiteboxTool for FeaturePreservingDenoise {
output.add_metadata_entry(format!("Filter size: {}", filter_size));
output.add_metadata_entry(format!("Normal difference threshold: {}", max_norm_diff));
output.add_metadata_entry(format!("Iterations: {}", num_iter));
+ output.add_metadata_entry(format!("Max. z difference: {}", max_z_diff));
output.add_metadata_entry(format!("Z-factor: {}", z_factor));
output.add_metadata_entry(format!("Elapsed Time (excluding I/O): {}", elapsed_time));
if verbose {
println!("Saving data...")
- };
+ }
let _ = match output.write() {
Ok(_) => {
if verbose {
diff --git a/src/tools/terrain_analysis/percent_elev_range.rs b/src/tools/terrain_analysis/percent_elev_range.rs
index 7e2dab28f..1446d9c05 100644
--- a/src/tools/terrain_analysis/percent_elev_range.rs
+++ b/src/tools/terrain_analysis/percent_elev_range.rs
@@ -34,11 +34,11 @@ use std::thread;
/// to outliers in neighbouring elevations (e.g. the presence of off-terrain objects in the DEM).
///
/// # References
-/// Newman, D. R., Lindsay, J. B., & Cockburn, J. M. H. (2018). Evaluating metrics of local topographic position
+/// Newman, D. R., Lindsay, J. B., and Cockburn, J. M. H. (2018). Evaluating metrics of local topographic position
/// for multiscale geomorphometric analysis. Geomorphology, 312, 40-50.
///
/// # See Also
-/// `ElevPercentile`, `DevFromMeanElev`, `DiffFromMeanElev`
+/// `ElevPercentile`, `DevFromMeanElev`, `DiffFromMeanElev`, `RelativeTopographicPosition`
pub struct PercentElevRange {
name: String,
description: String,
diff --git a/src/tools/terrain_analysis/profile.rs b/src/tools/terrain_analysis/profile.rs
index 5c66fe835..b5761a492 100644
--- a/src/tools/terrain_analysis/profile.rs
+++ b/src/tools/terrain_analysis/profile.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: February 21, 2018
+Created: 21/02/2018
Last Modified: 12/10/2018
License: MIT
*/
@@ -20,6 +20,17 @@ use std::io::{Error, ErrorKind};
use std::path;
use std::process::Command;
+/// This tool can be used to plot the data profile, along a set of one or more vector lines (`--lines`), in
+/// an input (`--surface`) digital elevation model (DEM), or other surface model. The data profile plots
+/// surface height (y-axis) against distance along profile (x-axis). The tool outputs an interactive SVG line
+/// graph embedded in an HTML document (`--output`). If the vector lines file contains multiple line features,
+/// the output plot will contain each of the input profiles.
+///
+/// If you want to extract the [longitudinal profile](http://www.fao.org/docrep/003/X6841E/X6841E02.HTM) of a river,
+/// use the `LongProfile` tool instead.
+///
+/// # See Also
+/// `LongProfile`, `HypsometricAnalysis`
pub struct Profile {
name: String,
description: String,
diff --git a/src/tools/terrain_analysis/relative_aspect.rs b/src/tools/terrain_analysis/relative_aspect.rs
index 034267921..d306e91f6 100644
--- a/src/tools/terrain_analysis/relative_aspect.rs
+++ b/src/tools/terrain_analysis/relative_aspect.rs
@@ -17,6 +17,23 @@ use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
+/// This tool creates a new raster in which each grid cell is assigned the terrain aspect relative to a user-specified
+/// direction (`--azimuth`). Relative terrain aspect is the angular distance (measured in degrees) between the land-surface
+/// aspect and the assumed regional wind azimuth (Bohner and Antonic, 2007). It is bound between 0-degrees (windward direction)
+/// and 180-degrees (leeward direction). Relative terrain aspect is the simplest of the measures of topographic exposure to
+/// wind, taking into account terrain orientation only and neglecting the influences of topographic shadowing by distant
+/// landforms and the deflection of wind by topography.
+///
+/// The user must specify the name of a digital elevation model (DEM) (`--dem`) and an azimuth (i.e. a wind direction). The
+/// Z Conversion Factor (`--zfactor`) is only important when the vertical and horizontal units are not the same in the DEM.
+/// When this is the case, the algorithm will multiply each elevation in the DEM by the Z Conversion Factor.
+///
+/// # Reference
+/// Böhner, J., and Antonić, O. (2009). Land-surface parameters specific to topo-climatology. Developments in Soil
+/// Science, 33, 195-226.
+///
+/// # See Also
+/// `Aspect`
pub struct RelativeAspect {
name: String,
description: String,
diff --git a/src/tools/terrain_analysis/relative_topographic_position.rs b/src/tools/terrain_analysis/relative_topographic_position.rs
index c85e560ee..f183dff33 100644
--- a/src/tools/terrain_analysis/relative_topographic_position.rs
+++ b/src/tools/terrain_analysis/relative_topographic_position.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: June 6, 2017
+Created: 06/06/2017
Last Modified: 12/10/2018
License: MIT
*/
@@ -18,6 +18,35 @@ use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
+/// Relative topographic position (RTP) is an index of local topographic position (i.e. how
+/// elevated or low-lying a site is relative to its surroundings) and is a modification of percent
+/// elevation range (PER; `PercentElevRange`) and accounts for the elevation distribution. Rather than
+/// positioning the central cell's elevation solely between the filter extrema, RTP is a piece-wise
+/// function that positions the central elevation relative to the minimum (zmin), mean (μ),
+/// and maximum values (zmax), within a local neighbourhood of a user-specified size (`--filterx`,
+/// `--filtery`), such that:
+///
+/// > RTP = (z0 − μ) / (μ − zmin), if z0 < μ
+/// >
+/// > OR
+/// >
+/// > RTP = (z0 − μ) / (zmax - μ), if z0 >= μ
+///
+///
+/// The resulting index is bound by the interval [−1, 1], where the sign indicates if the cell is above or below
+/// than the filter mean. Although RTP uses the mean to define two linear functions, the reliance on the filter
+/// extrema is expected to result in sensitivity to outliers. Furthermore, the use of the mean implies assumptions
+/// of unimodal and symmetrical elevation distribution.
+///
+/// In many cases, Elevation Percentile (`ElevPercentile`) and deviation from mean elevation (`DevFromMeanElev`)
+/// provide more suitable and robust measures of relative topographic position.
+///
+/// # Reference
+/// Newman, D. R., Lindsay, J. B., and Cockburn, J. M. H. (2018). Evaluating metrics of local topographic
+/// position for multiscale geomorphometric analysis. Geomorphology, 312, 40-50.
+///
+/// # See Also
+/// `DevFromMeanElev`, `DiffFromMeanElev`, `ElevPercentile`, `PercentElevRange`
pub struct RelativeTopographicPosition {
name: String,
description: String,
diff --git a/whitebox_plugin_generator.py b/whitebox_plugin_generator.py
index 3b465397c..f3a480e1a 100644
--- a/whitebox_plugin_generator.py
+++ b/whitebox_plugin_generator.py
@@ -123,8 +123,7 @@ def camel_to_snake(s):
# fn_def = fn_def.rstrip(', ')
fn_def += "callback=None):"
- doc_str += "{}callback -- Custom function for handling tool text outputs.".format(
- st_val)
+ doc_str += " callback -- Custom function for handling tool text outputs."
fn = """
{}
diff --git a/whitebox_tools.py b/whitebox_tools.py
index 540859916..f0a005d93 100644
--- a/whitebox_tools.py
+++ b/whitebox_tools.py
@@ -366,6 +366,8 @@ def list_tools(self, keywords=[]):
+
+
##############
# Data Tools #
##############
@@ -376,7 +378,7 @@ def add_point_coordinates_to_table(self, i, callback=None):
Keyword arguments:
i -- Input vector Points file.
-callback -- Custom function for handling tool text outputs.
+ callback -- Custom function for handling tool text outputs.
"""
args = []
args.append("--input='{}'".format(i))
@@ -548,7 +550,7 @@ def print_geo_tiff_tags(self, i, callback=None):
Keyword arguments:
i -- Input GeoTIFF file.
-callback -- Custom function for handling tool text outputs.
+ callback -- Custom function for handling tool text outputs.
"""
args = []
args.append("--input='{}'".format(i))
@@ -588,7 +590,7 @@ def reinitialize_attribute_table(self, i, callback=None):
Keyword arguments:
i -- Input vector file.
-callback -- Custom function for handling tool text outputs.
+ callback -- Custom function for handling tool text outputs.
"""
args = []
args.append("--input='{}'".format(i))
@@ -966,18 +968,20 @@ def extract_nodes(self, i, output, callback=None):
args.append("--output='{}'".format(output))
return self.run_tool('extract_nodes', args, callback) # returns 1 if error
- def extract_raster_values_at_points(self, inputs, points, callback=None):
+ def extract_raster_values_at_points(self, inputs, points, out_text=False, callback=None):
"""Extracts the values of raster(s) at vector point locations.
Keyword arguments:
inputs -- Input raster files.
points -- Input vector points file.
+ out_text -- Output point values as text? Otherwise, the only output is to to the points file's attribute table.
callback -- Custom function for handling tool text outputs.
"""
args = []
args.append("--inputs='{}'".format(inputs))
args.append("--points='{}'".format(points))
+ if out_text: args.append("--out_text")
return self.run_tool('extract_raster_values_at_points', args, callback) # returns 1 if error
def find_lowest_or_highest_points(self, i, output, out_type="lowest", callback=None):
@@ -1148,7 +1152,7 @@ def polygon_area(self, i, callback=None):
Keyword arguments:
i -- Input vector polygon file.
-callback -- Custom function for handling tool text outputs.
+ callback -- Custom function for handling tool text outputs.
"""
args = []
args.append("--input='{}'".format(i))
@@ -1174,7 +1178,7 @@ def polygon_perimeter(self, i, callback=None):
Keyword arguments:
i -- Input vector polygon file.
-callback -- Custom function for handling tool text outputs.
+ callback -- Custom function for handling tool text outputs.
"""
args = []
args.append("--input='{}'".format(i))
@@ -1860,7 +1864,7 @@ def compactness_ratio(self, i, callback=None):
Keyword arguments:
i -- Input vector polygon file.
-callback -- Custom function for handling tool text outputs.
+ callback -- Custom function for handling tool text outputs.
"""
args = []
args.append("--input='{}'".format(i))
@@ -1888,7 +1892,7 @@ def elongation_ratio(self, i, callback=None):
Keyword arguments:
i -- Input vector polygon file.
-callback -- Custom function for handling tool text outputs.
+ callback -- Custom function for handling tool text outputs.
"""
args = []
args.append("--input='{}'".format(i))
@@ -1914,7 +1918,7 @@ def hole_proportion(self, i, callback=None):
Keyword arguments:
i -- Input vector polygon file.
-callback -- Custom function for handling tool text outputs.
+ callback -- Custom function for handling tool text outputs.
"""
args = []
args.append("--input='{}'".format(i))
@@ -1926,7 +1930,7 @@ def linearity_index(self, i, callback=None):
Keyword arguments:
i -- Input vector polygon file.
-callback -- Custom function for handling tool text outputs.
+ callback -- Custom function for handling tool text outputs.
"""
args = []
args.append("--input='{}'".format(i))
@@ -1938,7 +1942,7 @@ def patch_orientation(self, i, callback=None):
Keyword arguments:
i -- Input vector polygon file.
-callback -- Custom function for handling tool text outputs.
+ callback -- Custom function for handling tool text outputs.
"""
args = []
args.append("--input='{}'".format(i))
@@ -1950,7 +1954,7 @@ def perimeter_area_ratio(self, i, callback=None):
Keyword arguments:
i -- Input vector polygon file.
-callback -- Custom function for handling tool text outputs.
+ callback -- Custom function for handling tool text outputs.
"""
args = []
args.append("--input='{}'".format(i))
@@ -1978,7 +1982,7 @@ def related_circumscribing_circle(self, i, callback=None):
Keyword arguments:
i -- Input vector polygon file.
-callback -- Custom function for handling tool text outputs.
+ callback -- Custom function for handling tool text outputs.
"""
args = []
args.append("--input='{}'".format(i))
@@ -1990,7 +1994,7 @@ def shape_complexity_index(self, i, callback=None):
Keyword arguments:
i -- Input vector polygon file.
-callback -- Custom function for handling tool text outputs.
+ callback -- Custom function for handling tool text outputs.
"""
args = []
args.append("--input='{}'".format(i))
@@ -2104,7 +2108,7 @@ def downslope_index(self, dem, output, drop=2.0, out_type="tangent", callback=No
args.append("--out_type={}".format(out_type))
return self.run_tool('downslope_index', args, callback) # returns 1 if error
- def drainage_preserving_smoothing(self, dem, output, filter=11, norm_diff=15.0, num_iter=10, max_diff=2.0, reduction=80.0, dfm=0.15, zfactor=1.0, callback=None):
+ def drainage_preserving_smoothing(self, dem, output, filter=11, norm_diff=15.0, num_iter=3, max_diff=0.5, reduction=80.0, dfm=0.15, zfactor=1.0, callback=None):
"""Reduces short-scale variation in an input DEM while preserving breaks-in-slope and small drainage features using a modified Sun et al. (2007) algorithm.
Keyword arguments:
@@ -2216,7 +2220,7 @@ def elev_relative_to_watershed_min_max(self, dem, watersheds, output, callback=N
args.append("--output='{}'".format(output))
return self.run_tool('elev_relative_to_watershed_min_max', args, callback) # returns 1 if error
- def feature_preserving_denoise(self, dem, output, filter=11, norm_diff=15.0, num_iter=10, max_diff=2.0, zfactor=1.0, callback=None):
+ def feature_preserving_denoise(self, dem, output, filter=11, norm_diff=15.0, num_iter=3, max_diff=0.5, zfactor=1.0, callback=None):
"""Reduces short-scale variation in an input DEM using a modified Sun et al. (2007) algorithm.
Keyword arguments:
@@ -3418,7 +3422,7 @@ def hillslopes(self, d8_pntr, streams, output, esri_pntr=False, callback=None):
if esri_pntr: args.append("--esri_pntr")
return self.run_tool('hillslopes', args, callback) # returns 1 if error
- def impoundment_index(self, dem, output, damlength, out_type="depth", callback=None):
+ def impoundment_size_index(self, dem, output, damlength, out_type="depth", callback=None):
"""Calculates the impoundment size resulting from damming a DEM.
Keyword arguments:
@@ -3434,7 +3438,7 @@ def impoundment_index(self, dem, output, damlength, out_type="depth", callback=N
args.append("--output='{}'".format(output))
args.append("--out_type={}".format(out_type))
args.append("--damlength='{}'".format(damlength))
- return self.run_tool('impoundment_index', args, callback) # returns 1 if error
+ return self.run_tool('impoundment_size_index', args, callback) # returns 1 if error
def isobasins(self, dem, output, size, callback=None):
"""Divides a landscape into nearly equal sized drainage basins (i.e. watersheds).
@@ -3969,7 +3973,7 @@ def opening(self, i, output, filterx=11, filtery=11, callback=None):
return self.run_tool('opening', args, callback) # returns 1 if error
def remove_spurs(self, i, output, iterations=10, callback=None):
- """Removes the spurs (pruning operation) from a Boolean line image.; intended to be used on the output of the LineThinning tool.
+ """Removes the spurs (pruning operation) from a Boolean line image; intended to be used on the output of the LineThinning tool.
Keyword arguments:
@@ -4753,7 +4757,7 @@ def direct_decorrelation_stretch(self, i, output, k=0.5, clip=1.0, callback=None
return self.run_tool('direct_decorrelation_stretch', args, callback) # returns 1 if error
def gamma_correction(self, i, output, gamma=0.5, callback=None):
- """Performs a sigmoidal contrast stretch on input images.
+ """Performs a gamma correction on an input images.
Keyword arguments:
@@ -4876,7 +4880,7 @@ def panchromatic_sharpening(self, pan, output, red=None, green=None, blue=None,
args.append("--method={}".format(method))
return self.run_tool('panchromatic_sharpening', args, callback) # returns 1 if error
- def percentage_contrast_stretch(self, i, output, clip=0.0, tail="both", num_tones=256, callback=None):
+ def percentage_contrast_stretch(self, i, output, clip=1.0, tail="both", num_tones=256, callback=None):
"""Performs a percentage linear contrast stretch on input images.
Keyword arguments:
@@ -5040,7 +5044,7 @@ def las_to_ascii(self, inputs, callback=None):
Keyword arguments:
inputs -- Input LiDAR files.
-callback -- Custom function for handling tool text outputs.
+ callback -- Custom function for handling tool text outputs.
"""
args = []
args.append("--inputs='{}'".format(inputs))
@@ -5052,7 +5056,7 @@ def las_to_multipoint_shapefile(self, i=None, callback=None):
Keyword arguments:
i -- Input LiDAR file.
-callback -- Custom function for handling tool text outputs.
+ callback -- Custom function for handling tool text outputs.
"""
args = []
if i is not None: args.append("--input='{}'".format(i))
@@ -5064,7 +5068,7 @@ def las_to_shapefile(self, i=None, callback=None):
Keyword arguments:
i -- Input LiDAR file.
-callback -- Custom function for handling tool text outputs.
+ callback -- Custom function for handling tool text outputs.
"""
args = []
if i is not None: args.append("--input='{}'".format(i))
@@ -5542,14 +5546,14 @@ def lidar_thin_high_density(self, i, output, density, resolution=1.0, save_filte
if save_filtered: args.append("--save_filtered")
return self.run_tool('lidar_thin_high_density', args, callback) # returns 1 if error
- def lidar_tile(self, i, width_x=1000.0, width_y=1000.0, origin_x=0.0, origin_y=0.0, min_points=2, callback=None):
+ def lidar_tile(self, i, width=1000.0, height=1000.0, origin_x=0.0, origin_y=0.0, min_points=2, callback=None):
"""Tiles a LiDAR LAS file into multiple LAS files.
Keyword arguments:
i -- Input LiDAR file.
- width_x -- Width of tiles in the X dimension; default 1000.0.
- width_y -- Width of tiles in the Y dimension.
+ width -- Width of tiles in the X dimension; default 1000.0.
+ height -- Height of tiles in the Y dimension.
origin_x -- Origin point X coordinate for tile grid.
origin_y -- Origin point Y coordinate for tile grid.
min_points -- Minimum number of points contained in a tile for it to be saved.
@@ -5557,8 +5561,8 @@ def lidar_tile(self, i, width_x=1000.0, width_y=1000.0, origin_x=0.0, origin_y=0
"""
args = []
args.append("--input='{}'".format(i))
- args.append("--width_x={}".format(width_x))
- args.append("--width_y={}".format(width_y))
+ args.append("--width={}".format(width))
+ args.append("--height={}".format(height))
args.append("--origin_x={}".format(origin_x))
args.append("--origin_y={}".format(origin_y))
args.append("--min_points={}".format(min_points))
@@ -6438,20 +6442,20 @@ def power(self, input1, input2, output, callback=None):
args.append("--output='{}'".format(output))
return self.run_tool('power', args, callback) # returns 1 if error
- def principal_component_analysis(self, inputs, out_html, num_comp=None, standardized=False, callback=None):
+ def principal_component_analysis(self, inputs, output, num_comp=None, standardized=False, callback=None):
"""Performs a principal component analysis (PCA) on a multi-spectral dataset.
Keyword arguments:
inputs -- Input raster files.
- out_html -- Output HTML report file.
+ output -- Output HTML report file.
num_comp -- Number of component images to output; <= to num. input images.
standardized -- Perform standardized PCA?.
callback -- Custom function for handling tool text outputs.
"""
args = []
args.append("--inputs='{}'".format(inputs))
- args.append("--out_html='{}'".format(out_html))
+ args.append("--output='{}'".format(output))
if num_comp is not None: args.append("--num_comp='{}'".format(num_comp))
if standardized: args.append("--standardized")
return self.run_tool('principal_component_analysis', args, callback) # returns 1 if error
@@ -6522,7 +6526,7 @@ def raster_summary_stats(self, i, callback=None):
Keyword arguments:
i -- Input raster file.
-callback -- Custom function for handling tool text outputs.
+ callback -- Custom function for handling tool text outputs.
"""
args = []
args.append("--input='{}'".format(i))