1#![allow(unsafe_code)]
4
5use core::fmt;
75
76#[cfg(not(feature = "std"))]
77use crate::compat::*;
78use crate::error::{OxiGeoError, Result};
79#[cfg(not(feature = "std"))]
80use crate::math::FloatExt;
81use crate::types::{NoDataValue, RasterDataType};
82
83mod band_iterator;
84pub use band_iterator::{BandIterator, BandRef, MultiBandBuffer};
85
86mod raster_window;
87pub use raster_window::RasterWindow;
88
89pub mod mask;
90pub use mask::Mask;
91
92#[cfg(feature = "std")]
93pub use crate::simd_buffer::{ArenaTile, TileIteratorArena};
94
95#[cfg(feature = "arrow")]
96pub mod arrow_convert;
97
98#[derive(Clone)]
100pub struct RasterBuffer {
101 data: Vec<u8>,
103 width: u64,
105 height: u64,
107 data_type: RasterDataType,
109 nodata: NoDataValue,
111}
112
113impl RasterBuffer {
114 pub fn new(
119 data: Vec<u8>,
120 width: u64,
121 height: u64,
122 data_type: RasterDataType,
123 nodata: NoDataValue,
124 ) -> Result<Self> {
125 let expected_size = width * height * data_type.size_bytes() as u64;
126 if data.len() as u64 != expected_size {
127 return Err(OxiGeoError::InvalidParameter {
128 parameter: "data",
129 message: format!(
130 "Data size mismatch: expected {} bytes for {}x{} {:?}, got {}",
131 expected_size,
132 width,
133 height,
134 data_type,
135 data.len()
136 ),
137 });
138 }
139
140 Ok(Self {
141 data,
142 width,
143 height,
144 data_type,
145 nodata,
146 })
147 }
148
149 #[must_use]
151 pub fn zeros(width: u64, height: u64, data_type: RasterDataType) -> Self {
152 let size = (width * height * data_type.size_bytes() as u64) as usize;
153 Self {
154 data: vec![0u8; size],
155 width,
156 height,
157 data_type,
158 nodata: NoDataValue::None,
159 }
160 }
161
162 #[must_use]
164 pub fn nodata_filled(
165 width: u64,
166 height: u64,
167 data_type: RasterDataType,
168 nodata: NoDataValue,
169 ) -> Self {
170 let mut buffer = Self::zeros(width, height, data_type);
171 buffer.nodata = nodata;
172
173 if let Some(value) = nodata.as_f64() {
175 buffer.fill_value(value);
176 }
177
178 buffer
179 }
180
181 pub fn fill_value(&mut self, value: f64) {
183 match self.data_type {
184 RasterDataType::UInt8 => {
185 let v = value as u8;
186 self.data.fill(v);
187 }
188 RasterDataType::Int8 => {
189 let v = value as i8;
190 self.data.fill(v as u8);
191 }
192 RasterDataType::UInt16 => {
193 let v = (value as u16).to_ne_bytes();
194 for chunk in self.data.chunks_exact_mut(2) {
195 chunk.copy_from_slice(&v);
196 }
197 }
198 RasterDataType::Int16 => {
199 let v = (value as i16).to_ne_bytes();
200 for chunk in self.data.chunks_exact_mut(2) {
201 chunk.copy_from_slice(&v);
202 }
203 }
204 RasterDataType::UInt32 => {
205 let v = (value as u32).to_ne_bytes();
206 for chunk in self.data.chunks_exact_mut(4) {
207 chunk.copy_from_slice(&v);
208 }
209 }
210 RasterDataType::Int32 => {
211 let v = (value as i32).to_ne_bytes();
212 for chunk in self.data.chunks_exact_mut(4) {
213 chunk.copy_from_slice(&v);
214 }
215 }
216 RasterDataType::Float32 => {
217 let v = (value as f32).to_ne_bytes();
218 for chunk in self.data.chunks_exact_mut(4) {
219 chunk.copy_from_slice(&v);
220 }
221 }
222 RasterDataType::Float64 => {
223 let v = value.to_ne_bytes();
224 for chunk in self.data.chunks_exact_mut(8) {
225 chunk.copy_from_slice(&v);
226 }
227 }
228 RasterDataType::UInt64 => {
229 let v = (value as u64).to_ne_bytes();
230 for chunk in self.data.chunks_exact_mut(8) {
231 chunk.copy_from_slice(&v);
232 }
233 }
234 RasterDataType::Int64 => {
235 let v = (value as i64).to_ne_bytes();
236 for chunk in self.data.chunks_exact_mut(8) {
237 chunk.copy_from_slice(&v);
238 }
239 }
240 RasterDataType::CFloat32 => {
241 let real_bytes = (value as f32).to_ne_bytes();
244 let imag_bytes = 0f32.to_ne_bytes();
245 for chunk in self.data.chunks_exact_mut(8) {
246 chunk[..4].copy_from_slice(&real_bytes);
247 chunk[4..].copy_from_slice(&imag_bytes);
248 }
249 }
250 RasterDataType::CFloat64 => {
251 let real_bytes = value.to_ne_bytes();
254 let imag_bytes = 0f64.to_ne_bytes();
255 for chunk in self.data.chunks_exact_mut(16) {
256 chunk[..8].copy_from_slice(&real_bytes);
257 chunk[8..].copy_from_slice(&imag_bytes);
258 }
259 }
260 }
261 }
262
263 #[must_use]
265 pub const fn width(&self) -> u64 {
266 self.width
267 }
268
269 #[must_use]
271 pub const fn height(&self) -> u64 {
272 self.height
273 }
274
275 #[must_use]
277 pub const fn data_type(&self) -> RasterDataType {
278 self.data_type
279 }
280
281 #[must_use]
283 pub const fn nodata(&self) -> NoDataValue {
284 self.nodata
285 }
286
287 #[must_use]
289 pub const fn pixel_count(&self) -> u64 {
290 self.width * self.height
291 }
292
293 #[must_use]
295 pub fn as_bytes(&self) -> &[u8] {
296 &self.data
297 }
298
299 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
301 &mut self.data
302 }
303
304 #[must_use]
306 pub fn into_bytes(self) -> Vec<u8> {
307 self.data
308 }
309
310 pub fn from_typed_vec<T: Copy + 'static>(
321 width: usize,
322 height: usize,
323 data: Vec<T>,
324 data_type: RasterDataType,
325 ) -> Result<Self> {
326 let expected_pixels = width * height;
327 if data.len() != expected_pixels {
328 return Err(OxiGeoError::InvalidParameter {
329 parameter: "data",
330 message: format!(
331 "Data length mismatch: expected {} pixels for {}x{}, got {}",
332 expected_pixels,
333 width,
334 height,
335 data.len()
336 ),
337 });
338 }
339
340 let type_size = core::mem::size_of::<T>();
342 let expected_type_size = data_type.size_bytes();
343 if type_size != expected_type_size {
344 return Err(OxiGeoError::InvalidParameter {
345 parameter: "data_type",
346 message: format!(
347 "Type size mismatch: provided type has {} bytes, {:?} expects {} bytes",
348 type_size, data_type, expected_type_size
349 ),
350 });
351 }
352
353 let byte_data: Vec<u8> = data
354 .iter()
355 .flat_map(|v| {
356 let ptr = v as *const T as *const u8;
358 unsafe { core::slice::from_raw_parts(ptr, type_size) }.to_vec()
359 })
360 .collect();
361
362 Self::new(
363 byte_data,
364 width as u64,
365 height as u64,
366 data_type,
367 NoDataValue::None,
368 )
369 }
370
371 pub fn as_slice<T: Copy + 'static>(&self) -> Result<&[T]> {
379 let type_size = core::mem::size_of::<T>();
380 let expected_size = self.data_type.size_bytes();
381
382 if type_size != expected_size {
383 return Err(OxiGeoError::InvalidParameter {
384 parameter: "T",
385 message: format!(
386 "Type size mismatch: requested type has {} bytes, buffer contains {:?} ({} bytes)",
387 type_size, self.data_type, expected_size
388 ),
389 });
390 }
391
392 let pixel_count = (self.width * self.height) as usize;
393 let slice =
396 unsafe { core::slice::from_raw_parts(self.data.as_ptr() as *const T, pixel_count) };
397 Ok(slice)
398 }
399
400 pub fn as_slice_mut<T: Copy + 'static>(&mut self) -> Result<&mut [T]> {
408 let type_size = core::mem::size_of::<T>();
409 let expected_size = self.data_type.size_bytes();
410
411 if type_size != expected_size {
412 return Err(OxiGeoError::InvalidParameter {
413 parameter: "T",
414 message: format!(
415 "Type size mismatch: requested type has {} bytes, buffer contains {:?} ({} bytes)",
416 type_size, self.data_type, expected_size
417 ),
418 });
419 }
420
421 let pixel_count = (self.width * self.height) as usize;
422 let slice = unsafe {
425 core::slice::from_raw_parts_mut(self.data.as_mut_ptr() as *mut T, pixel_count)
426 };
427 Ok(slice)
428 }
429
430 pub fn get_pixel(&self, x: u64, y: u64) -> Result<f64> {
435 if x >= self.width || y >= self.height {
436 return Err(OxiGeoError::OutOfBounds {
437 message: format!(
438 "Pixel ({}, {}) out of bounds for {}x{} buffer",
439 x, y, self.width, self.height
440 ),
441 });
442 }
443
444 let pixel_size = self.data_type.size_bytes();
445 let offset = (y * self.width + x) as usize * pixel_size;
446
447 let value = match self.data_type {
448 RasterDataType::UInt8 => f64::from(self.data[offset]),
449 RasterDataType::Int8 => f64::from(self.data[offset] as i8),
450 RasterDataType::UInt16 => {
451 let bytes: [u8; 2] = self.data[offset..offset + 2].try_into().map_err(|_| {
452 OxiGeoError::Internal {
453 message: "Invalid slice length".to_string(),
454 }
455 })?;
456 f64::from(u16::from_ne_bytes(bytes))
457 }
458 RasterDataType::Int16 => {
459 let bytes: [u8; 2] = self.data[offset..offset + 2].try_into().map_err(|_| {
460 OxiGeoError::Internal {
461 message: "Invalid slice length".to_string(),
462 }
463 })?;
464 f64::from(i16::from_ne_bytes(bytes))
465 }
466 RasterDataType::UInt32 => {
467 let bytes: [u8; 4] = self.data[offset..offset + 4].try_into().map_err(|_| {
468 OxiGeoError::Internal {
469 message: "Invalid slice length".to_string(),
470 }
471 })?;
472 f64::from(u32::from_ne_bytes(bytes))
473 }
474 RasterDataType::Int32 => {
475 let bytes: [u8; 4] = self.data[offset..offset + 4].try_into().map_err(|_| {
476 OxiGeoError::Internal {
477 message: "Invalid slice length".to_string(),
478 }
479 })?;
480 f64::from(i32::from_ne_bytes(bytes))
481 }
482 RasterDataType::Float32 => {
483 let bytes: [u8; 4] = self.data[offset..offset + 4].try_into().map_err(|_| {
484 OxiGeoError::Internal {
485 message: "Invalid slice length".to_string(),
486 }
487 })?;
488 f64::from(f32::from_ne_bytes(bytes))
489 }
490 RasterDataType::Float64 => {
491 let bytes: [u8; 8] = self.data[offset..offset + 8].try_into().map_err(|_| {
492 OxiGeoError::Internal {
493 message: "Invalid slice length".to_string(),
494 }
495 })?;
496 f64::from_ne_bytes(bytes)
497 }
498 RasterDataType::UInt64 => {
499 let bytes: [u8; 8] = self.data[offset..offset + 8].try_into().map_err(|_| {
500 OxiGeoError::Internal {
501 message: "Invalid slice length".to_string(),
502 }
503 })?;
504 u64::from_ne_bytes(bytes) as f64
505 }
506 RasterDataType::Int64 => {
507 let bytes: [u8; 8] = self.data[offset..offset + 8].try_into().map_err(|_| {
508 OxiGeoError::Internal {
509 message: "Invalid slice length".to_string(),
510 }
511 })?;
512 i64::from_ne_bytes(bytes) as f64
513 }
514 RasterDataType::CFloat32 => {
515 let bytes: [u8; 4] = self.data[offset..offset + 4].try_into().map_err(|_| {
517 OxiGeoError::Internal {
518 message: "Invalid slice length".to_string(),
519 }
520 })?;
521 f64::from(f32::from_ne_bytes(bytes))
522 }
523 RasterDataType::CFloat64 => {
524 let bytes: [u8; 8] = self.data[offset..offset + 8].try_into().map_err(|_| {
526 OxiGeoError::Internal {
527 message: "Invalid slice length".to_string(),
528 }
529 })?;
530 f64::from_ne_bytes(bytes)
531 }
532 };
533
534 Ok(value)
535 }
536
537 pub fn set_pixel(&mut self, x: u64, y: u64, value: f64) -> Result<()> {
542 if x >= self.width || y >= self.height {
543 return Err(OxiGeoError::OutOfBounds {
544 message: format!(
545 "Pixel ({}, {}) out of bounds for {}x{} buffer",
546 x, y, self.width, self.height
547 ),
548 });
549 }
550
551 let pixel_size = self.data_type.size_bytes();
552 let offset = (y * self.width + x) as usize * pixel_size;
553
554 match self.data_type {
555 RasterDataType::UInt8 => {
556 self.data[offset] = value as u8;
557 }
558 RasterDataType::Int8 => {
559 self.data[offset] = (value as i8) as u8;
560 }
561 RasterDataType::UInt16 => {
562 let bytes = (value as u16).to_ne_bytes();
563 self.data[offset..offset + 2].copy_from_slice(&bytes);
564 }
565 RasterDataType::Int16 => {
566 let bytes = (value as i16).to_ne_bytes();
567 self.data[offset..offset + 2].copy_from_slice(&bytes);
568 }
569 RasterDataType::UInt32 => {
570 let bytes = (value as u32).to_ne_bytes();
571 self.data[offset..offset + 4].copy_from_slice(&bytes);
572 }
573 RasterDataType::Int32 => {
574 let bytes = (value as i32).to_ne_bytes();
575 self.data[offset..offset + 4].copy_from_slice(&bytes);
576 }
577 RasterDataType::Float32 => {
578 let bytes = (value as f32).to_ne_bytes();
579 self.data[offset..offset + 4].copy_from_slice(&bytes);
580 }
581 RasterDataType::Float64 => {
582 let bytes = value.to_ne_bytes();
583 self.data[offset..offset + 8].copy_from_slice(&bytes);
584 }
585 RasterDataType::UInt64 => {
586 let bytes = (value as u64).to_ne_bytes();
587 self.data[offset..offset + 8].copy_from_slice(&bytes);
588 }
589 RasterDataType::Int64 => {
590 let bytes = (value as i64).to_ne_bytes();
591 self.data[offset..offset + 8].copy_from_slice(&bytes);
592 }
593 RasterDataType::CFloat32 => {
594 let bytes = (value as f32).to_ne_bytes();
596 self.data[offset..offset + 4].copy_from_slice(&bytes);
597 }
598 RasterDataType::CFloat64 => {
599 let bytes = value.to_ne_bytes();
601 self.data[offset..offset + 8].copy_from_slice(&bytes);
602 }
603 }
604
605 Ok(())
606 }
607
608 pub fn get_u8(&self, x: u64, y: u64) -> Result<u8> {
615 self.check_bounds(x, y)?;
616 self.check_type(RasterDataType::UInt8)?;
617 let offset = (y * self.width + x) as usize;
618 Ok(self.data[offset])
619 }
620
621 pub fn get_i8(&self, x: u64, y: u64) -> Result<i8> {
626 self.check_bounds(x, y)?;
627 self.check_type(RasterDataType::Int8)?;
628 let offset = (y * self.width + x) as usize;
629 Ok(self.data[offset] as i8)
630 }
631
632 pub fn get_u16(&self, x: u64, y: u64) -> Result<u16> {
637 self.check_bounds(x, y)?;
638 self.check_type(RasterDataType::UInt16)?;
639 let offset = (y * self.width + x) as usize * 2;
640 let bytes: [u8; 2] =
641 self.data[offset..offset + 2]
642 .try_into()
643 .map_err(|_| OxiGeoError::Internal {
644 message: "Invalid slice length".to_string(),
645 })?;
646 Ok(u16::from_ne_bytes(bytes))
647 }
648
649 pub fn get_i16(&self, x: u64, y: u64) -> Result<i16> {
654 self.check_bounds(x, y)?;
655 self.check_type(RasterDataType::Int16)?;
656 let offset = (y * self.width + x) as usize * 2;
657 let bytes: [u8; 2] =
658 self.data[offset..offset + 2]
659 .try_into()
660 .map_err(|_| OxiGeoError::Internal {
661 message: "Invalid slice length".to_string(),
662 })?;
663 Ok(i16::from_ne_bytes(bytes))
664 }
665
666 pub fn get_u32(&self, x: u64, y: u64) -> Result<u32> {
671 self.check_bounds(x, y)?;
672 self.check_type(RasterDataType::UInt32)?;
673 let offset = (y * self.width + x) as usize * 4;
674 let bytes: [u8; 4] =
675 self.data[offset..offset + 4]
676 .try_into()
677 .map_err(|_| OxiGeoError::Internal {
678 message: "Invalid slice length".to_string(),
679 })?;
680 Ok(u32::from_ne_bytes(bytes))
681 }
682
683 pub fn get_i32(&self, x: u64, y: u64) -> Result<i32> {
688 self.check_bounds(x, y)?;
689 self.check_type(RasterDataType::Int32)?;
690 let offset = (y * self.width + x) as usize * 4;
691 let bytes: [u8; 4] =
692 self.data[offset..offset + 4]
693 .try_into()
694 .map_err(|_| OxiGeoError::Internal {
695 message: "Invalid slice length".to_string(),
696 })?;
697 Ok(i32::from_ne_bytes(bytes))
698 }
699
700 pub fn get_u64(&self, x: u64, y: u64) -> Result<u64> {
705 self.check_bounds(x, y)?;
706 self.check_type(RasterDataType::UInt64)?;
707 let offset = (y * self.width + x) as usize * 8;
708 let bytes: [u8; 8] =
709 self.data[offset..offset + 8]
710 .try_into()
711 .map_err(|_| OxiGeoError::Internal {
712 message: "Invalid slice length".to_string(),
713 })?;
714 Ok(u64::from_ne_bytes(bytes))
715 }
716
717 pub fn get_i64(&self, x: u64, y: u64) -> Result<i64> {
722 self.check_bounds(x, y)?;
723 self.check_type(RasterDataType::Int64)?;
724 let offset = (y * self.width + x) as usize * 8;
725 let bytes: [u8; 8] =
726 self.data[offset..offset + 8]
727 .try_into()
728 .map_err(|_| OxiGeoError::Internal {
729 message: "Invalid slice length".to_string(),
730 })?;
731 Ok(i64::from_ne_bytes(bytes))
732 }
733
734 pub fn get_f32(&self, x: u64, y: u64) -> Result<f32> {
739 self.check_bounds(x, y)?;
740 self.check_type(RasterDataType::Float32)?;
741 let offset = (y * self.width + x) as usize * 4;
742 let bytes: [u8; 4] =
743 self.data[offset..offset + 4]
744 .try_into()
745 .map_err(|_| OxiGeoError::Internal {
746 message: "Invalid slice length".to_string(),
747 })?;
748 Ok(f32::from_ne_bytes(bytes))
749 }
750
751 pub fn get_f64(&self, x: u64, y: u64) -> Result<f64> {
756 self.check_bounds(x, y)?;
757 self.check_type(RasterDataType::Float64)?;
758 let offset = (y * self.width + x) as usize * 8;
759 let bytes: [u8; 8] =
760 self.data[offset..offset + 8]
761 .try_into()
762 .map_err(|_| OxiGeoError::Internal {
763 message: "Invalid slice length".to_string(),
764 })?;
765 Ok(f64::from_ne_bytes(bytes))
766 }
767
768 pub fn set_u8(&mut self, x: u64, y: u64, value: u8) -> Result<()> {
775 self.check_bounds(x, y)?;
776 self.check_type(RasterDataType::UInt8)?;
777 let offset = (y * self.width + x) as usize;
778 self.data[offset] = value;
779 Ok(())
780 }
781
782 pub fn set_f32(&mut self, x: u64, y: u64, value: f32) -> Result<()> {
787 self.check_bounds(x, y)?;
788 self.check_type(RasterDataType::Float32)?;
789 let offset = (y * self.width + x) as usize * 4;
790 self.data[offset..offset + 4].copy_from_slice(&value.to_ne_bytes());
791 Ok(())
792 }
793
794 pub fn set_f64(&mut self, x: u64, y: u64, value: f64) -> Result<()> {
799 self.check_bounds(x, y)?;
800 self.check_type(RasterDataType::Float64)?;
801 let offset = (y * self.width + x) as usize * 8;
802 self.data[offset..offset + 8].copy_from_slice(&value.to_ne_bytes());
803 Ok(())
804 }
805
806 pub fn row_slice<T: Copy + 'static>(&self, y: u64) -> Result<&[T]> {
813 if y >= self.height {
814 return Err(OxiGeoError::OutOfBounds {
815 message: format!("Row {} out of bounds for height {}", y, self.height),
816 });
817 }
818 let type_size = core::mem::size_of::<T>();
819 let expected_size = self.data_type.size_bytes();
820 if type_size != expected_size {
821 return Err(OxiGeoError::InvalidParameter {
822 parameter: "T",
823 message: format!(
824 "Type size {} doesn't match {:?} size {}",
825 type_size, self.data_type, expected_size
826 ),
827 });
828 }
829 let row_start = (y * self.width) as usize * expected_size;
830 let row_end = row_start + self.width as usize * expected_size;
831 let slice = unsafe {
833 core::slice::from_raw_parts(
834 self.data[row_start..row_end].as_ptr() as *const T,
835 self.width as usize,
836 )
837 };
838 Ok(slice)
839 }
840
841 pub fn window(&self, x: u64, y: u64, width: u64, height: u64) -> Result<Self> {
846 if x + width > self.width || y + height > self.height {
847 return Err(OxiGeoError::OutOfBounds {
848 message: format!(
849 "Window ({},{}) {}x{} exceeds buffer {}x{}",
850 x, y, width, height, self.width, self.height
851 ),
852 });
853 }
854 let pixel_size = self.data_type.size_bytes();
855 let row_bytes = width as usize * pixel_size;
856 let mut data = Vec::with_capacity(height as usize * row_bytes);
857 for row in y..y + height {
858 let src_start = (row * self.width + x) as usize * pixel_size;
859 data.extend_from_slice(&self.data[src_start..src_start + row_bytes]);
860 }
861 Self::new(data, width, height, self.data_type, self.nodata)
862 }
863
864 fn check_bounds(&self, x: u64, y: u64) -> Result<()> {
867 if x >= self.width || y >= self.height {
868 return Err(OxiGeoError::OutOfBounds {
869 message: format!(
870 "Pixel ({}, {}) out of bounds for {}x{} buffer",
871 x, y, self.width, self.height
872 ),
873 });
874 }
875 Ok(())
876 }
877
878 fn check_type(&self, expected: RasterDataType) -> Result<()> {
879 if self.data_type != expected {
880 return Err(OxiGeoError::InvalidParameter {
881 parameter: "data_type",
882 message: format!(
883 "Buffer contains {:?} data, requested {:?}",
884 self.data_type, expected
885 ),
886 });
887 }
888 Ok(())
889 }
890
891 #[must_use]
893 pub fn is_nodata(&self, value: f64) -> bool {
894 match self.nodata.as_f64() {
895 Some(nd) => {
896 if nd.is_nan() && value.is_nan() {
897 true
898 } else {
899 (nd - value).abs() < f64::EPSILON
900 }
901 }
902 None => false,
903 }
904 }
905
906 pub fn convert_to(&self, target_type: RasterDataType) -> Result<Self> {
911 if target_type == self.data_type {
912 return Ok(self.clone());
913 }
914
915 let mut result = Self::zeros(self.width, self.height, target_type);
916 result.nodata = self.nodata;
917
918 for y in 0..self.height {
919 for x in 0..self.width {
920 let value = self.get_pixel(x, y)?;
921 result.set_pixel(x, y, value)?;
922 }
923 }
924
925 Ok(result)
926 }
927
928 pub fn compute_statistics(&self) -> Result<BufferStatistics> {
930 let mut min = f64::MAX;
931 let mut max = f64::MIN;
932 let mut sum = 0.0;
933 let mut sum_sq = 0.0;
934 let mut valid_count = 0u64;
935
936 for y in 0..self.height {
937 for x in 0..self.width {
938 let value = self.get_pixel(x, y)?;
939 if !self.is_nodata(value) && value.is_finite() {
940 min = min.min(value);
941 max = max.max(value);
942 sum += value;
943 sum_sq += value * value;
944 valid_count += 1;
945 }
946 }
947 }
948
949 if valid_count == 0 {
950 return Ok(BufferStatistics {
951 min: f64::NAN,
952 max: f64::NAN,
953 mean: f64::NAN,
954 std_dev: f64::NAN,
955 valid_count: 0,
956 histogram: None,
957 });
958 }
959
960 let mean = sum / valid_count as f64;
961 let variance = (sum_sq / valid_count as f64) - (mean * mean);
962 let std_dev = variance.sqrt();
963
964 Ok(BufferStatistics {
965 min,
966 max,
967 mean,
968 std_dev,
969 valid_count,
970 histogram: None,
971 })
972 }
973
974 pub fn compute_statistics_with_histogram(&self, bin_count: usize) -> Result<BufferStatistics> {
995 if bin_count == 0 {
996 return Err(OxiGeoError::InvalidParameter {
997 parameter: "bin_count",
998 message: "bin_count must be at least 1".to_string(),
999 });
1000 }
1001
1002 let mut min = f64::MAX;
1004 let mut max = f64::MIN;
1005 let mut sum = 0.0f64;
1006 let mut sum_sq = 0.0f64;
1007 let mut valid_count = 0u64;
1008 let total_pixels = (self.width * self.height) as usize;
1010 let mut valid_values: Vec<f64> = Vec::with_capacity(total_pixels);
1011
1012 for y in 0..self.height {
1013 for x in 0..self.width {
1014 let value = self.get_pixel(x, y)?;
1015 if !self.is_nodata(value) && value.is_finite() {
1016 min = min.min(value);
1017 max = max.max(value);
1018 sum += value;
1019 sum_sq += value * value;
1020 valid_count += 1;
1021 valid_values.push(value);
1022 }
1023 }
1024 }
1025
1026 let mut bins = vec![0u64; bin_count];
1028
1029 if valid_count > 0 {
1030 let range = max - min;
1031 if range == 0.0 {
1032 bins[0] = valid_count;
1034 } else {
1035 for v in &valid_values {
1036 let bin_idx = (((v - min) / range) * bin_count as f64).floor() as usize;
1037 let bin_idx = bin_idx.min(bin_count - 1);
1039 bins[bin_idx] += 1;
1040 }
1041 }
1042 }
1043
1044 if valid_count == 0 {
1045 return Ok(BufferStatistics {
1046 min: f64::NAN,
1047 max: f64::NAN,
1048 mean: f64::NAN,
1049 std_dev: f64::NAN,
1050 valid_count: 0,
1051 histogram: Some(bins),
1052 });
1053 }
1054
1055 let mean = sum / valid_count as f64;
1056 let variance = (sum_sq / valid_count as f64) - (mean * mean);
1057 let std_dev = variance.max(0.0).sqrt();
1058
1059 Ok(BufferStatistics {
1060 min,
1061 max,
1062 mean,
1063 std_dev,
1064 valid_count,
1065 histogram: Some(bins),
1066 })
1067 }
1068}
1069
1070impl fmt::Debug for RasterBuffer {
1071 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1072 f.debug_struct("RasterBuffer")
1073 .field("width", &self.width)
1074 .field("height", &self.height)
1075 .field("data_type", &self.data_type)
1076 .field("nodata", &self.nodata)
1077 .field("bytes", &self.data.len())
1078 .finish()
1079 }
1080}
1081
1082#[derive(Debug, Clone, PartialEq)]
1087pub struct BufferStatistics {
1088 pub min: f64,
1090 pub max: f64,
1092 pub mean: f64,
1094 pub std_dev: f64,
1096 pub valid_count: u64,
1098 pub histogram: Option<Vec<u64>>,
1103}
1104
1105#[cfg(feature = "arrow")]
1106mod arrow_support {
1107 use arrow_array::{Array, Float64Array};
1110
1111 use super::{OxiGeoError, RasterBuffer, Result};
1112
1113 impl RasterBuffer {
1114 pub fn from_arrow_array<A: Array>(_array: &A, _width: u64, _height: u64) -> Result<Self> {
1119 Err(OxiGeoError::NotSupported {
1122 operation: "Arrow array conversion".to_string(),
1123 })
1124 }
1125
1126 pub fn to_float64_array(&self) -> Result<Float64Array> {
1128 let mut values = Vec::with_capacity(self.pixel_count() as usize);
1129 for y in 0..self.height {
1130 for x in 0..self.width {
1131 values.push(self.get_pixel(x, y)?);
1132 }
1133 }
1134 Ok(Float64Array::from(values))
1135 }
1136 }
1137}
1138
1139#[cfg(test)]
1140mod tests {
1141 #![allow(clippy::expect_used)]
1142
1143 use super::*;
1144
1145 #[test]
1146 fn test_buffer_creation() {
1147 let buffer = RasterBuffer::zeros(100, 100, RasterDataType::UInt8);
1148 assert_eq!(buffer.width(), 100);
1149 assert_eq!(buffer.height(), 100);
1150 assert_eq!(buffer.pixel_count(), 10_000);
1151 assert_eq!(buffer.as_bytes().len(), 10_000);
1152 }
1153
1154 #[test]
1155 fn test_pixel_access() {
1156 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::Float32);
1157
1158 buffer.set_pixel(5, 5, 42.0).expect("set should work");
1159 let value = buffer.get_pixel(5, 5).expect("get should work");
1160 assert!((value - 42.0).abs() < f64::EPSILON);
1161
1162 assert!(buffer.get_pixel(100, 0).is_err());
1164 assert!(buffer.set_pixel(0, 100, 0.0).is_err());
1165 }
1166
1167 #[test]
1168 fn test_nodata() {
1169 let buffer = RasterBuffer::nodata_filled(
1170 10,
1171 10,
1172 RasterDataType::Float32,
1173 NoDataValue::Float(-9999.0),
1174 );
1175
1176 assert!(buffer.is_nodata(-9999.0));
1177 assert!(!buffer.is_nodata(0.0));
1178 }
1179
1180 #[test]
1181 fn test_statistics() {
1182 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::Float32);
1183
1184 for y in 0..10 {
1186 for x in 0..10 {
1187 let value = (y * 10 + x) as f64;
1188 buffer.set_pixel(x, y, value).expect("set should work");
1189 }
1190 }
1191
1192 let stats = buffer.compute_statistics().expect("stats should work");
1193 assert!((stats.min - 0.0).abs() < f64::EPSILON);
1194 assert!((stats.max - 99.0).abs() < f64::EPSILON);
1195 assert!((stats.mean - 49.5).abs() < 0.01);
1196 assert_eq!(stats.valid_count, 100);
1197 }
1198
1199 #[test]
1200 fn test_data_validation() {
1201 let result = RasterBuffer::new(
1203 vec![0u8; 100],
1204 10,
1205 10,
1206 RasterDataType::UInt16, NoDataValue::None,
1208 );
1209 assert!(result.is_err());
1210
1211 let result = RasterBuffer::new(
1213 vec![0u8; 200],
1214 10,
1215 10,
1216 RasterDataType::UInt16,
1217 NoDataValue::None,
1218 );
1219 assert!(result.is_ok());
1220 }
1221
1222 #[test]
1223 fn test_typed_get_u8() {
1224 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::UInt8);
1225 buffer.set_pixel(3, 4, 200.0).expect("set should work");
1226 assert_eq!(buffer.get_u8(3, 4).expect("get_u8"), 200);
1227 assert!(buffer.get_f32(3, 4).is_err());
1229 }
1230
1231 #[test]
1232 fn test_typed_get_i8() {
1233 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::Int8);
1234 buffer.set_pixel(0, 0, -42.0).expect("set should work");
1235 assert_eq!(buffer.get_i8(0, 0).expect("get_i8"), -42);
1236 }
1237
1238 #[test]
1239 fn test_typed_get_u16() {
1240 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::UInt16);
1241 buffer.set_pixel(5, 5, 60000.0).expect("set should work");
1242 assert_eq!(buffer.get_u16(5, 5).expect("get_u16"), 60000);
1243 }
1244
1245 #[test]
1246 fn test_typed_get_i16() {
1247 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::Int16);
1248 buffer.set_pixel(2, 3, -1234.0).expect("set should work");
1249 assert_eq!(buffer.get_i16(2, 3).expect("get_i16"), -1234);
1250 }
1251
1252 #[test]
1253 fn test_typed_get_u32() {
1254 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::UInt32);
1255 buffer.set_pixel(0, 0, 100_000.0).expect("set should work");
1256 assert_eq!(buffer.get_u32(0, 0).expect("get_u32"), 100_000);
1257 }
1258
1259 #[test]
1260 fn test_typed_get_i32() {
1261 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::Int32);
1262 buffer.set_pixel(1, 1, -50_000.0).expect("set should work");
1263 assert_eq!(buffer.get_i32(1, 1).expect("get_i32"), -50_000);
1264 }
1265
1266 #[test]
1267 fn test_typed_get_u64() {
1268 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::UInt64);
1269 buffer
1270 .set_pixel(0, 0, 1_000_000.0)
1271 .expect("set should work");
1272 assert_eq!(buffer.get_u64(0, 0).expect("get_u64"), 1_000_000);
1273 }
1274
1275 #[test]
1276 fn test_typed_get_i64() {
1277 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::Int64);
1278 buffer.set_pixel(0, 0, -999_999.0).expect("set should work");
1279 assert_eq!(buffer.get_i64(0, 0).expect("get_i64"), -999_999);
1280 }
1281
1282 #[test]
1283 fn test_typed_get_f32() {
1284 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::Float32);
1285 buffer
1286 .set_f32(7, 8, core::f32::consts::PI)
1287 .expect("set_f32 should work");
1288 let val = buffer.get_f32(7, 8).expect("get_f32");
1289 assert!((val - core::f32::consts::PI).abs() < 1e-5);
1290 }
1291
1292 #[test]
1293 fn test_typed_get_f64() {
1294 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::Float64);
1295 buffer
1296 .set_f64(9, 9, core::f64::consts::E)
1297 .expect("set_f64 should work");
1298 let val = buffer.get_f64(9, 9).expect("get_f64");
1299 assert!((val - core::f64::consts::E).abs() < 1e-9);
1300 }
1301
1302 #[test]
1303 fn test_typed_set_u8() {
1304 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::UInt8);
1305 buffer.set_u8(0, 0, 255).expect("set_u8 should work");
1306 assert_eq!(buffer.get_u8(0, 0).expect("get_u8"), 255);
1307 }
1308
1309 #[test]
1310 fn test_typed_out_of_bounds() {
1311 let buffer = RasterBuffer::zeros(10, 10, RasterDataType::Float32);
1312 assert!(buffer.get_f32(10, 0).is_err());
1313 assert!(buffer.get_f32(0, 10).is_err());
1314 }
1315
1316 #[test]
1317 fn test_typed_wrong_type() {
1318 let buffer = RasterBuffer::zeros(10, 10, RasterDataType::UInt8);
1319 assert!(buffer.get_f32(0, 0).is_err());
1320 assert!(buffer.get_u16(0, 0).is_err());
1321 assert!(buffer.get_i32(0, 0).is_err());
1322 assert!(buffer.get_f64(0, 0).is_err());
1323 }
1324
1325 #[test]
1326 fn test_row_slice() {
1327 let mut buffer = RasterBuffer::zeros(5, 3, RasterDataType::Float32);
1328 for x in 0..5 {
1329 buffer
1330 .set_pixel(x, 1, (x + 10) as f64)
1331 .expect("set should work");
1332 }
1333 let row: &[f32] = buffer.row_slice(1).expect("row_slice should work");
1334 assert_eq!(row.len(), 5);
1335 assert!((row[0] - 10.0).abs() < 1e-5);
1336 assert!((row[4] - 14.0).abs() < 1e-5);
1337 }
1338
1339 #[test]
1340 fn test_row_slice_out_of_bounds() {
1341 let buffer = RasterBuffer::zeros(5, 3, RasterDataType::Float32);
1342 assert!(buffer.row_slice::<f32>(3).is_err());
1343 }
1344
1345 #[test]
1346 fn test_window() {
1347 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::UInt8);
1348 buffer.set_pixel(5, 5, 42.0).expect("set should work");
1349 buffer.set_pixel(6, 6, 99.0).expect("set should work");
1350
1351 let win = buffer.window(4, 4, 4, 4).expect("window should work");
1352 assert_eq!(win.width(), 4);
1353 assert_eq!(win.height(), 4);
1354 let val = win.get_pixel(1, 1).expect("get should work");
1356 assert!((val - 42.0).abs() < f64::EPSILON);
1357 let val = win.get_pixel(2, 2).expect("get should work");
1359 assert!((val - 99.0).abs() < f64::EPSILON);
1360 }
1361
1362 #[test]
1363 fn test_window_out_of_bounds() {
1364 let buffer = RasterBuffer::zeros(10, 10, RasterDataType::UInt8);
1365 assert!(buffer.window(8, 8, 4, 4).is_err());
1366 }
1367
1368 #[test]
1369 fn test_fill_value_cfloat32_writes_real_zero_imag() {
1370 let fill: f64 = 1.23456789;
1372 let mut buf = RasterBuffer::zeros(2, 2, RasterDataType::CFloat32);
1373 buf.fill_value(fill);
1374 let v = buf.get_pixel(0, 0).expect("pixel access");
1376 assert!(
1377 (v - fill).abs() < 1e-5,
1378 "real part should be {fill}, got {v}"
1379 );
1380 let raw = buf.as_bytes();
1382 let imag_bytes: [u8; 4] = raw[4..8].try_into().expect("slice");
1383 let imag = f32::from_ne_bytes(imag_bytes);
1384 assert_eq!(imag, 0.0f32, "imaginary part should be 0.0");
1385 }
1386
1387 #[test]
1388 fn test_fill_value_cfloat64_writes_real_zero_imag() {
1389 let fill: f64 = 9.876_543_21_f64;
1391 let mut buf = RasterBuffer::zeros(2, 2, RasterDataType::CFloat64);
1392 buf.fill_value(fill);
1393 let v = buf.get_pixel(0, 0).expect("pixel access");
1394 assert!((v - fill).abs() < 1e-9, "real part mismatch, got {v}");
1395 let raw = buf.as_bytes();
1397 let imag_bytes: [u8; 8] = raw[8..16].try_into().expect("slice");
1398 let imag = f64::from_ne_bytes(imag_bytes);
1399 assert_eq!(imag, 0.0f64, "imaginary part should be 0.0");
1400 }
1401}