use super::feature_collection_p_buffer::*;
use crate::{
ArcGISGeometry, ArcGISMultipoint, ArcGISPoint, ArcGISPolygon, ArcGISPolyline, GeometryType,
Result,
};
pub fn decode_geometry(
pbf_geometry: &Geometry,
geometry_type: GeometryType,
transform: Option<&Transform>,
_has_z: bool,
_has_m: bool,
) -> Result<ArcGISGeometry> {
let (x_scale, y_scale, x_translate, y_translate) = if let Some(t) = transform {
let scale = t.scale.as_ref().ok_or_else(|| {
crate::Error::from(crate::ErrorKind::Other(
"Missing scale in transform".to_string(),
))
})?;
let translate = t.translate.as_ref().ok_or_else(|| {
crate::Error::from(crate::ErrorKind::Other(
"Missing translate in transform".to_string(),
))
})?;
(
scale.x_scale,
scale.y_scale,
translate.x_translate,
translate.y_translate,
)
} else {
(1.0, 1.0, 0.0, 0.0)
};
match geometry_type {
GeometryType::Point => decode_point(
&pbf_geometry.coords,
x_scale,
y_scale,
x_translate,
y_translate,
),
GeometryType::Multipoint => decode_multipoint(
&pbf_geometry.coords,
x_scale,
y_scale,
x_translate,
y_translate,
),
GeometryType::Polyline => decode_polyline(
&pbf_geometry.coords,
&pbf_geometry.lengths,
x_scale,
y_scale,
x_translate,
y_translate,
),
GeometryType::Polygon => decode_polygon(
&pbf_geometry.coords,
&pbf_geometry.lengths,
x_scale,
y_scale,
x_translate,
y_translate,
),
GeometryType::Envelope => Err(crate::Error::from(crate::ErrorKind::Other(
"Envelope geometry not yet supported in PBF".to_string(),
))),
}
}
fn decode_point(
coords: &[i64],
x_scale: f64,
y_scale: f64,
x_translate: f64,
y_translate: f64,
) -> Result<ArcGISGeometry> {
if coords.len() < 2 {
return Err(crate::Error::from(crate::ErrorKind::Other(
"Point geometry requires at least 2 coordinates".to_string(),
)));
}
let x = (coords[0] as f64 * x_scale) + x_translate;
let y = (coords[1] as f64 * y_scale) + y_translate;
Ok(ArcGISGeometry::Point(ArcGISPoint::new(x, y)))
}
fn decode_multipoint(
coords: &[i64],
x_scale: f64,
y_scale: f64,
x_translate: f64,
y_translate: f64,
) -> Result<ArcGISGeometry> {
if coords.len() % 2 != 0 {
return Err(crate::Error::from(crate::ErrorKind::Other(
"Multipoint coordinates must be in pairs".to_string(),
)));
}
let mut points = Vec::new();
let mut x_accum: i64 = 0;
let mut y_accum: i64 = 0;
for chunk in coords.chunks(2) {
x_accum += chunk[0];
y_accum += chunk[1];
let x = (x_accum as f64 * x_scale) + x_translate;
let y = (y_accum as f64 * y_scale) + y_translate;
points.push(vec![x, y]);
}
Ok(ArcGISGeometry::Multipoint(ArcGISMultipoint::new(points)))
}
fn decode_polyline(
coords: &[i64],
lengths: &[u32],
x_scale: f64,
y_scale: f64,
x_translate: f64,
y_translate: f64,
) -> Result<ArcGISGeometry> {
let paths = decode_paths(coords, lengths, x_scale, y_scale, x_translate, y_translate)?;
Ok(ArcGISGeometry::Polyline(ArcGISPolyline::new(paths)))
}
fn decode_polygon(
coords: &[i64],
lengths: &[u32],
x_scale: f64,
y_scale: f64,
x_translate: f64,
y_translate: f64,
) -> Result<ArcGISGeometry> {
let rings = decode_paths(coords, lengths, x_scale, y_scale, x_translate, y_translate)?;
Ok(ArcGISGeometry::Polygon(ArcGISPolygon::new(rings)))
}
fn decode_paths(
coords: &[i64],
lengths: &[u32],
x_scale: f64,
y_scale: f64,
x_translate: f64,
y_translate: f64,
) -> Result<Vec<Vec<Vec<f64>>>> {
let mut paths = Vec::new();
let mut coord_idx = 0;
let mut x_accum: i64 = 0;
let mut y_accum: i64 = 0;
for &length in lengths {
let mut path = Vec::new();
let point_count = length as usize;
for _ in 0..point_count {
if coord_idx + 1 >= coords.len() {
return Err(crate::Error::from(crate::ErrorKind::Other(
"Insufficient coordinates for geometry".to_string(),
)));
}
x_accum += coords[coord_idx];
y_accum += coords[coord_idx + 1];
coord_idx += 2;
let x = (x_accum as f64 * x_scale) + x_translate;
let y = (y_accum as f64 * y_scale) + y_translate;
path.push(vec![x, y]);
}
paths.push(path);
}
Ok(paths)
}