Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into esri-pm
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew authored and Andrew committed Aug 11, 2020
2 parents 34c406b + 5733c2a commit e28d6d7
Show file tree
Hide file tree
Showing 55 changed files with 18,715 additions and 323 deletions.
4 changes: 3 additions & 1 deletion cmake/gtest.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ add_subdirectory(vendor/gtest)

find_package(absl QUIET)
if (absl_FOUND)
cmake_policy(SET CMP0079 NEW)
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13.0")
cmake_policy(SET CMP0079 NEW)
endif()
target_compile_definitions(gtest PUBLIC GTEST_HAS_ABSL=1)
target_compile_definitions(gtest_main PUBLIC GTEST_HAS_ABSL=1)
target_link_libraries(gtest PRIVATE absl::algorithm
Expand Down
88 changes: 67 additions & 21 deletions doc/stages/filters.assign.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,9 @@ to a provided value that pass a range filter.

.. streamable::

Example 1
---------

This pipeline resets the ``Classification`` of all points with classifications
2 or 3 to 0 and all points with classification of 5 to 4.

.. code-block:: json
[
"autzen-dd.las",
{
"type":"filters.assign",
"assignment" : "Classification[2:3]=0",
"assignment" : "Classification[5:5]=4"
},
{
"filename":"attributed.las",
"scale_x":0.0000001,
"scale_y":0.0000001
}
]
.. note::
The `assignment` and `condition` options are deprecated and may be removed in a
future release.

Options
-------
Expand All @@ -44,5 +26,69 @@ condition
A list of :ref:`ranges <ranges>` that a point's values must pass in order
for the assignment to be performed. [Default: none]

value
A list of :ref:`assignment expressions <Assignment Expressions>` to be applied to points.
The list of values is evaluated in order. [Default: none]

.. include:: filter_opts.rst

.. _assignment expressions:

Assignment Expressions
======================

The assignment expression syntax is an expansion on the :ref:`PDAL expression` syntax
that provides for assignment of values to points. The generic expression is:

.. code-block::
"values" : "Dimension = ValueExpression [WHERE ConditionalExpression)]"
``Dimension`` is the name of a PDAL dimension.

A ``ValueExpression`` consists of constants, dimension names and mathematical operators
that evaluates to a numeric value. The supported mathematical operations are addition(`+`),
subtraction(`-`), multiplication(`*`) and division(`\\`).

A :ref:`ConditionalExpression <PDAL expression>` is an optional boolean value that must
evaluate to `true` for the ``ValueExpression`` to be applied.

Example 1
=========

.. code-block::
"value" : "Red = Red / 256"
This scales the ``Red`` value by 1/256. If the input values are in the range 0 - 65535, the output
value will be in the range 0 - 255.


Example 2
=========

.. code-block::
"value" :
[
"Classification = 2 WHERE HeightAboveGround < 5",
"Classification = 1 WHERE HeightAboveGround >= 5"
]
This sets the classification of points to either ``Ground`` or ``Unassigned`` depending on the
value of the ``HeightAboveGround`` dimension.

Example 3
=========

.. code-block::
"value" :
[
"X = 1",
"X = 2 WHERE X > 10"
]
This sets the value of ``X`` for all points to 1. The second statement is essentially ignored
since the first statement sets the ``X`` value of all points to 1 and therefore no points
the ``ConditionalExpression`` of the second statement.
6 changes: 6 additions & 0 deletions doc/stages/readers.bpf.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,11 @@ Options
filename
BPF file to read [Required]

fix_dims
BPF files may contain dimension names that aren't allowed by PDAL. When this
option is 'true', invalid characters in dimension names are replaced by '_' in
order to make the names valid.
[Default: true]

.. include:: reader_opts.rst

44 changes: 44 additions & 0 deletions doc/stages/readers.obj.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
.. _readers.obj:

readers.obj
===============

The **OBJ reader** reads data from files in the OBJ format.
This reader constructs a mesh from the faces specified in the OBJ file, ignoring
vertices that are not associated with any face. Faces, vertices, vertex normals and vertex
textures are read, while all other obj elements (such as lines and curves) are ignored.

.. plugin::

Example
-------
This pipeline reads from an example OBJ file outputs
the vertices as a point to a LAS file.

.. code-block:: json
[
{
"type": "readers.obj",
"filename": "test/data/obj/1.2-with-color.obj"
},
{
"type" : "writers.las",
"filename": "output.las",
"scale_x": 1.0e-5,
"scale_y": 1.0e-5,
"scale_z": 1.0e-5,
"offset_x": "auto",
"offset_y": "auto",
"offset_z": "auto"
}
]
Options
-------

.. include:: reader_opts.rst

filename
File to read. [Required]
16 changes: 15 additions & 1 deletion filters/AssignFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include <pdal/util/ProgramArgs.hpp>

#include "private/DimRange.hpp"
#include "private/expr/AssignStatement.hpp"

namespace pdal
{
Expand All @@ -61,6 +62,7 @@ struct AssignArgs
{
std::vector<AssignRange> m_assignments;
DimRange m_condition;
std::vector<expr::AssignStatement> m_statements;
};

void AssignRange::parse(const std::string& r)
Expand Down Expand Up @@ -128,9 +130,11 @@ AssignFilter::~AssignFilter()
void AssignFilter::addArgs(ProgramArgs& args)
{
args.add("assignment", "Values to assign to dimensions based on range.",
m_args->m_assignments).setPositional();
m_args->m_assignments);
args.add("condition", "Condition for assignment based on range.",
m_args->m_condition);
args.add("value", "Value to assign to dimension based on expression.",
m_args->m_statements);
}


Expand All @@ -146,6 +150,12 @@ void AssignFilter::prepared(PointTableRef table)
throwError("Invalid dimension name in 'assignment' option: '" +
r.m_name + "'.");
}
for (expr::AssignStatement& expr : m_args->m_statements)
{
auto status = expr.prepare(layout);
if (!status)
throwError(status.what());
}
}


