1#[cfg(test)]
2mod test {
3 use crate::array_ops::{trimm_array4_owned, fill_nodata_simple};
4 use crate::types::{Dimension, RasterDataShape, BlockSize, ImageSize, SamplingMethod};
5
6 use crate::data_sources::{DataSource, DataSourceBuilder};
7 use crate::metadata::Layer;
8 use crate::selection::Stack;
9 use crate::rasterdataset::builder::get_overlap;
10 use crate::*;
11 use std::path::PathBuf;
12 use std::collections::BTreeMap;
13 use ndarray::Array4;
14
15 macro_rules! td {
16 ($rel:expr_2021) => {
17 std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
18 .join("data")
19 .join($rel)
20 };
21 }
22
23 use num_traits::FromPrimitive;
24
25 use std::ops::{Add, Div};
26 #[test]
27 fn test_fill_nodata_simple() {
28 let nodata = -999.0;
30 let bands = 3;
31 let size = 256;
32
33 let mut data = Array3::<f32>::zeros((bands, size, size));
35 for b in 0..bands {
36 for r in 0..size {
37 for c in 0..size {
38 data[[b, r, c]] = (r * size + c) as f32 + b as f32 * 10000.0;
39 }
40 }
41 }
42
43 data[[0, 50, 50]] = nodata;
46 data[[1, 50, 50]] = nodata;
47 data[[2, 50, 50]] = nodata;
48
49 for i in 0..10 {
51 data[[0, 100 + i, 100]] = nodata;
52 data[[1, 100 + i, 100]] = nodata;
53 data[[2, 100 + i, 100]] = nodata;
54 }
55
56 for i in 0..20 {
58 data[[0, 150 + i, 150]] = nodata;
59 data[[1, 150 + i, 150]] = nodata;
60 data[[2, 150 + i, 150]] = nodata;
61 }
62
63 fill_nodata_simple(&mut data, nodata);
65
66 println!("data {data:?}");
67
68 fill_nodata_simple(&mut data, nodata);
70 println!("data {data:?}");
71
72 for val in data.iter() {
74 assert_ne!(*val, nodata, "Found remaining nodata value!");
75 }
76
77 let filled_val = data[[0, 0, 2]];
80 assert!(
81 filled_val > 0.0 && filled_val < 10.0,
82 "Filled value seems wrong"
83 );
84 }
85
86 #[test]
87 fn test_align() {
88 let data_source = DataSourceBuilder::from_file(&PathBuf::from(
89 "data/cemsre_t55jfm_20200614_sub_abam5.tif",
90 ))
91 .build();
92 let rds_1 = RasterDatasetBuilder::<i16>::from_source(&data_source).build();
93
94 let data_source = DataSourceBuilder::from_file(&PathBuf::from(
95 "data/cemsre_t55jfm_20200614_suba_abam5.tif",
96 ))
97 .build();
98 let rds_2 = RasterDatasetBuilder::<i16>::from_source(&data_source)
99 .set_template(&rds_1)
100 .build();
101 assert_eq!(rds_1.metadata.shape, rds_2.metadata.shape);
102 assert_eq!(rds_1.metadata.geo_transform, rds_2.metadata.geo_transform);
103
104 let data_source = DataSourceBuilder::from_file(&PathBuf::from(
105 "data/cemsre_t55jfm_20200614_suba_abam6.tif",
106 ))
107 .build();
108 let rds_2 = RasterDatasetBuilder::<i16>::from_source(&data_source)
109 .set_template(&rds_1)
110 .build();
111 println!(
112 "rds_1 {:?} rds_2{:?}",
113 rds_1.metadata.geo_transform, rds_2.metadata.geo_transform
114 );
115 assert_eq!(rds_1.metadata.shape, rds_2.metadata.shape);
116 assert_eq!(rds_1.metadata.geo_transform, rds_2.metadata.geo_transform);
117 }
118
119 #[test]
120 fn test_iter() {
121 let data_source = DataSourceBuilder::from_file(&PathBuf::from(td!(
122 "cemsre_t55jfm_20200614_sub_abam5.tif"
123 )))
124 .build();
125 let rds = RasterDatasetBuilder::<i16>::from_source(&data_source).build();
126 for iter in rds.iter() {
127 let _ = iter.parent.read_block::<i32>(iter.iter_index);
128 }
129 }
130 #[test]
131 fn test_stacked_dims() {
132 let mut shp_1 = RasterDataShape {
133 times: 1,
134 layers: 1,
135 rows: 1024,
136 cols: 1024,
137 };
138 let shp_2 = RasterDataShape {
139 times: 1,
140 layers: 1,
141 rows: 1024,
142 cols: 1024,
143 };
144 let e = RasterDataShape {
145 times: 1,
146 layers: 2,
147 rows: 1024,
148 cols: 1024,
149 };
150 shp_1.stack(shp_2, Dimension::Layer);
151 assert_eq!(shp_1, e);
152 let mut shp_1 = RasterDataShape {
153 times: 1,
154 layers: 1,
155 rows: 1024,
156 cols: 1024,
157 };
158 let shp_2 = RasterDataShape {
159 times: 1,
160 layers: 2,
161 rows: 1024,
162 cols: 1024,
163 };
164 let e = RasterDataShape {
165 times: 1,
166 layers: 3,
167 rows: 1024,
168 cols: 1024,
169 };
170 shp_1.stack(shp_2, Dimension::Layer);
171 assert_eq!(shp_1, e);
172 let mut shp_1 = RasterDataShape {
173 times: 1,
174 layers: 1,
175 rows: 1024,
176 cols: 1024,
177 };
178 let shp_2 = RasterDataShape {
179 times: 1,
180 layers: 1,
181 rows: 1024,
182 cols: 1024,
183 };
184 let e = RasterDataShape {
185 times: 2,
186 layers: 1,
187 rows: 1024,
188 cols: 1024,
189 };
190 shp_1.stack(shp_2, Dimension::Time);
191 assert_eq!(shp_1, e);
192 }
193
194 #[test]
195 fn test_shape_extend() {
196 let mut shp_1 = RasterDataShape {
197 times: 1,
198 layers: 1,
199 rows: 1024,
200 cols: 1024,
201 };
202 let shp_2 = RasterDataShape {
203 times: 1,
204 layers: 1,
205 rows: 1024,
206 cols: 1024,
207 };
208 let e = RasterDataShape {
209 times: 2,
210 layers: 1,
211 rows: 1024,
212 cols: 1024,
213 };
214 shp_1.extend(shp_2);
215 assert_eq!(shp_1, e);
216
217 let mut shp_1 = RasterDataShape {
218 times: 1,
219 layers: 2,
220 rows: 1024,
221 cols: 1024,
222 };
223 let shp_2 = RasterDataShape {
224 times: 1,
225 layers: 2,
226 rows: 1024,
227 cols: 1024,
228 };
229 let e = RasterDataShape {
230 times: 2,
231 layers: 2,
232 rows: 1024,
233 cols: 1024,
234 };
235 shp_1.extend(shp_2);
236 assert_eq!(shp_1, e);
237 }
238 #[test]
239 #[should_panic(expected = "Unable to extend layers")]
240 fn test_shape_extend_diff_layer() {
241 let mut shp_1 = RasterDataShape {
242 times: 1,
243 layers: 1,
244 rows: 1024,
245 cols: 1024,
246 };
247 let shp_2 = RasterDataShape {
248 times: 1,
249 layers: 2,
250 rows: 1024,
251 cols: 1024,
252 };
253 let _e = RasterDataShape {
254 times: 2,
255 layers: 2,
256 rows: 1024,
257 cols: 1024,
258 };
259 shp_1.extend(shp_2);
260 }
261
262 #[test]
263 #[should_panic(expected = "Unable to stack layers")]
264 fn test_shape_stack_diff_time() {
265 let mut shp_1 = RasterDataShape {
266 times: 1,
267 layers: 1,
268 rows: 1024,
269 cols: 1024,
270 };
271 let shp_2 = RasterDataShape {
272 times: 2,
273 layers: 1,
274 rows: 1024,
275 cols: 1024,
276 };
277 shp_1.stack(shp_2, Dimension::Layer);
278 }
279
280 #[test]
281 #[should_panic(expected = "Unable to stack layers")]
282 fn test_shape_stack_diff_time2() {
283 let mut shp_1 = RasterDataShape {
284 times: 1,
285 layers: 1,
286 rows: 1024,
287 cols: 1024,
288 };
289 let shp_2 = RasterDataShape {
290 times: 2,
291 layers: 2,
292 rows: 1024,
293 cols: 1024,
294 };
295 shp_1.stack(shp_2, Dimension::Layer);
296 }
297
298 #[test]
299 fn test_layer_stack_position() {
300 let layer_1: Layer = Layer {
301 source: PathBuf::from("image_1.img"),
302 layer_pos: 0,
303 time_pos: 0,
304 };
305 let mut layer_2: Layer = Layer {
306 source: PathBuf::from("image_1.img"),
307 layer_pos: 0,
308 time_pos: 0,
309 };
310 let max_time_rds_1 = 3;
311 layer_2.stack_position(layer_1, Dimension::Layer, max_time_rds_1);
312 assert_eq!(layer_2.layer_pos, 3);
313 }
314
315 #[test]
316 fn test_reduce() {
317 unsafe { std::env::set_var("RAYON_NUM_THREADS", "1") };
319
320 fn worker<T>(rdb: &RasterDataBlock<T>, dimension: Dimension) -> Array3<T>
321 where
322 T: RasterType + FromPrimitive + Add<Output = T> + Div<Output = T>,
323 {
324 use crate::selection::SumDimension;
325 rdb.data.sum_dimension(dimension)
326 }
327 let source = DataSource {
329 source: td!("cemsre_t55jfm_20200614_sub_abam5.tif"),
330 bands: [1, 2].to_vec(),
331 layer_names: Vec::new(),
332 };
333 let source_2 = DataSource {
334 source: td!("cemsre_t55jfm_20200614_sub_abam5.tif"),
335 bands: [1, 2].to_vec(),
336 layer_names: Vec::new(),
337 };
338
339 let sources = vec![source, source_2];
340
341 let rds = RasterDatasetBuilder::<i16>::from_sources(&sources)
342 .block_size(BlockSize {
343 rows: 1024,
344 cols: 1024,
345 })
346 .source_composition_dimension(Dimension::Time)
347 .band_composition_dimension(Dimension::Layer)
348 .build();
349
350 let out_file = PathBuf::from("/tmp/tmp.tif");
351 let dimension = Dimension::Layer;
352 rds.apply_reduction::<i16>(worker, dimension, 1, &out_file, 32767);
353 }
354
355 #[test]
356 fn test_overlap() {
357 let source = DataSource {
358 source: PathBuf::from("data/cemsre_t55jfm_20200614_sub_abam5.tif"),
359 bands: [1].to_vec(),
360 layer_names: Vec::new(),
361 };
362 let rds_over_0: RasterDataset<i16> = RasterDatasetBuilder::<i16>::from_source(&source)
363 .overlap_size(0)
364 .build();
365
366 let source = DataSource {
367 source: PathBuf::from("data/cemsre_t55jfm_20200614_sub_abam5.tif"),
368 bands: [1].to_vec(),
369 layer_names: Vec::new(),
370 };
371 let rds_over_1: RasterDataset<i16> = RasterDatasetBuilder::<i16>::from_source(&source)
372 .overlap_size(1)
373 .build();
374 println!("rds_over: {rds_over_1}");
375
376 let n_blocks = 4;
379 for block_id in 0..n_blocks {
380 let array_with_overlap = rds_over_1.read_block::<i32>(block_id);
382 let overlap = rds_over_1.blocks[block_id].overlap;
384 let array_with_overlap_trimmed = trimm_array4_owned(&array_with_overlap, &overlap);
387 let array_with_no_overlap = rds_over_0.read_block::<i32>(block_id);
391 assert_eq!(array_with_no_overlap, array_with_overlap_trimmed);
393 }
394 }
395 #[test]
396 fn test_get_overlap() {
397 let image_size = ImageSize {
398 rows: 1580,
399 cols: 1514,
400 };
401 let block_size = BlockSize {
402 rows: 1024,
403 cols: 1024,
404 };
405 let overlap_size = 1;
406 let block_col_rows = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
407 for block_col_row in block_col_rows {
408 get_overlap(image_size, block_size, block_col_row, overlap_size);
409 }
410 }
411
412 #[test]
413 fn test_extract_blockwise() {
414 let mut e: BTreeMap<i16, Vec<i16>> = BTreeMap::new();
415 e.insert(1, vec![1910]);
416 e.insert(2, vec![957]);
417 e.insert(3, vec![1680]);
418 e.insert(4, vec![1847]);
419 e.insert(5, vec![1746]);
420 e.insert(6, vec![624]);
421 e.insert(7, vec![818]);
422 e.insert(8, vec![1103]);
423
424 let raster_path = td!("cemsre_t55jfm_20200614_sub_abam5.tif");
425 let vector_path = td!("cemsre_t55jfm_20200614_sub_abam5.gpkg");
426 let bls: Vec<usize> = vec![16, 32, 64, 128, 256, 512];
427 bls.iter().for_each(|b| {
428 let data_source = DataSourceBuilder::from_file(&raster_path)
429 .bands(vec![1])
430 .build();
431 let rds = RasterDatasetBuilder::<i16>::from_source(&data_source)
432 .block_size(BlockSize { rows: *b, cols: *b })
433 .build();
434 let data = rds.extract_blockwise(&vector_path, "id", SamplingMethod::Value, None);
435 assert_eq!(e, data);
436 });
437 }
438
439 #[test]
440 #[cfg(feature = "use_polars")]
441 fn extract_blockwise_time() {
442 let source = DataSource {
443 source: td!("cemsre_t55jfm_20200614_sub_abam5.tif"),
444 bands: [1, 3].to_vec(),
445 layer_names: Vec::new(),
446 };
447
448 let vector_path = td!("cemsre_t55jfm_20200614_sub_abam5.gpkg");
449 let overlap_size = 0;
450 let rds: RasterDataset<i16> = RasterDatasetBuilder::from_source(&source)
452 .overlap_size(overlap_size)
453 .build();
454 let _extracted_2b =
455 rds.extract_blockwise(&vector_path, "id", SamplingMethod::Value, None);
456 println!("Extracted: {_extracted_2b:?}");
457 }
458
459 #[test]
460 fn test_extract_blockwise_padding() {
461 let source = DataSource {
462 source: td!("cemsre_t55jfm_20200614_sub_abam5.tif"),
463 bands: [1, 3].to_vec(),
464 layer_names: Vec::new(),
465 };
466
467 let vector_path = td!("cemsre_t55jfm_20200614_sub_abam5.gpkg");
468 let overlap_size = 0;
469 let rds: RasterDataset<i16> = RasterDatasetBuilder::<i16>::from_source(&source)
471 .overlap_size(overlap_size)
472 .build();
473 let _extracted_2b = rds.extract_blockwise(&vector_path, "id", SamplingMethod::Value, None);
474 println!("Extracted: {_extracted_2b:?}");
475
476 let source = DataSource {
478 source: td!("cemsre_t55jfm_20200614_sub_abam5.tif"),
479 bands: [1, 3].to_vec(),
480 layer_names: Vec::new(),
481 };
482 let rds: RasterDataset<i16> = RasterDatasetBuilder::<i16>::from_source(&source)
483 .overlap_size(overlap_size)
484 .build();
485 let extracted_1 = rds.extract_blockwise(&vector_path, "id", SamplingMethod::Value, None);
486
487 let overlap_size = 1;
488 let source = DataSource {
489 source: td!("cemsre_t55jfm_20200614_sub_abam5.tif"),
490 bands: [1, 3].to_vec(),
491 layer_names: Vec::new(),
492 };
493 let rds: RasterDataset<i16> = RasterDatasetBuilder::<i16>::from_source(&source)
494 .overlap_size(overlap_size)
495 .build();
496 let extracted_2 = rds.extract_blockwise(&vector_path, "id", SamplingMethod::Value, None);
497 assert_eq!(extracted_1, extracted_2);
498
499 let overlap_size = 0;
501 let source = DataSource {
502 source: td!("cemsre_t55jfm_20200614_sub_abam5.tif"),
503 bands: [1, 3].to_vec(),
504 layer_names: Vec::new(),
505 };
506 let rds: RasterDataset<i16> = RasterDatasetBuilder::<i16>::from_source(&source)
507 .overlap_size(overlap_size)
508 .build();
509 let extracted_mode = rds.extract_blockwise(&vector_path, "id", SamplingMethod::Value, None);
510 assert_eq!(extracted_1, extracted_mode);
511 }
512
513 #[test]
514 fn test_stack_datasets() {
515 let src_fn = td!("cemsre_t55jfm_20200614_sub_abam5.tif");
516 let source_1 = DataSourceBuilder::from_file(&src_fn).build();
517 let source_2 = source_1.clone();
518 let mut rds_1 = RasterDatasetBuilder::<i16>::from_source(&source_1)
519 .band_composition_dimension(Dimension::Layer)
520 .build();
521 let rds_2 = RasterDatasetBuilder::<i16>::from_source(&source_2)
522 .band_composition_dimension(Dimension::Layer)
523 .build();
524 rds_1.stack(&rds_2, Dimension::Layer);
525
526 let block_id = 0;
527 let data = rds_1.read_block::<i32>(block_id);
528 assert_eq!(data.shape(), [1, 12, 1024, 1024]);
529 }
530
531 #[test]
532 fn test_extend_datasets() {
533 let source = DataSource {
534 source: td!("cemsre_t55jfm_20200614_sub_abam5.tif"),
535 bands: [1, 2, 3, 4, 5, 6].to_vec(),
536 layer_names: Vec::new(),
537 };
538 let source_2 = source.clone();
539 let mut rds_1 = RasterDatasetBuilder::<i16>::from_source(&source)
540 .band_composition_dimension(Dimension::Layer)
541 .build();
542 let rds_2 = RasterDatasetBuilder::<i16>::from_source(&source_2)
543 .band_composition_dimension(Dimension::Layer)
544 .build();
545 rds_1.extend(&rds_2);
546 let block_id = 0;
547 let data = rds_1.read_block::<i32>(block_id);
548 assert_eq!(data.shape(), [2, 6, 1024, 1024]);
549 }
550
551 #[test]
552 fn test_epsg() {
553 fn worker(rdb: &RasterDataBlock<i16>) -> anyhow::Result<Array4<i16>> {
554 Ok(rdb.data.to_owned())
555 }
556
557 let source = DataSource {
558 source: td!("cemsre_t55jfm_20200614_suba_abam5.tif"),
559 bands: [1, 2].to_vec(),
560 layer_names: Vec::new(),
561 };
562
563 let rds = RasterDatasetBuilder::<i16>::from_source(&source)
564 .set_epsg(3577)
565 .build();
566 assert_eq!(
567 rds.metadata.shape,
568 RasterDataShape {
569 times: 1,
570 layers: 2,
571 rows: 1419,
572 cols: 1613
573 }
574 );
575
576 rds.apply(worker, 1, &PathBuf::from("test.tif")).ok();
577
578 let rds = RasterDatasetBuilder::<i16>::from_source(&source)
579 .band_composition_dimension(Dimension::Time)
580 .set_epsg(4326)
581 .build();
582 assert_eq!(
583 rds.metadata.shape,
584 RasterDataShape {
585 times: 2,
586 layers: 1,
587 rows: 1179,
588 cols: 1559
589 }
590 );
591 }
592
593 #[test]
594 fn test_layers_from_source() {
595 let source = DataSource {
596 source: td!("cemsre_t55jfm_20200614_sub_abam5.tif"),
597 bands: [1, 2, 3, 4, 5, 6].to_vec(),
598 layer_names: Vec::new(),
599 };
600
601 let rds = RasterDatasetBuilder::<i16>::from_source(&source)
602 .band_composition_dimension(Dimension::Layer)
603 .build();
604 assert_eq!(
605 rds.metadata.shape,
606 RasterDataShape {
607 times: 1,
608 layers: 6,
609 rows: 1580,
610 cols: 1514
611 }
612 );
613
614 let source = DataSource {
615 source: td!("cemsre_t55jfm_20200614_sub_abam5.tif"),
616 bands: [1, 2, 3, 4, 5, 6].to_vec(),
617 layer_names: Vec::new(),
618 };
619
620 let rds = RasterDatasetBuilder::<i16>::from_source(&source)
621 .band_composition_dimension(Dimension::Time)
622 .build();
623 assert_eq!(
624 rds.metadata.shape,
625 RasterDataShape {
626 times: 6,
627 layers: 1,
628 rows: 1580,
629 cols: 1514
630 }
631 );
632 }
633
634 #[test]
635 fn test_zip() {
636 let data_source = DataSourceBuilder::from_file(&PathBuf::from(td!(
637 "cemsre_t55jfm_20200614_sub_abam5.tif"
638 )))
639 .build();
640
641 let rds_1 = RasterDatasetBuilder::<i16>::from_source(&data_source).build();
642 let rds_2 = RasterDatasetBuilder::<i16>::from_source(&data_source).build();
643
644 for (b_1, b_2) in rds_1.iter().zip(rds_2.iter()) {
645 let id = b_1.iter_index;
646 let b1_data = b_1.parent.read_block::<i16>(id);
647 let b2_data = b_2.parent.read_block::<i16>(id);
648 let mask = b2_data.mapv(|v| (v > 1000) as i16);
649 let _result = b1_data * mask;
650 }
651 }
652
653 #[test]
654 fn test_raster_data_source_builder() {
655 let data_source = DataSourceBuilder::from_file(&PathBuf::from(
656 "data/cemsre_t55jfm_20200614_sub_abam5.tif",
657 ))
658 .build();
659
660 let _ = RasterDatasetBuilder::<i16>::from_source(&data_source).build();
661 }
662
663 #[test]
664 fn test_data_source_builder() {
665 let _data_source = DataSourceBuilder::from_file(&PathBuf::from(
666 "data/cemsre_t55jfm_20200614_sub_abam5.tif",
667 ))
668 .build();
669 let _data_source = DataSourceBuilder::from_file(&PathBuf::from(
670 "data/cemsre_t55jfm_20200614_sub_abam5.tif",
671 ))
672 .bands(vec![1, 3])
673 .build();
674 let _e = [
675 "cemsre_t55jfm_20200614_sub_abam5_1",
676 "cemsre_t55jfm_20200614_sub_abam5_3",
677 ];
678
679 let _data_source = DataSourceBuilder::from_file(&PathBuf::from(
680 "data/cemsre_t55jfm_20200614_sub_abam5.tif",
681 ))
682 .bands(vec![1, 4])
683 .build();
684 }
685
686 #[test]
687 fn test_compositions() {
688 let data_source = DataSource {
689 source: PathBuf::from("data/cemsre_t55jfm_20200614_sub_abam5.tif"),
690 bands: [1, 2, 3].to_vec(),
691 layer_names: Vec::new(),
692 };
693 let data_source_2 = DataSource {
694 source: PathBuf::from("data/cemsre_t55jfm_20200614_sub_abam5.tif"),
695 bands: [1, 2, 3].to_vec(),
696 layer_names: Vec::new(),
697 };
698 let rds =
699 RasterDatasetBuilder::<i16>::from_sources(&vec![data_source_2, data_source]).build();
700 assert_eq!(
701 rds.metadata.shape,
702 RasterDataShape {
703 times: 2,
704 layers: 3,
705 rows: 1580,
706 cols: 1514
707 }
708 );
709
710 let data_source = DataSource {
711 source: td!("cemsre_t55jfm_20200614_sub_abam5.tif"),
712 bands: [1, 2, 3].to_vec(),
713 layer_names: Vec::new(),
714 };
715 let data_source_2 = DataSource {
716 source: td!("cemsre_t55jfm_20200614_sub_abam5.tif"),
717 bands: [1, 2, 3].to_vec(),
718 layer_names: Vec::new(),
719 };
720 let rds = RasterDatasetBuilder::<i16>::from_sources(&vec![data_source_2, data_source])
721 .band_composition_dimension(Dimension::Layer)
722 .source_composition_dimension(Dimension::Layer)
723 .build();
724 assert_eq!(
725 rds.metadata.shape,
726 RasterDataShape {
727 times: 1,
728 layers: 6,
729 rows: 1580,
730 cols: 1514
731 }
732 );
733
734 let data_source = DataSource {
735 source: td!("cemsre_t55jfm_20200614_sub_abam5.tif"),
736 bands: [1, 2, 3].to_vec(),
737 layer_names: Vec::new(),
738 };
739 let data_source_2 = DataSource {
740 source: td!("cemsre_t55jfm_20200614_sub_abam5.tif"),
741 bands: [1, 2, 3].to_vec(),
742 layer_names: Vec::new(),
743 };
744 let rds = RasterDatasetBuilder::<i16>::from_sources(&vec![data_source_2, data_source])
745 .band_composition_dimension(Dimension::Time)
746 .source_composition_dimension(Dimension::Time)
747 .build();
748 assert_eq!(
749 rds.metadata.shape,
750 RasterDataShape {
751 times: 6,
752 layers: 1,
753 rows: 1580,
754 cols: 1514
755 }
756 );
757
758 let data_source = DataSource {
759 source: td!("cemsre_t55jfm_20200614_sub_abam5.tif"),
760 bands: [1, 2, 3].to_vec(),
761 layer_names: Vec::new(),
762 };
763 let data_source_2 = DataSource {
764 source: td!("cemsre_t55jfm_20200614_sub_abam5.tif"),
765 bands: [1, 2, 3].to_vec(),
766 layer_names: Vec::new(),
767 };
768 let rds = RasterDatasetBuilder::<i16>::from_sources(&vec![data_source_2, data_source])
769 .band_composition_dimension(Dimension::Time)
770 .source_composition_dimension(Dimension::Layer)
771 .build();
772 assert_eq!(
773 rds.metadata.shape,
774 RasterDataShape {
775 times: 3,
776 layers: 2,
777 rows: 1580,
778 cols: 1514
779 }
780 );
781 }
782
783 #[test]
786 fn test_trimm_array3_asymmetric_trimming() {
787 use ndarray::Array3;
788 use crate::types::Overlap;
789
790 let mut data: Array3<u16> = Array3::zeros((3, 10, 10));
792 for b in 0..3 {
793 for r in 0..10 {
794 for c in 0..10 {
795 data[[b, r, c]] = (r * 10 + c) as u16 + b as u16 * 100;
796 }
797 }
798 }
799
800 let overlap = Overlap { left: 0, top: 0, right: 0, bottom: 0 };
802 let trimmed = crate::array_ops::trimm_array3_asymmetric(&data, &overlap);
803 assert_eq!(trimmed.shape(), &[3, 10, 10]);
804 assert_eq!(trimmed[[0, 0, 0]], 0);
805
806 let overlap = Overlap { left: 2, top: 2, right: 2, bottom: 2 };
808 let trimmed = crate::array_ops::trimm_array3_asymmetric(&data, &overlap);
809 assert_eq!(trimmed.shape(), &[3, 6, 6]);
810 assert_eq!(trimmed[[0, 0, 0]], data[[0, 2, 2]]);
811
812 let overlap = Overlap { left: 1, top: 2, right: 3, bottom: 4 };
814 let trimmed = crate::array_ops::trimm_array3_asymmetric(&data, &overlap);
815 assert_eq!(trimmed.shape(), &[3, 4, 6]);
816 assert_eq!(trimmed[[0, 0, 0]], data[[0, 2, 1]]);
817 }
818
819 #[test]
820 fn test_create_output_geotiff_small() {
821 use crate::parallel_writer::create_output_geotiff;
822 use crate::types::GeoTransform;
823
824
825 let dir = std::env::temp_dir();
826 let path = dir.join(format!("test_output_{}.tif", std::process::id()));
827 let _ = std::fs::remove_file(&path);
828
829 let geo_transform = GeoTransform {
830 x_ul: 500000.0,
831 x_res: 30.0,
832 x_rot: 0.0,
833 y_ul: 7000000.0,
834 y_rot: 0.0,
835 y_res: -30.0,
836 };
837
838 let result = create_output_geotiff::<u16>(
839 &path,
840 &geo_transform,
841 32755,
842 64,
843 64,
844 3,
845 0u16,
846 );
847 assert!(result.is_ok(), "Failed to create GeoTIFF: {:?}", result.err());
848 assert!(path.exists(), "Output file was not created");
849
850 let ds = gdal::Dataset::open(&path).expect("Failed to open created GeoTIFF");
852 assert_eq!(ds.raster_count(), 3);
853 let size = ds.raster_size();
854 assert_eq!(size.0, 64); assert_eq!(size.1, 64); let _ = std::fs::remove_file(&path);
858 }
859
860 #[test]
861 fn test_write_block_roundtrip() {
862 use crate::parallel_writer::{self, ParallelGeoTiffWriter};
863 use crate::types::{GeoTransform, Offset, ReadWindow, Size};
864 use ndarray::Array3;
865
866 let dir = std::env::temp_dir();
867 let path = dir.join(format!("test_write_{}.tif", std::process::id()));
868 let _ = std::fs::remove_file(&path);
869
870 let geo_transform = GeoTransform {
871 x_ul: 500000.0,
872 x_res: 30.0,
873 x_rot: 0.0,
874 y_ul: 7000000.0,
875 y_rot: 0.0,
876 y_res: -30.0,
877 };
878
879 parallel_writer::create_output_geotiff::<u16>(
881 &path,
882 &geo_transform,
883 32755,
884 128, 128, 2, 0u16,
888 ).expect("Failed to create output");
889
890 let writer = ParallelGeoTiffWriter::new(
891 path.clone(),
892 geo_transform,
893 32755,
894 128,
895 128,
896 2,
897 );
898
899 let mut block_data: Array3<u16> = Array3::zeros((2, 64, 64));
901 for b in 0..2 {
903 for r in 0..64 {
904 for c in 0..64 {
905 block_data[[b, r, c]] = (r * 64 + c) as u16 + b as u16 * 10000;
906 }
907 }
908 }
909
910 let window = ReadWindow {
911 offset: Offset { cols: 32, rows: 32 },
912 size: Size { cols: 64, rows: 64 },
913 };
914
915 parallel_writer::write_block(
916 &writer,
917 block_data.view(),
918 window,
919 ).expect("Failed to write block");
920
921 drop(writer);
923
924 let ds = gdal::Dataset::open(&path).expect("Failed to open for verification");
926 assert_eq!(ds.raster_count(), 2);
927
928 for band_idx in 1..=2 {
929 let band = ds.rasterband(band_idx).expect("Failed to get band");
930 let data = band.read_as::<u16>(
931 (32, 32), (64, 64), (64, 64), None,
935 ).expect("Failed to read band").to_array().expect("Failed to convert to array");
936
937 for r in 0..64 {
938 for c in 0..64 {
939 let expected = (r * 64 + c) as u16 + (band_idx - 1) as u16 * 10000;
940 assert_eq!(data[[r, c]], expected,
941 "Mismatch at band={}, row={}, col={}", band_idx, r, c);
942 }
943 }
944 }
945
946 let _ = std::fs::remove_file(&path);
947 }
948
949 #[test]
952 fn test_build_overviews() {
953 use crate::parallel_writer::{self, ParallelGeoTiffWriter};
954 use crate::types::GeoTransform;
955
956 let dir = std::env::temp_dir();
957 let path = dir.join(format!("test_overviews_{}.tif", std::process::id()));
958 let _ = std::fs::remove_file(&path);
959
960 let geo_transform = GeoTransform {
961 x_ul: 500000.0,
962 x_res: 30.0,
963 x_rot: 0.0,
964 y_ul: 7000000.0,
965 y_rot: 0.0,
966 y_res: -30.0,
967 };
968
969 parallel_writer::create_output_geotiff::<u16>(
971 &path,
972 &geo_transform,
973 32755,
974 128,
975 128,
976 1,
977 0u16,
978 ).expect("Failed to create output");
979
980 let writer = ParallelGeoTiffWriter::new(
981 path.clone(),
982 geo_transform,
983 32755,
984 128,
985 128,
986 1,
987 );
988
989 writer.build_overviews("CUBIC", &[2, 4, 8]).expect("Failed to build overviews");
991 drop(writer);
992
993 let ds = gdal::Dataset::open(&path).expect("Failed to open for verification");
995 let band = ds.rasterband(1).expect("Failed to get band");
996 let overview_count = band.overview_count().expect("Failed to get overview count");
997 assert!(
998 overview_count > 0,
999 "Expected overviews to exist, but got count={}",
1000 overview_count
1001 );
1002
1003 let _ = std::fs::remove_file(&path);
1004 }
1005
1006 #[test]
1007 fn test_translate_to_cog() {
1008 use crate::gdal_utils::translate_to_cog;
1009 use crate::parallel_writer::{self, ParallelGeoTiffWriter};
1010 use crate::types::{GeoTransform, Offset, ReadWindow, Size};
1011 use ndarray::Array3;
1012
1013 let dir = std::env::temp_dir();
1014 let intermediate = dir.join(format!("test_cog_inter_{}.tif", std::process::id()));
1015 let cog_output = dir.join(format!("test_cog_{}.tif", std::process::id()));
1016 let _ = std::fs::remove_file(&intermediate);
1017 let _ = std::fs::remove_file(&cog_output);
1018
1019 let geo_transform = GeoTransform {
1020 x_ul: 500000.0,
1021 x_res: 30.0,
1022 x_rot: 0.0,
1023 y_ul: 7000000.0,
1024 y_rot: 0.0,
1025 y_res: -30.0,
1026 };
1027
1028 let img_size = 1024usize;
1030 let block_size = 256usize;
1031
1032 parallel_writer::create_output_geotiff::<u16>(
1034 &intermediate,
1035 &geo_transform,
1036 32755,
1037 img_size,
1038 img_size,
1039 1,
1040 0u16,
1041 ).expect("Failed to create intermediate");
1042
1043 let writer = ParallelGeoTiffWriter::new(
1044 intermediate.clone(),
1045 geo_transform,
1046 32755,
1047 img_size,
1048 img_size,
1049 1,
1050 );
1051
1052 let mut block_data: Array3<u16> = Array3::zeros((1, block_size, block_size));
1054 for r in 0..block_size {
1055 for c in 0..block_size {
1056 block_data[[0, r, c]] = (r * block_size + c) as u16;
1057 }
1058 }
1059 let window = ReadWindow {
1060 offset: Offset { cols: block_size as isize, rows: block_size as isize },
1061 size: Size { cols: block_size as isize, rows: block_size as isize },
1062 };
1063 parallel_writer::write_block(&writer, block_data.view(), window)
1064 .expect("Failed to write block");
1065 drop(writer);
1066
1067 translate_to_cog(
1069 &intermediate, &cog_output,
1070 "DEFLATE", "NEAREST",
1071 ).expect("Failed to translate to COG");
1072
1073 let ds = gdal::Dataset::open(&cog_output).expect("Failed to open COG");
1075 let band = ds.rasterband(1).expect("Failed to get band");
1076 let overview_count = band.overview_count().expect("Failed to get overview count");
1077 assert!(
1078 overview_count > 0,
1079 "COG should have auto-generated overviews, but got count={}",
1080 overview_count
1081 );
1082
1083 let size = ds.raster_size();
1085 assert_eq!(size.0, img_size); assert_eq!(size.1, img_size); let band = ds.rasterband(1).expect("Failed to get band");
1090 let data = band.read_as::<u16>(
1091 (block_size as isize, block_size as isize),
1092 (block_size, block_size),
1093 (block_size, block_size),
1094 None,
1095 ).expect("Failed to read band").to_array().expect("Failed to convert to array");
1096 assert_eq!(data[[0, 0]], 0u16);
1097 assert_eq!(data[[1, 1]], (block_size + 1) as u16);
1098
1099 let ov = band.overview(0).expect("Failed to get overview");
1101 let ov_size_x = ov.x_size();
1102 let ov_size_y = ov.y_size();
1103 let read_offset = ((ov_size_x as isize) / 2 - 8, (ov_size_y as isize) / 2 - 8);
1105 let ov_data = ov.read_as::<u16>(
1106 read_offset,
1107 (16, 16),
1108 (16, 16),
1109 None,
1110 ).expect("Failed to read overview").to_array().expect("Failed to convert overview to array");
1111 assert!(
1112 ov_data.iter().any(|v| *v != 0),
1113 "Overview should contain non-zero data"
1114 );
1115
1116 let _ = std::fs::remove_file(&intermediate);
1117 let _ = std::fs::remove_file(&cog_output);
1118 }
1119
1120 #[test]
1121 fn test_output_config_defaults() {
1122 use crate::types::{OutputConfig, OutputFormat};
1123
1124 let config = OutputConfig::default();
1125 assert_eq!(config.format, OutputFormat::GeoTiff);
1126 assert_eq!(config.compression, "LZW");
1127 assert_eq!(config.overview_resampling, "CUBIC");
1128 assert_eq!(config.overview_levels, vec![2, 4, 8, 16, 32]);
1129 }
1130
1131 #[test]
1132 fn test_output_format_variants() {
1133 use crate::types::OutputFormat;
1134
1135 let _geotiff = OutputFormat::GeoTiff;
1137 let _geotiff_overviews = OutputFormat::GeoTiffOverviews;
1138 let _cog = OutputFormat::COG;
1139
1140 assert_eq!(OutputFormat::default(), OutputFormat::GeoTiff);
1142
1143 assert_eq!(OutputFormat::GeoTiff, OutputFormat::GeoTiff);
1145 assert_ne!(OutputFormat::GeoTiff, OutputFormat::COG);
1146 }
1147}