Skip to content

Commit 42e4ddc

Browse files
authored
Integrate KdQuiver, tested nearest with filter options (#12)
* sorted->sort, misc updates * export quiver & bush * export arrows * before add filter logic * add xyz filter * filter by xyz * filter * filter by angle * okay * integrate nearest filter * fix position * fix nearest * release * update upwards * fix test
1 parent a832f75 commit 42e4ddc

12 files changed

+710
-134
lines changed

docs/about/release-notes.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ To upgrade `fast-crossing` to the latest version, use pip:
1010
pip install -U fast-crossing
1111
```
1212

13+
## Version 0.0.6 (2023-03-12)
14+
15+
* Integrate KdQuiver (kdtree + dirs)
16+
1317
## Version 0.0.5 (2023-03-08)
1418

1519
* Add nanaflann KdTree, use identical interface to `scipy.spatial.cKDTree`

fast_crossing/spatial.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ def query_ball_point(
7171
ii, dd = self.tree.nearest(
7272
xyz,
7373
radius=r,
74+
sort=return_sorted,
7475
return_squared_l2=True,
75-
sorted=return_sorted,
7676
)
7777
if return_length:
7878
return len(ii)
@@ -87,8 +87,8 @@ def query_ball_point(
8787
ii, dd = self.tree.nearest(
8888
xyz,
8989
radius=rr,
90+
sort=return_sorted,
9091
return_squared_l2=True,
91-
sorted=return_sorted,
9292
)
9393
ret_ii.append(ii.tolist())
9494
if return_length:

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def build_extension(self, ext):
124124
# logic and declaration, and simpler if you include description/version in a file.
125125
setup(
126126
name="fast_crossing",
127-
version="0.0.5",
127+
version="0.0.6",
128128
author="tzx",
129129
author_email="dvorak4tzx@gmail.com",
130130
url="https://fast-crossing.readthedocs.io",

src/fast_crossing.hpp

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,6 @@ struct FastCrossing
7676
return;
7777
}
7878
if (!quiver_) {
79-
bush_ = FlatBush();
80-
bush_->Finish();
8179
return;
8280
}
8381
auto &polylines = quiver_->polylines();
@@ -562,37 +560,32 @@ struct FastCrossing
562560
return nearest(point_index(index[0], index[1]), return_squared_l2);
563561
}
564562

565-
std::pair<IntNx2, Eigen::VectorXd>
566-
nearest(const Eigen::Vector3d &position, //
567-
std::optional<int> k = std::nullopt,
568-
std::optional<double> radius = std::nullopt,
569-
bool sorted = true, //
570-
bool return_squared_l2 = false) const
563+
std::pair<IntNx2, Eigen::VectorXd> nearest(
564+
const Eigen::Vector3d &position, //
565+
std::optional<int> k = std::nullopt,
566+
std::optional<double> radius = std::nullopt,
567+
bool sort = true, //
568+
bool return_squared_l2 = false,
569+
std::optional<std::pair<Eigen::Vector3d, Quiver::FilterParams>> filter =
570+
std::nullopt) const
571571
{
572-
if (k) {
573-
auto [ii, dd] =
574-
quiver_->nearest(position, *k, sorted, return_squared_l2);
575-
auto vec_ii = point_index(ii);
576-
return {
577-
Eigen::Map<const IntNx2>(vec_ii[0].data(), vec_ii.size(), 2),
578-
dd};
579-
} else if (radius) {
580-
auto [ii, dd] =
581-
quiver_->nearest(position, *radius, sorted, return_squared_l2);
582-
auto vec_ii = point_index(ii);
583-
return {
584-
Eigen::Map<const IntNx2>(vec_ii[0].data(), vec_ii.size(), 2),
585-
dd};
586-
} else {
572+
if (!k && !radius) {
587573
throw std::invalid_argument("should specify k or radius");
588574
}
575+
auto [ii, dd] =
576+
k ? quiver_->nearest(position, *k, sort, return_squared_l2)
577+
: quiver_->nearest(position, *radius, sort, return_squared_l2);
578+
if (filter) {
579+
auto [dir, params] = *filter;
580+
auto ii_dd = quiver_->filter(ii, dd, Arrow(position, dir), params);
581+
ii = ii_dd.first;
582+
dd = ii_dd.second;
583+
}
584+
auto vec_ii = point_index(ii);
585+
return {Eigen::Map<const IntNx2>(vec_ii[0].data(), vec_ii.size(), 2),
586+
dd};
589587
}
590588

591-
const FlatBush &bush() const
592-
{
593-
finish();
594-
return *bush_;
595-
}
596589
bool is_wgs84() const { return is_wgs84_; }
597590
int num_poylines() const
598591
{
@@ -614,6 +607,18 @@ struct FastCrossing
614607
return quiver_->polyline(label);
615608
}
616609

610+
const FlatBush *export_bush(bool autobuild = true) const
611+
{
612+
if (autobuild) {
613+
finish();
614+
}
615+
return bush_ ? &*bush_ : nullptr;
616+
}
617+
const KdQuiver *export_quiver() const
618+
{
619+
return quiver_ ? &*quiver_ : nullptr;
620+
}
621+
617622
private:
618623
const bool is_wgs84_{false};
619624

@@ -631,6 +636,11 @@ struct FastCrossing
631636

632637
// auto rebuild flatbush
633638
mutable std::optional<FlatBush> bush_;
639+
const FlatBush &bush() const
640+
{
641+
finish();
642+
return *bush_;
643+
}
634644
};
635645
} // namespace cubao
636646

src/kd_quiver.hpp

Lines changed: 107 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -72,26 +72,28 @@ struct KdQuiver : Quiver
7272
std::pair<Eigen::VectorXi, Eigen::VectorXd>
7373
nearest(const Eigen::Vector3d &position, //
7474
int k, //
75-
bool sorted = true, //
75+
bool sort = true, //
7676
bool return_squared_l2 = false) const
7777
{
7878
return tree().nearest(is_wgs84_ ? lla2enu(position) : position, //
7979
k, //
80-
sorted, //
80+
sort, //
8181
return_squared_l2);
8282
}
8383
std::pair<Eigen::VectorXi, Eigen::VectorXd>
8484
nearest(const Eigen::Vector3d &position, //
8585
double radius, //
86-
bool sorted = true, //
86+
bool sort = true, //
8787
bool return_squared_l2 = false) const
8888
{
8989
return tree().nearest(is_wgs84_ ? lla2enu(position) : position, //
9090
radius, //
91-
sorted, //
91+
sort, //
9292
return_squared_l2);
9393
}
9494

95+
Eigen::Map<const RowVectors> positions() const { return tree().points(); }
96+
9597
RowVectors positions(const Eigen::VectorXi &hits) const
9698
{
9799
const int N = hits.size();
@@ -118,7 +120,7 @@ struct KdQuiver : Quiver
118120
}
119121
return coords;
120122
}
121-
std::vector<Arrow> arrows(const Eigen::VectorXi &hits)
123+
std::vector<Arrow> arrows(const Eigen::VectorXi &hits) const
122124
{
123125
const int N = hits.size();
124126
std::vector<Arrow> arrows;
@@ -127,41 +129,12 @@ struct KdQuiver : Quiver
127129
auto _ = index(hits[i]);
128130
int line_idx = _[0];
129131
int pt_idx = _[1];
130-
auto &ruler = polylines_.at(line_idx);
131-
arrows.push_back(Arrow(ruler.at(pt_idx), ruler.dir(pt_idx)));
132+
arrows.push_back(arrow(line_idx, pt_idx));
132133
}
133134
return arrows;
134135
}
135136

136-
static Eigen::VectorXi
137-
filter(const std::vector<Arrow> &arrows, const Arrow &arrow,
138-
// angle
139-
std::optional<Eigen::VectorXd> angle_slots = std::nullopt,
140-
// positions
141-
std::optional<Eigen::VectorXd> x_slots = std::nullopt,
142-
std::optional<Eigen::VectorXd> y_slots = std::nullopt,
143-
std::optional<Eigen::VectorXd> z_slots = std::nullopt,
144-
bool is_wgs84 = false)
145-
{
146-
Eigen::VectorXi mask(arrows.size());
147-
return mask;
148-
}
149-
150-
Eigen::VectorXi
151-
filter(const Eigen::VectorXi &hits, const Arrow &arrow,
152-
std::optional<Eigen::VectorXd> angle_slots = std::nullopt,
153-
std::optional<Eigen::VectorXd> x_slots = std::nullopt,
154-
std::optional<Eigen::VectorXd> y_slots = std::nullopt,
155-
std::optional<Eigen::VectorXd> z_slots = std::nullopt)
156-
{
157-
return filter(arrows(hits), arrow, //
158-
angle_slots, //
159-
x_slots, y_slots, z_slots, //
160-
is_wgs84_);
161-
}
162-
163-
// helpers
164-
Arrow arrow(const PolylineRuler &ruler, int segment_index, double t)
137+
Arrow arrow(const PolylineRuler &ruler, int segment_index, double t) const
165138
{
166139

167140
auto &polyline_ = ruler.polyline();
@@ -177,11 +150,108 @@ struct KdQuiver : Quiver
177150
}
178151
return arrow;
179152
}
180-
Arrow arrow(const PolylineRuler &ruler, double range)
153+
Arrow arrow(const PolylineRuler &ruler, double range) const
181154
{
182155
auto [seg_idx, t] = ruler.segment_index_t(range);
183156
return arrow(ruler, seg_idx, t);
184157
}
158+
Arrow arrow(int point_index) const
159+
{
160+
Eigen::Vector2i ii = this->index(point_index);
161+
return arrow(ii[0], ii[1]);
162+
}
163+
Arrow arrow(int polyline_index, int segment_index) const
164+
{
165+
auto ruler = polyline(polyline_index);
166+
return Arrow(ruler->at(segment_index), ruler->dir(segment_index)) //
167+
.polyline_index(polyline_index) //
168+
.segment_index(segment_index) //
169+
.t(0.0) //
170+
.range(ruler->range(segment_index)) //
171+
;
172+
}
173+
Arrow arrow(int polyline_index, int segment_index, double t) const
174+
{
175+
auto ruler = polyline(polyline_index);
176+
return Arrow(ruler->at(segment_index, t), ruler->dir(segment_index))
177+
.polyline_index(polyline_index) //
178+
.segment_index(segment_index) //
179+
.t(t) //
180+
.range(ruler->range(segment_index, t)) //
181+
;
182+
}
183+
Arrow arrow(int polyline_index, double range) const
184+
{
185+
auto ruler = polyline(polyline_index);
186+
auto [xyz, dir] = ruler->arrow(range, false);
187+
auto [idx, t] = ruler->segment_index_t(range);
188+
return Arrow(xyz, dir) //
189+
.polyline_index(polyline_index) //
190+
.segment_index(idx) //
191+
.t(t) //
192+
.range(range) //
193+
;
194+
}
195+
196+
static Eigen::VectorXi filter(const std::vector<Arrow> &arrows,
197+
const Arrow &arrow,
198+
const Quiver::FilterParams &params,
199+
bool is_wgs84 = false)
200+
{
201+
if (is_wgs84) {
202+
Quiver quiver(arrow.position());
203+
return quiver.filter(arrows, arrow, params);
204+
} else {
205+
Quiver quiver;
206+
return quiver.filter(arrows, arrow, params);
207+
}
208+
}
209+
210+
static Eigen::VectorXi select_by_mask(const Eigen::VectorXi &indexes,
211+
const Eigen::VectorXi &mask)
212+
{
213+
std::vector<int> ret;
214+
ret.reserve(mask.sum());
215+
for (int i = 0, N = indexes.size(); i < N; ++i) {
216+
if (mask[i]) {
217+
ret.push_back(indexes[i]);
218+
}
219+
}
220+
return Eigen::VectorXi::Map(&ret[0], ret.size());
221+
}
222+
static Eigen::VectorXd select_by_mask(const Eigen::VectorXd &norms,
223+
const Eigen::VectorXi &mask)
224+
{
225+
std::vector<double> ret;
226+
ret.reserve(mask.sum());
227+
for (int i = 0, N = norms.size(); i < N; ++i) {
228+
if (mask[i]) {
229+
ret.push_back(norms[i]);
230+
}
231+
}
232+
return Eigen::VectorXd::Map(&ret[0], ret.size());
233+
}
234+
235+
Eigen::VectorXi filter(const Eigen::VectorXi &hits, //
236+
const Arrow &arrow, //
237+
const Quiver::FilterParams &params) const
238+
{
239+
auto mask = filter(arrows(hits), arrow, //
240+
params, is_wgs84_);
241+
return select_by_mask(hits, mask);
242+
}
243+
244+
std::pair<Eigen::VectorXi, Eigen::VectorXd>
245+
filter(const Eigen::VectorXi &hits, //
246+
const Eigen::VectorXd &norms, //
247+
const Arrow &arrow, //
248+
const Quiver::FilterParams &params) const
249+
{
250+
auto mask = filter(arrows(hits), arrow, //
251+
params, is_wgs84_);
252+
return {select_by_mask(hits, mask), //
253+
select_by_mask(norms, mask)};
254+
}
185255

186256
void reset() { reset_index(); }
187257

src/main.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@
2121
#include "pybind11_nanoflann_kdtree.hpp"
2222
#include "pybind11_quiver.hpp"
2323

24+
#define CUBAO_ARGV_DEFAULT_NONE(argv) py::arg_v(#argv, std::nullopt, "None")
25+
2426
#include "pybind11_polyline_ruler.hpp"
27+
#include "pybind11_crs_transform.hpp"
2528

2629
#include "point_in_polygon.hpp"
2730
#include "densify_polyline.hpp"
@@ -40,6 +43,9 @@ PYBIND11_MODULE(_pybind11_fast_crossing, m)
4043
cubao::bind_quiver(m);
4144
cubao::bind_polyline_ruler(m);
4245

46+
auto tf = m.def_submodule("tf");
47+
cubao::bind_crs_transform(tf);
48+
4349
m.def("point_in_polygon", &cubao::point_in_polygon, //
4450
py::kw_only(), "points"_a, "polygon"_a,
4551
"point-in-polygon test, returns 0-1 mask");

src/nanoflann_kdtree.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,15 @@ struct KdTree : PointCloud
122122
std::pair<Eigen::VectorXi, Eigen::VectorXd>
123123
nearest(const Eigen::Vector3d &position, //
124124
int k, //
125-
bool sorted = true, //
125+
bool sort = true, //
126126
bool return_squared_l2 = false) const
127127
{
128128
auto indexes = std::vector<int>(k);
129129
auto distances = std::vector<double>(k);
130130
auto resultSet = nanoflann::KNNResultSet<double, int, int>(k);
131131
resultSet.init(&indexes[0], &distances[0]);
132132
auto params = nanoflann::SearchParams();
133-
params.sorted = sorted;
133+
params.sorted = sort;
134134
index().findNeighbors(resultSet, position.data(), params);
135135
const int N = resultSet.size();
136136
return std::make_pair(
@@ -142,11 +142,11 @@ struct KdTree : PointCloud
142142
std::pair<Eigen::VectorXi, Eigen::VectorXd>
143143
nearest(const Eigen::Vector3d &position, //
144144
double radius, //
145-
bool sorted = true, //
145+
bool sort = true, //
146146
bool return_squared_l2 = false) const
147147
{
148148
auto params = nanoflann::SearchParams();
149-
params.sorted = sorted;
149+
params.sorted = sort;
150150
std::vector<std::pair<int, double>> indices_dists;
151151
index().radiusSearch(position.data(), //
152152
radius * radius, //

0 commit comments

Comments
 (0)