Skip to content

Commit

Permalink
FEAT-700: Writer constructor fix for copying CopcConfig and updating …
Browse files Browse the repository at this point in the history
…values (#98)

* Fix writer copy constructor from reader config.

* Extended tests for writer constructor changes.

* Add copy of extents.

* Update tests to fully test copy from reader to writer.

* Update CHANGELOG.md.

* Clean-up code and add python tests.

* Update CHANGELOG.md.

* Review clean-up.

* Fix python test.
  • Loading branch information
leo-stan authored Feb 2, 2022
1 parent c1d028f commit fc6d425
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 29 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed

- **\[Python/C++\]** Fix bug with some attributes not getting copied when constructing `FileWriter` from `CopcConfig`.

## [2.2.2] - 2022-01-31

### Fixed
Expand Down
17 changes: 12 additions & 5 deletions cpp/include/copc-lib/copc/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,27 @@ class CopcConfigWriter : public CopcConfig
const Vector3 &offset = Vector3::DefaultOffset(), const std::string &wkt = "",
const las::EbVlr &extra_bytes_vlr = las::EbVlr(0), bool has_extended_stats = false);

// Copy constructor
CopcConfigWriter(const CopcConfigWriter &copc_config)
: CopcConfig(copc_config.LasHeader(), copc_config.CopcInfo(), copc_config.CopcExtents(), copc_config.Wkt(),
copc_config.ExtraBytesVlr())
// Copy Constructor
CopcConfigWriter(const CopcConfigWriter &copc_config_writer)
: CopcConfig(copc_config_writer.LasHeader(), copc_config_writer.CopcInfo(), copc_config_writer.CopcExtents(),
copc_config_writer.Wkt(), copc_config_writer.ExtraBytesVlr())
{
}

// Allow copy from CopcFile
// Copy Constructor from CopcFile
CopcConfigWriter(const CopcConfig &copc_config)
: CopcConfig(copc_config.LasHeader(), copc_config.CopcInfo(), copc_config.CopcExtents(), copc_config.Wkt(),
copc_config.ExtraBytesVlr())
{
}

// Constructor from Config elements
CopcConfigWriter(const las::LasHeader &header, const copc::CopcInfo &copc_info,
const copc::CopcExtents &copc_extents, const std::string &wkt, const las::EbVlr &extra_bytes_vlr)
: CopcConfig(header, copc_info, copc_extents, wkt, extra_bytes_vlr)
{
}

std::shared_ptr<las::LasHeader> LasHeader() { return header_; }
las::LasHeader LasHeader() const override { return *header_; }

Expand Down
8 changes: 7 additions & 1 deletion cpp/include/copc-lib/copc/extents.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ class CopcExtents
CopcExtents(int8_t point_format_id, uint16_t num_eb_items = 0, bool has_extended_stats = false);

// Copy constructor
CopcExtents(const CopcExtents &extents);
CopcExtents(const CopcExtents &other);

// Copy constructor with updated protected attributes
CopcExtents(const CopcExtents &other, int8_t point_format_id, uint16_t num_eb_items, bool has_extended_stats);

// VLR constructor
CopcExtents(const las::CopcExtentsVlr &vlr, int8_t point_format_id, uint16_t num_eb_items = 0,
Expand Down Expand Up @@ -198,5 +201,8 @@ class CopcExtents
bool has_extended_stats_{false};
std::vector<std::shared_ptr<CopcExtent>> extents_;
};

uint8_t PointBaseNumberExtents(const int8_t &point_format_id);

} // namespace copc
#endif // COPCLIB_COPC_EXTENTS_H_
4 changes: 4 additions & 0 deletions cpp/include/copc-lib/las/header.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ class LasHeader
point_count_(point_count), vlr_count_(vlr_count), scale_(scale), offset_(offset), evlr_offset_(evlr_offset),
evlr_count_(evlr_count){};

// Copy constructor with modification of protected attributes
LasHeader(const LasHeader &header, int8_t point_format_id, uint16_t point_record_length, const Vector3 &scale,
const Vector3 &offset);

static LasHeader FromLazPerf(const lazperf::header14 &header);
lazperf::header14 ToLazPerf(uint32_t point_offset, uint64_t point_count, uint64_t evlr_offset, uint32_t evlr_count,
bool eb_flag, bool extended_stats_flag) const;
Expand Down
54 changes: 46 additions & 8 deletions cpp/src/copc/extents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,46 @@ CopcExtents::CopcExtents(int8_t point_format_id, uint16_t num_eb_items, bool has
}

