tp_lib_core/crs/
transform.rs1use crate::errors::ProjectionError;
7use geo::Point;
8use proj4rs::proj::Proj;
9
10pub struct CrsTransformer {
15 source_crs: String,
16 target_crs: String,
17 from_proj: Proj,
18 to_proj: Proj,
19 source_is_geographic: bool,
20 target_is_geographic: bool,
21}
22
23impl CrsTransformer {
24 pub fn new(source_crs: String, target_crs: String) -> Result<Self, ProjectionError> {
30 let source_proj_str = Self::epsg_to_proj_string(&source_crs)?;
32 let target_proj_str = Self::epsg_to_proj_string(&target_crs)?;
33
34 let from_proj = Proj::from_proj_string(&source_proj_str).map_err(|e| {
35 ProjectionError::InvalidCrs(format!(
36 "Failed to create source projection from {}: {:?}",
37 source_crs, e
38 ))
39 })?;
40
41 let to_proj = Proj::from_proj_string(&target_proj_str).map_err(|e| {
42 ProjectionError::InvalidCrs(format!(
43 "Failed to create target projection from {}: {:?}",
44 target_crs, e
45 ))
46 })?;
47
48 let source_is_geographic = source_proj_str.contains("+proj=longlat");
50 let target_is_geographic = target_proj_str.contains("+proj=longlat");
51
52 Ok(Self {
53 source_crs,
54 target_crs,
55 from_proj,
56 to_proj,
57 source_is_geographic,
58 target_is_geographic,
59 })
60 }
61
62 fn epsg_to_proj_string(epsg: &str) -> Result<String, ProjectionError> {
63 let code = if epsg.starts_with("EPSG:") {
65 epsg.strip_prefix("EPSG:")
66 .and_then(|s| s.parse::<u16>().ok())
67 } else {
68 epsg.parse::<u16>().ok()
69 };
70
71 if let Some(code) = code {
72 crs_definitions::from_code(code)
74 .ok_or_else(|| ProjectionError::InvalidCrs(format!("Unknown EPSG code: {}", epsg)))
75 .map(|def| def.proj4.to_string())
76 } else {
77 Ok(epsg.to_string())
79 }
80 }
81
82 pub fn transform(&self, point: Point<f64>) -> Result<Point<f64>, ProjectionError> {
86 let mut coord = (point.x(), point.y(), 0.0);
87
88 if self.source_is_geographic {
90 coord.0 = coord.0.to_radians();
91 coord.1 = coord.1.to_radians();
92 }
93
94 proj4rs::transform::transform(&self.from_proj, &self.to_proj, &mut coord).map_err(|e| {
96 ProjectionError::TransformFailed(format!(
97 "proj4rs transformation failed ({} -> {}): {:?}",
98 self.source_crs, self.target_crs, e
99 ))
100 })?;
101
102 if self.target_is_geographic {
104 coord.0 = coord.0.to_degrees();
105 coord.1 = coord.1.to_degrees();
106 }
107
108 Ok(Point::new(coord.0, coord.1))
109 }
110}