diff --git a/bu/CopcSupport.cpp b/bu/CopcSupport.cpp index fece79c..ff34d0b 100644 --- a/bu/CopcSupport.cpp +++ b/bu/CopcSupport.cpp @@ -24,7 +24,6 @@ #include "../untwine/Common.hpp" #include "../untwine/FileDimInfo.hpp" -#include "../untwine/Las.hpp" namespace untwine { diff --git a/bu/PointAccessor.hpp b/bu/PointAccessor.hpp index c06bda7..83cd7d6 100644 --- a/bu/PointAccessor.hpp +++ b/bu/PointAccessor.hpp @@ -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 diff --git a/bu/Processor.cpp b/bu/Processor.cpp index 5196a60..90fb5e0 100644 --- a/bu/Processor.cpp +++ b/bu/Processor.cpp @@ -14,7 +14,6 @@ #include #include "../untwine/GridKey.hpp" -#include "../untwine/Las.hpp" #include #include @@ -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) @@ -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(); @@ -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 chunk = compressor.done(); @@ -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& buf) +void Processor::fillPointBuf(pdal::PointRef& point, std::vector& 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; @@ -589,51 +586,16 @@ void Processor::fillPointBuf(pdal::PointRef& point, std::vector& buf) ostream << z; ostream << point.getFieldAs(Id::Intensity); - - uint8_t scanChannel = point.getFieldAs(Id::ScanChannel); - uint8_t scanDirectionFlag = point.getFieldAs(Id::ScanDirectionFlag); - uint8_t edgeOfFlightLine = point.getFieldAs(Id::EdgeOfFlightLine); - uint8_t classification = point.getFieldAs(Id::Classification); - - if (has14PointFormat) - { - uint8_t bits = returnNumber | (numberOfReturns << 4); - ostream << bits; - - uint8_t classFlags; - if (point.hasDim(Id::ClassFlags)) - classFlags = point.getFieldAs(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(bitsDim); + ostream << point.getFieldAs(Id::Classification); uint8_t userData = point.getFieldAs(Id::UserData); - if (has14PointFormat) - { - // Guaranteed to fit if scan angle rank isn't wonky. - int16_t scanAngleRank = - static_cast(std::round( - point.getFieldAs(Id::ScanAngleRank) / .006f)); - ostream << userData << scanAngleRank; - } - else - { - int8_t scanAngleRank = point.getFieldAs(Id::ScanAngleRank); - ostream << scanAngleRank << userData; - } + // Guaranteed to fit if scan angle rank isn't wonky. + int16_t scanAngleRank = + static_cast(std::round( + point.getFieldAs(Id::ScanAngleRank) / .006f)); + ostream << userData << scanAngleRank; ostream << point.getFieldAs(Id::PointSourceId); diff --git a/bu/Processor.hpp b/bu/Processor.hpp index d5d7dce..e3bc076 100644 --- a/bu/Processor.hpp +++ b/bu/Processor.hpp @@ -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& buf); + void fillPointBuf(pdal::PointRef& point, std::vector& buf, pdal::Dimension::Id bitsDim); VoxelInfo m_vi; const BaseInfo& m_b; diff --git a/epf/Epf.cpp b/epf/Epf.cpp index 53b606a..738e014 100644 --- a/epf/Epf.cpp +++ b/epf/Epf.cpp @@ -17,7 +17,6 @@ #include "Reprocessor.hpp" #include "Writer.hpp" #include "../untwine/Common.hpp" -#include "../untwine/Las.hpp" #include #include @@ -32,7 +31,6 @@ #include #include - namespace { @@ -100,6 +98,24 @@ std::vector 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 @@ -117,7 +133,6 @@ Epf::Epf(BaseInfo& common) : m_b(common), m_pool(NumFileProcessors) Epf::~Epf() {} - void Epf::run(ProgressWriter& progress) { using namespace pdal; @@ -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(); @@ -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); + } } } @@ -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); } @@ -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 + "'."); @@ -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() + minor.value(); + } + // 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) diff --git a/epf/EpfTypes.hpp b/epf/EpfTypes.hpp index 895d6e8..df25b06 100644 --- a/epf/EpfTypes.hpp +++ b/epf/EpfTypes.hpp @@ -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; @@ -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(); } diff --git a/epf/FileProcessor.cpp b/epf/FileProcessor.cpp index a5c98b8..3415db5 100644 --- a/epf/FileProcessor.cpp +++ b/epf/FileProcessor.cpp @@ -23,11 +23,109 @@ namespace untwine namespace epf { +namespace +{ + +// The dimension IDs need to come from the *source* layout because it's possible in the +// case of user-defined dimensions that the IDs could vary for the same-named dimension +// in different input files. The "dim" field represents the ID of the dimension +// we're reading from. "offset" is the corresponding notion in the output packed point data. +void setDimensions(pdal::PointLayoutPtr layout, FileInfo& fi) +{ + for (FileDimInfo& di : fi.dimInfo) + { + di.dim = layout->findDim(di.name); + assert(di.dim != pdal::Dimension::Id::Unknown); + + // If we have a bit offset, then we're really writing to the classflags field in the + // output point. Fetch that offset and set it. We will probably do this several + // times (once for each bit), but the value should always be the same. + if (di.shift != -1) + fi.untwineBitsOffset = di.offset; + } +} + +} // unnamed namespace + + FileProcessor::FileProcessor(const FileInfo& fi, size_t pointSize, const Grid& grid, Writer *writer, ProgressWriter& progress) : m_fi(fi), m_cellMgr(pointSize, writer), m_grid(grid), m_progress(progress) {} +class BasePointProcessor +{ +public: + BasePointProcessor(const FileInfo& fi) : m_fi(fi) + {} + virtual ~BasePointProcessor() + {} + + virtual void fill(const pdal::PointRef& src, Point& dst) = 0; + +protected: + const FileInfo& m_fi; +}; +using PointProcessorPtr = std::unique_ptr; + +// These processors could probably be improved performance-wise by breaking the dimensions +// up into types in the ctor to avoid the conditionals in fill(). +// Could also make FileProcessor take these as a template type to avoid having fill() be +// virtual. +class StdPointProcessor : public BasePointProcessor +{ +public: + using BasePointProcessor::BasePointProcessor; + + void fill(const pdal::PointRef& src, Point& dst) override + { + uint8_t untwineBits = 0; + for (const FileDimInfo& fdi : m_fi.dimInfo) + { + if (fdi.shift == -1) + src.getField(reinterpret_cast(dst.data() + fdi.offset), + fdi.dim, fdi.type); + else + untwineBits |= (src.getFieldAs(fdi.dim) << fdi.shift); + } + + // We pack all the bitfields into the "untwine bits" field. + memcpy(dst.data() + m_fi.untwineBitsOffset, &untwineBits, 1); + } +}; + +class LegacyLasPointProcessor : public BasePointProcessor +{ +public: + using BasePointProcessor::BasePointProcessor; + + void fill(const pdal::PointRef& src, Point& dst) override + { + uint8_t untwineBits = 0; + for (const FileDimInfo& fdi : m_fi.dimInfo) + { + if (fdi.dim == pdal::Dimension::Id::Classification) + { + uint8_t classification = src.getFieldAs(fdi.dim); + if (classification == 12) + untwineBits |= 0x08; // Set the overlap bit. + untwineBits |= (classification >> 5); + classification &= 0x1F; + memcpy(dst.data() + fdi.offset, &classification, 1); + } + else if (fdi.shift == -1) + src.getField(reinterpret_cast(dst.data() + fdi.offset), + fdi.dim, fdi.type); + else + untwineBits |= (src.getFieldAs(fdi.dim) << fdi.shift); + } + + // We pack all the bitfields into the "untwine bits" field. + memcpy(dst.data() + m_fi.untwineBitsOffset, &untwineBits, 1); + } +}; + + void FileProcessor::run() { pdal::Options opts; @@ -53,15 +151,19 @@ void FileProcessor::run() // into which we can write data. Cell *cell = m_cellMgr.get(VoxelKey()); + PointProcessorPtr ptProcessor; + if (m_fi.driver == "readers.las" && m_fi.fileVersion < 14) + ptProcessor = std::make_unique(m_fi); + else + ptProcessor = std::make_unique(m_fi); + pdal::StreamCallbackFilter f; - f.setCallback([this, &count, &cell](pdal::PointRef& point) + f.setCallback([this, &count, &cell, ptProcessor = ptProcessor.get()](pdal::PointRef& point) { // Write the data into the point buffer in the cell. This is the *last* // cell buffer that we used. We're hoping that it's the right one. Point p = cell->point(); - for (const FileDimInfo& fdi : m_fi.dimInfo) - point.getField(reinterpret_cast(p.data() + fdi.offset), - fdi.dim, fdi.type); + ptProcessor->fill(point, p); // Find the actual cell that this point belongs in. If it's not the one // we chose, copy the data to the correct cell. @@ -95,6 +197,7 @@ void FileProcessor::run() try { f.prepare(t); + setDimensions(t.layout(), m_fi); f.execute(t); } catch (const pdal::pdal_error& err) diff --git a/untwine/Common.hpp b/untwine/Common.hpp index 56a681a..79ee710 100644 --- a/untwine/Common.hpp +++ b/untwine/Common.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -68,6 +69,74 @@ struct BaseInfo d3 offset {}; }; +// We make a special dimension to store the bits (class flags, scanner channel, scan dir, eofl). +const std::string UntwineBitsDimName { "UntwineBitsUntwine" }; +// That special dimension is a byte in size. +const pdal::Dimension::Type UntwineBitsType { pdal::Dimension::Type::Unsigned8 }; + +// PDAL explodes the class flags, leaving us with the following dimensions that map to the +// special "untwine bits" dimension. +inline bool isUntwineBitsDim(const std::string& s) +{ + static const std::vector bitsDims + { "Synthetic", "KeyPoint", "Overlap", "Withheld", "ScanChannel", + "ScanDirectionFlag", "EdgeOfFlightLine", "ClassFlags" }; + + return std::find(bitsDims.begin(), bitsDims.end(), s) != bitsDims.end(); +} + +// The position is the bit position of a bit, or the number of bits to shift an integer +// value to get it to the right location in the UntwineBits dimension. +inline int getUntwineBitPos(const std::string& s) +{ + static std::unordered_map positions { + {"Synthetic", 0}, + {"KeyPoint", 1}, + {"Withheld", 2}, + {"Overlap", 3}, + {"ScanChannel", 5}, + {"ScanDirectionFlag", 6}, + {"EdgeOfFlightLine", 7}, + {"ClassFlags", 0} + }; + auto it = positions.find(s); + if (it == positions.end()) + return -1; + return it->second; +} + +inline bool isExtraDim(const std::string& name) +{ + using namespace pdal; + using D = Dimension::Id; + + static const std::array lasDims + { + D::X, + D::Y, + D::Z, + D::Intensity, + D::ReturnNumber, + D::NumberOfReturns, + D::ReturnNumber, + D::Classification, + D::UserData, + D::ScanAngleRank, + D::PointSourceId, + D::GpsTime, + D::Red, + D::Green, + D::Blue, + D::Infrared + }; + + D id = Dimension::id(name); + for (Dimension::Id lasId : lasDims) + if (lasId == id) + return false; + return (name != UntwineBitsDimName); +} + const std::string MetadataFilename {"info2.txt"}; // We check both _WIN32 and _MSC_VER to deal with MinGW, which doesn't support the special @@ -94,9 +163,11 @@ inline std::string fromNative(const std::wstring& in) if (in.empty()) return std::string(); - int len = WideCharToMultiByte(CP_UTF8, 0, in.data(), in.length(), nullptr, 0, nullptr, nullptr); + int len = WideCharToMultiByte(CP_UTF8, 0, in.data(), in.length(), nullptr, + 0, nullptr, nullptr); std::string out(len, 0); - if (WideCharToMultiByte(CP_UTF8, 0, in.data(), in.length(), out.data(), len, nullptr, nullptr) == 0) + if (WideCharToMultiByte(CP_UTF8, 0, in.data(), in.length(), out.data(), + len, nullptr, nullptr) == 0) { char buf[200] {}; len = FormatMessageA(0, 0, GetLastError(), 0, buf, 199, 0); diff --git a/untwine/FileDimInfo.hpp b/untwine/FileDimInfo.hpp index d7f1993..057dba1 100644 --- a/untwine/FileDimInfo.hpp +++ b/untwine/FileDimInfo.hpp @@ -19,15 +19,16 @@ namespace untwine struct FileDimInfo { - FileDimInfo() + FileDimInfo() : shift(-1), extraDim(false) {} - FileDimInfo(const std::string& name) : name(name), extraDim(false) + FileDimInfo(const std::string& name) : name(name), shift(-1), extraDim(false) {} std::string name; pdal::Dimension::Type type; int offset; + int shift; pdal::Dimension::Id dim; bool extraDim; }; diff --git a/untwine/Las.cpp b/untwine/Las.cpp deleted file mode 100644 index e257820..0000000 --- a/untwine/Las.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/***************************************************************************** - * Copyright (c) 2021, Hobu, Inc. (info@hobu.co) * - * * - * All rights reserved. * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 3 of the License, or * - * (at your option) any later version. * - * * - ****************************************************************************/ - -#include "Las.hpp" - -namespace untwine -{ - -const pdal::Dimension::IdList& pdrfDims(int pdrf) -{ - using namespace pdal; - - if (pdrf < 0 || pdrf > 10) - pdrf = 10; - - using D = Dimension::Id; - static const Dimension::IdList dims[11] - { - // 0 - { D::X, D::Y, D::Z, D::Intensity, D::ReturnNumber, D::NumberOfReturns, D::ScanDirectionFlag, - D::EdgeOfFlightLine, D::Classification, D::ScanAngleRank, D::UserData, D::PointSourceId }, - // 1 - { D::X, D::Y, D::Z, D::Intensity, D::ReturnNumber, D::NumberOfReturns, D::ScanDirectionFlag, - D::EdgeOfFlightLine, D::Classification, D::ScanAngleRank, D::UserData, D::PointSourceId, - D::GpsTime }, - // 2 - { D::X, D::Y, D::Z, D::Intensity, D::ReturnNumber, D::NumberOfReturns, D::ScanDirectionFlag, - D::EdgeOfFlightLine, D::Classification, D::ScanAngleRank, D::UserData, D::PointSourceId, - D::Red, D::Green, D::Blue }, - // 3 - { D::X, D::Y, D::Z, D::Intensity, D::ReturnNumber, D::NumberOfReturns, D::ScanDirectionFlag, - D::EdgeOfFlightLine, D::Classification, D::ScanAngleRank, D::UserData, D::PointSourceId, - D::GpsTime, D::Red, D::Green, D::Blue }, - {}, - {}, - // 6 - { D::X, D::Y, D::Z, D::Intensity, D::ReturnNumber, D::NumberOfReturns, D::ScanDirectionFlag, - D::EdgeOfFlightLine, D::Classification, D::ScanAngleRank, D::UserData, D::PointSourceId, - D::GpsTime, D::ScanChannel, D::ClassFlags }, - // 7 - { D::X, D::Y, D::Z, D::Intensity, D::ReturnNumber, D::NumberOfReturns, D::ScanDirectionFlag, - D::EdgeOfFlightLine, D::Classification, D::ScanAngleRank, D::UserData, D::PointSourceId, - D::GpsTime, D::ScanChannel, D::ClassFlags, D::Red, D::Green, D::Blue }, - // 8 - { D::X, D::Y, D::Z, D::Intensity, D::ReturnNumber, D::NumberOfReturns, D::ScanDirectionFlag, - D::EdgeOfFlightLine, D::Classification, D::ScanAngleRank, D::UserData, D::PointSourceId, - D::GpsTime, D::ScanChannel, D::ClassFlags, D::Red, D::Green, D::Blue, D::Infrared }, - {}, - {} - }; - return dims[pdrf]; -} - -const pdal::Dimension::IdList& extentDims(int pdrf) -{ - using namespace pdal; - - if (pdrf < 0 || pdrf > 10) - pdrf = 10; - - using D = Dimension::Id; - static const Dimension::IdList dims[11] - { - {}, // 0 - {}, // 1 - {}, // 2 - {}, // 3 - {}, // 4 - {}, // 5 - // 6 - { D::X, D::Y, D::Z, D::Intensity, D::ReturnNumber, D::NumberOfReturns, D::ScanChannel, - D::ScanDirectionFlag, D::EdgeOfFlightLine, D::Classification, D::UserData, - D::ScanAngleRank, D::PointSourceId, D::GpsTime }, - // 7 - { D::X, D::Y, D::Z, D::Intensity, D::ReturnNumber, D::NumberOfReturns, D::ScanChannel, - D::ScanDirectionFlag, D::EdgeOfFlightLine, D::Classification, D::UserData, - D::ScanAngleRank, D::PointSourceId, D::GpsTime, D::Red, D::Green, D::Blue }, - // 8 - { D::X, D::Y, D::Z, D::Intensity, D::ReturnNumber, D::NumberOfReturns, D::ScanChannel, - D::ScanDirectionFlag, D::EdgeOfFlightLine, D::Classification, D::UserData, - D::ScanAngleRank, D::PointSourceId, D::GpsTime, D::Red, D::Green, D::Blue, D::Infrared }, - {}, - {} - }; - return dims[pdrf]; -} - -} // namespace untwine diff --git a/untwine/Las.hpp b/untwine/Las.hpp deleted file mode 100644 index 2f575f4..0000000 --- a/untwine/Las.hpp +++ /dev/null @@ -1,21 +0,0 @@ -/***************************************************************************** - * Copyright (c) 2021, Hobu, Inc. (info@hobu.co) * - * * - * All rights reserved. * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 3 of the License, or * - * (at your option) any later version. * - * * - ****************************************************************************/ - -#include - -namespace untwine -{ - -const pdal::Dimension::IdList& pdrfDims(int pdrf); -const pdal::Dimension::IdList& extentDims(int pdrf); - -} // namespace untwine diff --git a/untwine/Untwine.cpp b/untwine/Untwine.cpp index cd6c0cf..0adbdf2 100644 --- a/untwine/Untwine.cpp +++ b/untwine/Untwine.cpp @@ -83,7 +83,10 @@ bool handleOptions(pdal::StringList& arglist, Options& options) if (options.singleFile) options.tempDir = options.outputName + "_tmp"; else - options.tempDir = options.outputName + "/temp"; + { + throw FatalError("This version of Untwine does not support EPT output."); + //options.tempDir = options.outputName + "/temp"; + } } if (options.singleFile) options.stats = true;