Skip to content

Spatial Indexing#3

Open
ryanstocks00 wants to merge 28 commits intomainfrom
spatial_index
Open

Spatial Indexing#3
ryanstocks00 wants to merge 28 commits intomainfrom
spatial_index

Conversation

@ryanstocks00
Copy link
Contributor

No description provided.

@codecov
Copy link

codecov bot commented Nov 7, 2025

Codecov Report

❌ Patch coverage is 93.55078% with 87 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/spatial_index.hpp 84.38% 42 Missing ⚠️
src/vlr.hpp 8.33% 22 Missing ⚠️
src/las_header.hpp 78.04% 9 Missing ⚠️
src/las_reader.hpp 93.60% 8 Missing ⚠️
src/utilities/printing.hpp 89.74% 4 Missing ⚠️
src/laz/stream.hpp 92.30% 1 Missing ⚠️
src/utilities/thread_pool.hpp 96.77% 1 Missing ⚠️
Flag Coverage Δ
unittests 92.85% <93.55%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/example_custom_las_point.hpp 100.00% <ø> (ø)
src/las_point.hpp 44.25% <ø> (ø)
src/las_writer.hpp 95.47% <100.00%> (ø)
src/laz/chunktable.hpp 100.00% <ø> (ø)
src/laz/layered_stream.hpp 100.00% <ø> (ø)
src/laz/laz_reader.hpp 87.50% <ø> (ø)
src/laz/laz_writer.hpp 90.47% <ø> (ø)
src/laz/tests/test_laszip_interop.cpp 99.12% <100.00%> (ø)
src/laz/tests/test_spatial_index.cpp 100.00% <100.00%> (ø)
src/tests/test_bound2d.cpp 100.00% <100.00%> (ø)
... and 13 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for reading/writing LAStools (.lax / EVLR) quadtree spatial indexes and integrates it into copy/conversion workflows, while also refactoring parallelism utilities, printing helpers, and build configuration.

Changes:

  • Introduce QuadtreeSpatialIndex and add LASReader/LASWriter support for detecting, copying, and optionally generating LAStools spatial index EVLRs.
  • Replace <execution>-based parallel loops with an internal thread-pool approach, including a new parallel reduction helper.
  • Add printing helpers (indentation + limited map printing), new tests, and adjust CMake/CI to improve cross-compiler compatibility.

Reviewed changes