// Copy constructor
CopcExtents::CopcExtents(const CopcExtents &extents)
: point_format_id_(extents.PointFormatId()), has_extended_stats_(extents.HasExtendedStats())
CopcExtents::CopcExtents(const CopcExtents &other)
: point_format_id_(other.PointFormatId()), has_extended_stats_(other.HasExtendedStats())
{
extents_.reserve(extents.NumberOfExtents());
for (int i{0}; i < extents.NumberOfExtents(); i++)
extents_.push_back(std::make_shared<CopcExtent>(extents.Extents()[i]));
extents_.reserve(other.NumberOfExtents());
for (int i{0}; i < other.NumberOfExtents(); i++)
extents_.push_back(std::make_shared<CopcExtent>(other.Extents()[i]));
}

CopcExtents::CopcExtents(const CopcExtents &other, int8_t point_format_id, uint16_t num_eb_items,
bool has_extended_stats)
: CopcExtents(point_format_id, num_eb_items, has_extended_stats)
{
// Copy generic extents
for (int i{0}; i <= 10; i++)
extents_[i] = other.extents_[i];

// Copy RGB
if (point_format_id > 6 && other.point_format_id_ > 6)
{
extents_[11] = other.extents_[11];
extents_[12] = other.extents_[12];
extents_[13] = other.extents_[13];
}

// Copy NIR
if (point_format_id == 8 && other.point_format_id_ == 8)
extents_[14] = other.extents_[14];

// Copy EBs
auto other_num_eb = other.NumberOfExtents() - PointBaseNumberExtents(other.PointFormatId());
if (num_eb_items != other_num_eb)
{
std::cout << "CopcExtents: Warning, number of extra byte has changed, can't copy values over" << std::endl;
}
else
{
for (int i{0}; i < num_eb_items; i++)
extents_[PointBaseNumberExtents(point_format_id_) + i] =
other.extents_[PointBaseNumberExtents(other.PointFormatId()) + i];
}
}

// VLR constructor
Expand Down Expand Up @@ -126,8 +160,7 @@ void CopcExtents::SetExtendedStats(const las::CopcExtentsVlr &vlr)

int CopcExtents::NumberOfExtents(int8_t point_format_id, uint16_t num_eb_items)
{
return las::PointBaseNumberDimensions(point_format_id) - 3 +
num_eb_items; // -3 disregards x,y,z since they are not handled in Extents
return PointBaseNumberExtents(point_format_id) + num_eb_items;
}

size_t CopcExtents::ByteSize(int8_t point_format_id, uint16_t num_eb_items)
Expand Down Expand Up @@ -159,11 +192,16 @@ std::string CopcExtents::ToString() const
if (point_format_id_ == 8)
ss << "\tNIR: " << extents_[14]->ToString() << std::endl;
ss << "\tExtra Bytes:" << std::endl;
for (int i = las::PointBaseNumberDimensions(point_format_id_); i < extents_.size(); i++)
for (int i = PointBaseNumberExtents(point_format_id_); i < extents_.size(); i++)
{
ss << "\t\t" << extents_[i]->ToString() << std::endl;
}
return ss.str();
}

