Skip to content

Commit

Permalink
Merge pull request #1454 from PDAL/groupby
Browse files Browse the repository at this point in the history
Add a filter to split an incoming PointView by data in a given dimension
  • Loading branch information
chambbj committed Dec 21, 2016
2 parents c9e2f72 + ac9ce6c commit b8b6f76
Show file tree
Hide file tree
Showing 6 changed files with 274 additions and 0 deletions.
29 changes: 29 additions & 0 deletions doc/stages/filters.groupby.rst
@@ -0,0 +1,29 @@
.. _filters.groupby:

filters.groupby
===============================================================================

The groupby filter takes a single PointView as its input and creates a PointView
for each category in the named ``dimension`` as its output.

Example
-------

.. code-block:: json
{
"pipeline":[
"input.las",
{
"type":"filters.groupby",
"dimension":"Classification"
},
"output_#.las"
]
}
Options
-------

dimension
The dimension containing data to be grouped.
98 changes: 98 additions & 0 deletions filters/GroupByFilter.cpp
@@ -0,0 +1,98 @@
/******************************************************************************
* Copyright (c) 2016, 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 "GroupByFilter.hpp"

#include <pdal/pdal_macros.hpp>
#include <pdal/util/ProgramArgs.hpp>

namespace pdal
{

static PluginInfo const s_info =
PluginInfo("filters.groupby", "Split data categorically by dimension.",
"http://pdal.io/stages/filters.groupby.html");

CREATE_STATIC_PLUGIN(1, 0, GroupByFilter, Filter, s_info)

GroupByFilter::GroupByFilter() : m_viewMap()
{}

std::string GroupByFilter::getName() const
{
return s_info.name;
}

void GroupByFilter::addArgs(ProgramArgs& args)
{
args.add("dimension", "Dimension containing data to be grouped", m_dimName);
}

void GroupByFilter::prepared(PointTableRef table)
{
PointLayoutPtr layout(table.layout());
m_dimId = layout->findDim(m_dimName);
if (m_dimId == Dimension::Id::Unknown)
{
std::ostringstream oss;
oss << "Invalid dimension name in filters.groupby 'dimension' "
"option: '" << m_dimName << "'.";
throw pdal_error(oss.str());
}
// also need to check that we have a dimension with discrete values
}

PointViewSet GroupByFilter::run(PointViewPtr inView)
{
PointViewSet viewSet;
if (!inView->size())
return viewSet;

for (PointId idx = 0; idx < inView->size(); idx++)
{
uint64_t val = inView->getFieldAs<uint64_t>(m_dimId, idx);
PointViewPtr& outView = m_viewMap[val];
if (!outView)
outView = inView->makeNew();
outView->appendPoint(*inView.get(), idx);
}

// Pull the buffers out of the map and stick them in the standard
// output set.
for (auto bi = m_viewMap.begin(); bi != m_viewMap.end(); ++bi)
viewSet.insert(bi->second);
return viewSet;
}

} // pdal
74 changes: 74 additions & 0 deletions filters/GroupByFilter.hpp
@@ -0,0 +1,74 @@
/******************************************************************************
* Copyright (c) 2016, 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 <pdal/Filter.hpp>
#include <pdal/plugin.hpp>

#include <map>
#include <string>

extern "C" int32_t GroupByFilter_ExitFunc();
extern "C" PF_ExitFunc GroupByFilter_InitPlugin();

namespace pdal
{

class PointView;
class ProgramArgs;

class PDAL_DLL GroupByFilter : public Filter
{
public:
GroupByFilter();

static void * create();
static int32_t destroy(void *);
std::string getName() const;

private:
std::map<uint64_t, PointViewPtr> m_viewMap;
std::string m_dimName;
Dimension::Id m_dimId;

virtual void addArgs(ProgramArgs& args);
virtual void prepared(PointTableRef table);
virtual PointViewSet run(PointViewPtr view);

GroupByFilter& operator=(const GroupByFilter&); // not implemented
GroupByFilter(const GroupByFilter&); // not implemented
};

} // namespace pdal
2 changes: 2 additions & 0 deletions pdal/StageFactory.cpp
Expand Up @@ -48,6 +48,7 @@
#include <filters/EigenvaluesFilter.hpp>
#include <filters/EstimateRankFilter.hpp>
#include <filters/FerryFilter.hpp>
#include <filters/GroupByFilter.hpp>
#include <filters/HAGFilter.hpp>
#include <filters/IQRFilter.hpp>
#include <filters/KDistanceFilter.hpp>
Expand Down Expand Up @@ -244,6 +245,7 @@ StageFactory::StageFactory(bool no_plugins)
PluginManager::initializePlugin(EigenvaluesFilter_InitPlugin);
PluginManager::initializePlugin(EstimateRankFilter_InitPlugin);
PluginManager::initializePlugin(FerryFilter_InitPlugin);
PluginManager::initializePlugin(GroupByFilter_InitPlugin);
PluginManager::initializePlugin(HAGFilter_InitPlugin);
PluginManager::initializePlugin(IQRFilter_InitPlugin);
PluginManager::initializePlugin(KDistanceFilter_InitPlugin);
Expand Down
1 change: 1 addition & 0 deletions test/unit/CMakeLists.txt
Expand Up @@ -86,6 +86,7 @@ PDAL_ADD_TEST(pdal_filters_decimation_test FILES
filters/DecimationFilterTest.cpp)
PDAL_ADD_TEST(pdal_filters_divider_test FILES filters/DividerFilterTest.cpp)
PDAL_ADD_TEST(pdal_filters_ferry_test FILES filters/FerryFilterTest.cpp)
PDAL_ADD_TEST(pdal_filters_groupby_test FILES filters/GroupByFilterTest.cpp)
PDAL_ADD_TEST(pdal_filters_merge_test FILES filters/MergeTest.cpp)
PDAL_ADD_TEST(pdal_filters_additional_merge_test FILES
filters/AdditionalMergeTest.cpp)
Expand Down
70 changes: 70 additions & 0 deletions test/unit/filters/GroupByFilterTest.cpp
@@ -0,0 +1,70 @@
/******************************************************************************
* Copyright (c) 2016, 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 <pdal/pdal_test_main.hpp>

#include <io/LasReader.hpp>
#include <filters/GroupByFilter.hpp>
#include "Support.hpp"

using namespace pdal;

TEST(GroupByTest, basic_test)
{
Options ro;
ro.add("filename", Support::datapath("las/1.2-with-color.las"));
LasReader r;
r.setOptions(ro);

Options fo;
fo.add("dimension", "Classification");

GroupByFilter s;
s.setOptions(fo);
s.setInput(r);

PointTable table;
PointViewPtr view(new PointView(table));
s.prepare(table);
PointViewSet viewSet = s.execute(table);

EXPECT_EQ(2u, viewSet.size());

std::vector<PointViewPtr> views;
for (auto it = viewSet.begin(); it != viewSet.end(); ++it)
views.push_back(*it);

EXPECT_EQ(789u, views[0]->size());
EXPECT_EQ(276u, views[1]->size());
}

0 comments on commit b8b6f76

Please sign in to comment.