From 608d3801e9468fbd67bbc0974a07a5e40bcb478c Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Wed, 14 Feb 2024 18:00:09 +0100 Subject: [PATCH 01/49] add search radius plugin fix environment test ci fix compil windows radius_search : remove dimRange and use special attributes fix cmakelist --- .gitignore | 3 +- CMakeLists.txt | 2 + ci/build.sh | 1 + doc/grid_radius_search.md | 45 +++++++ environment.yml | 3 + src/filter_grid_decimation/CMakeLists.txt | 6 +- .../grid_decimationFilter.cpp | 2 +- src/filter_radius_search/CMakeLists.txt | 16 +++ .../radius_searchFilter.cpp | 126 ++++++++++++++++++ .../radius_searchFilter.hpp | 53 ++++++++ test/test_grid_decimation.py | 5 +- test/test_radius_search.py | 117 ++++++++++++++++ 12 files changed, 370 insertions(+), 9 deletions(-) create mode 100755 doc/grid_radius_search.md create mode 100755 src/filter_radius_search/CMakeLists.txt create mode 100644 src/filter_radius_search/radius_searchFilter.cpp create mode 100644 src/filter_radius_search/radius_searchFilter.hpp create mode 100755 test/test_radius_search.py diff --git a/.gitignore b/.gitignore index 0518d62..d8c4899 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ xcode install __pycache__ -test/__pycache__ +test/__pycache_ +test/.idea diff --git a/CMakeLists.txt b/CMakeLists.txt index 47fe925..bd23b83 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required( VERSION 3.5 ) project(MY_READER LANGUAGES CXX) set(CMAKE_PREFIX_PATH ${CONDA_PREFIX}) +set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "-o linker-signed") find_package(PDAL REQUIRED) @@ -11,4 +12,5 @@ set(CMAKE_DEBUG_POSTFIX d) ## add plugin add_subdirectory(src/filter_grid_decimation) +add_subdirectory(src/filter_radius_search) diff --git a/ci/build.sh b/ci/build.sh index d745403..b226faf 100755 --- a/ci/build.sh +++ b/ci/build.sh @@ -24,6 +24,7 @@ fi conda activate pdal_ign_plugin export CONDA_PREFIX=$CONDA_PREFIX +echo conda is $CONDA_PREFIX mkdir build cd build diff --git a/doc/grid_radius_search.md b/doc/grid_radius_search.md new file mode 100755 index 0000000..09b385b --- /dev/null +++ b/doc/grid_radius_search.md @@ -0,0 +1,45 @@ +# filter grid decimation + +Purpose +--------------------------------------------------------------------------------------------------------- + +The **radius search filter** add a new attribut where the value depends on their neighbors in a given radius: For each point in the domain src_domain_, if it has any neighbor with a distance lower than radius_ that belongs to the domain reference_domain_, it is updated. + + +Example +--------------------------------------------------------------------------------------------------------- + +This pipeline updates the Keypoint dimension of all points with classification 1 to 2 (unclassified and ground) that are closer than 1 meter from a point with classification 6 (building) + + +``` + [ + "file-input.las", + { + "type" : "filters.radiussearch", + "src_domain" : "Classification[1:2]", + "reference_domain" : "Classification[6:6]", + "radius" : 1, + "output_name_attribut": "radius" + }, + "output.las" + ] +``` + +Options +--------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +**src_domain** : + A :ref:`range ` which selects points to be processed by the filter. Can be specified multiple times. Points satisfying any range will be processed + +**reference_domain** : + A :ref:`range ` which selects points that can are considered as potential neighbors. Can be specified multiple times. + +**radius** : + An positive float which specifies the radius for the neighbors search. + +**update_expression** : + A list of :ref:`assignment expressions ` to be applied to the points that satisfy the radius search. The list of values is evaluated in order. + +**output_name_attribut**: The name of the new attribut. [Default: radius] + diff --git a/environment.yml b/environment.yml index fdc1eaf..781039d 100755 --- a/environment.yml +++ b/environment.yml @@ -1,6 +1,7 @@ name: pdal_ign_plugin channels: - conda-forge + - anaconda dependencies: - pdal - python-pdal @@ -8,6 +9,8 @@ dependencies: - black - isort - shapely + - gdal + - cmake - pip: - ign-pdal-tools diff --git a/src/filter_grid_decimation/CMakeLists.txt b/src/filter_grid_decimation/CMakeLists.txt index 2e358ca..40ffda7 100755 --- a/src/filter_grid_decimation/CMakeLists.txt +++ b/src/filter_grid_decimation/CMakeLists.txt @@ -1,7 +1,7 @@ -file( GLOB_RECURSE GD_SRCS ${CMAKE_SOURCE_DIR} *) - -message("GD_SRCS ${GD_SRCS}") +file( GLOB_RECURSE GD_SRCS + ${CMAKE_SOURCE_DIR}/src/filter_grid_decimation/*.hpp + ${CMAKE_SOURCE_DIR}/src/filter_grid_decimation/*.cpp) PDAL_CREATE_PLUGIN( TYPE filter diff --git a/src/filter_grid_decimation/grid_decimationFilter.cpp b/src/filter_grid_decimation/grid_decimationFilter.cpp index 60f03a9..69a9e28 100755 --- a/src/filter_grid_decimation/grid_decimationFilter.cpp +++ b/src/filter_grid_decimation/grid_decimationFilter.cpp @@ -39,7 +39,7 @@ void GridDecimationFilter::addArgs(ProgramArgs& args) { args.add("resolution", "Cell edge size, in units of X/Y",m_args->m_edgeLength, 1.); args.add("output_type", "Point keept into the cells ('min', 'max')", m_args->m_methodKeep, "max" ); - args.add("output_name_attribut", "Name of the add attribut", m_args->m_nameAddAttribut, "grid" ); + args.add("output_name_attribut", "Name of the added attribut", m_args->m_nameAddAttribut, "grid" ); args.add("output_wkt", "Export the grid as wkt", m_args->m_nameWktgrid, "" ); } diff --git a/src/filter_radius_search/CMakeLists.txt b/src/filter_radius_search/CMakeLists.txt new file mode 100755 index 0000000..b7ba4e6 --- /dev/null +++ b/src/filter_radius_search/CMakeLists.txt @@ -0,0 +1,16 @@ + +file( GLOB_RECURSE GD_SRCS + ${CMAKE_SOURCE_DIR}/src/filter_radius_search/*.hpp + ${CMAKE_SOURCE_DIR}/src/filter_radius_search/*.cpp) + +PDAL_CREATE_PLUGIN( + TYPE filter + NAME radius_search + VERSION 1.0 + SOURCES ${GD_SRCS} +) + +install(TARGETS + pdal_plugin_filter_radius_search +) + diff --git a/src/filter_radius_search/radius_searchFilter.cpp b/src/filter_radius_search/radius_searchFilter.cpp new file mode 100644 index 0000000..82fb455 --- /dev/null +++ b/src/filter_radius_search/radius_searchFilter.cpp @@ -0,0 +1,126 @@ +#include "radius_searchFilter.hpp" + +#include +#include +#include + +#include + +#include +#include + +namespace pdal +{ + +static PluginInfo const s_info = PluginInfo( + "filters.radius_search", + "Re-assign some point attributes based KNN voting", + "" ); + +CREATE_SHARED_STAGE(RadiusSearchFilter, s_info) + +std::string RadiusSearchFilter::getName() const { return s_info.name; } + +RadiusSearchFilter::RadiusSearchFilter() : +m_args(new RadiusSearchFilter::RadiusSearchArgs) +{} + + +RadiusSearchFilter::~RadiusSearchFilter() +{} + + +void RadiusSearchFilter::addArgs(ProgramArgs& args) +{ + args.add("src_domain", "Selects which points will be subject to radius-based neighbors search", m_args->m_srcDomain, "SRS_DOMAIN"); + args.add("reference_domain", "Selects which points will be considered as potential neighbors", m_args->m_referenceDomain, "REF_DOMAIN"); + args.add("radius", "Distance of neighbors to consult", m_args->m_radius, 1.); + args.add("output_name_attribute", "Name of the added attribut", m_args->m_nameAddAttribute, "radius" ); + args.add("3d_search", "Search in 3d", m_args->search3d, false ); +} + +void RadiusSearchFilter::addDimensions(PointLayoutPtr layout) +{ + m_args->m_dim = layout->registerOrAssignDim(m_args->m_nameAddAttribute, Dimension::Type::Double); + m_args->m_dim_ref = layout->registerOrAssignDim(m_args->m_referenceDomain,Dimension::Type::Unsigned8); + m_args->m_dim_src = layout->registerOrAssignDim(m_args->m_srcDomain,Dimension::Type::Unsigned8); +} + +void RadiusSearchFilter::initialize() +{ + if (m_args->m_referenceDomain.empty()) + throwError("The reference_domain must be given."); + if (m_args->m_radius <= 0) + throwError("Invalid 'radius' option: " + std::to_string(m_args->m_radius) + ", must be > 0"); + if (m_args->m_nameAddAttribute.empty()) + throwError("The output_name_attribut must be given."); +} + +void RadiusSearchFilter::prepared(PointTableRef table) +{ + PointLayoutPtr layout(table.layout()); +} + +void RadiusSearchFilter::ready(PointTableRef) +{ + m_args->m_ptsToUpdate.clear(); +} + +void RadiusSearchFilter::doOneNoDomain(PointRef &point) +{ + // build3dIndex and build2dIndex are buuild once + PointIdList iNeighbors; + if (m_args->search3d) iNeighbors = refView->build3dIndex().radius(point, m_args->m_radius); + else iNeighbors = refView->build2dIndex().radius(point, m_args->m_radius); + + if (iNeighbors.size() == 0) + return; + + m_args->m_ptsToUpdate.push_back(point.pointId()); +} + + +// update point. kdi and temp both reference the NN point cloud +bool RadiusSearchFilter::doOne(PointRef& point) +{ + if (m_args->m_srcDomain.empty()) // No domain, process all points + doOneNoDomain(point); + else if (point.getFieldAs(m_args->m_dim_src)>0) + doOneNoDomain(point); + return true; +} + +void RadiusSearchFilter::filter(PointView& view) +{ + PointRef point_src(view, 0); + PointRef temp(view, 0); + + refView = view.makeNew(); + for (PointId id = 0; id < view.size(); ++id) + { + temp.setPointId(id); + temp.setField(m_args->m_dim, int64_t(0)); + + // process only points that satisfy a domain condition + //if (r.valuePasses(temp.getFieldAs(r.m_id))) + if (temp.getFieldAs(m_args->m_dim_ref)>0) + { + refView->appendPoint(view, id); + break; + } + } + + for (PointId id = 0; id < view.size(); ++id) + { + point_src.setPointId(id); + doOne(point_src); + } + for (auto id: m_args->m_ptsToUpdate) + { + temp.setPointId(id); + temp.setField(m_args->m_dim, int64_t(1)); + } +} + +} // namespace pdal + diff --git a/src/filter_radius_search/radius_searchFilter.hpp b/src/filter_radius_search/radius_searchFilter.hpp new file mode 100644 index 0000000..515c155 --- /dev/null +++ b/src/filter_radius_search/radius_searchFilter.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include + +extern "C" int32_t RadiusSearchFilter_ExitFunc(); +extern "C" PF_ExitFunc RadiusSearchFilter_InitPlugin(); + +namespace pdal +{ + +class PDAL_DLL RadiusSearchFilter : public Filter +{ +public: + RadiusSearchFilter(); + ~RadiusSearchFilter(); + + static void * create(); + static int32_t destroy(void *); + std::string getName() const; + +private: + + struct RadiusSearchArgs + { + std::string m_referenceDomain; + std::string m_srcDomain; + double m_radius; + PointIdList m_ptsToUpdate; + std::string m_nameAddAttribute; + Dimension::Id m_dim; + bool search3d; + Dimension::Id m_dim_ref, m_dim_src; + }; + std::unique_ptr m_args; + PointViewPtr refView; + + virtual void addArgs(ProgramArgs& args); + virtual void prepared(PointTableRef table); + virtual void filter(PointView& view); + virtual void initialize(); + virtual void addDimensions(PointLayoutPtr layout); + virtual void ready(PointTableRef); + + bool doOne(PointRef& point); + void doOneNoDomain(PointRef &point); + + RadiusSearchFilter& operator=(const RadiusSearchFilter&) = delete; + RadiusSearchFilter(const RadiusSearchFilter&) = delete; +}; + +} // namespace pdal diff --git a/test/test_grid_decimation.py b/test/test_grid_decimation.py index 9491d5a..ab7f8d9 100755 --- a/test/test_grid_decimation.py +++ b/test/test_grid_decimation.py @@ -1,17 +1,15 @@ import csv import json import math -import os import tempfile from test import utils import pdal import pdaltools.las_info as li import pytest -import shapely - def test_grid_decimation(): + ini_las = "test/data/4_6.las" resolution = 10 @@ -26,7 +24,6 @@ def test_grid_decimation(): d_width = math.floor((bounds[0][1] - bounds[0][0]) / resolution) + 1 d_height = math.floor((bounds[1][1] - bounds[1][0]) / resolution) + 1 nb_dalle = d_width * d_height - print("size of the grid", nb_dalle) PIPELINE = [ {"type": "readers.las", "filename": ini_las}, diff --git a/test/test_radius_search.py b/test/test_radius_search.py new file mode 100755 index 0000000..2e077e2 --- /dev/null +++ b/test/test_radius_search.py @@ -0,0 +1,117 @@ +import json +import tempfile +from test import utils + +from shapely.geometry import Point + +from random import * +import numpy as np +import pdal +import random as rand +from math import * + +import pytest + +def test_radius_search(): + + distance_radius = 1 + + #shapely is 2d library + def distance2d(pt1, pt2): + return pt1.distance(pt2) + def distance3d(pt1, pt2): + return sqrt( (pt1.x-pt2.x)**2 + (pt1.y-pt2.y)**2 + (pt1.z-pt2.z)**2 ) + + pt_x = 1639825.15 + pt_y = 1454924.63 + pt_z = 7072.17 + pt_ini = (pt_x, pt_y, pt_z, 1) + pt1 = Point(pt_x, pt_y, pt_z) + + dtype = [('X', 'SRS_DOMAIN" + }, + { + "type": "filters.ferry", + "dimensions": "=>REF_DOMAIN" + }, + { + "type": "filters.assign", + "value": [ + "SRS_DOMAIN = 1 WHERE Classification==2", + "SRS_DOMAIN = 0 WHERE Classification!=2", + "REF_DOMAIN = 1 WHERE Classification==1", + "REF_DOMAIN = 0 WHERE Classification!=1", + ], + }, + { + "type": filter, + "radius": "1.", + "src_domain": "SRS_DOMAIN", + "reference_domain": "REF_DOMAIN", + "output_name_attribute": "radius_2D", + "3d_search":False + }, + { + "type": filter, + "radius": "1.", + "src_domain": "SRS_DOMAIN", + "reference_domain": "REF_DOMAIN", + "output_name_attribute": "radius_3D", + "3d_search": True + } + ] + + pipeline = pdal.Pipeline(json.dumps(PIPELINE)) + + # execute the pipeline + pipeline.execute() + arrays = pipeline.arrays + array = arrays[0] + + nb_pts_radius_2d = 0 + nb_pts_radius_3d = 0 + for pt in array: + if pt["radius_2D"] > 0: + nb_pts_radius_2d += 1 + if pt["radius_3D"] > 0: + nb_pts_radius_3d += 1 + + assert nb_pts_radius_2d == nb_points_take_2d + assert nb_pts_radius_3d == nb_points_take_3d \ No newline at end of file From 4c22409bd2a99a74a7bc280d04aafc8f677ca824 Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Tue, 26 Mar 2024 15:37:29 +0100 Subject: [PATCH 02/49] Add possibility to restrict the cylinder with 2D serach --- doc/grid_radius_search.md | 6 +++++ .../radius_searchFilter.cpp | 20 +++++++++++++++- .../radius_searchFilter.hpp | 1 + test/test_radius_search.py | 23 +++++++++++++++++-- 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/doc/grid_radius_search.md b/doc/grid_radius_search.md index 09b385b..cd62bd5 100755 --- a/doc/grid_radius_search.md +++ b/doc/grid_radius_search.md @@ -43,3 +43,9 @@ Options **output_name_attribut**: The name of the new attribut. [Default: radius] +**3d_search**: Search in 3d (as a ball). [Default: false] + +**2d_search_above**: If 3d_search is false, take points only if exists points between the referent point and a distance above. [Default: 0.] + +**2d_search_bellow**: If 3d_search is false, take points only if exists points between the referent point and a distance bellow. [Default: 0.] + diff --git a/src/filter_radius_search/radius_searchFilter.cpp b/src/filter_radius_search/radius_searchFilter.cpp index 82fb455..4566181 100644 --- a/src/filter_radius_search/radius_searchFilter.cpp +++ b/src/filter_radius_search/radius_searchFilter.cpp @@ -37,6 +37,8 @@ void RadiusSearchFilter::addArgs(ProgramArgs& args) args.add("radius", "Distance of neighbors to consult", m_args->m_radius, 1.); args.add("output_name_attribute", "Name of the added attribut", m_args->m_nameAddAttribute, "radius" ); args.add("3d_search", "Search in 3d", m_args->search3d, false ); + args.add("2d_search_above", "if search in 2d : filter point above the distance", m_args->m_search_bellow, 0. ); + args.add("2d_search_bellow", "if search in 2d : filter point bellow the distance", m_args->m_search_above, 0. ); } void RadiusSearchFilter::addDimensions(PointLayoutPtr layout) @@ -68,7 +70,7 @@ void RadiusSearchFilter::ready(PointTableRef) void RadiusSearchFilter::doOneNoDomain(PointRef &point) { - // build3dIndex and build2dIndex are buuild once + // build3dIndex and build2dIndex are build once PointIdList iNeighbors; if (m_args->search3d) iNeighbors = refView->build3dIndex().radius(point, m_args->m_radius); else iNeighbors = refView->build2dIndex().radius(point, m_args->m_radius); @@ -76,6 +78,22 @@ void RadiusSearchFilter::doOneNoDomain(PointRef &point) if (iNeighbors.size() == 0) return; + if (!m_args->search3d) + { + double Zref = point.getFieldAs(Dimension::Id::Z); + if (m_args->m_search_bellow>0 || m_args->m_search_above>0) + { + bool take (false); + for (PointId ptId : iNeighbors) + { + double Zpt = refView->point(ptId).getFieldAs(Dimension::Id::Z); + if (m_args->m_search_bellow>0 && Zpt>Zref && (Zpt-Zref)m_search_bellow) {take=true; break;} + if (m_args->m_search_above>0 && Zptm_search_above) {take=true; break;} + } + if (!take) return; + } + } + m_args->m_ptsToUpdate.push_back(point.pointId()); } diff --git a/src/filter_radius_search/radius_searchFilter.hpp b/src/filter_radius_search/radius_searchFilter.hpp index 515c155..3a4346e 100644 --- a/src/filter_radius_search/radius_searchFilter.hpp +++ b/src/filter_radius_search/radius_searchFilter.hpp @@ -32,6 +32,7 @@ class PDAL_DLL RadiusSearchFilter : public Filter Dimension::Id m_dim; bool search3d; Dimension::Id m_dim_ref, m_dim_src; + double m_search_bellow, m_search_above; }; std::unique_ptr m_args; PointViewPtr refView; diff --git a/test/test_radius_search.py b/test/test_radius_search.py index 2e077e2..cb35c0d 100755 --- a/test/test_radius_search.py +++ b/test/test_radius_search.py @@ -35,6 +35,7 @@ def distance3d(pt1, pt2): nb_points = randint(10, 20) nb_points_take_2d = 0 nb_points_take_3d = 0 + nb_points_take_2d_above_bellow = 0 for i in range(nb_points): pti_x = pt_x + rand.uniform(-1.5, 1.5) @@ -51,6 +52,10 @@ def distance3d(pt1, pt2): if distance_i_2d < distance_radius: nb_points_take_2d += 1 + if pti_z>pt_z and pti_z-pt_z>0.5: + nb_points_take_2d_above_bellow += 1 + if pti_z0.5: + nb_points_take_2d_above_bellow += 1 if distance_i_3d < distance_radius: nb_points_take_3d += 1 @@ -86,7 +91,17 @@ def distance3d(pt1, pt2): "src_domain": "SRS_DOMAIN", "reference_domain": "REF_DOMAIN", "output_name_attribute": "radius_2D", - "3d_search":False + "3d_search":False, + }, + { + "type": filter, + "radius": "1.", + "src_domain": "SRS_DOMAIN", + "reference_domain": "REF_DOMAIN", + "output_name_attribute": "radius_2D_above_bellow", + "3d_search": False, + "2d_search_above": 0.5, + "2d_search_bellow": 0.5, }, { "type": filter, @@ -107,11 +122,15 @@ def distance3d(pt1, pt2): nb_pts_radius_2d = 0 nb_pts_radius_3d = 0 + nb_pts_radius_2d_above_bellow = 0 for pt in array: + if pt["radius_2D_above_bellow"] > 0: + nb_pts_radius_2d_above_bellow += 1 if pt["radius_2D"] > 0: nb_pts_radius_2d += 1 if pt["radius_3D"] > 0: nb_pts_radius_3d += 1 assert nb_pts_radius_2d == nb_points_take_2d - assert nb_pts_radius_3d == nb_points_take_3d \ No newline at end of file + assert nb_pts_radius_3d == nb_points_take_3d + assert nb_pts_radius_2d_above_bellow == nb_points_take_2d_above_bellow \ No newline at end of file From 518e9bf0d43df756ab8885fc379b4667ab4d0d9e Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Wed, 27 Mar 2024 12:14:59 +0100 Subject: [PATCH 03/49] fix test --- test/test_radius_search.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/test_radius_search.py b/test/test_radius_search.py index cb35c0d..cca3775 100755 --- a/test/test_radius_search.py +++ b/test/test_radius_search.py @@ -40,7 +40,7 @@ def distance3d(pt1, pt2): for i in range(nb_points): pti_x = pt_x + rand.uniform(-1.5, 1.5) pti_y = pt_y + rand.uniform(-1.5, 1.5) - pti_z = pt_z + rand.uniform(-1.5, 1.5) + pti_z = round(pt_z + rand.uniform(-1.5, 1.5),2) #pdal only takes 2 numbers precision pt_i = (pti_x, pti_y, pti_z, 2) arrays_pti = np.array([pt_i], dtype=dtype) @@ -52,9 +52,7 @@ def distance3d(pt1, pt2): if distance_i_2d < distance_radius: nb_points_take_2d += 1 - if pti_z>pt_z and pti_z-pt_z>0.5: - nb_points_take_2d_above_bellow += 1 - if pti_z0.5: + if abs(pti_z-pt_z)<0.5: nb_points_take_2d_above_bellow += 1 if distance_i_3d < distance_radius: nb_points_take_3d += 1 From 7e771697d675615d35f9b66f62dac5e1383c47e1 Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Wed, 10 Apr 2024 18:07:52 +0200 Subject: [PATCH 04/49] why this break ?!! --- src/filter_radius_search/radius_searchFilter.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/filter_radius_search/radius_searchFilter.cpp b/src/filter_radius_search/radius_searchFilter.cpp index 4566181..54b9e35 100644 --- a/src/filter_radius_search/radius_searchFilter.cpp +++ b/src/filter_radius_search/radius_searchFilter.cpp @@ -117,15 +117,11 @@ void RadiusSearchFilter::filter(PointView& view) for (PointId id = 0; id < view.size(); ++id) { temp.setPointId(id); - temp.setField(m_args->m_dim, int64_t(0)); + temp.setField(m_args->m_dim, int64_t(0)); // initialisation // process only points that satisfy a domain condition - //if (r.valuePasses(temp.getFieldAs(r.m_id))) if (temp.getFieldAs(m_args->m_dim_ref)>0) - { refView->appendPoint(view, id); - break; - } } for (PointId id = 0; id < view.size(); ++id) From 92cef3f0b68ba80f2221de0a8ceeac2463f5013e Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Thu, 11 Apr 2024 17:28:26 +0200 Subject: [PATCH 05/49] fix find cell --- src/filter_grid_decimation/grid_decimationFilter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/filter_grid_decimation/grid_decimationFilter.cpp b/src/filter_grid_decimation/grid_decimationFilter.cpp index 69a9e28..bc771f4 100755 --- a/src/filter_grid_decimation/grid_decimationFilter.cpp +++ b/src/filter_grid_decimation/grid_decimationFilter.cpp @@ -81,8 +81,8 @@ void GridDecimationFilter::processOne(BOX2D bounds, PointRef& point, PointViewPt double y = point.getFieldAs(Dimension::Id::Y); int id = point.getFieldAs(Dimension::Id::PointId); - double d_width_pt = std::floor((x - bounds.minx) / m_args->m_edgeLength) + 1; - double d_height_pt = std::floor((y - bounds.miny) / m_args->m_edgeLength) + 1; + double d_width_pt = std::floor((x - bounds.minx) / m_args->m_edgeLength); + double d_height_pt = std::floor((y - bounds.miny) / m_args->m_edgeLength); int width = static_cast(d_width_pt); int height = static_cast(d_height_pt); From 1fa4fbc1fe65ad7b51e8029988f3cdfbc1f2ab00 Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Fri, 12 Apr 2024 14:28:11 +0200 Subject: [PATCH 06/49] change name of attribute --- src/filter_radius_search/radius_searchFilter.cpp | 6 +++--- test/test_radius_search.py | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/filter_radius_search/radius_searchFilter.cpp b/src/filter_radius_search/radius_searchFilter.cpp index 54b9e35..824ebfd 100644 --- a/src/filter_radius_search/radius_searchFilter.cpp +++ b/src/filter_radius_search/radius_searchFilter.cpp @@ -36,9 +36,9 @@ void RadiusSearchFilter::addArgs(ProgramArgs& args) args.add("reference_domain", "Selects which points will be considered as potential neighbors", m_args->m_referenceDomain, "REF_DOMAIN"); args.add("radius", "Distance of neighbors to consult", m_args->m_radius, 1.); args.add("output_name_attribute", "Name of the added attribut", m_args->m_nameAddAttribute, "radius" ); - args.add("3d_search", "Search in 3d", m_args->search3d, false ); - args.add("2d_search_above", "if search in 2d : filter point above the distance", m_args->m_search_bellow, 0. ); - args.add("2d_search_bellow", "if search in 2d : filter point bellow the distance", m_args->m_search_above, 0. ); + args.add("search_3d", "Search in 3d", m_args->search3d, false ); + args.add("search_2d_above", "if search in 2d : filter point above the distance", m_args->m_search_bellow, 0. ); + args.add("search_2d_bellow", "if search in 2d : filter point bellow the distance", m_args->m_search_above, 0. ); } void RadiusSearchFilter::addDimensions(PointLayoutPtr layout) diff --git a/test/test_radius_search.py b/test/test_radius_search.py index cca3775..1734f9e 100755 --- a/test/test_radius_search.py +++ b/test/test_radius_search.py @@ -89,7 +89,7 @@ def distance3d(pt1, pt2): "src_domain": "SRS_DOMAIN", "reference_domain": "REF_DOMAIN", "output_name_attribute": "radius_2D", - "3d_search":False, + "search_3d":False, }, { "type": filter, @@ -97,9 +97,9 @@ def distance3d(pt1, pt2): "src_domain": "SRS_DOMAIN", "reference_domain": "REF_DOMAIN", "output_name_attribute": "radius_2D_above_bellow", - "3d_search": False, - "2d_search_above": 0.5, - "2d_search_bellow": 0.5, + "search_3d": False, + "search_2d_above": 0.5, + "search_2d_bellow": 0.5, }, { "type": filter, @@ -107,7 +107,7 @@ def distance3d(pt1, pt2): "src_domain": "SRS_DOMAIN", "reference_domain": "REF_DOMAIN", "output_name_attribute": "radius_3D", - "3d_search": True + "search_3d": True } ] From 06ef55083a5494c493417250a71d0ad5124c24a6 Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Fri, 12 Apr 2024 14:28:37 +0200 Subject: [PATCH 07/49] add pdal pipeline examples --- macro/__init__.py | 0 macro/ex_filtering_points.py | 55 ++++++++++++++++++++++++ macro/macro.py | 81 ++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100755 macro/__init__.py create mode 100755 macro/ex_filtering_points.py create mode 100755 macro/macro.py diff --git a/macro/__init__.py b/macro/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/macro/ex_filtering_points.py b/macro/ex_filtering_points.py new file mode 100755 index 0000000..a825205 --- /dev/null +++ b/macro/ex_filtering_points.py @@ -0,0 +1,55 @@ +import argparse +import pdal +from macro import * + +""" +This tool shows how to use function of macro in a pdal pipeline +""" + +def parse_args(): + parser = argparse.ArgumentParser("Tools for apply pdal pipelines") + parser.add_argument("--input", "-i", type=str, required=True, help="Input las file") + parser.add_argument("--output", "-o", type=str, required=True, help="Output las file") + return parser.parse_args() + + +if __name__ == "__main__": + args = parse_args() + + pipeline = pdal.Reader.las(args.input) + + # step 1 a 9 + pipeline = add_radius_search(pipeline, 1, False, "Classification==2", build_condition("Classification", [4,5]), "Classification=102") + pipeline = add_radius_search(pipeline, 1, False, "Classification==102", "Classification==2", "Classification=2") + pipeline = add_radius_search(pipeline, 1, False, "Classification==3", "Classification==5", "Classification=103") + pipeline = add_grid_decimation(pipeline, 0.75, "max", build_condition("Classification", [4,5,102,103]), "Classification=100") + pipeline |= pdal.Filter.assign(value="Classification=2", where="Classification==102") + pipeline |= pdal.Filter.assign(value="Classification=3", where="Classification==103") + pipeline = add_grid_decimation(pipeline, 0.5, "max", "Classification==2", "Classification=102") + pipeline = add_grid_decimation(pipeline, 0.5, "max", build_condition("Classification", [2,3,4,5,6,9,17,64,100]), "Classification=200") + pipeline = add_radius_search(pipeline, 1.5, False, "Classification==102", build_condition("Classification", [4,5,6,9,17,64,100]), "Classification=100") + pipeline |= pdal.Filter.assign(value="Classification=2", where="Classification==102") + + # step 10 + pipeline = add_radius_search(pipeline, 1.5, False, "Classification==2", "Classification==17", "Classification=102") + pipeline = add_radius_search(pipeline, 1.5, False, "Classification==102", build_condition("Classification", [2,3,4,5]), "Classification=2") + + # step 11 + pipeline = add_radius_search(pipeline, 1.5, False, "Classification==3", "Classification==17", "Classification=103") + pipeline = add_radius_search(pipeline, 1.5, False, "Classification==103", build_condition("Classification", [2,3,4,5]), "Classification=3") + + # step 12 + pipeline = add_radius_search(pipeline, 1.5, False, "Classification==4", "Classification==17", "Classification=104") + pipeline = add_radius_search(pipeline, 1.5, False, "Classification==104", build_condition("Classification", [2,3,4,5]), "Classification=4") + + # step 13 + pipeline = add_radius_search(pipeline, 1.5, False, "Classification==5", "Classification==17", "Classification=105") + pipeline = add_radius_search(pipeline, 1.5, False, "Classification==105", build_condition("Classification", [2,3,4,5]), "Classification=5") + + # step 14 + pipeline = add_radius_search(pipeline, 1.5, False, "Classification==9", "Classification==17", "Classification=109") + pipeline = add_radius_search(pipeline, 1.5, False, "Classification==109", "Classification==9", "Classification=9") + + pipeline |= pdal.Writer.las(extra_dims="all",minor_version=4,dataformat_id=6,filename=args.output) + pipeline.execute() + diff --git a/macro/macro.py b/macro/macro.py new file mode 100755 index 0000000..e38dc4b --- /dev/null +++ b/macro/macro.py @@ -0,0 +1,81 @@ +import argparse +import pdal + +""" +Some useful filters combinations for complete pdal pipeline +""" + + +def add_radius_search(pipeline, radius, search_3d, condition_src, condition_ref, condition_out ): + """ + search points from "condition_src" closed from "condition_ref", and reassign them to "condition_out" + This combination is equivalent to the CloseBy macro of TerraScan + radius : the search distance + search_3d : the distance reseach is in 3d if True + condition_src, condition_ref, condition_out : a pdal condition as "Classification==2" + """ + pipeline |= pdal.Filter.ferry(dimensions=f"=>REF_DOMAIN, =>SRS_DOMAIN, =>radius_search") + pipeline |= pdal.Filter.assign(value=["SRS_DOMAIN = 0", f"SRS_DOMAIN = 1 WHERE {condition_src}", + "REF_DOMAIN = 0", f"REF_DOMAIN = 1 WHERE {condition_ref}", + "radius_search = 0"]) + pipeline |= pdal.Filter.radius_search(radius=radius, src_domain="SRS_DOMAIN",reference_domain="REF_DOMAIN", + output_name_attribute="radius_search", search_3d=search_3d) + pipeline |= pdal.Filter.assign(value=condition_out,where="radius_search==1") + return pipeline + + + +def add_grid_decimation(pipeline, grid_resolution, output_type, condition, condition_out): + """ + Select a points in a grid from "condition"; points not selected are reassign to "condition_out" + This combination is equivalent to the Thin Points macro of TerraScan + grid_resolution : resolution of the grid + output_type : "max" or "min" (the highest or lower points of the grid) + condition, condition_out : a pdal condition as "Classification==2" + """ + pipeline |= pdal.Filter.ferry(dimensions=f"=>grid,") + pipeline |= pdal.Filter.assign(value="grid = 0") + pipeline |= pdal.Filter.grid_decimation(resolution=grid_resolution, output_name_attribut="grid", + output_type=output_type, where=condition) + pipeline |= pdal.Filter.assign(value=condition_out,where=f"grid==0 && ({condition})") + return pipeline + + + +def classify_hgt_ground(pipeline, hmin, hmax, condition, condition_out): + """ + reassign points from "condition" between "hmin" and "hmax" of the ground to "condition_out" + This combination is equivalent to the ClassifyHgtGrd macro of TerraScan + condition, condition_out : a pdal condition as "Classification==2" + """ + pipeline |= pdal.Filter.hag_delaunay(allow_extrapolation=True) + condition_h = f"HeightAboveGround>{hmin} && HeightAboveGround<={hmax}" + condition_h += " && " + condition + pipeline |= pdal.Filter.assign(value=condition_out, where=condition_h) + return pipeline + + + +def keep_non_planar_pts(pipeline, condition, condition_out): + """ + reassign points from "condition" who are planar to "condition_out" + This combination is equivalent to the ClassifyModelKey macro of TerraScan + condition, condition_out : a pdal condition as "Classification==2" + """ + pipeline |= pdal.Filter.approximatecoplanar(knn=8,thresh1=25,thresh2=6,where=condition) + pipeline |= pdal.Filter.assign(value=condition_out,where=f"Coplanar==0 && ({condition})") + return pipeline + + + + +def build_condition(key, values): + """ + build 'key==values[0] || key==values[1] ...' + """ + condition = "" + for v in values: + condition += key+"=="+str(v) + if v!=values[-1]:condition += " || " + return condition + From 83bf42cbbf77b7417d9143bdaf5c8f5b99b3d567 Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Tue, 23 Apr 2024 16:37:15 +0200 Subject: [PATCH 08/49] update test --- Dockerfile | 2 ++ test/test_grid_decimation.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index ce43677..f4ed699 100755 --- a/Dockerfile +++ b/Dockerfile @@ -21,3 +21,5 @@ COPY --from=build /tmp/install/lib /tmp/install/lib ENV PATH=$PATH:/opt/conda/envs/pdal_ign_plugin/bin/ ENV PROJ_LIB=/opt/conda/envs/pdal_ign_plugin/share/proj/ ENV PDAL_DRIVER_PATH=/tmp/install/lib + +COPY --from=build /tmp/macro /tmp/macro \ No newline at end of file diff --git a/test/test_grid_decimation.py b/test/test_grid_decimation.py index ab7f8d9..4d25d45 100755 --- a/test/test_grid_decimation.py +++ b/test/test_grid_decimation.py @@ -54,7 +54,7 @@ def test_grid_decimation(): if pt["grid"] > 0: nb_pts_grid += 1 - assert nb_pts_grid == 3196 + assert nb_pts_grid == 3234 assert nb_pts_grid <= nb_dalle data = [] From 989e7440ad4e97a1dba81c038c19f7877ab07ac0 Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Wed, 24 Apr 2024 17:00:37 +0200 Subject: [PATCH 09/49] update DockerFile --- Dockerfile | 5 +++-- macro/ex_filtering_points.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index f4ed699..719ec36 100755 --- a/Dockerfile +++ b/Dockerfile @@ -9,6 +9,7 @@ RUN apt-get update && apt-get install --no-install-recommends -y cmake make buil COPY src src COPY CMakeLists.txt CMakeLists.txt +COPY macro macro RUN cmake -G"Unix Makefiles" -DCONDA_PREFIX=$CONDA_PREFIX -DCMAKE_BUILD_TYPE=Release RUN make -j4 install @@ -17,9 +18,9 @@ FROM debian:bullseye-slim COPY --from=build /opt/conda/envs/pdal_ign_plugin /opt/conda/envs/pdal_ign_plugin COPY --from=build /tmp/install/lib /tmp/install/lib - +COPY --from=build /tmp/macro /macro + ENV PATH=$PATH:/opt/conda/envs/pdal_ign_plugin/bin/ ENV PROJ_LIB=/opt/conda/envs/pdal_ign_plugin/share/proj/ ENV PDAL_DRIVER_PATH=/tmp/install/lib -COPY --from=build /tmp/macro /tmp/macro \ No newline at end of file diff --git a/macro/ex_filtering_points.py b/macro/ex_filtering_points.py index a825205..988fc6d 100755 --- a/macro/ex_filtering_points.py +++ b/macro/ex_filtering_points.py @@ -3,7 +3,7 @@ from macro import * """ -This tool shows how to use function of macro in a pdal pipeline +This tool shows how to use functions of macro in a pdal pipeline """ def parse_args(): From 285fbccd621aee09153f64e0adb4683c0a175190 Mon Sep 17 00:00:00 2001 From: alavenant Date: Wed, 15 May 2024 17:12:52 +0200 Subject: [PATCH 10/49] add pip to environment.yml Co-authored-by: leavauchier <120112647+leavauchier@users.noreply.github.com> --- environment.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/environment.yml b/environment.yml index 781039d..f1114f4 100755 --- a/environment.yml +++ b/environment.yml @@ -11,6 +11,7 @@ dependencies: - shapely - gdal - cmake + - pip - pip: - ign-pdal-tools From dd551eda5c7dd10ca9ea57fff51232b495266842 Mon Sep 17 00:00:00 2001 From: alavenant Date: Wed, 15 May 2024 17:24:56 +0200 Subject: [PATCH 11/49] Update macro/ex_filtering_points.py update parser help Co-authored-by: leavauchier <120112647+leavauchier@users.noreply.github.com> --- macro/ex_filtering_points.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macro/ex_filtering_points.py b/macro/ex_filtering_points.py index 988fc6d..81855ce 100755 --- a/macro/ex_filtering_points.py +++ b/macro/ex_filtering_points.py @@ -7,7 +7,7 @@ """ def parse_args(): - parser = argparse.ArgumentParser("Tools for apply pdal pipelines") + parser = argparse.ArgumentParser("Tool to apply pdal pipelines to modify classification") parser.add_argument("--input", "-i", type=str, required=True, help="Input las file") parser.add_argument("--output", "-o", type=str, required=True, help="Output las file") return parser.parse_args() From ecd119373fbe5f6f2c1260c3fca1c745da113703 Mon Sep 17 00:00:00 2001 From: alavenant Date: Wed, 15 May 2024 17:26:39 +0200 Subject: [PATCH 12/49] Update src/filter_radius_search/radius_searchFilter.cpp Co-authored-by: leavauchier <120112647+leavauchier@users.noreply.github.com> --- src/filter_radius_search/radius_searchFilter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/filter_radius_search/radius_searchFilter.cpp b/src/filter_radius_search/radius_searchFilter.cpp index 824ebfd..45d7ff7 100644 --- a/src/filter_radius_search/radius_searchFilter.cpp +++ b/src/filter_radius_search/radius_searchFilter.cpp @@ -32,7 +32,7 @@ RadiusSearchFilter::~RadiusSearchFilter() void RadiusSearchFilter::addArgs(ProgramArgs& args) { - args.add("src_domain", "Selects which points will be subject to radius-based neighbors search", m_args->m_srcDomain, "SRS_DOMAIN"); + args.add("src_domain", "Selects which points will be subject to radius-based neighbors search", m_args->m_srcDomain, "SRC_DOMAIN"); args.add("reference_domain", "Selects which points will be considered as potential neighbors", m_args->m_referenceDomain, "REF_DOMAIN"); args.add("radius", "Distance of neighbors to consult", m_args->m_radius, 1.); args.add("output_name_attribute", "Name of the added attribut", m_args->m_nameAddAttribute, "radius" ); From efe8f4362994dc8b25e0f848bba0741eb7d39591 Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Wed, 15 May 2024 17:28:29 +0200 Subject: [PATCH 13/49] update exemple --- macro/ex_filtering_points.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/macro/ex_filtering_points.py b/macro/ex_filtering_points.py index 81855ce..25994f9 100755 --- a/macro/ex_filtering_points.py +++ b/macro/ex_filtering_points.py @@ -1,6 +1,6 @@ import argparse import pdal -from macro import * +import macro """ This tool shows how to use functions of macro in a pdal pipeline @@ -19,36 +19,36 @@ def parse_args(): pipeline = pdal.Reader.las(args.input) # step 1 a 9 - pipeline = add_radius_search(pipeline, 1, False, "Classification==2", build_condition("Classification", [4,5]), "Classification=102") - pipeline = add_radius_search(pipeline, 1, False, "Classification==102", "Classification==2", "Classification=2") - pipeline = add_radius_search(pipeline, 1, False, "Classification==3", "Classification==5", "Classification=103") - pipeline = add_grid_decimation(pipeline, 0.75, "max", build_condition("Classification", [4,5,102,103]), "Classification=100") + pipeline = macro.add_radius_search(pipeline, 1, False, "Classification==2", macro.build_condition("Classification", [4,5]), "Classification=102") + pipeline = macro.add_radius_search(pipeline, 1, False, "Classification==102", "Classification==2", "Classification=2") + pipeline = macro.add_radius_search(pipeline, 1, False, "Classification==3", "Classification==5", "Classification=103") + pipeline = macro.add_grid_decimation(pipeline, 0.75, "max", macro.build_condition("Classification", [4,5,102,103]), "Classification=100") pipeline |= pdal.Filter.assign(value="Classification=2", where="Classification==102") pipeline |= pdal.Filter.assign(value="Classification=3", where="Classification==103") - pipeline = add_grid_decimation(pipeline, 0.5, "max", "Classification==2", "Classification=102") - pipeline = add_grid_decimation(pipeline, 0.5, "max", build_condition("Classification", [2,3,4,5,6,9,17,64,100]), "Classification=200") - pipeline = add_radius_search(pipeline, 1.5, False, "Classification==102", build_condition("Classification", [4,5,6,9,17,64,100]), "Classification=100") + pipeline = macro.add_grid_decimation(pipeline, 0.5, "max", "Classification==2", "Classification=102") + pipeline = macro.add_grid_decimation(pipeline, 0.5, "max", macro.build_condition("Classification", [2,3,4,5,6,9,17,64,100]), "Classification=200") + pipeline = macro.add_radius_search(pipeline, 1.5, False, "Classification==102", macro.build_condition("Classification", [4,5,6,9,17,64,100]), "Classification=100") pipeline |= pdal.Filter.assign(value="Classification=2", where="Classification==102") # step 10 - pipeline = add_radius_search(pipeline, 1.5, False, "Classification==2", "Classification==17", "Classification=102") - pipeline = add_radius_search(pipeline, 1.5, False, "Classification==102", build_condition("Classification", [2,3,4,5]), "Classification=2") + pipeline = macro.add_radius_search(pipeline, 1.5, False, "Classification==2", "Classification==17", "Classification=102") + pipeline = macro.add_radius_search(pipeline, 1.5, False, "Classification==102", macro.build_condition("Classification", [2,3,4,5]), "Classification=2") # step 11 - pipeline = add_radius_search(pipeline, 1.5, False, "Classification==3", "Classification==17", "Classification=103") - pipeline = add_radius_search(pipeline, 1.5, False, "Classification==103", build_condition("Classification", [2,3,4,5]), "Classification=3") + pipeline = macro.add_radius_search(pipeline, 1.5, False, "Classification==3", "Classification==17", "Classification=103") + pipeline = macro.add_radius_search(pipeline, 1.5, False, "Classification==103", macro.build_condition("Classification", [2,3,4,5]), "Classification=3") # step 12 - pipeline = add_radius_search(pipeline, 1.5, False, "Classification==4", "Classification==17", "Classification=104") - pipeline = add_radius_search(pipeline, 1.5, False, "Classification==104", build_condition("Classification", [2,3,4,5]), "Classification=4") + pipeline = macro.add_radius_search(pipeline, 1.5, False, "Classification==4", "Classification==17", "Classification=104") + pipeline = macro.add_radius_search(pipeline, 1.5, False, "Classification==104", macro.build_condition("Classification", [2,3,4,5]), "Classification=4") # step 13 - pipeline = add_radius_search(pipeline, 1.5, False, "Classification==5", "Classification==17", "Classification=105") - pipeline = add_radius_search(pipeline, 1.5, False, "Classification==105", build_condition("Classification", [2,3,4,5]), "Classification=5") + pipeline = macro.add_radius_search(pipeline, 1.5, False, "Classification==5", "Classification==17", "Classification=105") + pipeline = macro.add_radius_search(pipeline, 1.5, False, "Classification==105", macro.build_condition("Classification", [2,3,4,5]), "Classification=5") # step 14 - pipeline = add_radius_search(pipeline, 1.5, False, "Classification==9", "Classification==17", "Classification=109") - pipeline = add_radius_search(pipeline, 1.5, False, "Classification==109", "Classification==9", "Classification=9") + pipeline = macro.add_radius_search(pipeline, 1.5, False, "Classification==9", "Classification==17", "Classification=109") + pipeline = macro.add_radius_search(pipeline, 1.5, False, "Classification==109", "Classification==9", "Classification=9") pipeline |= pdal.Writer.las(extra_dims="all",minor_version=4,dataformat_id=6,filename=args.output) pipeline.execute() From f4e40b48b7a7bd2c91234c1c9c5ff47bae7912ac Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Fri, 17 May 2024 10:20:12 +0200 Subject: [PATCH 14/49] no more shapely + fix test --- test/test_radius_search.py | 145 ++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 74 deletions(-) diff --git a/test/test_radius_search.py b/test/test_radius_search.py index 1734f9e..7ae9b63 100755 --- a/test/test_radius_search.py +++ b/test/test_radius_search.py @@ -2,8 +2,6 @@ import tempfile from test import utils -from shapely.geometry import Point - from random import * import numpy as np import pdal @@ -14,19 +12,21 @@ def test_radius_search(): + filter = "filters.radius_search" + utils.pdal_has_plugin(filter) + distance_radius = 1 + distance_cylinder = 0.5 - #shapely is 2d library def distance2d(pt1, pt2): - return pt1.distance(pt2) + return sqrt( (pt1[0]-pt2[0])**2 + (pt1[1]-pt2[1])**2 ) def distance3d(pt1, pt2): - return sqrt( (pt1.x-pt2.x)**2 + (pt1.y-pt2.y)**2 + (pt1.z-pt2.z)**2 ) + return sqrt( (pt1[0]-pt2[0])**2 + (pt1[1]-pt2[1])**2 + (pt1[2]-pt2[2])**2 ) pt_x = 1639825.15 pt_y = 1454924.63 pt_z = 7072.17 pt_ini = (pt_x, pt_y, pt_z, 1) - pt1 = Point(pt_x, pt_y, pt_z) dtype = [('X', 'SRS_DOMAIN" - }, - { - "type": "filters.ferry", - "dimensions": "=>REF_DOMAIN" - }, - { - "type": "filters.assign", - "value": [ - "SRS_DOMAIN = 1 WHERE Classification==2", - "SRS_DOMAIN = 0 WHERE Classification!=2", - "REF_DOMAIN = 1 WHERE Classification==1", - "REF_DOMAIN = 0 WHERE Classification!=1", - ], - }, - { - "type": filter, - "radius": "1.", - "src_domain": "SRS_DOMAIN", - "reference_domain": "REF_DOMAIN", - "output_name_attribute": "radius_2D", - "search_3d":False, - }, - { - "type": filter, - "radius": "1.", - "src_domain": "SRS_DOMAIN", - "reference_domain": "REF_DOMAIN", - "output_name_attribute": "radius_2D_above_bellow", - "search_3d": False, - "search_2d_above": 0.5, - "search_2d_bellow": 0.5, - }, - { - "type": filter, - "radius": "1.", - "src_domain": "SRS_DOMAIN", - "reference_domain": "REF_DOMAIN", - "output_name_attribute": "radius_3D", - "search_3d": True - } - ] - - pipeline = pdal.Pipeline(json.dumps(PIPELINE)) - - # execute the pipeline - pipeline.execute() - arrays = pipeline.arrays - array = arrays[0] + with tempfile.NamedTemporaryFile(suffix="_las_tmp.las") as las: + pipeline = pdal.Writer.las(filename=las.name).pipeline(arrays_pts) + pipeline.execute() + + PIPELINE = [ + {"type": "readers.las", "filename": las.name}, + { + "type": "filters.ferry", + "dimensions": "=>SRS_DOMAIN" + }, + { + "type": "filters.ferry", + "dimensions": "=>REF_DOMAIN" + }, + { + "type": "filters.assign", + "value": [ + "SRS_DOMAIN = 1 WHERE Classification==2", + "SRS_DOMAIN = 0 WHERE Classification!=2", + "REF_DOMAIN = 1 WHERE Classification==1", + "REF_DOMAIN = 0 WHERE Classification!=1", + ], + }, + { + "type": filter, + "radius": "1.", + "src_domain": "SRS_DOMAIN", + "reference_domain": "REF_DOMAIN", + "output_name_attribute": "radius_2D", + "search_3d":False, + }, + { + "type": filter, + "radius": "1.", + "src_domain": "SRS_DOMAIN", + "reference_domain": "REF_DOMAIN", + "output_name_attribute": "radius_2D_above_bellow", + "search_3d": False, + "search_2d_above": distance_cylinder, + "search_2d_bellow": distance_cylinder, + }, + { + "type": filter, + "radius": "1.", + "src_domain": "SRS_DOMAIN", + "reference_domain": "REF_DOMAIN", + "output_name_attribute": "radius_3D", + "search_3d": True + } + ] + + pipeline = pdal.Pipeline(json.dumps(PIPELINE)) + pipeline.execute() + arrays = pipeline.arrays + array = arrays[0] nb_pts_radius_2d = 0 nb_pts_radius_3d = 0 @@ -131,4 +128,4 @@ def distance3d(pt1, pt2): assert nb_pts_radius_2d == nb_points_take_2d assert nb_pts_radius_3d == nb_points_take_3d - assert nb_pts_radius_2d_above_bellow == nb_points_take_2d_above_bellow \ No newline at end of file + assert nb_pts_radius_2d_above_bellow == nb_points_take_2d_above_below \ No newline at end of file From 24f417f4b37f46870c6306d76db02d26453d40b0 Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Fri, 17 May 2024 10:21:52 +0200 Subject: [PATCH 15/49] add pre-commit --- .pre-commit-config.yaml | 16 ++++++++++++++++ environment.yml | 10 ++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..2fe0fe5 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,16 @@ +repos: + - repo: https://github.com/ambv/black + rev: 23.12.0 + hooks: + - id: black + language_version: python3.11 + - repo: https://github.com/pycqa/flake8 + rev: 6.1.0 + hooks: + - id: flake8 + - repo: https://github.com/pycqa/isort + rev: 5.13.2 + hooks: + - id: isort + name: isort (python) + args: ["--profile", "black"] diff --git a/environment.yml b/environment.yml index f1114f4..1c30b24 100755 --- a/environment.yml +++ b/environment.yml @@ -5,14 +5,16 @@ channels: dependencies: - pdal - python-pdal - - pytest - - black - - isort - - shapely - gdal - cmake - pip - pip: - ign-pdal-tools +# --------- dev dep --------- # + - pre-commit # hooks for applying linters on commit + - black # code formatting + - isort # import sorting + - flake8 # code analysis + - pytest From c3b3459e31f6e8f033e147a24b55911a29fe10d6 Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Fri, 17 May 2024 12:36:36 +0200 Subject: [PATCH 16/49] update test : 3 tests indeed one --- test/test_radius_search.py | 181 ++++++++++++++++++++++++------------- 1 file changed, 116 insertions(+), 65 deletions(-) diff --git a/test/test_radius_search.py b/test/test_radius_search.py index 7ae9b63..f12e283 100755 --- a/test/test_radius_search.py +++ b/test/test_radius_search.py @@ -10,57 +10,36 @@ import pytest -def test_radius_search(): - filter = "filters.radius_search" - utils.pdal_has_plugin(filter) - - distance_radius = 1 - distance_cylinder = 0.5 - - def distance2d(pt1, pt2): - return sqrt( (pt1[0]-pt2[0])**2 + (pt1[1]-pt2[1])**2 ) - def distance3d(pt1, pt2): - return sqrt( (pt1[0]-pt2[0])**2 + (pt1[1]-pt2[1])**2 + (pt1[2]-pt2[2])**2 ) - - pt_x = 1639825.15 - pt_y = 1454924.63 - pt_z = 7072.17 - pt_ini = (pt_x, pt_y, pt_z, 1) +def distance2d(pt1, pt2): + return sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2) - dtype = [('X', ' 0: - nb_pts_radius_2d_above_bellow += 1 - if pt["radius_2D"] > 0: + if pt["radius_search"] > 0: + nb_pts_radius_3d += 1 + + assert nb_pts_radius_3d == nb_points_take_3d + + +def test_radius_search_2d(): + + distance_radius = 1 + nb_points_take_2d = 0 + + pt_x = 1639825.15 + pt_y = 1454924.63 + pt_z = 7072.17 + pt_ini = (pt_x, pt_y, pt_z, 1) + + arrays_pts = build_random_points_arround_one_point(pt_ini) + + dtype = [('X', ' 0: nb_pts_radius_2d += 1 - if pt["radius_3D"] > 0: - nb_pts_radius_3d += 1 assert nb_pts_radius_2d == nb_points_take_2d - assert nb_pts_radius_3d == nb_points_take_3d - assert nb_pts_radius_2d_above_bellow == nb_points_take_2d_above_below \ No newline at end of file + + +def test_radius_search_2d_cylinder(): + + distance_radius = 1 + distance_cylinder = 0.25 + + nb_points_take_2d = 0 + + pt_x = 1639825.15 + pt_y = 1454924.63 + pt_z = 7072.17 + pt_ini = (pt_x, pt_y, pt_z, 1) + + arrays_pts = build_random_points_arround_one_point(pt_ini) + + dtype = [('X', ' 0: + nb_pts_radius_2d += 1 + + assert nb_pts_radius_2d == nb_points_take_2d \ No newline at end of file From 0c27b5d65fc55900cca88be5e439ff17df10204c Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Fri, 17 May 2024 13:17:13 +0200 Subject: [PATCH 17/49] update test_radius_search --- test/test_radius_search.py | 126 +++++++++++++------------------------ 1 file changed, 45 insertions(+), 81 deletions(-) diff --git a/test/test_radius_search.py b/test/test_radius_search.py index f12e283..373cbb3 100755 --- a/test/test_radius_search.py +++ b/test/test_radius_search.py @@ -18,21 +18,6 @@ def distance2d(pt1, pt2): def distance3d(pt1, pt2): return sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2 + (pt1[2] - pt2[2]) ** 2) -def build_random_points_arround_one_point(pt_ini): - - nb_points = randint(20, 50) - arrays_pts = [] - for i in range(nb_points): - pti_x = pt_ini[0] + rand.uniform(-1.5, 1.5) - pti_y = pt_ini[1] + rand.uniform(-1.5, 1.5) - - # pdal write takes 2 numbers precision (scale_z=0.01 and offset_z=0 by default) - pti_z = round(pt_ini[2] + rand.uniform(-1.5, 1.5), 2) - pt_i = (pti_x, pti_y, pti_z, 2) - arrays_pts.append(pt_i) - - return arrays_pts - def run_filter(arrays_las, distance_radius, search_3d, distance_cylinder=0. ): filter = "filters.radius_search" @@ -78,71 +63,69 @@ def run_filter(arrays_las, distance_radius, search_3d, distance_cylinder=0. ): arrays = pipeline.arrays array = arrays[0] - return array + nb_pts_radius_search = 0 + for pt in array: + if pt["radius_search"] > 0: + nb_pts_radius_search += 1 -def test_radius_search_3d(): + return nb_pts_radius_search - distance_radius = 1 - nb_points_take_3d = 0 + +def build_random_points_around_one_point(test_function): pt_x = 1639825.15 pt_y = 1454924.63 pt_z = 7072.17 pt_ini = (pt_x, pt_y, pt_z, 1) - arrays_pts = build_random_points_arround_one_point(pt_ini) - dtype = [('X', ' 0: - nb_pts_radius_3d += 1 + nb_points_take += test_function(pt_ini, pt_i) - assert nb_pts_radius_3d == nb_points_take_3d + return arrays_las, nb_points_take -def test_radius_search_2d(): +def test_radius_search_3d(): distance_radius = 1 - nb_points_take_2d = 0 - - pt_x = 1639825.15 - pt_y = 1454924.63 - pt_z = 7072.17 - pt_ini = (pt_x, pt_y, pt_z, 1) - arrays_pts = build_random_points_arround_one_point(pt_ini) + def func_test(pt_ini, pt): + distance_i = distance3d(pt_ini, pt) + if distance_i < distance_radius: + return 1 + return 0 - dtype = [('X', ' 0: - nb_pts_radius_2d += 1 + def func_test(pt_ini, pt): + distance_i = distance2d(pt_ini, pt) + if distance_i < distance_radius: + return 1 + return 0 + arrays_las, nb_points_take_2d = build_random_points_around_one_point(func_test) + nb_pts_radius_2d = run_filter(arrays_las, distance_radius, False) assert nb_pts_radius_2d == nb_points_take_2d @@ -151,32 +134,13 @@ def test_radius_search_2d_cylinder(): distance_radius = 1 distance_cylinder = 0.25 - nb_points_take_2d = 0 - - pt_x = 1639825.15 - pt_y = 1454924.63 - pt_z = 7072.17 - pt_ini = (pt_x, pt_y, pt_z, 1) - - arrays_pts = build_random_points_arround_one_point(pt_ini) - - dtype = [('X', ' 0: - nb_pts_radius_2d += 1 + return 1 + return 0 - assert nb_pts_radius_2d == nb_points_take_2d \ No newline at end of file + arrays_las, nb_points_take_2d = build_random_points_around_one_point(func_test) + nb_pts_radius_2d_cylinder = run_filter(arrays_las, distance_radius, False, distance_cylinder) + assert nb_pts_radius_2d_cylinder == nb_points_take_2d \ No newline at end of file From e1d024ccc605f6827ef091fed970932358f07c48 Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Fri, 17 May 2024 13:25:19 +0200 Subject: [PATCH 18/49] add test to grid_decimation --- test/test_grid_decimation.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/test/test_grid_decimation.py b/test/test_grid_decimation.py index 4d25d45..159d38a 100755 --- a/test/test_grid_decimation.py +++ b/test/test_grid_decimation.py @@ -8,7 +8,7 @@ import pdaltools.las_info as li import pytest -def test_grid_decimation(): +def run_filter(type): ini_las = "test/data/4_6.las" resolution = 10 @@ -30,7 +30,7 @@ def test_grid_decimation(): { "type": filter, "resolution": resolution, - "output_type": "max", + "output_type": type, "output_name_attribut": "grid", "output_wkt": tmp_out_wkt, }, @@ -64,3 +64,13 @@ def test_grid_decimation(): data.append(line[0]) assert len(data) == nb_dalle + + return nb_pts_grid + +def test_grid_decimation_max(): + run_filter("max") + +def test_grid_decimation_max(): + run_filter("min") + + From 56ed739bc624fee7826b8fb8202a49652b7d225a Mon Sep 17 00:00:00 2001 From: alavenant Date: Fri, 17 May 2024 13:26:56 +0200 Subject: [PATCH 19/49] Update doc/grid_radius_search.md Co-authored-by: leavauchier <120112647+leavauchier@users.noreply.github.com> --- doc/grid_radius_search.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/grid_radius_search.md b/doc/grid_radius_search.md index cd62bd5..2d076a7 100755 --- a/doc/grid_radius_search.md +++ b/doc/grid_radius_search.md @@ -41,7 +41,7 @@ Options **update_expression** : A list of :ref:`assignment expressions ` to be applied to the points that satisfy the radius search. The list of values is evaluated in order. -**output_name_attribut**: The name of the new attribut. [Default: radius] +**output_name_attribut**: The name of the new attribute'. [Default: radius] **3d_search**: Search in 3d (as a ball). [Default: false] From ebfbd474fbf7060b9def641521bf3d73bbc5d02a Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Fri, 17 May 2024 13:43:17 +0200 Subject: [PATCH 20/49] filter search => assign --- CMakeLists.txt | 2 +- ...radius_search.md => grid_radius_assign.md} | 6 ++-- macro/ex_filtering_points.py | 28 +++++++++---------- macro/macro.py | 4 +-- src/filter_radius_assign/CMakeLists.txt | 16 +++++++++++ .../radius_assignFilter.cpp} | 4 +-- .../radius_assignFilter.hpp} | 0 src/filter_radius_search/CMakeLists.txt | 16 ----------- ...radius_search.py => test_radius_assign.py} | 8 +++--- 9 files changed, 42 insertions(+), 42 deletions(-) rename doc/{grid_radius_search.md => grid_radius_assign.md} (94%) create mode 100755 src/filter_radius_assign/CMakeLists.txt rename src/{filter_radius_search/radius_searchFilter.cpp => filter_radius_assign/radius_assignFilter.cpp} (98%) rename src/{filter_radius_search/radius_searchFilter.hpp => filter_radius_assign/radius_assignFilter.hpp} (100%) delete mode 100755 src/filter_radius_search/CMakeLists.txt rename test/{test_radius_search.py => test_radius_assign.py} (96%) diff --git a/CMakeLists.txt b/CMakeLists.txt index bd23b83..e5785cb 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,5 +12,5 @@ set(CMAKE_DEBUG_POSTFIX d) ## add plugin add_subdirectory(src/filter_grid_decimation) -add_subdirectory(src/filter_radius_search) +add_subdirectory(src/filter_radius_assign) diff --git a/doc/grid_radius_search.md b/doc/grid_radius_assign.md similarity index 94% rename from doc/grid_radius_search.md rename to doc/grid_radius_assign.md index cd62bd5..dab4543 100755 --- a/doc/grid_radius_search.md +++ b/doc/grid_radius_assign.md @@ -1,9 +1,9 @@ -# filter grid decimation +# filter radius assign Purpose --------------------------------------------------------------------------------------------------------- -The **radius search filter** add a new attribut where the value depends on their neighbors in a given radius: For each point in the domain src_domain_, if it has any neighbor with a distance lower than radius_ that belongs to the domain reference_domain_, it is updated. +The **radius assign filter** add a new attribut where the value depends on their neighbors in a given radius: For each point in the domain src_domain_, if it has any neighbor with a distance lower than radius_ that belongs to the domain reference_domain_, it is updated. Example @@ -16,7 +16,7 @@ This pipeline updates the Keypoint dimension of all points with classification 1 [ "file-input.las", { - "type" : "filters.radiussearch", + "type" : "filters.radius_assign", "src_domain" : "Classification[1:2]", "reference_domain" : "Classification[6:6]", "radius" : 1, diff --git a/macro/ex_filtering_points.py b/macro/ex_filtering_points.py index 25994f9..403c26f 100755 --- a/macro/ex_filtering_points.py +++ b/macro/ex_filtering_points.py @@ -19,36 +19,36 @@ def parse_args(): pipeline = pdal.Reader.las(args.input) # step 1 a 9 - pipeline = macro.add_radius_search(pipeline, 1, False, "Classification==2", macro.build_condition("Classification", [4,5]), "Classification=102") - pipeline = macro.add_radius_search(pipeline, 1, False, "Classification==102", "Classification==2", "Classification=2") - pipeline = macro.add_radius_search(pipeline, 1, False, "Classification==3", "Classification==5", "Classification=103") + pipeline = macro.add_radius_assign(pipeline, 1, False, "Classification==2", macro.build_condition("Classification", [4,5]), "Classification=102") + pipeline = macro.add_radius_assign(pipeline, 1, False, "Classification==102", "Classification==2", "Classification=2") + pipeline = macro.add_radius_assign(pipeline, 1, False, "Classification==3", "Classification==5", "Classification=103") pipeline = macro.add_grid_decimation(pipeline, 0.75, "max", macro.build_condition("Classification", [4,5,102,103]), "Classification=100") pipeline |= pdal.Filter.assign(value="Classification=2", where="Classification==102") pipeline |= pdal.Filter.assign(value="Classification=3", where="Classification==103") pipeline = macro.add_grid_decimation(pipeline, 0.5, "max", "Classification==2", "Classification=102") pipeline = macro.add_grid_decimation(pipeline, 0.5, "max", macro.build_condition("Classification", [2,3,4,5,6,9,17,64,100]), "Classification=200") - pipeline = macro.add_radius_search(pipeline, 1.5, False, "Classification==102", macro.build_condition("Classification", [4,5,6,9,17,64,100]), "Classification=100") + pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==102", macro.build_condition("Classification", [4,5,6,9,17,64,100]), "Classification=100") pipeline |= pdal.Filter.assign(value="Classification=2", where="Classification==102") # step 10 - pipeline = macro.add_radius_search(pipeline, 1.5, False, "Classification==2", "Classification==17", "Classification=102") - pipeline = macro.add_radius_search(pipeline, 1.5, False, "Classification==102", macro.build_condition("Classification", [2,3,4,5]), "Classification=2") + pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==2", "Classification==17", "Classification=102") + pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==102", macro.build_condition("Classification", [2,3,4,5]), "Classification=2") # step 11 - pipeline = macro.add_radius_search(pipeline, 1.5, False, "Classification==3", "Classification==17", "Classification=103") - pipeline = macro.add_radius_search(pipeline, 1.5, False, "Classification==103", macro.build_condition("Classification", [2,3,4,5]), "Classification=3") + pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==3", "Classification==17", "Classification=103") + pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==103", macro.build_condition("Classification", [2,3,4,5]), "Classification=3") # step 12 - pipeline = macro.add_radius_search(pipeline, 1.5, False, "Classification==4", "Classification==17", "Classification=104") - pipeline = macro.add_radius_search(pipeline, 1.5, False, "Classification==104", macro.build_condition("Classification", [2,3,4,5]), "Classification=4") + pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==4", "Classification==17", "Classification=104") + pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==104", macro.build_condition("Classification", [2,3,4,5]), "Classification=4") # step 13 - pipeline = macro.add_radius_search(pipeline, 1.5, False, "Classification==5", "Classification==17", "Classification=105") - pipeline = macro.add_radius_search(pipeline, 1.5, False, "Classification==105", macro.build_condition("Classification", [2,3,4,5]), "Classification=5") + pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==5", "Classification==17", "Classification=105") + pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==105", macro.build_condition("Classification", [2,3,4,5]), "Classification=5") # step 14 - pipeline = macro.add_radius_search(pipeline, 1.5, False, "Classification==9", "Classification==17", "Classification=109") - pipeline = macro.add_radius_search(pipeline, 1.5, False, "Classification==109", "Classification==9", "Classification=9") + pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==9", "Classification==17", "Classification=109") + pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==109", "Classification==9", "Classification=9") pipeline |= pdal.Writer.las(extra_dims="all",minor_version=4,dataformat_id=6,filename=args.output) pipeline.execute() diff --git a/macro/macro.py b/macro/macro.py index e38dc4b..0cddaf7 100755 --- a/macro/macro.py +++ b/macro/macro.py @@ -6,7 +6,7 @@ """ -def add_radius_search(pipeline, radius, search_3d, condition_src, condition_ref, condition_out ): +def add_radius_assign(pipeline, radius, search_3d, condition_src, condition_ref, condition_out ): """ search points from "condition_src" closed from "condition_ref", and reassign them to "condition_out" This combination is equivalent to the CloseBy macro of TerraScan @@ -18,7 +18,7 @@ def add_radius_search(pipeline, radius, search_3d, condition_src, condition_ref, pipeline |= pdal.Filter.assign(value=["SRS_DOMAIN = 0", f"SRS_DOMAIN = 1 WHERE {condition_src}", "REF_DOMAIN = 0", f"REF_DOMAIN = 1 WHERE {condition_ref}", "radius_search = 0"]) - pipeline |= pdal.Filter.radius_search(radius=radius, src_domain="SRS_DOMAIN",reference_domain="REF_DOMAIN", + pipeline |= pdal.Filter.radius_assign(radius=radius, src_domain="SRS_DOMAIN",reference_domain="REF_DOMAIN", output_name_attribute="radius_search", search_3d=search_3d) pipeline |= pdal.Filter.assign(value=condition_out,where="radius_search==1") return pipeline diff --git a/src/filter_radius_assign/CMakeLists.txt b/src/filter_radius_assign/CMakeLists.txt new file mode 100755 index 0000000..2a49e7c --- /dev/null +++ b/src/filter_radius_assign/CMakeLists.txt @@ -0,0 +1,16 @@ + +file( GLOB_RECURSE GD_SRCS + ${CMAKE_SOURCE_DIR}/src/filter_radius_assign/*.hpp + ${CMAKE_SOURCE_DIR}/src/filter_radius_assign/*.cpp) + +PDAL_CREATE_PLUGIN( + TYPE filter + NAME radius_assign + VERSION 1.0 + SOURCES ${GD_SRCS} +) + +install(TARGETS + pdal_plugin_filter_radius_assign +) + diff --git a/src/filter_radius_search/radius_searchFilter.cpp b/src/filter_radius_assign/radius_assignFilter.cpp similarity index 98% rename from src/filter_radius_search/radius_searchFilter.cpp rename to src/filter_radius_assign/radius_assignFilter.cpp index 45d7ff7..cad6fbf 100644 --- a/src/filter_radius_search/radius_searchFilter.cpp +++ b/src/filter_radius_assign/radius_assignFilter.cpp @@ -1,4 +1,4 @@ -#include "radius_searchFilter.hpp" +#include "radius_assignFilter.hpp" #include #include @@ -13,7 +13,7 @@ namespace pdal { static PluginInfo const s_info = PluginInfo( - "filters.radius_search", + "filters.radius_assign", "Re-assign some point attributes based KNN voting", "" ); diff --git a/src/filter_radius_search/radius_searchFilter.hpp b/src/filter_radius_assign/radius_assignFilter.hpp similarity index 100% rename from src/filter_radius_search/radius_searchFilter.hpp rename to src/filter_radius_assign/radius_assignFilter.hpp diff --git a/src/filter_radius_search/CMakeLists.txt b/src/filter_radius_search/CMakeLists.txt deleted file mode 100755 index b7ba4e6..0000000 --- a/src/filter_radius_search/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ - -file( GLOB_RECURSE GD_SRCS - ${CMAKE_SOURCE_DIR}/src/filter_radius_search/*.hpp - ${CMAKE_SOURCE_DIR}/src/filter_radius_search/*.cpp) - -PDAL_CREATE_PLUGIN( - TYPE filter - NAME radius_search - VERSION 1.0 - SOURCES ${GD_SRCS} -) - -install(TARGETS - pdal_plugin_filter_radius_search -) - diff --git a/test/test_radius_search.py b/test/test_radius_assign.py similarity index 96% rename from test/test_radius_search.py rename to test/test_radius_assign.py index 373cbb3..f517252 100755 --- a/test/test_radius_search.py +++ b/test/test_radius_assign.py @@ -20,7 +20,7 @@ def distance3d(pt1, pt2): def run_filter(arrays_las, distance_radius, search_3d, distance_cylinder=0. ): - filter = "filters.radius_search" + filter = "filters.radius_assign" utils.pdal_has_plugin(filter) with tempfile.NamedTemporaryFile(suffix="_las_tmp.las") as las: @@ -99,7 +99,7 @@ def build_random_points_around_one_point(test_function): return arrays_las, nb_points_take -def test_radius_search_3d(): +def test_radius_assign_3d(): distance_radius = 1 @@ -114,7 +114,7 @@ def func_test(pt_ini, pt): assert nb_pts_radius_3d == nb_points_take_3d -def test_radius_search_2d(): +def test_radius_assign_2d(): distance_radius = 1 @@ -129,7 +129,7 @@ def func_test(pt_ini, pt): assert nb_pts_radius_2d == nb_points_take_2d -def test_radius_search_2d_cylinder(): +def test_radius_assign_2d_cylinder(): distance_radius = 1 distance_cylinder = 0.25 From 2d2b3f1d6c900ad66a7bfc04568273627fb907c7 Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Fri, 17 May 2024 14:47:14 +0200 Subject: [PATCH 21/49] update name attribute --- macro/macro.py | 2 +- src/filter_radius_assign/radius_assignFilter.cpp | 6 +++--- src/filter_radius_assign/radius_assignFilter.hpp | 2 +- test/test_radius_assign.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/macro/macro.py b/macro/macro.py index 0cddaf7..809b410 100755 --- a/macro/macro.py +++ b/macro/macro.py @@ -19,7 +19,7 @@ def add_radius_assign(pipeline, radius, search_3d, condition_src, condition_ref, "REF_DOMAIN = 0", f"REF_DOMAIN = 1 WHERE {condition_ref}", "radius_search = 0"]) pipeline |= pdal.Filter.radius_assign(radius=radius, src_domain="SRS_DOMAIN",reference_domain="REF_DOMAIN", - output_name_attribute="radius_search", search_3d=search_3d) + output_dimension="radius_search", search_3d=search_3d) pipeline |= pdal.Filter.assign(value=condition_out,where="radius_search==1") return pipeline diff --git a/src/filter_radius_assign/radius_assignFilter.cpp b/src/filter_radius_assign/radius_assignFilter.cpp index cad6fbf..55becf3 100644 --- a/src/filter_radius_assign/radius_assignFilter.cpp +++ b/src/filter_radius_assign/radius_assignFilter.cpp @@ -35,7 +35,7 @@ void RadiusSearchFilter::addArgs(ProgramArgs& args) args.add("src_domain", "Selects which points will be subject to radius-based neighbors search", m_args->m_srcDomain, "SRC_DOMAIN"); args.add("reference_domain", "Selects which points will be considered as potential neighbors", m_args->m_referenceDomain, "REF_DOMAIN"); args.add("radius", "Distance of neighbors to consult", m_args->m_radius, 1.); - args.add("output_name_attribute", "Name of the added attribut", m_args->m_nameAddAttribute, "radius" ); + args.add("output_dimension", "Name of the added attribut", m_args->m_outputDimension, "radius" ); args.add("search_3d", "Search in 3d", m_args->search3d, false ); args.add("search_2d_above", "if search in 2d : filter point above the distance", m_args->m_search_bellow, 0. ); args.add("search_2d_bellow", "if search in 2d : filter point bellow the distance", m_args->m_search_above, 0. ); @@ -43,7 +43,7 @@ void RadiusSearchFilter::addArgs(ProgramArgs& args) void RadiusSearchFilter::addDimensions(PointLayoutPtr layout) { - m_args->m_dim = layout->registerOrAssignDim(m_args->m_nameAddAttribute, Dimension::Type::Double); + m_args->m_dim = layout->registerOrAssignDim(m_args->m_outputDimension, Dimension::Type::Double); m_args->m_dim_ref = layout->registerOrAssignDim(m_args->m_referenceDomain,Dimension::Type::Unsigned8); m_args->m_dim_src = layout->registerOrAssignDim(m_args->m_srcDomain,Dimension::Type::Unsigned8); } @@ -54,7 +54,7 @@ void RadiusSearchFilter::initialize() throwError("The reference_domain must be given."); if (m_args->m_radius <= 0) throwError("Invalid 'radius' option: " + std::to_string(m_args->m_radius) + ", must be > 0"); - if (m_args->m_nameAddAttribute.empty()) + if (m_args->m_outputDimension.empty()) throwError("The output_name_attribut must be given."); } diff --git a/src/filter_radius_assign/radius_assignFilter.hpp b/src/filter_radius_assign/radius_assignFilter.hpp index 3a4346e..edd3527 100644 --- a/src/filter_radius_assign/radius_assignFilter.hpp +++ b/src/filter_radius_assign/radius_assignFilter.hpp @@ -28,7 +28,7 @@ class PDAL_DLL RadiusSearchFilter : public Filter std::string m_srcDomain; double m_radius; PointIdList m_ptsToUpdate; - std::string m_nameAddAttribute; + std::string m_outputDimension; Dimension::Id m_dim; bool search3d; Dimension::Id m_dim_ref, m_dim_src; diff --git a/test/test_radius_assign.py b/test/test_radius_assign.py index f517252..4bc759b 100755 --- a/test/test_radius_assign.py +++ b/test/test_radius_assign.py @@ -51,7 +51,7 @@ def run_filter(arrays_las, distance_radius, search_3d, distance_cylinder=0. ): "radius": distance_radius, "src_domain": "SRS_DOMAIN", "reference_domain": "REF_DOMAIN", - "output_name_attribute": "radius_search", + "output_dimension": "radius_search", "search_3d": search_3d, "search_2d_above": distance_cylinder, "search_2d_bellow": distance_cylinder, From bc3536921b07b4e719f14ec72b70d942b2f9678d Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Fri, 17 May 2024 14:50:32 +0200 Subject: [PATCH 22/49] update class name --- .../radius_assignFilter.cpp | 32 +++++++++---------- .../radius_assignFilter.hpp | 18 +++++------ 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/filter_radius_assign/radius_assignFilter.cpp b/src/filter_radius_assign/radius_assignFilter.cpp index 55becf3..754dfef 100644 --- a/src/filter_radius_assign/radius_assignFilter.cpp +++ b/src/filter_radius_assign/radius_assignFilter.cpp @@ -17,38 +17,38 @@ static PluginInfo const s_info = PluginInfo( "Re-assign some point attributes based KNN voting", "" ); -CREATE_SHARED_STAGE(RadiusSearchFilter, s_info) +CREATE_SHARED_STAGE(RadiusAssignFilter, s_info) -std::string RadiusSearchFilter::getName() const { return s_info.name; } +std::string RadiusAssignFilter::getName() const { return s_info.name; } -RadiusSearchFilter::RadiusSearchFilter() : -m_args(new RadiusSearchFilter::RadiusSearchArgs) +RadiusAssignFilter::RadiusAssignFilter() : +m_args(new RadiusAssignFilter::RadiusAssignArgs) {} -RadiusSearchFilter::~RadiusSearchFilter() +RadiusAssignFilter::~RadiusAssignFilter() {} -void RadiusSearchFilter::addArgs(ProgramArgs& args) +void RadiusAssignFilter::addArgs(ProgramArgs& args) { args.add("src_domain", "Selects which points will be subject to radius-based neighbors search", m_args->m_srcDomain, "SRC_DOMAIN"); args.add("reference_domain", "Selects which points will be considered as potential neighbors", m_args->m_referenceDomain, "REF_DOMAIN"); args.add("radius", "Distance of neighbors to consult", m_args->m_radius, 1.); - args.add("output_dimension", "Name of the added attribut", m_args->m_outputDimension, "radius" ); + args.add("output_dimension", "Name of the added attribut", m_args->m_outputDimension, "radius"); args.add("search_3d", "Search in 3d", m_args->search3d, false ); - args.add("search_2d_above", "if search in 2d : filter point above the distance", m_args->m_search_bellow, 0. ); - args.add("search_2d_bellow", "if search in 2d : filter point bellow the distance", m_args->m_search_above, 0. ); + args.add("search_2d_above", "if search in 2d : filter point above the distance", m_args->m_search_bellow, 0.); + args.add("search_2d_bellow", "if search in 2d : filter point bellow the distance", m_args->m_search_above, 0.); } -void RadiusSearchFilter::addDimensions(PointLayoutPtr layout) +void RadiusAssignFilter::addDimensions(PointLayoutPtr layout) { m_args->m_dim = layout->registerOrAssignDim(m_args->m_outputDimension, Dimension::Type::Double); m_args->m_dim_ref = layout->registerOrAssignDim(m_args->m_referenceDomain,Dimension::Type::Unsigned8); m_args->m_dim_src = layout->registerOrAssignDim(m_args->m_srcDomain,Dimension::Type::Unsigned8); } -void RadiusSearchFilter::initialize() +void RadiusAssignFilter::initialize() { if (m_args->m_referenceDomain.empty()) throwError("The reference_domain must be given."); @@ -58,17 +58,17 @@ void RadiusSearchFilter::initialize() throwError("The output_name_attribut must be given."); } -void RadiusSearchFilter::prepared(PointTableRef table) +void RadiusAssignFilter::prepared(PointTableRef table) { PointLayoutPtr layout(table.layout()); } -void RadiusSearchFilter::ready(PointTableRef) +void RadiusAssignFilter::ready(PointTableRef) { m_args->m_ptsToUpdate.clear(); } -void RadiusSearchFilter::doOneNoDomain(PointRef &point) +void RadiusAssignFilter::doOneNoDomain(PointRef &point) { // build3dIndex and build2dIndex are build once PointIdList iNeighbors; @@ -99,7 +99,7 @@ void RadiusSearchFilter::doOneNoDomain(PointRef &point) // update point. kdi and temp both reference the NN point cloud -bool RadiusSearchFilter::doOne(PointRef& point) +bool RadiusAssignFilter::doOne(PointRef& point) { if (m_args->m_srcDomain.empty()) // No domain, process all points doOneNoDomain(point); @@ -108,7 +108,7 @@ bool RadiusSearchFilter::doOne(PointRef& point) return true; } -void RadiusSearchFilter::filter(PointView& view) +void RadiusAssignFilter::filter(PointView& view) { PointRef point_src(view, 0); PointRef temp(view, 0); diff --git a/src/filter_radius_assign/radius_assignFilter.hpp b/src/filter_radius_assign/radius_assignFilter.hpp index edd3527..18bf817 100644 --- a/src/filter_radius_assign/radius_assignFilter.hpp +++ b/src/filter_radius_assign/radius_assignFilter.hpp @@ -4,17 +4,17 @@ #include #include -extern "C" int32_t RadiusSearchFilter_ExitFunc(); -extern "C" PF_ExitFunc RadiusSearchFilter_InitPlugin(); +extern "C" int32_t RadiusAssignFilter_ExitFunc(); +extern "C" PF_ExitFunc RadiusAssignFilter_InitPlugin(); namespace pdal { -class PDAL_DLL RadiusSearchFilter : public Filter +class PDAL_DLL RadiusAssignFilter : public Filter { public: - RadiusSearchFilter(); - ~RadiusSearchFilter(); + RadiusAssignFilter(); + ~RadiusAssignFilter(); static void * create(); static int32_t destroy(void *); @@ -22,7 +22,7 @@ class PDAL_DLL RadiusSearchFilter : public Filter private: - struct RadiusSearchArgs + struct RadiusAssignArgs { std::string m_referenceDomain; std::string m_srcDomain; @@ -34,7 +34,7 @@ class PDAL_DLL RadiusSearchFilter : public Filter Dimension::Id m_dim_ref, m_dim_src; double m_search_bellow, m_search_above; }; - std::unique_ptr m_args; + std::unique_ptr m_args; PointViewPtr refView; virtual void addArgs(ProgramArgs& args); @@ -47,8 +47,8 @@ class PDAL_DLL RadiusSearchFilter : public Filter bool doOne(PointRef& point); void doOneNoDomain(PointRef &point); - RadiusSearchFilter& operator=(const RadiusSearchFilter&) = delete; - RadiusSearchFilter(const RadiusSearchFilter&) = delete; + RadiusAssignFilter& operator=(const RadiusAssignFilter&) = delete; + RadiusAssignFilter(const RadiusAssignFilter&) = delete; }; } // namespace pdal From 20cac64eeb7020668b36c22067da9bd7e36c2247 Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Fri, 17 May 2024 14:54:59 +0200 Subject: [PATCH 23/49] update name attribute --- doc/grid_radius_assign.md | 9 +++++---- macro/macro.py | 2 +- src/filter_radius_assign/radius_assignFilter.cpp | 6 +++--- test/test_radius_assign.py | 6 +++--- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/doc/grid_radius_assign.md b/doc/grid_radius_assign.md index 52fd15c..3812175 100755 --- a/doc/grid_radius_assign.md +++ b/doc/grid_radius_assign.md @@ -20,7 +20,8 @@ This pipeline updates the Keypoint dimension of all points with classification 1 "src_domain" : "Classification[1:2]", "reference_domain" : "Classification[6:6]", "radius" : 1, - "output_name_attribut": "radius" + "output_name_attribut": "radius", + "is3d": True }, "output.las" ] @@ -43,9 +44,9 @@ Options **output_name_attribut**: The name of the new attribute'. [Default: radius] -**3d_search**: Search in 3d (as a ball). [Default: false] +**is3d**: Search in 3d (as a ball). [Default: false] -**2d_search_above**: If 3d_search is false, take points only if exists points between the referent point and a distance above. [Default: 0.] +**is2d_above**: If is3d is false, take points only if exists points between the referent point and a distance above. [Default: 0.] -**2d_search_bellow**: If 3d_search is false, take points only if exists points between the referent point and a distance bellow. [Default: 0.] +**is2d_bellow**: If is3d is false, take points only if exists points between the referent point and a distance bellow. [Default: 0.] diff --git a/macro/macro.py b/macro/macro.py index 809b410..f4f5b5f 100755 --- a/macro/macro.py +++ b/macro/macro.py @@ -19,7 +19,7 @@ def add_radius_assign(pipeline, radius, search_3d, condition_src, condition_ref, "REF_DOMAIN = 0", f"REF_DOMAIN = 1 WHERE {condition_ref}", "radius_search = 0"]) pipeline |= pdal.Filter.radius_assign(radius=radius, src_domain="SRS_DOMAIN",reference_domain="REF_DOMAIN", - output_dimension="radius_search", search_3d=search_3d) + output_dimension="radius_search", is3d=search_3d) pipeline |= pdal.Filter.assign(value=condition_out,where="radius_search==1") return pipeline diff --git a/src/filter_radius_assign/radius_assignFilter.cpp b/src/filter_radius_assign/radius_assignFilter.cpp index 754dfef..4ac5c9a 100644 --- a/src/filter_radius_assign/radius_assignFilter.cpp +++ b/src/filter_radius_assign/radius_assignFilter.cpp @@ -36,9 +36,9 @@ void RadiusAssignFilter::addArgs(ProgramArgs& args) args.add("reference_domain", "Selects which points will be considered as potential neighbors", m_args->m_referenceDomain, "REF_DOMAIN"); args.add("radius", "Distance of neighbors to consult", m_args->m_radius, 1.); args.add("output_dimension", "Name of the added attribut", m_args->m_outputDimension, "radius"); - args.add("search_3d", "Search in 3d", m_args->search3d, false ); - args.add("search_2d_above", "if search in 2d : filter point above the distance", m_args->m_search_bellow, 0.); - args.add("search_2d_bellow", "if search in 2d : filter point bellow the distance", m_args->m_search_above, 0.); + args.add("is3d", "Search in 3d", m_args->search3d, false ); + args.add("is2d_above", "if search in 2d : filter point above the distance", m_args->m_search_bellow, 0.); + args.add("is2d_bellow", "if search in 2d : filter point bellow the distance", m_args->m_search_above, 0.); } void RadiusAssignFilter::addDimensions(PointLayoutPtr layout) diff --git a/test/test_radius_assign.py b/test/test_radius_assign.py index 4bc759b..07a04bb 100755 --- a/test/test_radius_assign.py +++ b/test/test_radius_assign.py @@ -52,9 +52,9 @@ def run_filter(arrays_las, distance_radius, search_3d, distance_cylinder=0. ): "src_domain": "SRS_DOMAIN", "reference_domain": "REF_DOMAIN", "output_dimension": "radius_search", - "search_3d": search_3d, - "search_2d_above": distance_cylinder, - "search_2d_bellow": distance_cylinder, + "is3d": search_3d, + "is2d_above": distance_cylinder, + "is2d_bellow": distance_cylinder, } ] From 6f2a07bfa2d028941bf39e21d0b26411b025a85d Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Fri, 17 May 2024 15:03:30 +0200 Subject: [PATCH 24/49] fix equality --- src/filter_radius_assign/radius_assignFilter.cpp | 6 +++--- test/test_radius_assign.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/filter_radius_assign/radius_assignFilter.cpp b/src/filter_radius_assign/radius_assignFilter.cpp index 4ac5c9a..298f3b1 100644 --- a/src/filter_radius_assign/radius_assignFilter.cpp +++ b/src/filter_radius_assign/radius_assignFilter.cpp @@ -43,7 +43,7 @@ void RadiusAssignFilter::addArgs(ProgramArgs& args) void RadiusAssignFilter::addDimensions(PointLayoutPtr layout) { - m_args->m_dim = layout->registerOrAssignDim(m_args->m_outputDimension, Dimension::Type::Double); + m_args->m_dim = layout->registerOrAssignDim(m_args->m_outputDimension, Dimension::Type::Unsigned8); m_args->m_dim_ref = layout->registerOrAssignDim(m_args->m_referenceDomain,Dimension::Type::Unsigned8); m_args->m_dim_src = layout->registerOrAssignDim(m_args->m_srcDomain,Dimension::Type::Unsigned8); } @@ -87,8 +87,8 @@ void RadiusAssignFilter::doOneNoDomain(PointRef &point) for (PointId ptId : iNeighbors) { double Zpt = refView->point(ptId).getFieldAs(Dimension::Id::Z); - if (m_args->m_search_bellow>0 && Zpt>Zref && (Zpt-Zref)m_search_bellow) {take=true; break;} - if (m_args->m_search_above>0 && Zptm_search_above) {take=true; break;} + if (m_args->m_search_bellow>0 && Zpt>Zref && (Zpt-Zref)<=m_args->m_search_bellow) {take=true; break;} + if (m_args->m_search_above>0 && Zptm_search_above) {take=true; break;} } if (!take) return; } diff --git a/test/test_radius_assign.py b/test/test_radius_assign.py index 07a04bb..33673e8 100755 --- a/test/test_radius_assign.py +++ b/test/test_radius_assign.py @@ -105,7 +105,7 @@ def test_radius_assign_3d(): def func_test(pt_ini, pt): distance_i = distance3d(pt_ini, pt) - if distance_i < distance_radius: + if distance_i <= distance_radius: return 1 return 0 @@ -120,7 +120,7 @@ def test_radius_assign_2d(): def func_test(pt_ini, pt): distance_i = distance2d(pt_ini, pt) - if distance_i < distance_radius: + if distance_i <= distance_radius: return 1 return 0 @@ -136,8 +136,8 @@ def test_radius_assign_2d_cylinder(): def func_test(pt_ini, pt): distance_i = distance2d(pt_ini, pt) - if distance_i < distance_radius: - if abs(pt_ini[2] - pt[2]) < distance_cylinder: + if distance_i <= distance_radius: + if abs(pt_ini[2] - pt[2]) <= distance_cylinder: return 1 return 0 From a2cf0bec5ebf1b07021f7933364b1234f094834e Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Fri, 17 May 2024 15:16:25 +0200 Subject: [PATCH 25/49] fix : name, orthograph, ... --- doc/{grid_radius_assign.md => radius_assign.md} | 4 ++-- macro/macro.py | 2 +- ...d_decimationFilter.cpp => GridDecimationFilter.cpp} | 8 ++++---- ...d_decimationFilter.hpp => GridDecimationFilter.hpp} | 2 +- ...{radius_assignFilter.cpp => RadiusAssignFilter.cpp} | 10 +++++----- ...{radius_assignFilter.hpp => RadiusAssignFilter.hpp} | 2 +- test/test_grid_decimation.py | 2 +- test/test_radius_assign.py | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) rename doc/{grid_radius_assign.md => radius_assign.md} (81%) rename src/filter_grid_decimation/{grid_decimationFilter.cpp => GridDecimationFilter.cpp} (96%) rename src/filter_grid_decimation/{grid_decimationFilter.hpp => GridDecimationFilter.hpp} (95%) rename src/filter_radius_assign/{radius_assignFilter.cpp => RadiusAssignFilter.cpp} (85%) rename src/filter_radius_assign/{radius_assignFilter.hpp => RadiusAssignFilter.hpp} (96%) diff --git a/doc/grid_radius_assign.md b/doc/radius_assign.md similarity index 81% rename from doc/grid_radius_assign.md rename to doc/radius_assign.md index 3812175..521cd40 100755 --- a/doc/grid_radius_assign.md +++ b/doc/radius_assign.md @@ -46,7 +46,7 @@ Options **is3d**: Search in 3d (as a ball). [Default: false] -**is2d_above**: If is3d is false, take points only if exists points between the referent point and a distance above. [Default: 0.] +**is2d_above**: If search in 2d : upward maximum distance in Z for potential neighbors (corresponds to a search in a cylinder with a height = is2d_above above the source point). Default (0) = infinite height [Default: 0.] -**is2d_bellow**: If is3d is false, take points only if exists points between the referent point and a distance bellow. [Default: 0.] +**is2d_below**: If search in 2d : upward maximum distance in Z for potential neighbors (corresponds to a search in a cylinder with a height = is2d_below below the source point). Default (0) = infinite height [Default: 0.] diff --git a/macro/macro.py b/macro/macro.py index f4f5b5f..db638fd 100755 --- a/macro/macro.py +++ b/macro/macro.py @@ -35,7 +35,7 @@ def add_grid_decimation(pipeline, grid_resolution, output_type, condition, condi """ pipeline |= pdal.Filter.ferry(dimensions=f"=>grid,") pipeline |= pdal.Filter.assign(value="grid = 0") - pipeline |= pdal.Filter.grid_decimation(resolution=grid_resolution, output_name_attribut="grid", + pipeline |= pdal.Filter.grid_decimation(resolution=grid_resolution, output_name_attribute="grid", output_type=output_type, where=condition) pipeline |= pdal.Filter.assign(value=condition_out,where=f"grid==0 && ({condition})") return pipeline diff --git a/src/filter_grid_decimation/grid_decimationFilter.cpp b/src/filter_grid_decimation/GridDecimationFilter.cpp similarity index 96% rename from src/filter_grid_decimation/grid_decimationFilter.cpp rename to src/filter_grid_decimation/GridDecimationFilter.cpp index bc771f4..1ece7c0 100755 --- a/src/filter_grid_decimation/grid_decimationFilter.cpp +++ b/src/filter_grid_decimation/GridDecimationFilter.cpp @@ -5,7 +5,7 @@ * ****************************************************************************/ -#include "grid_decimationFilter.hpp" +#include "GridDecimationFilter.hpp" #include #include @@ -39,7 +39,7 @@ void GridDecimationFilter::addArgs(ProgramArgs& args) { args.add("resolution", "Cell edge size, in units of X/Y",m_args->m_edgeLength, 1.); args.add("output_type", "Point keept into the cells ('min', 'max')", m_args->m_methodKeep, "max" ); - args.add("output_name_attribut", "Name of the added attribut", m_args->m_nameAddAttribut, "grid" ); + args.add("output_name_attribute", "Name of the added attribut", m_args->m_nameAddAttribute, "grid" ); args.add("output_wkt", "Export the grid as wkt", m_args->m_nameWktgrid, "" ); } @@ -61,7 +61,7 @@ void GridDecimationFilter::ready(PointTableRef table) if (m_args->m_methodKeep != "max" && m_args->m_methodKeep != "min") throwError("The output_type must be 'max' or 'min'."); - if (m_args->m_nameAddAttribut.empty()) + if (m_args->m_nameAddAttribute.empty()) throwError("The output_name_attribut must be given."); if (!m_args->m_nameWktgrid.empty()) @@ -70,7 +70,7 @@ void GridDecimationFilter::ready(PointTableRef table) void GridDecimationFilter::addDimensions(PointLayoutPtr layout) { - m_args->m_dim = layout->registerOrAssignDim(m_args->m_nameAddAttribut, + m_args->m_dim = layout->registerOrAssignDim(m_args->m_nameAddAttribute, Dimension::Type::Double); } diff --git a/src/filter_grid_decimation/grid_decimationFilter.hpp b/src/filter_grid_decimation/GridDecimationFilter.hpp similarity index 95% rename from src/filter_grid_decimation/grid_decimationFilter.hpp rename to src/filter_grid_decimation/GridDecimationFilter.hpp index 781d8eb..fb5e810 100755 --- a/src/filter_grid_decimation/grid_decimationFilter.hpp +++ b/src/filter_grid_decimation/GridDecimationFilter.hpp @@ -32,7 +32,7 @@ class PDAL_DLL GridDecimationFilter : public Filter { std::string m_methodKeep; // type of output (min, max) double m_edgeLength; // lenght of grid - std::string m_nameAddAttribut; // name of the new attribut + std::string m_nameAddAttribute; // name of the new attribut std::string m_nameWktgrid; // export wkt grid Dimension::Id m_dim; }; diff --git a/src/filter_radius_assign/radius_assignFilter.cpp b/src/filter_radius_assign/RadiusAssignFilter.cpp similarity index 85% rename from src/filter_radius_assign/radius_assignFilter.cpp rename to src/filter_radius_assign/RadiusAssignFilter.cpp index 298f3b1..be4e72b 100644 --- a/src/filter_radius_assign/radius_assignFilter.cpp +++ b/src/filter_radius_assign/RadiusAssignFilter.cpp @@ -1,4 +1,4 @@ -#include "radius_assignFilter.hpp" +#include "RadiusAssignFilter.hpp" #include #include @@ -37,8 +37,8 @@ void RadiusAssignFilter::addArgs(ProgramArgs& args) args.add("radius", "Distance of neighbors to consult", m_args->m_radius, 1.); args.add("output_dimension", "Name of the added attribut", m_args->m_outputDimension, "radius"); args.add("is3d", "Search in 3d", m_args->search3d, false ); - args.add("is2d_above", "if search in 2d : filter point above the distance", m_args->m_search_bellow, 0.); - args.add("is2d_bellow", "if search in 2d : filter point bellow the distance", m_args->m_search_above, 0.); + args.add("is2d_above", "if search in 2d : upward maximum distance in Z for potential neighbors (corresponds to a search in a cylinder with a height = is2d_above above the source point). Default (0) = infinite height", m_args->m_search_above, 0.); + args.add("is2d_below", "if search in 2d : upward maximum distance in Z for potential neighbors (corresponds to a search in a cylinder with a height = is2d_below below the source point). Default (0) = infinite height", m_args->m_search_below, 0.); } void RadiusAssignFilter::addDimensions(PointLayoutPtr layout) @@ -81,13 +81,13 @@ void RadiusAssignFilter::doOneNoDomain(PointRef &point) if (!m_args->search3d) { double Zref = point.getFieldAs(Dimension::Id::Z); - if (m_args->m_search_bellow>0 || m_args->m_search_above>0) + if (m_args->m_search_below>0 || m_args->m_search_above>0) { bool take (false); for (PointId ptId : iNeighbors) { double Zpt = refView->point(ptId).getFieldAs(Dimension::Id::Z); - if (m_args->m_search_bellow>0 && Zpt>Zref && (Zpt-Zref)<=m_args->m_search_bellow) {take=true; break;} + if (m_args->m_search_below>0 && Zpt>Zref && (Zpt-Zref)<=m_args->m_search_below) {take=true; break;} if (m_args->m_search_above>0 && Zptm_search_above) {take=true; break;} } if (!take) return; diff --git a/src/filter_radius_assign/radius_assignFilter.hpp b/src/filter_radius_assign/RadiusAssignFilter.hpp similarity index 96% rename from src/filter_radius_assign/radius_assignFilter.hpp rename to src/filter_radius_assign/RadiusAssignFilter.hpp index 18bf817..3581e34 100644 --- a/src/filter_radius_assign/radius_assignFilter.hpp +++ b/src/filter_radius_assign/RadiusAssignFilter.hpp @@ -32,7 +32,7 @@ class PDAL_DLL RadiusAssignFilter : public Filter Dimension::Id m_dim; bool search3d; Dimension::Id m_dim_ref, m_dim_src; - double m_search_bellow, m_search_above; + double m_search_below, m_search_above; }; std::unique_ptr m_args; PointViewPtr refView; diff --git a/test/test_grid_decimation.py b/test/test_grid_decimation.py index 159d38a..324afba 100755 --- a/test/test_grid_decimation.py +++ b/test/test_grid_decimation.py @@ -31,7 +31,7 @@ def run_filter(type): "type": filter, "resolution": resolution, "output_type": type, - "output_name_attribut": "grid", + "output_name_attribute": "grid", "output_wkt": tmp_out_wkt, }, { diff --git a/test/test_radius_assign.py b/test/test_radius_assign.py index 33673e8..4d9b351 100755 --- a/test/test_radius_assign.py +++ b/test/test_radius_assign.py @@ -54,7 +54,7 @@ def run_filter(arrays_las, distance_radius, search_3d, distance_cylinder=0. ): "output_dimension": "radius_search", "is3d": search_3d, "is2d_above": distance_cylinder, - "is2d_bellow": distance_cylinder, + "is2d_below": distance_cylinder, } ] From 641dfafd44fff0f056c14d16e07b07d18b827383 Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Fri, 17 May 2024 15:21:55 +0200 Subject: [PATCH 26/49] update nom attributes --- doc/grid_decimation.md | 2 +- doc/radius_assign.md | 2 +- macro/macro.py | 2 +- src/filter_grid_decimation/GridDecimationFilter.cpp | 7 +++---- src/filter_grid_decimation/GridDecimationFilter.hpp | 2 +- src/filter_radius_assign/RadiusAssignFilter.cpp | 2 +- test/test_grid_decimation.py | 2 +- 7 files changed, 9 insertions(+), 10 deletions(-) diff --git a/doc/grid_decimation.md b/doc/grid_decimation.md index 573a46b..cd2b37f 100755 --- a/doc/grid_decimation.md +++ b/doc/grid_decimation.md @@ -37,6 +37,6 @@ Options **resolution** : The resolution of the cells in meter. [Default: 1.] -**output_name_attribut**: The name of the new attribut. [Default: grid] +**output_dimension**: The name of the new dimension. [Default: grid] **output_wkt**: the name of the export grid file as wkt polygon. If none, no export [Default:""] diff --git a/doc/radius_assign.md b/doc/radius_assign.md index 521cd40..43c8479 100755 --- a/doc/radius_assign.md +++ b/doc/radius_assign.md @@ -42,7 +42,7 @@ Options **update_expression** : A list of :ref:`assignment expressions ` to be applied to the points that satisfy the radius search. The list of values is evaluated in order. -**output_name_attribut**: The name of the new attribute'. [Default: radius] +**output_dimension**: The name of the new dimension'. [Default: radius] **is3d**: Search in 3d (as a ball). [Default: false] diff --git a/macro/macro.py b/macro/macro.py index db638fd..3403f77 100755 --- a/macro/macro.py +++ b/macro/macro.py @@ -35,7 +35,7 @@ def add_grid_decimation(pipeline, grid_resolution, output_type, condition, condi """ pipeline |= pdal.Filter.ferry(dimensions=f"=>grid,") pipeline |= pdal.Filter.assign(value="grid = 0") - pipeline |= pdal.Filter.grid_decimation(resolution=grid_resolution, output_name_attribute="grid", + pipeline |= pdal.Filter.grid_decimation(resolution=grid_resolution, output_dimension="grid", output_type=output_type, where=condition) pipeline |= pdal.Filter.assign(value=condition_out,where=f"grid==0 && ({condition})") return pipeline diff --git a/src/filter_grid_decimation/GridDecimationFilter.cpp b/src/filter_grid_decimation/GridDecimationFilter.cpp index 1ece7c0..0461f6e 100755 --- a/src/filter_grid_decimation/GridDecimationFilter.cpp +++ b/src/filter_grid_decimation/GridDecimationFilter.cpp @@ -39,7 +39,7 @@ void GridDecimationFilter::addArgs(ProgramArgs& args) { args.add("resolution", "Cell edge size, in units of X/Y",m_args->m_edgeLength, 1.); args.add("output_type", "Point keept into the cells ('min', 'max')", m_args->m_methodKeep, "max" ); - args.add("output_name_attribute", "Name of the added attribut", m_args->m_nameAddAttribute, "grid" ); + args.add("output_dimension", "Name of the added dimension", m_args->m_nameOutDimension, "grid" ); args.add("output_wkt", "Export the grid as wkt", m_args->m_nameWktgrid, "" ); } @@ -61,7 +61,7 @@ void GridDecimationFilter::ready(PointTableRef table) if (m_args->m_methodKeep != "max" && m_args->m_methodKeep != "min") throwError("The output_type must be 'max' or 'min'."); - if (m_args->m_nameAddAttribute.empty()) + if (m_args->m_nameOutDimension.empty()) throwError("The output_name_attribut must be given."); if (!m_args->m_nameWktgrid.empty()) @@ -70,8 +70,7 @@ void GridDecimationFilter::ready(PointTableRef table) void GridDecimationFilter::addDimensions(PointLayoutPtr layout) { - m_args->m_dim = layout->registerOrAssignDim(m_args->m_nameAddAttribute, - Dimension::Type::Double); + m_args->m_dim = layout->registerOrAssignDim(m_args->m_nameOutDimension, Dimension::Type::Double); } void GridDecimationFilter::processOne(BOX2D bounds, PointRef& point, PointViewPtr view) diff --git a/src/filter_grid_decimation/GridDecimationFilter.hpp b/src/filter_grid_decimation/GridDecimationFilter.hpp index fb5e810..231a6a1 100755 --- a/src/filter_grid_decimation/GridDecimationFilter.hpp +++ b/src/filter_grid_decimation/GridDecimationFilter.hpp @@ -32,7 +32,7 @@ class PDAL_DLL GridDecimationFilter : public Filter { std::string m_methodKeep; // type of output (min, max) double m_edgeLength; // lenght of grid - std::string m_nameAddAttribute; // name of the new attribut + std::string m_nameOutDimension; // name of the new dimension std::string m_nameWktgrid; // export wkt grid Dimension::Id m_dim; }; diff --git a/src/filter_radius_assign/RadiusAssignFilter.cpp b/src/filter_radius_assign/RadiusAssignFilter.cpp index be4e72b..c2cff07 100644 --- a/src/filter_radius_assign/RadiusAssignFilter.cpp +++ b/src/filter_radius_assign/RadiusAssignFilter.cpp @@ -35,7 +35,7 @@ void RadiusAssignFilter::addArgs(ProgramArgs& args) args.add("src_domain", "Selects which points will be subject to radius-based neighbors search", m_args->m_srcDomain, "SRC_DOMAIN"); args.add("reference_domain", "Selects which points will be considered as potential neighbors", m_args->m_referenceDomain, "REF_DOMAIN"); args.add("radius", "Distance of neighbors to consult", m_args->m_radius, 1.); - args.add("output_dimension", "Name of the added attribut", m_args->m_outputDimension, "radius"); + args.add("output_dimension", "Name of the added dimension", m_args->m_outputDimension, "radius"); args.add("is3d", "Search in 3d", m_args->search3d, false ); args.add("is2d_above", "if search in 2d : upward maximum distance in Z for potential neighbors (corresponds to a search in a cylinder with a height = is2d_above above the source point). Default (0) = infinite height", m_args->m_search_above, 0.); args.add("is2d_below", "if search in 2d : upward maximum distance in Z for potential neighbors (corresponds to a search in a cylinder with a height = is2d_below below the source point). Default (0) = infinite height", m_args->m_search_below, 0.); diff --git a/test/test_grid_decimation.py b/test/test_grid_decimation.py index 324afba..947a43a 100755 --- a/test/test_grid_decimation.py +++ b/test/test_grid_decimation.py @@ -31,7 +31,7 @@ def run_filter(type): "type": filter, "resolution": resolution, "output_type": type, - "output_name_attribute": "grid", + "output_dimension": "grid", "output_wkt": tmp_out_wkt, }, { From c18abdbd32b4ad354c29eb6d60bc5b0c673428ec Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Fri, 17 May 2024 15:23:14 +0200 Subject: [PATCH 27/49] floor => ceil --- src/filter_grid_decimation/GridDecimationFilter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/filter_grid_decimation/GridDecimationFilter.cpp b/src/filter_grid_decimation/GridDecimationFilter.cpp index 0461f6e..5f6fec9 100755 --- a/src/filter_grid_decimation/GridDecimationFilter.cpp +++ b/src/filter_grid_decimation/GridDecimationFilter.cpp @@ -107,8 +107,8 @@ void GridDecimationFilter::processOne(BOX2D bounds, PointRef& point, PointViewPt void GridDecimationFilter::createGrid(BOX2D bounds) { - double d_width = std::floor((bounds.maxx - bounds.minx) / m_args->m_edgeLength) + 1; - double d_height = std::floor((bounds.maxy - bounds.miny) / m_args->m_edgeLength) + 1; + double d_width = std::ceil((bounds.maxx - bounds.minx) / m_args->m_edgeLength); + double d_height = std::ceil((bounds.maxy - bounds.miny) / m_args->m_edgeLength); if (d_width < 0.0 || d_width > (std::numeric_limits::max)()) throwError("Grid width out of range."); From 4c94f7734a3795a7adcb672277f0f7d6ca76a62d Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Fri, 17 May 2024 16:01:48 +0200 Subject: [PATCH 28/49] update macro --- macro/macro.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/macro/macro.py b/macro/macro.py index 3403f77..219ed58 100755 --- a/macro/macro.py +++ b/macro/macro.py @@ -33,8 +33,6 @@ def add_grid_decimation(pipeline, grid_resolution, output_type, condition, condi output_type : "max" or "min" (the highest or lower points of the grid) condition, condition_out : a pdal condition as "Classification==2" """ - pipeline |= pdal.Filter.ferry(dimensions=f"=>grid,") - pipeline |= pdal.Filter.assign(value="grid = 0") pipeline |= pdal.Filter.grid_decimation(resolution=grid_resolution, output_dimension="grid", output_type=output_type, where=condition) pipeline |= pdal.Filter.assign(value=condition_out,where=f"grid==0 && ({condition})") From 0559a0b3125062ec4a0d25b9aa929a02414e135f Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Fri, 17 May 2024 17:27:46 +0200 Subject: [PATCH 29/49] deprectated grid_decimation --- doc/grid_decimation.md | 2 ++ macro/ex_filtering_points.py | 6 +++--- macro/macro.py | 16 ---------------- .../GridDecimationFilter.cpp | 4 ++-- 4 files changed, 7 insertions(+), 21 deletions(-) diff --git a/doc/grid_decimation.md b/doc/grid_decimation.md index cd2b37f..9ce662f 100755 --- a/doc/grid_decimation.md +++ b/doc/grid_decimation.md @@ -1,5 +1,7 @@ # filter grid decimation +**Deprecated** : *better use the gridDecimation filter of pdal > 2.7* + Purpose --------------------------------------------------------------------------------------------------------- diff --git a/macro/ex_filtering_points.py b/macro/ex_filtering_points.py index 403c26f..6bd34cc 100755 --- a/macro/ex_filtering_points.py +++ b/macro/ex_filtering_points.py @@ -22,11 +22,11 @@ def parse_args(): pipeline = macro.add_radius_assign(pipeline, 1, False, "Classification==2", macro.build_condition("Classification", [4,5]), "Classification=102") pipeline = macro.add_radius_assign(pipeline, 1, False, "Classification==102", "Classification==2", "Classification=2") pipeline = macro.add_radius_assign(pipeline, 1, False, "Classification==3", "Classification==5", "Classification=103") - pipeline = macro.add_grid_decimation(pipeline, 0.75, "max", macro.build_condition("Classification", [4,5,102,103]), "Classification=100") + pipeline |= pdal.Filter.gridDecimation(resolution=0.75, value="Classification=100", output_type="max", where=macro.build_condition("Classification", [4,5,102,103])) pipeline |= pdal.Filter.assign(value="Classification=2", where="Classification==102") pipeline |= pdal.Filter.assign(value="Classification=3", where="Classification==103") - pipeline = macro.add_grid_decimation(pipeline, 0.5, "max", "Classification==2", "Classification=102") - pipeline = macro.add_grid_decimation(pipeline, 0.5, "max", macro.build_condition("Classification", [2,3,4,5,6,9,17,64,100]), "Classification=200") + pipeline |= pdal.Filter.gridDecimation(resolution=0.5, value="Classification=102", output_type="max", where="Classification==2") + pipeline |= pdal.Filter.gridDecimation(resolution=0.5, value="Classification=200", output_type="max", where=macro.build_condition("Classification", [2,3,4,5,6,9,17,64,100])) pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==102", macro.build_condition("Classification", [4,5,6,9,17,64,100]), "Classification=100") pipeline |= pdal.Filter.assign(value="Classification=2", where="Classification==102") diff --git a/macro/macro.py b/macro/macro.py index 219ed58..fa50364 100755 --- a/macro/macro.py +++ b/macro/macro.py @@ -24,22 +24,6 @@ def add_radius_assign(pipeline, radius, search_3d, condition_src, condition_ref, return pipeline - -def add_grid_decimation(pipeline, grid_resolution, output_type, condition, condition_out): - """ - Select a points in a grid from "condition"; points not selected are reassign to "condition_out" - This combination is equivalent to the Thin Points macro of TerraScan - grid_resolution : resolution of the grid - output_type : "max" or "min" (the highest or lower points of the grid) - condition, condition_out : a pdal condition as "Classification==2" - """ - pipeline |= pdal.Filter.grid_decimation(resolution=grid_resolution, output_dimension="grid", - output_type=output_type, where=condition) - pipeline |= pdal.Filter.assign(value=condition_out,where=f"grid==0 && ({condition})") - return pipeline - - - def classify_hgt_ground(pipeline, hmin, hmax, condition, condition_out): """ reassign points from "condition" between "hmin" and "hmax" of the ground to "condition_out" diff --git a/src/filter_grid_decimation/GridDecimationFilter.cpp b/src/filter_grid_decimation/GridDecimationFilter.cpp index 5f6fec9..a0d82e7 100755 --- a/src/filter_grid_decimation/GridDecimationFilter.cpp +++ b/src/filter_grid_decimation/GridDecimationFilter.cpp @@ -80,8 +80,8 @@ void GridDecimationFilter::processOne(BOX2D bounds, PointRef& point, PointViewPt double y = point.getFieldAs(Dimension::Id::Y); int id = point.getFieldAs(Dimension::Id::PointId); - double d_width_pt = std::floor((x - bounds.minx) / m_args->m_edgeLength); - double d_height_pt = std::floor((y - bounds.miny) / m_args->m_edgeLength); + double d_width_pt = std::ceil((x - bounds.minx) / m_args->m_edgeLength); + double d_height_pt = std::ceil((y - bounds.miny) / m_args->m_edgeLength); int width = static_cast(d_width_pt); int height = static_cast(d_height_pt); From cecf78e08faf43ea822e170bca5fa861acc15949 Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Fri, 17 May 2024 17:31:15 +0200 Subject: [PATCH 30/49] fix test --- test/test_grid_decimation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_grid_decimation.py b/test/test_grid_decimation.py index 947a43a..ea21a79 100755 --- a/test/test_grid_decimation.py +++ b/test/test_grid_decimation.py @@ -54,7 +54,7 @@ def run_filter(type): if pt["grid"] > 0: nb_pts_grid += 1 - assert nb_pts_grid == 3234 + assert nb_pts_grid == 3231 assert nb_pts_grid <= nb_dalle data = [] From 9ed4361d7e6bc396b6a1132b9d8665c0ade6cf2f Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Fri, 17 May 2024 17:37:22 +0200 Subject: [PATCH 31/49] fix doc --- doc/radius_assign.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/doc/radius_assign.md b/doc/radius_assign.md index 43c8479..f5dc42f 100755 --- a/doc/radius_assign.md +++ b/doc/radius_assign.md @@ -20,7 +20,7 @@ This pipeline updates the Keypoint dimension of all points with classification 1 "src_domain" : "Classification[1:2]", "reference_domain" : "Classification[6:6]", "radius" : 1, - "output_name_attribut": "radius", + "output_dimension": "radius", "is3d": True }, "output.las" @@ -39,9 +39,6 @@ Options **radius** : An positive float which specifies the radius for the neighbors search. -**update_expression** : - A list of :ref:`assignment expressions ` to be applied to the points that satisfy the radius search. The list of values is evaluated in order. - **output_dimension**: The name of the new dimension'. [Default: radius] **is3d**: Search in 3d (as a ball). [Default: false] From 986a2087bcf8e4b87ba27a3e1da528d55a1a1916 Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Tue, 21 May 2024 09:36:58 +0200 Subject: [PATCH 32/49] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 89fda41..3f811c2 100755 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ python -m pytest -s ## List of Filters [grid decimation](./doc/grid_decimation.md) +[radius assign](./doc/radius_assign.md) ## Adding a filter From fef4da0dbf35edb0138c7280af6d84e774de6f49 Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Tue, 21 May 2024 11:05:20 +0200 Subject: [PATCH 33/49] fix readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3f811c2..c98a2f5 100755 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ python -m pytest -s ## List of Filters [grid decimation](./doc/grid_decimation.md) + [radius assign](./doc/radius_assign.md) ## Adding a filter From c436a0c02d2612a6e801eb68ea8d8c09ddf7965c Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Wed, 22 May 2024 16:42:26 +0200 Subject: [PATCH 34/49] add comment to example + try to recode example --- macro/ex_filtering_points.py | 83 +++++++++++++++++++++++++---------- macro/ex_filtering_points2.py | 72 ++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 22 deletions(-) create mode 100755 macro/ex_filtering_points2.py diff --git a/macro/ex_filtering_points.py b/macro/ex_filtering_points.py index 6bd34cc..4f011ab 100755 --- a/macro/ex_filtering_points.py +++ b/macro/ex_filtering_points.py @@ -9,7 +9,9 @@ def parse_args(): parser = argparse.ArgumentParser("Tool to apply pdal pipelines to modify classification") parser.add_argument("--input", "-i", type=str, required=True, help="Input las file") - parser.add_argument("--output", "-o", type=str, required=True, help="Output las file") + parser.add_argument("--output_las", "-o", type=str, required=True, help="Output cloud las file") + parser.add_argument("--output_dsm", "-s", type=str, required=True, help="Output dsm tiff file") + parser.add_argument("--output_dtm", "-t", type=str, required=True, help="Output dtm tiff file") return parser.parse_args() @@ -18,38 +20,75 @@ def parse_args(): pipeline = pdal.Reader.las(args.input) - # step 1 a 9 - pipeline = macro.add_radius_assign(pipeline, 1, False, "Classification==2", macro.build_condition("Classification", [4,5]), "Classification=102") - pipeline = macro.add_radius_assign(pipeline, 1, False, "Classification==102", "Classification==2", "Classification=2") - pipeline = macro.add_radius_assign(pipeline, 1, False, "Classification==3", "Classification==5", "Classification=103") + ## 1 - recherche des points max de végétation (4,5) sur une grille régulière, avec prise en compte des points sol (2) et basse + ## vegetation (3) proche de la végétation : on les affecte en 100 + + # bouche trou : assigne les points sol en 102 à l'intérieur de la veget (4,5) + pipeline = macro.add_radius_assign(pipeline, 1, False, condition_src="Classification==2", condition_ref=macro.build_condition("Classification", [4,5]), condition_out="Classification=102") + pipeline = macro.add_radius_assign(pipeline, 1, False, condition_src="Classification==102", condition_ref="Classification==2", condition_out="Classification=2") + + # selection des points de veget basse proche de la veget haute : assigne 103 + pipeline = macro.add_radius_assign(pipeline, 1, False, condition_src="Classification==3", condition_ref="Classification==5", condition_out="Classification=103") + + # max des points de veget (et surement veget - 102,103) sur une grille régulière : assigne 100 pipeline |= pdal.Filter.gridDecimation(resolution=0.75, value="Classification=100", output_type="max", where=macro.build_condition("Classification", [4,5,102,103])) + + # remise à zero des codes 102 et 103 pipeline |= pdal.Filter.assign(value="Classification=2", where="Classification==102") pipeline |= pdal.Filter.assign(value="Classification=3", where="Classification==103") + + ## 2 - sélection des points pour DTM et DSM + + # selection de points DTM (max) sur une grille régulière pipeline |= pdal.Filter.gridDecimation(resolution=0.5, value="Classification=102", output_type="max", where="Classification==2") + + # selection de points DSM (max) sur une grille régulière pipeline |= pdal.Filter.gridDecimation(resolution=0.5, value="Classification=200", output_type="max", where=macro.build_condition("Classification", [2,3,4,5,6,9,17,64,100])) - pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==102", macro.build_condition("Classification", [4,5,6,9,17,64,100]), "Classification=100") + + # assigne des points sol sélectionnés (102) en 100 : les points proches de la végaétation, des ponts, de l'eau et 64 + pipeline = macro.add_radius_assign(pipeline, 1.5, False, condition_src="Classification==102", + condition_ref=macro.build_condition("Classification", [4,5,6,9,17,64,100]), condition_out="Classification=100") + + # remise à zero du code 102 pipeline |= pdal.Filter.assign(value="Classification=2", where="Classification==102") - # step 10 - pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==2", "Classification==17", "Classification=102") - pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==102", macro.build_condition("Classification", [2,3,4,5]), "Classification=2") + ## 3 - gestion des ponts + + + + # bouche trou : on élimine les points sol (2) au milieu du pont en les mettant à 102 + pipeline = macro.add_radius_assign(pipeline, 1.5, False, condition_src="Classification==2", condition_ref="Classification==17", condition_out="Classification=102") + pipeline = macro.add_radius_assign(pipeline, 1.5, False, condition_src="Classification==102", + condition_ref=macro.build_condition("Classification", [2,3,4,5]), condition_out="Classification=2") + + # bouche trou : on élimine les points basse végétation (3) au milieu du pont en les mettant à 103 + pipeline = macro.add_radius_assign(pipeline, 1.5, False, condition_src="Classification==3", condition_ref="Classification==17", condition_out="Classification=103") + pipeline = macro.add_radius_assign(pipeline, 1.5, False, condition_src="Classification==103", + condition_ref=macro.build_condition("Classification", [2,3,4,5]), condition_out="Classification=3") + + # bouche trou : on élimine les points moyenne végétation (4) au milieu du pont en les mettant à 104 + pipeline = macro.add_radius_assign(pipeline, 1.5, False, condition_src="Classification==4", condition_ref="Classification==17", condition_out="Classification=104") + pipeline = macro.add_radius_assign(pipeline, 1.5, False, condition_src="Classification==104", + condition_ref=macro.build_condition("Classification", [2,3,4,5]), condition_out="Classification=4") + + # bouche trou : on élimine les points haute végétation (5) au milieu du pont en les mettant à 105 + pipeline = macro.add_radius_assign(pipeline, 1.5, False, condition_src="Classification==5", condition_ref="Classification==17", condition_out="Classification=105") + pipeline = macro.add_radius_assign(pipeline, 1.5, False, condition_src="Classification==105", + condition_ref=macro.build_condition("Classification", [2,3,4,5]), condition_out="Classification=5") - # step 11 - pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==3", "Classification==17", "Classification=103") - pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==103", macro.build_condition("Classification", [2,3,4,5]), "Classification=3") + # bouche trou : on élimine les points eau (9) au milieu du pont en les mettant à 109 + pipeline = macro.add_radius_assign(pipeline, 1.5, False, condition_src="Classification==9", condition_ref="Classification==17", condition_out="Classification=109") + pipeline = macro.add_radius_assign(pipeline, 1.5, False, condition_src="Classification==109", + condition_ref="Classification==9", condition_out="Classification=9") - # step 12 - pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==4", "Classification==17", "Classification=104") - pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==104", macro.build_condition("Classification", [2,3,4,5]), "Classification=4") + # step 15 et supression des points ?? - # step 13 - pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==5", "Classification==17", "Classification=105") - pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==105", macro.build_condition("Classification", [2,3,4,5]), "Classification=5") + # 4 - export du nuage + pipeline |= pdal.Writer.las(extra_dims="all",minor_version=4,dataformat_id=6,filename=args.output_las) - # step 14 - pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==9", "Classification==17", "Classification=109") - pipeline = macro.add_radius_assign(pipeline, 1.5, False, "Classification==109", "Classification==9", "Classification=9") + # export des DSM/DTM + pipeline |= pdal.Writer.gdal(gdaldriver="GTiff", output_type="max", resolution=2.0, filename=args.output_dtm, where=macro.build_condition("Classification", [2,66])) + pipeline |= pdal.Writer.gdal(gdaldriver="GTiff", output_type="max", resolution=2.0, filename=args.output_dsm, where=macro.build_condition("Classification", [2,3,4,5,5,17,64])) - pipeline |= pdal.Writer.las(extra_dims="all",minor_version=4,dataformat_id=6,filename=args.output) pipeline.execute() diff --git a/macro/ex_filtering_points2.py b/macro/ex_filtering_points2.py new file mode 100755 index 0000000..007ec08 --- /dev/null +++ b/macro/ex_filtering_points2.py @@ -0,0 +1,72 @@ +import argparse +import pdal +import macro + +""" +This tool shows how to use functions of macro in a pdal pipeline +""" + +def parse_args(): + parser = argparse.ArgumentParser("Tool to apply pdal pipelines to modify classification") + parser.add_argument("--input", "-i", type=str, required=True, help="Input las file") + parser.add_argument("--output_las", "-o", type=str, required=True, help="Output cloud las file") + parser.add_argument("--output_dsm", "-s", type=str, required=True, help="Output dsm tiff file") + parser.add_argument("--output_dtm", "-t", type=str, required=True, help="Output dtm tiff file") + return parser.parse_args() + + +if __name__ == "__main__": + args = parse_args() + + pipeline = pdal.Reader.las(args.input) + + ## 1 - recherche des points max de végétation (4,5) sur une grille régulière, avec prise en compte des points sol (2) et basse + ## vegetation (3) proche de la végétation + ## pour le calcul du DSM + + pipeline |= pdal.Filter.ferry(dimensions=f"=>PT_GRID_DSM, =>PT_DSM, =>PT_GRID_DTM") + pipeline |= pdal.Filter.assign(value=["PT_DSM = 1 WHERE " + macro.build_condition("Classification", [4,5])]) + + # bouche trou : assigne les points sol à l'intérieur de la veget (4,5) + pipeline = macro.add_radius_assign(pipeline, 1, False, condition_src="Classification==2", condition_ref=macro.build_condition("Classification", [4,5]), condition_out="PT_DSM=1") + pipeline = macro.add_radius_assign(pipeline, 1, False, condition_src="PT_DSM==1 && Classification==2", condition_ref="Classification==2", condition_out="Classification=2") + + # selection des points de veget basse proche de la veget haute + pipeline = macro.add_radius_assign(pipeline, 1, False, condition_src="Classification==3", condition_ref="Classification==5", condition_out="PT_DSM=1") + + # max des points de veget (et surement veget) sur une grille régulière : + pipeline |= pdal.Filter.gridDecimation(resolution=0.75, value="PT_GRID_DSM=1", output_type="max", where="PT_DSM==1") + + + ## 2 - sélection des points pour DTM et DSM + + # selection de points DTM (max) sur une grille régulière + pipeline |= pdal.Filter.gridDecimation(resolution=0.5, value="PT_GRID_DTM=1", output_type="max", where="Classification==2") + + # selection de points DSM (max) sur une grille régulière + pipeline |= pdal.Filter.gridDecimation(resolution=0.5, value="PT_GRID_DSM=1", output_type="max", where=macro.build_condition("Classification", [2,3,4,5,6,9,17,64,100])) + + # assigne des points sol sélectionnés : les points proches de la végétation, des ponts, de l'eau, 64 et 100 + pipeline = macro.add_radius_assign(pipeline, 1.5, False, condition_src="PT_GRID_DTM==1", + condition_ref= "(" + macro.build_condition("Classification", [4,5,6,9,17,64]) + ") && PT_GRID_DSM==1", + condition_out="PT_DSM=1") + + + ## 3 - gestion des ponts + # bouche trou : on filtre les points (2,3,4,5,9) au milieu du pont en les mettant à PT_ON_BRIDGE=1 + pipeline |= pdal.Filter.ferry(dimensions=f"=>PT_ON_BRIDGE") + pipeline = macro.add_radius_assign(pipeline, 1.5, False, condition_src=macro.build_condition("Classification", [2,3,4,5,9]), condition_ref="Classification==17", condition_out="PT_ON_BRIDGE=1") + pipeline = macro.add_radius_assign(pipeline, 1.5, False, condition_src="PT_ON_BRIDGE==1", + condition_ref=macro.build_condition("Classification", [2,3,4,5]), condition_out="PT_ON_BRIDGE=0") + + # step 15 et supression des points ?? + + # 4 - export du nuage + pipeline |= pdal.Writer.las(extra_dims="all", minor_version=4, dataformat_id=6, filename=args.output_las) + + # export des DSM/DTM + pipeline |= pdal.Writer.gdal(gdaldriver="GTiff", output_type="max", resolution=2.0, filename=args.output_dtm, where=macro.build_condition("Classification", [2,66])) + pipeline |= pdal.Writer.gdal(gdaldriver="GTiff", output_type="max", resolution=2.0, filename=args.output_dsm, where=macro.build_condition("Classification", [2,3,4,5,5,17,64])) + + pipeline.execute() + From 55da6b1065b8cc75772b58dc48988479ea0ccecc Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Fri, 24 May 2024 10:26:58 +0200 Subject: [PATCH 35/49] V.0 script example --- macro/ex_filtering_points.py | 4 ++-- macro/ex_filtering_points2.py | 39 +++++++++++++++++++---------------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/macro/ex_filtering_points.py b/macro/ex_filtering_points.py index 4f011ab..72b267d 100755 --- a/macro/ex_filtering_points.py +++ b/macro/ex_filtering_points.py @@ -39,7 +39,7 @@ def parse_args(): ## 2 - sélection des points pour DTM et DSM - # selection de points DTM (max) sur une grille régulière + # selection de points sol (max) sur une grille régulière pipeline |= pdal.Filter.gridDecimation(resolution=0.5, value="Classification=102", output_type="max", where="Classification==2") # selection de points DSM (max) sur une grille régulière @@ -88,7 +88,7 @@ def parse_args(): # export des DSM/DTM pipeline |= pdal.Writer.gdal(gdaldriver="GTiff", output_type="max", resolution=2.0, filename=args.output_dtm, where=macro.build_condition("Classification", [2,66])) - pipeline |= pdal.Writer.gdal(gdaldriver="GTiff", output_type="max", resolution=2.0, filename=args.output_dsm, where=macro.build_condition("Classification", [2,3,4,5,5,17,64])) + pipeline |= pdal.Writer.gdal(gdaldriver="GTiff", output_type="max", resolution=2.0, filename=args.output_dsm, where=macro.build_condition("Classification", [2,3,4,5,17,64])) pipeline.execute() diff --git a/macro/ex_filtering_points2.py b/macro/ex_filtering_points2.py index 007ec08..23d86f4 100755 --- a/macro/ex_filtering_points2.py +++ b/macro/ex_filtering_points2.py @@ -20,22 +20,25 @@ def parse_args(): pipeline = pdal.Reader.las(args.input) + # 0 - ajout d'attributs temporaires + pipeline |= pdal.Filter.ferry(dimensions=f"=>PT_GRID_DSM, =>PT_VEG_DSM, =>PT_GRID_DTM, =>PT_ON_BRIDGE") + + ## 1 - recherche des points max de végétation (4,5) sur une grille régulière, avec prise en compte des points sol (2) et basse ## vegetation (3) proche de la végétation ## pour le calcul du DSM - pipeline |= pdal.Filter.ferry(dimensions=f"=>PT_GRID_DSM, =>PT_DSM, =>PT_GRID_DTM") - pipeline |= pdal.Filter.assign(value=["PT_DSM = 1 WHERE " + macro.build_condition("Classification", [4,5])]) + pipeline |= pdal.Filter.assign(value=["PT_VEG_DSM = 1 WHERE " + macro.build_condition("Classification", [4,5])]) # bouche trou : assigne les points sol à l'intérieur de la veget (4,5) - pipeline = macro.add_radius_assign(pipeline, 1, False, condition_src="Classification==2", condition_ref=macro.build_condition("Classification", [4,5]), condition_out="PT_DSM=1") - pipeline = macro.add_radius_assign(pipeline, 1, False, condition_src="PT_DSM==1 && Classification==2", condition_ref="Classification==2", condition_out="Classification=2") + pipeline = macro.add_radius_assign(pipeline, 1, False, condition_src="Classification==2", condition_ref=macro.build_condition("Classification", [4,5]), condition_out="PT_VEG_DSM=1") + pipeline = macro.add_radius_assign(pipeline, 1, False, condition_src="PT_VEG_DSM==1 && Classification==2", condition_ref="Classification==2", condition_out="PT_VEG_DSM=0") # selection des points de veget basse proche de la veget haute - pipeline = macro.add_radius_assign(pipeline, 1, False, condition_src="Classification==3", condition_ref="Classification==5", condition_out="PT_DSM=1") + pipeline = macro.add_radius_assign(pipeline, 1, False, condition_src="Classification==3", condition_ref="Classification==5", condition_out="PT_VEG_DSM=1") - # max des points de veget (et surement veget) sur une grille régulière : - pipeline |= pdal.Filter.gridDecimation(resolution=0.75, value="PT_GRID_DSM=1", output_type="max", where="PT_DSM==1") + # max des points de veget (PT_VEG_DSM==1) sur une grille régulière : + pipeline |= pdal.Filter.gridDecimation(resolution=0.75, value="PT_GRID_DSM=1", output_type="max", where="PT_VEG_DSM==1") ## 2 - sélection des points pour DTM et DSM @@ -44,29 +47,29 @@ def parse_args(): pipeline |= pdal.Filter.gridDecimation(resolution=0.5, value="PT_GRID_DTM=1", output_type="max", where="Classification==2") # selection de points DSM (max) sur une grille régulière - pipeline |= pdal.Filter.gridDecimation(resolution=0.5, value="PT_GRID_DSM=1", output_type="max", where=macro.build_condition("Classification", [2,3,4,5,6,9,17,64,100])) + pipeline |= pdal.Filter.gridDecimation(resolution=0.5, value="PT_GRID_DSM=1", output_type="max", + where="(" + macro.build_condition("Classification", [2,3,4,5,6,9,17,64,100]) + ") || PT_GRID_DSM==1") - # assigne des points sol sélectionnés : les points proches de la végétation, des ponts, de l'eau, 64 et 100 + # assigne des points sol sélectionnés : les points proches de la végétation, des ponts, de l'eau, 64 pipeline = macro.add_radius_assign(pipeline, 1.5, False, condition_src="PT_GRID_DTM==1", - condition_ref= "(" + macro.build_condition("Classification", [4,5,6,9,17,64]) + ") && PT_GRID_DSM==1", - condition_out="PT_DSM=1") + condition_ref=macro.build_condition("Classification", [4,5,6,9,17,64]), + condition_out="PT_GRID_DSM=1") ## 3 - gestion des ponts # bouche trou : on filtre les points (2,3,4,5,9) au milieu du pont en les mettant à PT_ON_BRIDGE=1 - pipeline |= pdal.Filter.ferry(dimensions=f"=>PT_ON_BRIDGE") + pipeline = macro.add_radius_assign(pipeline, 1.5, False, condition_src=macro.build_condition("Classification", [2,3,4,5,9]), condition_ref="Classification==17", condition_out="PT_ON_BRIDGE=1") pipeline = macro.add_radius_assign(pipeline, 1.5, False, condition_src="PT_ON_BRIDGE==1", condition_ref=macro.build_condition("Classification", [2,3,4,5]), condition_out="PT_ON_BRIDGE=0") + pipeline |= pdal.Filter.assign(value=["PT_GRID_DSM = 0 WHERE " + macro.build_condition("Classification", [2,3,4,5,9]) + " && PT_ON_BRIDGE==1"]) - # step 15 et supression des points ?? - # 4 - export du nuage - pipeline |= pdal.Writer.las(extra_dims="all", minor_version=4, dataformat_id=6, filename=args.output_las) + # 4 - export du nuage et des DSM - # export des DSM/DTM - pipeline |= pdal.Writer.gdal(gdaldriver="GTiff", output_type="max", resolution=2.0, filename=args.output_dtm, where=macro.build_condition("Classification", [2,66])) - pipeline |= pdal.Writer.gdal(gdaldriver="GTiff", output_type="max", resolution=2.0, filename=args.output_dsm, where=macro.build_condition("Classification", [2,3,4,5,5,17,64])) + pipeline |= pdal.Writer.las(extra_dims="all", minor_version=4, dataformat_id=6, filename=args.output_las) + pipeline |= pdal.Writer.gdal(gdaldriver="GTiff", output_type="max", resolution=2.0, filename=args.output_dtm, where="PT_GRID_DTM==1") + pipeline |= pdal.Writer.gdal(gdaldriver="GTiff", output_type="max", resolution=2.0, filename=args.output_dsm, where="PT_GRID_DTM==1 || PT_GRID_DSM==1") pipeline.execute() From 179b90b6a7ecd636b77198eba08a7342ca9e80c2 Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Wed, 29 May 2024 09:24:48 +0200 Subject: [PATCH 36/49] update macro orthogaph --- macro/ex_filtering_points2.py | 2 +- macro/macro.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/macro/ex_filtering_points2.py b/macro/ex_filtering_points2.py index 23d86f4..71de88d 100755 --- a/macro/ex_filtering_points2.py +++ b/macro/ex_filtering_points2.py @@ -48,7 +48,7 @@ def parse_args(): # selection de points DSM (max) sur une grille régulière pipeline |= pdal.Filter.gridDecimation(resolution=0.5, value="PT_GRID_DSM=1", output_type="max", - where="(" + macro.build_condition("Classification", [2,3,4,5,6,9,17,64,100]) + ") || PT_GRID_DSM==1") + where="(" + macro.build_condition("Classification", [2,3,4,5,6,9,17,64]) + ") || PT_GRID_DSM==1") # assigne des points sol sélectionnés : les points proches de la végétation, des ponts, de l'eau, 64 pipeline = macro.add_radius_assign(pipeline, 1.5, False, condition_src="PT_GRID_DTM==1", diff --git a/macro/macro.py b/macro/macro.py index fa50364..71d3684 100755 --- a/macro/macro.py +++ b/macro/macro.py @@ -14,11 +14,11 @@ def add_radius_assign(pipeline, radius, search_3d, condition_src, condition_ref, search_3d : the distance reseach is in 3d if True condition_src, condition_ref, condition_out : a pdal condition as "Classification==2" """ - pipeline |= pdal.Filter.ferry(dimensions=f"=>REF_DOMAIN, =>SRS_DOMAIN, =>radius_search") - pipeline |= pdal.Filter.assign(value=["SRS_DOMAIN = 0", f"SRS_DOMAIN = 1 WHERE {condition_src}", + pipeline |= pdal.Filter.ferry(dimensions=f"=>REF_DOMAIN, =>SRC_DOMAIN, =>radius_search") + pipeline |= pdal.Filter.assign(value=["SRS_DOMAIN = 0", f"SRC_DOMAIN = 1 WHERE {condition_src}", "REF_DOMAIN = 0", f"REF_DOMAIN = 1 WHERE {condition_ref}", "radius_search = 0"]) - pipeline |= pdal.Filter.radius_assign(radius=radius, src_domain="SRS_DOMAIN",reference_domain="REF_DOMAIN", + pipeline |= pdal.Filter.radius_assign(radius=radius, src_domain="SRC_DOMAIN",reference_domain="REF_DOMAIN", output_dimension="radius_search", is3d=search_3d) pipeline |= pdal.Filter.assign(value=condition_out,where="radius_search==1") return pipeline From b21732428129cecf66548c3e02a428a4e26d85f0 Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Wed, 29 May 2024 09:27:14 +0200 Subject: [PATCH 37/49] fix script orthograph --- test/test_radius_assign.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_radius_assign.py b/test/test_radius_assign.py index 4d9b351..403bbf6 100755 --- a/test/test_radius_assign.py +++ b/test/test_radius_assign.py @@ -31,7 +31,7 @@ def run_filter(arrays_las, distance_radius, search_3d, distance_cylinder=0. ): {"type": "readers.las", "filename": las.name}, { "type": "filters.ferry", - "dimensions": "=>SRS_DOMAIN" + "dimensions": "=>SRC_DOMAIN" }, { "type": "filters.ferry", @@ -49,7 +49,7 @@ def run_filter(arrays_las, distance_radius, search_3d, distance_cylinder=0. ): { "type": filter, "radius": distance_radius, - "src_domain": "SRS_DOMAIN", + "src_domain": "SRC_DOMAIN", "reference_domain": "REF_DOMAIN", "output_dimension": "radius_search", "is3d": search_3d, From 43bba12556ff1661df86cf9cfb479882043a300c Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Wed, 29 May 2024 09:38:52 +0200 Subject: [PATCH 38/49] maj nom attributs --- src/filter_radius_assign/RadiusAssignFilter.cpp | 10 +++++----- src/filter_radius_assign/RadiusAssignFilter.hpp | 2 +- test/test_radius_assign.py | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/filter_radius_assign/RadiusAssignFilter.cpp b/src/filter_radius_assign/RadiusAssignFilter.cpp index c2cff07..7e8de09 100644 --- a/src/filter_radius_assign/RadiusAssignFilter.cpp +++ b/src/filter_radius_assign/RadiusAssignFilter.cpp @@ -37,8 +37,8 @@ void RadiusAssignFilter::addArgs(ProgramArgs& args) args.add("radius", "Distance of neighbors to consult", m_args->m_radius, 1.); args.add("output_dimension", "Name of the added dimension", m_args->m_outputDimension, "radius"); args.add("is3d", "Search in 3d", m_args->search3d, false ); - args.add("is2d_above", "if search in 2d : upward maximum distance in Z for potential neighbors (corresponds to a search in a cylinder with a height = is2d_above above the source point). Default (0) = infinite height", m_args->m_search_above, 0.); - args.add("is2d_below", "if search in 2d : upward maximum distance in Z for potential neighbors (corresponds to a search in a cylinder with a height = is2d_below below the source point). Default (0) = infinite height", m_args->m_search_below, 0.); + args.add("max2d_above", "if search in 2d : upward maximum distance in Z for potential neighbors (corresponds to a search in a cylinder with a height = max2d_above above the source point). Default (0) = infinite height", m_args->m_max2d_above, 0.); + args.add("max2d_below", "if search in 2d : upward maximum distance in Z for potential neighbors (corresponds to a search in a cylinder with a height = max2d_below below the source point). Default (0) = infinite height", m_args->m_max2d_below, 0.); } void RadiusAssignFilter::addDimensions(PointLayoutPtr layout) @@ -81,14 +81,14 @@ void RadiusAssignFilter::doOneNoDomain(PointRef &point) if (!m_args->search3d) { double Zref = point.getFieldAs(Dimension::Id::Z); - if (m_args->m_search_below>0 || m_args->m_search_above>0) + if (m_args->m_max2d_below>0 || m_args->m_max2d_above>0) { bool take (false); for (PointId ptId : iNeighbors) { double Zpt = refView->point(ptId).getFieldAs(Dimension::Id::Z); - if (m_args->m_search_below>0 && Zpt>Zref && (Zpt-Zref)<=m_args->m_search_below) {take=true; break;} - if (m_args->m_search_above>0 && Zptm_search_above) {take=true; break;} + if (m_args->m_max2d_below>0 && Zpt>Zref && (Zpt-Zref)<=m_args->m_max2d_below) {take=true; break;} + if (m_args->m_max2d_above>0 && Zptm_max2d_above) {take=true; break;} } if (!take) return; } diff --git a/src/filter_radius_assign/RadiusAssignFilter.hpp b/src/filter_radius_assign/RadiusAssignFilter.hpp index 3581e34..d94f6a8 100644 --- a/src/filter_radius_assign/RadiusAssignFilter.hpp +++ b/src/filter_radius_assign/RadiusAssignFilter.hpp @@ -32,7 +32,7 @@ class PDAL_DLL RadiusAssignFilter : public Filter Dimension::Id m_dim; bool search3d; Dimension::Id m_dim_ref, m_dim_src; - double m_search_below, m_search_above; + double m_max2d_above, m_max2d_below; }; std::unique_ptr m_args; PointViewPtr refView; diff --git a/test/test_radius_assign.py b/test/test_radius_assign.py index 403bbf6..14716d3 100755 --- a/test/test_radius_assign.py +++ b/test/test_radius_assign.py @@ -53,8 +53,8 @@ def run_filter(arrays_las, distance_radius, search_3d, distance_cylinder=0. ): "reference_domain": "REF_DOMAIN", "output_dimension": "radius_search", "is3d": search_3d, - "is2d_above": distance_cylinder, - "is2d_below": distance_cylinder, + "max2d_above": distance_cylinder, + "max2d_below": distance_cylinder, } ] From a60688293e8263518484f5407d07873e06974274 Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Wed, 29 May 2024 09:41:17 +0200 Subject: [PATCH 39/49] fix doc code --- src/filter_radius_assign/RadiusAssignFilter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/filter_radius_assign/RadiusAssignFilter.cpp b/src/filter_radius_assign/RadiusAssignFilter.cpp index 7e8de09..97d944b 100644 --- a/src/filter_radius_assign/RadiusAssignFilter.cpp +++ b/src/filter_radius_assign/RadiusAssignFilter.cpp @@ -14,7 +14,7 @@ namespace pdal static PluginInfo const s_info = PluginInfo( "filters.radius_assign", - "Re-assign some point attributes based KNN voting", + "Re-assign some point attributes based on KNN voting", "" ); CREATE_SHARED_STAGE(RadiusAssignFilter, s_info) From 8ff93f0c8c42b6e41bee01ca338cb0f4933843f6 Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Wed, 29 May 2024 10:30:56 +0200 Subject: [PATCH 40/49] fix test --- test/test_radius_assign.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/test_radius_assign.py b/test/test_radius_assign.py index 14716d3..ac863e4 100755 --- a/test/test_radius_assign.py +++ b/test/test_radius_assign.py @@ -12,11 +12,11 @@ def distance2d(pt1, pt2): - return sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2) + return round(sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2), 2) def distance3d(pt1, pt2): - return sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2 + (pt1[2] - pt2[2]) ** 2) + return round(sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2 + (pt1[2] - pt2[2]) ** 2), 2) def run_filter(arrays_las, distance_radius, search_3d, distance_cylinder=0. ): @@ -40,8 +40,8 @@ def run_filter(arrays_las, distance_radius, search_3d, distance_cylinder=0. ): { "type": "filters.assign", "value": [ - "SRS_DOMAIN = 1 WHERE Classification==2", - "SRS_DOMAIN = 0 WHERE Classification!=2", + "SRC_DOMAIN = 1 WHERE Classification==2", + "SRC_DOMAIN = 0 WHERE Classification!=2", "REF_DOMAIN = 1 WHERE Classification==1", "REF_DOMAIN = 0 WHERE Classification!=1", ], @@ -137,7 +137,7 @@ def test_radius_assign_2d_cylinder(): def func_test(pt_ini, pt): distance_i = distance2d(pt_ini, pt) if distance_i <= distance_radius: - if abs(pt_ini[2] - pt[2]) <= distance_cylinder: + if abs(pt_ini[2] - pt[2]) < distance_cylinder: return 1 return 0 From 213c7b146a4aba76fef730326bfbb0c962a2caa6 Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Wed, 29 May 2024 11:50:40 +0200 Subject: [PATCH 41/49] fix test --- src/filter_radius_assign/RadiusAssignFilter.cpp | 4 ++-- test/test_radius_assign.py | 17 ++++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/filter_radius_assign/RadiusAssignFilter.cpp b/src/filter_radius_assign/RadiusAssignFilter.cpp index 97d944b..db06c88 100644 --- a/src/filter_radius_assign/RadiusAssignFilter.cpp +++ b/src/filter_radius_assign/RadiusAssignFilter.cpp @@ -87,8 +87,8 @@ void RadiusAssignFilter::doOneNoDomain(PointRef &point) for (PointId ptId : iNeighbors) { double Zpt = refView->point(ptId).getFieldAs(Dimension::Id::Z); - if (m_args->m_max2d_below>0 && Zpt>Zref && (Zpt-Zref)<=m_args->m_max2d_below) {take=true; break;} - if (m_args->m_max2d_above>0 && Zptm_max2d_above) {take=true; break;} + if (m_args->m_max2d_below>0 && Zpt>=Zref && (Zpt-Zref)<=m_args->m_max2d_below) {take=true; break;} + if (m_args->m_max2d_above>0 && Zpt<=Zref && (Zref-Zpt)<=m_args->m_max2d_above) {take=true; break;} } if (!take) return; } diff --git a/test/test_radius_assign.py b/test/test_radius_assign.py index ac863e4..9e884de 100755 --- a/test/test_radius_assign.py +++ b/test/test_radius_assign.py @@ -84,11 +84,10 @@ def build_random_points_around_one_point(test_function): nb_points = randint(20, 50) nb_points_take = 0 for i in range(nb_points): - pti_x = pt_ini[0] + rand.uniform(-1.5, 1.5) - pti_y = pt_ini[1] + rand.uniform(-1.5, 1.5) - - # pdal write takes 2 numbers precision (scale_z=0.01 and offset_z=0 by default) - pti_z = round(pt_ini[2] + rand.uniform(-1.5, 1.5), 2) + # round at 1 to avoid precision numeric pb + pti_x = round(pt_ini[0] + rand.uniform(-1.5, 1.5), 1) + pti_y = round(pt_ini[1] + rand.uniform(-1.5, 1.5), 1) + pti_z = round(pt_ini[2] + rand.uniform(-1.5, 1.5), 1) pt_i = (pti_x, pti_y, pti_z, 2) arrays_pti = np.array([pt_i], dtype=dtype) @@ -105,7 +104,7 @@ def test_radius_assign_3d(): def func_test(pt_ini, pt): distance_i = distance3d(pt_ini, pt) - if distance_i <= distance_radius: + if distance_i < distance_radius: return 1 return 0 @@ -120,7 +119,7 @@ def test_radius_assign_2d(): def func_test(pt_ini, pt): distance_i = distance2d(pt_ini, pt) - if distance_i <= distance_radius: + if distance_i < distance_radius: return 1 return 0 @@ -136,8 +135,8 @@ def test_radius_assign_2d_cylinder(): def func_test(pt_ini, pt): distance_i = distance2d(pt_ini, pt) - if distance_i <= distance_radius: - if abs(pt_ini[2] - pt[2]) < distance_cylinder: + if distance_i < distance_radius: + if abs(pt_ini[2] - pt[2]) <= distance_cylinder: return 1 return 0 From 38d8bc758939056b9665ea747d09965e7f1dbdb8 Mon Sep 17 00:00:00 2001 From: alavenant Date: Wed, 29 May 2024 17:01:33 +0200 Subject: [PATCH 42/49] Update doc/radius_assign.md Co-authored-by: leavauchier <120112647+leavauchier@users.noreply.github.com> --- doc/radius_assign.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/radius_assign.md b/doc/radius_assign.md index f5dc42f..fdac359 100755 --- a/doc/radius_assign.md +++ b/doc/radius_assign.md @@ -3,7 +3,9 @@ Purpose --------------------------------------------------------------------------------------------------------- -The **radius assign filter** add a new attribut where the value depends on their neighbors in a given radius: For each point in the domain src_domain_, if it has any neighbor with a distance lower than radius_ that belongs to the domain reference_domain_, it is updated. +The **radius assign filter** overwrites the output_dimension_ dimension with boolean values: +* 1 if the point has any neighbor with a distance lower than radius_ that belongs to the domain reference_domain_ +* 0 otherwise. Example From 6bd89a5341b5c7632627c780e63ca8ed086f71ca Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Wed, 29 May 2024 17:02:39 +0200 Subject: [PATCH 43/49] fix doc + rename file --- doc/radius_assign.md | 4 ++-- environment.yml | 11 +++++------ macro/ex_filtering_points.py | 2 +- ...2.py => ex_filtering_points_with_add_attributs.py} | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) rename macro/{ex_filtering_points2.py => ex_filtering_points_with_add_attributs.py} (98%) diff --git a/doc/radius_assign.md b/doc/radius_assign.md index f5dc42f..60e0f66 100755 --- a/doc/radius_assign.md +++ b/doc/radius_assign.md @@ -43,7 +43,7 @@ Options **is3d**: Search in 3d (as a ball). [Default: false] -**is2d_above**: If search in 2d : upward maximum distance in Z for potential neighbors (corresponds to a search in a cylinder with a height = is2d_above above the source point). Default (0) = infinite height [Default: 0.] +**max2d_above**: If search in 2d : upward maximum distance in Z for potential neighbors (corresponds to a search in a cylinder with a height = max2d_above above the source point). Default (0) = infinite height [Default: 0.] -**is2d_below**: If search in 2d : upward maximum distance in Z for potential neighbors (corresponds to a search in a cylinder with a height = is2d_below below the source point). Default (0) = infinite height [Default: 0.] +**max2d_below**: If search in 2d : upward maximum distance in Z for potential neighbors (corresponds to a search in a cylinder with a height = max2d_below below the source point). Default (0) = infinite height [Default: 0.] diff --git a/environment.yml b/environment.yml index 1c30b24..695737c 100755 --- a/environment.yml +++ b/environment.yml @@ -6,15 +6,14 @@ dependencies: - pdal - python-pdal - gdal - - cmake - - pip - - pip: - - ign-pdal-tools # --------- dev dep --------- # + - cmake - pre-commit # hooks for applying linters on commit - black # code formatting - isort # import sorting - flake8 # code analysis - pytest - - +# --------- pip & pip librairies --------- # + - pip + - pip: + - ign-pdal-tools diff --git a/macro/ex_filtering_points.py b/macro/ex_filtering_points.py index 72b267d..ae1320b 100755 --- a/macro/ex_filtering_points.py +++ b/macro/ex_filtering_points.py @@ -7,7 +7,7 @@ """ def parse_args(): - parser = argparse.ArgumentParser("Tool to apply pdal pipelines to modify classification") + parser = argparse.ArgumentParser("Tool to apply pdal pipelines for DSM and DTM calculation") parser.add_argument("--input", "-i", type=str, required=True, help="Input las file") parser.add_argument("--output_las", "-o", type=str, required=True, help="Output cloud las file") parser.add_argument("--output_dsm", "-s", type=str, required=True, help="Output dsm tiff file") diff --git a/macro/ex_filtering_points2.py b/macro/ex_filtering_points_with_add_attributs.py similarity index 98% rename from macro/ex_filtering_points2.py rename to macro/ex_filtering_points_with_add_attributs.py index 71de88d..8d9a9a4 100755 --- a/macro/ex_filtering_points2.py +++ b/macro/ex_filtering_points_with_add_attributs.py @@ -7,7 +7,7 @@ """ def parse_args(): - parser = argparse.ArgumentParser("Tool to apply pdal pipelines to modify classification") + parser = argparse.ArgumentParser("Tool to apply pdal pipelines for DSM and DTM calculation (with add attributs for the concerned points)") parser.add_argument("--input", "-i", type=str, required=True, help="Input las file") parser.add_argument("--output_las", "-o", type=str, required=True, help="Output cloud las file") parser.add_argument("--output_dsm", "-s", type=str, required=True, help="Output dsm tiff file") From 030654accd00aea4a11f8c2a26223d2361ad057e Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Wed, 29 May 2024 17:05:43 +0200 Subject: [PATCH 44/49] gridDecimation : modification du type de sortie --- src/filter_grid_decimation/GridDecimationFilter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/filter_grid_decimation/GridDecimationFilter.cpp b/src/filter_grid_decimation/GridDecimationFilter.cpp index a0d82e7..956e0a5 100755 --- a/src/filter_grid_decimation/GridDecimationFilter.cpp +++ b/src/filter_grid_decimation/GridDecimationFilter.cpp @@ -70,7 +70,7 @@ void GridDecimationFilter::ready(PointTableRef table) void GridDecimationFilter::addDimensions(PointLayoutPtr layout) { - m_args->m_dim = layout->registerOrAssignDim(m_args->m_nameOutDimension, Dimension::Type::Double); + m_args->m_dim = layout->registerOrAssignDim(m_args->m_nameOutDimension, Dimension::Type::Unsigned8); } void GridDecimationFilter::processOne(BOX2D bounds, PointRef& point, PointViewPtr view) From fb77f919da89050f9263522ea82c4ce00939c25c Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Wed, 29 May 2024 17:23:40 +0200 Subject: [PATCH 45/49] update example --- macro/ex_filtering_points_with_add_attributs.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/macro/ex_filtering_points_with_add_attributs.py b/macro/ex_filtering_points_with_add_attributs.py index 8d9a9a4..c5700dc 100755 --- a/macro/ex_filtering_points_with_add_attributs.py +++ b/macro/ex_filtering_points_with_add_attributs.py @@ -65,11 +65,14 @@ def parse_args(): pipeline |= pdal.Filter.assign(value=["PT_GRID_DSM = 0 WHERE " + macro.build_condition("Classification", [2,3,4,5,9]) + " && PT_ON_BRIDGE==1"]) - # 4 - export du nuage et des DSM + ## 4 - point pour DTM servent au DSM également + pipeline |= pdal.Filter.assign(value=["PT_GRID_DSM = 1 WHERE PT_GRID_DTM==1"]) + + ## 5 - export du nuage et des DSM pipeline |= pdal.Writer.las(extra_dims="all", minor_version=4, dataformat_id=6, filename=args.output_las) pipeline |= pdal.Writer.gdal(gdaldriver="GTiff", output_type="max", resolution=2.0, filename=args.output_dtm, where="PT_GRID_DTM==1") - pipeline |= pdal.Writer.gdal(gdaldriver="GTiff", output_type="max", resolution=2.0, filename=args.output_dsm, where="PT_GRID_DTM==1 || PT_GRID_DSM==1") + pipeline |= pdal.Writer.gdal(gdaldriver="GTiff", output_type="max", resolution=2.0, filename=args.output_dsm, where="PT_GRID_DSM==1") pipeline.execute() From 93457e9969dab49a519bd9f9a144f4f9bacd7d8a Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Wed, 29 May 2024 17:29:30 +0200 Subject: [PATCH 46/49] example - fix write gdal --- macro/ex_filtering_points.py | 2 +- macro/ex_filtering_points_with_add_attributs.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/macro/ex_filtering_points.py b/macro/ex_filtering_points.py index ae1320b..f79af6d 100755 --- a/macro/ex_filtering_points.py +++ b/macro/ex_filtering_points.py @@ -84,7 +84,7 @@ def parse_args(): # step 15 et supression des points ?? # 4 - export du nuage - pipeline |= pdal.Writer.las(extra_dims="all",minor_version=4,dataformat_id=6,filename=args.output_las) + pipeline |= pdal.Writer.las(extra_dims="all",forward="all",filename=args.output_las) # export des DSM/DTM pipeline |= pdal.Writer.gdal(gdaldriver="GTiff", output_type="max", resolution=2.0, filename=args.output_dtm, where=macro.build_condition("Classification", [2,66])) diff --git a/macro/ex_filtering_points_with_add_attributs.py b/macro/ex_filtering_points_with_add_attributs.py index c5700dc..413cfb4 100755 --- a/macro/ex_filtering_points_with_add_attributs.py +++ b/macro/ex_filtering_points_with_add_attributs.py @@ -70,7 +70,7 @@ def parse_args(): ## 5 - export du nuage et des DSM - pipeline |= pdal.Writer.las(extra_dims="all", minor_version=4, dataformat_id=6, filename=args.output_las) + pipeline |= pdal.Writer.las(extra_dims="all", forward="all", filename=args.output_las) pipeline |= pdal.Writer.gdal(gdaldriver="GTiff", output_type="max", resolution=2.0, filename=args.output_dtm, where="PT_GRID_DTM==1") pipeline |= pdal.Writer.gdal(gdaldriver="GTiff", output_type="max", resolution=2.0, filename=args.output_dsm, where="PT_GRID_DSM==1") From 623a47c5af53dda5e558b1edeadd665b86c287ef Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Thu, 30 May 2024 09:38:40 +0200 Subject: [PATCH 47/49] grid_decimation to deprecated --- src/filter_grid_decimation/GridDecimationFilter.cpp | 2 +- test/test_grid_decimation.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/filter_grid_decimation/GridDecimationFilter.cpp b/src/filter_grid_decimation/GridDecimationFilter.cpp index 956e0a5..f1c61b3 100755 --- a/src/filter_grid_decimation/GridDecimationFilter.cpp +++ b/src/filter_grid_decimation/GridDecimationFilter.cpp @@ -18,7 +18,7 @@ namespace pdal static StaticPluginInfo const s_info { - "filters.grid_decimation", + "filters.grid_decimation_deprecated", // better to use the pdal gridDecimation plugIN "keep max or min points in a grid", "", }; diff --git a/test/test_grid_decimation.py b/test/test_grid_decimation.py index ea21a79..c5c3d36 100755 --- a/test/test_grid_decimation.py +++ b/test/test_grid_decimation.py @@ -16,7 +16,7 @@ def run_filter(type): tmp_out_las = tempfile.NamedTemporaryFile(suffix=".las").name tmp_out_wkt = tempfile.NamedTemporaryFile(suffix=".wkt").name - filter = "filters.grid_decimation" + filter = "filters.grid_decimation_deprecated" utils.pdal_has_plugin(filter) bounds = li.las_get_xy_bounds(ini_las) From 2bdc54c09617940a9b6b8d0387110d460b0e9caf Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Thu, 30 May 2024 11:28:01 +0200 Subject: [PATCH 48/49] attribut => dimension --- doc/grid_decimation.md | 4 ++-- ...s.py => ex_filtering_points_with_add_dimensions.py} | 10 +++++----- src/filter_grid_decimation/GridDecimationFilter.cpp | 2 +- src/filter_radius_assign/RadiusAssignFilter.cpp | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) rename macro/{ex_filtering_points_with_add_attributs.py => ex_filtering_points_with_add_dimensions.py} (90%) diff --git a/doc/grid_decimation.md b/doc/grid_decimation.md index 9ce662f..6638898 100755 --- a/doc/grid_decimation.md +++ b/doc/grid_decimation.md @@ -5,7 +5,7 @@ Purpose --------------------------------------------------------------------------------------------------------- -The **grid decimation filter** transform only one point in each cells of a grid calculated from the points cloud and a resolution therm. The transformation is done by the value information. The selected point could be the highest or the lowest point on the cell. It can be used, for exemple, to quickly filter vegetation points in order to keep only the canopy points. A new attribut is created with the value '1' for the grid, and '0' for the other points. +The **grid decimation filter** transform only one point in each cells of a grid calculated from the points cloud and a resolution therm. The transformation is done by the value information. The selected point could be the highest or the lowest point on the cell. It can be used, for exemple, to quickly filter vegetation points in order to keep only the canopy points. A new dimension is created with the value '1' for the grid, and '0' for the other points. Example @@ -20,7 +20,7 @@ This example transform highest points of classification 5 in classification 9, o { "type": "filters.gridDecimation", "output_type":"max", - "output_name_attribut": "grid", + "output_dimension": "grid", "output_wkt":"file-output.wkt" }, { diff --git a/macro/ex_filtering_points_with_add_attributs.py b/macro/ex_filtering_points_with_add_dimensions.py similarity index 90% rename from macro/ex_filtering_points_with_add_attributs.py rename to macro/ex_filtering_points_with_add_dimensions.py index 413cfb4..107a263 100755 --- a/macro/ex_filtering_points_with_add_attributs.py +++ b/macro/ex_filtering_points_with_add_dimensions.py @@ -7,7 +7,7 @@ """ def parse_args(): - parser = argparse.ArgumentParser("Tool to apply pdal pipelines for DSM and DTM calculation (with add attributs for the concerned points)") + parser = argparse.ArgumentParser("Tool to apply pdal pipelines for DSM and DTM calculation (with add dimensions for the concerned points)") parser.add_argument("--input", "-i", type=str, required=True, help="Input las file") parser.add_argument("--output_las", "-o", type=str, required=True, help="Output cloud las file") parser.add_argument("--output_dsm", "-s", type=str, required=True, help="Output dsm tiff file") @@ -20,7 +20,7 @@ def parse_args(): pipeline = pdal.Reader.las(args.input) - # 0 - ajout d'attributs temporaires + # 0 - ajout de dimensions temporaires pipeline |= pdal.Filter.ferry(dimensions=f"=>PT_GRID_DSM, =>PT_VEG_DSM, =>PT_GRID_DTM, =>PT_ON_BRIDGE") @@ -48,7 +48,7 @@ def parse_args(): # selection de points DSM (max) sur une grille régulière pipeline |= pdal.Filter.gridDecimation(resolution=0.5, value="PT_GRID_DSM=1", output_type="max", - where="(" + macro.build_condition("Classification", [2,3,4,5,6,9,17,64]) + ") || PT_GRID_DSM==1") + where="(" + macro.build_condition("Classification", [6,9,17,64]) + ") || PT_GRID_DSM==1") # assigne des points sol sélectionnés : les points proches de la végétation, des ponts, de l'eau, 64 pipeline = macro.add_radius_assign(pipeline, 1.5, False, condition_src="PT_GRID_DTM==1", @@ -62,11 +62,11 @@ def parse_args(): pipeline = macro.add_radius_assign(pipeline, 1.5, False, condition_src=macro.build_condition("Classification", [2,3,4,5,9]), condition_ref="Classification==17", condition_out="PT_ON_BRIDGE=1") pipeline = macro.add_radius_assign(pipeline, 1.5, False, condition_src="PT_ON_BRIDGE==1", condition_ref=macro.build_condition("Classification", [2,3,4,5]), condition_out="PT_ON_BRIDGE=0") - pipeline |= pdal.Filter.assign(value=["PT_GRID_DSM = 0 WHERE " + macro.build_condition("Classification", [2,3,4,5,9]) + " && PT_ON_BRIDGE==1"]) + pipeline |= pdal.Filter.assign(value=["PT_GRID_DSM=0 WHERE PT_ON_BRIDGE==1"]) ## 4 - point pour DTM servent au DSM également - pipeline |= pdal.Filter.assign(value=["PT_GRID_DSM = 1 WHERE PT_GRID_DTM==1"]) + pipeline |= pdal.Filter.assign(value=["PT_GRID_DSM=1 WHERE PT_GRID_DTM==1"]) ## 5 - export du nuage et des DSM diff --git a/src/filter_grid_decimation/GridDecimationFilter.cpp b/src/filter_grid_decimation/GridDecimationFilter.cpp index f1c61b3..1366dcb 100755 --- a/src/filter_grid_decimation/GridDecimationFilter.cpp +++ b/src/filter_grid_decimation/GridDecimationFilter.cpp @@ -62,7 +62,7 @@ void GridDecimationFilter::ready(PointTableRef table) throwError("The output_type must be 'max' or 'min'."); if (m_args->m_nameOutDimension.empty()) - throwError("The output_name_attribut must be given."); + throwError("The output_dimension must be given."); if (!m_args->m_nameWktgrid.empty()) std::remove(m_args->m_nameWktgrid.c_str()); diff --git a/src/filter_radius_assign/RadiusAssignFilter.cpp b/src/filter_radius_assign/RadiusAssignFilter.cpp index db06c88..ca54ee0 100644 --- a/src/filter_radius_assign/RadiusAssignFilter.cpp +++ b/src/filter_radius_assign/RadiusAssignFilter.cpp @@ -14,7 +14,7 @@ namespace pdal static PluginInfo const s_info = PluginInfo( "filters.radius_assign", - "Re-assign some point attributes based on KNN voting", + "Re-assign some point dimension based on KNN voting", "" ); CREATE_SHARED_STAGE(RadiusAssignFilter, s_info) @@ -55,7 +55,7 @@ void RadiusAssignFilter::initialize() if (m_args->m_radius <= 0) throwError("Invalid 'radius' option: " + std::to_string(m_args->m_radius) + ", must be > 0"); if (m_args->m_outputDimension.empty()) - throwError("The output_name_attribut must be given."); + throwError("The output_dimension must be given."); } void RadiusAssignFilter::prepared(PointTableRef table) From 0c7c7721d75210e6a610f6d4a9841cac646e4c02 Mon Sep 17 00:00:00 2001 From: Antoine Lavenant Date: Thu, 30 May 2024 14:57:23 +0200 Subject: [PATCH 49/49] update docker --- Dockerfile | 11 ++++++----- environment_docker.yml | 7 +++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 719ec36..3a1cbfd 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM mambaorg/micromamba:bullseye-slim as build -COPY environment.yml /environment_docker.yml +COPY environment_docker.yml /environment_docker.yml USER root RUN micromamba env create -f /environment_docker.yml @@ -11,16 +11,17 @@ COPY src src COPY CMakeLists.txt CMakeLists.txt COPY macro macro -RUN cmake -G"Unix Makefiles" -DCONDA_PREFIX=$CONDA_PREFIX -DCMAKE_BUILD_TYPE=Release +RUN cmake -G"Unix Makefiles" -DCONDA_PREFIX=$CONDA_PREFIX -DCMAKE_BUILD_TYPE=Release RUN make -j4 install FROM debian:bullseye-slim -COPY --from=build /opt/conda/envs/pdal_ign_plugin /opt/conda/envs/pdal_ign_plugin -COPY --from=build /tmp/install/lib /tmp/install/lib +COPY --from=build /opt/conda/envs/pdal_ign_plugin /opt/conda/envs/pdal_ign_plugin +RUN mkdir -p /pdal_ign_plugin +COPY --from=build /tmp/install/lib /pdal_ign_plugin/install/lib COPY --from=build /tmp/macro /macro ENV PATH=$PATH:/opt/conda/envs/pdal_ign_plugin/bin/ ENV PROJ_LIB=/opt/conda/envs/pdal_ign_plugin/share/proj/ -ENV PDAL_DRIVER_PATH=/tmp/install/lib +ENV PDAL_DRIVER_PATH=/pdal_ign_plugin/install/lib diff --git a/environment_docker.yml b/environment_docker.yml index b49f938..7e23ef6 100755 --- a/environment_docker.yml +++ b/environment_docker.yml @@ -1,6 +1,13 @@ name: pdal_ign_plugin channels: - conda-forge + - anaconda dependencies: - pdal + - python-pdal + - gdal +# --------- pip & pip librairies --------- # + - pip + - pip: + - ign-pdal-tools