use crate::error::Result;
#[derive(Clone, Copy)]
pub(crate) struct NodeTuple {
pub id: i64,
pub lat: i32,
pub lon: i32,
}
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
pub(crate) fn extract_node_tuples(
decompressed: &[u8],
out: &mut Vec<NodeTuple>,
group_starts: &mut Vec<(usize, usize)>,
) -> Result<()> {
use crate::read::wire::{Cursor, PackedSint64Iter, WIRE_LEN, WIRE_VARINT, WireDenseNodes};
let buffer = decompressed;
let mut cursor = Cursor::new(buffer);
let mut granularity: i64 = 100;
let mut lat_offset: i64 = 0;
let mut lon_offset: i64 = 0;
group_starts.clear();
while let Some((field, wire_type)) = cursor.read_tag()? {
match (field, wire_type) {
(1, WIRE_LEN) => {
cursor.skip_field(wire_type)?;
}
(2, WIRE_LEN) => {
let data = cursor.read_len_delimited()?;
let offset = data.as_ptr() as usize - buffer.as_ptr() as usize;
group_starts.push((offset, data.len()));
}
(17, WIRE_VARINT) => {
granularity = cursor.read_varint()? as i64;
}
(19, WIRE_VARINT) => {
lat_offset = cursor.read_varint_i64()?;
}
(20, WIRE_VARINT) => {
lon_offset = cursor.read_varint_i64()?;
}
_ => {
cursor.skip_field(wire_type)?;
}
}
}
for &(off, len) in group_starts.iter() {
let group_data = &buffer[off..off + len];
let mut gcursor = Cursor::new(group_data);
let mut dense_data: Option<&[u8]> = None;
while let Some((field, wire_type)) = gcursor.read_tag()? {
match (field, wire_type) {
(1, WIRE_LEN) => {
return Err(crate::error::new_wire_error(
"PrimitiveGroup contains non-dense Node messages (field 1); \
scan::node only supports DenseNodes (field 2)",
));
}
(2, WIRE_LEN) => {
dense_data = Some(gcursor.read_len_delimited()?);
break;
}
_ => {
gcursor.skip_field(wire_type)?;
}
}
}
let Some(dd) = dense_data else { continue };
let dense = WireDenseNodes::parse(dd)?;
let mut ids = PackedSint64Iter::new(dense.id_data);
let mut lats = PackedSint64Iter::new(dense.lat_data);
let mut lons = PackedSint64Iter::new(dense.lon_data);
let mut cum_id: i64 = 0;
let mut cum_lat: i64 = 0;
let mut cum_lon: i64 = 0;
while let (Some(did), Some(dlat), Some(dlon)) = (ids.next(), lats.next(), lons.next()) {
cum_id += did;
cum_lat += dlat;
cum_lon += dlon;
out.push(NodeTuple {
id: cum_id,
lat: ((lat_offset + granularity * cum_lat) / 100) as i32,
lon: ((lon_offset + granularity * cum_lon) / 100) as i32,
});
}
}
Ok(())
}