From 5d7c9857b6af960f295f0f85a8a7557edefb546d Mon Sep 17 00:00:00 2001 From: Andrew Bell Date: Mon, 7 May 2018 15:26:35 -0400 Subject: [PATCH 1/3] Use pimpl/unique_ptr to make private headers private (#1990) * Use PIMPL for args where the types are private. --- filters/ColorizationFilter.cpp | 8 ++ filters/ColorizationFilter.hpp | 4 +- filters/CropFilter.cpp | 78 +++++++++------ filters/CropFilter.hpp | 16 +-- filters/GreedyProjection.hpp | 4 + filters/NormalFilter.cpp | 42 +++++--- filters/NormalFilter.hpp | 13 +-- filters/PMFFilter.cpp | 94 +++++++++++------- filters/PMFFilter.hpp | 18 ++-- filters/SMRFilter.cpp | 176 ++++++++++++++++++++------------- filters/SMRFilter.hpp | 20 ++-- 11 files changed, 281 insertions(+), 192 deletions(-) diff --git a/filters/ColorizationFilter.cpp b/filters/ColorizationFilter.cpp index 0b543a3317..2372683e7e 100644 --- a/filters/ColorizationFilter.cpp +++ b/filters/ColorizationFilter.cpp @@ -126,6 +126,14 @@ ColorizationFilter::BandInfo parseDim(const std::string& dim, } // unnamed namespace +ColorizationFilter::ColorizationFilter() +{} + + +ColorizationFilter::~ColorizationFilter() +{} + + void ColorizationFilter::addArgs(ProgramArgs& args) { args.add("raster", "Raster filename", m_rasterFilename); diff --git a/filters/ColorizationFilter.hpp b/filters/ColorizationFilter.hpp index 807ec095cb..da6bc2d356 100644 --- a/filters/ColorizationFilter.hpp +++ b/filters/ColorizationFilter.hpp @@ -69,9 +69,9 @@ class PDAL_DLL ColorizationFilter : public Filter, public Streamable Dimension::Type m_type; }; + ColorizationFilter(); + ~ColorizationFilter(); - ColorizationFilter() - {} ColorizationFilter& operator=(const ColorizationFilter&) = delete; ColorizationFilter(const ColorizationFilter&) = delete; diff --git a/filters/CropFilter.cpp b/filters/CropFilter.cpp index 48008e2611..efbf888826 100644 --- a/filters/CropFilter.cpp +++ b/filters/CropFilter.cpp @@ -58,6 +58,16 @@ static StaticPluginInfo const s_info CREATE_STATIC_STAGE(CropFilter, s_info) +struct CropArgs +{ + bool m_cropOutside; + SpatialReference m_assignedSrs; + std::vector m_bounds; + std::vector m_centers; + double m_distance; + std::vector m_polys; +}; + CropFilter::ViewGeom::ViewGeom(const Polygon& poly) : m_poly(poly) {} @@ -67,25 +77,28 @@ CropFilter::ViewGeom::ViewGeom(ViewGeom&& vg) : std::string CropFilter::getName() const { return s_info.name; } -CropFilter::CropFilter() : m_cropOutside(false) +CropFilter::CropFilter() : m_args(new CropArgs) {} + CropFilter::~CropFilter() {} + void CropFilter::addArgs(ProgramArgs& args) { args.add("outside", "Whether we keep points inside or outside of the " - "bounding region", m_cropOutside); - args.add("a_srs", "Spatial reference for bounding region", m_assignedSrs); - args.add("bounds", "Point box for cropped points", m_bounds); + "bounding region", m_args->m_cropOutside); + args.add("a_srs", "Spatial reference for bounding region", + m_args->m_assignedSrs); + args.add("bounds", "Point box for cropped points", m_args->m_bounds); args.add("point", "Center of circular/spherical crop region. Use with " - "'distance'.", m_centers).setErrorText("Invalid point specification. " - "Must be valid GeoJSON/WKT. " - "Ex: \"(1.00, 1.00)\" or \"(1.00, 1.00, 1.00)\""); + "'distance'.", m_args->m_centers). + setErrorText("Invalid point specification. Must be valid " + "GeoJSON/WKT. Ex: \"(1.00, 1.00)\" or \"(1.00, 1.00, 1.00)\""); args.add("distance", "Crop with this distance from 2D or 3D 'point'", - m_distance); - args.add("polygon", "Bounding polying for cropped points", m_polys). + m_args->m_distance); + args.add("polygon", "Bounding polying for cropped points", m_args->m_polys). setErrorText("Invalid polygon specification. " "Must be valid GeoJSON/WKT"); } @@ -94,10 +107,10 @@ void CropFilter::addArgs(ProgramArgs& args) void CropFilter::initialize() { // Set geometry from polygons. - if (m_polys.size()) + if (m_args->m_polys.size()) { m_geoms.clear(); - for (Polygon& poly : m_polys) + for (Polygon& poly : m_args->m_polys) { // Throws if invalid. poly.valid(); @@ -106,26 +119,26 @@ void CropFilter::initialize() } m_boxes.clear(); - for (auto& bound : m_bounds) + for (auto& bound : m_args->m_bounds) m_boxes.push_back(bound.to2d()); - m_distance2 = m_distance * m_distance; + m_distance2 = m_args->m_distance * m_args->m_distance; } void CropFilter::ready(PointTableRef table) { // If the user didn't provide an SRS, take one from the table. - if (m_assignedSrs.empty()) + if (m_args->m_assignedSrs.empty()) { - m_assignedSrs = table.anySpatialReference(); + m_args->m_assignedSrs = table.anySpatialReference(); if (!table.spatialReferenceUnique()) log()->get(LogLevel::Warning) << "Can't determine spatial " "reference for provided bounds. Consider using 'a_srs' " "option.\n"; } for (auto& geom : m_geoms) - geom.m_poly.setSpatialReference(m_assignedSrs); + geom.m_poly.setSpatialReference(m_args->m_assignedSrs); } @@ -140,7 +153,7 @@ bool CropFilter::processOne(PointRef& point) if (!crop(point, box)) return false; - for (auto& center: m_centers) + for (auto& center: m_args->m_centers) if (!crop(point, center)) return false; @@ -177,26 +190,27 @@ void CropFilter::transform(const SpatialReference& srs) } // If we don't have any SRS, do nothing. - if (srs.empty() && m_assignedSrs.empty()) + if (srs.empty() && m_args->m_assignedSrs.empty()) return; - if (srs.empty() || m_assignedSrs.empty()) + if (srs.empty() || m_args->m_assignedSrs.empty()) throwError("Unable to transform crop geometry to point " "coordinate system."); for (auto& box : m_boxes) { - if (!gdal::reprojectBounds(box, m_assignedSrs.getWKT(), srs.getWKT())) + if (!gdal::reprojectBounds(box, m_args->m_assignedSrs.getWKT(), + srs.getWKT())) throwError("Unable to reproject bounds."); } - for (auto& point : m_centers) + for (auto& point : m_args->m_centers) { if (!gdal::reprojectPoint(point.x, point.y, point.z, - m_assignedSrs.getWKT(), srs.getWKT())) + m_args->m_assignedSrs.getWKT(), srs.getWKT())) throwError("Unable to reproject point center."); } // Set the assigned SRS for the points/bounds to the one we've // transformed to. - m_assignedSrs = srs; + m_args->m_assignedSrs = srs; } @@ -219,7 +233,7 @@ PointViewSet CropFilter::run(PointViewPtr view) viewSet.insert(outView); } - for (auto& point: m_centers) + for (auto& point: m_args->m_centers) { PointViewPtr outView = view->makeNew(); crop(point, *view, *outView); @@ -236,7 +250,7 @@ bool CropFilter::crop(const PointRef& point, const BOX2D& box) double y = point.getFieldAs(Dimension::Id::Y); // Return true if we're keeping a point. - return (m_cropOutside != box.contains(x, y)); + return (m_args->m_cropOutside != box.contains(x, y)); } @@ -246,7 +260,7 @@ void CropFilter::crop(const BOX2D& box, PointView& input, PointView& output) for (PointId idx = 0; idx < input.size(); ++idx) { point.setPointId(idx); - if (m_cropOutside != crop(point, box)) + if (m_args->m_cropOutside != crop(point, box)) output.appendPoint(input, idx); } } @@ -256,7 +270,7 @@ bool CropFilter::crop(const PointRef& point, GridPnp& g) { double x = point.getFieldAs(Dimension::Id::X); double y = point.getFieldAs(Dimension::Id::Y); - return (m_cropOutside != g.inside(x, y)); + return (m_args->m_cropOutside != g.inside(x, y)); } @@ -281,21 +295,21 @@ bool CropFilter::crop(const PointRef& point, const filter::Point& center) double y = point.getFieldAs(Dimension::Id::Y); x = std::abs(x - center.x); y = std::abs(y - center.y); - if (x > m_distance || y > m_distance) - return (m_cropOutside); + if (x > m_args->m_distance || y > m_args->m_distance) + return (m_args->m_cropOutside); bool inside; if (center.is3d()) { double z = point.getFieldAs(Dimension::Id::Z); z = std::abs(z - center.z); - if (z > m_distance) - return (m_cropOutside); + if (z > m_args->m_distance) + return (m_args->m_cropOutside); inside = (x * x + y * y + z * z < m_distance2); } else inside = (x * x + y * y < m_distance2); - return (m_cropOutside != inside); + return (m_args->m_cropOutside != inside); } diff --git a/filters/CropFilter.hpp b/filters/CropFilter.hpp index 52089d020c..a9740721af 100644 --- a/filters/CropFilter.hpp +++ b/filters/CropFilter.hpp @@ -35,18 +35,22 @@ #pragma once #include +#include #include #include #include -#include "private/Point.hpp" - namespace pdal { class ProgramArgs; class GridPnp; +struct CropArgs; +namespace filter +{ + class Point; +} // removes any points outside of the given range // updates the header accordingly @@ -55,6 +59,7 @@ class PDAL_DLL CropFilter : public Filter, public Streamable public: CropFilter(); ~CropFilter(); + std::string getName() const; private: @@ -68,13 +73,8 @@ class PDAL_DLL CropFilter : public Filter, public Streamable Polygon m_poly; std::vector> m_gridPnps; }; - std::vector m_bounds; - bool m_cropOutside; - std::vector m_polys; - SpatialReference m_assignedSrs; - double m_distance; + std::unique_ptr m_args; double m_distance2; - std::vector m_centers; std::vector m_geoms; std::vector m_boxes; diff --git a/filters/GreedyProjection.hpp b/filters/GreedyProjection.hpp index be86806730..3db5e190cd 100644 --- a/filters/GreedyProjection.hpp +++ b/filters/GreedyProjection.hpp @@ -39,6 +39,10 @@ #pragma once +// This is for M_PI on Windows. cmath +#define _USE_MATH_DEFINES +#include + #include #include diff --git a/filters/NormalFilter.cpp b/filters/NormalFilter.cpp index 3cd7d26e81..8f2dff6b2b 100644 --- a/filters/NormalFilter.cpp +++ b/filters/NormalFilter.cpp @@ -33,6 +33,7 @@ ****************************************************************************/ #include "NormalFilter.hpp" +#include "private/Point.hpp" #include #include @@ -55,6 +56,21 @@ static StaticPluginInfo const s_info CREATE_STATIC_STAGE(NormalFilter, s_info) +struct NormalArgs +{ + int m_knn; + filter::Point m_viewpoint; + bool m_up; +}; + +NormalFilter::NormalFilter() : m_args(new NormalArgs) +{} + + +NormalFilter::~NormalFilter() +{} + + std::string NormalFilter::getName() const { return s_info.name; @@ -62,11 +78,11 @@ std::string NormalFilter::getName() const void NormalFilter::addArgs(ProgramArgs& args) { - args.add("knn", "k-Nearest Neighbors", m_knn, 8); - m_viewpointArg = - &args.add("viewpoint", "Viewpoint as WKT or GeoJSON", m_viewpoint); - args.add("always_up", "Normals always oriented with positive Z?", m_up, - true); + args.add("knn", "k-Nearest Neighbors", m_args->m_knn, 8); + m_viewpointArg = &args.add("viewpoint", + "Viewpoint as WKT or GeoJSON", m_args->m_viewpoint); + args.add("always_up", "Normals always oriented with positive Z?", + m_args->m_up, true); } void NormalFilter::addDimensions(PointLayoutPtr layout) @@ -80,7 +96,7 @@ void NormalFilter::addDimensions(PointLayoutPtr layout) // public method to access filter, used by GreedyProjection and Poisson filters void NormalFilter::doFilter(PointView& view, int knn) { - m_knn = knn; + m_args->m_knn = knn; ProgramArgs args; addArgs(args); // We're never parsing anything, so we'll just end up with default vals. @@ -90,11 +106,11 @@ void NormalFilter::doFilter(PointView& view, int knn) void NormalFilter::prepared(PointTableRef table) { - if (m_up && m_viewpointArg->set()) + if (m_args->m_up && m_viewpointArg->set()) { log()->get(LogLevel::Warning) << "Viewpoint provided. Ignoring always_up = TRUE." << std::endl; - m_up = false; + m_args->m_up = false; } } @@ -105,7 +121,7 @@ void NormalFilter::filter(PointView& view) for (PointId i = 0; i < view.size(); ++i) { // find the k-nearest neighbors - auto ids = kdi.neighbors(i, m_knn); + auto ids = kdi.neighbors(i, m_args->m_knn); // compute covariance of the neighborhood auto B = eigen::computeCovariance(view, ids); @@ -121,13 +137,13 @@ void NormalFilter::filter(PointView& view) { PointRef p = view.point(i); Eigen::Vector3f vp( - m_viewpoint.x - p.getFieldAs(Dimension::Id::X), - m_viewpoint.y - p.getFieldAs(Dimension::Id::Y), - m_viewpoint.z - p.getFieldAs(Dimension::Id::Z)); + m_args->m_viewpoint.x - p.getFieldAs(Dimension::Id::X), + m_args->m_viewpoint.y - p.getFieldAs(Dimension::Id::Y), + m_args->m_viewpoint.z - p.getFieldAs(Dimension::Id::Z)); if (vp.dot(normal) < 0) normal *= -1.0; } - else if (m_up) + else if (m_args->m_up) { if (normal[2] < 0) normal *= -1.0; diff --git a/filters/NormalFilter.hpp b/filters/NormalFilter.hpp index f38283a77b..681d7141d6 100644 --- a/filters/NormalFilter.hpp +++ b/filters/NormalFilter.hpp @@ -37,9 +37,6 @@ #include #include -#include "private/Point.hpp" - -#include #include #include @@ -49,12 +46,14 @@ namespace pdal class Options; class PointLayout; class PointView; +struct NormalArgs; class PDAL_DLL NormalFilter : public Filter { public: - NormalFilter() : Filter() - {} + NormalFilter(); + ~NormalFilter(); + NormalFilter& operator=(const NormalFilter&) = delete; NormalFilter(const NormalFilter&) = delete; @@ -63,10 +62,8 @@ class PDAL_DLL NormalFilter : public Filter std::string getName() const; private: - int m_knn; - filter::Point m_viewpoint; + std::unique_ptr m_args; Arg* m_viewpointArg; - bool m_up; virtual void addArgs(ProgramArgs& args); virtual void addDimensions(PointLayoutPtr layout); diff --git a/filters/PMFFilter.cpp b/filters/PMFFilter.cpp index c644314067..e15f82ddb0 100644 --- a/filters/PMFFilter.cpp +++ b/filters/PMFFilter.cpp @@ -56,8 +56,29 @@ static StaticPluginInfo const s_info "http://pdal.io/stages/filters.pmf.html" }; +struct PMFArgs +{ + double m_cellSize; + bool m_exponential; + DimRange m_ignored; + double m_initialDistance; + bool m_lastOnly; + double m_maxDistance; + double m_maxWindowSize; + double m_slope; +}; + + CREATE_STATIC_STAGE(PMFFilter, s_info) +PMFFilter::PMFFilter() : m_args(new PMFArgs) +{} + + +PMFFilter::~PMFFilter() +{} + + std::string PMFFilter::getName() const { return s_info.name; @@ -65,15 +86,17 @@ std::string PMFFilter::getName() const void PMFFilter::addArgs(ProgramArgs& args) { - args.add("cell_size", "Cell size", m_cellSize, 1.0); - args.add("exponential", "Exponential growth of window size?", m_exponential, - true); - args.add("ignore", "Ignore values", m_ignored); - args.add("initial_distance", "Initial distance", m_initialDistance, 0.15); - args.add("last", "Consider last returns only?", m_lastOnly, true); - args.add("max_distance", "Maximum distance", m_maxDistance, 2.5); - args.add("max_window_size", "Maximum window size", m_maxWindowSize, 33.0); - args.add("slope", "Slope", m_slope, 1.0); + args.add("cell_size", "Cell size", m_args->m_cellSize, 1.0); + args.add("exponential", "Exponential growth of window size?", + m_args->m_exponential, true); + args.add("ignore", "Ignore values", m_args->m_ignored); + args.add("initial_distance", "Initial distance", + m_args->m_initialDistance, 0.15); + args.add("last", "Consider last returns only?", m_args->m_lastOnly, true); + args.add("max_distance", "Maximum distance", m_args->m_maxDistance, 2.5); + args.add("max_window_size", "Maximum window size", + m_args->m_maxWindowSize, 33.0); + args.add("slope", "Slope", m_args->m_slope, 1.0); } void PMFFilter::addDimensions(PointLayoutPtr layout) @@ -85,9 +108,9 @@ void PMFFilter::prepared(PointTableRef table) { const PointLayoutPtr layout(table.layout()); - m_ignored.m_id = layout->findDim(m_ignored.m_name); + m_args->m_ignored.m_id = layout->findDim(m_args->m_ignored.m_name); - if (m_lastOnly) + if (m_args->m_lastOnly) { if (!layout->hasDim(Dimension::Id::ReturnNumber) || !layout->hasDim(Dimension::Id::NumberOfReturns)) @@ -96,7 +119,7 @@ void PMFFilter::prepared(PointTableRef table) "NumberOfReturns. Skipping " "segmentation of last returns and " "proceeding with all returns.\n"; - m_lastOnly = false; + m_args->m_lastOnly = false; } } } @@ -110,10 +133,11 @@ PointViewSet PMFFilter::run(PointViewPtr input) // Segment input view into ignored/kept views. PointViewPtr ignoredView = input->makeNew(); PointViewPtr keptView = input->makeNew(); - if (m_ignored.m_id == Dimension::Id::Unknown) + if (m_args->m_ignored.m_id == Dimension::Id::Unknown) keptView->append(*input); else - Segmentation::ignoreDimRange(m_ignored, input, keptView, ignoredView); + Segmentation::ignoreDimRange(m_args->m_ignored, input, + keptView, ignoredView); // Classify remaining points with value of 1. processGround will mark ground // returns as 2. @@ -123,7 +147,7 @@ PointViewSet PMFFilter::run(PointViewPtr input) // Segment kept view into last/other-than-last return views. PointViewPtr lastView = keptView->makeNew(); PointViewPtr nonlastView = keptView->makeNew(); - if (m_lastOnly) + if (m_args->m_lastOnly) Segmentation::segmentLastReturns(keptView, lastView, nonlastView); else lastView->append(*keptView); @@ -146,8 +170,8 @@ void PMFFilter::processGround(PointViewPtr view) // initialize bounds, rows, columns, and surface BOX2D bounds; view->calculateBounds(bounds); - size_t cols = ((bounds.maxx - bounds.minx) / m_cellSize) + 1; - size_t rows = ((bounds.maxy - bounds.miny) / m_cellSize) + 1; + size_t cols = ((bounds.maxx - bounds.minx) / m_args->m_cellSize) + 1; + size_t rows = ((bounds.maxy - bounds.miny) / m_args->m_cellSize) + 1; // initialize surface to NaN std::vector ZImin(rows * cols, @@ -160,8 +184,8 @@ void PMFFilter::processGround(PointViewPtr view) double x = view->getFieldAs(Dimension::Id::X, i); double y = view->getFieldAs(Dimension::Id::Y, i); double z = view->getFieldAs(Dimension::Id::Z, i); - int c = static_cast(floor(x - bounds.minx) / m_cellSize); - int r = static_cast(floor(y - bounds.miny) / m_cellSize); + int c = static_cast(floor(x - bounds.minx) / m_args->m_cellSize); + int r = static_cast(floor(y - bounds.miny) / m_args->m_cellSize); size_t idx = c * rows + r; if (z < ZImin[idx] || std::isnan(ZImin[idx])) ZImin[idx] = z; @@ -177,8 +201,8 @@ void PMFFilter::processGround(PointViewPtr view) size_t idx = c * rows + r; if (std::isnan(ZImin[idx])) continue; - double x = bounds.minx + (c + 0.5) * m_cellSize; - double y = bounds.miny + (r + 0.5) * m_cellSize; + double x = bounds.minx + (c + 0.5) * m_args->m_cellSize; + double y = bounds.miny + (r + 0.5) * m_args->m_cellSize; temp->setField(Dimension::Id::X, i, x); temp->setField(Dimension::Id::Y, i, y); temp->setField(Dimension::Id::Z, i, ZImin[idx]); @@ -200,8 +224,8 @@ void PMFFilter::processGround(PointViewPtr view) size_t idx = c * rows + r; if (!std::isnan(out[idx])) continue; - double x = bounds.minx + (c + 0.5) * m_cellSize; - double y = bounds.miny + (r + 0.5) * m_cellSize; + double x = bounds.minx + (c + 0.5) * m_args->m_cellSize; + double y = bounds.miny + (r + 0.5) * m_args->m_cellSize; int k = 1; std::vector neighbors(k); std::vector sqr_dists(k); @@ -225,24 +249,24 @@ void PMFFilter::processGround(PointViewPtr view) float ht = 0.0f; // pre-compute window sizes and height thresholds - while (ws < m_maxWindowSize) + while (ws < m_args->m_maxWindowSize) { // Determine the initial window size. - if (m_exponential) - ws = m_cellSize * (2.0f * std::pow(2, iter) + 1.0f); + if (m_args->m_exponential) + ws = m_args->m_cellSize * (2.0f * std::pow(2, iter) + 1.0f); else - ws = m_cellSize * (2.0f * (iter + 1) * 2 + 1.0f); + ws = m_args->m_cellSize * (2.0f * (iter + 1) * 2 + 1.0f); // Calculate the height threshold to be used in the next iteration. if (iter == 0) - ht = m_initialDistance; + ht = m_args->m_initialDistance; else - ht = m_slope * (ws - wsvec[iter - 1]) * m_cellSize + - m_initialDistance; + ht = m_args->m_slope * (ws - wsvec[iter - 1]) * + m_args->m_cellSize + m_args->m_initialDistance; // Enforce max distance on height threshold - if (ht > m_maxDistance) - ht = m_maxDistance; + if (ht > m_args->m_maxDistance) + ht = m_args->m_maxDistance; wsvec.push_back(ws); htvec.push_back(ht); @@ -269,8 +293,10 @@ void PMFFilter::processGround(PointViewPtr view) double y = view->getFieldAs(Dimension::Id::Y, p_idx); double z = view->getFieldAs(Dimension::Id::Z, p_idx); - int c = static_cast(floor((x - bounds.minx) / m_cellSize)); - int r = static_cast(floor((y - bounds.miny) / m_cellSize)); + int c = static_cast(floor((x - bounds.minx) / + m_args->m_cellSize)); + int r = static_cast(floor((y - bounds.miny) / + m_args->m_cellSize)); if ((z - mo[c * rows + r]) < htvec[j]) groundNewIdx.push_back(p_idx); diff --git a/filters/PMFFilter.hpp b/filters/PMFFilter.hpp index 24da5aef35..901521b9f5 100644 --- a/filters/PMFFilter.hpp +++ b/filters/PMFFilter.hpp @@ -36,29 +36,23 @@ #include -#include "private/DimRange.hpp" +#include namespace pdal { +struct PMFArgs; + class PDAL_DLL PMFFilter : public Filter { public: - PMFFilter() : Filter() - { - } + PMFFilter(); + ~PMFFilter(); std::string getName() const; private: - double m_cellSize; - bool m_exponential; - DimRange m_ignored; - double m_initialDistance; - bool m_lastOnly; - double m_maxDistance; - double m_maxWindowSize; - double m_slope; + std::unique_ptr m_args; virtual void addDimensions(PointLayoutPtr layout); virtual void addArgs(ProgramArgs& args); diff --git a/filters/SMRFilter.cpp b/filters/SMRFilter.cpp index 8c68a66f19..adbaefbab5 100644 --- a/filters/SMRFilter.cpp +++ b/filters/SMRFilter.cpp @@ -73,6 +73,27 @@ static StaticPluginInfo const s_info CREATE_STATIC_STAGE(SMRFilter, s_info) +struct SMRArgs +{ + double m_cell; + double m_slope; + double m_window; + double m_scalar; + double m_threshold; + double m_cut; + std::string m_dir; + DimRange m_ignored; + bool m_lastOnly; +}; + +SMRFilter::SMRFilter() : m_args(new SMRArgs) +{} + + +SMRFilter::~SMRFilter() +{} + + std::string SMRFilter::getName() const { return s_info.name; @@ -80,15 +101,15 @@ std::string SMRFilter::getName() const void SMRFilter::addArgs(ProgramArgs& args) { - args.add("cell", "Cell size?", m_cell, 1.0); - args.add("slope", "Percent slope?", m_slope, 0.15); - args.add("window", "Max window size?", m_window, 18.0); - args.add("scalar", "Elevation scalar?", m_scalar, 1.25); - args.add("threshold", "Elevation threshold?", m_threshold, 0.5); - args.add("cut", "Cut net size?", m_cut, 0.0); - args.add("dir", "Optional output directory for debugging", m_dir); - args.add("ignore", "Ignore values", m_ignored); - args.add("last", "Consider last returns only?", m_lastOnly, true); + args.add("cell", "Cell size?", m_args->m_cell, 1.0); + args.add("slope", "Percent slope?", m_args->m_slope, 0.15); + args.add("window", "Max window size?", m_args->m_window, 18.0); + args.add("scalar", "Elevation scalar?", m_args->m_scalar, 1.25); + args.add("threshold", "Elevation threshold?", m_args->m_threshold, 0.5); + args.add("cut", "Cut net size?", m_args->m_cut, 0.0); + args.add("dir", "Optional output directory for debugging", m_args->m_dir); + args.add("ignore", "Ignore values", m_args->m_ignored); + args.add("last", "Consider last returns only?", m_args->m_lastOnly, true); } void SMRFilter::addDimensions(PointLayoutPtr layout) @@ -100,9 +121,9 @@ void SMRFilter::prepared(PointTableRef table) { const PointLayoutPtr layout(table.layout()); - m_ignored.m_id = layout->findDim(m_ignored.m_name); + m_args->m_ignored.m_id = layout->findDim(m_args->m_ignored.m_name); - if (m_lastOnly) + if (m_args->m_lastOnly) { if (!layout->hasDim(Dimension::Id::ReturnNumber) || !layout->hasDim(Dimension::Id::NumberOfReturns)) @@ -111,18 +132,18 @@ void SMRFilter::prepared(PointTableRef table) "NumberOfReturns. Skipping " "segmentation of last returns and " "proceeding with all returns.\n"; - m_lastOnly = false; + m_args->m_lastOnly = false; } } } void SMRFilter::ready(PointTableRef table) { - if (m_dir.empty()) + if (m_args->m_dir.empty()) return; - if (!FileUtils::directoryExists(m_dir)) - throwError("Output directory '" + m_dir + "' does not exist"); + if (!FileUtils::directoryExists(m_args->m_dir)) + throwError("Output directory '" + m_args->m_dir + "' does not exist"); } PointViewSet SMRFilter::run(PointViewPtr view) @@ -134,15 +155,16 @@ PointViewSet SMRFilter::run(PointViewPtr view) // Segment input view into ignored/kept views. PointViewPtr ignoredView = view->makeNew(); PointViewPtr keptView = view->makeNew(); - if (m_ignored.m_id == Dimension::Id::Unknown) + if (m_args->m_ignored.m_id == Dimension::Id::Unknown) keptView->append(*view); else - Segmentation::ignoreDimRange(m_ignored, view, keptView, ignoredView); + Segmentation::ignoreDimRange(m_args->m_ignored, view, keptView, + ignoredView); // Segment kept view into last/other-than-last return views. PointViewPtr lastView = keptView->makeNew(); PointViewPtr nonlastView = keptView->makeNew(); - if (m_lastOnly) + if (m_args->m_lastOnly) Segmentation::segmentLastReturns(keptView, lastView, nonlastView); else lastView->append(*keptView); @@ -153,8 +175,8 @@ PointViewSet SMRFilter::run(PointViewPtr view) m_srs = lastView->spatialReference(); lastView->calculateBounds(m_bounds); - m_cols = ((m_bounds.maxx - m_bounds.minx) / m_cell) + 1; - m_rows = ((m_bounds.maxy - m_bounds.miny) / m_cell) + 1; + m_cols = ((m_bounds.maxx - m_bounds.minx) / m_args->m_cell) + 1; + m_rows = ((m_bounds.maxy - m_bounds.miny) / m_args->m_cell) + 1; // Create raster of minimum Z values per element. std::vector ZImin = createZImin(lastView); @@ -207,7 +229,7 @@ void SMRFilter::classifyGround(PointViewPtr view, std::vector& ZIpro) MatrixXd thresh(m_rows, m_cols); { MatrixXd ZIproM = Map(ZIpro.data(), m_rows, m_cols); - MatrixXd scaled = ZIproM / m_cell; + MatrixXd scaled = ZIproM / m_args->m_cell; MatrixXd gx = gradX(scaled); MatrixXd gy = gradY(scaled); @@ -216,26 +238,31 @@ void SMRFilter::classifyGround(PointViewPtr view, std::vector& ZIpro) gsurfs.data() + gsurfs.size()); std::vector gsurfs_fillV = knnfill(view, gsurfsV); gsurfs = Map(gsurfs_fillV.data(), m_rows, m_cols); - thresh = (m_threshold + m_scalar * gsurfs.array()).matrix(); + thresh = (m_args->m_threshold + m_args->m_scalar * + gsurfs.array()).matrix(); - if (!m_dir.empty()) + if (!m_args->m_dir.empty()) { - std::string fname = FileUtils::toAbsolutePath("gx.tif", m_dir); - writeMatrix(gx, fname, "GTiff", m_cell, m_bounds, m_srs); + std::string fname = FileUtils::toAbsolutePath("gx.tif", + m_args->m_dir); + writeMatrix(gx, fname, "GTiff", m_args->m_cell, m_bounds, m_srs); - fname = FileUtils::toAbsolutePath("gy.tif", m_dir); - writeMatrix(gy, fname, "GTiff", m_cell, m_bounds, m_srs); + fname = FileUtils::toAbsolutePath("gy.tif", m_args->m_dir); + writeMatrix(gy, fname, "GTiff", m_args->m_cell, m_bounds, m_srs); - fname = FileUtils::toAbsolutePath("gsurfs.tif", m_dir); - writeMatrix(gsurfs, fname, "GTiff", m_cell, m_bounds, m_srs); + fname = FileUtils::toAbsolutePath("gsurfs.tif", m_args->m_dir); + writeMatrix(gsurfs, fname, "GTiff", m_args->m_cell, + m_bounds, m_srs); - fname = FileUtils::toAbsolutePath("gsurfs_fill.tif", m_dir); + fname = FileUtils::toAbsolutePath("gsurfs_fill.tif", m_args->m_dir); MatrixXd gsurfs_fill = Map(gsurfs_fillV.data(), m_rows, m_cols); - writeMatrix(gsurfs_fill, fname, "GTiff", m_cell, m_bounds, m_srs); + writeMatrix(gsurfs_fill, fname, "GTiff", m_args->m_cell, + m_bounds, m_srs); - fname = FileUtils::toAbsolutePath("thresh.tif", m_dir); - writeMatrix(thresh, fname, "GTiff", m_cell, m_bounds, m_srs); + fname = FileUtils::toAbsolutePath("thresh.tif", m_args->m_dir); + writeMatrix(thresh, fname, "GTiff", m_args->m_cell, + m_bounds, m_srs); } } @@ -245,8 +272,10 @@ void SMRFilter::classifyGround(PointViewPtr view, std::vector& ZIpro) double y = view->getFieldAs(Id::Y, i); double z = view->getFieldAs(Id::Z, i); - size_t c = static_cast(std::floor(x - m_bounds.minx) / m_cell); - size_t r = static_cast(std::floor(y - m_bounds.miny) / m_cell); + size_t c = static_cast(std::floor(x - m_bounds.minx) / + m_args->m_cell); + size_t r = static_cast(std::floor(y - m_bounds.miny) / + m_args->m_cell); // TODO(chambbj): We don't quite do this by the book and yet it seems to // work reasonably well: @@ -287,12 +316,13 @@ std::vector SMRFilter::createLowMask(std::vector const& ZImin) [](double v) { return -v; }); std::vector LowV = progressiveFilter(negZImin, 5.0, 1.0); - if (!m_dir.empty()) + if (!m_args->m_dir.empty()) { - std::string fname = FileUtils::toAbsolutePath("zilow.tif", m_dir); + std::string fname = FileUtils::toAbsolutePath("zilow.tif", + m_args->m_dir); MatrixXi Low = Map(LowV.data(), m_rows, m_cols); - writeMatrix(Low.cast(), fname, "GTiff", m_cell, m_bounds, - m_srs); + writeMatrix(Low.cast(), fname, "GTiff", m_args->m_cell, + m_bounds, m_srs); } return LowV; @@ -308,9 +338,9 @@ std::vector SMRFilter::createNetMask() // values are found by applying a morphological open operation with a disk // shaped structuring element of radius (2*wkmax)." std::vector isNetCell(m_rows * m_cols, 0); - if (m_cut > 0.0) + if (m_args->m_cut > 0.0) { - int v = std::ceil(m_cut / m_cell); + int v = std::ceil(m_args->m_cut / m_args->m_cell); for (auto c = 0; c < m_cols; c += v) { @@ -336,14 +366,16 @@ std::vector SMRFilter::createObjMask(std::vector const& ZImin) // "The second stage of the ground identification algorithm involves the // application of a progressive morphological filter to the minimum surface // grid (ZImin)." - std::vector ObjV = progressiveFilter(ZImin, m_slope, m_window); + std::vector ObjV = progressiveFilter(ZImin, m_args->m_slope, + m_args->m_window); - if (!m_dir.empty()) + if (!m_args->m_dir.empty()) { - std::string fname = FileUtils::toAbsolutePath("ziobj.tif", m_dir); + std::string fname = FileUtils::toAbsolutePath("ziobj.tif", + m_args->m_dir); MatrixXi Obj = Map(ObjV.data(), m_rows, m_cols); - writeMatrix(Obj.cast(), fname, "GTiff", m_cell, m_bounds, - m_srs); + writeMatrix(Obj.cast(), fname, "GTiff", + m_args->m_cell, m_bounds, m_srs); } return ObjV; @@ -365,8 +397,8 @@ std::vector SMRFilter::createZImin(PointViewPtr view) double y = view->getFieldAs(Id::Y, i); double z = view->getFieldAs(Id::Z, i); - int c = static_cast(floor(x - m_bounds.minx) / m_cell); - int r = static_cast(floor(y - m_bounds.miny) / m_cell); + int c = static_cast(floor(x - m_bounds.minx) / m_args->m_cell); + int r = static_cast(floor(y - m_bounds.miny) / m_args->m_cell); if (z < ZIminV[c * m_rows + r] || std::isnan(ZIminV[c * m_rows + r])) ZIminV[c * m_rows + r] = z; @@ -378,15 +410,16 @@ std::vector SMRFilter::createZImin(PointViewPtr view) // matrix) with values calculated from other nearby values." std::vector ZImin_fillV = knnfill(view, ZIminV); - if (!m_dir.empty()) + if (!m_args->m_dir.empty()) { - std::string fname = FileUtils::toAbsolutePath("zimin.tif", m_dir); + std::string fname = FileUtils::toAbsolutePath("zimin.tif", + m_args->m_dir); MatrixXd ZImin = Map(ZIminV.data(), m_rows, m_cols); - writeMatrix(ZImin, fname, "GTiff", m_cell, m_bounds, m_srs); + writeMatrix(ZImin, fname, "GTiff", m_args->m_cell, m_bounds, m_srs); - fname = FileUtils::toAbsolutePath("zimin_fill.tif", m_dir); + fname = FileUtils::toAbsolutePath("zimin_fill.tif", m_args->m_dir); MatrixXd ZImin_fill = Map(ZImin_fillV.data(), m_rows, m_cols); - writeMatrix(ZImin_fill, fname, "GTiff", m_cell, m_bounds, m_srs); + writeMatrix(ZImin_fill, fname, "GTiff", m_args->m_cell, m_bounds, m_srs); } return ZImin_fillV; @@ -403,9 +436,9 @@ std::vector SMRFilter::createZInet(std::vector const& ZImin, // values are found by applying a morphological open operation with a disk // shaped structuring element of radius (2*wkmax)." std::vector ZInetV = ZImin; - if (m_cut > 0.0) + if (m_args->m_cut > 0.0) { - int v = std::ceil(m_cut / m_cell); + int v = std::ceil(m_args->m_cut / m_args->m_cell); std::vector bigErode = erodeDiamond(ZImin, m_rows, m_cols, 2 * v); std::vector bigOpen = @@ -422,11 +455,12 @@ std::vector SMRFilter::createZInet(std::vector const& ZImin, } } - if (!m_dir.empty()) + if (!m_args->m_dir.empty()) { - std::string fname = FileUtils::toAbsolutePath("zinet.tif", m_dir); + std::string fname = FileUtils::toAbsolutePath("zinet.tif", + m_args->m_dir); MatrixXd ZInet = Map(ZInetV.data(), m_rows, m_cols); - writeMatrix(ZInet, fname, "GTiff", m_cell, m_bounds, m_srs); + writeMatrix(ZInet, fname, "GTiff", m_args->m_cell, m_bounds, m_srs); } return ZInetV; @@ -453,15 +487,17 @@ std::vector SMRFilter::createZIpro(PointViewPtr view, // previously, producing a provisional DEM (ZIpro)." std::vector ZIpro_fillV = knnfill(view, ZIproV); - if (!m_dir.empty()) + if (!m_args->m_dir.empty()) { - std::string fname = FileUtils::toAbsolutePath("zipro.tif", m_dir); + std::string fname = FileUtils::toAbsolutePath("zipro.tif", + m_args->m_dir); MatrixXd ZIpro = Map(ZIproV.data(), m_rows, m_cols); - writeMatrix(ZIpro, fname, "GTiff", m_cell, m_bounds, m_srs); + writeMatrix(ZIpro, fname, "GTiff", m_args->m_cell, m_bounds, m_srs); - fname = FileUtils::toAbsolutePath("zipro_fill.tif", m_dir); + fname = FileUtils::toAbsolutePath("zipro_fill.tif", m_args->m_dir); MatrixXd ZIpro_fill = Map(ZIpro_fillV.data(), m_rows, m_cols); - writeMatrix(ZIpro_fill, fname, "GTiff", m_cell, m_bounds, m_srs); + writeMatrix(ZIpro_fill, fname, "GTiff", m_args->m_cell, + m_bounds, m_srs); } return ZIpro_fillV; @@ -482,8 +518,10 @@ std::vector SMRFilter::knnfill(PointViewPtr view, if (std::isnan(cz[c * m_rows + r])) continue; - temp->setField(Id::X, i, m_bounds.minx + (c + 0.5) * m_cell); - temp->setField(Id::Y, i, m_bounds.miny + (r + 0.5) * m_cell); + temp->setField(Id::X, i, m_bounds.minx + (c + 0.5) * + m_args->m_cell); + temp->setField(Id::Y, i, m_bounds.miny + (r + 0.5) * + m_args->m_cell); temp->setField(Id::Z, i, cz[c * m_rows + r]); i++; } @@ -503,8 +541,8 @@ std::vector SMRFilter::knnfill(PointViewPtr view, if (!std::isnan(out[c * m_rows + r])) continue; - double x = m_bounds.minx + (c + 0.5) * m_cell; - double y = m_bounds.miny + (r + 0.5) * m_cell; + double x = m_bounds.minx + (c + 0.5) * m_args->m_cell; + double y = m_bounds.miny + (r + 0.5) * m_args->m_cell; int k = 8; std::vector neighbors(k); std::vector sqr_dists(k); @@ -536,7 +574,7 @@ std::vector SMRFilter::progressiveFilter(std::vector const& ZImin, // but is internally converted to a pixel equivalent by dividing it by the // cell size and rounding the result toward positive infinity (i.e., taking // the ceiling value)." - int max_radius = std::ceil(max_window / m_cell); + int max_radius = std::ceil(max_window / m_args->m_cell); std::vector prevSurface = ZImin; std::vector prevErosion = ZImin; @@ -557,7 +595,7 @@ std::vector SMRFilter::progressiveFilter(std::vector const& ZImin, // "An elevation threshold is then calculated, where the value is equal // to the supplied slope tolerance parameter multiplied by the product // of the window radius and the cell size." - double threshold = slope * m_cell * radius; + double threshold = slope * m_args->m_cell * radius; // "This elevation threshold is applied to the difference of the minimum // and the opened surfaces." diff --git a/filters/SMRFilter.hpp b/filters/SMRFilter.hpp index d30baede68..baf2383326 100644 --- a/filters/SMRFilter.hpp +++ b/filters/SMRFilter.hpp @@ -36,36 +36,28 @@ #include -#include "private/DimRange.hpp" - +#include #include namespace pdal { +struct SMRArgs; + class PDAL_DLL SMRFilter : public Filter { public: - SMRFilter() : Filter() - { - } + SMRFilter(); + ~SMRFilter(); std::string getName() const; private: int m_rows; int m_cols; - double m_cell; - double m_cut; - double m_slope; - double m_window; - double m_scalar; - double m_threshold; - std::string m_dir; - DimRange m_ignored; - bool m_lastOnly; BOX2D m_bounds; SpatialReference m_srs; + std::unique_ptr m_args; virtual void addArgs(ProgramArgs& args); virtual void addDimensions(PointLayoutPtr layout); From ac13a007993e2615d0d8638f9ac7924f75be9bac Mon Sep 17 00:00:00 2001 From: Andrew Bell Date: Mon, 7 May 2018 16:02:38 -0400 Subject: [PATCH 2/3] Allow replacement of artifacts (#2004) * Add replace(), erase() and exists() functionality for ArtifactManager. * Remove const for consistency. Add replaceOrPut(). * Test for replaceOrPut. --- pdal/ArtifactManager.hpp | 31 +++++++++++++++++++++++++++ test/unit/ArtifactTest.cpp | 44 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/pdal/ArtifactManager.hpp b/pdal/ArtifactManager.hpp index 39e2993eb0..aedd5c5ed2 100644 --- a/pdal/ArtifactManager.hpp +++ b/pdal/ArtifactManager.hpp @@ -51,6 +51,37 @@ class ArtifactManager return m_storage.insert(std::make_pair(name, artifact)).second; } + template + bool replace(const std::string& name, std::shared_ptr art) + { + auto it = m_storage.find(name); + if (it == m_storage.end()) + return false; + + if (!std::dynamic_pointer_cast(it->second)) + return false; + it->second = art; + return true; + } + + template + bool replaceOrPut(const std::string& name, std::shared_ptr art) + { + if (!replace(name, art)) + return put(name, art); + return true; + } + + bool erase(const std::string& name) + { + return m_storage.erase(name); + } + + bool exists(const std::string& name) + { + return (m_storage.find(name) != m_storage.end()); + } + template std::shared_ptr get(const std::string& name) { diff --git a/test/unit/ArtifactTest.cpp b/test/unit/ArtifactTest.cpp index 1cef298980..5252eb93b8 100644 --- a/test/unit/ArtifactTest.cpp +++ b/test/unit/ArtifactTest.cpp @@ -77,4 +77,48 @@ TEST(ArtifactTest, simple) EXPECT_EQ(t.artifactManager().get("MyTest"), nullptr); } +TEST(ArtifactTest, replace) +{ + using TAPtr = std::shared_ptr; + using TAPtr2 = std::shared_ptr; + TAPtr ta(new TestArtifact("MyTest")); + TAPtr taa(new TestArtifact("MyTestA")); + TAPtr2 ta2(new TestArtifact2); + + PointTable t; + EXPECT_FALSE(t.artifactManager().exists("MyTest")); + EXPECT_FALSE(t.artifactManager().replace("MyTest", ta)); + t.artifactManager().put("MyTest", ta); + EXPECT_FALSE(t.artifactManager().replace("MyTest", ta2)); + EXPECT_TRUE(t.artifactManager().replace("MyTest", taa)); + EXPECT_TRUE(t.artifactManager().exists("MyTest")); + EXPECT_EQ(t.artifactManager().get("MyTest")->m_val, + "MyTestA"); + EXPECT_FALSE(t.artifactManager().erase("MyOtherTest")); + EXPECT_TRUE(t.artifactManager().erase("MyTest")); + EXPECT_FALSE(t.artifactManager().exists("MyTest")); +} + +TEST(ArtifactTest, replaceOrPut) +{ + using TAPtr = std::shared_ptr; + using TAPtr2 = std::shared_ptr; + + TAPtr ta(new TestArtifact("MyTest")); + TAPtr taa(new TestArtifact("MyTestA")); + TAPtr2 ta2(new TestArtifact2); + + PointTable t; + EXPECT_FALSE(t.artifactManager().exists("MyTest")); + EXPECT_TRUE(t.artifactManager().replaceOrPut("MyTest", ta)); + EXPECT_EQ(t.artifactManager().get("MyTest")->m_val, + "MyTest"); + EXPECT_TRUE(t.artifactManager().exists("MyTest")); + EXPECT_TRUE(t.artifactManager().replaceOrPut("MyTest", taa)); + EXPECT_TRUE(t.artifactManager().exists("MyTest")); + EXPECT_EQ(t.artifactManager().get("MyTest")->m_val, + "MyTestA"); + EXPECT_FALSE(t.artifactManager().replaceOrPut("MyTest", ta2)); +} + } // namespace pdal From d9f49f2683a01f421b8d70b10395ccc7e3cb0d8d Mon Sep 17 00:00:00 2001 From: Andrew Bell Date: Mon, 7 May 2018 16:19:50 -0400 Subject: [PATCH 3/3] Fixed missed PUBLIC -> PRIVATE conversion in macros.cmake. --- cmake/macros.cmake | 2 +- plugins/pgpointcloud/CMakeLists.txt | 5 ++++- plugins/sqlite/CMakeLists.txt | 9 +++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/cmake/macros.cmake b/cmake/macros.cmake index 9fab9d34a9..5833980851 100644 --- a/cmake/macros.cmake +++ b/cmake/macros.cmake @@ -132,7 +132,7 @@ macro(PDAL_ADD_PLUGIN _name _type _shortname) ${PROJECT_BINARY_DIR}/include ${PDAL_INCLUDE_DIR}) target_link_libraries(${${_name}} - PUBLIC + PRIVATE ${PDAL_BASE_LIB_NAME} ${PDAL_UTIL_LIB_NAME} ${PDAL_ADD_PLUGIN_LINK_WITH} diff --git a/plugins/pgpointcloud/CMakeLists.txt b/plugins/pgpointcloud/CMakeLists.txt index 2cb1349178..d3ba7344c1 100644 --- a/plugins/pgpointcloud/CMakeLists.txt +++ b/plugins/pgpointcloud/CMakeLists.txt @@ -54,7 +54,10 @@ if (BUILD_PGPOINTCLOUD_TESTS) FILES test/PgpointcloudWriterTest.cpp LINK_WITH - ${reader_libname} ${writer_libname}) + ${reader_libname} + ${writer_libname} + ${POSTGRESQL_LIBRARIES} + ) target_include_directories(pgpointcloudtest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include) endif() diff --git a/plugins/sqlite/CMakeLists.txt b/plugins/sqlite/CMakeLists.txt index 845ed909e8..22461373ae 100644 --- a/plugins/sqlite/CMakeLists.txt +++ b/plugins/sqlite/CMakeLists.txt @@ -36,8 +36,13 @@ target_include_directories(${writer_libname} PRIVATE ${LIBXML2_INCLUDE_DIR}) # if(BUILD_SQLITE_TESTS) PDAL_ADD_TEST(sqlitetest - FILES test/SQLiteTest.cpp - LINK_WITH ${reader_libname} ${writer_libname}) + FILES + test/SQLiteTest.cpp + LINK_WITH + ${reader_libname} + ${writer_libname} + ${SQLITE3_LIBRARY} + ) target_include_directories(sqlitetest PRIVATE ${PDAL_IO_DIR} ${LIBXML2_INCLUDE_DIR})