Skip to content

Commit

Permalink
Streaming info kernel.
Browse files Browse the repository at this point in the history
Close #2170
  • Loading branch information
abellgithub committed Sep 20, 2018
1 parent e667a93 commit 8874790
Show file tree
Hide file tree
Showing 10 changed files with 539 additions and 291 deletions.
263 changes: 263 additions & 0 deletions filters/InfoFilter.cpp
@@ -0,0 +1,263 @@
/******************************************************************************
* Copyright (c) 2018, 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 Hobu, Inc. 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 "InfoFilter.hpp"

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

#include <json/json.h>

namespace pdal
{

static StaticPluginInfo const s_info
{
"filters.info",
"Gather basic info about points.",
"http://pdal.io/stages/filters.info.html"
};

CREATE_STATIC_STAGE(InfoFilter, s_info)

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

void InfoFilter::addArgs(ProgramArgs& args)
{
args.add("point,p", "Point to dump\n--point=\"1-5,10,100-200\" (0 indexed)",
m_pointSpec);
args.add("query",
"Return points in order of distance from the specified "
"location (2D or 3D)\n"
"--query Xcoord,Ycoord[,Zcoord][/count]",
m_querySpec);
}


void InfoFilter::parsePointSpec()
{
auto parseInt = [this](const std::string& s)
{
uint32_t i;

if (!Utils::fromString(s, i))
throwError("Invalid integer '" + s + "in 'point' option");
return i;
};

auto addRange = [this, &parseInt](const std::string& begin,
const std::string& end)
{
PointId low = parseInt(begin);
PointId high = parseInt(end);
if (low > high)
throwError("Invalid range in 'point' option: '" +
begin + "-" + end);
while (low <= high)
m_idList.push_back(low++);
};

//Remove whitespace from string with awful remove/erase idiom.
Utils::trim(m_pointSpec);

StringList ranges = Utils::split2(m_pointSpec, ',');
for (std::string& s : ranges)
{
StringList limits = Utils::split(s, '-');
if (limits.size() == 1)
m_idList.push_back(parseInt(limits[0]));
else if (limits.size() == 2)
addRange(limits[0], limits[1]);
else
throwError("Invalid point range in 'point' option: " + s);
}
}


void InfoFilter::parseQuerySpec()
{
// See if there's a provided point count.
StringList parts = Utils::split2(m_querySpec, '/');
if (parts.size() == 2)
{
if (!Utils::fromString(parts[1], m_queryCount))
throwError("Invalid query count in 'query' option: " + parts[1]);
}
else if (parts.size() != 1)
throwError("Invalid point location specification. Sytax: "
"--query=\"X,Y[/count]\"");

//ABELL - This doesn't match syntax below, but keeping because
// history.
auto seps = [](char c) { return (c == ',' || c == '|' || c == ' '); };

StringList tokens = Utils::split2(parts[0], seps);
if (tokens.size() != 2 && tokens.size() != 3)
throwError("Invalid point location specification. Sytax: "
"--query=\"X,Y[/count]\"");

bool ok = true;
ok &= Utils::fromString(tokens[0], m_queryX);
ok &= Utils::fromString(tokens[1], m_queryY);
if (tokens.size() == 3)
ok &= Utils::fromString(tokens[1], m_queryZ);
if (!ok)
throwError("Invalid point location specification. Sytax: "
"--query=\"X,Y[/count]\"");
}


void InfoFilter::prepared(PointTableRef table)
{
m_dims = table.layout()->dimTypes();
m_pointSize = table.layout()->pointSize();
if (m_pointSpec.size())
parsePointSpec();
if (m_querySpec.size())
parseQuerySpec();
}


void InfoFilter::initialize(PointTableRef table)
{
getMetadata().add(table.layout()->toMetadata());
}


void InfoFilter::ready(PointTableRef)
{
m_count = 0;
m_idCur = m_idList.begin();
}


void InfoFilter::filter(PointView& view)
{
PointRef point(view, 0);
for (PointId idx = 0; idx < view.size(); ++idx)
{
point.setPointId(idx);
processOne(point);
}
}


bool InfoFilter::processOne(PointRef& point)
{
double x = point.getFieldAs<double>(Dimension::Id::X);
double y = point.getFieldAs<double>(Dimension::Id::Y);
double z = point.getFieldAs<double>(Dimension::Id::Z);

// Accumulate min/max.
m_bounds.grow(x, y, z);

// Create metadata for requsted points.
// We may get a point list or a query list, but not both.
if (m_idCur != m_idList.end() && *m_idCur == m_count)
{
// This is kind of a fake, in that the points in the list aren't
// near anything, but it makes for a single set of code to
// extract the metadata of reported points.
std::vector<char> buf(m_pointSize);
point.getPackedData(m_dims, buf.data());
m_results.emplace_back(m_count, 0, std::move(buf));
m_idCur++;
}
else if (m_querySpec.size() && m_queryCount)
{
double dist = std::pow(x - m_queryX, 2) + std::pow(y - m_queryY, 2);
if (!isnan(m_queryZ))
dist += std::pow(z - m_queryZ, 2);
if (m_results.size() < m_queryCount || dist < m_results.back().m_dist)
{
std::vector<char> buf(m_pointSize);
point.getPackedData(m_dims, buf.data());
NearPoint np(m_count, dist, std::move(buf));
m_results.insert(
std::upper_bound(m_results.begin(), m_results.end(), np),
std::move(np));
if (m_results.size() > m_queryCount)
m_results.pop_back();
}
}
m_count++;
return true;
}


void InfoFilter::done(PointTableRef table)
{
std::cerr << "InfoFilter::done!\n";
// Point list
MetadataNode points("points");
for (NearPoint& np: m_results)
{
MetadataNode node("point");
const char *buf = np.m_data.data();
Everything e;
for (DimType& dt : m_dims)
{
size_t dimSize = Dimension::size(dt.m_type);
std::copy(buf, buf + dimSize, reinterpret_cast<char *>(&e));
double d = Utils::toDouble(e, dt.m_type);
node.add(table.layout()->dimName(dt.m_id), d);
buf += dimSize;
}
node.add("PointId", np.m_id);
points.add(node);
}
if (points.hasChildren())
getMetadata().add(points);

// Bounds
getMetadata().add(Utils::toMetadata(m_bounds));

// Point count
getMetadata().add("num_points", m_count);

// Dimension names
std::string dims;
for (auto di = m_dims.begin(); di != m_dims.end();)
{
dims += table.layout()->dimName(di->m_id);
if (++di != m_dims.end())
dims += ", ";
}
getMetadata().add("dimensions", dims);

// Spatial reference
getMetadata().add(table.anySpatialReference().toMetadata());
}

} // namespace pdal
103 changes: 103 additions & 0 deletions filters/InfoFilter.hpp
@@ -0,0 +1,103 @@
/******************************************************************************
* Copyright (c) 2018, 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 Hobu, Inc. 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/Streamable.hpp>
#include <pdal/util/Bounds.hpp>

namespace pdal
{

// This is just a pass-through filter, which collects some data about
// the points that are fed through it
class PDAL_DLL InfoFilter : public Filter, public Streamable
{
struct NearPoint
{
NearPoint(PointId id, double dist, std::vector<char>&& data) :
m_id(id), m_dist(dist), m_data(data)
{}

PointId m_id;
double m_dist;
std::vector<char> m_data;

bool operator < (const NearPoint& other) const
{ return m_dist < other.m_dist; }
};

public:
InfoFilter() :
m_pointRoot("point"), m_queryCount(10),
m_queryZ(std::numeric_limits<double>::quiet_NaN())
{}

std::string getName() const;
BOX3D bounds() const
{ return m_bounds; }

private:
virtual void addArgs(ProgramArgs& args);
virtual void initialize(PointTableRef table);
virtual void ready(PointTableRef table);
virtual bool processOne(PointRef& point);
virtual void prepared(PointTableRef table);
virtual void done(PointTableRef table);
virtual void filter(PointView& view);

void parsePointSpec();
void parseQuerySpec();

MetadataNode m_pointRoot;

std::string m_querySpec;
point_count_t m_queryCount;
double m_queryX;
double m_queryY;
double m_queryZ;
std::list<NearPoint> m_results;

std::string m_pointSpec;
std::vector<PointId> m_idList;
std::vector<PointId>::const_iterator m_idCur;
DimTypeList m_dims;
size_t m_pointSize;
PointId m_count;

BOX3D m_bounds;
};

} // namespace pdal

0 comments on commit 8874790

Please sign in to comment.