Skip to content

Commit

Permalink
Add support for VLR forwarding.
Browse files Browse the repository at this point in the history
  • Loading branch information
abellgithub committed Sep 3, 2015
1 parent 7d5855c commit dcef66c
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 32 deletions.
19 changes: 15 additions & 4 deletions doc/stages/writers.las.rst
Expand Up @@ -50,10 +50,21 @@ forward
which is equivalent to specifying all the values EXCEPT the scale and
offset values. Scale and offset values can be forwarded as a group by
using the special values 'scale' and 'offset' respectively. The special
value 'all' is equivalent to specifying 'header', 'scale' and 'offset'.
If an option is specified explicitly, it will override any forwarded value.
If a LAS file is the result of multiple LAS input files, the values to be
forwarded must match or they will be ignored.
value 'all' is equivalent to specifying 'header', 'scale', 'offset' and
'vlr' (see below).
If a header option is specified explicitly, it will override any forwarded
header value.
If a LAS file is the result of multiple LAS input files, the header values
to be forwarded must match or they will be ignored and a default will
be used instead.

VLRs can be forwarded by using the special value 'vlr'. VLRs containing
the following User IDs are NOT forwarded: 'LASF_Projection', 'LASF_Spec',
'liblas', 'laszip encoded'. These VLRs are known to contain information
regarding the formatting of the data and will be rebuilt properly in the
output file as necessary. Unlike header values, VLRs from multiple input
files are accumulated and each is written to the output file. Forwarded
VLRs may contain duplicate User ID/Record ID pairs.

minor_version
All LAS files are version 1, but the minor version (0 - 4) can be specified
Expand Down
15 changes: 15 additions & 0 deletions include/pdal/Metadata.hpp
Expand Up @@ -558,6 +558,21 @@ class PDAL_DLL MetadataNode
return MetadataNode();
}

template <typename PREDICATE>
MetadataNodeList findChildren(PREDICATE p)
{
MetadataNodeList matches;

auto nodes = children();
for (auto ai = nodes.begin(); ai != nodes.end(); ++ai)
{
MetadataNode& n = *ai;
if (p(n))
matches.push_back(n);
}
return matches;
}