uint8_t PointBaseNumberExtents(const int8_t &point_format_id)
{
return las::PointBaseNumberDimensions(point_format_id) - 3; // -3 is because we don't have x,y,z extents
}

} // namespace copc
15 changes: 12 additions & 3 deletions cpp/src/io/writer_public.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,18 @@ void Writer::InitWriter(std::ostream &out_stream, const CopcConfigWriter &copc_c
if (has_extended_stats)
new_has_extended_stats = *has_extended_stats;

CopcConfigWriter cfg(new_point_format_id, new_scale, new_offset, new_wkt, new_extra_bytes_vlr,
new_has_extended_stats);
this->config_ = std::make_shared<CopcConfigWriter>(cfg);
las::LasHeader new_header(copc_config_writer.LasHeader(), new_point_format_id,
las::PointBaseByteSize(new_point_format_id) +
las::NumBytesFromExtraBytes(new_extra_bytes_vlr.items),
new_scale, new_offset);

copc::CopcExtents new_extents(copc_config_writer.CopcExtents(), new_point_format_id,
new_extra_bytes_vlr.items.size(), new_has_extended_stats);

copc::CopcConfigWriter new_copc_config_writer(new_header, copc_config_writer.CopcInfo(), new_extents, new_wkt,
new_extra_bytes_vlr);

this->config_ = std::make_shared<CopcConfigWriter>(new_copc_config_writer);
}
else
{
Expand Down
11 changes: 11 additions & 0 deletions cpp/src/las/header.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@

namespace copc::las
{

LasHeader::LasHeader(const LasHeader &header, int8_t point_format_id, uint16_t point_record_length,
const Vector3 &scale, const Vector3 &offset)
: LasHeader(header)
{
point_format_id_ = point_format_id;
point_record_length_ = point_record_length;
scale_ = scale;
offset_ = offset;
}

Box LasHeader::Bounds() const { return Box(min, max); }

uint16_t LasHeader::EbByteSize() const { return copc::las::EbByteSize(point_format_id_, point_record_length_); }
Expand Down
67 changes: 61 additions & 6 deletions test/writer_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,36 @@ TEST_CASE("Writer Config Tests", "[Writer]")

SECTION("Copy and update")
{
FileReader orig("autzen-classified.copc.laz");
// Create test file
int8_t orig_point_format_id(7);
Vector3 orig_scale(0.01, 0.01, 0.01);
Vector3 orig_offset(10, 10, 10);
std::string orig_wkt("orig_wkt");
bool orig_has_extended_stats(false);
las::EbVlr orig_eb_vlr(1);
std::string orig_guid("orig_guid");
double orig_spacing(10);
double orig_intensity(23.5);

CopcConfigWriter orig_cfg(orig_point_format_id, orig_scale, orig_offset, orig_wkt, orig_eb_vlr,
orig_has_extended_stats);

FileWriter writer("orig_test.copc.laz", orig_cfg);
writer.CopcConfig()->LasHeader()->GUID(orig_guid);
writer.CopcConfig()->CopcInfo()->spacing = orig_spacing;
writer.CopcConfig()->CopcExtents()->Intensity()->maximum = orig_intensity;
writer.Close();

// Read test file
FileReader orig("orig_test.copc.laz");

string file_path = "writer_test.copc.laz";
auto cfg = orig.CopcConfig();

uint8_t new_point_format_id(8);
int8_t new_point_format_id(8);
Vector3 new_scale(10, 10, 10);
Vector3 new_offset(100, 100, 100);
std::string new_wkt("test_wkt");
std::string new_wkt("new_wkt");
bool new_has_extended_stats(true);
las::EbVlr new_eb_vlr(2);
// Update Point Format ID
Expand All @@ -174,10 +195,14 @@ TEST_CASE("Writer Config Tests", "[Writer]")
orig.CopcConfig().ExtraBytesVlr().items.size());
REQUIRE(writer.CopcConfig()->CopcExtents()->HasExtendedStats() ==
orig.CopcConfig().CopcExtents().HasExtendedStats());
// Check that other attributes have been copied
REQUIRE(writer.CopcConfig()->CopcInfo()->spacing == orig.CopcConfig().CopcInfo().spacing);
REQUIRE(writer.CopcConfig()->LasHeader()->GUID() == orig.CopcConfig().LasHeader().GUID());
REQUIRE(writer.CopcConfig()->CopcExtents()->Intensity()->maximum ==
orig.CopcConfig().CopcExtents().Intensity()->maximum);

// Check that we can add a point of new format
auto new_points = las::Points(new_point_format_id, writer.CopcConfig()->LasHeader()->Scale(),
writer.CopcConfig()->LasHeader()->Offset());
auto new_points = las::Points(*writer.CopcConfig()->LasHeader());
auto new_point = new_points.CreatePoint();
new_point->UnscaledX(10);
new_point->UnscaledY(15);
Expand All @@ -193,7 +218,7 @@ TEST_CASE("Writer Config Tests", "[Writer]")
auto old_points = las::Points(orig.CopcConfig().LasHeader());
auto old_point = old_points.CreatePoint();
old_points.AddPoint(old_point);
REQUIRE_THROWS(writer.AddNode(VoxelKey::RootKey(), old_points));
// REQUIRE_THROWS(writer.AddNode(VoxelKey::RootKey(), old_points));

writer.Close();

Expand Down Expand Up @@ -231,6 +256,11 @@ TEST_CASE("Writer Config Tests", "[Writer]")
orig.CopcConfig().ExtraBytesVlr().items.size());
REQUIRE(writer.CopcConfig()->CopcExtents()->HasExtendedStats() ==
orig.CopcConfig().CopcExtents().HasExtendedStats());
// Check that other attributes have been copied
REQUIRE(writer.CopcConfig()->CopcInfo()->spacing == orig.CopcConfig().CopcInfo().spacing);
REQUIRE(writer.CopcConfig()->LasHeader()->GUID() == orig.CopcConfig().LasHeader().GUID());
REQUIRE(writer.CopcConfig()->CopcExtents()->Intensity()->maximum ==
orig.CopcConfig().CopcExtents().Intensity()->maximum);
writer.Close();

FileReader reader(file_path);
Expand All @@ -257,6 +287,11 @@ TEST_CASE("Writer Config Tests", "[Writer]")
orig.CopcConfig().ExtraBytesVlr().items.size());
REQUIRE(writer.CopcConfig()->CopcExtents()->HasExtendedStats() ==
orig.CopcConfig().CopcExtents().HasExtendedStats());
// Check that other attributes have been copied
REQUIRE(writer.CopcConfig()->CopcInfo()->spacing == orig.CopcConfig().CopcInfo().spacing);
REQUIRE(writer.CopcConfig()->LasHeader()->GUID() == orig.CopcConfig().LasHeader().GUID());
REQUIRE(writer.CopcConfig()->CopcExtents()->Intensity()->maximum ==
orig.CopcConfig().CopcExtents().Intensity()->maximum);
writer.Close();

