Skip to content

Commit

Permalink
Deal with ClassFlags appropriately for PDAL 2.6+ (#148)
Browse files Browse the repository at this point in the history
* Write bits dimensions to a special PDAL dimension.

* Add missed header.

* Add support for version 1.3 and earlier LAS with PDAL 2.5 and earlier.

* Disable EPT support for the moment.

* Fix setting of extra dims.

---------

Co-authored-by: Andrew Bell <[email protected]>
  • Loading branch information
hobu and abellgithub authored Dec 13, 2023
1 parent b150b25 commit 77a242e
Show file tree
Hide file tree
Showing 12 changed files with 260 additions and 200 deletions.
1 change: 0 additions & 1 deletion bu/CopcSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@

#include "../untwine/Common.hpp"
#include "../untwine/FileDimInfo.hpp"
#include "../untwine/Las.hpp"

namespace untwine
{
Expand Down
2 changes: 2 additions & 0 deletions bu/PointAccessor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ class PointAccessor

size_t size()
{
// start() returns the number of points in the file infos before the last one.
// adding numPoints() provides the total number of points.
if (m_fileInfos.empty())
return 0;
else
Expand Down
62 changes: 12 additions & 50 deletions bu/Processor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
#include <random>

#include "../untwine/GridKey.hpp"
#include "../untwine/Las.hpp"

#include <pdal/PDALUtils.hpp>
#include <pdal/StageFactory.hpp>
Expand Down Expand Up @@ -330,7 +329,6 @@ Processor::writeOctantCompressed(const OctantInfo& o, Index& index, IndexIter po
// there's no reason why it should change. We should modify things to use a single
// layout.

Dimension::IdList lasDims = pdrfDims(m_b.pointFormatId);
DimInfoList dims = m_b.dimInfo;
m_extraDims.clear();
for (FileDimInfo& fdi : dims)
Expand All @@ -346,7 +344,7 @@ Processor::writeOctantCompressed(const OctantInfo& o, Index& index, IndexIter po
else
stats.push_back({fdi.dim, Stats(fdi.name, Stats::EnumType::NoEnum, false)});
}
if (!Utils::contains(lasDims, fdi.dim))
if (fdi.extraDim)
m_extraDims.push_back(DimType(fdi.dim, fdi.type));
}
table.finalize();
Expand Down Expand Up @@ -530,7 +528,7 @@ void Processor::createChunk(const VoxelKey& key, pdal::PointViewPtr view)
for (PointId idx = 0; idx < view->size(); ++idx)
{
PointRef point(*view, idx);
fillPointBuf(point, buf);
fillPointBuf(point, buf, layout->findDim(UntwineBitsDimName));
compressor.compress(buf.data());
}
std::vector<unsigned char> chunk = compressor.done();
Expand All @@ -546,14 +544,13 @@ void Processor::createChunk(const VoxelKey& key, pdal::PointViewPtr view)
throw FatalError("Failure writing to '" + m_b.opts.outputName + "'.");
}

void Processor::fillPointBuf(pdal::PointRef& point, std::vector<char>& buf)
void Processor::fillPointBuf(pdal::PointRef& point, std::vector<char>& buf,
pdal::Dimension::Id bitsDim)
{
using namespace pdal;

LeInserter ostream(buf.data(), buf.size());

// We only write PDRF 6, 7, or 8.
bool has14PointFormat = true;
bool hasTime = true; // m_lasHeader.hasTime();
bool hasColor = m_b.pointFormatId == 7 || m_b.pointFormatId == 8;
bool hasInfrared = m_b.pointFormatId == 8;
Expand Down Expand Up @@ -589,51 +586,16 @@ void Processor::fillPointBuf(pdal::PointRef& point, std::vector<char>& buf)
ostream << z;

ostream << point.getFieldAs<uint16_t>(Id::Intensity);

uint8_t scanChannel = point.getFieldAs<uint8_t>(Id::ScanChannel);
uint8_t scanDirectionFlag = point.getFieldAs<uint8_t>(Id::ScanDirectionFlag);
uint8_t edgeOfFlightLine = point.getFieldAs<uint8_t>(Id::EdgeOfFlightLine);
uint8_t classification = point.getFieldAs<uint8_t>(Id::Classification);

if (has14PointFormat)
{
uint8_t bits = returnNumber | (numberOfReturns << 4);
ostream << bits;

uint8_t classFlags;
if (point.hasDim(Id::ClassFlags))
classFlags = point.getFieldAs<uint8_t>(Id::ClassFlags);
else
classFlags = classification >> 5;
bits = (classFlags & 0x0F) |
((scanChannel & 0x03) << 4) |
((scanDirectionFlag & 0x01) << 6) |
((edgeOfFlightLine & 0x01) << 7);
ostream << bits;
}
else
{
uint8_t bits = returnNumber | (numberOfReturns << 3) |
(scanDirectionFlag << 6) | (edgeOfFlightLine << 7);
ostream << bits;
}

ostream << classification;
ostream << (uint8_t)(returnNumber | (numberOfReturns << 4));
ostream << point.getFieldAs<uint8_t>(bitsDim);
ostream << point.getFieldAs<uint8_t>(Id::Classification);

uint8_t userData = point.getFieldAs<uint8_t>(Id::UserData);
if (has14PointFormat)
{
// Guaranteed to fit if scan angle rank isn't wonky.
int16_t scanAngleRank =
static_cast<int16_t>(std::round(
point.getFieldAs<float>(Id::ScanAngleRank) / .006f));
ostream << userData << scanAngleRank;
}
else
{
int8_t scanAngleRank = point.getFieldAs<int8_t>(Id::ScanAngleRank);
ostream << scanAngleRank << userData;
}
// Guaranteed to fit if scan angle rank isn't wonky.
int16_t scanAngleRank =
static_cast<int16_t>(std::round(
point.getFieldAs<float>(Id::ScanAngleRank) / .006f));
ostream << userData << scanAngleRank;

ostream << point.getFieldAs<uint16_t>(Id::PointSourceId);

Expand Down
2 changes: 1 addition & 1 deletion bu/Processor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class Processor
void writeEptFile(const std::string& filename, pdal::PointViewPtr view);
void createChunk(const VoxelKey& key, pdal::PointViewPtr view);
void sortChunk(pdal::PointViewPtr view);
void fillPointBuf(pdal::PointRef& point, std::vector<char>& buf);
void fillPointBuf(pdal::PointRef& point, std::vector<char>& buf, pdal::Dimension::Id bitsDim);

VoxelInfo m_vi;
const BaseInfo& m_b;
Expand Down
72 changes: 52 additions & 20 deletions epf/Epf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
#include "Reprocessor.hpp"
#include "Writer.hpp"
#include "../untwine/Common.hpp"
#include "../untwine/Las.hpp"

#include <cmath>
#include <filesystem>
Expand All @@ -32,7 +31,6 @@
#include <pdal/util/FileUtils.hpp>
#include <pdal/util/ProgramArgs.hpp>


namespace
{

Expand Down Expand Up @@ -100,6 +98,24 @@ std::vector<std::string> directoryList(const std::string& dir)
}
#endif

pdal::Dimension::Type getDimensionType(const std::string& name)
{
using namespace pdal;

if (name == untwine::UntwineBitsDimName)
return untwine::UntwineBitsType;

Dimension::Type type = Dimension::Type::Double;
try
{
type = Dimension::defaultType(Dimension::id(name));
}
catch (pdal_error&)
{}

return type;
}

} // unnamed namespace

namespace untwine
Expand All @@ -117,7 +133,6 @@ Epf::Epf(BaseInfo& common) : m_b(common), m_pool(NumFileProcessors)
Epf::~Epf()
{}


void Epf::run(ProgressWriter& progress)
{
using namespace pdal;
Expand Down Expand Up @@ -148,21 +163,17 @@ void Epf::run(ProgressWriter& progress)
for (const FileDimInfo& fdi : fi.dimInfo)
allDimNames.insert(fdi.name);

// Create an OUTPUT layout.
// Register the dimensions, either as the default type or double if we don't know
// what it is.
PointLayoutPtr layout(new PointLayout());
for (const std::string& dimName : allDimNames)
for (std::string dimName : allDimNames)
{
Dimension::Type type;
try
{
type = Dimension::defaultType(Dimension::id(dimName));
}
catch (pdal::pdal_error&)
{
type = Dimension::Type::Double;
}
layout->registerOrAssignDim(dimName, type);
// If this is a "bit" dimension, don't add it, but instead register the proxy
// untwine bits dimension.
if (isUntwineBitsDim(dimName))
dimName = UntwineBitsDimName;
layout->registerOrAssignDim(dimName, getDimensionType(dimName));
}
layout->finalize();

Expand All @@ -171,9 +182,20 @@ void Epf::run(ProgressWriter& progress)
{
for (FileDimInfo& di : fi.dimInfo)
{
di.dim = layout->findDim(di.name);
di.type = layout->dimType(di.dim);
di.offset = layout->dimOffset(di.dim);
// If this dimension is one of the bit dimensions, set the shift and offset accordingly.
int bitPos = getUntwineBitPos(di.name);
if (bitPos != -1)
{
di.type = UntwineBitsType;
di.offset = layout->dimOffset(layout->findDim(UntwineBitsDimName));
di.shift = bitPos;
}
else
{
Dimension::Id dim = layout->findDim(di.name);
di.type = layout->dimType(dim);
di.offset = layout->dimOffset(dim);
}
}
}

Expand Down Expand Up @@ -274,15 +296,13 @@ void Epf::fillMetadata(const pdal::PointLayoutPtr layout)
else
m_b.pointFormatId = 6;

const Dimension::IdList& lasDims = pdrfDims(m_b.pointFormatId);
for (Dimension::Id id : layout->dims())
{
FileDimInfo di;
di.name = layout->dimName(id);
di.type = layout->dimType(id);
di.offset = layout->dimOffset(id);
di.dim = id;
di.extraDim = !Utils::contains(lasDims, id);
di.extraDim = isExtraDim(di.name);
m_b.pointSize += pdal::Dimension::size(di.type);
m_b.dimInfo.push_back(di);
}
Expand Down Expand Up @@ -374,6 +394,8 @@ PointCount Epf::createFileInfo(const StringList& input, StringList dimNames,

QuickInfo qi = s->preview();

// Detect LAS input with LAS version < 4 so that we can handle the legacy
// classification bits.
if (!qi.valid())
throw FatalError("Couldn't get quick info for '" + filename + "'.");

Expand Down Expand Up @@ -404,6 +426,16 @@ PointCount Epf::createFileInfo(const StringList& input, StringList dimNames,
fi.filename = filename;
fi.driver = driver;

// Detect LAS input with LAS version < 4 so that we can handle the legacy
// classification bits.
if (driver == "readers.las")
{
pdal::MetadataNode minor = root.findChild("minor_version");
pdal::MetadataNode major = root.findChild("major_version");
if (minor.valid() && major.valid())
fi.fileVersion = 10 * major.value<int>() + minor.value<int>();
}

// Accept dimension names if there are no limits or this name is in the list
// of desired dimensions.
for (const std::string& name : qi.m_dimNames)
Expand Down
7 changes: 6 additions & 1 deletion epf/EpfTypes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ constexpr int NumFileProcessors = 8;

struct FileInfo
{
FileInfo() : numPoints(0), start(0)
FileInfo() :
numPoints(0), start(0), untwineBitsDim(pdal::Dimension::Id::Unknown), fileVersion(0)
{}

std::string filename;
Expand All @@ -51,6 +52,10 @@ struct FileInfo
uint64_t start;
pdal::BOX3D bounds;
pdal::SpatialReference srs;
pdal::Dimension::Id untwineBitsDim;
int untwineBitsOffset;
// Currently only set for LAS files.
int fileVersion;

bool valid() const
{ return filename.size(); }
Expand Down
Loading

0 comments on commit 77a242e

Please sign in to comment.