Skip to content

Commit

Permalink
Add support for 'all' 'extra-dims' in LasWriter.
Browse files Browse the repository at this point in the history
  • Loading branch information
abellgithub committed Oct 12, 2015
1 parent 9876244 commit 9616af2
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 52 deletions.
5 changes: 5 additions & 0 deletions doc/stages/writers.las.rst
Expand Up @@ -152,5 +152,10 @@ extra_dims
bytes VLR (User ID: LASF_Spec, Record ID: 4), is created that describes the
extra dimensions specified by this option.

The special value 'all' can be used in place of a dimension/type list
to request
that all dimensions that can't be stored in the predefined LAS point
record get added as extra data at the end of each point record.

.. _LAS format: http://asprs.org/Committee-General/LASer-LAS-File-Format-Exchange-Activities.html

29 changes: 28 additions & 1 deletion io/las/LasHeader.cpp
Expand Up @@ -90,7 +90,7 @@ void LasHeader::setSummary(const SummaryData& summary)
{
m_pointCount = summary.getTotalNumPoints();
for (size_t num = 0; num < RETURN_COUNT; ++num)
m_pointCountByReturn[num] = summary.getReturnCount(num);
m_pointCountByReturn[num] = (int)summary.getReturnCount(num);
m_bounds = summary.getBounds();
}

Expand Down Expand Up @@ -197,6 +197,33 @@ void LasHeader::put(OLeStream& out, boost::uuids::uuid uuid)
}


Dimension::IdList LasHeader::usedDims() const
{
using namespace Dimension;

Dimension::Id::Enum dims[] = { Id::ReturnNumber, Id::NumberOfReturns,
Id::X, Id::Y, Id::Z, Id::Intensity, Id::ScanChannel,
Id::ScanDirectionFlag, Id::EdgeOfFlightLine, Id::Classification,
Id::UserData, Id::ScanAngleRank, Id::PointSourceId };

// This mess is because MSVC doesn't support initializer lists.
Dimension::IdList ids;
std::copy(std::begin(dims), std::end(dims), std::back_inserter(ids));

if (hasTime())
ids.push_back(Id::GpsTime);
if (hasColor())
{
ids.push_back(Id::Red);
ids.push_back(Id::Green);
ids.push_back(Id::Blue);
}
if (hasInfrared())
ids.push_back(Id::Infrared);

return ids;
}

