use geo::{Bearing, BoundingRect, Centroid, Distance, Geodesic, GeodesicArea};
use geo_types::{Geometry, LineString, Point, Polygon};
use serde::Serialize;
#[derive(Debug, Clone, Serialize)]
pub struct DistanceResult {
pub distance_m: f64,
pub distance_km: f64,
pub distance_mi: f64,
pub bearing_deg: f64,
}
#[derive(Debug, Clone)]
pub struct BoundsResult {
pub bbox: [f64; 4],
pub envelope: Polygon<f64>,
pub width_km: f64,
pub height_km: f64,
pub area_km2: f64,
}
pub fn geodesic_distance(from: Point<f64>, to: Point<f64>) -> DistanceResult {
let distance_m = Geodesic.distance(from, to);
let bearing_deg = Geodesic.bearing(from, to);
DistanceResult {
distance_m,
distance_km: distance_m / 1000.0,
distance_mi: distance_m / 1609.344,
bearing_deg,
}
}
pub fn bounding_box(geom: &Geometry<f64>) -> Option<BoundsResult> {
let rect = geom.bounding_rect()?;
let min_lat = rect.min().y;
let min_lon = rect.min().x;
let max_lat = rect.max().y;
let max_lon = rect.max().x;
let width_m = Geodesic.distance(Point::new(min_lon, min_lat), Point::new(max_lon, min_lat));
let height_m = Geodesic.distance(Point::new(min_lon, min_lat), Point::new(min_lon, max_lat));
let envelope = Polygon::new(
LineString::from(vec![
(min_lon, min_lat),
(max_lon, min_lat),
(max_lon, max_lat),
(min_lon, max_lat),
(min_lon, min_lat),
]),
vec![],
);
let area_m2 = envelope.geodesic_area_unsigned();
Some(BoundsResult {
bbox: [min_lat, min_lon, max_lat, max_lon],
envelope,
width_km: width_m / 1000.0,
height_km: height_m / 1000.0,
area_km2: area_m2 / 1_000_000.0,
})
}
pub fn centroid(geom: &Geometry<f64>) -> Option<Point<f64>> {
geom.centroid()
}