Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
40deaa9
Initial spatial index implementation
ryanstocks00 Nov 7, 2025
23b1198
Added non const offsets method
ryanstocks00 Nov 7, 2025
b731146
Fixing bugs with point format 14 boundary updating
ryanstocks00 Nov 8, 2025
ffb9185
Reading subset of chunks from spatial index
ryanstocks00 Nov 9, 2025
78f13d0
Write with chunks
ryanstocks00 Nov 10, 2025
c7bc94a
Bit of cleanup
ryanstocks00 Dec 2, 2025
6540489
Merge branch 'main' into spatial_index
ryanstocks00 Mar 6, 2026
f5d111a
MIT License
ryanstocks00 Mar 6, 2026
5792e80
Remove redundant casts
ryanstocks00 Mar 6, 2026
6dfb768
Minor fixes to spatial index test
ryanstocks00 Mar 6, 2026
a5266ed
Make constructor explicit
ryanstocks00 Mar 6, 2026
c5caf70
Fix redundant cast warnings
ryanstocks00 Mar 6, 2026
70be227
Remove tbb dependency
ryanstocks00 Mar 6, 2026
f86196c
Update clang warnings
ryanstocks00 Mar 7, 2026
cbe12bf
Fix unaligned reads
ryanstocks00 Mar 7, 2026
3f7a79c
Add parallel reduction
ryanstocks00 Mar 7, 2026
69a205a
Move pragma pack
ryanstocks00 Mar 7, 2026
a97a617
Minor warning fixes
ryanstocks00 Mar 7, 2026
ea72478
Remove unused parallel headers
ryanstocks00 Mar 7, 2026
cfe0636
Clean up includes
ryanstocks00 Mar 8, 2026
8525dad
Parallel chunk subset read
ryanstocks00 Mar 8, 2026
640b377
Correct packing
ryanstocks00 Mar 8, 2026
f7e7b59
Move project call earlier
ryanstocks00 Mar 8, 2026
500b3db
Update src/utilities/printing.hpp
ryanstocks00 Mar 8, 2026
607a28f
Update src/utilities/assert.hpp
ryanstocks00 Mar 8, 2026
e067be2
Update src/las_writer.hpp
ryanstocks00 Mar 8, 2026
65a32a2
Update src/las_writer.hpp
ryanstocks00 Mar 8, 2026
44be0fe
Update src/utilities/printing.hpp
ryanstocks00 Mar 8, 2026
de0c2f5
Cleanup import orders
ryanstocks00 Apr 3, 2026
f4f1980
Better dealing with null terminations
ryanstocks00 Apr 3, 2026
324865b
Fixing span ref passing to stream layers
ryanstocks00 Apr 4, 2026
f287385
Streaming point copy without spatial index
ryanstocks00 Apr 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/workflows/cmake-multi-platform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ jobs:
run: |
echo "build-output-dir=${{ github.workspace }}/build" >> \
"$GITHUB_OUTPUT"
- name: Install OpenMP
run: sudo apt -y install libomp-18-dev
if: matrix.cpp_compiler == 'clang++'
- name: Configure CMake
run: >
cmake -B ${{ steps.strings.outputs.build-output-dir }} -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -S ${{ github.workspace }}
Expand Down
168 changes: 97 additions & 71 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@

cmake_minimum_required(VERSION 3.20)

project(
LAS++
VERSION 0.0.0
LANGUAGES CXX)

option(LASPP_DEBUG_ASSERTS "Enable debugging asserts" ON)
option(LASPP_BUILD_TESTS "Build tests" ON)
# Default benchmarks to OFF if this is a subproject (to avoid downloading
Expand All @@ -18,11 +23,6 @@ option(LASPP_AGGRESSIVE_OPTIMIZATIONS
"Enable aggressive performance optimizations (-march=native, etc.)" ON)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

project(
LAS++
VERSION 0.0.0
LANGUAGES CXX)
set(COPYRIGHT "Copyright (c) 2024 Trailblaze Software. All rights reserved.")

if(LASPP_BUILD_TESTS OR LASPP_BUILD_BENCHMARK)
Expand Down Expand Up @@ -86,81 +86,103 @@ endif()

