use connectivity_core::ndarray::{Array2, Array3, Zip};
use connectivity_core::{connectivity, level_transforms, pixel_coverage, Config, Inputs};
use std::error::Error;
fn summary(name: &str, values: &Array2<f32>) {
let finite: Vec<f32> = values.iter().copied().filter(|v| v.is_finite()).collect();
let mean = finite.iter().map(|&v| v as f64).sum::<f64>() / finite.len().max(1) as f64;
let max = finite.iter().copied().fold(f32::NEG_INFINITY, f32::max);
println!("{name:24} {} cells evaluated, mean {mean:.4}, max {max:.4}", finite.len());
}
fn main() -> Result<(), Box<dyn Error>> {
let (rows, cols, offsets) = (96, 128, (40, 24));
let base = (100.0, 0.0, 400_000.0, 0.0, -100.0, 6_100_000.0);
let levels = [1, 2, 4, 8];
let transforms = level_transforms(base, &levels, offsets);
let condition = Array2::from_shape_fn((rows, cols), |(i, j)| {
let (y, x) = (i as f32 - 48.0, j as f32 - 64.0);
if x * x + y * y < 120.0 {
f32::NAN
} else {
(0.5 + 0.5 * (i as f32 / 9.0).sin() * (j as f32 / 13.0).cos()).clamp(0.0, 1.0)
}
});
let mask = condition.mapv(f32::is_nan);
let inputs = Inputs { condition: &condition, mask: &mask, traversal: None, transgrids: None, transforms: &transforms };
let config = Config { levels: &levels, lambdas: &[2.0, 20.0], outer_window: 5, offsets, ..Config::default() };
let connectedness = connectivity(&inputs, &config)?;
summary("connectedness", &connectedness);
let mut habitat = connectedness.clone();
Zip::from(&mut habitat).and(&condition).for_each(|h, &c| *h = (c * *h).sqrt());
summary("connected habitat", &habitat);
let traversal = Array2::from_shape_fn((rows, cols), |(i, j)| if i == 70 || i == 71 { 0.05 } else { condition[[i, j]] });
let with_road = connectivity(&Inputs { traversal: Some(&traversal), ..inputs }, &config)?;
summary("connectedness, road", &with_road);
let tile = transforms[&1];
let (x0, y0) = (tile.x_origin, tile.y_origin);
let square = |a: f64, b: f64, c: f64, d: f64| vec![(x0 + a, y0 - b), (x0 + c, y0 - b), (x0 + c, y0 - d), (x0 + a, y0 - d)];
let reserve = vec![(square(500.0, 300.0, 4_050.0, 3_520.0), vec![square(1_500.0, 1_000.0, 2_500.0, 2_000.0)])];
let pa = pixel_coverage(&reserve, &tile, rows, cols);
let parc_condition = Zip::from(&condition).and(&pa).map_collect(|&c, &p| if c.is_nan() { c } else { c.max(p) });
let parc_mask = Zip::from(&parc_condition).and(&pa).map_collect(|&c, &p| !(p > 0.0 && c.is_finite()));
let parc_inputs = Inputs { condition: &parc_condition, mask: &parc_mask, ..inputs };
summary("PARC", &connectivity(&parc_inputs, &config)?);
summary("PARC, pa_to_pa", &connectivity(&parc_inputs, &Config { pa_to_pa: true, ..config })?);
let climate = |shift: f32| {
Array3::from_shape_fn((rows, cols, 3), |(i, j, b)| (i as f32 / 40.0 + j as f32 / 60.0) * (b + 1) as f32 * 0.1 + shift)
};
let scenarios = [climate(0.0), climate(0.05), climate(0.15)];
summary("BERI", &connectivity(&Inputs { transgrids: Some(&scenarios), ..inputs }, &config)?);
Ok(())
}