Copilot reviewed 32 out of 32 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
src/vlr.hpp Improves VLR/EVLR printing and adds helpers to identify LAStools spatial index records.
src/utilities/thread_pool.hpp Adds parallel_for_reduction and related includes/utilities.
src/utilities/tests/test_printing.cpp Adds tests for new printing helpers.
src/utilities/printing.hpp Adds Indented/LimitedMap wrappers and std::map streaming support.
src/utilities/assert.hpp Adjusts seek macro warnings and headers/includes.
src/tests/test_writer_copy_from_reader.cpp Adds coverage for writer copy-from-reader and spatial index behavior.
src/tests/test_transform_accessors.cpp Adds tests for new non-const Transform accessors.
src/tests/test_reader_constructors.cpp Adds tests for reader constructors and .lax discovery/precedence behavior.
src/tests/test_reader.cpp Adds tests for interval→chunk mapping and reading non-contiguous chunk lists.
src/tests/test_bound2d.cpp Adds tests for new Bound2D.
src/spatial_index.hpp New LAStools spatial index implementation (read/write + cell computations).
src/laz/tests/test_spatial_index.cpp Adds LAZ-side tests for spatial index functionality.
src/laz/tests/test_laszip_interop.cpp Updates Transform usage to new API.
src/laz/stream.hpp Tightens bounds-checked reads and seek behavior for pointer-backed streams.
src/laz/laz_writer.hpp Adds handling for (unsupported) LAZ item type Byte14 and removes <execution>.
src/laz/laz_reader.hpp Adds handling for (unsupported) LAZ item type Byte14.
src/laz/layered_stream.hpp Formatting/initialization tweak for the empty-layer buffer.
src/laz/chunktable.hpp Fixes #pragma pack(pop) placement.
src/las_writer.hpp Adds copy-from-reader and spatial index writing; refactors parallel copy/stats to thread-pool utilities.
src/las_reader.hpp Adds spatial index detection (EVLR + .lax), stream-parallel LAZ chunk reads, and interval/chunk helpers.
src/las_point.hpp Minor include cleanup.
src/las_header.hpp Introduces Bound2D, adds Transform accessors, and exposes points-by-return helper.
src/example_custom_las_point.hpp Adds missing standard includes for example point type.
cmake/SetupLazperf.cmake Makes warning suppression flags compiler-specific (GCC vs Clang).
cmake/SetupLaszip.cmake Makes warning suppression flags compiler-specific (GCC vs Clang).
apps/validate_spatial_index.cpp New CLI tool to validate spatial index correctness vs points.
apps/las2las++.cpp Updates CLI to use copy-from-reader and optionally add spatial index.
apps/inspect_vlrs.cpp New CLI tool to inspect VLR/EVLRs and spatial index details.
apps/create_test_file.cpp Updates Transform usage to new API.
apps/CMakeLists.txt Builds/installs new CLI tools and adds MSVC /bigobj handling.
CMakeLists.txt Scopes strict flags/aggressive opts to top-level project; MSVC /bigobj for tests.
.github/workflows/cmake-multi-platform.yml Removes Clang OpenMP install step.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +292 to +296
bool is_laz_vlr() const {
return (reserved == 0 || reserved == 0xAABB) &&
(std::string(user_id) == "LAZ encoded" || std::string(user_id) == "laszip encoded") &&
record_id == 22204;
}
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_laz_vlr() uses std::string(user_id) on a fixed-size char[16] that may not be null-terminated, which can read past the field (UB) and cause incorrect comparisons. Construct the string with an explicit length (e.g., std::string(user_id, 16) plus trimming) before comparing.

Copilot uses AI. Check for mistakes.
Comment on lines +9 to 13
#include <algorithm>
#include <array>
#include <chrono>
#include <cstddef>
#include <cstdint>
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

las_header.hpp uses std::stringstream (e.g., in global_encoding_string()), but <sstream> is no longer included here. Add <sstream> to avoid relying on transitive includes.

Copilot uses AI. Check for mistakes.
Comment on lines +249 to +251
bool is_lastools_spatial_index_vlr() const {
return std::string(user_id) == "LAStools" && record_id == 30;
}
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_lastools_spatial_index_vlr() constructs std::string(user_id) from a fixed-size char[16] field that is not guaranteed to be null-terminated. That can read past the array bounds (UB) and also mis-detect IDs containing embedded nulls/spaces. Use a bounded construction like std::string(user_id, 16) (and trim trailing nulls/spaces) similar to the printing code above.

Copilot uses AI. Check for mistakes.
Comment on lines +298 to +300
bool is_lastools_spatial_index_evlr() const {
return std::string(user_id) == "LAStools" && record_id == 30;
}
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_lastools_spatial_index_evlr() has the same issue as the VLR version: std::string(user_id) assumes null-termination for a fixed-size char[16], which is not guaranteed and can read out of bounds. Use a bounded string construction and trimming before comparing.

Copilot uses AI. Check for mistakes.
Comment on lines +168 to +191
LASPP_ASSERT(false, "Invalid spatial index signature");
}

// Read version
uint32_t version;
LASPP_CHECK_READ(is, &version, 4);
LASPP_ASSERT_EQ(version, 0u);

// Read quadtree header
LASPP_CHECK_READ(is, &m_quadtree_header, sizeof(QuadtreeHeader));
LASPP_ASSERT(memcmp(m_quadtree_header.spatial_signature, "LASS", 4) == 0,
"Invalid quadtree spatial signature");
LASPP_ASSERT_EQ(m_quadtree_header.type, 0u);
LASPP_ASSERT(memcmp(m_quadtree_header.quadtree_signature, "LASQ", 4) == 0,
"Invalid quadtree signature");