template <typename PREDICATE>
MetadataNode findChild(PREDICATE p) const
{
Expand Down
13 changes: 10 additions & 3 deletions io/las/LasReader.cpp
Expand Up @@ -159,6 +159,7 @@ void LasReader::ready(PointTableRef table, MetadataNode& m)
setSrsFromVlrs(m);
MetadataNode forward = table.privateMetadata("lasforward");
extractHeaderMetadata(forward, m);
extractVlrMetadata(forward, m);

if (m_lasHeader.compressed())
{
Expand Down Expand Up @@ -209,13 +210,13 @@ template <typename T>
void addForwardMetadata(MetadataNode& forward, MetadataNode& m,
const std::string& name, T val, const std::string description = "")
{
m.add(name, val, description);
MetadataNode n = m.add(name, val, description);

// If the entry doesn't already exist, just add it.
MetadataNode f = forward.findChild(name);
if (!f.valid())
{
forward.add(name, val);
forward.add(n);
return;
}

Expand Down Expand Up @@ -484,7 +485,7 @@ SpatialReference LasReader::getSrsFromGeotiffVlr()
}


void LasReader::extractVlrMetadata(MetadataNode& m)
void LasReader::extractVlrMetadata(MetadataNode& forward, MetadataNode& m)
{
static const size_t DATA_LEN_MAX = 1000000;

Expand All @@ -506,6 +507,12 @@ void LasReader::extractVlrMetadata(MetadataNode& m)
vlrNode.add("record_id", vlr.recordId(),
"Record ID specified by the user.");
vlrNode.add("description", vlr.description());

if ((vlr.userId() != TRANSFORM_USER_ID) &&
(vlr.userId() != SPEC_USER_ID) &&
(vlr.userId() != LASZIP_USER_ID) &&
(vlr.userId() != LIBLAS_USER_ID))
forward.add(vlrNode);
}
}

Expand Down
19 changes: 10 additions & 9 deletions io/las/LasReader.hpp
Expand Up @@ -57,7 +57,8 @@ class PDAL_DLL LasReader : public pdal::Reader
{
friend class NitfReader;
public:
LasReader() : pdal::Reader(), m_index(0), m_istream(NULL), m_initialized(false)
LasReader() : pdal::Reader(), m_index(0), m_istream(NULL),
m_initialized(false)
{}

virtual ~LasReader()
Expand All @@ -66,7 +67,6 @@ class PDAL_DLL LasReader : public pdal::Reader
static void * create();
static int32_t destroy(void *);
std::string getName() const;

Options getDefaultOptions();

const LasHeader& header() const
Expand All @@ -88,14 +88,15 @@ class PDAL_DLL LasReader : public pdal::Reader
return m_istream;
}
virtual void destroyStream()
{
if (m_istream && m_initialized)
{
if (m_istream && m_initialized)
{
FileUtils::closeFile(m_istream);
m_istream = NULL;
m_initialized = false;
}
FileUtils::closeFile(m_istream);
m_istream = NULL;
m_initialized = false;
}
}

private:
LasError m_error;
LasHeader m_lasHeader;
Expand All @@ -117,7 +118,7 @@ class PDAL_DLL LasReader : public pdal::Reader
SpatialReference getSrsFromWktVlr();
SpatialReference getSrsFromGeotiffVlr();
void extractHeaderMetadata(MetadataNode& forward, MetadataNode& m);
void extractVlrMetadata(MetadataNode& m);
void extractVlrMetadata(MetadataNode& forward, MetadataNode& m);
virtual QuickInfo inspect();
virtual void ready(PointTableRef table)
{ ready(table, m_metadata); }
Expand Down
34 changes: 19 additions & 15 deletions io/las/LasWriter.cpp
Expand Up @@ -197,7 +197,10 @@ void LasWriter::fillForwardList(const Options &options)
for (auto& name : forwards)
{
if (name == "all")
{
m_forwards.insert(all.begin(), all.end());
m_forwardVlrs = true;
}
else if (name == "header")
m_forwards.insert(header.begin(), header.end());
else if (name == "scale")
Expand Down Expand Up @@ -290,11 +293,11 @@ void LasWriter::readyTable(PointTableRef table)
m_srs = getSpatialReference().empty() ?
table.spatialRef() : getSpatialReference();

setVlrsFromMetadata();
setVlrsFromSpatialRef();
setExtraBytesVlr();
MetadataNode forward = table.privateMetadata("lasforward");
fillHeader(forward);
setVlrsFromMetadata(forward);
}


Expand Down Expand Up @@ -379,26 +382,27 @@ MetadataNode LasWriter::findVlrMetadata(MetadataNode node,

/// Set VLRs from metadata for forwarded info, or from option-provided data
/// otherwise.
void LasWriter::setVlrsFromMetadata()
void LasWriter::setVlrsFromMetadata(MetadataNode& forward)
{
std::vector<uint8_t> data;

for (auto oi = m_optionInfos.begin(); oi != m_optionInfos.end(); ++oi)
{
VlrOptionInfo& vlrInfo = *oi;
if (!m_forwardVlrs)
return;

auto pred = [](MetadataNode n)
{ return Utils::startsWith(n.name(), "vlr_"); };

if (vlrInfo.m_name == "FORWARD")
MetadataNodeList nodes = forward.findChildren(pred);
for (auto& n : nodes)
{
const MetadataNode& userIdNode = n.findChild("user_id");
const MetadataNode& recordIdNode = n.findChild("record_id");
if (recordIdNode.valid() && userIdNode.valid())
{
MetadataNode m = findVlrMetadata(m_metadata, vlrInfo.m_recordId,
vlrInfo.m_userId);
if (m.empty())
continue;
data = Utils::base64_decode(m.value());
data = Utils::base64_decode(n.value());
uint16_t recordId = (uint16_t)std::stoi(recordIdNode.value());
addVlr(userIdNode.value(), recordId, n.description(), data);
}
else
data = Utils::base64_decode(vlrInfo.m_value);
addVlr(vlrInfo.m_userId, vlrInfo.m_recordId, vlrInfo.m_description,
data);
}
}

Expand Down
2 changes: 1 addition & 1 deletion io/las/LasWriter.hpp
Expand Up @@ -135,7 +135,7 @@ class PDAL_DLL LasWriter : public FlexWriter
void fillHeader(MetadataNode& forward);
point_count_t fillWriteBuf(const PointView& view, PointId startId,
std::vector<char>& buf);
void setVlrsFromMetadata();
void setVlrsFromMetadata(MetadataNode& forward);
MetadataNode findVlrMetadata(MetadataNode node, uint16_t recordId,
const std::string& userId);
void setExtraBytesVlr();
Expand Down
44 changes: 44 additions & 0 deletions test/unit/io/las/LasWriterTest.cpp
Expand Up @@ -300,6 +300,50 @@ TEST(LasWriterTest, forward)
EXPECT_EQ(n1.findChild("creation_year").value<uint16_t>(), 2014);
}

TEST(LasWriterTest, forwardvlr)
{
Options readerOps1;

readerOps1.add("filename", Support::datapath("las/lots_of_vlr.las"));
LasReader r1;
r1.addOptions(readerOps1);

std::string testfile = Support::temppath("tmp.las");
FileUtils::deleteFile(testfile);

Options writerOps;
writerOps.add("forward", "vlr");
writerOps.add("filename", testfile);

LasWriter w;
w.setInput(r1);
w.addOptions(writerOps);

PointTable t;

w.prepare(t);
w.execute(t);

Options readerOps;
readerOps.add("filename", testfile);

LasReader r;

r.setOptions(readerOps);

PointTable t2;

r.prepare(t2);
r.execute(t2);

MetadataNode forward = t2.privateMetadata("lasforward");

auto pred = [](MetadataNode temp)
{ return Utils::startsWith(temp.name(), "vlr_"); };
MetadataNodeList nodes = forward.findChildren(pred);
EXPECT_EQ(nodes.size(), 388UL);
}

// Test that data from three input views gets written to separate output files.
TEST(LasWriterTest, flex)
{
Expand Down

0 comments on commit dcef66c

Please sign in to comment.