# ── Performance optimization flags for aggressive Release builds
# ──────────────────────────
if(CMAKE_BUILD_TYPE MATCHES "Release" AND LASPP_AGGRESSIVE_OPTIMIZATIONS)
if(MSVC)
# Apply only to Release configuration using generator expressions
add_compile_options("$<$<CONFIG:Release>:/O2>" "$<$<CONFIG:Release>:/Ob2>"
"$<$<CONFIG:Release>:/GL>")
add_link_options("$<$<CONFIG:Release>:/LTCG>")
if(HAVE_AVX2)
add_compile_options("$<$<CONFIG:Release>:/arch:AVX2>")
message(STATUS "AVX2 optimizations enabled for Release builds")
endif()
message(
STATUS "Aggressive performance optimizations enabled for Release builds")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
# For single-config generators (Unix Makefiles), CMAKE_BUILD_TYPE check is
# sufficient
add_compile_options("-O3" "-march=native" "-flto" "-funroll-loops")
add_link_options("-flto")
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
add_compile_options("-fomit-frame-pointer" "-fno-semantic-interposition")
# Only apply when this is the top-level project to avoid affecting consuming
# projects
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
if(CMAKE_BUILD_TYPE MATCHES "Release" AND LASPP_AGGRESSIVE_OPTIMIZATIONS)
if(MSVC)
# Apply only to Release configuration using generator expressions
add_compile_options(
"$<$<CONFIG:Release>:/O2>" "$<$<CONFIG:Release>:/Ob2>"
"$<$<CONFIG:Release>:/GL>")
add_link_options("$<$<CONFIG:Release>:/LTCG>")
if(HAVE_AVX2)
add_compile_options("$<$<CONFIG:Release>:/arch:AVX2>")
message(STATUS "AVX2 optimizations enabled for Release builds")
endif()
message(
STATUS "Aggressive performance optimizations enabled for Release builds"
)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
# For single-config generators (Unix Makefiles), CMAKE_BUILD_TYPE check is
# sufficient
add_compile_options("-O3" "-march=native" "-flto" "-funroll-loops")
add_link_options("-flto")
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
add_compile_options("-fomit-frame-pointer"
"-fno-semantic-interposition")
endif()
message(
STATUS "Aggressive performance optimizations enabled for Release builds"
)
endif()
elseif(CMAKE_BUILD_TYPE MATCHES "Release")
message(
STATUS "Aggressive performance optimizations enabled for Release builds")
STATUS
"Using standard Release optimizations (LASPP_AGGRESSIVE_OPTIMIZATIONS=OFF)"
)
endif()
elseif(CMAKE_BUILD_TYPE MATCHES "Release")
message(
STATUS
"Using standard Release optimizations (LASPP_AGGRESSIVE_OPTIMIZATIONS=OFF)"
)
endif()

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(WIN32)
if(${CMAKE_CXX_COMPILER} MATCHES ".*mingw.*")
add_compile_options("-fstrict-enums" "-Wall" "-Wextra" "-Wpedantic"
"-Werror")
else()
add_compile_options(
"/W4"
"/WX"
# C4267/C4244: size_t/type narrowing inside MSVC STL template bodies.
"/wd4267" # size_t narrowing in MSVC STL <xutility> template
# instantiations
"/wd4244") # type narrowing in MSVC STL <xutility> template instantiations
endif()
elseif(UNIX)
if(NOT CMAKE_BUILD_TYPE MATCHES "Release")
add_compile_options("-Werror")
endif()
if(CMAKE_BUILD_TYPE MATCHES "Debug")
add_compile_options("-fsanitize=address,undefined"
"-fno-sanitize-recover=all")
add_link_options("-fsanitize=address,undefined")
endif()
add_compile_options(
"-fstrict-enums"
"-Wall"
"-Wextra"
"-Wpedantic"
"-Wdouble-promotion"
"-Wshadow"
"-Wpointer-arith"
"-Wstrict-aliasing"
"-Wnull-dereference"
"-Wdouble-promotion"
"-Wold-style-cast"
"-Wconversion"
"-Wsign-conversion")
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")

