Skip to content

Add support for pointGroupingSchemes/groupingByLine - #21

Open
ohfaro wants to merge 1 commit into
cry-inc:masterfrom
ohfaro:point-grouping-schemes
Open

Add support for pointGroupingSchemes/groupingByLine#21
ohfaro wants to merge 1 commit into
cry-inc:masterfrom
ohfaro:point-grouping-schemes

Conversation

@ohfaro

@ohfaro ohfaro commented Aug 10, 2026

Copy link
Copy Markdown

A structured point cloud may carry a per-scan-line index alongside its points —
one group per row or column, saying where that line's points begin in the point
vector and how many there are. A reader can use it to seek to a single scan line
without decoding the whole cloud. The crate currently ignores it on read and
cannot produce it on write.

I ran into this writing files that have to match, byte for byte, what an
existing libE57Format-based writer produces: it emits groupingByLine for every
structured scan, so files written without it are not equivalent. It also can't
be added from outside the crate — the groups are a compressed vector, and the
packet, bytestream and paged-writer machinery is private. finalize_customized_xml
can add the XML but not the binary section it has to point at.

What this adds

// Writing
let groups = PointGroups::new(RecordName::ColumnIndex, groups);
pc_writer.set_point_groups(Some(groups));   // after the last point, before finalize()
pc_writer.finalize()?;

// Reading
let header = pointcloud.point_groups;                  // Option<PointGroupsHeader>
let groups = reader.point_groups(&pointcloud)?;        // Option<Vec<PointGroup>>

New public types in src/point_groups.rs: PointGroup, PointGroupLimits,
PointGroups, PointGroupsHeader.

Notes on the design

Both directions reuse what's already there. The groups are an ordinary
compressed vector, so the writer lays down a second section during finalize
using the same packet/bytestream path as points, and the reader describes the
section as a PointCloud and hands it to PointCloudReaderRaw. No new format
code.

Limits are derived but overridable. PointGroups::new computes the declared
maxima from the groups, which is what you want writing a cloud of your own. The
limits field stays public because matching another implementation sometimes
means declaring something other than the obvious maximum — not every writer out
there gets this right, and reproducing a file faithfully means reproducing what
it declared. Happy to make this private if you'd rather keep the API narrow;
the derived path covers the common case on its own.

Out-of-range values are refused, not written. A value that doesn't fit the
limits it's declared under would be encoded in too few bits and silently lost.
libE57Format rejects this with E57_ERROR_VALUE_OUT_OF_BOUNDS; this does too.

Only groupingByLine is supported. Extension schemes are ignored rather
than misparsed — they don't necessarily have the same fields. Your existing
testdata/las2e57_no_images_tag.e57 is a good example: it carries
las:groupingByReturnIndex, which has no startPointIndex at all. There's a
test asserting that file still reads and reports no groups.

Tests

  • tests/writer_tests.rs — round trip of a 4x2 structured scan; a cloud
    without groups reporting None; out-of-range values refused; a grouping keyed
    on something other than row/column refused.
  • tests/reader_tests.rs — the LAS extension scheme above is ignored.
  • src/point_groups.rs — limit derivation, the prototype, and XML round trip.

Full CI gate green locally on Linux x86-64: cargo build --release --all,
cargo test --release --all (102 tests, 0 failures), cargo clippy --release --all --all-targets --all-features -- -D warnings, cargo fmt --all -- --check,
and RUSTDOCFLAGS="-Dwarnings" cargo doc -p e57.

Added an ## [Unreleased] CHANGELOG entry; move or reword it as you prefer.

Prepared by GitHub Copilot on behalf of @ohfaro.

A structured point cloud may carry a per-scan-line index alongside its
points: one group per row or column, saying where that line's points
begin in the point vector and how many there are. A reader can use it to
seek to a single scan line without decoding the whole cloud.

This adds reading and writing of it. The groups are an ordinary
compressed vector, so both directions reuse the machinery that already
handles points: the writer lays down a second section during finalize,
and the reader describes the section as a point cloud and hands it to
PointCloudReaderRaw.

`PointGroups::new` derives the declared limits from the groups, which is
what you want when writing a cloud of your own. They stay public so they
can be set explicitly, which is what you want when matching a file
another implementation would have written -- not every writer out there
declares the obvious maxima. Values outside their declared limits are
refused rather than written, since they would be encoded in too few bits
and silently lost.

Only `groupingByLine` is supported; grouping schemes from extensions,
such as the `las:groupingByReturnIndex` in the existing test file
testdata/las2e57_no_images_tag.e57, are ignored rather than misparsed --
they do not necessarily have the same fields.
@ohfaro
ohfaro marked this pull request as ready for review August 10, 2026 19:16
@ohfaro

ohfaro commented Aug 10, 2026

Copy link
Copy Markdown
Author

For convenience, since fork PRs need your approval before checks run here: the full CI matrix passes on my fork against this branch — Linux x86-64 and arm64, Windows x86-64 and arm64, macOS x86-64 and arm64, all six green, including clippy -D warnings, cargo fmt --check and the doc warning gate.

https://github.com/ohfaro/e57/actions/runs/31422975885

Prepared by GitHub Copilot on behalf of @ohfaro.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants