Computes isolines, contour polygons and isobands by applying marching squares to a rectangular array of numeric values.
Outputs ring coordinates or geo-types geometries.
The results can also easily be serialised to GeoJSON.
Note : The core of the algorithm is ported from d3-contour.
Add this to your Cargo.toml
:
[dependencies]
contour = "0.13.1"
and this to your crate root:
extern crate contour;
The API exposes:
-
a
ContourBuilder
struct, which computes isorings coordinates for aVec
of threshold values and transform them either :- in
Contour
s (a type containing the threshold value and the geometry as aMultiPolygon
), or, - in
Line
s (a type containing the threshold value and the geometry as aMultiLineString
). - in
Band
s (a type containing a minimum value, a maximum value and the geometry as aMultiPolygon
).
- in
-
a
contour_rings
function, which computes isorings coordinates for a single threshold value (returns aVec
of rings coordinates - this is what is used internally by theContourBuilder
).
ContourBuilder
is the recommended way to use this crate, as it is more flexible and easier to use (it enables to specify the origin and the step of the grid, and to smooth the contours, while contour_rings
only speak in grid coordinates and doesn't smooth the resulting rings).
Line
, Contour
and Band
can be serialised to GeoJSON using the geojson
feature.
Without defining origin and step:
let c = ContourBuilder::new(10, 10, false); // x dim., y dim., smoothing
let res = c.contours(&vec![
0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 1., 1., 1., 0., 0., 0., 0.,
0., 0., 0., 1., 1., 1., 0., 0., 0., 0.,
0., 0., 0., 1., 1., 1., 0., 0., 0., 0.,
0., 0., 0., 1., 1., 1., 0., 0., 0., 0.,
0., 0., 0., 1., 1., 1., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0.
], &[0.5])?; // values, thresholds
With origin and step
let c = ContourBuilder::new(10, 10, true) // x dim., y dim., smoothing
.x_step(2) // The horizontal coordinate for the origin of the grid.
.y_step(2) // The vertical coordinate for the origin of the grid.
.x_origin(100) // The horizontal step for the grid
.y_origin(200); // The vertical step for the grid
let res = c.contours(&[
0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 1., 1., 0., 1., 0., 0., 0.,
0., 0., 0., 1., 1., 0., 1., 0., 0., 0.,
0., 0., 0., 1., 1., 0., 1., 0., 0., 0.,
0., 0., 0., 1., 1., 0., 1., 0., 0., 0.,
0., 0., 0., 1., 1., 0., 1., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0.
], &[0.5]).unwrap(); // values, thresholds
Using the geojson
feature
The geojson
feature is not enabled by default, so you need to specify it in your Cargo.toml
:
[dependencies]
contour = { version = "0.13.1", features = ["geojson"] }
let c = ContourBuilder::new(10, 10, true) // x dim., y dim., smoothing
.x_step(2) // The horizontal coordinate for the origin of the grid.
.y_step(2) // The vertical coordinate for the origin of the grid.
.x_origin(100) // The horizontal step for the grid
.y_origin(200); // The vertical step for the grid
let res = c.contours(&[
0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 1., 1., 0., 1., 0., 0., 0.,
0., 0., 0., 1., 1., 0., 1., 0., 0., 0.,
0., 0., 0., 1., 1., 0., 1., 0., 0., 0.,
0., 0., 0., 1., 1., 0., 1., 0., 0., 0.,
0., 0., 0., 1., 1., 0., 1., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0.
], &[0.5]).unwrap(); // values, thresholds
println!("{:?}", res[0].to_geojson()); // prints the GeoJSON representation of the first contour
Output:
Feature {
bbox: None,
geometry: Some(Geometry {
bbox: None,
value: MultiPolygon([
[[
[110.0, 215.0], [110.0, 213.0], [110.0, 211.0], [110.0, 209.0],
[110.0, 207.0], [109.0, 206.0], [107.0, 206.0], [106.0, 207.0],
[106.0, 209.0], [106.0, 211.0], [106.0, 213.0], [106.0, 215.0],
[107.0, 216.0], [109.0, 216.0], [110.0, 215.0]
]],
[[
[114.0, 215.0], [114.0, 213.0], [114.0, 211.0], [114.0, 209.0],
[114.0, 207.0], [113.0, 206.0], [112.0, 207.0], [112.0, 209.0],
[112.0, 211.0], [112.0, 213.0], [112.0, 215.0], [113.0, 216.0],
[114.0, 215.0]
]]
]),
foreign_members: None
}),
id: None,
properties: Some({"threshold": Number(0.5)}),
foreign_members: None
}
Using the f32
feature
By default, this crate expects f64
values as input and uses f64
values for its computations.
If you want to use f32
values instead, you need to specify the f32
feature in your Cargo.toml
:
[dependencies]
contour = { version = "0.13.1", features = ["f32"] }
Demo of this crate compiled to WebAssembly and used from JavaScript : wasm_demo_contour.
Difference with the contour-isobands crate (from mthh/contour-isobands-rs repository)
While this crate computes isolines (cf. wikipedia:Marching_squares) from which it derives contour polygons (i.e. polygons that contain all points above the threshold defined for a given isoline) and isobands (i.e. polygons that contain all points between a minimum and a maximum bound), contour-isobands-rs is only dedicated to compute isobands and use a slightly different implementation of the marching squares algorithm for the disambiguation of saddle points (cf. wikipedia:Marching_squares).
Licensed under either of
- Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.