# Only apply strict compiler flags when this is the top-level project.
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
if(WIN32)
if(${CMAKE_CXX_COMPILER} MATCHES ".*mingw.*")
add_compile_options("-fstrict-enums" "-Wall" "-Wextra" "-Wpedantic"
"-Werror")
else()
add_compile_options(
"/W4"
"/WX"
# C4267/C4244: size_t/type narrowing inside MSVC STL template bodies.
"/wd4267" # size_t narrowing in MSVC STL <xutility> template
# instantiations
"/wd4244") # type narrowing in MSVC STL <xutility> template
# instantiations
endif()
elseif(UNIX)
if(NOT CMAKE_BUILD_TYPE MATCHES "Release")
add_compile_options("-Werror")
endif()
if(CMAKE_BUILD_TYPE MATCHES "Debug")
# Clang's static ASan conflicts with WSL2's address space layout (Windows
# kernel mappings can overlap with ASan's shadow memory region), causing
# random segfaults during ASan initialization. Use UBSan only for Clang;
# GCC's dynamic ASan works fine.
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
add_compile_options("-fsanitize=undefined" "-fno-sanitize-recover=all")
add_link_options("-fsanitize=undefined")
else()
add_compile_options("-fsanitize=address,undefined"
"-fno-sanitize-recover=all")
add_link_options("-fsanitize=address,undefined")
endif()
endif()
add_compile_options(
"-Wduplicated-branches" "-Wlogical-op" "-Wrestrict" "-Wcast-align=strict"
"-Wduplicated-cond" "-Wuseless-cast")
"-fstrict-enums"
"-Wall"
"-Wextra"
"-Wpedantic"
"-Wdouble-promotion"
"-Wshadow"
"-Wpointer-arith"
"-Wstrict-aliasing"
"-Wnull-dereference"
"-Wdouble-promotion"
"-Wold-style-cast"
"-Wconversion"
"-Wsign-conversion")
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
add_compile_options(
"-Wduplicated-branches" "-Wlogical-op" "-Wrestrict"
"-Wcast-align=strict" "-Wduplicated-cond" "-Wuseless-cast")
endif()
else()
message(FATAL_ERROR "Unsupported platform")
endif()
else()
message(FATAL_ERROR "Unsupported platform")
endif()

set(LIBRARY_NAME las++)
Expand Down Expand Up @@ -203,6 +225,10 @@ if(LASPP_BUILD_TESTS)
target_link_libraries(${TEST_NAME} PRIVATE ${LIBRARY_NAME})
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
target_compile_definitions(${TEST_NAME} PRIVATE LASPP_DEBUG_ASSERTS)
# MSVC needs /bigobj for large translation units in debug mode
if(MSVC)
target_compile_options(${TEST_NAME} PRIVATE $<$<CONFIG:Debug>:/bigobj>)
endif()
endforeach()

if(TARGET test_laszip_interop)
Expand Down
21 changes: 20 additions & 1 deletion apps/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,31 @@ add_executable(${LAS2LAS++_EXE_NAME} las2las++.cpp)

target_link_libraries(${LAS2LAS++_EXE_NAME} ${LIBRARY_NAME})

# MSVC needs /bigobj for large translation units in debug mode
if(MSVC)
target_compile_options(${LAS2LAS++_EXE_NAME}
PRIVATE $<$<CONFIG:Debug>:/bigobj>)
endif()

# Utility to create test files for benchmarking
add_executable(create_test_file create_test_file.cpp)
target_link_libraries(create_test_file PRIVATE ${LIBRARY_NAME})

set(INSPECT_VLRS_EXE_NAME las++-inspect-vlrs)

add_executable(${INSPECT_VLRS_EXE_NAME} inspect_vlrs.cpp)

target_link_libraries(${INSPECT_VLRS_EXE_NAME} ${LIBRARY_NAME})

set(VALIDATE_SPATIAL_INDEX_EXE_NAME las++-validate-spatial-index)

add_executable(${VALIDATE_SPATIAL_INDEX_EXE_NAME} validate_spatial_index.cpp)

target_link_libraries(${VALIDATE_SPATIAL_INDEX_EXE_NAME} ${LIBRARY_NAME})

install(
TARGETS ${LAS2LAS++_EXE_NAME}
TARGETS ${LAS2LAS++_EXE_NAME} ${INSPECT_VLRS_EXE_NAME}
${VALIDATE_SPATIAL_INDEX_EXE_NAME}
DESTINATION bin
COMPONENT applications)

