-
Notifications
You must be signed in to change notification settings - Fork 10
Overflow/Artifacts with large rasters (>40k*40k) #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Hm, when generating contours for large rasters affected by this issue using my fork I get artifacts that do not appear when only generating contours for a small region. |
Thank you for your feedback and for starting to try to solve the problem. It looks particularly bad, it would be great to find a solution. Do you have the possibility of sharing the dataset with which the problem occurs? I also have a library that generate isobands : https://github.com/mthh/contour-isobands-rs (it also uses marching-squares, but a slightly different implementation).
Thanks, I've had a look at your changes, in any case I think you're right about the |
I sadly cannot share the original dataset, but I can try creating a reproducer manually. UPDATE: Even when search+replacing all f64s in |
No problem. I'll try to generate one manually too if you can't.
Yes sadly the algorithm implemented in https://github.com/mthh/contour-isobands-rs is more memory-intensive (it has to manage more cases than the one in https://github.com/mthh/contour-rs, cf. https://en.wikipedia.org/wiki/Marching_squares#Isobands - maybe there's a way of making it lighter in terms of memory use, but I'm afraid that's beyond my skills and/or the time I have to devote to it) |
I'm actually able to send an old version of the dataset after all, l'll send it to you via E-Mail once I prepare it. |
Running without the |
I strongly suspect that this is a numeric type conversion issue (because when I'm using only an extract of your dataset, but with the f32 feature, the result is correct) but I can't find out where in the code (in fact I've identified several places that could be problematic but haven't really found the culprit or how to fix it). In rust, conversions with "as" are safe (in the sense that they don't panic) but they may not be correct. For example here https://github.com/SenseLabsDE/contour-rs/blob/17053d29cad089500eadc2e266dec3988348eba9/src/isoringbuilder.rs#L106, But there is also other places, for example here https://github.com/mthh/contour-rs/blob/master/src/isoringbuilder.rs#L169 we have a I'll keep investigating, if I can't fix it it will probably be to return an error message (clearer than the one you reported in your initial issue) when using grids that are too large, depending on the numeric type used (f32 / f64) and thus prevent the artifacts obtained with your modified version from appearing... |
contour-rs/src/isoringbuilder.rs Lines 180 to 187 in 08c1d83
looks like a good candidate tbh, though I'd be surprised if the values passed there are large enough to cause issues. I'll see if I can insert some checks there. UPDATE: I added UPDATE 2: Adding a |
Indeed, that's what I think of it too, reading the code (and knowing the max size of f32) and that's why I still can't figure out what's going on 😓 |
I think I found how to fix it, it now works without artifact on the dataset you sent me, using the f32 feature. Can you try by replacing https://github.com/mthh/contour-rs/blob/master/src/isoringbuilder.rs#L169 by |
I can confirm that this fixes the artifacting in my larger, original dataset as well, thank you! |
Great! 👍 |
While the artifacts I showed earlier don't come up anymore, using |
Alright. Thank you for reporting these issues and helping to fix them! |
I was able to reproduce this using the previously shared dataset after all. use std::fs;
use gdal::Dataset;
use contour::ContourBuilder;
use geojson::{GeoJson, Geometry};
fn main() {
let dataset = Dataset::open("dc.tiff").unwrap();
let raster = dataset.rasterband(1).unwrap();
let size = raster.size();
let mut values: Vec<f32> = raster.read_as::<f32>((0, 0), size, size, None).unwrap().data;
let values: Vec<f64> = values.drain(..).map(|v| v as f64).collect();
let contours = ContourBuilder::new(size.0, size.1, false).contours(&values, &[1000.0]).unwrap();
fs::write("./out-f64.json", GeoJson::Geometry(Geometry::new(geojson::Value::from(&contours[0].geometry().clone()))).to_string()).unwrap();
println!("Subpolygons: {}", contours[0].geometry().0.len());
} use std::fs;
use gdal::Dataset;
use contour::ContourBuilder;
use geojson::{GeoJson, Geometry};
fn main() {
let dataset = Dataset::open("dc.tiff").unwrap();
let raster = dataset.rasterband(1).unwrap();
let size = raster.size();
let values: Vec<f32> = raster.read_as::<f32>((0, 0), size, size, None).unwrap().data;
let contours = ContourBuilder::new(size.0, size.1, false).contours(&values, &[1000.0]).unwrap();
fs::write("./out-f32.json", GeoJson::Geometry(Geometry::new(geojson::Value::from(&contours[0].geometry().clone()))).to_string()).unwrap();
println!("Subpolygons: {}", contours[0].geometry().0.len());
} using these deps: [dependencies]
contour = { git = "https://github.com/SenseLabsDE/contour-rs.git", branch = "fix-overflow", features = [] }
gdal = "0.16"
geojson = "0.24" (and with The |
Interesting. In addition, I was unable to find these differences visually despite a lengthy inspection in QGIS 😓 I tested (still on the dataset you sent me) your two snippets using the The good news is that I'm getting exactly the same results in |
I think I've figured out how to fix it (and get exactly the same result for contours / isobands with the "f32" feature and the default feature with your example dataset), by always calculating the area (in area.rs) in f64. I tried to push on the branch used for your PR #13 but it seems I'm not allowed to. If you want to apply the following diff to your branch and test: diff --git a/src/area.rs b/src/area.rs
index a3cb8f4..774bffd 100644
--- a/src/area.rs
+++ b/src/area.rs
@@ -1,10 +1,15 @@
use crate::{Float, Pt};
-pub fn area(ring: &[Pt]) -> Float {
+#[allow(clippy::unnecessary_cast)]
+// Note that we need to disable the clippy warning about unnecessary casts
+// because of the "f32" optional feature (and because we want to ensure we always
+// use "f64" in this function, both in the default feature and in the "f32" feature).
+pub fn area(ring: &[Pt]) -> f64 {
let n = ring.len();
- let mut area = ring[n - 1].y * ring[0].x - ring[n - 1].x * ring[0].y;
+ let mut area =
+ ring[n - 1].y as f64 * ring[0].x as f64 - ring[n - 1].x as f64 * ring[0].y as f64;
for i in 1..n {
- area += ring[i - 1].y * ring[i].x - ring[i - 1].x * ring[i].y;
+ area += ring[i - 1].y as f64 * ring[i].x as f64 - ring[i - 1].x as f64 * ring[i].y as f64;
}
// Note that in the shoelace formula you need to divide this result by 2 to get the actual area.
// Here we skip this division because we only use this area formula to calculate the winding |
I can confirm that I now get the same amount of subpolygons with either type on both of my datasets, but I hope that there aren't similar issues hidden anywhere else. |
If I pass in a raster larger than about 40000*40000 (not sure about the exact value, but 50k*50k fails) to
ContourBuilder::contours
, I get an error similar to this:Increasing the width of the involved local variables to
i64
seems to have fixed this issue, and I'll create a PR containing those changes once I test it against real data.The text was updated successfully, but these errors were encountered: