Bulk-load from segment tile entries (assumed Hilbert-ordered by the writer). Time complexity O(n) — one pass per level.
(entries: &[TileEntry])
| 31 | /// Bulk-load from segment tile entries (assumed Hilbert-ordered by |
| 32 | /// the writer). Time complexity O(n) — one pass per level. |
| 33 | pub fn build(entries: &[TileEntry]) -> Self { |
| 34 | let mut nodes: Vec<RNode> = Vec::new(); |
| 35 | if entries.is_empty() { |
| 36 | return Self { nodes, root: None }; |
| 37 | } |
| 38 | // Leaves — chunk consecutive tiles, recording each tile's |
| 39 | // absolute index and individual bbox so per-tile filtering can |
| 40 | // happen at leaf descent time. |
| 41 | let mut current_level: Vec<usize> = Vec::new(); |
| 42 | let mut cursor = 0usize; |
| 43 | for chunk in entries.chunks(FANOUT) { |
| 44 | let bbox = chunk_bbox(chunk); |
| 45 | let tiles: Vec<(usize, BBox)> = chunk |
| 46 | .iter() |
| 47 | .enumerate() |
| 48 | .map(|(i, e)| (cursor + i, BBox::from_mbr(&e.mbr))) |
| 49 | .collect(); |
| 50 | cursor += chunk.len(); |
| 51 | nodes.push(RNode { |
| 52 | bbox, |
| 53 | kind: RNodeKind::Leaf { tiles }, |
| 54 | }); |
| 55 | current_level.push(nodes.len() - 1); |
| 56 | } |
| 57 | // Internal levels |
| 58 | while current_level.len() > 1 { |
| 59 | let mut next_level: Vec<usize> = Vec::new(); |
| 60 | for chunk in current_level.chunks(FANOUT) { |
| 61 | let mut bbox = BBox { |
| 62 | min: vec![], |
| 63 | max: vec![], |
| 64 | }; |
| 65 | for &child in chunk { |
| 66 | bbox.extend(&nodes[child].bbox); |
| 67 | } |
| 68 | let children = chunk.to_vec(); |
| 69 | nodes.push(RNode { |
| 70 | bbox, |
| 71 | kind: RNodeKind::Internal { children }, |
| 72 | }); |
| 73 | next_level.push(nodes.len() - 1); |
| 74 | } |
| 75 | current_level = next_level; |
| 76 | } |
| 77 | let root = current_level.first().copied(); |
| 78 | Self { nodes, root } |
| 79 | } |
| 80 | |
| 81 | pub fn node_count(&self) -> usize { |
| 82 | self.nodes.len() |