FileReader reader(file_path);
Expand Down Expand Up @@ -284,6 +319,11 @@ TEST_CASE("Writer Config Tests", "[Writer]")
orig.CopcConfig().ExtraBytesVlr().items.size());
REQUIRE(writer.CopcConfig()->CopcExtents()->HasExtendedStats() ==
orig.CopcConfig().CopcExtents().HasExtendedStats());
// Check that other attributes have been copied
REQUIRE(writer.CopcConfig()->CopcInfo()->spacing == orig.CopcConfig().CopcInfo().spacing);
REQUIRE(writer.CopcConfig()->LasHeader()->GUID() == orig.CopcConfig().LasHeader().GUID());
REQUIRE(writer.CopcConfig()->CopcExtents()->Intensity()->maximum ==
orig.CopcConfig().CopcExtents().Intensity()->maximum);
writer.Close();

FileReader reader(file_path);
Expand Down Expand Up @@ -311,6 +351,11 @@ TEST_CASE("Writer Config Tests", "[Writer]")
REQUIRE(writer.CopcConfig()->ExtraBytesVlr().items.size() == new_eb_vlr.items.size());
REQUIRE(writer.CopcConfig()->CopcExtents()->HasExtendedStats() ==
orig.CopcConfig().CopcExtents().HasExtendedStats());
// Check that other attributes have been copied
REQUIRE(writer.CopcConfig()->CopcInfo()->spacing == orig.CopcConfig().CopcInfo().spacing);
REQUIRE(writer.CopcConfig()->LasHeader()->GUID() == orig.CopcConfig().LasHeader().GUID());
REQUIRE(writer.CopcConfig()->CopcExtents()->Intensity()->maximum ==
orig.CopcConfig().CopcExtents().Intensity()->maximum);
writer.Close();

FileReader reader(file_path);
Expand All @@ -336,6 +381,11 @@ TEST_CASE("Writer Config Tests", "[Writer]")
REQUIRE(writer.CopcConfig()->ExtraBytesVlr().items.size() ==
orig.CopcConfig().ExtraBytesVlr().items.size());
REQUIRE(writer.CopcConfig()->CopcExtents()->HasExtendedStats() == new_has_extended_stats);
// Check that other attributes have been copied
REQUIRE(writer.CopcConfig()->CopcInfo()->spacing == orig.CopcConfig().CopcInfo().spacing);
REQUIRE(writer.CopcConfig()->LasHeader()->GUID() == orig.CopcConfig().LasHeader().GUID());
REQUIRE(writer.CopcConfig()->CopcExtents()->Intensity()->maximum ==
orig.CopcConfig().CopcExtents().Intensity()->maximum);
writer.Close();

FileReader reader(file_path);
Expand All @@ -360,6 +410,11 @@ TEST_CASE("Writer Config Tests", "[Writer]")
REQUIRE(writer.CopcConfig()->Wkt() == new_wkt);
REQUIRE(writer.CopcConfig()->ExtraBytesVlr().items.size() == new_eb_vlr.items.size());
REQUIRE(writer.CopcConfig()->CopcExtents()->HasExtendedStats() == new_has_extended_stats);
// Check that other attributes have been copied
REQUIRE(writer.CopcConfig()->CopcInfo()->spacing == orig.CopcConfig().CopcInfo().spacing);
REQUIRE(writer.CopcConfig()->LasHeader()->GUID() == orig.CopcConfig().LasHeader().GUID());
REQUIRE(writer.CopcConfig()->CopcExtents()->Intensity()->maximum ==
orig.CopcConfig().CopcExtents().Intensity()->maximum);
writer.Close();

FileReader reader(file_path);
Expand Down
Loading

0 comments on commit fc6d425

Please sign in to comment.