Skip to content

Commit f93227c

Browse files
committed
Make ContourBuilder generic over the raster data type
Signed-off-by: netthier <[email protected]>
1 parent 2053186 commit f93227c

File tree

8 files changed

+66
-67
lines changed

8 files changed

+66
-67
lines changed

Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@ license = "MIT OR Apache-2.0"
1414

1515
[dependencies]
1616
geojson = { version = ">=0.16, <=0.24", optional = true }
17-
geo-types= { version = "0.7" }
17+
geo-types = { version = "0.7" }
1818
lazy_static = "1.0"
1919
serde_json = { version = "^1.0", optional = true }
2020
rustc-hash = "1.0"
2121
slab = "0.4"
22+
num-traits = "0.2"
2223

2324
[dev-dependencies]
2425
serde_json = "^1.0"

src/area.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{Float, Pt};
1+
use crate::{ContourValue, Float, Pt};
22

33
#[allow(clippy::unnecessary_cast)]
44
// Note that we need to disable the clippy warning about unnecessary casts

src/band.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
1-
use crate::Float;
2-
use geo_types::MultiPolygon;
1+
use crate::{ContourValue, Float};
2+
use geo_types::{CoordFloat, MultiPolygon};
33

44
/// An isoband has the geometry and min / max values of a contour ring, built by [`ContourBuilder`].
55
#[derive(Debug, Clone)]
6-
pub struct Band {
6+
pub struct Band<V: ContourValue> {
77
pub(crate) geometry: MultiPolygon<Float>,
8-
pub(crate) min_v: Float,
9-
pub(crate) max_v: Float,
8+
pub(crate) min_v: V,
9+
pub(crate) max_v: V,
1010
}
1111

12-
impl Band {
12+
impl<V: ContourValue> Band<V> {
1313
/// Borrow the [`MultiPolygon`](geo_types::MultiPolygon) geometry of this contour.
1414
pub fn geometry(&self) -> &MultiPolygon<Float> {
1515
&self.geometry
1616
}
1717

1818
/// Get the owned polygons and thresholds (min and max) of this band.
19-
pub fn into_inner(self) -> (MultiPolygon<Float>, Float, Float) {
19+
pub fn into_inner(self) -> (MultiPolygon<Float>, V, V) {
2020
(self.geometry, self.min_v, self.max_v)
2121
}
2222

2323
/// Get the minimum value used to construct this band.
24-
pub fn min_v(&self) -> Float {
24+
pub fn min_v(&self) -> V {
2525
self.min_v
2626
}
2727

2828
/// Get the maximum value used to construct this band.
29-
pub fn max_v(&self) -> Float {
29+
pub fn max_v(&self) -> V {
3030
self.max_v
3131
}
3232

src/contour.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
1-
use crate::Float;
2-
use geo_types::MultiPolygon;
1+
use crate::{ContourValue, Float};
2+
use geo_types::{CoordFloat, MultiPolygon};
33

44
/// A contour has the geometry and threshold of a contour ring, built by [`ContourBuilder`].
55
#[derive(Debug, Clone)]
6-
pub struct Contour {
6+
pub struct Contour<V: ContourValue> {
77
pub(crate) geometry: MultiPolygon<Float>,
8-
pub(crate) threshold: Float,
8+
pub(crate) threshold: V,
99
}
1010

11-
impl Contour {
11+
impl<V: ContourValue> Contour<V> {
1212
/// Borrow the [`MultiPolygon`](geo_types::MultiPolygon) geometry of this contour.
1313
pub fn geometry(&self) -> &MultiPolygon<Float> {
1414
&self.geometry
1515
}
1616

1717
/// Get the owned polygons and threshold of this contour.
18-
pub fn into_inner(self) -> (MultiPolygon<Float>, Float) {
18+
pub fn into_inner(self) -> (MultiPolygon<Float>, V) {
1919
(self.geometry, self.threshold)
2020
}
2121

2222
/// Get the threshold used to construct this contour.
23-
pub fn threshold(&self) -> Float {
23+
pub fn threshold(&self) -> V {
2424
self.threshold
2525
}
2626

src/contourbuilder.rs

+27-27
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::area::{area, contains};
22
use crate::error::{new_error, ErrorKind, Result};
33
use crate::isoringbuilder::IsoRingBuilder;
4-
use crate::{Band, Contour, Float, Line, Ring};
5-
use geo_types::{LineString, MultiLineString, MultiPolygon, Polygon};
4+
use crate::{Band, Contour, ContourValue, Float, Line, Ring};
5+
use geo_types::{CoordFloat, LineString, MultiLineString, MultiPolygon, Polygon};
66
use rustc_hash::FxHashMap;
77

88
/// Contours generator, using builder pattern, to
@@ -43,10 +43,10 @@ impl ContourBuilder {
4343
dx,
4444
dy,
4545
smooth,
46-
x_origin: 0.,
47-
y_origin: 0.,
48-
x_step: 1.,
49-
y_step: 1.,
46+
x_origin: 0.0,
47+
y_origin: 0.0,
48+
x_step: 1.0,
49+
y_step: 1.0,
5050
}
5151
}
5252

@@ -74,7 +74,7 @@ impl ContourBuilder {
7474
self
7575
}
7676

77-
fn smooth_linear(&self, ring: &mut Ring, values: &[Float], value: Float) {
77+
fn smooth_linear<V: ContourValue>(&self, ring: &mut Ring, values: &[V], value: V) {
7878
let dx = self.dx;
7979
let dy = self.dy;
8080
let len_values = values.len();
@@ -91,11 +91,11 @@ impl ContourBuilder {
9191
let v1 = values[ix];
9292
if x > 0.0 && x < (dx as Float) && (xt as Float - x).abs() < Float::EPSILON {
9393
v0 = values[yt * dx + xt - 1];
94-
point.x = x + (value - v0) / (v1 - v0) - 0.5;
94+
point.x = x + num_traits::cast::<V, Float>( (value - v0) / (v1 - v0) - num_traits::cast(0.5).unwrap()).unwrap();
9595
}
9696
if y > 0.0 && y < (dy as Float) && (yt as Float - y).abs() < Float::EPSILON {
9797
v0 = values[(yt - 1) * dx + xt];
98-
point.y = y + (value - v0) / (v1 - v0) - 0.5;
98+
point.y = y + num_traits::cast::<V, Float>((value - v0) / (v1 - v0) - num_traits::cast(0.5).unwrap()).unwrap();
9999
}
100100
}
101101
})
@@ -111,7 +111,7 @@ impl ContourBuilder {
111111
///
112112
/// * `values` - The slice of values to be used.
113113
/// * `thresholds` - The slice of thresholds values to be used.
114-
pub fn lines(&self, values: &[Float], thresholds: &[Float]) -> Result<Vec<Line>> {
114+
pub fn lines<V: ContourValue>(&self, values: &[V], thresholds: &[V]) -> Result<Vec<Line<V>>> {
115115
if values.len() != self.dx * self.dy {
116116
return Err(new_error(ErrorKind::BadDimension));
117117
}
@@ -122,12 +122,12 @@ impl ContourBuilder {
122122
.collect()
123123
}
124124

125-
fn line(
125+
fn line<V: ContourValue>(
126126
&self,
127-
values: &[Float],
128-
threshold: Float,
127+
values: &[V],
128+
threshold: V,
129129
isoring: &mut IsoRingBuilder,
130-
) -> Result<Line> {
130+
) -> Result<Line<V>> {
131131
let mut result = isoring.compute(values, threshold)?;
132132
let mut linestrings = Vec::new();
133133

@@ -148,7 +148,7 @@ impl ContourBuilder {
148148
linestrings.push(LineString(ring));
149149
});
150150
Ok(Line {
151-
geometry: MultiLineString::<Float>(linestrings),
151+
geometry: MultiLineString(linestrings),
152152
threshold,
153153
})
154154
}
@@ -162,7 +162,7 @@ impl ContourBuilder {
162162
///
163163
/// * `values` - The slice of values to be used.
164164
/// * `thresholds` - The slice of thresholds values to be used.
165-
pub fn contours(&self, values: &[Float], thresholds: &[Float]) -> Result<Vec<Contour>> {
165+
pub fn contours<V: ContourValue>(&self, values: &[V], thresholds: &[V]) -> Result<Vec<Contour<V>>> {
166166
if values.len() != self.dx * self.dy {
167167
return Err(new_error(ErrorKind::BadDimension));
168168
}
@@ -173,12 +173,12 @@ impl ContourBuilder {
173173
.collect()
174174
}
175175

176-
fn contour(
176+
fn contour<V: ContourValue>(
177177
&self,
178-
values: &[Float],
179-
threshold: Float,
178+
values: &[V],
179+
threshold: V,
180180
isoring: &mut IsoRingBuilder,
181-
) -> Result<Contour> {
181+
) -> Result<Contour<V>> {
182182
let (mut polygons, mut holes) = (Vec::new(), Vec::new());
183183
let mut result = isoring.compute(values, threshold)?;
184184

@@ -197,7 +197,7 @@ impl ContourBuilder {
197197
});
198198
}
199199
if area(&ring) > 0.0 {
200-
polygons.push(Polygon::<Float>::new(LineString::new(ring), vec![]))
200+
polygons.push(Polygon::new(LineString::new(ring), vec![]))
201201
} else {
202202
holes.push(LineString::new(ring));
203203
}
@@ -213,7 +213,7 @@ impl ContourBuilder {
213213
});
214214

215215
Ok(Contour {
216-
geometry: MultiPolygon::<Float>(polygons),
216+
geometry: MultiPolygon(polygons),
217217
threshold,
218218
})
219219
}
@@ -228,7 +228,7 @@ impl ContourBuilder {
228228
/// * `values` - The slice of values to be used.
229229
/// * `thresholds` - The slice of thresholds values to be used
230230
/// (have to be equal to or greater than 2).
231-
pub fn isobands(&self, values: &[Float], thresholds: &[Float]) -> Result<Vec<Band>> {
231+
pub fn isobands<V: ContourValue>(&self, values: &[V], thresholds: &[V]) -> Result<Vec<Band<V>>> {
232232
// We will compute rings as previously, but we will
233233
// iterate over the contours in pairs and use the paths from the lower threshold
234234
// and the path from the upper threshold to create the isoband.
@@ -268,7 +268,7 @@ impl ContourBuilder {
268268
.collect::<Vec<Ring>>();
269269
Ok((rings, *threshold))
270270
})
271-
.collect::<Result<Vec<(Vec<Ring>, Float)>>>()?;
271+
.collect::<Result<Vec<(Vec<Ring>, V)>>>()?;
272272

273273
// We now have the rings for each isolines for all the given thresholds,
274274
// we can iterate over them in pairs to compute the isobands.
@@ -281,7 +281,7 @@ impl ContourBuilder {
281281
})
282282
.collect::<Vec<_>>();
283283

284-
let mut bands: Vec<Band> = Vec::new();
284+
let mut bands: Vec<Band<V>> = Vec::new();
285285
// Reconstruction of the polygons
286286
b.into_iter().for_each(|(rings, min_v, max_v)| {
287287
let mut rings_and_area = rings
@@ -314,7 +314,7 @@ impl ContourBuilder {
314314

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

333333
bands.push(Band {
334-
geometry: MultiPolygon::<Float>(polygons),
334+
geometry: MultiPolygon(polygons),
335335
min_v: *min_v,
336336
max_v: *max_v,
337337
});

src/isoringbuilder.rs

+7-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
use geo_types::{Coord, CoordFloat};
12
use crate::error::{new_error, ErrorKind, Result};
2-
use crate::{Float, Pt, Ring};
3+
use crate::{ContourValue, Float, Pt, Ring};
34
use lazy_static::lazy_static;
45
use rustc_hash::FxHashMap;
56
use slab::Slab;
@@ -49,12 +50,8 @@ struct Fragment {
4950
/// * `threshold` - The threshold value.
5051
/// * `dx` - The number of columns in the grid.
5152
/// * `dy` - The number of rows in the grid.
52-
pub fn contour_rings(
53-
values: &[Float],
54-
threshold: Float,
55-
dx: usize,
56-
dy: usize,
57-
) -> Result<Vec<Ring>> {
53+
54+
pub fn contour_rings<V: ContourValue>(values: &[V], threshold: V, dx: usize, dy: usize) -> Result<Vec<Ring>> {
5855
let mut isoring = IsoRingBuilder::new(dx, dy);
5956
isoring.compute(values, threshold)
6057
}
@@ -94,7 +91,7 @@ impl IsoRingBuilder {
9491
///
9592
/// * `values` - The slice of values to be used.
9693
/// * `threshold` - The threshold value to use.
97-
pub fn compute(&mut self, values: &[Float], threshold: Float) -> Result<Vec<Ring>> {
94+
pub fn compute<V: ContourValue>(&mut self, values: &[V], threshold: V) -> Result<Vec<Ring>> {
9895
macro_rules! case_stitch {
9996
($ix:expr, $x:ident, $y:ident, $result:expr) => {
10097
CASES[$ix]
@@ -177,12 +174,11 @@ impl IsoRingBuilder {
177174
y: i64,
178175
result: &mut Vec<Ring>,
179176
) -> Result<()> {
180-
assert_eq!((x, y), ((x as Float) as i64, (y as Float) as i64));
181-
let start = Pt {
177+
let start = Coord {
182178
x: line[0][0] + x as Float,
183179
y: line[0][1] + y as Float,
184180
};
185-
let end = Pt {
181+
let end = Coord {
186182
x: line[1][0] + x as Float,
187183
y: line[1][1] + y as Float,
188184
};

src/lib.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,19 @@ mod error;
6363
mod isoringbuilder;
6464
mod line;
6565

66+
pub trait ContourValue: PartialOrd + Copy + Num + NumCast{}
67+
impl<T> ContourValue for T where T: PartialOrd + Copy + Num + NumCast {}
68+
6669
#[cfg(feature = "f32")]
6770
pub type Float = f32;
6871
#[cfg(not(feature = "f32"))]
6972
pub type Float = f64;
70-
#[cfg(feature = "f32")]
71-
pub type Pt = geo_types::Coord<f32>;
72-
#[cfg(not(feature = "f32"))]
73-
pub type Pt = geo_types::Coord;
7473

74+
pub type Pt = geo_types::Coord<Float>;
7575
pub type Ring = Vec<Pt>;
7676

77+
use geo_types::CoordNum;
78+
use num_traits::{Num, NumCast};
7779
pub use crate::band::Band;
7880
pub use crate::contour::Contour;
7981
pub use crate::contourbuilder::ContourBuilder;

src/line.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
1-
use crate::Float;
2-
use geo_types::MultiLineString;
1+
use crate::{ContourValue, Float};
2+
use geo_types::{CoordFloat, MultiLineString};
33

44
/// A line has the geometry and threshold of a contour ring, built by [`ContourBuilder`].
55
#[derive(Debug, Clone)]
6-
pub struct Line {
6+
pub struct Line< V: ContourValue> {
77
pub(crate) geometry: MultiLineString<Float>,
8-
pub(crate) threshold: Float,
8+
pub(crate) threshold: V,
99
}
1010

11-
impl Line {
11+
impl<V: ContourValue> Line<V> {
1212
/// Borrow the [`MultiLineString`](geo_types::MultiLineString) geometry of this contour.
1313
pub fn geometry(&self) -> &MultiLineString<Float> {
1414
&self.geometry
1515
}
1616

1717
/// Get the owned lines and threshold of this contour.
18-
pub fn into_inner(self) -> (MultiLineString<Float>, Float) {
18+
pub fn into_inner(self) -> (MultiLineString<Float>, V) {
1919
(self.geometry, self.threshold)
2020
}
2121

2222
/// Get the threshold used to construct this isoline.
23-
pub fn threshold(&self) -> Float {
23+
pub fn threshold(&self) -> V {
2424
self.threshold
2525
}
2626

0 commit comments

Comments
 (0)