ILeStream& operator>>(ILeStream& in, LasHeader& h)
{
uint8_t versionMajor;
Expand Down
2 changes: 2 additions & 0 deletions io/las/LasHeader.hpp
Expand Up @@ -41,6 +41,7 @@
#include <boost/uuid/uuid.hpp>
#include <boost/property_tree/ptree.hpp>

#include <pdal/Dimension.hpp>
#include <pdal/util/Bounds.hpp>
#include <pdal/pdal_config.hpp>
#include <pdal/gitsha.h>
Expand Down Expand Up @@ -366,6 +367,7 @@ class PDAL_DLL LasHeader

void setSummary(const SummaryData& summary);
bool valid() const;
Dimension::IdList usedDims() const;

friend ILeStream& operator>>(ILeStream&, LasHeader& h);
friend OLeStream& operator<<(OLeStream&, const LasHeader& h);
Expand Down
16 changes: 16 additions & 0 deletions io/las/LasUtils.cpp
Expand Up @@ -185,9 +185,16 @@ namespace LasUtils
std::vector<ExtraDim> parse(const StringList& dimString)
{
std::vector<ExtraDim> extraDims;
bool all = false;

for (auto& dim : dimString)
{
if (dim == "all")
{
all = true;
continue;
}

StringList s = Utils::split2(dim, '=');
if (s.size() != 2)
{
Expand All @@ -211,6 +218,15 @@ std::vector<ExtraDim> parse(const StringList& dimString)
ExtraDim ed(s[0], type);
extraDims.push_back(ed);
}

if (all)
{
if (extraDims.size())
throw (pdal_error("Can't specify specific extra dimensions with "
"special 'all' keyword."));
extraDims.push_back(ExtraDim("all", Dimension::Type::None));
}

return extraDims;
}

Expand Down
17 changes: 17 additions & 0 deletions io/las/LasWriter.cpp
Expand Up @@ -156,6 +156,23 @@ void LasWriter::processOptions(const Options& options)

void LasWriter::prepared(PointTableRef table)
{
PointLayoutPtr layout = table.layout();

// If we've asked for all dimensions, add to extraDims all dimensions
// in the layout that aren't already destined for LAS output.
if (m_extraDims.size() == 1 && m_extraDims[0].m_name == "all")
{
m_extraDims.clear();
Dimension::IdList ids = m_lasHeader.usedDims();
DimTypeList dimTypes = layout->dimTypes();
for (auto& dt : dimTypes)
{
if (!Utils::contains(ids, dt.m_id))
m_extraDims.push_back(
ExtraDim(layout->dimName(dt.m_id), dt.m_type));
}
}

m_extraByteLen = 0;
for (auto& dim : m_extraDims)
{
Expand Down
Binary file added test/data/bpf/simple-extra.bpf
Binary file not shown.
106 changes: 55 additions & 51 deletions test/unit/io/las/LasWriterTest.cpp
Expand Up @@ -41,6 +41,7 @@
#include <LasHeader.hpp>
#include <LasReader.hpp>
#include <LasWriter.hpp>
#include <BpfReader.hpp>

#include <pdal/PointView.hpp>
#include <pdal/StageFactory.hpp>
Expand Down Expand Up @@ -232,6 +233,60 @@ TEST(LasWriterTest, extra_dims)
}
}

TEST(LasWriterTest, all_extra_dims)
{
Options readerOps;

readerOps.add("filename", Support::datapath("bpf/simple-extra.bpf"));
BpfReader reader;
reader.setOptions(readerOps);

FileUtils::deleteFile(Support::temppath("simple.las"));

Options writerOps;
writerOps.add("extra_dims", "all");
writerOps.add("filename", Support::temppath("simple.las"));
writerOps.add("minor_version", 4);
LasWriter writer;
writer.setInput(reader);
writer.setOptions(writerOps);

PointTable table;
writer.prepare(table);
writer.execute(table);

Options ops;
ops.add("filename", Support::temppath("simple.las"));

LasReader r;
r.setOptions(ops);

PointTable t2;
r.prepare(t2);
Dimension::Id::Enum foo = t2.layout()->findDim("Foo");
Dimension::Id::Enum bar = t2.layout()->findDim("Bar");
Dimension::Id::Enum baz = t2.layout()->findDim("Baz");

PointViewSet s = r.execute(t2);
EXPECT_EQ(s.size(), 1u);
PointViewPtr v = *s.begin();

// We test for floats instead of doubles because when X, Y and Z
// get written, they are written scaled, which loses precision. The
// foo, bar and baz values are written as full-precision doubles.
for (PointId i = 0; i < v->size(); ++i)
{
using namespace Dimension;

EXPECT_FLOAT_EQ(v->getFieldAs<float>(Id::X, i),
v->getFieldAs<float>(foo, i));
EXPECT_FLOAT_EQ(v->getFieldAs<float>(Id::Y, i),
v->getFieldAs<float>(bar, i));
EXPECT_FLOAT_EQ(v->getFieldAs<float>(Id::Z, i),
v->getFieldAs<float>(baz, i));
}
}

// Merge a couple of 1.4 LAS files with point formats 1 and 6. Write the
// output with version 3. Verify that you get point format 3 (default),
// LAS 1.3 output and other header data forwarded.
Expand Down Expand Up @@ -591,57 +646,6 @@ TEST(LasWriterTest, simple)
}
**/

//ABELL
/**
TEST(LasWriterTest, LasWriterTest_test_simple_laz)
{
PointTable table;
WriterOpts writerOpts;
writerOpts.add("compressed", true);
writerOpts.add("creation_year", 0);
writerOpts.add("creation_doy", 0);
writerOpts.add("system_id", "");
writerOpts.add("software_id", "TerraScan");
// remove file from earlier run, if needed
FileUtils::deleteFile("laszip/LasWriterTest_test_simple_laz.laz");
LasReader reader(Support::datapath("laszip/basefile.las"));
std::ostream* ofs = FileUtils::createFile(
Support::temppath("LasWriterTest_test_simple_laz.laz"));
// need to scope the writer, so that's it dtor can use the stream
std::shared_ptr<LasWriter> writer(new LasWriter)(ofs);
writer->setOptions(writer);
writer->setInput(&reader);
writer->prepare(table);
writer->execute(table);
FileUtils::closeFile(ofs);
{
LasReader reader(
Support::temppath("LasWriterTest_test_simple_laz.laz"));
}
// these two files only differ by the description string in the VLR.
// This now skips the entire LASzip VLR for comparison.
const uint32_t numdiffs =Support::diff_files(
Support::temppath("LasWriterTest_test_simple_laz.laz"),
Support::datapath("laszip/laszip-generated.laz"),
227, 106);
EXPECT_EQ(numdiffs, 0u);
if (numdiffs == 0)
FileUtils::deleteFile(
Support::temppath("LasWriterTest_test_simple_laz.laz"));
}
**/

//ABELL
/**
static void test_a_format(const std::string& refFile, uint8_t majorVersion,
Expand Down

0 comments on commit 9616af2

Please sign in to comment.