Expand All @@ -160,6 +170,10 @@ bool AssignFilter::processOne(PointRef& point)
for (AssignRange& r : m_args->m_assignments)
if (r.valuePasses(point.getFieldAs<double>(r.m_id)))
point.setField(r.m_id, r.m_value);
for (expr::AssignStatement& expr : m_args->m_statements)
if (expr.conditionalExpr().eval(point))
point.setField(expr.identExpr().eval(), expr.valueExpr().eval(point));

return true;
}

Expand Down
30 changes: 18 additions & 12 deletions filters/LOFFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,22 @@ void LOFFilter::filter(PointView& view)
// First pass: Compute the k-distance for each point.
// The k-distance is the Euclidean distance to k-th nearest neighbor.
log()->get(LogLevel::Debug) << "Computing k-distances...\n";

typedef std::pair<PointId, size_t> PointIdKPair;
typedef std::pair<PointId, double> PointIdDistPair;
typedef std::map<PointIdKPair, PointIdDistPair> AdjacencyMap;
AdjacencyMap mat;

for (PointId i = 0; i < view.size(); ++i)
{
PointIdList indices(m_minpts);
std::vector<double> sqr_dists(m_minpts);
index.knnSearch(i, m_minpts, &indices, &sqr_dists);
view.setField(m_kdist, i, std::sqrt(sqr_dists[m_minpts-1]));

for (size_t j = 0; j < m_minpts; ++j)
mat[std::make_pair(i, j)] = std::make_pair(indices[j], std::sqrt(sqr_dists[j]));

view.setField(m_kdist, i, std::sqrt(sqr_dists[m_minpts - 1]));
}

// Second pass: Compute the local reachability distance for each point.
Expand All @@ -98,15 +108,13 @@ void LOFFilter::filter(PointView& view)
log()->get(LogLevel::Debug) << "Computing lrd...\n";
for (PointId i = 0; i < view.size(); ++i)
{
PointIdList indices(m_minpts);
std::vector<double> sqr_dists(m_minpts);
index.knnSearch(i, m_minpts, &indices, &sqr_dists);
double M1 = 0.0;
point_count_t n = 0;
for (PointId j = 0; j < indices.size(); ++j)
for (size_t j = 0; j < m_minpts; ++j)
{
double k = view.getFieldAs<double>(m_kdist, indices[j]);
double reachdist = (std::max)(k, std::sqrt(sqr_dists[j]));
auto val = mat[std::make_pair(i, j)];
double k = view.getFieldAs<double>(m_kdist, val.first);
double reachdist = (std::max)(k, val.second);
M1 += (reachdist - M1) / ++n;
}
view.setField(m_lrd, i, 1.0 / M1);
Expand All @@ -118,14 +126,12 @@ void LOFFilter::filter(PointView& view)
for (PointId i = 0; i < view.size(); ++i)
{
double lrdp = view.getFieldAs<double>(m_lrd, i);
PointIdList indices(m_minpts);
std::vector<double> sqr_dists(m_minpts);
index.knnSearch(i, m_minpts, &indices, &sqr_dists);
double M1 = 0.0;
point_count_t n = 0;
for (auto const& j : indices)
for (size_t j = 0; j < m_minpts; ++j)
{
M1 += (view.getFieldAs<double>(m_lrd, j) / lrdp - M1) / ++n;
auto val = mat[std::make_pair(i, j)];
M1 += (view.getFieldAs<double>(m_lrd, val.first) / lrdp - M1) / ++n;
}
view.setField(m_lof, i, M1);
}
Expand Down
63 changes: 63 additions & 0 deletions filters/private/expr/AssignParser.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#include "AssignParser.hpp"
#include "AssignStatement.hpp"
#include "ConditionalParser.hpp"
#include "MathParser.hpp"

namespace pdal
{
namespace expr
{

bool AssignParser::statement(AssignStatement& expr)
{
return assignment(expr);
}

bool AssignParser::assignment(AssignStatement& expr)
{
if (!match(TokenType::Identifier))
{
setError("Expected dimension name for assignment.");
return false;
}
expr.identExpr().pushNode(NodePtr(new VarNode(curToken().sval())));

if (!match(TokenType::Assign))
{
setError("Expected '=' after dimension name in assignment.");
return false;
}

MathParser parser(lexer());
if (!parser.expression(expr.valueExpr()))
{
setError(parser.error());
return false;
}

return where(expr);
}

bool AssignParser::where(AssignStatement& expr)
{
if (match(TokenType::Eof))
return true;

if (match(TokenType::Identifier))
{
std::string ident = Utils::toupper(curToken().sval());
if (ident != "WHERE")
{
setError("Expected keyword 'WHERE' to precede condition assignment.");
return false;
}
}
ConditionalParser parser(lexer());
bool status = parser.expression(expr.conditionalExpr());
if (!status)
setError(parser.error());
return status;
}

} // namespace expr
} // namespace pdal
26 changes: 26 additions & 0 deletions filters/private/expr/AssignParser.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

#include "BaseParser.hpp"

namespace pdal
{
namespace expr
{

class AssignStatement;

class AssignParser : public BaseParser
{
public:
AssignParser(Lexer& lexer) : BaseParser(lexer)
{}

bool statement(AssignStatement& expr);

protected:
bool assignment(AssignStatement& expr);
bool where(AssignStatement& expr);
};

} // namespace expr
} // namespace pdal

0 comments on commit e28d6d7

Please sign in to comment.