Expand Down
12 changes: 6 additions & 6 deletions apps/create_test_file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,12 @@ int main(int argc, char* argv[]) {

// Format 1 with compression (0x81)
LASWriter writer(ofs, 0x81);
writer.header().transform().m_scale_factors.x() = 0.01;
writer.header().transform().m_scale_factors.y() = 0.01;
writer.header().transform().m_scale_factors.z() = 0.01;
writer.header().transform().m_offsets.x() = 0.0;
writer.header().transform().m_offsets.y() = 0.0;
writer.header().transform().m_offsets.z() = 0.0;
writer.header().transform().scale_factors().x() = 0.01;
writer.header().transform().scale_factors().y() = 0.01;
writer.header().transform().scale_factors().z() = 0.01;
writer.header().transform().offsets().x() = 0.0;
writer.header().transform().offsets().y() = 0.0;
writer.header().transform().offsets().z() = 0.0;

writer.write_points<LASPointFormat1>(std::span<const LASPointFormat1>(points), 50000);

Expand Down
91 changes: 91 additions & 0 deletions apps/inspect_vlrs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* SPDX-FileCopyrightText: (c) 2025-2026 Trailblaze Software, all rights reserved
* SPDX-License-Identifier: MIT
*/

#include <cmath>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>

#include "las_reader.hpp"
#include "spatial_index.hpp"
#include "utilities/assert.hpp"
#include "utilities/printing.hpp"
#include "vlr.hpp"

using namespace laspp;

int main(int argc, char* argv[]) {

Check warning on line 20 in apps/inspect_vlrs.cpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

apps/inspect_vlrs.cpp#L20

Method main has 60 lines of code (limit is 50)

Check warning on line 20 in apps/inspect_vlrs.cpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

apps/inspect_vlrs.cpp#L20

Method main has a cyclomatic complexity of 12 (limit is 8)

Check warning on line 20 in apps/inspect_vlrs.cpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

apps/inspect_vlrs.cpp#L20

Parameter 'argv' can be declared as const array
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <laz_file>" << std::endl;
return 1;
}

std::string filename = argv[1];
std::filesystem::path file_path(filename);
LASReader reader(file_path);

std::cout << "=== VLRs ===" << std::endl;
const auto& vlrs = reader.vlr_headers();
for (size_t i = 0; i < vlrs.size(); ++i) {
const auto& vlr = vlrs[i];
std::cout << "VLR " << i << ":" << std::endl;
std::cout << indented(vlr, " ");
std::cout << " Is LAZ VLR: " << (vlr.is_laz_vlr() ? "yes" : "no") << std::endl;
std::cout << " Is LAStools spatial index VLR: "
<< (vlr.is_lastools_spatial_index_vlr() ? "yes" : "no") << std::endl;
std::cout << std::endl;
}

std::cout << "=== EVLRs ===" << std::endl;
const auto& evlrs = reader.evlr_headers();
for (size_t i = 0; i < evlrs.size(); ++i) {
const auto& evlr = evlrs[i];
std::cout << "EVLR " << i << ":" << std::endl;
std::cout << indented(evlr, " ");
std::cout << " Is LAZ VLR: " << (evlr.is_laz_vlr() ? "yes" : "no") << std::endl;
std::cout << " Is LAStools spatial index EVLR: "
<< (evlr.is_lastools_spatial_index_evlr() ? "yes" : "no") << std::endl;

// If it's a LAStools spatial index, show more details
if (evlr.is_lastools_spatial_index_evlr()) {
LASPP_ASSERT(reader.has_lastools_spatial_index(),
"Failed to load LAStools spatial index from EVLR.");
std::cout << "Spatial index: " << std::endl;
const auto& index = reader.lastools_spatial_index();
const auto& quadtree = index.quadtree_header();

std::cout << " Spatial index header:" << std::endl;
std::cout << indented(quadtree, " ");
std::cout << " Number of cells: " << index.num_cells() << std::endl;

// Show cell details
size_t total_intervals = 0;
for (const auto& [cell_index, cell] : index.cells()) {
total_intervals += cell.intervals.size();
}
std::cout << " Total intervals: " << total_intervals << std::endl;
std::cout << " Average intervals per cell: " << std::fixed << std::setprecision(1)
<< (index.num_cells() > 0 ? static_cast<double>(total_intervals) /
static_cast<double>(index.num_cells())
: 0.0)
<< std::endl;

// Show first few cells as examples
std::cout << indented(limited_map(index.cells(), 3), " ") << std::endl;
}
std::cout << std::endl;
}

if (reader.has_lastools_spatial_index()) {
const auto& spatial_index = reader.lastools_spatial_index();
std::cout << "Loaded LAStools spatial index with " << spatial_index.num_cells() << " cells."
<< std::endl;
std::cout << "Spatial index details:" << std::endl;
std::cout << indented(spatial_index, " ") << std::endl;
}

return 0;
}
Loading
Loading