1use crate::Result;
6use serde::{Deserialize, Serialize};
7use std::path::Path;
8
9pub struct DataGenerator {
11 seed: u64,
13}
14
15impl DataGenerator {
16 pub fn new() -> Self {
18 Self { seed: 12345 }
19 }
20
21 pub fn with_seed(seed: u64) -> Self {
23 Self { seed }
24 }
25
26 pub fn generate_raster(&self, width: usize, height: usize, pattern: RasterPattern) -> Vec<f64> {
28 let mut data = vec![0.0; width * height];
29
30 match pattern {
31 RasterPattern::Flat(value) => {
32 data.fill(value);
33 }
34 RasterPattern::Gradient {
35 from,
36 to,
37 direction,
38 } => {
39 for y in 0..height {
40 for x in 0..width {
41 let t = match direction {
42 GradientDirection::Horizontal => x as f64 / (width - 1) as f64,
43 GradientDirection::Vertical => y as f64 / (height - 1) as f64,
44 GradientDirection::Diagonal => {
45 ((x + y) as f64) / ((width + height - 2) as f64)
46 }
47 };
48 data[y * width + x] = from + (to - from) * t;
49 }
50 }
51 }
52 RasterPattern::Checkerboard {
53 size,
54 color1,
55 color2,
56 } => {
57 for y in 0..height {
58 for x in 0..width {
59 let is_odd = ((x / size) + (y / size)) % 2 == 1;
60 data[y * width + x] = if is_odd { color1 } else { color2 };
61 }
62 }
63 }
64 RasterPattern::Noise { min, max } => {
65 for (i, item) in data.iter_mut().enumerate() {
66 *item = min + (max - min) * self.pseudo_random(i);
67 }
68 }
69 RasterPattern::Sine {
70 amplitude,
71 frequency,
72 } => {
73 use std::f64::consts::PI;
74 for y in 0..height {
75 for x in 0..width {
76 let phase = 2.0 * PI * frequency * (x as f64 / width as f64);
77 data[y * width + x] = amplitude * phase.sin();
78 }
79 }
80 }
81 }
82
83 data
84 }
85
86 fn pseudo_random(&self, index: usize) -> f64 {
88 let a = 1103515245u64;
89 let c = 12345u64;
90 let m = 2u64.pow(31);
91
92 let x = ((a
93 .wrapping_mul(self.seed.wrapping_add(index as u64))
94 .wrapping_add(c))
95 % m) as f64;
96 x / m as f64
97 }
98
99 pub fn generate_points(&self, count: usize, bounds: Bounds) -> Vec<Point> {
101 let mut points = Vec::with_capacity(count);
102
103 for i in 0..count {
104 let x = bounds.min_x + (bounds.max_x - bounds.min_x) * self.pseudo_random(i * 2);
105 let y = bounds.min_y + (bounds.max_y - bounds.min_y) * self.pseudo_random(i * 2 + 1);
106
107 points.push(Point { x, y });
108 }
109
110 points
111 }
112
113 pub fn generate_grid(&self, rows: usize, cols: usize, bounds: Bounds) -> Vec<Point> {
115 let mut points = Vec::with_capacity(rows * cols);
116
117 let dx = (bounds.max_x - bounds.min_x) / (cols - 1) as f64;
118 let dy = (bounds.max_y - bounds.min_y) / (rows - 1) as f64;
119
120 for row in 0..rows {
121 for col in 0..cols {
122 let x = bounds.min_x + col as f64 * dx;
123 let y = bounds.min_y + row as f64 * dy;
124 points.push(Point { x, y });
125 }
126 }
127
128 points
129 }
130}
131
132impl Default for DataGenerator {
133 fn default() -> Self {
134 Self::new()
135 }
136}
137
138#[derive(Debug, Clone)]
140pub enum RasterPattern {
141 Flat(f64),
143 Gradient {
145 from: f64,
147 to: f64,
149 direction: GradientDirection,
151 },
152 Checkerboard {
154 size: usize,
156 color1: f64,
158 color2: f64,
160 },
161 Noise {
163 min: f64,
165 max: f64,
167 },
168 Sine {
170 amplitude: f64,
172 frequency: f64,
174 },
175}
176
177#[derive(Debug, Clone, Copy)]
179pub enum GradientDirection {
180 Horizontal,
182 Vertical,
184 Diagonal,
186}
187
188#[derive(Debug, Clone, Serialize, Deserialize)]
190pub struct Point {
191 pub x: f64,
193 pub y: f64,
195}
196
197#[derive(Debug, Clone, Copy)]
199pub struct Bounds {
200 pub min_x: f64,
202 pub min_y: f64,
204 pub max_x: f64,
206 pub max_y: f64,
208}
209
210impl Bounds {
211 pub fn new(min_x: f64, min_y: f64, max_x: f64, max_y: f64) -> Self {
213 Self {
214 min_x,
215 min_y,
216 max_x,
217 max_y,
218 }
219 }
220}
221
222pub struct FileGenerator;
224
225impl FileGenerator {
226 pub fn generate_geotiff(_path: &Path, _width: usize, _height: usize) -> Result<()> {
228 Ok(())
230 }
231
232 pub fn generate_geojson(path: &Path, points: &[Point]) -> Result<()> {
234 use std::io::Write;
235
236 let mut geojson =
237 String::from("{\n \"type\": \"FeatureCollection\",\n \"features\": [\n");
238
239 for (i, point) in points.iter().enumerate() {
240 geojson.push_str(&format!(
241 " {{\n \"type\": \"Feature\",\n \"geometry\": {{\n \"type\": \"Point\",\n \"coordinates\": [{}, {}]\n }},\n \"properties\": {{\n \"id\": {}\n }}\n }}",
242 point.x, point.y, i
243 ));
244
245 if i < points.len() - 1 {
246 geojson.push_str(",\n");
247 } else {
248 geojson.push('\n');
249 }
250 }
251
252 geojson.push_str(" ]\n}");
253
254 let mut file = std::fs::File::create(path)?;
255 file.write_all(geojson.as_bytes())?;
256
257 Ok(())
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264
265 #[test]
266 fn test_generator_creation() {
267 let generator = DataGenerator::new();
268 assert_eq!(generator.seed, 12345);
269 }
270
271 #[test]
272 fn test_generate_flat_raster() {
273 let generator = DataGenerator::new();
274 let data = generator.generate_raster(10, 10, RasterPattern::Flat(42.0));
275 assert_eq!(data.len(), 100);
276 assert!(data.iter().all(|&v| v == 42.0));
277 }
278
279 #[test]
280 fn test_generate_gradient_raster() {
281 let generator = DataGenerator::new();
282 let data = generator.generate_raster(
283 10,
284 10,
285 RasterPattern::Gradient {
286 from: 0.0,
287 to: 100.0,
288 direction: GradientDirection::Horizontal,
289 },
290 );
291 assert_eq!(data.len(), 100);
292 assert_eq!(data[0], 0.0); assert!((data[9] - 100.0).abs() < 0.01); }
295
296 #[test]
297 fn test_generate_checkerboard() {
298 let generator = DataGenerator::new();
299 let data = generator.generate_raster(
300 10,
301 10,
302 RasterPattern::Checkerboard {
303 size: 5,
304 color1: 0.0,
305 color2: 100.0,
306 },
307 );
308 assert_eq!(data.len(), 100);
309 }
310
311 #[test]
312 fn test_generate_noise() {
313 let generator = DataGenerator::new();
314 let data = generator.generate_raster(
315 10,
316 10,
317 RasterPattern::Noise {
318 min: 0.0,
319 max: 100.0,
320 },
321 );
322 assert_eq!(data.len(), 100);
323 assert!(data.iter().all(|&v| (0.0..=100.0).contains(&v)));
324 }
325
326 #[test]
327 fn test_generate_points() {
328 let generator = DataGenerator::new();
329 let bounds = Bounds::new(0.0, 0.0, 100.0, 100.0);
330 let points = generator.generate_points(10, bounds);
331 assert_eq!(points.len(), 10);
332 assert!(points.iter().all(|p| p.x >= 0.0 && p.x <= 100.0));
333 assert!(points.iter().all(|p| p.y >= 0.0 && p.y <= 100.0));
334 }
335
336 #[test]
337 fn test_generate_grid() {
338 let generator = DataGenerator::new();
339 let bounds = Bounds::new(0.0, 0.0, 100.0, 100.0);
340 let points = generator.generate_grid(5, 5, bounds);
341 assert_eq!(points.len(), 25);
342 }
343
344 #[test]
345 fn test_generate_geojson() -> Result<()> {
346 use tempfile::NamedTempFile;
347
348 let temp_file = NamedTempFile::new()?;
349 let points = vec![Point { x: 0.0, y: 0.0 }, Point { x: 1.0, y: 1.0 }];
350
351 FileGenerator::generate_geojson(temp_file.path(), &points)?;
352
353 let content = std::fs::read_to_string(temp_file.path())?;
354 assert!(content.contains("FeatureCollection"));
355 assert!(content.contains("Point"));
356
357 Ok(())
358 }
359}