use std::os::unix::fs::FileExt as _;
use std::path::Path;
use std::sync::Arc;
use crate::blob_meta::ElemKind;
use crate::block::PrimitiveBlock;
use crate::elements::{Element, MemberId};
use super::super::Result;
use super::blob_meta::BlobMeta;
use crate::idset::IdSet;
pub(super) struct RelationScanOutput {
pub(super) member_node_ids: Option<IdSet>,
pub(super) member_way_ids: Option<IdSet>,
}
#[hotpath::measure]
pub(super) fn collect_relation_member_ids_indexed(
input: &Path,
blob_meta: &[BlobMeta],
want_nodes: bool,
want_ways: bool,
) -> Result<RelationScanOutput> {
let file = Arc::new(
std::fs::File::open(input).map_err(|e| format!("open pbf for relation scan: {e}"))?,
);
let mut node_ids = want_nodes.then(IdSet::new);
let mut way_ids = want_ways.then(IdSet::new);
let mut read_buf: Vec<u8> = Vec::new();
let mut decompress_buf: Vec<u8> = Vec::new();
let mut st_scratch: Vec<(u32, u32)> = Vec::new();
let mut gr_scratch: Vec<(u32, u32)> = Vec::new();
for meta in blob_meta
.iter()
.filter(|m| matches!(m.kind, ElemKind::Relation))
{
read_buf.resize(meta.data_size, 0);
file.read_exact_at(&mut read_buf, meta.data_offset)
.map_err(|e| format!("relation scan pread: {e}"))?;
crate::blob::decompress_blob_raw(&read_buf, &mut decompress_buf)
.map_err(|e| format!("relation scan decompress: {e}"))?;
let block = PrimitiveBlock::from_vec_with_scratch(
std::mem::take(&mut decompress_buf),
&mut st_scratch,
&mut gr_scratch,
)?;
for element in block.elements_skip_metadata() {
if let Element::Relation(r) = element {
let complete = r.tags().any(|(key, value)| {
key == "type" && matches!(value, "multipolygon" | "boundary")
});
for member in r.members() {
match member.id {
MemberId::Node(id) if want_nodes && id >= 0 => {
node_ids.as_mut().expect("requested").set(id);
}
MemberId::Way(id) if want_ways && complete && id >= 0 => {
way_ids.as_mut().expect("requested").set(id);
}
_ => {}
}
}
}
}
}
Ok(RelationScanOutput {
member_node_ids: node_ids,
member_way_ids: way_ids,
})
}