static void test_a_format(const std::string& file, uint8_t majorVersion, uint8_t minorVersion, int pointFormat, double xref, double yref, double zref, double tref, uint16_t rref, uint16_t gref, uint16_t bref) { PointTable table; Options ops1; ops1.add("filename", Support::datapath(file)); ops1.add("count", 1); LasReader reader; reader.setOptions(ops1); reader.prepare(table); EXPECT_EQ(reader.header().pointFormat(), pointFormat); EXPECT_EQ(reader.header().versionMajor(), majorVersion); EXPECT_EQ(reader.header().versionMinor(), minorVersion); PointViewSet viewSet = reader.execute(table); EXPECT_EQ(viewSet.size(), 1u); PointViewPtr view = *viewSet.begin(); EXPECT_EQ(view->size(), 1u); Support::check_pN(*view, 0, xref, yref, zref, tref, rref, gref, bref); }
TEST(LasReaderTest, IgnoreVLRs) { PointTable table; Options readOps; readOps.add("filename", Support::datapath("las/lots_of_vlr.las")); readOps.add("ignore_vlr", "Merrick"); LasReader reader; reader.setOptions(readOps); reader.prepare(table); PointViewSet viewSet = reader.execute(table); // First two VLRs are SRS info, the other 388 would be // Merrick ones that we want to ignore/remove MetadataNode root = reader.getMetadata(); for (size_t i = 2; i < 390; ++i) { std::string name("vlr_"); name += std::to_string(i); MetadataNode m = root.findChild(name); EXPECT_FALSE(!m.empty()) << "No node " << i; m = m.findChild("data"); EXPECT_FALSE(!m.empty()) << "No value for node " << i; } }
// Test that data from three input views gets written to separate output files. TEST(LasWriterTest, flex) { std::array<std::string, 3> outname = {{ "test_1.las", "test_2.las", "test_3.las" }}; Options readerOps; readerOps.add("filename", Support::datapath("las/simple.las")); PointTable table; LasReader reader; reader.setOptions(readerOps); reader.prepare(table); PointViewSet views = reader.execute(table); PointViewPtr v = *(views.begin()); PointViewPtr v1(new PointView(table)); PointViewPtr v2(new PointView(table)); PointViewPtr v3(new PointView(table)); std::vector<PointViewPtr> vs; vs.push_back(v1); vs.push_back(v2); vs.push_back(v3); for (PointId i = 0; i < v->size(); ++i) vs[i % 3]->appendPoint(*v, i); for (size_t i = 0; i < outname.size(); ++i) FileUtils::deleteFile(Support::temppath(outname[i])); BufferReader reader2; reader2.addView(v1); reader2.addView(v2); reader2.addView(v3); Options writerOps; writerOps.add("filename", Support::temppath("test_#.las")); LasWriter writer; writer.setOptions(writerOps); writer.setInput(reader2); writer.prepare(table); writer.execute(table); for (size_t i = 0; i < outname.size(); ++i) { std::string filename = Support::temppath(outname[i]); EXPECT_TRUE(FileUtils::fileExists(filename)); Options ops; ops.add("filename", filename); LasReader r; r.setOptions(ops); EXPECT_EQ(r.preview().m_pointCount, 355u); } }
// LAZ files are normally written in chunks of 50,000, so a file of size // 110,000 ensures we read some whole chunks and a partial. TEST(LasReaderTest, lazperf) { Options ops1; ops1.add("filename", Support::datapath("laz/autzen_trim.laz")); ops1.add("compression", "lazperf"); LasReader lazReader; lazReader.setOptions(ops1); PointTable t1; lazReader.prepare(t1); PointViewSet pbSet = lazReader.execute(t1); EXPECT_EQ(pbSet.size(), 1UL); PointViewPtr view1 = *pbSet.begin(); EXPECT_EQ(view1->size(), (point_count_t)110000); Options ops2; ops2.add("filename", Support::datapath("las/autzen_trim.las")); LasReader lasReader; lasReader.setOptions(ops2); PointTable t2; lasReader.prepare(t2); pbSet = lasReader.execute(t2); EXPECT_EQ(pbSet.size(), 1UL); PointViewPtr view2 = *pbSet.begin(); EXPECT_EQ(view2->size(), (point_count_t)110000); DimTypeList dims = view1->dimTypes(); size_t pointSize = view1->pointSize(); EXPECT_EQ(view1->pointSize(), view2->pointSize()); // Validate some point data. std::unique_ptr<char> buf1(new char[pointSize]); std::unique_ptr<char> buf2(new char[pointSize]); for (PointId i = 0; i < 110000; i += 100) { view1->getPackedPoint(dims, i, buf1.get()); view2->getPackedPoint(dims, i, buf2.get()); EXPECT_EQ(memcmp(buf1.get(), buf2.get(), pointSize), 0); } }
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 foo = t2.layout()->findDim("Foo"); Dimension::Id bar = t2.layout()->findDim("Bar"); Dimension::Id 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; ASSERT_FLOAT_EQ(v->getFieldAs<float>(Id::X, i), v->getFieldAs<float>(foo, i)); ASSERT_FLOAT_EQ(v->getFieldAs<float>(Id::Y, i), v->getFieldAs<float>(bar, i)); ASSERT_FLOAT_EQ(v->getFieldAs<float>(Id::Z, i), v->getFieldAs<float>(baz, i)); } }
// Compare the source LAS file with the extracted OCI data. // Candidate is the OCI reader's view. void compare(const PointViewPtr candidate, std::string filename) { Options options; Option fn("filename", filename); options.add(fn); PointTable table; LasReader reader; reader.setOptions(options); reader.prepare(table); PointViewSet viewSet = reader.execute(table); EXPECT_EQ(viewSet.size(), 1u); PointViewPtr source = *viewSet.begin(); EXPECT_EQ(source->size(), candidate->size()); PointId limit = std::min(source->size(), candidate->size()); for (PointId i = 0; i < limit; ++i) { using namespace Dimension; int32_t sx = source->getFieldAs<int32_t>(Id::X, i); int32_t sy = source->getFieldAs<int32_t>(Id::Y, i); int32_t sz = source->getFieldAs<int32_t>(Id::Z, i); uint16_t sintensity = source->getFieldAs<uint16_t>(Id::Intensity, i); uint16_t sred = source->getFieldAs<uint16_t>(Id::Red, i); uint16_t sgreen = source->getFieldAs<uint16_t>(Id::Green, i); uint16_t sblue = source->getFieldAs<uint16_t>(Id::Blue, i); int32_t cx = candidate->getFieldAs<int32_t>(Id::X, i); int32_t cy = candidate->getFieldAs<int32_t>(Id::Y, i); int32_t cz = candidate->getFieldAs<int32_t>(Id::Z, i); uint16_t cintensity = candidate->getFieldAs<uint16_t>(Id::Intensity, i); uint16_t cred = candidate->getFieldAs<uint16_t>(Id::Red, i); uint16_t cgreen = candidate->getFieldAs<uint16_t>(Id::Green, i); uint16_t cblue = candidate->getFieldAs<uint16_t>(Id::Blue, i); EXPECT_EQ(sx, cx); EXPECT_EQ(sy, cy); EXPECT_EQ(sz, cz); EXPECT_EQ(sintensity, cintensity); EXPECT_EQ(sred, cred); EXPECT_EQ(sgreen, cgreen); EXPECT_EQ(sblue, cblue); } }
// The header of 1.2-with-color-clipped says that it has 1065 points, // but it really only has 1064. TEST(LasReaderTest, LasHeaderIncorrentPointcount) { PointTable table; Options readOps; readOps.add("filename", Support::datapath("las/1.2-with-color-clipped.las")); LasReader reader; reader.setOptions(readOps); reader.prepare(table); PointViewSet viewSet = reader.execute(table); PointViewPtr view = *viewSet.begin(); EXPECT_EQ(1064u, view->size()); }
TEST(LasWriterTest, auto_offset) { using namespace Dimension; const std::string FILENAME(Support::temppath("offset_test.las")); PointTable table; table.layout()->registerDim(Id::X); BufferReader bufferReader; PointViewPtr view(new PointView(table)); view->setField(Id::X, 0, 125000.00); view->setField(Id::X, 1, 74529.00); view->setField(Id::X, 2, 523523.02); bufferReader.addView(view); Options writerOps; writerOps.add("filename", FILENAME); writerOps.add("offset_x", "auto"); writerOps.add("scale_x", "auto"); LasWriter writer; writer.setOptions(writerOps); writer.setInput(bufferReader); writer.prepare(table); writer.execute(table); Options readerOps; readerOps.add("filename", FILENAME); PointTable readTable; LasReader reader; reader.setOptions(readerOps); reader.prepare(readTable); EXPECT_DOUBLE_EQ(74529.00, reader.header().offsetX()); PointViewSet viewSet = reader.execute(readTable); EXPECT_EQ(viewSet.size(), 1u); view = *viewSet.begin(); EXPECT_EQ(view->size(), 3u); EXPECT_NEAR(125000.00, view->getFieldAs<double>(Id::X, 0), .0001); EXPECT_NEAR(74529.00, view->getFieldAs<double>(Id::X, 1), .0001); EXPECT_NEAR(523523.02, view->getFieldAs<double>(Id::X, 2), .0001); FileUtils::deleteFile(FILENAME); }
TEST(LasReaderTest, EmptyGeotiffVlr) { PointTable table; Options readOps; readOps.add("filename", Support::datapath("las/1.2-empty-geotiff-vlrs.las")); LasReader reader; reader.setOptions(readOps); reader.prepare(table); PointViewSet viewSet = reader.execute(table); PointViewPtr view = *viewSet.begin(); EXPECT_EQ(43u, view->size()); }
TEST(LasReaderTest, header) { PointTable table; Options ops; ops.add("filename", Support::datapath("las/simple.las")); LasReader reader; reader.setOptions(ops); reader.prepare(table); // This tests the copy ctor, too. LasHeader h = reader.header(); EXPECT_EQ(h.fileSignature(), "LASF"); EXPECT_EQ(h.fileSourceId(), 0); EXPECT_TRUE(h.projectId().isNull()); EXPECT_EQ(h.versionMajor(), 1); EXPECT_EQ(h.versionMinor(), 2); EXPECT_EQ(h.creationDOY(), 0); EXPECT_EQ(h.creationYear(), 0); EXPECT_EQ(h.vlrOffset(), 227); EXPECT_EQ(h.pointFormat(), 3); EXPECT_EQ(h.pointCount(), 1065u); EXPECT_DOUBLE_EQ(h.scaleX(), .01); EXPECT_DOUBLE_EQ(h.scaleY(), .01); EXPECT_DOUBLE_EQ(h.scaleZ(), .01); EXPECT_DOUBLE_EQ(h.offsetX(), 0); EXPECT_DOUBLE_EQ(h.offsetY(), 0); EXPECT_DOUBLE_EQ(h.offsetZ(), 0); EXPECT_DOUBLE_EQ(h.maxX(), 638982.55); EXPECT_DOUBLE_EQ(h.maxY(), 853535.43); EXPECT_DOUBLE_EQ(h.maxZ(), 586.38); EXPECT_DOUBLE_EQ(h.minX(), 635619.85); EXPECT_DOUBLE_EQ(h.minY(), 848899.70); EXPECT_DOUBLE_EQ(h.minZ(), 406.59); EXPECT_EQ(h.compressed(), false); EXPECT_EQ(h.compressionInfo(), ""); EXPECT_EQ(h.pointCountByReturn(0), 925u); EXPECT_EQ(h.pointCountByReturn(1), 114u); EXPECT_EQ(h.pointCountByReturn(2), 21u); EXPECT_EQ(h.pointCountByReturn(3), 5u); EXPECT_EQ(h.pointCountByReturn(4), 0u); }
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(LasReaderTest, test_vlr) { PointTable table; Options ops1; ops1.add("filename", Support::datapath("las/lots_of_vlr.las")); LasReader reader; reader.setOptions(ops1); reader.prepare(table); reader.execute(table); MetadataNode root = reader.getMetadata(); for (size_t i = 0; i < 390; ++i) { std::string name("vlr_"); name += std::to_string(i); MetadataNode m = root.findChild(name); EXPECT_TRUE(!m.value().empty()) << "No node " << i; } }
TEST(LasReaderTest, callback) { PointTable table; point_count_t count = 0; Options ops; ops.add("filename", Support::datapath("las/simple.las")); Reader::PointReadFunc cb = [&count](PointView& view, PointId id) { count++; }; LasReader reader; reader.setOptions(ops); reader.setReadCb(cb); reader.prepare(table); reader.execute(table); EXPECT_EQ(count, (point_count_t)1065); }
TEST(LasReaderTest, test_sequential) { PointTable table; Options ops1; ops1.add("filename", Support::datapath("las/1.2-with-color.las")); ops1.add("count", 103); LasReader reader; reader.setOptions(ops1); reader.prepare(table); PointViewSet viewSet = reader.execute(table); EXPECT_EQ(viewSet.size(), 1u); PointViewPtr view = *viewSet.begin(); Support::check_p0_p1_p2(*view); PointViewPtr view2 = view->makeNew(); view2->appendPoint(*view, 100); view2->appendPoint(*view, 101); view2->appendPoint(*view, 102); Support::check_p100_p101_p102(*view2); }
TEST(LasReaderTest, test_sequential) { PointContext ctx; Options ops1; ops1.add("filename", Support::datapath("las/1.2-with-color.las")); ops1.add("count", 103); LasReader reader; reader.setOptions(ops1); reader.prepare(ctx); PointBufferSet pbSet = reader.execute(ctx); EXPECT_EQ(pbSet.size(), 1u); PointBufferPtr buf = *pbSet.begin(); Support::check_p0_p1_p2(*buf); PointBufferPtr buf2 = buf->makeNew(); buf2->appendPoint(*buf, 100); buf2->appendPoint(*buf, 101); buf2->appendPoint(*buf, 102); Support::check_p100_p101_p102(*buf2); }
TEST(LasWriterTest, fix1063_1064_1065) { std::string outfile = Support::temppath("out.las"); std::string infile = Support::datapath("las/test1_4.las"); FileUtils::deleteFile(outfile); std::string cmd = "pdal translate --writers.las.forward=all " "--writers.las.a_srs=\"EPSG:4326\" " + infile + " " + outfile; std::string output; Utils::run_shell_command(Support::binpath(cmd), output); Options o; o.add("filename", outfile); LasReader r; r.setOptions(o); PointTable t; r.prepare(t); PointViewSet s = r.execute(t); EXPECT_EQ(s.size(), 1u); PointViewPtr v = *s.begin(); EXPECT_EQ(v->size(), 1000u); // https://github.com/PDAL/PDAL/issues/1063 for (PointId idx = 0; idx < v->size(); ++idx) EXPECT_EQ(8, v->getFieldAs<int>(Dimension::Id::ClassFlags, idx)); // https://github.com/PDAL/PDAL/issues/1064 MetadataNode m = r.getMetadata(); m = m.findChild("global_encoding"); EXPECT_EQ(17, m.value<int>()); // https://github.com/PDAL/PDAL/issues/1065 SpatialReference ref = v->spatialReference(); std::string wkt = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]]"; EXPECT_EQ(ref.getWKT(), wkt); }
// Make sure that we can translate this special test data to 1.4, dataformat 6. TEST(LasWriterTest, issue2320) { std::string outfile(Support::temppath("2320.laz")); { LasReader r; Options ro; ro.add("filename", Support::datapath("las/wontcompress3.las")); r.setOptions(ro); LasWriter w; Options wo; wo.add("minor_version", 4); wo.add("dataformat_id", 6); wo.add("filename", outfile); w.setOptions(wo); w.setInput(r); PointTable t; w.prepare(t); w.execute(t); } // Check that we can read. { LasReader r; Options ro; ro.add("filename", outfile); r.setOptions(ro); PointTable t; r.prepare(t); PointViewSet s = r.execute(t); EXPECT_EQ(s.size(), 1U); PointViewPtr v = *s.begin(); EXPECT_EQ(v->size(), 1000U); } }
void compareTextLas(const std::string& textFilename, const std::string& lasFilename) { TextReader t; Options to; to.add("filename", textFilename); t.setOptions(to); LasReader l; Options lo; lo.add("filename", lasFilename); l.setOptions(lo); PointTable tt; t.prepare(tt); PointViewSet ts = t.execute(tt); EXPECT_EQ(ts.size(), 1U); PointViewPtr tv = *ts.begin(); PointTable lt; l.prepare(lt); PointViewSet ls = l.execute(lt); EXPECT_EQ(ls.size(), 1U); PointViewPtr lv = *ls.begin(); EXPECT_EQ(tv->size(), lv->size()); // Validate some point data. for (PointId i = 0; i < lv->size(); ++i) { EXPECT_DOUBLE_EQ(tv->getFieldAs<double>(Dimension::Id::X, i), lv->getFieldAs<double>(Dimension::Id::X, i)); EXPECT_DOUBLE_EQ(tv->getFieldAs<double>(Dimension::Id::Y, i), lv->getFieldAs<double>(Dimension::Id::Y, i)); EXPECT_DOUBLE_EQ(tv->getFieldAs<double>(Dimension::Id::Z, i), lv->getFieldAs<double>(Dimension::Id::Z, i)); } }
static void test_a_format(const std::string& file, uint8_t majorVersion, uint8_t minorVersion, int pointFormat, double xref, double yref, double zref, double tref, uint16_t rref, uint16_t gref, uint16_t bref) { PointContext ctx; Options ops1; ops1.add("filename", Support::datapath(file)); ops1.add("count", 1); LasReader reader; reader.setOptions(ops1); reader.prepare(ctx); EXPECT_EQ(reader.header().pointFormat(), pointFormat); EXPECT_EQ(reader.header().versionMajor(), majorVersion); EXPECT_EQ(reader.header().versionMinor(), minorVersion); PointBufferSet pbSet = reader.execute(ctx); EXPECT_EQ(pbSet.size(), 1u); PointBufferPtr buf = *pbSet.begin(); EXPECT_EQ(buf->size(), 1u); Support::check_pN(*buf, 0, xref, yref, zref, tref, rref, gref, bref); }
TEST(Random, extra_ops) { std::string outfile(Support::temppath("out.las")); const std::string cmd = appName() + " --count=100 --writers.las.minor_version=3 " + outfile; FileUtils::deleteFile(outfile); std::string output; Utils::run_shell_command(cmd, output); Options o; o.add("filename", outfile); PointTable t; LasReader r; r.setOptions(o); r.prepare(t); MetadataNode n = r.getMetadata(); EXPECT_EQ(n.findChild("minor_version").value<uint8_t>(), 3); }
TEST(LasWriterTest, flex_vlr) { std::array<std::string, 3> outname = {{ "test_1.laz", "test_2.laz", "test_3.laz" }}; Options readerOps; readerOps.add("filename", Support::datapath("las/simple.las")); PointTable table; LasReader reader; reader.setOptions(readerOps); reader.prepare(table); PointViewSet views = reader.execute(table); PointViewPtr v = *(views.begin()); PointViewPtr v1(new PointView(table)); PointViewPtr v2(new PointView(table)); PointViewPtr v3(new PointView(table)); std::vector<PointViewPtr> vs; vs.push_back(v1); vs.push_back(v2); vs.push_back(v3); for (PointId i = 0; i < v->size(); ++i) vs[i % 3]->appendPoint(*v, i); for (size_t i = 0; i < outname.size(); ++i) FileUtils::deleteFile(Support::temppath(outname[i])); BufferReader reader2; reader2.addView(v1); reader2.addView(v2); reader2.addView(v3); Options writerOps; writerOps.add("filename", Support::temppath("test_#.laz")); writerOps.add("pdal_metadata", true); LasWriter writer; writer.setOptions(writerOps); writer.setInput(reader2); writer.prepare(table); writer.execute(table); // Make sure that the files have the same three VLRs. for (size_t i = 0; i < outname.size(); ++i) { std::string filename = Support::temppath(outname[i]); EXPECT_TRUE(FileUtils::fileExists(filename)); Options ops; ops.add("filename", filename); LasReader r; r.setOptions(ops); PointTable t; r.prepare(t); r.execute(t); const VlrList& vlrs = r.header().vlrs(); EXPECT_EQ(vlrs.size(), 3U); EXPECT_NE(r.header().findVlr("laszip encoded", 22204), nullptr); EXPECT_NE(r.header().findVlr("PDAL", 12), nullptr); EXPECT_NE(r.header().findVlr("PDAL", 13), nullptr); } }
// 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. TEST(LasWriterTest, forward) { Options readerOps1; readerOps1.add("filename", Support::datapath("las/4_1.las")); Options readerOps2; readerOps2.add("filename", Support::datapath("las/4_6.las")); LasReader r1; r1.addOptions(readerOps1); LasReader r2; r2.addOptions(readerOps2); StageFactory sf; Stage *m = sf.createStage("filters.merge"); m->setInput(r1); m->setInput(r2); std::string testfile = Support::temppath("tmp.las"); FileUtils::deleteFile(testfile); Options writerOps; writerOps.add("forward", "header"); writerOps.add("minor_version", 3); writerOps.add("filename", testfile); LasWriter w; w.setInput(*m); 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 n1 = r.getMetadata(); EXPECT_EQ(n1.findChild("major_version").value<uint8_t>(), 1); EXPECT_EQ(n1.findChild("minor_version").value<uint8_t>(), 3); EXPECT_EQ(n1.findChild("dataformat_id").value<uint8_t>(), 3); EXPECT_EQ(n1.findChild("filesource_id").value<uint8_t>(), 0); // Global encoding doesn't match because 4_1.las has a bad value, so we // get the default. EXPECT_EQ(n1.findChild("global_encoding").value<uint16_t>(), 0); EXPECT_EQ(n1.findChild("project_id").value<Uuid>(), Uuid()); EXPECT_EQ(n1.findChild("system_id").value(), ""); EXPECT_EQ(n1.findChild("software_id").value(), "TerraScan"); EXPECT_EQ(n1.findChild("creation_doy").value<uint16_t>(), 142); EXPECT_EQ(n1.findChild("creation_year").value<uint16_t>(), 2014); }
// Identical to above, but writes each input view to a separate output file. TEST(LasWriterTest, auto_offset2) { using namespace Dimension; const std::string outname(Support::temppath("offset_test#.las")); const std::string inname1(Support::temppath("offset_test1.las")); const std::string inname2(Support::temppath("offset_test2.las")); PointTable table; table.layout()->registerDims({Id::X, Id::Y, Id::Z}); BufferReader bufferReader; PointViewPtr view(new PointView(table)); view->setField(Id::X, 0, 125000.00); view->setField(Id::X, 1, 74529.00); view->setField(Id::X, 2, 1000000.02); view->setField(Id::Y, 0, 0); view->setField(Id::Y, 1, 1); view->setField(Id::Y, 2, 2); view->setField(Id::Z, 0, -123); view->setField(Id::Z, 1, 456.78); view->setField(Id::Z, 2, 945.23); bufferReader.addView(view); view.reset(new PointView(table)); view->setField(Id::X, 0, 25.00); view->setField(Id::X, 1, 74529.00); view->setField(Id::X, 2, 534252.35); view->setField(Id::Y, 0, 3); view->setField(Id::Y, 1, 4); view->setField(Id::Y, 2, 5); view->setField(Id::Z, 0, 1.5); view->setField(Id::Z, 1, 2147483524); view->setField(Id::Z, 2, 745.23); bufferReader.addView(view); Options writerOps; writerOps.add("filename", outname); writerOps.add("offset_x", "auto"); writerOps.add("scale_x", "auto"); writerOps.add("offset_z", "auto"); writerOps.add("scale_z", "auto"); LasWriter writer; writer.setOptions(writerOps); writer.setInput(bufferReader); writer.prepare(table); writer.execute(table); { Options readerOps; readerOps.add("filename", inname1); PointTable readTable; LasReader reader; reader.setOptions(readerOps); reader.prepare(readTable); EXPECT_DOUBLE_EQ(74529.00, reader.header().offsetX()); EXPECT_DOUBLE_EQ(0, reader.header().offsetY()); EXPECT_DOUBLE_EQ(-123, reader.header().offsetZ()); EXPECT_NEAR(4.30956e-4, reader.header().scaleX(), 1e-4); EXPECT_DOUBLE_EQ(.01, reader.header().scaleY()); // (max - min) are chosen to yield std::numeric_limits<int>::max(); EXPECT_NEAR(4.9743e-7, reader.header().scaleZ(), 1e-7); PointViewSet viewSet = reader.execute(readTable); EXPECT_EQ(viewSet.size(), 1u); view = *viewSet.begin(); EXPECT_EQ(view->size(), 3u); EXPECT_NEAR(125000.00, view->getFieldAs<double>(Id::X, 0), .001); EXPECT_NEAR(74529.00, view->getFieldAs<double>(Id::X, 1), .001); EXPECT_NEAR(1000000.02, view->getFieldAs<double>(Id::X, 2), .0001); } { Options readerOps; readerOps.add("filename", inname2); PointTable readTable; LasReader reader; reader.setOptions(readerOps); reader.prepare(readTable); EXPECT_DOUBLE_EQ(25.0, reader.header().offsetX()); EXPECT_DOUBLE_EQ(0, reader.header().offsetY()); EXPECT_DOUBLE_EQ(1.5, reader.header().offsetZ()); EXPECT_NEAR(2.4876e-4, reader.header().scaleX(), 1e-7); EXPECT_DOUBLE_EQ(.01, reader.header().scaleY()); EXPECT_NEAR(.99999, reader.header().scaleZ(), 1e-5); PointViewSet viewSet = reader.execute(readTable); EXPECT_EQ(viewSet.size(), 1u); view = *viewSet.begin(); EXPECT_EQ(view->size(), 3u); EXPECT_NEAR(25.00, view->getFieldAs<double>(Id::X, 0), .0001); EXPECT_NEAR(74529.00, view->getFieldAs<double>(Id::X, 1), .001); EXPECT_NEAR(534252.35, view->getFieldAs<double>(Id::X, 2), .0001); } FileUtils::deleteFile(inname1); FileUtils::deleteFile(inname2); }
// Test that stage options passed via --stage.<tagname>.<option> work. TEST(json, stagetags) { auto checkValue = [](const std::string filename, double value) { Options o; o.add("filename", filename); LasReader r; r.setOptions(o); PointTable t; r.prepare(t); PointViewSet s = r.execute(t); EXPECT_EQ(s.size(), 1u); PointViewPtr v = *s.begin(); for (PointId i = 0; i < v->size(); ++i) EXPECT_DOUBLE_EQ(value, v->getFieldAs<double>(Dimension::Id::Z, i)); FileUtils::deleteFile(filename); }; std::string outFilename(Support::temppath("assigned.las")); std::string base(appName() + " " + Support::configuredpath("pipeline/options.json")); std::string output; int stat; stat = Utils::run_shell_command(base, output); EXPECT_EQ(stat, 0); checkValue(outFilename, 25); stat = Utils::run_shell_command(base + " --filters.assign.assignment=Z[:]=101", output); EXPECT_EQ(stat, 0); checkValue(outFilename, 101); stat = Utils::run_shell_command(base + " --stage.assigner.assignment=Z[:]=1987", output); EXPECT_EQ(stat, 0); checkValue(outFilename, 1987); // Make sure that tag options override stage options. stat = Utils::run_shell_command(base + " --filters.assign.assignment=Z[:]=25 " "--stage.assigner.assignment=Z[:]=555", output); EXPECT_EQ(stat, 0); checkValue(outFilename, 555); stat = Utils::run_shell_command(base + " --stage.assigner.assignment=Z[:]=555 " "--filters.assign.assignment=Z[:]=25 ", output); EXPECT_EQ(stat, 0); checkValue(outFilename, 555); // Check that bad tag fails. stat = Utils::run_shell_command(base + " --stage.foobar.assignment=Z[:]=1987", output); EXPECT_NE(stat, 0); // Check that bad option name fails. stat = Utils::run_shell_command(base + " --stage.assigner.blah=Z[:]=1987", output); EXPECT_NE(stat, 0); // Check that multiply specified option fails. stat = Utils::run_shell_command(base + " --stage.reader.compression=laszip " "--stage.reader.compression=lazperf", output); EXPECT_NE(stat, 0); }
TEST(LasReaderTest, extraBytes) { PointTable table; PointLayoutPtr layout(table.layout()); Options readOps; readOps.add("filename", Support::datapath("las/extrabytes.las")); LasReader reader; reader.setOptions(readOps); reader.prepare(table); DimTypeList dimTypes = layout->dimTypes(); EXPECT_EQ(dimTypes.size(), (size_t)24); Dimension::Id color0 = layout->findProprietaryDim("Colors0"); EXPECT_EQ(layout->dimType(color0), Dimension::Type::Unsigned16); Dimension::Id color1 = layout->findProprietaryDim("Colors1"); EXPECT_EQ(layout->dimType(color1), Dimension::Type::Unsigned16); Dimension::Id color2 = layout->findProprietaryDim("Colors2"); EXPECT_EQ(layout->dimType(color2), Dimension::Type::Unsigned16); Dimension::Id flag0 = layout->findProprietaryDim("Flags0"); EXPECT_EQ(layout->dimType(flag0), Dimension::Type::Signed8); Dimension::Id flag1 = layout->findProprietaryDim("Flags1"); EXPECT_EQ(layout->dimType(flag1), Dimension::Type::Signed8); Dimension::Id time2 = layout->findDim("Time"); EXPECT_EQ(layout->dimType(time2), Dimension::Type::Unsigned64); PointViewSet viewSet = reader.execute(table); EXPECT_EQ(viewSet.size(), (size_t)1); PointViewPtr view = *viewSet.begin(); Dimension::Id red = layout->findDim("Red"); Dimension::Id green = layout->findDim("Green"); Dimension::Id blue = layout->findDim("Blue"); Dimension::Id returnNum = layout->findDim("ReturnNumber"); Dimension::Id numReturns = layout->findDim("NumberOfReturns"); Dimension::Id intensity = layout->findDim("Intensity"); Dimension::Id time = layout->findDim("GpsTime"); for (PointId idx = 0; idx < view->size(); ++idx) { ASSERT_EQ(view->getFieldAs<uint16_t>(red, idx), view->getFieldAs<uint16_t>(color0, idx)); ASSERT_EQ(view->getFieldAs<uint16_t>(green, idx), view->getFieldAs<uint16_t>(color1, idx)); ASSERT_EQ(view->getFieldAs<uint16_t>(blue, idx), view->getFieldAs<uint16_t>(color2, idx)); ASSERT_EQ(view->getFieldAs<uint16_t>(flag0, idx), view->getFieldAs<uint16_t>(returnNum, idx)); ASSERT_EQ(view->getFieldAs<uint16_t>(flag1, idx), view->getFieldAs<uint16_t>(numReturns, idx)); // Time was written truncated rather than rounded. ASSERT_NEAR(view->getFieldAs<double>(time, idx), view->getFieldAs<double>(time2, idx), 1.0); } }
void streamTest(const std::string src, const std::string compression) { Options ops1; ops1.add("filename", src); ops1.add("compression", compression); LasReader lasReader; lasReader.setOptions(ops1); PointTable t; lasReader.prepare(t); PointViewSet s = lasReader.execute(t); PointViewPtr p = *s.begin(); class Checker : public Filter { public: std::string getName() const { return "checker"; } Checker(PointViewPtr v) : m_cnt(0), m_view(v), m_bulkBuf(v->pointSize()), m_buf(v->pointSize()), m_dims(v->dimTypes()) {} private: point_count_t m_cnt; PointViewPtr m_view; std::vector<char> m_bulkBuf; std::vector<char> m_buf; DimTypeList m_dims; bool processOne(PointRef& point) { PointRef bulkPoint = m_view->point(m_cnt); bulkPoint.getPackedData(m_dims, m_bulkBuf.data()); point.getPackedData(m_dims, m_buf.data()); EXPECT_EQ(memcmp(m_buf.data(), m_bulkBuf.data(), m_view->pointSize()), 0); m_cnt++; return true; } void done(PointTableRef) { EXPECT_EQ(m_cnt, 110000u); } }; Options ops2; ops2.add("filename", Support::datapath("las/autzen_trim.las")); LasReader lazReader; lazReader.setOptions(ops2); Checker c(p); c.setInput(lazReader); FixedPointTable fixed(100); c.prepare(fixed); c.execute(fixed); }
TEST(Compression, Simple) { const std::string file(Support::datapath("las/1.2-with-color.las")); const pdal::Option opt_filename("filename", file); pdal::Options opts; opts.add(opt_filename); LasReader reader; reader.setOptions(opts); PointTable table; PointLayoutPtr layout(table.layout()); reader.prepare(table); PointViewSet viewSet = reader.execute(table); PointViewPtr view = *viewSet.begin(); EXPECT_EQ(layout->pointSize(), 52U); std::vector<unsigned char> rawBuf; LazPerfBuf b(rawBuf); DimTypeList dimTypes = layout->dimTypes(); LazPerfCompressor<LazPerfBuf> compressor(b, dimTypes); std::vector<char> tmpbuf(compressor.pointSize()); for (PointId idx = 0; idx < view->size(); ++idx) { view->getPackedPoint(dimTypes, idx, tmpbuf.data()); compressor.compress(tmpbuf.data(), compressor.pointSize()); } compressor.done(); EXPECT_EQ(view->size() * compressor.pointSize(), (size_t)55380); EXPECT_EQ(rawBuf.size(), (size_t)30945); LazPerfBuf b2(rawBuf); LazPerfDecompressor<LazPerfBuf> decompressor(b2, dimTypes); size_t outbufSize = decompressor.pointSize() * view->size(); std::vector<char> outbuf(outbufSize); decompressor.decompress(outbuf.data(), outbufSize); PointViewPtr otherView(new PointView(table)); char *pos = outbuf.data(); for (PointId nextId = 0; nextId < 11; nextId++) { otherView->setPackedPoint(dimTypes, nextId, pos); pos += decompressor.pointSize(); } EXPECT_EQ(otherView->size(), 11U); EXPECT_EQ(getBytes(otherView).size(), (size_t)(52 * 11)); uint16_t r = otherView->getFieldAs<uint16_t>(Dimension::Id::Red, 10); EXPECT_EQ(r, 64U); int32_t x = otherView->getFieldAs<int32_t>(Dimension::Id::X, 10); EXPECT_EQ(x, 636038); double xd = otherView->getFieldAs<double>(Dimension::Id::X, 10); EXPECT_FLOAT_EQ(xd, 636037.53); int32_t y = otherView->getFieldAs<int32_t>(Dimension::Id::Y, 10); EXPECT_EQ(y, 849338); }
TEST(Compression, simple) { const std::string file(Support::datapath("las/1.2-with-color.las")); const pdal::Option opt_filename("filename", file); pdal::Options opts; opts.add(opt_filename); LasReader reader; reader.setOptions(opts); PointTable table; PointLayoutPtr layout(table.layout()); reader.prepare(table); PointViewSet viewSet = reader.execute(table); PointViewPtr view = *viewSet.begin(); EXPECT_EQ(layout->pointSize(), 52U); std::vector<unsigned char> rawBuf; DimTypeList dimTypes = layout->dimTypes(); auto cb = [&rawBuf](char *buf, size_t bufsize) { unsigned char *ubuf = reinterpret_cast<unsigned char *>(buf); rawBuf.insert(rawBuf.end(), ubuf, ubuf + bufsize); }; LazPerfCompressor compressor(cb, dimTypes); std::vector<char> tmpbuf(layout->pointSize()); for (PointId idx = 0; idx < view->size(); ++idx) { view->getPackedPoint(dimTypes, idx, tmpbuf.data()); compressor.compress(tmpbuf.data(), layout->pointSize()); } compressor.done(); EXPECT_EQ(view->size() * layout->pointSize(), (size_t)55380); EXPECT_EQ(rawBuf.size(), (size_t)30945); PointViewPtr otherView(new PointView(table)); PointId nextId(0); auto cb2 = [&otherView, &dimTypes, &nextId](char *buf, size_t bufsize) { otherView->setPackedPoint(dimTypes, nextId, buf); nextId++; }; LazPerfDecompressor(cb2, dimTypes, view->size()). decompress(reinterpret_cast<const char *>(rawBuf.data()), rawBuf.size()); EXPECT_EQ(otherView->size(), 1065U); EXPECT_EQ(getBytes(otherView).size(), (size_t)(52 * 1065)); uint16_t r = otherView->getFieldAs<uint16_t>(Dimension::Id::Red, 10); EXPECT_EQ(r, 64U); int32_t x = otherView->getFieldAs<int32_t>(Dimension::Id::X, 10); EXPECT_EQ(x, 636038); double xd = otherView->getFieldAs<double>(Dimension::Id::X, 10); EXPECT_FLOAT_EQ(xd, 636037.53); int32_t y = otherView->getFieldAs<int32_t>(Dimension::Id::Y, 10); EXPECT_EQ(y, 849338); }
TEST(PointTable, userView) { class UserTable : public PointTable { private: double m_x; double m_y; double m_z; public: PointId addPoint() { return 0; } char *getPoint(PointId idx) { return NULL; } void setField(const Dimension::Detail *d, PointId idx, const void *value) { if (d->id() == Dimension::Id::X) m_x = *(const double *)value; else if (d->id() == Dimension::Id::Y) m_y = *(const double *)value; else if (d->id() == Dimension::Id::Z) m_z = *(const double *)value; } void getField(const Dimension::Detail *d, PointId idx, void *value) { if (d->id() == Dimension::Id::X) *(double *)value = m_x; else if (d->id() == Dimension::Id::Y) *(double *)value = m_y; else if (d->id() == Dimension::Id::Z) *(double *)value = m_z; } }; LasReader reader; Options opts; opts.add("filename", Support::datapath("las/simple.las")); opts.add("count", 100); reader.setOptions(opts); PointTable defTable; reader.prepare(defTable); PointViewSet viewSet = reader.execute(defTable); PointViewPtr defView = *viewSet.begin(); bool called(false); auto readCb = [defView, &called](PointView& customView, PointId id) { called = true; double xDef = defView->getFieldAs<double>(Dimension::Id::X, id); double yDef = defView->getFieldAs<double>(Dimension::Id::Y, id); double zDef = defView->getFieldAs<double>(Dimension::Id::Z, id); double x = customView.getFieldAs<double>(Dimension::Id::X, id); double y = customView.getFieldAs<double>(Dimension::Id::Y, id); double z = customView.getFieldAs<double>(Dimension::Id::Z, id); EXPECT_FLOAT_EQ(xDef, x); EXPECT_FLOAT_EQ(yDef, y); EXPECT_FLOAT_EQ(zDef, z); }; reader.setReadCb(readCb); UserTable table; reader.prepare(table); reader.execute(table); EXPECT_TRUE(called); }