Skip to content

Commit dd7cfee

Browse files
Integrate PackedRTree (rbush) (#24)
* update headers * update headers * add planet * add tree * crop features * query * test query * update to 0.1.7 * fix index * bump flake: PyCQA/flake8#1885 * fix lint --------- Co-authored-by: TANG ZHIXIONG <zhixiong.tang@momenta.ai>
1 parent 9205a44 commit dd7cfee

File tree

12 files changed

+354
-8
lines changed

12 files changed

+354
-8
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ tests/rapidjson.png.json
66
__pycache__
77
benchmarks/*.json
88
benchmarks/*.pbf*
9+
# https://github.com/cubao/nano-fmm/blob/master/data/Makefile
10+
data/suzhoubeizhan.json

.pre-commit-config.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ repos:
4545
rev: 5.11.5
4646
hooks:
4747
- id: isort
48+
args: [--profile, black]
4849

4950
# Upgrade older Python syntax
5051
- repo: https://github.com/asottile/pyupgrade
@@ -61,7 +62,7 @@ repos:
6162
exclude: ^(docs|Makefile|benchmarks/Makefile)
6263

6364
- repo: https://github.com/PyCQA/flake8
64-
rev: 3.9.2
65+
rev: 6.1.0
6566
hooks:
6667
- id: flake8
6768
additional_dependencies: [flake8-bugbear]

.vscode/settings.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,14 @@
9393
"regex": "cpp",
9494
"future": "cpp",
9595
"__bits": "cpp",
96-
"bit": "cpp"
96+
"bit": "cpp",
97+
"charconv": "cpp",
98+
"concepts": "cpp",
99+
"memory_resource": "cpp",
100+
"ranges": "cpp",
101+
"shared_mutex": "cpp",
102+
"span": "cpp",
103+
"stop_token": "cpp"
97104
},
98105
"workbench.colorCustomizations": {
99106
"activityBar.background": "#143328",

data/suzhoubeizhan.pbf

165 KB
Binary file not shown.

data/suzhoubeizhan_crossover.json

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
{
2+
"type": "Feature",
3+
"properties": {},
4+
"geometry": {
5+
"coordinates": [
6+
[
7+
[
8+
120.65771383441779,
9+
31.406929844034934
10+
],
11+
[
12+
120.65685254868885,
13+
31.405354623300482
14+
],
15+
[
16+
120.65945178597582,
17+
31.403989410609327
18+
],
19+
[
20+
120.66031307170232,
21+
31.402597923391937
22+
],
23+
[
24+
120.66214330387294,
25+
31.401980936609206
26+
],
27+
[
28+
120.66508090340801,
29+
31.403634977144364
30+
],
31+
[
32+
120.66558844678389,
33+
31.4051183379097
34+
],
35+
[
36+
120.66348137277282,
37+
31.406772323151472
38+
],
39+
[
40+
120.66488096208019,
41+
31.407730571105034
42+
],
43+
[
44+
120.66429651819169,
45+
31.408557544349165
46+
],
47+
[
48+
120.66354289318042,
49+
31.409581405887053
50+
],
51+
[
52+
120.66086675538332,
53+
31.409030097216544
54+
],
55+
[
56+
120.66140505896414,
57+
31.407336771760555
58+
],
59+
[
60+
120.66147901958857,
61+
31.407062414261205
62+
],
63+
[
64+
120.66108904538675,
65+
31.406620542422047
66+
],
67+
[
68+
120.65915262176173,
69+
31.40651150879127
70+
],
71+
[
72+
120.65771383441779,
73+
31.406929844034934
74+
]
75+
]
76+
],
77+
"type": "Polygon"
78+
}
79+
}

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 `pybind11-geobuf` to the latest version, use pip:
1010
pip install -U pybind11-geobuf
1111
```
1212

13+
## Version 0.1.7 (2023-11-11)
14+
15+
* Integrate PackedRTree (rbush)
16+
1317
## Version 0.1.6 (2023-07-02)
1418

1519
* Crop geojson features by polygon (alpha release)

headers

Submodule headers updated 774 files

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ def build_extension(self, ext):
127127
# logic and declaration, and simpler if you include description/version in a file.
128128
setup(
129129
name="pybind11_geobuf",
130-
version="0.1.6",
130+
version="0.1.7",
131131
author="tzx",
132132
author_email="dvorak4tzx@gmail.com",
133133
url="https://geobuf-cpp.readthedocs.io",

src/geobuf/geojson_cropping.hpp

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
namespace cubao
2121
{
2222
using RowVectors = Eigen::Matrix<double, Eigen::Dynamic, 3, Eigen::RowMajor>;
23+
using RowVectorsNx2 = Eigen::Matrix<double, Eigen::Dynamic, 2, Eigen::RowMajor>;
2324
using BboxType = mapbox::geometry::box<double>;
2425

2526
inline BboxType row_vectors_to_bbox(const RowVectors &coords)
@@ -30,7 +31,7 @@ inline BboxType row_vectors_to_bbox(const RowVectors &coords)
3031
auto bbox = BboxType({max_t, max_t, max_t}, {min_t, min_t, min_t});
3132
auto &min = bbox.min;
3233
auto &max = bbox.max;
33-
for (int i = 1, N = coords.rows(); i < N; ++i) {
34+
for (int i = 0, N = coords.rows(); i < N; ++i) {
3435
double x = coords(i, 0);
3536
double y = coords(i, 1);
3637
double z = coords(i, 2);
@@ -50,6 +51,30 @@ inline BboxType row_vectors_to_bbox(const RowVectors &coords)
5051
return bbox;
5152
}
5253

54+
inline BboxType
55+
row_vectors_to_bbox(const Eigen::Ref<const RowVectorsNx2> &coords)
56+
{
57+
using limits = std::numeric_limits<double>;
58+
double min_t = limits::has_infinity ? -limits::infinity() : limits::min();
59+
double max_t = limits::has_infinity ? limits::infinity() : limits::max();
60+
auto bbox = BboxType({max_t, max_t, 0.0}, {min_t, min_t, 0.0});
61+
auto &min = bbox.min;
62+
auto &max = bbox.max;
63+
for (int i = 0, N = coords.rows(); i < N; ++i) {
64+
double x = coords(i, 0);
65+
double y = coords(i, 1);
66+
if (min.x > x)
67+
min.x = x;
68+
if (min.y > y)
69+
min.y = y;
70+
if (max.x < x)
71+
max.x = x;
72+
if (max.y < y)
73+
max.y = y;
74+
}
75+
return bbox;
76+
}
77+
5378
inline RowVectors bbox2row_vectors(const BboxType &bbox)
5479
{
5580
auto coords = RowVectors(5, 3);
@@ -176,7 +201,7 @@ inline int geojson_cropping(const mapbox::geojson::feature &feature,
176201
double len2 = std::get<5>(k2) - std::get<2>(k2);
177202
return len1 < len2;
178203
});
179-
keys = {*itr};
204+
keys = {*itr}; // pick longest
180205
}
181206
for (auto &key : keys) {
182207
auto &coords = segs[key];

src/geobuf/planet.hpp

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
#ifndef CUBAO_PLANET_HPP
2+
#define CUBAO_PLANET_HPP
3+
4+
// https://github.com/microsoft/vscode-cpptools/issues/9692
5+
#if __INTELLISENSE__
6+
#undef __ARM_NEON
7+
#undef __ARM_NEON__
8+
#endif
9+
10+
#include "geojson_cropping.hpp"
11+
#include "packedrtree.hpp"
12+
13+
namespace cubao
14+
{
15+
struct Planet
16+
{
17+
using FeatureCollection = mapbox::geojson::feature_collection;
18+
Planet() = default;
19+
Planet(const FeatureCollection &features) { this->features(features); }
20+
21+
const FeatureCollection &features() const { return features_; }
22+
Planet &features(const FeatureCollection &features)
23+
{
24+
features_ = features;
25+
rtree_.reset();
26+
return *this;
27+
}
28+
29+
void build() const { this->rtree(); }
30+
31+
// TODO, query by style expression
32+
Eigen::VectorXi query(const Eigen::Vector2d &min,
33+
const Eigen::Vector2d &max) const
34+
{
35+
auto &tree = this->rtree();
36+
auto hits = tree.search(min[0], min[1], max[0], max[1]);
37+
Eigen::VectorXi index(hits.size());
38+
for (int i = 0, N = hits.size(); i < N; ++i) {
39+
index[i] = hits[i].offset;
40+
}
41+
return index;
42+
}
43+
FeatureCollection copy(const Eigen::VectorXi &index) const
44+
{
45+
auto fc = FeatureCollection();
46+
fc.reserve(index.size());
47+
for (int i = 0, N = index.size(); i < N; ++i) {
48+
fc.push_back(features_[index[i]]);
49+
}
50+
return fc;
51+
}
52+
53+
FeatureCollection crop(const Eigen::Ref<const RowVectorsNx2> &polygon,
54+
const std::string &clipping_mode = "longest",
55+
bool strip_properties = false,
56+
bool is_wgs84 = true) const
57+
{
58+
auto bbox = row_vectors_to_bbox(polygon);
59+
auto hits =
60+
this->query({bbox.min.x, bbox.min.y}, {bbox.max.x, bbox.max.y});
61+
auto fc = FeatureCollection();
62+
fc.reserve(hits.size());
63+
for (auto idx : hits) {
64+
auto &feature = features_[idx];
65+
if (!feature.geometry.is<mapbox::geojson::line_string>() ||
66+
clipping_mode == "whole") {
67+
// only check centroid
68+
auto centroid = geometry_to_centroid(feature.geometry);
69+
auto mask = point_in_polygon(
70+
Eigen::Map<const RowVectorsNx2>(&centroid.x, 1, 2),
71+
polygon);
72+
if (mask[0]) {
73+
fc.emplace_back(
74+
feature.geometry,
75+
strip_properties
76+
? mapbox::feature::property_map{{"index", idx}}
77+
: feature.properties,
78+
feature.id);
79+
}
80+
continue;
81+
}
82+
auto &line_string =
83+
feature.geometry.get<mapbox::geojson::line_string>();
84+
auto polyline = Eigen::Map<const RowVectors>(&line_string[0].x,
85+
line_string.size(), 3);
86+
auto segs = polyline_in_polygon(polyline, polygon, is_wgs84);
87+
if (segs.empty()) {
88+
continue;
89+
}
90+
if (clipping_mode == "first") {
91+
auto &coords = segs.begin()->second;
92+
auto geom = mapbox::geojson::line_string();
93+
geom.resize(coords.rows());
94+
as_row_vectors(geom) = coords;
95+
fc.emplace_back(
96+
geom,
97+
strip_properties
98+
? mapbox::feature::property_map{{"index", idx}}
99+
: feature.properties,
100+
feature.id);
101+
continue;
102+
}
103+
// longest or all
104+
std::vector<PolylineChunks::key_type> keys;
105+
keys.reserve(segs.size());
106+
for (auto &pair : segs) {
107+
keys.push_back(pair.first);
108+
}
109+
if (clipping_mode == "longest") { // else assume all
110+
auto itr = std::max_element(
111+
keys.begin(), keys.end(),
112+
[](const auto &k1, const auto &k2) {
113+
double len1 = std::get<5>(k1) - std::get<2>(k1);
114+
double len2 = std::get<5>(k2) - std::get<2>(k2);
115+
return len1 < len2;
116+
});
117+
keys = {*itr}; // pick longest
118+
}
119+
for (auto &key : keys) {
120+
auto &coords = segs[key];
121+
auto geom = mapbox::geojson::line_string();
122+
geom.resize(coords.rows());
123+
as_row_vectors(geom) = coords;
124+
fc.emplace_back(
125+
geom,
126+
strip_properties
127+
? mapbox::feature::property_map{{"index", idx}}
128+
: feature.properties,
129+
feature.id);
130+
}
131+
}
132+
return fc;
133+
}
134+
135+
private:
136+
FeatureCollection features_;
137+
138+
mutable std::optional<FlatGeobuf::PackedRTree> rtree_;
139+
140+
template <typename G>
141+
static FlatGeobuf::NodeItem envelope_2d(G const &geometry, uint64_t index)
142+
{
143+
// mapbox/geometry/envelope.hpp
144+
using limits = std::numeric_limits<double>;
145+
constexpr double min_t =
146+
limits::has_infinity ? -limits::infinity() : limits::min();
147+
constexpr double max_t =
148+
limits::has_infinity ? limits::infinity() : limits::max();
149+
double min_x = max_t;
150+
double min_y = max_t;
151+
double max_x = min_t;
152+
double max_y = min_t;
153+
mapbox::geometry::for_each_point(
154+
geometry, [&](mapbox::geojson::point const &point) {
155+
if (min_x > point.x)
156+
min_x = point.x;
157+
if (min_y > point.y)
158+
min_y = point.y;
159+
if (max_x < point.x)
160+
max_x = point.x;
161+
if (max_y < point.y)
162+
max_y = point.y;
163+
});
164+
return {min_x, min_y, max_x, max_y, index};
165+
}
166+
167+
FlatGeobuf::PackedRTree &rtree() const
168+
{
169+
if (rtree_) {
170+
return *rtree_;
171+
}
172+
auto nodes = std::vector<FlatGeobuf::NodeItem>{};
173+
nodes.reserve(features_.size());
174+
uint64_t index{0};
175+
for (auto &feature : features_) {
176+
nodes.emplace_back(envelope_2d(feature.geometry, index++));
177+
}
178+
auto extent = calcExtent(nodes);
179+
hilbertSort(nodes, extent);
180+
rtree_ = FlatGeobuf::PackedRTree(nodes, extent);
181+
return *rtree_;
182+
}
183+
};
184+
} // namespace cubao
185+
186+
#endif

0 commit comments

Comments
 (0)