From 70ee281119a722f0e02110502513737a59814f18 Mon Sep 17 00:00:00 2001 From: Bradley J Chambers Date: Mon, 19 Mar 2018 11:37:09 +0100 Subject: [PATCH 1/4] Add returns filter --- doc/stages/filters.returns.rst | 41 +++++++++++ filters/ReturnsFilter.cpp | 126 +++++++++++++++++++++++++++++++++ filters/ReturnsFilter.hpp | 78 ++++++++++++++++++++ 3 files changed, 245 insertions(+) create mode 100644 doc/stages/filters.returns.rst create mode 100644 filters/ReturnsFilter.cpp create mode 100644 filters/ReturnsFilter.hpp diff --git a/doc/stages/filters.returns.rst b/doc/stages/filters.returns.rst new file mode 100644 index 0000000000..0a038efea6 --- /dev/null +++ b/doc/stages/filters.returns.rst @@ -0,0 +1,41 @@ +.. _filters.returns: + +filters.returns +=============================================================================== + +The returns filter takes a single PointView as its input and creates a PointView +for each of the user-specified ``groups`` defined below. + +``first`` is defined as those points whose ``ReturnNumber`` is 1 when the ``NumberOfReturns`` is greater than 1. + +``intermediate`` is defined as those points whose ``ReturnNumber`` is greater than 1 and less than ``NumberOfReturns`` when ``NumberOfReturns`` is greater than 2. + +``last`` is defined as those points whose ``ReturnNumber`` is equal to ``NumberOfReturns`` when ``NumberOfReturns`` is greater than 1. + +``only`` is defined as those points whose ``NumberOfReturns`` is 1. + +.. embed:: + +Example +------- + +This example creates separate output files for the ``last`` and ``only`` returns. + +.. code-block:: json + + { + "pipeline":[ + "input.las", + { + "type":"filters.returns", + "groups":"last,only" + }, + "output_#.las" + ] + } + +Options +------- + +groups + Comma-separated list of return number groupings ('first', 'last', 'intermediate', or 'only') diff --git a/filters/ReturnsFilter.cpp b/filters/ReturnsFilter.cpp new file mode 100644 index 0000000000..575736b791 --- /dev/null +++ b/filters/ReturnsFilter.cpp @@ -0,0 +1,126 @@ +/****************************************************************************** + * Copyright (c) 2018, Bradley J Chambers (brad.chambers@gmail.com) + * + * 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 Hobu, Inc. or Flaxen Geo Consulting 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. + ****************************************************************************/ + +#include "ReturnsFilter.hpp" + +#include + +namespace pdal +{ + +static StaticPluginInfo const s_info{ + "filters.returns", "Split data by return order", + "http://pdal.io/stages/filters.returns.html"}; + +CREATE_STATIC_STAGE(ReturnsFilter, s_info) + +std::string ReturnsFilter::getName() const +{ + return s_info.name; +} + +void ReturnsFilter::addArgs(ProgramArgs& args) +{ + args.add("groups", + "Comma-separated list of return number groupings ('first', " + "'last', 'intermediate', or 'only')", + m_returnsString, {"last"}); +} + +void ReturnsFilter::prepared(PointTableRef table) +{ + const PointLayoutPtr layout(table.layout()); + if (!layout->hasDim(Dimension::Id::ReturnNumber) || + !layout->hasDim(Dimension::Id::NumberOfReturns)) + { + log()->get(LogLevel::Warning) + << "Could not find ReturnNumber or " + "NumberOfReturns. Proceeding with all returns.\n"; + } +} + +PointViewSet ReturnsFilter::run(PointViewPtr inView) +{ + PointViewSet viewSet; + if (!inView->size()) + return viewSet; + + for (auto& r : m_returnsString) + { + Utils::trim(r); + if (r == "first") + m_outputTypes |= returnFirst; + else if (r == "intermediate") + m_outputTypes |= returnIntermediate; + else if (r == "last") + m_outputTypes |= returnLast; + else if (r == "only") + m_outputTypes |= returnOnly; + else + throwError("Invalid output type: '" + r + "'."); + } + + PointViewPtr firstView = inView->makeNew(); + PointViewPtr intermediateView = inView->makeNew(); + PointViewPtr lastView = inView->makeNew(); + PointViewPtr onlyView = inView->makeNew(); + + for (PointId idx = 0; idx < inView->size(); idx++) + { + PointRef p = inView->point(idx); + uint8_t rn = p.getFieldAs(Dimension::Id::ReturnNumber); + uint8_t nr = p.getFieldAs(Dimension::Id::NumberOfReturns); + if ((m_outputTypes & returnFirst) && (rn == 1) && (nr > 1)) + firstView->appendPoint(*inView.get(), idx); + if ((m_outputTypes & returnIntermediate) && (rn > 1) && (rn < nr) && + (nr > 2)) + intermediateView->appendPoint(*inView.get(), idx); + if ((m_outputTypes & returnLast) && (rn == nr) && (nr > 1)) + lastView->appendPoint(*inView.get(), idx); + if ((m_outputTypes & returnOnly) && (nr == 1)) + onlyView->appendPoint(*inView.get(), idx); + } + + if (firstView->size()) + viewSet.insert(firstView); + if (intermediateView->size()) + viewSet.insert(intermediateView); + if (lastView->size()) + viewSet.insert(lastView); + if (onlyView->size()) + viewSet.insert(onlyView); + return viewSet; +} + +} // namespace pdal diff --git a/filters/ReturnsFilter.hpp b/filters/ReturnsFilter.hpp new file mode 100644 index 0000000000..5cbf0cff77 --- /dev/null +++ b/filters/ReturnsFilter.hpp @@ -0,0 +1,78 @@ +/****************************************************************************** + * Copyright (c) 2018, Bradley J Chambers (brad.chambers@gmail.com) + * + * 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 Hobu, Inc. or Flaxen Geo Consulting 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 + +extern "C" int32_t ReturnsFilter_ExitFunc(); +extern "C" PF_ExitFunc ReturnsFilter_InitPlugin(); + +namespace pdal +{ + +class PointView; +class ProgramArgs; + +class PDAL_DLL ReturnsFilter : public Filter +{ +public: + ReturnsFilter() {} + + static const int returnFirst = 1; + static const int returnIntermediate = 2; + static const int returnLast = 4; + static const int returnOnly = 8; + + static void* create(); + static int32_t destroy(void*); + std::string getName() const; + +private: + std::map m_viewMap; + StringList m_returnsString; + int m_outputTypes; + + virtual void addArgs(ProgramArgs& args); + virtual void prepared(PointTableRef table); + virtual PointViewSet run(PointViewPtr view); + + ReturnsFilter& operator=(const ReturnsFilter&); // not implemented + ReturnsFilter(const ReturnsFilter&); // not implemented +}; + +} // namespace pdal From 2ceba6b8d70afab83e07e95a7c4fae0a8a0add3d Mon Sep 17 00:00:00 2001 From: Bradley J Chambers Date: Mon, 19 Mar 2018 13:03:34 +0100 Subject: [PATCH 2/4] Issue a warning when ReturnsFilter creates an empty set, remove old plugin boilerplate --- filters/ReturnsFilter.cpp | 44 ++++++++++++++++++++++++++++++++------- filters/ReturnsFilter.hpp | 9 ++------ 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/filters/ReturnsFilter.cpp b/filters/ReturnsFilter.cpp index 575736b791..e41d98ac0f 100644 --- a/filters/ReturnsFilter.cpp +++ b/filters/ReturnsFilter.cpp @@ -112,14 +112,42 @@ PointViewSet ReturnsFilter::run(PointViewPtr inView) onlyView->appendPoint(*inView.get(), idx); } - if (firstView->size()) - viewSet.insert(firstView); - if (intermediateView->size()) - viewSet.insert(intermediateView); - if (lastView->size()) - viewSet.insert(lastView); - if (onlyView->size()) - viewSet.insert(onlyView); + if (m_outputTypes & returnFirst) + { + if (firstView->size()) + viewSet.insert(firstView); + else + log()->get(LogLevel::Warning) + << "Requested first returns set it empty\n"; + } + + if (m_outputTypes & returnIntermediate) + { + if (intermediateView->size()) + viewSet.insert(intermediateView); + else + log()->get(LogLevel::Warning) + << "Requested intermediate returns set is empty\n"; + } + + if (m_outputTypes & returnLast) + { + if (lastView->size()) + viewSet.insert(lastView); + else + log()->get(LogLevel::Warning) + << "Requested last returns set is empty\n"; + } + + if (m_outputTypes & returnOnly) + { + if (onlyView->size()) + viewSet.insert(onlyView); + else + log()->get(LogLevel::Warning) + << "Requested only returns set is empty\n"; + } + return viewSet; } diff --git a/filters/ReturnsFilter.hpp b/filters/ReturnsFilter.hpp index 5cbf0cff77..116453aa3f 100644 --- a/filters/ReturnsFilter.hpp +++ b/filters/ReturnsFilter.hpp @@ -39,9 +39,6 @@ #include #include -extern "C" int32_t ReturnsFilter_ExitFunc(); -extern "C" PF_ExitFunc ReturnsFilter_InitPlugin(); - namespace pdal { @@ -58,8 +55,6 @@ class PDAL_DLL ReturnsFilter : public Filter static const int returnLast = 4; static const int returnOnly = 8; - static void* create(); - static int32_t destroy(void*); std::string getName() const; private: @@ -71,8 +66,8 @@ class PDAL_DLL ReturnsFilter : public Filter virtual void prepared(PointTableRef table); virtual PointViewSet run(PointViewPtr view); - ReturnsFilter& operator=(const ReturnsFilter&); // not implemented - ReturnsFilter(const ReturnsFilter&); // not implemented + ReturnsFilter& operator=(const ReturnsFilter&) = delete; // not implemented + ReturnsFilter(const ReturnsFilter&) = delete; // not implemented }; } // namespace pdal From 9820c21e6f7e1e639ee7744953745bbbbe75eb0d Mon Sep 17 00:00:00 2001 From: Bradley J Chambers Date: Mon, 19 Mar 2018 13:46:01 +0100 Subject: [PATCH 3/4] Remove unused viewSet --- filters/ReturnsFilter.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/filters/ReturnsFilter.hpp b/filters/ReturnsFilter.hpp index 116453aa3f..2caec34199 100644 --- a/filters/ReturnsFilter.hpp +++ b/filters/ReturnsFilter.hpp @@ -36,7 +36,6 @@ #include -#include #include namespace pdal @@ -58,7 +57,6 @@ class PDAL_DLL ReturnsFilter : public Filter std::string getName() const; private: - std::map m_viewMap; StringList m_returnsString; int m_outputTypes; From a10bb5b8e24053d452941e601da5186836b59514 Mon Sep 17 00:00:00 2001 From: Bradley J Chambers Date: Wed, 21 Mar 2018 12:41:24 +0100 Subject: [PATCH 4/4] Improve the warning message --- filters/ReturnsFilter.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/filters/ReturnsFilter.cpp b/filters/ReturnsFilter.cpp index e41d98ac0f..a792e41f48 100644 --- a/filters/ReturnsFilter.cpp +++ b/filters/ReturnsFilter.cpp @@ -118,7 +118,7 @@ PointViewSet ReturnsFilter::run(PointViewPtr inView) viewSet.insert(firstView); else log()->get(LogLevel::Warning) - << "Requested first returns set it empty\n"; + << "Requested returns group 'first' is empty\n"; } if (m_outputTypes & returnIntermediate) @@ -127,7 +127,7 @@ PointViewSet ReturnsFilter::run(PointViewPtr inView) viewSet.insert(intermediateView); else log()->get(LogLevel::Warning) - << "Requested intermediate returns set is empty\n"; + << "Requested returns group 'intermediate' is empty\n"; } if (m_outputTypes & returnLast) @@ -136,7 +136,7 @@ PointViewSet ReturnsFilter::run(PointViewPtr inView) viewSet.insert(lastView); else log()->get(LogLevel::Warning) - << "Requested last returns set is empty\n"; + << "Requested returns group 'last' is empty\n"; } if (m_outputTypes & returnOnly) @@ -145,7 +145,7 @@ PointViewSet ReturnsFilter::run(PointViewPtr inView) viewSet.insert(onlyView); else log()->get(LogLevel::Warning) - << "Requested only returns set is empty\n"; + << "Requested returns group 'only' is empty\n"; } return viewSet;