use ndarray::{Array2, ArrayView2, FoldWhile, Zip, s};
use crate::{Warper, WarperError};
impl Warper {
#[inline]
fn validate_source_raster_shape(
&self,
source_raster: &ArrayView2<f64>,
) -> Result<(), WarperError> {
if source_raster.dim().0 != self.source_shape.0
|| source_raster.dim().1 != self.source_shape.1
{
return Err(WarperError::InvalidRasterDimensions);
}
Ok(())
}
pub fn warp_unchecked<'a, A: Into<ArrayView2<'a, f64>>>(
&self,
source_raster: A,
) -> Array2<f64> {
let source_raster: ArrayView2<f64> = source_raster.into();
self.internals.map(|intr| {
let values = source_raster.slice(s![
(intr.anchor_idx.1 - 1)..(intr.anchor_idx.1 + 3),
(intr.anchor_idx.0 - 1)..(intr.anchor_idx.0 + 3)
]);
let mut weight_accum = 0.0;
let mut result_accum = 0.0;
for j in 0..4 {
let mut inner_weight_accum = 0.0;
let mut inner_result_accum = 0.0;
for i in 0..4 {
let value = values[[j, i]];
let x_weight = intr.x_weights[i];
inner_weight_accum += x_weight;
inner_result_accum += x_weight * value;
}
let y_weight = intr.y_weights[j];
weight_accum += inner_weight_accum * y_weight;
result_accum += inner_result_accum * y_weight;
}
result_accum / weight_accum
})
}
pub fn warp_ignore_nodata<'a, A: Into<ArrayView2<'a, f64>>>(
&self,
source_raster: A,
) -> Result<Array2<f64>, WarperError> {
let source_raster: ArrayView2<f64> = source_raster.into();
self.validate_source_raster_shape(&source_raster)?;
let mut target_raster = Array2::from_elem(self.internals.raw_dim(), f64::NEG_INFINITY);
Zip::from(&mut target_raster)
.and(&self.internals)
.fold_while(Ok(()), |_, v, intr| {
let values = source_raster.slice(s![
(intr.anchor_idx.1 - 1)..(intr.anchor_idx.1 + 3),
(intr.anchor_idx.0 - 1)..(intr.anchor_idx.0 + 3)
]);
let mut weight_accum = 0.0;
let mut result_accum = 0.0;
for j in 0..4 {
let mut inner_weight_accum = 0.0;
let mut inner_result_accum = 0.0;
for i in 0..4 {
let value = values[[j, i]];
if !value.is_nan() {
let x_weight = intr.x_weights[i];
inner_weight_accum += x_weight;
inner_result_accum += x_weight * value;
}
}
let y_weight = intr.y_weights[j];
weight_accum += inner_weight_accum * y_weight;
result_accum += inner_result_accum * y_weight;
}
if (weight_accum).abs() < f64::EPSILON {
*v = f64::NAN;
return FoldWhile::Continue(Ok(()));
}
let result = result_accum / weight_accum;
if result.is_finite() {
*v = result;
FoldWhile::Continue(Ok(()))
} else {
FoldWhile::Done(Err(WarperError::WarpingError))
}
})
.into_inner()?;
Ok(target_raster)
}
pub fn warp_reject_nodata<'a, A: Into<ArrayView2<'a, f64>>>(
&self,
source_raster: A,
) -> Result<Array2<f64>, WarperError> {
let source_raster: ArrayView2<f64> = source_raster.into();
self.validate_source_raster_shape(&source_raster)?;
let mut target_raster = Array2::from_elem(self.internals.raw_dim(), f64::NEG_INFINITY);
Zip::from(&mut target_raster)
.and(&self.internals)
.fold_while(Ok(()), |_, v, intr| {
let values = source_raster.slice(s![
(intr.anchor_idx.1 - 1)..(intr.anchor_idx.1 + 3),
(intr.anchor_idx.0 - 1)..(intr.anchor_idx.0 + 3)
]);
let mut weight_accum = 0.0;
let mut result_accum = 0.0;
for j in 0..4 {
let mut inner_weight_accum = 0.0;
let mut inner_result_accum = 0.0;
for i in 0..4 {
let value = values[[j, i]];
if value.is_nan() {
return FoldWhile::Done(Err(WarperError::NanError));
}
let x_weight = intr.x_weights[i];
inner_weight_accum += x_weight;
inner_result_accum += x_weight * value;
}
let y_weight = intr.y_weights[j];
weight_accum += inner_weight_accum * y_weight;
result_accum += inner_result_accum * y_weight;
}
let result = result_accum / weight_accum;
if result.is_finite() {
*v = result;
FoldWhile::Continue(Ok(()))
} else {
FoldWhile::Done(Err(WarperError::WarpingError))
}
})
.into_inner()?;
Ok(target_raster)
}
pub fn warp_discard_nodata<'a, A: Into<ArrayView2<'a, f64>>>(
&self,
source_raster: A,
) -> Result<Array2<f64>, WarperError> {
let source_raster: ArrayView2<f64> = source_raster.into();
self.validate_source_raster_shape(&source_raster)?;
let mut target_raster = Array2::from_elem(self.internals.raw_dim(), f64::NEG_INFINITY);
Zip::from(&mut target_raster)
.and(&self.internals)
.fold_while(Ok(()), |_, v, intr| {
let values = source_raster.slice(s![
(intr.anchor_idx.1 - 1)..(intr.anchor_idx.1 + 3),
(intr.anchor_idx.0 - 1)..(intr.anchor_idx.0 + 3)
]);
let mut weight_accum = 0.0;
let mut result_accum = 0.0;
for j in 0..4 {
let mut inner_weight_accum = 0.0;
let mut inner_result_accum = 0.0;
for i in 0..4 {
let value = values[[j, i]];
if value.is_nan() {
*v = f64::NAN;
return FoldWhile::Continue(Ok(()));
}
let x_weight = intr.x_weights[i];
inner_weight_accum += x_weight;
inner_result_accum += x_weight * value;
}
let y_weight = intr.y_weights[j];
weight_accum += inner_weight_accum * y_weight;
result_accum += inner_result_accum * y_weight;
}
let result = result_accum / weight_accum;
if result.is_finite() {
*v = result;
FoldWhile::Continue(Ok(()))
} else {
FoldWhile::Done(Err(WarperError::WarpingError))
}
})
.into_inner()?;
Ok(target_raster)
}
}
#[cfg(feature = "multithreading")]
#[cfg_attr(docsrs, doc(cfg(feature = "multithreading")))]
impl Warper {
pub fn warp_unchecked_parallel<'a, A: Into<ArrayView2<'a, f64>>>(
&self,
source_raster: A,
) -> Array2<f64> {
let source_raster: ArrayView2<f64> = source_raster.into();
Zip::from(&self.internals).par_map_collect(|intr| {
let values = source_raster.slice(s![
(intr.anchor_idx.1 - 1)..(intr.anchor_idx.1 + 3),
(intr.anchor_idx.0 - 1)..(intr.anchor_idx.0 + 3)
]);
let mut weight_accum = 0.0;
let mut result_accum = 0.0;
for j in 0..4 {
let mut inner_weight_accum = 0.0;
let mut inner_result_accum = 0.0;
for i in 0..4 {
let value = values[[j, i]];
let x_weight = intr.x_weights[i];
inner_weight_accum += x_weight;
inner_result_accum += x_weight * value;
}
let y_weight = intr.y_weights[j];
weight_accum += inner_weight_accum * y_weight;
result_accum += inner_result_accum * y_weight;
}
result_accum / weight_accum
})
}
#[cfg(feature = "multithreading")]
#[cfg_attr(docsrs, doc(cfg(feature = "multithreading")))]
pub fn warp_ignore_nodata_parallel<'a, A: Into<ArrayView2<'a, f64>>>(
&self,
source_raster: A,
) -> Result<Array2<f64>, WarperError> {
use ndarray::parallel::prelude::{IntoParallelIterator, ParallelIterator};
let source_raster: ArrayView2<f64> = source_raster.into();
self.validate_source_raster_shape(&source_raster)?;
let mut target_raster = Array2::from_elem(self.internals.raw_dim(), f64::NEG_INFINITY);
Zip::from(&mut target_raster)
.and(&self.internals)
.into_par_iter()
.try_for_each(|(v, intr)| {
let values = source_raster.slice(s![
(intr.anchor_idx.1 - 1)..(intr.anchor_idx.1 + 3),
(intr.anchor_idx.0 - 1)..(intr.anchor_idx.0 + 3)
]);
let mut weight_accum = 0.0;
let mut result_accum = 0.0;
for j in 0..4 {
let mut inner_weight_accum = 0.0;
let mut inner_result_accum = 0.0;
for i in 0..4 {
let value = values[[j, i]];
if !value.is_nan() {
let x_weight = intr.x_weights[i];
inner_weight_accum += x_weight;
inner_result_accum += x_weight * value;
}
}
let y_weight = intr.y_weights[j];
weight_accum += inner_weight_accum * y_weight;
result_accum += inner_result_accum * y_weight;
}
if (weight_accum).abs() < f64::EPSILON {
*v = f64::NAN;
return Ok(());
}
let result = result_accum / weight_accum;
if result.is_finite() {
*v = result;
Ok(())
} else {
Err(WarperError::WarpingError)
}
})?;
Ok(target_raster)
}
#[cfg(feature = "multithreading")]
#[cfg_attr(docsrs, doc(cfg(feature = "multithreading")))]
pub fn warp_reject_nodata_parallel<'a, A: Into<ArrayView2<'a, f64>>>(
&self,
source_raster: A,
) -> Result<Array2<f64>, WarperError> {
use ndarray::parallel::prelude::{IntoParallelIterator, ParallelIterator};
let source_raster: ArrayView2<f64> = source_raster.into();
self.validate_source_raster_shape(&source_raster)?;
Zip::from(&source_raster)
.into_par_iter()
.try_for_each(|(v,)| {
if v.is_nan() {
Err(WarperError::NanError)
} else {
Ok(())
}
})?;
let target_raster = self.warp_unchecked_parallel(source_raster);
Zip::from(&target_raster)
.into_par_iter()
.try_for_each(|(v,)| {
if v.is_finite() {
Ok(())
} else {
Err(WarperError::WarpingError)
}
})?;
Ok(target_raster)
}
#[cfg(feature = "multithreading")]
#[cfg_attr(docsrs, doc(cfg(feature = "multithreading")))]
pub fn warp_discard_nodata_parallel<'a, A: Into<ArrayView2<'a, f64>>>(
&self,
source_raster: A,
) -> Result<Array2<f64>, WarperError> {
use ndarray::parallel::prelude::{IntoParallelIterator, ParallelIterator};
let source_raster: ArrayView2<f64> = source_raster.into();
self.validate_source_raster_shape(&source_raster)?;
let mut target_raster = Array2::from_elem(self.internals.raw_dim(), f64::NEG_INFINITY);
Zip::from(&mut target_raster)
.and(&self.internals)
.into_par_iter()
.try_for_each(|(v, intr)| {
let values = source_raster.slice(s![
(intr.anchor_idx.1 - 1)..(intr.anchor_idx.1 + 3),
(intr.anchor_idx.0 - 1)..(intr.anchor_idx.0 + 3)
]);
let mut weight_accum = 0.0;
let mut result_accum = 0.0;
for j in 0..4 {
let mut inner_weight_accum = 0.0;
let mut inner_result_accum = 0.0;
for i in 0..4 {
let value = values[[j, i]];
if value.is_nan() {
*v = f64::NAN;
return Ok(());
}
let x_weight = intr.x_weights[i];
inner_weight_accum += x_weight;
inner_result_accum += x_weight * value;
}
let y_weight = intr.y_weights[j];
weight_accum += inner_weight_accum * y_weight;
result_accum += inner_result_accum * y_weight;
}
let result = result_accum / weight_accum;
if result.is_finite() {
*v = result;
Ok(())
} else {
Err(WarperError::WarpingError)
}
})?;
Ok(target_raster)
}
}