Skip to content

Commit

Permalink
Merge pull request #8 from hakolao/feature-f32
Browse files Browse the repository at this point in the history
Feature f32
  • Loading branch information
mthh authored Oct 6, 2023
2 parents 0dcef6a + e6fdde7 commit eec087b
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 89 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ serde_json = "^1.0"

[features]
geojson = ["dep:geojson", "dep:serde_json"]
f32 = []

[package.metadata.docs.rs]
all-features = true
Expand Down
28 changes: 23 additions & 5 deletions examples/ex.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
use contour::ContourBuilder;
use contour::{ContourBuilder, Float};
use geojson::{FeatureCollection, GeoJson};
use std::fs::File;
use std::io::{BufWriter, Write};

fn main() {
let pot_pop_fr = include_str!("../tests/fixtures/pot_pop_fr.json");
let raw_data: serde_json::Value = serde_json::from_str(pot_pop_fr).unwrap();
let matrix: Vec<f64> = raw_data["data"]
let matrix: Vec<Float> = raw_data["data"]
.as_array()
.unwrap()
.iter()
.map(|x| x.as_f64().unwrap())
.map(|x| {
#[cfg(not(feature = "f32"))]
{
x.as_f64().unwrap()
}
#[cfg(feature = "f32")]
{
x.as_f64().unwrap() as f32
}
})
.collect();
let h = raw_data["height"].as_u64().unwrap() as u32;
let w = raw_data["width"].as_u64().unwrap() as u32;
Expand Down Expand Up @@ -51,11 +60,20 @@ fn main() {

let volcano = include_str!("../tests/fixtures/volcano.json");
let raw_data: serde_json::Value = serde_json::from_str(volcano).unwrap();
let matrix: Vec<f64> = raw_data["data"]
let matrix: Vec<Float> = raw_data["data"]
.as_array()
.unwrap()
.iter()
.map(|x| x.as_f64().unwrap())
.map(|x| {
#[cfg(not(feature = "f32"))]
{
x.as_f64().unwrap()
}
#[cfg(feature = "f32")]
{
x.as_f64().unwrap() as f32
}
})
.collect();
let h = raw_data["height"].as_u64().unwrap() as u32;
let w = raw_data["width"].as_u64().unwrap() as u32;
Expand Down
10 changes: 5 additions & 5 deletions src/area.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::Pt;
use crate::{Float, Pt};

pub fn area(ring: &[Pt]) -> f64 {
pub fn area(ring: &[Pt]) -> Float {
let mut i = 0;
let n = ring.len() - 1;
let mut area = ring[n - 1].y * ring[0].x - ring[n - 1].x * ring[0].y;
Expand Down Expand Up @@ -51,7 +51,7 @@ fn ring_contains(ring: &[Pt], point: &Pt) -> i32 {

fn segment_contains(a: &Pt, b: &Pt, c: &Pt) -> bool {
if collinear(a, b, c) {
if (a.x - b.x).abs() < std::f64::EPSILON {
if (a.x - b.x).abs() < Float::EPSILON {
within(a.y, c.y, b.y)
} else {
within(a.x, c.x, b.x)
Expand All @@ -62,9 +62,9 @@ fn segment_contains(a: &Pt, b: &Pt, c: &Pt) -> bool {
}

fn collinear(a: &Pt, b: &Pt, c: &Pt) -> bool {
((b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y)).abs() < std::f64::EPSILON
((b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y)).abs() < Float::EPSILON
}

fn within(p: f64, q: f64, r: f64) -> bool {
fn within(p: Float, q: Float, r: Float) -> bool {
p <= q && q <= r || r <= q && q <= p
}
15 changes: 8 additions & 7 deletions src/band.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@
use crate::Float;
use geo_types::MultiPolygon;

/// An isoband has the geometry and min / max values of a contour ring, built by [`ContourBuilder`].
#[derive(Debug, Clone)]
pub struct Band {
pub(crate) geometry: MultiPolygon,
pub(crate) min_v: f64,
pub(crate) max_v: f64,
pub(crate) geometry: MultiPolygon<Float>,
pub(crate) min_v: Float,
pub(crate) max_v: Float,
}

impl Band {
/// Borrow the [`MultiPolygon`](geo_types::MultiPolygon) geometry of this contour.
pub fn geometry(&self) -> &MultiPolygon {
pub fn geometry(&self) -> &MultiPolygon<Float> {
&self.geometry
}

/// Get the owned polygons and thresholds (min and max) of this band.
pub fn into_inner(self) -> (MultiPolygon, f64, f64) {
pub fn into_inner(self) -> (MultiPolygon<Float>, Float, Float) {
(self.geometry, self.min_v, self.max_v)
}

/// Get the minimum value used to construct this band.
pub fn min_v(&self) -> f64 {
pub fn min_v(&self) -> Float {
self.min_v
}

/// Get the maximum value used to construct this band.
pub fn max_v(&self) -> f64 {
pub fn max_v(&self) -> Float {
self.max_v
}

Expand Down
11 changes: 6 additions & 5 deletions src/contour.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
use crate::Float;
use geo_types::MultiPolygon;

/// A contour has the geometry and threshold of a contour ring, built by [`ContourBuilder`].
#[derive(Debug, Clone)]
pub struct Contour {
pub(crate) geometry: MultiPolygon,
pub(crate) threshold: f64,
pub(crate) geometry: MultiPolygon<Float>,
pub(crate) threshold: Float,
}

impl Contour {
/// Borrow the [`MultiPolygon`](geo_types::MultiPolygon) geometry of this contour.
pub fn geometry(&self) -> &MultiPolygon {
pub fn geometry(&self) -> &MultiPolygon<Float> {
&self.geometry
}

/// Get the owned polygons and threshold of this contour.
pub fn into_inner(self) -> (MultiPolygon, f64) {
pub fn into_inner(self) -> (MultiPolygon<Float>, Float) {
(self.geometry, self.threshold)
}

/// Get the threshold used to construct this contour.
pub fn threshold(&self) -> f64 {
pub fn threshold(&self) -> Float {
self.threshold
}

Expand Down
77 changes: 41 additions & 36 deletions src/contourbuilder.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::area::{area, contains};
use crate::error::{new_error, ErrorKind, Result};
use crate::isoringbuilder::IsoRingBuilder;
use crate::{Band, Contour, Line, Ring};
use crate::{Band, Contour, Float, Line, Ring};
use geo_types::{LineString, MultiLineString, MultiPolygon, Polygon};
use rustc_hash::FxHashMap;

Expand All @@ -18,13 +18,13 @@ pub struct ContourBuilder {
/// Whether to smooth the contours
smooth: bool,
/// The horizontal coordinate for the origin of the grid.
x_origin: f64,
x_origin: Float,
/// The vertical coordinate for the origin of the grid.
y_origin: f64,
y_origin: Float,
/// The horizontal step for the grid
x_step: f64,
x_step: Float,
/// The vertical step for the grid
y_step: f64,
y_step: Float,
}

impl ContourBuilder {
Expand All @@ -43,38 +43,38 @@ impl ContourBuilder {
dx,
dy,
smooth,
x_origin: 0f64,
y_origin: 0f64,
x_step: 1f64,
y_step: 1f64,
x_origin: 0.,
y_origin: 0.,
x_step: 1.,
y_step: 1.,
}
}

/// Sets the x origin of the grid.
pub fn x_origin(mut self, x_origin: impl Into<f64>) -> Self {
pub fn x_origin(mut self, x_origin: impl Into<Float>) -> Self {
self.x_origin = x_origin.into();
self
}

/// Sets the y origin of the grid.
pub fn y_origin(mut self, y_origin: impl Into<f64>) -> Self {
pub fn y_origin(mut self, y_origin: impl Into<Float>) -> Self {
self.y_origin = y_origin.into();
self
}

/// Sets the x step of the grid.
pub fn x_step(mut self, x_step: impl Into<f64>) -> Self {
pub fn x_step(mut self, x_step: impl Into<Float>) -> Self {
self.x_step = x_step.into();
self
}

/// Sets the y step of the grid.
pub fn y_step(mut self, y_step: impl Into<f64>) -> Self {
pub fn y_step(mut self, y_step: impl Into<Float>) -> Self {
self.y_step = y_step.into();
self
}

fn smoooth_linear(&self, ring: &mut Ring, values: &[f64], value: f64) {
fn smoooth_linear(&self, ring: &mut Ring, values: &[Float], value: Float) {
let dx = self.dx;
let dy = self.dy;
let len_values = values.len();
Expand All @@ -89,11 +89,11 @@ impl ContourBuilder {
let ix = (yt * dx + xt) as usize;
if ix < len_values {
let v1 = values[ix];
if x > 0.0 && x < (dx as f64) && (xt as f64 - x).abs() < std::f64::EPSILON {
if x > 0.0 && x < (dx as Float) && (xt as Float - x).abs() < Float::EPSILON {
v0 = values[(yt * dx + xt - 1) as usize];
point.x = x + (value - v0) / (v1 - v0) - 0.5;
}
if y > 0.0 && y < (dy as f64) && (yt as f64 - y).abs() < std::f64::EPSILON {
if y > 0.0 && y < (dy as Float) && (yt as Float - y).abs() < Float::EPSILON {
v0 = values[((yt - 1) * dx + xt) as usize];
point.y = y + (value - v0) / (v1 - v0) - 0.5;
}
Expand All @@ -111,7 +111,7 @@ impl ContourBuilder {
///
/// * `values` - The slice of values to be used.
/// * `thresholds` - The slice of thresholds values to be used.
pub fn lines(&self, values: &[f64], thresholds: &[f64]) -> Result<Vec<Line>> {
pub fn lines(&self, values: &[Float], thresholds: &[Float]) -> Result<Vec<Line>> {
if values.len() as u32 != self.dx * self.dy {
return Err(new_error(ErrorKind::BadDimension));
}
Expand All @@ -122,7 +122,12 @@ impl ContourBuilder {
.collect()
}

fn line(&self, values: &[f64], threshold: f64, isoring: &mut IsoRingBuilder) -> Result<Line> {
fn line(
&self,
values: &[Float],
threshold: Float,
isoring: &mut IsoRingBuilder,
) -> Result<Line> {
let mut result = isoring.compute(values, threshold)?;
let mut linestrings = Vec::new();

Expand All @@ -132,8 +137,8 @@ impl ContourBuilder {
self.smoooth_linear(&mut ring, values, threshold);
}
// Compute the polygon coordinates according to the grid properties if needed
if (self.x_origin, self.y_origin) != (0f64, 0f64)
|| (self.x_step, self.y_step) != (1f64, 1f64)
if (self.x_origin, self.y_origin) != (0.0, 0.0)
|| (self.x_step, self.y_step) != (1.0, 1.0)
{
ring.iter_mut().for_each(|point| {
point.x = point.x * self.x_step + self.x_origin;
Expand All @@ -143,7 +148,7 @@ impl ContourBuilder {
linestrings.push(LineString(ring));
});
Ok(Line {
geometry: MultiLineString(linestrings),
geometry: MultiLineString::<Float>(linestrings),
threshold,
})
}
Expand All @@ -157,7 +162,7 @@ impl ContourBuilder {
///
/// * `values` - The slice of values to be used.
/// * `thresholds` - The slice of thresholds values to be used.
pub fn contours(&self, values: &[f64], thresholds: &[f64]) -> Result<Vec<Contour>> {
pub fn contours(&self, values: &[Float], thresholds: &[Float]) -> Result<Vec<Contour>> {
if values.len() as u32 != self.dx * self.dy {
return Err(new_error(ErrorKind::BadDimension));
}
Expand All @@ -170,8 +175,8 @@ impl ContourBuilder {

fn contour(
&self,
values: &[f64],
threshold: f64,
values: &[Float],
threshold: Float,
isoring: &mut IsoRingBuilder,
) -> Result<Contour> {
let (mut polygons, mut holes) = (Vec::new(), Vec::new());
Expand All @@ -183,16 +188,16 @@ impl ContourBuilder {
self.smoooth_linear(&mut ring, values, threshold);
}
// Compute the polygon coordinates according to the grid properties if needed
if (self.x_origin, self.y_origin) != (0f64, 0f64)
|| (self.x_step, self.y_step) != (1f64, 1f64)
if (self.x_origin, self.y_origin) != (0.0, 0.0)
|| (self.x_step, self.y_step) != (1.0, 1.0)
{
ring.iter_mut().for_each(|point| {
point.x = point.x * self.x_step + self.x_origin;
point.y = point.y * self.y_step + self.y_origin;
});
}
if area(&ring) > 0.0 {
polygons.push(Polygon::new(LineString::new(ring), vec![]))
polygons.push(Polygon::<Float>::new(LineString::new(ring), vec![]))
} else {
holes.push(LineString::new(ring));
}
Expand All @@ -208,7 +213,7 @@ impl ContourBuilder {
});

Ok(Contour {
geometry: MultiPolygon(polygons),
geometry: MultiPolygon::<Float>(polygons),
threshold,
})
}
Expand All @@ -223,7 +228,7 @@ impl ContourBuilder {
/// * `values` - The slice of values to be used.
/// * `thresholds` - The slice of thresholds values to be used
/// (have to be equal to or greater than 2).
pub fn isobands(&self, values: &[f64], thresholds: &[f64]) -> Result<Vec<Band>> {
pub fn isobands(&self, values: &[Float], thresholds: &[Float]) -> Result<Vec<Band>> {
// We will compute rings as previously, but we will
// iterate over the contours in pairs and use the paths from the lower threshold
// and the path from the upper threshold to create the isoband.
Expand All @@ -249,8 +254,8 @@ impl ContourBuilder {
}
ring.dedup();
// Compute the polygon coordinates according to the grid properties if needed
if (self.x_origin, self.y_origin) != (0f64, 0f64)
|| (self.x_step, self.y_step) != (1f64, 1f64)
if (self.x_origin, self.y_origin) != (0.0, 0.0)
|| (self.x_step, self.y_step) != (1.0, 1.0)
{
ring.iter_mut().for_each(|point| {
point.x = point.x * self.x_step + self.x_origin;
Expand All @@ -263,7 +268,7 @@ impl ContourBuilder {
.collect::<Vec<Ring>>();
Ok((rings, *threshold))
})
.collect::<Result<Vec<(Vec<Ring>, f64)>>>()?;
.collect::<Result<Vec<(Vec<Ring>, Float)>>>()?;

// We now have the rings for each isolines for all the given thresholds,
// we can iterate over them in pairs to compute the isobands.
Expand Down Expand Up @@ -304,12 +309,12 @@ impl ContourBuilder {
enclosed_by_n.insert(i, enclosed_by_j);
}

let mut polygons: Vec<Polygon<f64>> = Vec::new();
let mut interior_rings: Vec<LineString<f64>> = Vec::new();
let mut polygons: Vec<Polygon<Float>> = Vec::new();
let mut interior_rings: Vec<LineString<Float>> = Vec::new();

for (i, (ring, _)) in rings_and_area.into_iter().enumerate() {
if *enclosed_by_n.get(&i).unwrap() % 2 == 0 {
polygons.push(Polygon::new(ring.into(), vec![]));
polygons.push(Polygon::<Float>::new(ring.into(), vec![]));
} else {
interior_rings.push(ring.into());
}
Expand All @@ -326,7 +331,7 @@ impl ContourBuilder {
polygons.reverse();

bands.push(Band {
geometry: MultiPolygon(polygons),
geometry: MultiPolygon::<Float>(polygons),
min_v: *min_v,
max_v: *max_v,
});
Expand Down
Loading

0 comments on commit eec087b

Please sign in to comment.