use crate::tiff::errors::{TiffError, TiffResult};
use crate::coordinate::BoundingBox;
use std::f64::consts::PI;
use log::{debug, info};
pub fn coord_to_bbox(coord_str: &str, radius: f64, shape: &str, epsg: Option<u32>) -> TiffResult<String> {
debug!("Converting coordinate '{}' with radius {} meters to bounding box (shape: {})",
coord_str, radius, shape);
let parts: Vec<&str> = coord_str.split(',').collect();
if parts.len() != 2 {
return Err(TiffError::GenericError(
"Coordinate must be in format 'x,y' or 'lon,lat' for EPSG:4326".to_string()));
}
let x = parts[0].trim().parse::<f64>()
.map_err(|_| TiffError::GenericError("Invalid x/longitude coordinate".to_string()))?;
let y = parts[1].trim().parse::<f64>()
.map_err(|_| TiffError::GenericError("Invalid y/latitude coordinate".to_string()))?;
debug!("Parsed coordinates: x/lon={}, y/lat={}", x, y);
match shape.to_lowercase().as_str() {
"circle" => {
let (min_x, min_y, max_x, max_y) = calculate_circle_bbox(x, y, radius, epsg);
debug!("Calculated circular bounding box: min_x={}, min_y={}, max_x={}, max_y={}",
min_x, min_y, max_x, max_y);
Ok(format!("{},{},{},{}", min_x, min_y, max_x, max_y))
},
"square" | _ => {
if let Some(code) = epsg {
if code == 3857 || code == 3785 || code == 900913 {
debug!("Square bbox for projected coordinates (EPSG:{}) in meters", code);
return Ok(format!("{},{},{},{}",
x - radius, y - radius,
x + radius, y + radius));
}
else if code == 4326 {
debug!("Square bbox for WGS84 coordinates (EPSG:4326)");
let lon = x; let lat = y;
let lat_degree_meters = meters_per_latitude_degree();
let lon_degree_meters = meters_per_longitude_degree(lat);
let lat_buffer = radius / lat_degree_meters;
let lon_buffer = radius / lon_degree_meters;
debug!("Lat buffer: {} degrees, Lon buffer: {} degrees at latitude {}",
lat_buffer, lon_buffer, lat);
return Ok(format!("{},{},{},{}",
lon - lon_buffer, lat - lat_buffer,
lon + lon_buffer, lat + lat_buffer));
}
}
debug!("Using general calculation for square bbox");
let half_size = radius / meters_per_degree(y, epsg);
let bbox = format!("{},{},{},{}",
x - half_size, y - half_size,
x + half_size, y + half_size);
debug!("Calculated square bounding box: {}", bbox);
Ok(bbox)
}
}
}
fn calculate_circle_bbox(x: f64, y: f64, radius: f64, epsg: Option<u32>) -> (f64, f64, f64, f64) {
if let Some(code) = epsg {
if code == 3857 || code == 3785 || code == 900913 {
debug!("Circle bbox for Web Mercator (EPSG:{})", code);
return (x - radius, y - radius, x + radius, y + radius);
}
else if code == 4326 {
debug!("Circle bbox for WGS84 (EPSG:4326)");
let lat_degree_meters = meters_per_latitude_degree();
let lon_degree_meters = meters_per_longitude_degree(y);
let lat_buffer = radius / lat_degree_meters;
let lon_buffer = radius / lon_degree_meters;
debug!("Lat buffer: {} degrees, Lon buffer: {} degrees at latitude {}",
lat_buffer, lon_buffer, y);
return (x - lon_buffer, y - lat_buffer, x + lon_buffer, y + lat_buffer);
}
}
debug!("Generic circle bbox calculation");
let degrees_per_m = 1.0 / meters_per_degree(y, epsg);
let radius_deg = radius * degrees_per_m;
debug!("Converting radius {} meters to {} degrees at latitude/y={}",
radius, radius_deg, y);
(x - radius_deg, y - radius_deg, x + radius_deg, y + radius_deg)
}
fn meters_per_latitude_degree() -> f64 {
111_320.0
}
fn meters_per_longitude_degree(latitude: f64) -> f64 {
let lat_rad = latitude * PI / 180.0;
111_320.0 * f64::cos(lat_rad)
}
fn meters_per_degree(latitude: f64, epsg: Option<u32>) -> f64 {
if let Some(code) = epsg {
match code {
3857 | 3785 | 900913 => {
debug!("EPSG code {} indicates coordinates are in meters already", code);
return 1.0; },
4326 => {
let lat_meters = meters_per_latitude_degree();
let lon_meters = meters_per_longitude_degree(latitude);
debug!("At latitude {}: lat meters/deg={}, lon meters/deg={}",
latitude, lat_meters, lon_meters);
return (lat_meters + lon_meters) / 2.0; },
_ => {}
}
}
let lat_rad = latitude.abs() * PI / 180.0;
let lat_length = 111_132.92 - 559.82 * f64::cos(2.0 * lat_rad) + 1.175 * f64::cos(4.0 * lat_rad);
let lon_length = 111_412.84 * f64::cos(lat_rad) - 93.5 * f64::cos(3.0 * lat_rad);
let meters_per_deg = (lat_length + lon_length) / 2.0;
debug!("At latitude {}: estimated {} meters per degree", latitude, meters_per_deg);
meters_per_deg
}