Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEAT-449: Resolution query functions #48

Merged
merged 23 commits into from
Oct 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
473795d
FEAT-440: Add resolution query functions with tests and examples.
leo-stan Sep 28, 2021
da319e0
FEAT-440: Re-organize spatial functions and add Crosses.
leo-stan Sep 28, 2021
4dbd5f4
FEAT-431: Fix resolution definition.
leo-stan Sep 30, 2021
3d5e4d1
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 30, 2021
28fd867
FEAT-449: Address PR comments.
leo-stan Oct 3, 2021
33655b3
Merge remote-tracking branch 'rr/main' into FEAT-440_spatial2
leo-stan Oct 3, 2021
92cff40
FEAT-449: Merge new cmake structure.
leo-stan Oct 3, 2021
f5adb8c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 3, 2021
5e61769
FEAT-449: New resolution definition.
leo-stan Oct 4, 2021
d718aff
Merge remote-tracking branch 'origin/FEAT-440_spatial2' into FEAT-440…
leo-stan Oct 4, 2021
c82e62f
update test file TEMPORARY
CCInc Oct 5, 2021
b46f834
updated tests
CCInc Oct 5, 2021
212e00c
Merge branch 'main' into FEAT-440_spatial2
CCInc Oct 5, 2021
d77ead7
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 5, 2021
e825858
FEAT-449: Update tests for new autzen file and add Crosses tests.
leo-stan Oct 5, 2021
fccc994
Merge remote-tracking branch 'rr/main' into FEAT-440_spatial2
leo-stan Oct 5, 2021
54f8485
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 5, 2021
d6bd49f
Merge remote-tracking branch 'upstream/main' into FEAT-440_spatial2
CCInc Oct 5, 2021
3027640
Remove temporary test file, update test file link
CCInc Oct 5, 2021
e1bf736
FEAT-449: Address last PR comments.
leo-stan Oct 5, 2021
bd23d1d
FEAT-449: Update CHANGELOG.md.
leo-stan Oct 5, 2021
4afffa0
FEAT-449: Fix punctuation in CHANGELOG.md.
leo-stan Oct 5, 2021
56eb569
FEAT-449: Rename min_resolution to resolution and explain the behavio…
leo-stan Oct 5, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,3 @@ share/python-wheels/
MANIFEST

/CMakeSettings.json
/test/data/autzen-classified.copc.laz
/test/data/writer_test.copc.laz
/test/data/autzen-trimmed.copc.laz
/test/data/new-copc.copc.laz
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- **\[Python/C++\]** Add spatial function `Crosses` to `VoxelKey`
- **\[Python/C++\]** Add resolution function `Resolution` and `GetResolutionAtDepth` to `VoxelKey`
- **\[Python/C++\]** Add `Vector3` operators to `Vector3`
- **\[Python/C++\]** Add resolution query functions `GetDepthAtResolution`, `GetNodesAtResolution` and `GetNodesWithinResolution` to `Reader`
- **\[Python/C++\]** Add `BoundsTrimFileExample` and `ResolutionTrimFileExample` to example-writer files
### Changed

- **\[Python/C++\]** Change order of arguments in `VoxelKey` spatial functions `Intersects`, `Contains`, and `Within`
- **\[Python/C++\]** Add optional `resolution` argument to `Reader` spatial query functions `GetNodesWithinBox`, `GetNodesIntersectBox`, and `GetPointsWithinBox`. `resolution` can be used to limit the resolution during spatial queries
- **\[Python/C++\]** Update `span` of `autzen-classified.copc.laz` test file from 0 to 128

## [1.3.0] - 2021-10-04

### Added
Expand Down
3 changes: 2 additions & 1 deletion cmake/DownloadExampleFiles.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ macro(download_test_files OUT_PATH)

if(NOT EXISTS ${OUT_PATH})
if (EXISTS ${BaseAutzenFilePath})
file(WRITE ${OUT_PATH}) # this will create any necessary pathes, as CREATE_LINK doens't
file(CREATE_LINK ${BaseAutzenFilePath} ${OUT_PATH})
else()
message(STATUS "Downloading test files to ${OUT_PATH}")
file (DOWNLOAD
"https://github.com/PDAL/data/raw/62e514b6484ec59cd48bb48d5c6fe8a00216a6ac/autzen/autzen-classified.copc.laz"
"https://cloudfront.rockrobotic.com/public/laz-examples/copc/drafts/span/autzen-classified.copc.laz"
${OUT_PATH}
)

Expand Down
5 changes: 5 additions & 0 deletions cpp/include/copc-lib/geometry/vector3.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ struct Vector3
Vector3 operator+(const double &d) const { return Vector3(x + d, y + d, z + d); }
Vector3 operator-(const double &d) const { return Vector3(x - d, y - d, z - d); }

