/****************************************************************************** * Copyright (c) 2015, Hobu Inc. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following * conditions are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided * with the distribution. * * Neither the name of the Martin Isenburg or Iowa Department * of Natural Resources nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. ****************************************************************************/ #pragma once #include #include #include #include #include #include #include #include namespace pdal { class PointRef; namespace las { struct Header; class Srs; class Summary; struct Vlr; enum class Compression { True, False }; inline std::istream& operator>>(std::istream& in, Compression& c) { std::string s; in >> s; s = Utils::toupper(s); if (s == "LASZIP" || s == "TRUE" || s == "LAZPERF") c = Compression::True; else c = Compression::False; return in; } inline std::ostream& operator<<(std::ostream& out, const Compression& c) { switch (c) { case Compression::True: out << "true"; break; case Compression::False: out << "false"; break; } return out; } inline bool pointFormatSupported(int format) { switch (format) { case 0: case 1: case 2: case 3: case 6: case 7: case 8: return true; default: return false; } } // Metadata inline void addForwardMetadata(MetadataNode& forward, MetadataNode& m, const std::string& name, double val, const std::string description, size_t precision) { MetadataNode n = m.add(name, val, description, precision); // If the entry doesn't already exist, just add it. MetadataNode f = forward.findChild(name); if (!f.valid()) { forward.add(n); return; } // If the old value and new values aren't the same, set an invalid flag. MetadataNode temp = f.addOrUpdate("temp", val, description, precision); if (f.value() != temp.value()) forward.addOrUpdate(name + "INVALID", ""); } // Store data in the normal metadata place. Also store it in the private // lasforward metadata node. template void addForwardMetadata(MetadataNode& forward, MetadataNode& m, const std::string& name, T val, const std::string 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(n); return; } // If the old value and new values aren't the same, set an invalid flag. MetadataNode temp = f.addOrUpdate("temp", val); if (f.value() != temp.value()) forward.addOrUpdate(name + "INVALID", ""); } // Extra Dim struct ExtraDim { ExtraDim(const std::string name, Dimension::Type type, int byteOffset, double scale = 1.0, double offset = 0.0) : m_name(name), m_dimType(Dimension::Id::Unknown, type, scale, offset), m_size((uint8_t)Dimension::size(type)), m_byteOffset(byteOffset) {} ExtraDim(const std::string name, uint8_t size, int byteOffset) : m_name(name), m_dimType(Dimension::Id::Unknown, Dimension::Type::None), m_size(size), m_byteOffset(byteOffset) {} ExtraDim(const std::string name, Dimension::Type type, Dimension::Id id, size_t size, int byteOffset, double scale = 1.0, double offset = 0.0) : m_name(name), m_dimType(id, type, scale, offset), m_size((uint8_t)size), m_byteOffset(byteOffset) {} friend bool operator == (const ExtraDim& ed1, const ExtraDim& ed2); std::string m_name; DimType m_dimType; uint8_t m_size; int32_t m_byteOffset; }; using ExtraDims = std::vector; inline bool operator == (const ExtraDim& ed1, const ExtraDim& ed2) { // This is an incomplete comparison, but it should suffice since we // only use it to compare an ExtraDim specified in an option with // one created from a VLR entry. return (ed1.m_name == ed2.m_name && ed1.m_dimType.m_type == ed2.m_dimType.m_type && ed1.m_size == ed2.m_size); } // This is the structure of each record in the extra bytes spec. Not used // directly for storage, but here mostly for reference. #pragma pack(push, 1) struct ExtraBytesSpec { char m_reserved[2]; uint8_t m_dataType; uint8_t m_options; char m_name[32]; char m_reserved2[4]; uint64_t m_noData[3]; // 24 = 3*8 bytes double m_min[3]; // 24 = 3*8 bytes double m_max[3]; // 24 = 3*8 bytes double m_scale[3]; // 24 = 3*8 bytes double m_offset[3]; // 24 = 3*8 bytes char m_description[32]; }; #pragma pack(pop) const int ExtraBytesSpecSize = sizeof(ExtraBytesSpec); class ExtraBytesIf { public: ExtraBytesIf() : m_type(Dimension::Type::None), m_fieldCnt(0), m_size(0) { for (size_t i = 0; i < 3; ++i) { m_scale[i] = 1.0; m_offset[i] = 0.0; } } ExtraBytesIf(const std::string& name, Dimension::Type type, const std::string& description) : m_type(type), m_name(name), m_description(description), m_size(0) { for (size_t i = 0; i < 3; ++i) { // Setting the scale to 0 looks wrong, but it isn't. If the // scale option flag isn't set, the scale is supposed to be 0. // When we write the VLR, we always clear the scale flag. m_scale[i] = 0.0; m_offset[i] = 0.0; } m_fieldCnt = (m_type == Dimension::Type::None ? 0 : 1); } void appendTo(std::vector& ebBytes); void readFrom(const char *buf); void setType(uint8_t lastype); static std::vector toExtraDims(const char *buf, size_t bufsize, int byteOffset); private: Dimension::Type m_type; unsigned m_fieldCnt; // Must be 0 - 3; double m_scale[3]; double m_offset[3]; std::string m_name; std::string m_description; size_t m_size; }; // Function declarations void extractHeaderMetadata(const Header& h, MetadataNode& forward, MetadataNode& m); void extractSrsMetadata(const Srs& srs, MetadataNode& m); void addVlrMetadata(const Vlr& vlr, std::string name, MetadataNode& forward, MetadataNode& m); void setSummary(Header& header, const Summary& summary); std::string generateSoftwareId(); std::vector parse(const StringList& dimString, bool allOk); const Dimension::IdList& pdrfDims(int pdrf); uint8_t lasType(Dimension::Type type, int fieldCnt); struct error : public std::runtime_error { error(const std::string& err) : std::runtime_error(err) {} }; // Loader class LoaderDriver; class PointLoader { friend class LoaderDriver; public: virtual ~PointLoader() = default; private: virtual void load(PointRef& point, const char *buf, int bufsize) = 0; virtual void pack(const PointRef& point, char *buf, int bufsize) = 0; }; using PointLoaderPtr = std::unique_ptr; class PointFilter { friend class LoaderDriver; public: virtual ~PointFilter() = default; private: virtual bool passes(PointRef& point) = 0; }; using PointFilterPtr = std::unique_ptr; class V10BaseLoader : public PointLoader { public: V10BaseLoader(const Scaling& scaling); private: virtual void load(PointRef& point, const char *buf, int bufsize) override; virtual void pack(const PointRef& point, char *buf, int bufsize) override; Scaling m_scaling; }; class V14BaseLoader : public PointLoader { public: V14BaseLoader(const Scaling& scaling); private: virtual void load(PointRef& point, const char *buf, int bufsize) override; virtual void pack(const PointRef& point, char *buf, int bufsize) override; Scaling m_scaling; }; class GpstimeLoader : public PointLoader { public: GpstimeLoader(int offset) : m_offset(offset) {} private: virtual void load(PointRef& point, const char *buf, int bufsize) override; virtual void pack(const PointRef& point, char *buf, int bufsize) override; int m_offset; }; class ColorLoader : public PointLoader { public: ColorLoader(int offset) : m_offset(offset) {} private: virtual void load(PointRef& point, const char *buf, int bufsize) override; virtual void pack(const PointRef& point, char *buf, int bufsize) override; int m_offset; }; class NirLoader : public PointLoader { public: NirLoader(int offset) : m_offset(offset) {} private: virtual void load(PointRef& point, const char *buf, int bufsize) override; virtual void pack(const PointRef& point, char *buf, int bufsize) override; int m_offset; }; class ExtraDimLoader : public PointLoader { public: ExtraDimLoader(const ExtraDims& extraDims) : m_extraDims(extraDims) {} private: virtual void load(PointRef& point, const char *buf, int bufsize) override; virtual void pack(const PointRef& point, char *buf, int bufsize) override; ExtraDims m_extraDims; }; class LoaderDriver { public: LoaderDriver() = default; LoaderDriver(int pdrf, const Scaling& scaling, const ExtraDims& dims); void init(int pdrf, const Scaling& scaling, const ExtraDims& dims); bool load(PointRef& point, const char *buf, int bufsize); bool pack(const PointRef& point, char *buf, int bufsize); private: std::vector m_loaders; }; } // namespace las } // namespace pdal