TEST(LasWriterTest, srs) { Options readerOps; readerOps.add("filename", Support::datapath("las/utm15.las")); LasReader reader; reader.setOptions(readerOps); Options writerOps; writerOps.add("filename", Support::temppath("out.las")); LasWriter writer; writer.setInput(reader); writer.setOptions(writerOps); PointTable table; writer.prepare(table); writer.execute(table); LasTester tester; SpatialReference srs = tester.srs(writer); EXPECT_EQ(srs, SpatialReference("EPSG:26915")); }
TEST(LasWriterTest, pdal_metadata) { PointTable table; std::string infile(Support::datapath("las/1.2-with-color.las")); std::string outfile(Support::temppath("simple.las")); // remove file from earlier run, if needed FileUtils::deleteFile(outfile); Options readerOpts; readerOpts.add("filename", infile); Options writerOpts; writerOpts.add("pdal_metadata", true); writerOpts.add("filename", outfile); LasReader reader; reader.setOptions(readerOpts); LasWriter writer; writer.setOptions(writerOpts); writer.setInput(reader); writer.prepare(table); writer.execute(table); PointTable t2; Options readerOpts2; readerOpts2.add("filename", outfile); LasReader reader2; reader2.setOptions(readerOpts2); reader2.prepare(t2); reader2.execute(t2); EXPECT_EQ(reader2.getMetadata().children("pdal_metadata").size(), 1UL); EXPECT_EQ(reader2.getMetadata().children("pdal_pipeline").size(), 1UL); }
// 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); } }
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); } }
// This is the same test as the above, but for a 1.4-specific point format. // 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(LasWriterTest, laszip1_4) { Options readerOps; std::string baseFilename = Support::datapath("las/autzen_trim_7.las"); readerOps.add("filename", baseFilename); LasReader lazReader; lazReader.setOptions(readerOps); std::string testfile(Support::temppath("temp.laz")); FileUtils::deleteFile(testfile); Options writerOps; writerOps.add("filename", testfile); writerOps.add("dataformat_id", 7); writerOps.add("minor_version", 4); LasWriter lazWriter; lazWriter.setOptions(writerOps); lazWriter.setInput(lazReader); PointTable t; lazWriter.prepare(t); lazWriter.execute(t); // Now test the points were properly written. Use laszip. Options ops1; ops1.add("filename", testfile); LasReader r1; r1.setOptions(ops1); PointTable t1; r1.prepare(t1); PointViewSet set1 = r1.execute(t1); PointViewPtr view1 = *set1.begin(); Options ops2; ops2.add("filename", baseFilename); LasReader r2; r2.setOptions(ops2); PointTable t2; r2.prepare(t2); PointViewSet set2 = r2.execute(t2); PointViewPtr view2 = *set2.begin(); EXPECT_EQ(view1->size(), view2->size()); EXPECT_EQ(view1->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 idx = 0; idx < view1->size(); idx++) { view1->getPackedPoint(dims, idx, buf1.get()); view2->getPackedPoint(dims, idx, buf2.get()); char *b1 = buf1.get(); char *b2 = buf2.get(); // Uncomment this to figure out the exact byte at which things are // broken. /** for (size_t i = 0; i < pointSize; ++i) { if (*b1++ != *b2++) { { size_t s = 0; for (auto di = dims.begin(); di != dims.end(); ++di) { std::cerr << "Dim " << view1->dimName(di->m_id) << " at " << s << "!\n"; s += Dimension::size(di->m_type); } } throw pdal_error("Mismatch at byte = " + to_string(i) + "!"); } } **/ EXPECT_EQ(memcmp(buf1.get(), buf2.get(), pointSize), 0); } }
// 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(LasWriterTest, laszip) { Options readerOps; readerOps.add("filename", Support::datapath("las/autzen_trim.las")); LasReader lazReader; lazReader.setOptions(readerOps); std::string testfile(Support::temppath("temp.laz")); FileUtils::deleteFile(testfile); Options writerOps; writerOps.add("filename", testfile); LasWriter lazWriter; lazWriter.setOptions(writerOps); lazWriter.setInput(lazReader); PointTable t; lazWriter.prepare(t); lazWriter.execute(t); // Now test the points were properly written. Use laszip. Options ops1; ops1.add("filename", testfile); LasReader r1; r1.setOptions(ops1); PointTable t1; r1.prepare(t1); PointViewSet set1 = r1.execute(t1); PointViewPtr view1 = *set1.begin(); Options ops2; ops2.add("filename", Support::datapath("las/autzen_trim.las")); LasReader r2; r2.setOptions(ops2); PointTable t2; r2.prepare(t2); PointViewSet set2 = r2.execute(t2); PointViewPtr view2 = *set2.begin(); EXPECT_EQ(view1->size(), view2->size()); EXPECT_EQ(view1->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 < view1->size(); i += 100) { view1->getPackedPoint(dims, i, buf1.get()); view2->getPackedPoint(dims, i, buf2.get()); EXPECT_EQ(memcmp(buf1.get(), buf2.get(), pointSize), 0); } }
// 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); }
TEST(LasWriterTest, extra_dims) { Options readerOps; readerOps.add("filename", Support::datapath("las/1.2-with-color.las")); LasReader reader; reader.setOptions(readerOps); Options writerOps; writerOps.add("extra_dims", "Red=int32, Blue = int16, Green = int32_t"); writerOps.add("filename", Support::temppath("simple.las")); LasWriter writer; writer.setInput(reader); writer.setOptions(writerOps); PointTable table; writer.prepare(table); PointViewSet viewSet = writer.execute(table); LasTester tester; LasHeader *header = tester.header(writer); EXPECT_EQ(header->pointLen(), header->basePointLen() + 10); PointViewPtr pb = *viewSet.begin(); uint16_t colors[][3] = { { 68, 77, 88 }, { 92, 100, 110 }, { 79, 87, 87 }, { 100, 102, 116 }, { 162, 114, 145 }, { 163, 137, 155 }, { 154, 131, 144 }, { 104, 111, 126 }, { 164, 136, 156 }, { 72, 87, 82 }, { 117, 117, 136 } }; Options reader2Ops; reader2Ops.add("filename", Support::temppath("simple.las")); reader2Ops.add("extra_dims", "R1 =int32, B1= int16 ,G1=int32_t"); LasReader reader2; reader2.setOptions(reader2Ops); PointTable readTable; reader2.prepare(readTable); viewSet = reader2.execute(readTable); pb = *viewSet.begin(); Dimension::Id r1 = readTable.layout()->findDim("R1"); EXPECT_TRUE(r1 != Dimension::Id::Unknown); Dimension::Id b1 = readTable.layout()->findDim("B1"); EXPECT_TRUE(b1 != Dimension::Id::Unknown); Dimension::Id g1 = readTable.layout()->findDim("G1"); EXPECT_TRUE(g1 != Dimension::Id::Unknown); EXPECT_EQ(pb->size(), (size_t)1065); size_t j = 0; for (PointId i = 0; i < pb->size(); i += 100) { EXPECT_EQ(pb->getFieldAs<int16_t>(r1, i), colors[j][0]); EXPECT_EQ(pb->getFieldAs<int16_t>(g1, i), colors[j][1]); EXPECT_EQ(pb->getFieldAs<int16_t>(b1, i), colors[j][2]); j++; } }
// 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); }