// Read interval header
char interval_signature[4];
LASPP_CHECK_READ(is, interval_signature, 4);
LASPP_ASSERT(memcmp(interval_signature, "LASV", 4) == 0, "Invalid interval signature");

uint32_t interval_version;
LASPP_CHECK_READ(is, &interval_version, 4);
LASPP_ASSERT_EQ(interval_version, 0u);
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Version validation here relies on LASPP_ASSERT_EQ(version, 0u), which is compiled out in non-debug builds. Since this is parsing external data, consider turning version/signature checks into always-on runtime validation (e.g., throw on unexpected versions) rather than debug-only asserts.

Suggested change
LASPP_ASSERT(false, "Invalid spatial index signature");
}
// Read version
uint32_t version;
LASPP_CHECK_READ(is, &version, 4);
LASPP_ASSERT_EQ(version, 0u);
// Read quadtree header
LASPP_CHECK_READ(is, &m_quadtree_header, sizeof(QuadtreeHeader));
LASPP_ASSERT(memcmp(m_quadtree_header.spatial_signature, "LASS", 4) == 0,
"Invalid quadtree spatial signature");
LASPP_ASSERT_EQ(m_quadtree_header.type, 0u);
LASPP_ASSERT(memcmp(m_quadtree_header.quadtree_signature, "LASQ", 4) == 0,
"Invalid quadtree signature");
// Read interval header
char interval_signature[4];
LASPP_CHECK_READ(is, interval_signature, 4);
LASPP_ASSERT(memcmp(interval_signature, "LASV", 4) == 0, "Invalid interval signature");
uint32_t interval_version;
LASPP_CHECK_READ(is, &interval_version, 4);
LASPP_ASSERT_EQ(interval_version, 0u);
throw std::runtime_error("Invalid spatial index signature (expected 'LASX')");
}
// Read version
uint32_t version;
LASPP_CHECK_READ(is, &version, 4);
if (version != 0u) {
throw std::runtime_error("Unsupported spatial index version: " + std::to_string(version));
}
// Read quadtree header
LASPP_CHECK_READ(is, &m_quadtree_header, sizeof(QuadtreeHeader));
if (memcmp(m_quadtree_header.spatial_signature, "LASS", 4) != 0) {
throw std::runtime_error("Invalid quadtree spatial signature (expected 'LASS')");
}
if (m_quadtree_header.type != 0u) {
throw std::runtime_error("Unsupported quadtree type: " +
std::to_string(m_quadtree_header.type));
}
if (memcmp(m_quadtree_header.quadtree_signature, "LASQ", 4) != 0) {
throw std::runtime_error("Invalid quadtree signature (expected 'LASQ')");
}
// Read interval header
char interval_signature[4];
LASPP_CHECK_READ(is, interval_signature, 4);
if (memcmp(interval_signature, "LASV", 4) != 0) {
throw std::runtime_error("Invalid interval signature (expected 'LASV')");
}
uint32_t interval_version;
LASPP_CHECK_READ(is, &interval_version, 4);
if (interval_version != 0u) {
throw std::runtime_error("Unsupported interval version: " +
std::to_string(interval_version));
}

Copilot uses AI. Check for mistakes.
Comment on lines +422 to +425
void copy_points_with_spatial_index(LASReader& reader, bool add_spatial_index) {
std::vector<PointType> points(reader.num_points());
reader.read_chunks<PointType>(points, {0, reader.num_chunks()});

Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copy_points_with_spatial_index() reads the entire point set into memory unconditionally (std::vector<PointType> points(reader.num_points()) + read_chunks(...)), even when add_spatial_index == false (the else-branch just writes the points back). For large files this can be a major memory spike and defeats streaming. Consider a fast path that copies points chunk-by-chunk when no reordering/spatial index is requested.

Copilot uses AI. Check for mistakes.
ryanstocks00 and others added 5 commits March 8, 2026 22:13
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
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