Vector3 operator*(const Vector3 &other) const { return Vector3(x * other.x, y * other.y, z * other.z); }
Vector3 operator/(const Vector3 &other) const { return Vector3(x / other.x, y / other.y, z / other.z); }
Vector3 operator+(const Vector3 &other) const { return Vector3(x + other.x, y + other.y, z + other.z); }
Vector3 operator-(const Vector3 &other) const { return Vector3(x - other.x, y - other.y, z - other.z); }

std::string ToString()
{
std::stringstream ss;
Expand Down
15 changes: 11 additions & 4 deletions cpp/include/copc-lib/hierarchy/key.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "copc-lib/geometry/box.hpp"
#include "copc-lib/geometry/vector3.hpp"
#include "copc-lib/las/vlr.hpp"

namespace copc
{
Expand Down Expand Up @@ -46,10 +47,16 @@ class VoxelKey
// Tests whether the current key is a child of a given key
bool ChildOf(VoxelKey parent_key) const;

bool Intersects(const Box &box, const las::LasHeader &header) const;
bool Contains(const Box &vec, const las::LasHeader &header) const;
bool Contains(const Vector3 &point, const las::LasHeader &header) const;
bool Within(const Box &box, const las::LasHeader &header) const;
// Spatial query functions
// Definitions taken from https://shapely.readthedocs.io/en/stable/manual.html#binary-predicates
bool Intersects(const las::LasHeader &header, const Box &box) const;
bool Contains(const las::LasHeader &header, const Box &vec) const;
bool Contains(const las::LasHeader &header, const Vector3 &point) const;
bool Within(const las::LasHeader &header, const Box &box) const;
bool Crosses(const las::LasHeader &header, const Box &box) const;

double Resolution(const las::LasHeader &header, const las::CopcVlr &copc_info) const;
static double GetResolutionAtDepth(int32_t d, const las::LasHeader &header, const las::CopcVlr &copc_info);

int32_t d;
int32_t x;
Expand Down
16 changes: 13 additions & 3 deletions cpp/include/copc-lib/io/reader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define COPCLIB_IO_READER_H_

#include <istream>
#include <limits>
#include <string>

#include "copc-lib/copc/file.hpp"
Expand Down Expand Up @@ -44,10 +45,19 @@ class Reader : public BaseIO
// Helper function to get all points from the root
las::Points GetAllPoints();

// Resolution query functions
// The resulting resolution may not be exactly this value: the minimum possible resolution that is at least as
// precise as the requested resolution will be selected. Therefore the result may be a bit more precise than
// requested.
int32_t GetDepthAtResolution(double resolution);
std::vector<Node> GetNodesAtResolution(double resolution);
std::vector<Node> GetNodesWithinResolution(double resolution);

// Spatial query functions
std::vector<Node> GetNodesWithinBox(const Box &box);
std::vector<Node> GetNodesIntersectBox(const Box &box);
las::Points GetPointsWithinBox(const Box &box);
// Definitions taken from https://shapely.readthedocs.io/en/stable/manual.html#binary-predicates
std::vector<Node> GetNodesWithinBox(const Box &box, double resolution = 0);
std::vector<Node> GetNodesIntersectBox(const Box &box, double resolution = 0);
las::Points GetPointsWithinBox(const Box &box, double resolution = 0);

protected:
Reader() = default;
Expand Down
24 changes: 20 additions & 4 deletions cpp/src/hierarchy/key.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,31 @@ bool VoxelKey::ChildOf(VoxelKey parent_key) const
return false;
}

bool VoxelKey::Intersects(const Box &box, const las::LasHeader &header) const
double VoxelKey::Resolution(const las::LasHeader &header, const las::CopcVlr &copc_info) const
{
if (copc_info.span <= 0)
throw std::runtime_error("VoxelKey::Resolution: Octree span must be greater than 0.");
return (header.max.x - header.min.x) / copc_info.span / std::pow(2, d);
}

double VoxelKey::GetResolutionAtDepth(int32_t d, const las::LasHeader &header, const las::CopcVlr &copc_info)
{
return VoxelKey(d, 0, 0, 0).Resolution(header, copc_info);
}

bool VoxelKey::Intersects(const las::LasHeader &header, const Box &box) const
{
return Box(*this, header).Intersects(box);
}
bool VoxelKey::Contains(const Box &box, const las::LasHeader &header) const { return Box(*this, header).Contains(box); }
bool VoxelKey::Contains(const Vector3 &point, const las::LasHeader &header) const
bool VoxelKey::Contains(const las::LasHeader &header, const Box &box) const { return Box(*this, header).Contains(box); }
bool VoxelKey::Contains(const las::LasHeader &header, const Vector3 &point) const
{
return Box(*this, header).Contains(point);
}
bool VoxelKey::Within(const Box &box, const las::LasHeader &header) const { return Box(*this, header).Within(box); }
bool VoxelKey::Within(const las::LasHeader &header, const Box &box) const { return Box(*this, header).Within(box); }
bool VoxelKey::Crosses(const las::LasHeader &header, const Box &box) const
{
return Box(*this, header).Intersects(box) && !Box(*this, header).Within(box);
}

} // namespace copc
100 changes: 89 additions & 11 deletions cpp/src/io/reader.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#include "copc-lib/io/reader.hpp"
#include <cmath>
#include <stdexcept>

