1use std::fmt;
32use std::fs::File;
33use std::io::{Read, Write};
34use std::path::Path;
35
36#[derive(Debug, PartialEq)]
40pub struct WorldFile {
41 pub x_scale: f64,
42 pub y_scale: f64,
43
44 pub x_skew: f64,
45 pub y_skew: f64,
46
47 pub x_coord: f64,
48 pub y_coord: f64,
49}
50
51impl WorldFile {
52 pub fn from_path(p: impl AsRef<Path>) -> Result<Self, ()> {
54 let p: &Path = p.as_ref();
55 let mut f = File::open(p).or(Err(()))?;
56 Self::from_reader(&mut f)
57 }
58
59 pub fn from_reader(mut r: impl Read) -> Result<Self, ()> {
61 let mut s = String::new();
62 r.read_to_string(&mut s).or(Err(()))?;
63 Self::from_string(&s)
64 }
65
66 pub fn from_string(s: impl AsRef<str>) -> Result<Self, ()> {
68 let s: &str = s.as_ref();
69 let lines: Vec<&str> = s.lines().collect();
70 let x_scale = lines.get(0).and_then(|s| s.parse().ok()).ok_or(())?;
71 let y_skew = lines.get(1).and_then(|s| s.parse().ok()).ok_or(())?;
72 let x_skew = lines.get(2).and_then(|s| s.parse().ok()).ok_or(())?;
73 let y_scale = lines.get(3).and_then(|s| s.parse().ok()).ok_or(())?;
74 let x_coord = lines.get(4).and_then(|s| s.parse().ok()).ok_or(())?;
75 let y_coord = lines.get(5).and_then(|s| s.parse().ok()).ok_or(())?;
76 assert!(x_scale != 0.);
77 assert!(y_scale != 0.);
78
79 Ok(WorldFile {
80 x_scale,
81 y_scale,
82 x_skew,
83 y_skew,
84 x_coord,
85 y_coord,
86 })
87 }
88
89 pub fn to_string(&self) -> String {
91 format!(
92 "{}\n{}\n{}\n{}\n{}\n{}\n",
93 self.x_scale, self.y_skew, self.x_skew, self.y_scale, self.x_coord, self.y_coord
94 )
95 }
96
97 pub fn write_to_writer(&self, mut w: impl Write) {
99 write!(
100 w,
101 "{}\n{}\n{}\n{}\n{}\n{}\n",
102 self.x_scale, self.y_skew, self.x_skew, self.y_scale, self.x_coord, self.y_coord
103 )
104 .unwrap();
105 }
106
107 pub fn write_to_path(&self, p: impl AsRef<Path>) {
109 let mut f = File::create(p).unwrap();
110 self.write_to_writer(&mut f);
111 }
112
113 pub fn image_to_world(&self, image_x_y: impl Into<(f64, f64)>) -> (f64, f64) {
115 let x_y = image_x_y.into();
116 let x = x_y.0;
117 let y = x_y.1;
118
119 (
120 self.x_scale * x + self.x_skew * y + self.x_coord,
121 self.y_skew * x + self.y_scale * y + self.y_coord,
122 )
123 }
124
125 pub fn world_to_image(&self, world_x_y: impl Into<(f64, f64)>) -> (f64, f64) {
127 let x_y = world_x_y.into();
128 let x = x_y.0;
129 let y = x_y.1;
130
131 let a = self.x_scale;
132 let b = self.x_skew;
133 let c = self.x_coord;
134 let d = self.y_skew;
135 let e = self.y_scale;
136 let f = self.y_coord;
137
138 let det = a * e - b * d; assert!(det != 0.);
140
141 (
142 (x * e - b * y + b * f - c * e) / det,
143 (-x * d + a * y - a * f - c * d) / det,
144 )
145 }
146}
147
148impl fmt::Display for WorldFile {
149 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
150 write!(f, "{}", self.to_string())
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn test_simple() {
160 let test = "32.0\n0.0\n0.0\n-32.0\n691200.0\n4576000.0\n";
161 let w = WorldFile::from_string(&test).unwrap();
162 assert_eq!(w.image_to_world((171., 343.)), (696672., 4565024.));
163 assert_eq!(w.world_to_image((696672., 4565024.)), (171., 343.));
164 let p = (100., 200.);
165 assert_eq!(w.image_to_world(w.world_to_image(p)), p);
166 }
167}