From d31a8e7bef9509acf06c1bd17e4cfac0a2b7ae19 Mon Sep 17 00:00:00 2001 From: Jan Caha Date: Wed, 15 Apr 2026 09:54:48 +0200 Subject: [PATCH 01/10] bump version --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 2c01f33..56c0b5b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,7 +30,7 @@ extern int runTile(std::vector arglist); // tile/tile.cpp -std::string WRENCH_VERSION = "1.4.0"; +std::string WRENCH_VERSION = "1.5.0"; void printUsage() { From c862c072de2e15bc22bd6c45e33bbcafb726cf34 Mon Sep 17 00:00:00 2001 From: Jan Caha Date: Mon, 29 Jun 2026 15:40:10 +0200 Subject: [PATCH 02/10] create combined bounds from polygon extent and filter bounds --- src/alg.hpp | 3 +++ src/clip.cpp | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/alg.hpp b/src/alg.hpp index db9c117..86a7be0 100644 --- a/src/alg.hpp +++ b/src/alg.hpp @@ -196,6 +196,9 @@ struct Clip : public Alg std::vector tileOutputFiles; + // combined bounds of filterBounds and polygon file, can be empty if filteredBounds is empty + BOX2D combinedBounds; + // impl virtual void addArgs() override; virtual bool checkArgs() override; diff --git a/src/clip.cpp b/src/clip.cpp index c6b1231..10ec92c 100644 --- a/src/clip.cpp +++ b/src/clip.cpp @@ -162,6 +162,25 @@ void Clip::preparePipelines(std::vector>& pipel if (!loadPolygons(polygonFile, crop_opts, bbox)) return; + // equal combinedBounds to the polygon bounds + // it is never necessary to read more from the input than this + combinedBounds = BOX2D(bbox); + + // if filterBounds is set, combine it with the polygon bounds + if (!filterBounds.empty()) { + BOX2D filterBoundsBox; + std::string::size_type pos = 0; + filterBoundsBox.parse(filterBounds, pos); + + // combinedBounds is the intersection of the polygon bounds and the filter bounds + combinedBounds.clip(filterBoundsBox); + } + + if (!combinedBounds.valid()) { + std::cerr << "clip bounds are empty or polygon and filter bounds do not overlap" << std::endl; + return; + } + if (isVpcFilename(inputFile)) { // for /tmp/hello.vpc we will use /tmp/hello dir for all results From 0bc51240b462b3309d6d09c5589dc2cb8305956c Mon Sep 17 00:00:00 2001 From: Jan Caha Date: Mon, 29 Jun 2026 15:40:39 +0200 Subject: [PATCH 03/10] pass combinedBox to the pipeline --- src/clip.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/clip.cpp b/src/clip.cpp index 10ec92c..e8da821 100644 --- a/src/clip.cpp +++ b/src/clip.cpp @@ -113,7 +113,7 @@ bool loadPolygons(const std::string &polygonFile, pdal::Options& crop_opts, BOX2 } -static std::unique_ptr pipeline(ParallelJobInfo *tile, const pdal::Options &crop_opts) +static std::unique_ptr pipeline(ParallelJobInfo *tile, const pdal::Options &crop_opts, BOX2D combinedBox) { assert(tile); @@ -123,11 +123,16 @@ static std::unique_ptr pipeline(ParallelJobInfo *tile, const pd Stage *last = &r; - // filtering - if (!tile->filterBounds.empty()) + // filtering - either use combinedBox from polygon and filterBounds + // or use it from polygon only if filterBounds is empty + if (combinedBox.valid()) { + std::ostringstream oss; + oss << combinedBox; + std::string boundsBoxStr = oss.str(); + Options filter_opts; - filter_opts.add(pdal::Option("bounds", tile->filterBounds)); + filter_opts.add(pdal::Option("bounds", boundsBoxStr)); if (readerSupportsBounds(r)) { @@ -213,7 +218,7 @@ void Clip::preparePipelines(std::vector>& pipel tileOutputFiles.push_back(tile.outputFilename); - pipelines.push_back(pipeline(&tile, crop_opts)); + pipelines.push_back(pipeline(&tile, crop_opts, combinedBounds)); } } else @@ -225,7 +230,7 @@ void Clip::preparePipelines(std::vector>& pipel ParallelJobInfo tile(ParallelJobInfo::Single, BOX2D(), filterExpression, filterBounds); tile.inputFilenames.push_back(inputFile); tile.outputFilename = outputFile; - pipelines.push_back(pipeline(&tile, crop_opts)); + pipelines.push_back(pipeline(&tile, crop_opts, combinedBounds)); } } From dc8b7eb53c3ac05352e7e7091992de475c8dfe05 Mon Sep 17 00:00:00 2001 From: Jan Caha Date: Mon, 29 Jun 2026 15:41:20 +0200 Subject: [PATCH 04/10] version --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 56c0b5b..d8456e2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,7 +30,7 @@ extern int runTile(std::vector arglist); // tile/tile.cpp -std::string WRENCH_VERSION = "1.5.0"; +std::string WRENCH_VERSION = "1.4.1"; void printUsage() { From 39928ef56082368e6996200e69e4de0f8824db4a Mon Sep 17 00:00:00 2001 From: Jan Caha Date: Tue, 30 Jun 2026 16:40:05 +0200 Subject: [PATCH 05/10] use version from main --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index d8456e2..56c0b5b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,7 +30,7 @@ extern int runTile(std::vector arglist); // tile/tile.cpp -std::string WRENCH_VERSION = "1.4.1"; +std::string WRENCH_VERSION = "1.5.0"; void printUsage() { From 8db6955116e6d15bd662bf8f252d0f84ce179e73 Mon Sep 17 00:00:00 2001 From: Jan Caha Date: Tue, 30 Jun 2026 20:01:33 +0200 Subject: [PATCH 06/10] use ParallelJobInfo filterBounds instead of dedicated variable --- src/alg.hpp | 3 --- src/clip.cpp | 34 +++++++++++++++++----------------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/alg.hpp b/src/alg.hpp index 86a7be0..db9c117 100644 --- a/src/alg.hpp +++ b/src/alg.hpp @@ -196,9 +196,6 @@ struct Clip : public Alg std::vector tileOutputFiles; - // combined bounds of filterBounds and polygon file, can be empty if filteredBounds is empty - BOX2D combinedBounds; - // impl virtual void addArgs() override; virtual bool checkArgs() override; diff --git a/src/clip.cpp b/src/clip.cpp index e8da821..6849763 100644 --- a/src/clip.cpp +++ b/src/clip.cpp @@ -113,7 +113,7 @@ bool loadPolygons(const std::string &polygonFile, pdal::Options& crop_opts, BOX2 } -static std::unique_ptr pipeline(ParallelJobInfo *tile, const pdal::Options &crop_opts, BOX2D combinedBox) +static std::unique_ptr pipeline(ParallelJobInfo *tile, const pdal::Options &crop_opts) { assert(tile); @@ -125,16 +125,16 @@ static std::unique_ptr pipeline(ParallelJobInfo *tile, const pd // filtering - either use combinedBox from polygon and filterBounds // or use it from polygon only if filterBounds is empty - if (combinedBox.valid()) + if (!tile->filterBounds.empty()) { std::ostringstream oss; - oss << combinedBox; + oss << tile->filterBounds; std::string boundsBoxStr = oss.str(); - + Options filter_opts; filter_opts.add(pdal::Option("bounds", boundsBoxStr)); - if (readerSupportsBounds(r)) + if (readerSupportsBounds(r)) { // Reader of the format can do the filtering - use that whenever possible! r.addOptions(filter_opts); @@ -142,7 +142,7 @@ static std::unique_ptr pipeline(ParallelJobInfo *tile, const pd else { // Reader can't do the filtering - do it with a filter - last = &manager->makeFilter( "filters.crop", *last, filter_opts); + last = &manager->makeFilter("filters.crop", *last, filter_opts); } } if (!tile->filterExpression.empty()) @@ -167,21 +167,21 @@ void Clip::preparePipelines(std::vector>& pipel if (!loadPolygons(polygonFile, crop_opts, bbox)) return; - // equal combinedBounds to the polygon bounds - // it is never necessary to read more from the input than this - combinedBounds = BOX2D(bbox); - + // equal combinedBox to the polygon bounds + // it is never necessary to read more from the input than this + BOX2D combinedBox = BOX2D(bbox); + // if filterBounds is set, combine it with the polygon bounds if (!filterBounds.empty()) { BOX2D filterBoundsBox; std::string::size_type pos = 0; filterBoundsBox.parse(filterBounds, pos); - // combinedBounds is the intersection of the polygon bounds and the filter bounds - combinedBounds.clip(filterBoundsBox); + // combinedBox is the intersection of the polygon bounds and the filter bounds + combinedBox.clip(filterBoundsBox); } - if (!combinedBounds.valid()) { + if (!combinedBox.valid()) { std::cerr << "clip bounds are empty or polygon and filter bounds do not overlap" << std::endl; return; } @@ -211,14 +211,14 @@ void Clip::preparePipelines(std::vector>& pipel std::cout << "using " << f.filename << std::endl; } - ParallelJobInfo tile(ParallelJobInfo::FileBased, BOX2D(), filterExpression, filterBounds); + ParallelJobInfo tile(ParallelJobInfo::FileBased, combinedBox, filterExpression, filterBounds); tile.inputFilenames.push_back(f.filename); tile.outputFilename = tileOutputFileName(outputFile, outputFormatVpc, outputSubdir, f.filename); tileOutputFiles.push_back(tile.outputFilename); - pipelines.push_back(pipeline(&tile, crop_opts, combinedBounds)); + pipelines.push_back(pipeline(&tile, crop_opts)); } } else @@ -227,10 +227,10 @@ void Clip::preparePipelines(std::vector>& pipel { isStreaming = false; } - ParallelJobInfo tile(ParallelJobInfo::Single, BOX2D(), filterExpression, filterBounds); + ParallelJobInfo tile(ParallelJobInfo::Single, combinedBox, filterExpression, filterBounds); tile.inputFilenames.push_back(inputFile); tile.outputFilename = outputFile; - pipelines.push_back(pipeline(&tile, crop_opts, combinedBounds)); + pipelines.push_back(pipeline(&tile, crop_opts)); } } From 9f6b3089030fa2974864065bc028c9e9f38c663b Mon Sep 17 00:00:00 2001 From: Jan Caha Date: Wed, 1 Jul 2026 09:30:50 +0200 Subject: [PATCH 07/10] use combineBox as filterBounds of ParallelJobInfo --- src/clip.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clip.cpp b/src/clip.cpp index 6849763..aac5c52 100644 --- a/src/clip.cpp +++ b/src/clip.cpp @@ -211,7 +211,7 @@ void Clip::preparePipelines(std::vector>& pipel std::cout << "using " << f.filename << std::endl; } - ParallelJobInfo tile(ParallelJobInfo::FileBased, combinedBox, filterExpression, filterBounds); + ParallelJobInfo tile(ParallelJobInfo::FileBased, BOX2D() , filterExpression, combinedBox); tile.inputFilenames.push_back(f.filename); tile.outputFilename = tileOutputFileName(outputFile, outputFormatVpc, outputSubdir, f.filename); @@ -227,7 +227,7 @@ void Clip::preparePipelines(std::vector>& pipel { isStreaming = false; } - ParallelJobInfo tile(ParallelJobInfo::Single, combinedBox, filterExpression, filterBounds); + ParallelJobInfo tile(ParallelJobInfo::Single, BOX2D(), filterExpression, combinedBox); tile.inputFilenames.push_back(inputFile); tile.outputFilename = outputFile; pipelines.push_back(pipeline(&tile, crop_opts)); From f1ba4cd52519d4955316ca69a25dad69c669232b Mon Sep 17 00:00:00 2001 From: Jan Caha Date: Wed, 1 Jul 2026 09:37:49 +0200 Subject: [PATCH 08/10] the parameter is always string representing PDAL extent --- src/clip.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/clip.cpp b/src/clip.cpp index aac5c52..e022f5f 100644 --- a/src/clip.cpp +++ b/src/clip.cpp @@ -127,12 +127,8 @@ static std::unique_ptr pipeline(ParallelJobInfo *tile, const pd // or use it from polygon only if filterBounds is empty if (!tile->filterBounds.empty()) { - std::ostringstream oss; - oss << tile->filterBounds; - std::string boundsBoxStr = oss.str(); - Options filter_opts; - filter_opts.add(pdal::Option("bounds", boundsBoxStr)); + filter_opts.add(pdal::Option("bounds", tile->filterBounds)); if (readerSupportsBounds(r)) { @@ -211,7 +207,7 @@ void Clip::preparePipelines(std::vector>& pipel std::cout << "using " << f.filename << std::endl; } - ParallelJobInfo tile(ParallelJobInfo::FileBased, BOX2D() , filterExpression, combinedBox); + ParallelJobInfo tile(ParallelJobInfo::FileBased, BOX2D(), filterExpression, box_to_pdal_bounds(combinedBox)); tile.inputFilenames.push_back(f.filename); tile.outputFilename = tileOutputFileName(outputFile, outputFormatVpc, outputSubdir, f.filename); @@ -227,7 +223,7 @@ void Clip::preparePipelines(std::vector>& pipel { isStreaming = false; } - ParallelJobInfo tile(ParallelJobInfo::Single, BOX2D(), filterExpression, combinedBox); + ParallelJobInfo tile(ParallelJobInfo::Single, BOX2D(), filterExpression, box_to_pdal_bounds(combinedBox)); tile.inputFilenames.push_back(inputFile); tile.outputFilename = outputFile; pipelines.push_back(pipeline(&tile, crop_opts)); From 26234db3309db5a404d509838947cbea0ac7dca5 Mon Sep 17 00:00:00 2001 From: Stefanos Natsis Date: Wed, 1 Jul 2026 11:43:47 +0300 Subject: [PATCH 09/10] Apply suggestions from code review Co-authored-by: Stefanos Natsis --- src/clip.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/clip.cpp b/src/clip.cpp index e022f5f..4e5d5af 100644 --- a/src/clip.cpp +++ b/src/clip.cpp @@ -113,7 +113,7 @@ bool loadPolygons(const std::string &polygonFile, pdal::Options& crop_opts, BOX2 } -static std::unique_ptr pipeline(ParallelJobInfo *tile, const pdal::Options &crop_opts) +static std::unique_ptr pipeline(ParallelJobInfo *tile, const pdal::Options &crop_opts) { assert(tile); @@ -123,14 +123,13 @@ static std::unique_ptr pipeline(ParallelJobInfo *tile, const pd Stage *last = &r; - // filtering - either use combinedBox from polygon and filterBounds - // or use it from polygon only if filterBounds is empty + // filtering if (!tile->filterBounds.empty()) { Options filter_opts; filter_opts.add(pdal::Option("bounds", tile->filterBounds)); - if (readerSupportsBounds(r)) + if (readerSupportsBounds(r)) { // Reader of the format can do the filtering - use that whenever possible! r.addOptions(filter_opts); From ba13bb7efd510d1b7c3e70b925211c1968d9d27a Mon Sep 17 00:00:00 2001 From: Jan Caha Date: Wed, 1 Jul 2026 11:16:44 +0200 Subject: [PATCH 10/10] fix link --- tests/test_clip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_clip.py b/tests/test_clip.py index 95fa8df..8e78f04 100644 --- a/tests/test_clip.py +++ b/tests/test_clip.py @@ -51,7 +51,7 @@ def test_input_file_output_file( (utils.test_data_filepath("data_copc.vpc"), utils.test_data_filepath("clipped-vpc-copc-files.copc.laz"), 66911), (utils.test_data_filepath("data_copc.vpz"), utils.test_data_filepath("clipped-vpz-copc-files.vpc"), 66911), (utils.test_data_filepath("data_copc.vpz"), utils.test_data_filepath("clipped-vpz-copc-files.copc.laz"), 66911), - ("https://raw.githubusercontent.com/PDAL/wrench/refs/heads/fix-remote-vpc/tests/data/stadium.vpc", utils.test_data_filepath("clipped-vpz-copc-files.copc.laz"), 66905), + ("https://raw.githubusercontent.com/PDAL/wrench/f4b156c5081dd9a1d44fccfdb67f2c36e91e3566/tests/data/stadium.vpc", utils.test_data_filepath("clipped-vpz-copc-files.copc.laz"), 66905), ], ) def test_clip_vpc(