gadomski / las-rs

Read and write ASPRS las files, Rust edition.
MIT License
73 stars 32 forks source link

Point doesn't match header #73

Closed perrygeo closed 6 months ago

perrygeo commented 6 months ago

I'm trying to do a copy from one laz file to another but it's erroring out due to mismatched point format. The point I've read doesn't match the header from the same reader. Is there a trick to making this work?

I'm happy to contribute some tests and try to debug myself. I just want to make sure I'm not missing something obvious!

fn main() -> Result<(), Box<dyn Error>> {
    let mut reader = Reader::from_path("tests/data/red-rocks.laz")?;
    let header = reader.header().clone();
    for wrapped_point in reader.points() {
        let point = wrapped_point?;
        assert!(point.matches(header.point_format()));  // <-- fails
    }
}

Thanks for this great library!

gadomski commented 6 months ago

A Point's format is implicitly defined by its attributes: https://github.com/gadomski/las-rs/blob/ed49b3eec3607a0be2c19e7a90677128059d702e/src/point/mod.rs#L285-L291

To debug, I'd recommend checking the point format's atttributes (e.g. has_gps_time, etc) and the point's values:

dbg!(header.point_format());
dbg!(point);

One possibility is that the format has GPSTime but the values are set to zero — we (possibly incorrectly) convert 0 to None when reading points: https://github.com/gadomski/las-rs/blob/ed49b3eec3607a0be2c19e7a90677128059d702e/src/raw/point.rs#L379-L383

perrygeo commented 6 months ago

Boom. That was it, gps_time is always None. So the header claims gps_time but none of the points actually have it, seems to be a problem with the dataset.

[src/main.rs:110:9] header.point_format() = Format {
    has_gps_time: true,
    has_color: true,
    is_extended: false,
    has_waveform: false,
    has_nir: false,
    extra_bytes: 0,
    is_compressed: true,
}
[src/main.rs:111:9] &point = Point {
    x: 482309.13,
    y: 4391071.0,
    z: 1948.28,
    intensity: 0,
    return_number: 1,
    number_of_returns: 1,
    scan_direction: RightToLeft,
    is_edge_of_flight_line: false,
    classification: CreatedNeverClassified,
    is_synthetic: false,
    is_key_point: false,
    is_withheld: false,
    is_overlap: false,
    scanner_channel: 0,
    scan_angle: 0.0,
    user_data: 0,
    point_source_id: 0,
    gps_time: None,
    color: Some(
        Color {
            red: 50,
            green: 48,
            blue: 41,
        },
    ),
    waveform: None,
    nir: None,
    extra_bytes: [],
}

I tried with a USGS laz and confirmed that everything works, when the header doesn't lie!

Any thoughts on how to best handle files like this? If I know the data beforehand, I can adjust the header I pass to the writer. But in the general case, is there a good way to handle missing values in las?

gadomski commented 6 months ago

Any thoughts on how to best handle files like this?

Well, if you're not using the gps_time field, it doesn't seem like a critical problem. In fact, the LAS 1.4 doc says that:

For Aggregate Model Systems, the GPS Time should be set to zero unless assigned from a compo- nent measurement.

(Aggregate Model Systems are, e.g., photogrammetrically-derived point clouds).

So I think this library should allow zero values through and not set them to None, which should resolve the header/point format mismatch.

perrygeo commented 6 months ago

I confirmed that the change in #75 works for this dataset. Thanks @gadomski!