12constexpr std::uint32_t kHilbertMax = (1u << 16) - 1;
21std::uint32_t
hilbert(std::uint32_t x, std::uint32_t y) {
22 std::uint32_t a = x ^ y;
23 std::uint32_t b = 0xFFFF ^ a;
24 std::uint32_t c = 0xFFFF ^ (x | y);
25 std::uint32_t d = x & (y ^ 0xFFFF);
27 std::uint32_t aa = a | (b >> 1);
28 std::uint32_t bb = (a >> 1) ^ a;
29 std::uint32_t cc = ((c >> 1) ^ (b & (d >> 1))) ^ c;
30 std::uint32_t dd = ((a & (c >> 1)) ^ (d >> 1)) ^ d;
36 aa = (a & (a >> 2)) ^ (b & (b >> 2));
37 bb = (a & (b >> 2)) ^ (b & ((a ^ b) >> 2));
38 cc ^= (a & (c >> 2)) ^ (b & (d >> 2));
39 dd ^= (b & (c >> 2)) ^ ((a ^ b) & (d >> 2));
45 aa = (a & (a >> 4)) ^ (b & (b >> 4));
46 bb = (a & (b >> 4)) ^ (b & ((a ^ b) >> 4));
47 cc ^= (a & (c >> 4)) ^ (b & (d >> 4));
48 dd ^= (b & (c >> 4)) ^ ((a ^ b) & (d >> 4));
54 cc ^= (a & (c >> 8)) ^ (b & (d >> 8));
55 dd ^= (b & (c >> 8)) ^ ((a ^ b) & (d >> 8));
60 std::uint32_t i0 = x ^ y;
61 std::uint32_t i1 = b | (0xFFFF ^ (i0 | a));
63 i0 = (i0 | (i0 << 8)) & 0x00FF00FF;
64 i0 = (i0 | (i0 << 4)) & 0x0F0F0F0F;
65 i0 = (i0 | (i0 << 2)) & 0x33333333;
66 i0 = (i0 | (i0 << 1)) & 0x55555555;
68 i1 = (i1 | (i1 << 8)) & 0x00FF00FF;
69 i1 = (i1 | (i1 << 4)) & 0x0F0F0F0F;
70 i1 = (i1 | (i1 << 2)) & 0x33333333;
71 i1 = (i1 | (i1 << 1)) & 0x55555555;
73 return (i1 << 1) | i0;
85std::uint32_t saturating_f64_to_u32(
double v) {
88 if (v >=
static_cast<double>(std::numeric_limits<std::uint32_t>::max()))
89 return std::numeric_limits<std::uint32_t>::max();
90 return static_cast<std::uint32_t
>(v);
95 const double x = std::floor(hilbert_max * ((r.
min_x + r.
max_x) / 2.0 - extent.
min_x) /
97 const double y = std::floor(hilbert_max * ((r.
min_y + r.
max_y) / 2.0 - extent.
min_y) /
99 return hilbert(saturating_f64_to_u32(x), saturating_f64_to_u32(y));
103 std::stable_sort(items.begin(), items.end(), [&extent](
const NodeItem& a,
const NodeItem& b) {
104 const std::uint32_t ha = hilbert_bbox(a, kHilbertMax, extent);
105 const std::uint32_t hb = hilbert_bbox(b, kHilbertMax, extent);
112 for (
const auto& n : nodes)
131 "rtree node_size must be >= 2, got " + std::to_string(
node_size));
133 const std::uint16_t branching_factor =
node_size;
138 const std::uint64_t num_nodes = level_bounds.front().end;
140 std::vector<NodeItem> tree(
static_cast<std::size_t
>(num_nodes),
NodeItem::empty(0));
144 const std::uint64_t leaf_start = num_nodes - nodes.size();
145 for (std::size_t i = 0; i < nodes.size(); ++i)
146 tree[
static_cast<std::size_t
>(leaf_start) + i] = nodes[i];
151 for (std::size_t level = 0; level + 1 < level_bounds.size(); ++level) {
152 const LevelBound& children_level = level_bounds[level];
153 const LevelBound& parent_level = level_bounds[level + 1];
155 std::uint64_t parent_idx = parent_level.
start;
156 std::uint64_t child_idx = children_level.
start;
157 while (child_idx < children_level.
end) {
159 for (std::uint16_t j = 0; j < branching_factor; ++j) {
160 if (child_idx >= children_level.
end)
162 parent_node.
expand(tree[
static_cast<std::size_t
>(child_idx)]);
165 tree[
static_cast<std::size_t
>(parent_idx)] = parent_node;
175 for (std::size_t i = 0; i < tree.size(); ++i)
Every failure the library reports is one of these.
void hilbert_sort(std::vector< NodeItem > &items, const NodeItem &extent)
Sorts items in place by descending Hilbert index (the item furthest along the curve first) – a STABLE...
std::vector< std::uint8_t > encode_packed_rtree(const std::vector< NodeItem > &tree)
Serializes every node in tree (as returned by build_packed_rtree) in array order, 40 bytes each.
EncodedGeometry encode(const nlohmann::ordered_json &geometry)
Flattens one CityJSON geometry object – its boundaries and whatever semantics, material and texture i...
std::vector< NodeItem > build_packed_rtree(const std::vector< NodeItem > &nodes, const NodeItem &extent, std::uint16_t node_size)
Builds the full flat packed-R-tree node array (leaves first in nodes's own order at the array's tail ...
std::vector< LevelBound > rtree_level_bounds(std::uint64_t num_items, std::uint16_t node_size)
Mirrors generate_level_bounds (packed_rtree/mod.rs:342-375).
NodeItem calc_extent(const std::vector< NodeItem > &nodes)
The bbox union of every item, via repeated NodeItem::expand starting from NodeItem::empty(0).
std::uint32_t hilbert_bbox(const NodeItem &r, std::uint32_t hilbert_max, const NodeItem &extent)
A NodeItem's Hilbert index: its bbox center, scaled into [0, hilbert_max] against extent,...
std::uint32_t hilbert(std::uint32_t x, std::uint32_t y)
The Hilbert curve index of point (x, y) on a 65536x65536 grid (16 bits per axis).
Half-open [start, end) node index range for one tree level, in the flat node array shared by every le...
One R-tree node entry: 4 doubles then a u64, all little-endian, 40 bytes with no padding (packed_rtre...
static NodeItem empty(std::uint64_t offset)
The "empty" node used as the fold/aggregation identity: any real bbox's expand widens it.
void expand(const NodeItem &r)
Widens this node's bbox to also cover r, leaving offset untouched.
static constexpr std::size_t kSize