Skip to content

Commit

Permalink
writers.copc: fix legacy point/return counts
Browse files Browse the repository at this point in the history
According to the LAS 1.4 r15 spec (§2.4)
> Legacy Number of Point Records
> This field contains the total number of point records within the file
> if the file is maintaining legacy compatibility, the number of points
> is no greater than UINT32_MAX, and the Point Data Record Format is
> less than 6. Otherwise, it must be set to zero.

> Legacy Number of Points by Return
> These fields contain an array of the total point records per return if
> the file is maintaining legacy compatibility, the number of points is
> no greater than UINT32_MAX, and the Point Data Record Format is less
> than 6. Otherwise, each member of the array must be set to zero.

All COPC files are LAS1.4 & PDRF>=6, so alway set the legacy point
count and return count fields to zero to match the spec. PDAL#2235

* extend the LasTester class to access LasReader internal headers
* add the COPC tests to the issue2235 LAS test case
  • Loading branch information
rcoup committed Sep 21, 2023
1 parent 48080bc commit f1ac063
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 11 deletions.
5 changes: 5 additions & 0 deletions io/LasReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ const LasHeader& LasReader::header() const
return d->apiHeader;
}

const las::Header& LasReader::lasHeader() const
{
return d->header;
}

uint64_t LasReader::vlrData(const std::string& userId, uint16_t recordId, char const * & data)
{
const las::Vlr *vlr = las::findVlr(userId, recordId, d->vlrs);
Expand Down
4 changes: 4 additions & 0 deletions io/LasReader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ class LasHeader;

class PDAL_DLL LasReader : public Reader, public Streamable
{
friend class LasTester;

protected:
class LasStreamIf
{
Expand Down Expand Up @@ -121,6 +123,8 @@ class PDAL_DLL LasReader : public Reader, public Streamable
void loadExtraDims(LeExtractor& istream, PointRef& data);
point_count_t readFileBlock(std::vector<char>& buf, point_count_t maxPoints);

const las::Header& lasHeader() const;

struct Options;
struct Private;
std::unique_ptr<Private> d;
Expand Down
15 changes: 4 additions & 11 deletions io/private/copcwriter/Output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ Output::Output(const BaseInfo& b) : b(b)
m_header.minz = b.stats[(int)stats::Index::Z].minimum();
m_header.maxz = b.stats[(int)stats::Index::Z].maximum();

// legacy point counts are all zero, since COPC is LAS 1.4 only.
std::fill(std::begin(m_header.points_by_return), std::end(m_header.points_by_return), 0);
m_header.point_count = 0;

for (int i = 1; i <= 15; ++i)
{
point_count_t count = 0;
Expand All @@ -91,18 +95,8 @@ Output::Output(const BaseInfo& b) : b(b)
{}

m_header.points_by_return_14[i - 1] = count;
if (i <= 5)
{
if (m_header.points_by_return_14[i] <= (std::numeric_limits<uint32_t>::max)())
m_header.points_by_return[i - 1] = m_header.points_by_return_14[i - 1];
else
m_header.points_by_return[i - 1] = 0;
}
}

if (m_header.point_count_14 > (std::numeric_limits<uint32_t>::max)())
m_header.point_count = 0;

// Note that only these VLRs go in the standard VLR area. The rest are written as
// extended VLRs.
setupVlrs();
Expand Down Expand Up @@ -231,7 +225,6 @@ uint64_t Output::newChunk(const VoxelKey& key, int32_t size, int32_t count)
m_pointPos += size;
assert(count <= (std::numeric_limits<int32_t>::max)() && count >= 0);
m_chunkTable.push_back({(uint64_t)count, (uint64_t)size});
m_header.point_count += count;
m_header.point_count_14 += count;
m_hierarchy[key] = { chunkStart, size, count };
return chunkStart;
Expand Down
38 changes: 38 additions & 0 deletions test/unit/io/LasWriterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include <pdal/util/Algorithm.hpp>
#include <pdal/util/FileUtils.hpp>
#include <io/BufferReader.hpp>
#include <io/CopcWriter.hpp>
#include <io/LasHeader.hpp>
#include <io/LasReader.hpp>
#include <io/LasWriter.hpp>
Expand All @@ -68,6 +69,8 @@ class LasTester
public:
const las::Header& header(LasWriter& w)
{ return w.header(); }
const las::Header& header(LasReader& r)
{ return r.lasHeader(); }
SpatialReference srs(LasWriter& w)
{ return w.m_srs; }
void addVlr(LasWriter& w, const std::string& userId, uint16_t recordId,
Expand Down Expand Up @@ -2301,4 +2304,39 @@ TEST(LasWriterTest, issue2235)
EXPECT_NE(h46.ePointsByReturn[i], 0);
}
}

// test for COPC too
{
Options copcWriterOps;
copcWriterOps.add("filename", Support::temppath("out_4_6.copc.laz"));

CopcWriter copcWriter;
copcWriter.setInput(reader);
copcWriter.setOptions(copcWriterOps);

PointTable tableCopc;
copcWriter.prepare(tableCopc);
copcWriter.execute(tableCopc);

Options copcReaderOpts;
copcReaderOpts.add("filename", Support::temppath("out_4_6.las"));

LasReader readerCopc;
readerCopc.setOptions(copcReaderOpts);

PointTable tableCopcR;
readerCopc.prepare(tableCopcR);

const las::Header& headerCopc = tester.header(readerCopc);

EXPECT_EQ(headerCopc.legacyPointCount, 0);
EXPECT_NE(headerCopc.pointCount(), 0);

for (int i=0; i<headerCopc.LegacyReturnCount; i++)
{
EXPECT_EQ(headerCopc.legacyPointsByReturn[i], 0);
if (i < returnsWithPoints)
EXPECT_NE(headerCopc.ePointsByReturn[i], 0);
}
}
}

0 comments on commit f1ac063

Please sign in to comment.