#include "copc-lib/hierarchy/internal/hierarchy.hpp"
#include "copc-lib/io/reader.hpp"
#include "copc-lib/las/header.hpp"
#include "copc-lib/laz/decompressor.hpp"

Expand Down Expand Up @@ -186,53 +189,128 @@ las::Points Reader::GetAllPoints()
return out;
}

std::vector<Node> Reader::GetNodesWithinBox(const Box &box)
std::vector<Node> Reader::GetNodesWithinBox(const Box &box, double resolution)
{
std::vector<Node> out;

auto header = GetLasHeader();
auto copc_info = GetCopcHeader();
auto max_depth = GetDepthAtResolution(resolution);

for (const auto &node : GetAllChildren())
{
if (node.key.Within(box, header))
if (node.key.Within(header, box) && node.key.d <= max_depth)
out.push_back(node);
}

return out;
}

std::vector<Node> Reader::GetNodesIntersectBox(const Box &box)
std::vector<Node> Reader::GetNodesIntersectBox(const Box &box, double resolution)
{
std::vector<Node> out;

auto header = GetLasHeader();
auto copc_info = GetCopcHeader();
auto max_depth = GetDepthAtResolution(resolution);

// Get all nodes in octree
for (const auto &node : GetAllChildren())
{
if (node.key.Intersects(box, header))
if (node.key.Intersects(header, box) && node.key.d <= max_depth)
out.push_back(node);
}

return out;
}

las::Points Reader::GetPointsWithinBox(const Box &box)
las::Points Reader::GetPointsWithinBox(const Box &box, double resolution)
{
auto header = GetLasHeader();
auto copc_info = GetCopcHeader();
auto max_depth = GetDepthAtResolution(resolution);
auto out = las::Points(header);

// Get all nodes in octree
for (const auto &node : GetAllChildren())
{
// If node fits in Box
if (node.key.Intersects(box, header))
if (node.key.d <= max_depth)
{
// Add points that fit in the box
auto points = GetPoints(node);
out.AddPoints(points.GetWithin(box));
// If node fits in Box
if (node.key.Within(header, box))
{
// If the node is within the box add all points
out.AddPoints(GetPoints(node));
}
else if (node.key.Intersects(header, box))
{
// If the node only crosses the box then get subset of points within box
auto points = GetPoints(node);
out.AddPoints(points.GetWithin(box));
}
}
}
return out;
}

int32_t Reader::GetDepthAtResolution(double resolution)
leo-stan marked this conversation as resolved.
Show resolved Hide resolved
{
// Compute max depth
int32_t max_depth = -1;
// Get all nodes in octree
for (const auto &node : GetAllChildren())
{
if (node.key.d > max_depth)
max_depth = node.key.d;
}

// If query resolution is <=0 return the octree's max depth
if (resolution <= 0.0)
return max_depth;
if (GetCopcHeader().span <= 0)
throw std::runtime_error("Reader::GetDepthAtResolution: Octree span must be greater than 0.");

auto current_resolution = (GetLasHeader().max.x - GetLasHeader().min.x) / GetCopcHeader().span;

for (int32_t i = 0; i <= max_depth; i++)
{
if (current_resolution <= resolution)
return i;
current_resolution /= 2;
}
return max_depth;
}

std::vector<Node> Reader::GetNodesAtResolution(double resolution)
{
auto target_depth = GetDepthAtResolution(resolution);

std::vector<Node> out;

for (const auto &node : GetAllChildren())
{
if (node.key.d == target_depth)
out.push_back(node);
}

return out;
}

std::vector<Node> Reader::GetNodesWithinResolution(double resolution)
{
auto target_depth = GetDepthAtResolution(resolution);

std::vector<Node> out;

for (const auto &node : GetAllChildren())
{
if (node.key.d <= target_depth)
out.push_back(node);
}

return out;
}

las::EbVlr Reader::ReadExtraByteVlr(std::map<uint64_t, las::VlrHeader> &vlrs)
{
for (auto &[offset, vlr_header] : vlrs)
Expand Down
Loading