1#![allow(unsafe_code)]
4
5use core::fmt;
104
105#[cfg(not(feature = "std"))]
106use crate::compat::*;
107use crate::error::{OxiGeoError, Result};
108#[cfg(not(feature = "std"))]
109use crate::math::FloatExt;
110use crate::types::{NoDataValue, RasterDataType};
111
112pub mod element;
113pub use element::{
114 FloatToIntRounding, RasterElement, RasterElementKind, convert_raw_bytes, convert_raw_into,
115 convert_raw_into_with, elements_as_bytes,
116};
117
118mod band_iterator;
119pub use band_iterator::{BandIterator, BandRef, MultiBandBuffer};
120
121mod raster_window;
122pub use raster_window::RasterWindow;
123
124pub mod mask;
125pub use mask::Mask;
126
127#[cfg(feature = "std")]
128pub use crate::simd_buffer::{ArenaTile, TileIteratorArena};
129
130#[cfg(feature = "arrow")]
131pub mod arrow_convert;
132
133#[derive(Clone)]
135pub struct RasterBuffer {
136 data: Vec<u8>,
138 width: u64,
140 height: u64,
142 data_type: RasterDataType,
144 nodata: NoDataValue,
146}
147
148impl RasterBuffer {
149 pub fn new(
154 data: Vec<u8>,
155 width: u64,
156 height: u64,
157 data_type: RasterDataType,
158 nodata: NoDataValue,
159 ) -> Result<Self> {
160 let expected_size = width * height * data_type.size_bytes() as u64;
161 if data.len() as u64 != expected_size {
162 return Err(OxiGeoError::InvalidParameter {
163 parameter: "data",
164 message: format!(
165 "Data size mismatch: expected {} bytes for {}x{} {:?}, got {}",
166 expected_size,
167 width,
168 height,
169 data_type,
170 data.len()
171 ),
172 });
173 }
174
175 Ok(Self {
176 data,
177 width,
178 height,
179 data_type,
180 nodata,
181 })
182 }
183
184 #[must_use]
186 pub fn zeros(width: u64, height: u64, data_type: RasterDataType) -> Self {
187 let size = (width * height * data_type.size_bytes() as u64) as usize;
188 Self {
189 data: vec![0u8; size],
190 width,
191 height,
192 data_type,
193 nodata: NoDataValue::None,
194 }
195 }
196
197 #[must_use]
199 pub fn nodata_filled(
200 width: u64,
201 height: u64,
202 data_type: RasterDataType,
203 nodata: NoDataValue,
204 ) -> Self {
205 let mut buffer = Self::zeros(width, height, data_type);
206 buffer.nodata = nodata;
207
208 if let Some(value) = nodata.as_f64() {
210 buffer.fill_value(value);
211 }
212
213 buffer
214 }
215
216 pub fn fill_value(&mut self, value: f64) {
218 match self.data_type {
219 RasterDataType::UInt8 => {
220 let v = value as u8;
221 self.data.fill(v);
222 }
223 RasterDataType::Int8 => {
224 let v = value as i8;
225 self.data.fill(v as u8);
226 }
227 RasterDataType::UInt16 => {
228 let v = (value as u16).to_ne_bytes();
229 for chunk in self.data.chunks_exact_mut(2) {
230 chunk.copy_from_slice(&v);
231 }
232 }
233 RasterDataType::Int16 => {
234 let v = (value as i16).to_ne_bytes();
235 for chunk in self.data.chunks_exact_mut(2) {
236 chunk.copy_from_slice(&v);
237 }
238 }
239 RasterDataType::UInt32 => {
240 let v = (value as u32).to_ne_bytes();
241 for chunk in self.data.chunks_exact_mut(4) {
242 chunk.copy_from_slice(&v);
243 }
244 }
245 RasterDataType::Int32 => {
246 let v = (value as i32).to_ne_bytes();
247 for chunk in self.data.chunks_exact_mut(4) {
248 chunk.copy_from_slice(&v);
249 }
250 }
251 RasterDataType::Float32 => {
252 let v = (value as f32).to_ne_bytes();
253 for chunk in self.data.chunks_exact_mut(4) {
254 chunk.copy_from_slice(&v);
255 }
256 }
257 RasterDataType::Float64 => {
258 let v = value.to_ne_bytes();
259 for chunk in self.data.chunks_exact_mut(8) {
260 chunk.copy_from_slice(&v);
261 }
262 }
263 RasterDataType::UInt64 => {
264 let v = (value as u64).to_ne_bytes();
265 for chunk in self.data.chunks_exact_mut(8) {
266 chunk.copy_from_slice(&v);
267 }
268 }
269 RasterDataType::Int64 => {
270 let v = (value as i64).to_ne_bytes();
271 for chunk in self.data.chunks_exact_mut(8) {
272 chunk.copy_from_slice(&v);
273 }
274 }
275 RasterDataType::CFloat32 => {
276 let real_bytes = (value as f32).to_ne_bytes();
279 let imag_bytes = 0f32.to_ne_bytes();
280 for chunk in self.data.chunks_exact_mut(8) {
281 chunk[..4].copy_from_slice(&real_bytes);
282 chunk[4..].copy_from_slice(&imag_bytes);
283 }
284 }
285 RasterDataType::CFloat64 => {
286 let real_bytes = value.to_ne_bytes();
289 let imag_bytes = 0f64.to_ne_bytes();
290 for chunk in self.data.chunks_exact_mut(16) {
291 chunk[..8].copy_from_slice(&real_bytes);
292 chunk[8..].copy_from_slice(&imag_bytes);
293 }
294 }
295 }
296 }
297
298 #[must_use]
300 pub const fn width(&self) -> u64 {
301 self.width
302 }
303
304 #[must_use]
306 pub const fn height(&self) -> u64 {
307 self.height
308 }
309
310 #[must_use]
312 pub const fn data_type(&self) -> RasterDataType {
313 self.data_type
314 }
315
316 #[must_use]
318 pub const fn nodata(&self) -> NoDataValue {
319 self.nodata
320 }
321
322 #[must_use]
324 pub const fn pixel_count(&self) -> u64 {
325 self.width * self.height
326 }
327
328 #[must_use]
330 pub fn as_bytes(&self) -> &[u8] {
331 &self.data
332 }
333
334 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
336 &mut self.data
337 }
338
339 #[must_use]
341 pub fn into_bytes(self) -> Vec<u8> {
342 self.data
343 }
344
345 pub fn from_typed_vec<T: Copy + 'static>(
365 width: usize,
366 height: usize,
367 data: Vec<T>,
368 data_type: RasterDataType,
369 ) -> Result<Self> {
370 let expected_pixels = width * height;
371 if data.len() != expected_pixels {
372 return Err(OxiGeoError::InvalidParameter {
373 parameter: "data",
374 message: format!(
375 "Data length mismatch: expected {} pixels for {}x{}, got {}",
376 expected_pixels,
377 width,
378 height,
379 data.len()
380 ),
381 });
382 }
383
384 let type_size = core::mem::size_of::<T>();
386 let expected_type_size = data_type.size_bytes();
387 if type_size != expected_type_size {
388 return Err(OxiGeoError::InvalidParameter {
389 parameter: "data_type",
390 message: format!(
391 "Type size mismatch: provided type has {} bytes, {:?} expects {} bytes",
392 type_size, data_type, expected_type_size
393 ),
394 });
395 }
396
397 let byte_data: Vec<u8> = unsafe {
407 core::slice::from_raw_parts(data.as_ptr().cast::<u8>(), data.len() * type_size)
408 }
409 .to_vec();
410
411 Self::new(
412 byte_data,
413 width as u64,
414 height as u64,
415 data_type,
416 NoDataValue::None,
417 )
418 }
419
420 pub fn from_element_slice<T: RasterElement>(
441 width: u64,
442 height: u64,
443 data: &[T],
444 ) -> Result<Self> {
445 let expected_pixels = width * height;
446 if data.len() as u64 != expected_pixels {
447 return Err(OxiGeoError::InvalidParameter {
448 parameter: "data",
449 message: format!(
450 "Data length mismatch: expected {} pixels for {}x{}, got {}",
451 expected_pixels,
452 width,
453 height,
454 data.len()
455 ),
456 });
457 }
458
459 Self::new(
460 elements_as_bytes(data).to_vec(),
461 width,
462 height,
463 T::DATA_TYPE,
464 NoDataValue::None,
465 )
466 }
467
468 pub fn as_slice<T: Copy + 'static>(&self) -> Result<&[T]> {
479 Self::check_type_size::<T>(self.data_type)?;
480
481 let pixel_count = (self.width * self.height) as usize;
482 if pixel_count == 0 {
483 return Ok(&[]);
486 }
487
488 let ptr = self.data.as_ptr().cast::<T>();
489 Self::check_alignment(ptr)?;
490 let slice = unsafe { core::slice::from_raw_parts(ptr, pixel_count) };
496 Ok(slice)
497 }
498
499 pub fn as_slice_mut<T: Copy + 'static>(&mut self) -> Result<&mut [T]> {
508 Self::check_type_size::<T>(self.data_type)?;
509
510 let pixel_count = (self.width * self.height) as usize;
511 if pixel_count == 0 {
512 return Ok(&mut []);
513 }
514
515 let ptr = self.data.as_mut_ptr().cast::<T>();
516 Self::check_alignment(ptr.cast_const())?;
517 let slice = unsafe { core::slice::from_raw_parts_mut(ptr, pixel_count) };
520 Ok(slice)
521 }
522
523 pub fn copy_to_slice<T: RasterElement>(&self, dst: &mut [T]) -> Result<()> {
559 convert_raw_into(&self.data, self.data_type, dst)
560 }
561
562 pub fn copy_to_slice_with<T: RasterElement>(
568 &self,
569 dst: &mut [T],
570 rounding: FloatToIntRounding,
571 ) -> Result<()> {
572 convert_raw_into_with(&self.data, self.data_type, dst, rounding)
573 }
574
575 pub fn to_typed_vec<T: RasterElement>(&self) -> Result<Vec<T>> {
597 self.to_typed_vec_with(FloatToIntRounding::Nearest)
598 }
599
600 pub fn to_typed_vec_with<T: RasterElement>(
607 &self,
608 rounding: FloatToIntRounding,
609 ) -> Result<Vec<T>> {
610 let mut out = vec![T::default(); self.pixel_count() as usize];
611 self.copy_to_slice_with(&mut out, rounding)?;
612 Ok(out)
613 }
614
615 pub fn get_pixel(&self, x: u64, y: u64) -> Result<f64> {
620 if x >= self.width || y >= self.height {
621 return Err(OxiGeoError::OutOfBounds {
622 message: format!(
623 "Pixel ({}, {}) out of bounds for {}x{} buffer",
624 x, y, self.width, self.height
625 ),
626 });
627 }
628
629 let pixel_size = self.data_type.size_bytes();
630 let offset = (y * self.width + x) as usize * pixel_size;
631
632 let value = match self.data_type {
633 RasterDataType::UInt8 => f64::from(self.data[offset]),
634 RasterDataType::Int8 => f64::from(self.data[offset] as i8),
635 RasterDataType::UInt16 => {
636 let bytes: [u8; 2] = self.data[offset..offset + 2].try_into().map_err(|_| {
637 OxiGeoError::Internal {
638 message: "Invalid slice length".to_string(),
639 }
640 })?;
641 f64::from(u16::from_ne_bytes(bytes))
642 }
643 RasterDataType::Int16 => {
644 let bytes: [u8; 2] = self.data[offset..offset + 2].try_into().map_err(|_| {
645 OxiGeoError::Internal {
646 message: "Invalid slice length".to_string(),
647 }
648 })?;
649 f64::from(i16::from_ne_bytes(bytes))
650 }
651 RasterDataType::UInt32 => {
652 let bytes: [u8; 4] = self.data[offset..offset + 4].try_into().map_err(|_| {
653 OxiGeoError::Internal {
654 message: "Invalid slice length".to_string(),
655 }
656 })?;
657 f64::from(u32::from_ne_bytes(bytes))
658 }
659 RasterDataType::Int32 => {
660 let bytes: [u8; 4] = self.data[offset..offset + 4].try_into().map_err(|_| {
661 OxiGeoError::Internal {
662 message: "Invalid slice length".to_string(),
663 }
664 })?;
665 f64::from(i32::from_ne_bytes(bytes))
666 }
667 RasterDataType::Float32 => {
668 let bytes: [u8; 4] = self.data[offset..offset + 4].try_into().map_err(|_| {
669 OxiGeoError::Internal {
670 message: "Invalid slice length".to_string(),
671 }
672 })?;
673 f64::from(f32::from_ne_bytes(bytes))
674 }
675 RasterDataType::Float64 => {
676 let bytes: [u8; 8] = self.data[offset..offset + 8].try_into().map_err(|_| {
677 OxiGeoError::Internal {
678 message: "Invalid slice length".to_string(),
679 }
680 })?;
681 f64::from_ne_bytes(bytes)
682 }
683 RasterDataType::UInt64 => {
684 let bytes: [u8; 8] = self.data[offset..offset + 8].try_into().map_err(|_| {
685 OxiGeoError::Internal {
686 message: "Invalid slice length".to_string(),
687 }
688 })?;
689 u64::from_ne_bytes(bytes) as f64
690 }
691 RasterDataType::Int64 => {
692 let bytes: [u8; 8] = self.data[offset..offset + 8].try_into().map_err(|_| {
693 OxiGeoError::Internal {
694 message: "Invalid slice length".to_string(),
695 }
696 })?;
697 i64::from_ne_bytes(bytes) as f64
698 }
699 RasterDataType::CFloat32 => {
700 let bytes: [u8; 4] = self.data[offset..offset + 4].try_into().map_err(|_| {
702 OxiGeoError::Internal {
703 message: "Invalid slice length".to_string(),
704 }
705 })?;
706 f64::from(f32::from_ne_bytes(bytes))
707 }
708 RasterDataType::CFloat64 => {
709 let bytes: [u8; 8] = self.data[offset..offset + 8].try_into().map_err(|_| {
711 OxiGeoError::Internal {
712 message: "Invalid slice length".to_string(),
713 }
714 })?;
715 f64::from_ne_bytes(bytes)
716 }
717 };
718
719 Ok(value)
720 }
721
722 pub fn set_pixel(&mut self, x: u64, y: u64, value: f64) -> Result<()> {
727 if x >= self.width || y >= self.height {
728 return Err(OxiGeoError::OutOfBounds {
729 message: format!(
730 "Pixel ({}, {}) out of bounds for {}x{} buffer",
731 x, y, self.width, self.height
732 ),
733 });
734 }
735
736 let pixel_size = self.data_type.size_bytes();
737 let offset = (y * self.width + x) as usize * pixel_size;
738
739 match self.data_type {
740 RasterDataType::UInt8 => {
741 self.data[offset] = value as u8;
742 }
743 RasterDataType::Int8 => {
744 self.data[offset] = (value as i8) as u8;
745 }
746 RasterDataType::UInt16 => {
747 let bytes = (value as u16).to_ne_bytes();
748 self.data[offset..offset + 2].copy_from_slice(&bytes);
749 }
750 RasterDataType::Int16 => {
751 let bytes = (value as i16).to_ne_bytes();
752 self.data[offset..offset + 2].copy_from_slice(&bytes);
753 }
754 RasterDataType::UInt32 => {
755 let bytes = (value as u32).to_ne_bytes();
756 self.data[offset..offset + 4].copy_from_slice(&bytes);
757 }
758 RasterDataType::Int32 => {
759 let bytes = (value as i32).to_ne_bytes();
760 self.data[offset..offset + 4].copy_from_slice(&bytes);
761 }
762 RasterDataType::Float32 => {
763 let bytes = (value as f32).to_ne_bytes();
764 self.data[offset..offset + 4].copy_from_slice(&bytes);
765 }
766 RasterDataType::Float64 => {
767 let bytes = value.to_ne_bytes();
768 self.data[offset..offset + 8].copy_from_slice(&bytes);
769 }
770 RasterDataType::UInt64 => {
771 let bytes = (value as u64).to_ne_bytes();
772 self.data[offset..offset + 8].copy_from_slice(&bytes);
773 }
774 RasterDataType::Int64 => {
775 let bytes = (value as i64).to_ne_bytes();
776 self.data[offset..offset + 8].copy_from_slice(&bytes);
777 }
778 RasterDataType::CFloat32 => {
779 let bytes = (value as f32).to_ne_bytes();
781 self.data[offset..offset + 4].copy_from_slice(&bytes);
782 }
783 RasterDataType::CFloat64 => {
784 let bytes = value.to_ne_bytes();
786 self.data[offset..offset + 8].copy_from_slice(&bytes);
787 }
788 }
789
790 Ok(())
791 }
792
793 pub fn get_u8(&self, x: u64, y: u64) -> Result<u8> {
800 self.check_bounds(x, y)?;
801 self.check_type(RasterDataType::UInt8)?;
802 let offset = (y * self.width + x) as usize;
803 Ok(self.data[offset])
804 }
805
806 pub fn get_i8(&self, x: u64, y: u64) -> Result<i8> {
811 self.check_bounds(x, y)?;
812 self.check_type(RasterDataType::Int8)?;
813 let offset = (y * self.width + x) as usize;
814 Ok(self.data[offset] as i8)
815 }
816
817 pub fn get_u16(&self, x: u64, y: u64) -> Result<u16> {
822 self.check_bounds(x, y)?;
823 self.check_type(RasterDataType::UInt16)?;
824 let offset = (y * self.width + x) as usize * 2;
825 let bytes: [u8; 2] =
826 self.data[offset..offset + 2]
827 .try_into()
828 .map_err(|_| OxiGeoError::Internal {
829 message: "Invalid slice length".to_string(),
830 })?;
831 Ok(u16::from_ne_bytes(bytes))
832 }
833
834 pub fn get_i16(&self, x: u64, y: u64) -> Result<i16> {
839 self.check_bounds(x, y)?;
840 self.check_type(RasterDataType::Int16)?;
841 let offset = (y * self.width + x) as usize * 2;
842 let bytes: [u8; 2] =
843 self.data[offset..offset + 2]
844 .try_into()
845 .map_err(|_| OxiGeoError::Internal {
846 message: "Invalid slice length".to_string(),
847 })?;
848 Ok(i16::from_ne_bytes(bytes))
849 }
850
851 pub fn get_u32(&self, x: u64, y: u64) -> Result<u32> {
856 self.check_bounds(x, y)?;
857 self.check_type(RasterDataType::UInt32)?;
858 let offset = (y * self.width + x) as usize * 4;
859 let bytes: [u8; 4] =
860 self.data[offset..offset + 4]
861 .try_into()
862 .map_err(|_| OxiGeoError::Internal {
863 message: "Invalid slice length".to_string(),
864 })?;
865 Ok(u32::from_ne_bytes(bytes))
866 }
867
868 pub fn get_i32(&self, x: u64, y: u64) -> Result<i32> {
873 self.check_bounds(x, y)?;
874 self.check_type(RasterDataType::Int32)?;
875 let offset = (y * self.width + x) as usize * 4;
876 let bytes: [u8; 4] =
877 self.data[offset..offset + 4]
878 .try_into()
879 .map_err(|_| OxiGeoError::Internal {
880 message: "Invalid slice length".to_string(),
881 })?;
882 Ok(i32::from_ne_bytes(bytes))
883 }
884
885 pub fn get_u64(&self, x: u64, y: u64) -> Result<u64> {
890 self.check_bounds(x, y)?;
891 self.check_type(RasterDataType::UInt64)?;
892 let offset = (y * self.width + x) as usize * 8;
893 let bytes: [u8; 8] =
894 self.data[offset..offset + 8]
895 .try_into()
896 .map_err(|_| OxiGeoError::Internal {
897 message: "Invalid slice length".to_string(),
898 })?;
899 Ok(u64::from_ne_bytes(bytes))
900 }
901
902 pub fn get_i64(&self, x: u64, y: u64) -> Result<i64> {
907 self.check_bounds(x, y)?;
908 self.check_type(RasterDataType::Int64)?;
909 let offset = (y * self.width + x) as usize * 8;
910 let bytes: [u8; 8] =
911 self.data[offset..offset + 8]
912 .try_into()
913 .map_err(|_| OxiGeoError::Internal {
914 message: "Invalid slice length".to_string(),
915 })?;
916 Ok(i64::from_ne_bytes(bytes))
917 }
918
919 pub fn get_f32(&self, x: u64, y: u64) -> Result<f32> {
924 self.check_bounds(x, y)?;
925 self.check_type(RasterDataType::Float32)?;
926 let offset = (y * self.width + x) as usize * 4;
927 let bytes: [u8; 4] =
928 self.data[offset..offset + 4]
929 .try_into()
930 .map_err(|_| OxiGeoError::Internal {
931 message: "Invalid slice length".to_string(),
932 })?;
933 Ok(f32::from_ne_bytes(bytes))
934 }
935
936 pub fn get_f64(&self, x: u64, y: u64) -> Result<f64> {
941 self.check_bounds(x, y)?;
942 self.check_type(RasterDataType::Float64)?;
943 let offset = (y * self.width + x) as usize * 8;
944 let bytes: [u8; 8] =
945 self.data[offset..offset + 8]
946 .try_into()
947 .map_err(|_| OxiGeoError::Internal {
948 message: "Invalid slice length".to_string(),
949 })?;
950 Ok(f64::from_ne_bytes(bytes))
951 }
952
953 pub fn set_u8(&mut self, x: u64, y: u64, value: u8) -> Result<()> {
960 self.check_bounds(x, y)?;
961 self.check_type(RasterDataType::UInt8)?;
962 let offset = (y * self.width + x) as usize;
963 self.data[offset] = value;
964 Ok(())
965 }
966
967 pub fn set_f32(&mut self, x: u64, y: u64, value: f32) -> Result<()> {
972 self.check_bounds(x, y)?;
973 self.check_type(RasterDataType::Float32)?;
974 let offset = (y * self.width + x) as usize * 4;
975 self.data[offset..offset + 4].copy_from_slice(&value.to_ne_bytes());
976 Ok(())
977 }
978
979 pub fn set_f64(&mut self, x: u64, y: u64, value: f64) -> Result<()> {
984 self.check_bounds(x, y)?;
985 self.check_type(RasterDataType::Float64)?;
986 let offset = (y * self.width + x) as usize * 8;
987 self.data[offset..offset + 8].copy_from_slice(&value.to_ne_bytes());
988 Ok(())
989 }
990
991 pub fn row_slice<T: Copy + 'static>(&self, y: u64) -> Result<&[T]> {
999 if y >= self.height {
1000 return Err(OxiGeoError::OutOfBounds {
1001 message: format!("Row {} out of bounds for height {}", y, self.height),
1002 });
1003 }
1004 let expected_size = self.data_type.size_bytes();
1005 Self::check_type_size::<T>(self.data_type)?;
1006
1007 if self.width == 0 {
1008 return Ok(&[]);
1009 }
1010
1011 let row_start = (y * self.width) as usize * expected_size;
1012 let row_end = row_start + self.width as usize * expected_size;
1013 let ptr = self.data[row_start..row_end].as_ptr().cast::<T>();
1014 Self::check_alignment(ptr)?;
1015 let slice = unsafe { core::slice::from_raw_parts(ptr, self.width as usize) };
1020 Ok(slice)
1021 }
1022
1023 pub fn window(&self, x: u64, y: u64, width: u64, height: u64) -> Result<Self> {
1028 if x + width > self.width || y + height > self.height {
1029 return Err(OxiGeoError::OutOfBounds {
1030 message: format!(
1031 "Window ({},{}) {}x{} exceeds buffer {}x{}",
1032 x, y, width, height, self.width, self.height
1033 ),
1034 });
1035 }
1036 let pixel_size = self.data_type.size_bytes();
1037 let row_bytes = width as usize * pixel_size;
1038 let mut data = Vec::with_capacity(height as usize * row_bytes);
1039 for row in y..y + height {
1040 let src_start = (row * self.width + x) as usize * pixel_size;
1041 data.extend_from_slice(&self.data[src_start..src_start + row_bytes]);
1042 }
1043 Self::new(data, width, height, self.data_type, self.nodata)
1044 }
1045
1046 fn check_bounds(&self, x: u64, y: u64) -> Result<()> {
1049 if x >= self.width || y >= self.height {
1050 return Err(OxiGeoError::OutOfBounds {
1051 message: format!(
1052 "Pixel ({}, {}) out of bounds for {}x{} buffer",
1053 x, y, self.width, self.height
1054 ),
1055 });
1056 }
1057 Ok(())
1058 }
1059
1060 fn check_type_size<T>(data_type: RasterDataType) -> Result<()> {
1062 let type_size = core::mem::size_of::<T>();
1063 let expected_size = data_type.size_bytes();
1064 if type_size != expected_size {
1065 return Err(OxiGeoError::InvalidParameter {
1066 parameter: "T",
1067 message: format!(
1068 "Type size mismatch: requested type has {} bytes, buffer contains {:?} ({} bytes)",
1069 type_size, data_type, expected_size
1070 ),
1071 });
1072 }
1073 Ok(())
1074 }
1075
1076 fn check_alignment<T>(ptr: *const T) -> Result<()> {
1083 if ptr.is_aligned() {
1084 return Ok(());
1085 }
1086 Err(OxiGeoError::InvalidParameter {
1087 parameter: "T",
1088 message: format!(
1089 "Buffer storage is misaligned for the requested type: it needs {}-byte alignment but starts {} byte(s) past one; use `to_typed_vec`/`copy_to_slice` for an alignment-independent typed copy",
1090 core::mem::align_of::<T>(),
1091 ptr.addr() % core::mem::align_of::<T>()
1092 ),
1093 })
1094 }
1095
1096 fn check_type(&self, expected: RasterDataType) -> Result<()> {
1097 if self.data_type != expected {
1098 return Err(OxiGeoError::InvalidParameter {
1099 parameter: "data_type",
1100 message: format!(
1101 "Buffer contains {:?} data, requested {:?}",
1102 self.data_type, expected
1103 ),
1104 });
1105 }
1106 Ok(())
1107 }
1108
1109 #[must_use]
1111 pub fn is_nodata(&self, value: f64) -> bool {
1112 match self.nodata.as_f64() {
1113 Some(nd) => {
1114 if nd.is_nan() && value.is_nan() {
1115 true
1116 } else {
1117 (nd - value).abs() < f64::EPSILON
1118 }
1119 }
1120 None => false,
1121 }
1122 }
1123
1124 pub fn convert_to(&self, target_type: RasterDataType) -> Result<Self> {
1141 if target_type == self.data_type {
1142 return Ok(self.clone());
1143 }
1144
1145 let mut result = Self::zeros(self.width, self.height, target_type);
1146 result.nodata = self.nodata;
1147
1148 convert_raw_bytes(
1149 &self.data,
1150 self.data_type,
1151 &mut result.data,
1152 target_type,
1153 FloatToIntRounding::Truncate,
1154 )?;
1155
1156 Ok(result)
1157 }
1158
1159 pub fn compute_statistics(&self) -> Result<BufferStatistics> {
1161 let mut min = f64::MAX;
1162 let mut max = f64::MIN;
1163 let mut sum = 0.0;
1164 let mut sum_sq = 0.0;
1165 let mut valid_count = 0u64;
1166
1167 for y in 0..self.height {
1168 for x in 0..self.width {
1169 let value = self.get_pixel(x, y)?;
1170 if !self.is_nodata(value) && value.is_finite() {
1171 min = min.min(value);
1172 max = max.max(value);
1173 sum += value;
1174 sum_sq += value * value;
1175 valid_count += 1;
1176 }
1177 }
1178 }
1179
1180 if valid_count == 0 {
1181 return Ok(BufferStatistics {
1182 min: f64::NAN,
1183 max: f64::NAN,
1184 mean: f64::NAN,
1185 std_dev: f64::NAN,
1186 valid_count: 0,
1187 histogram: None,
1188 });
1189 }
1190
1191 let mean = sum / valid_count as f64;
1192 let variance = (sum_sq / valid_count as f64) - (mean * mean);
1193 let std_dev = variance.sqrt();
1194
1195 Ok(BufferStatistics {
1196 min,
1197 max,
1198 mean,
1199 std_dev,
1200 valid_count,
1201 histogram: None,
1202 })
1203 }
1204
1205 pub fn compute_statistics_with_histogram(&self, bin_count: usize) -> Result<BufferStatistics> {
1226 if bin_count == 0 {
1227 return Err(OxiGeoError::InvalidParameter {
1228 parameter: "bin_count",
1229 message: "bin_count must be at least 1".to_string(),
1230 });
1231 }
1232
1233 let mut min = f64::MAX;
1235 let mut max = f64::MIN;
1236 let mut sum = 0.0f64;
1237 let mut sum_sq = 0.0f64;
1238 let mut valid_count = 0u64;
1239 let total_pixels = (self.width * self.height) as usize;
1241 let mut valid_values: Vec<f64> = Vec::with_capacity(total_pixels);
1242
1243 for y in 0..self.height {
1244 for x in 0..self.width {
1245 let value = self.get_pixel(x, y)?;
1246 if !self.is_nodata(value) && value.is_finite() {
1247 min = min.min(value);
1248 max = max.max(value);
1249 sum += value;
1250 sum_sq += value * value;
1251 valid_count += 1;
1252 valid_values.push(value);
1253 }
1254 }
1255 }
1256
1257 let mut bins = vec![0u64; bin_count];
1259
1260 if valid_count > 0 {
1261 let range = max - min;
1262 if range == 0.0 {
1263 bins[0] = valid_count;
1265 } else {
1266 for v in &valid_values {
1267 let bin_idx = (((v - min) / range) * bin_count as f64).floor() as usize;
1268 let bin_idx = bin_idx.min(bin_count - 1);
1270 bins[bin_idx] += 1;
1271 }
1272 }
1273 }
1274
1275 if valid_count == 0 {
1276 return Ok(BufferStatistics {
1277 min: f64::NAN,
1278 max: f64::NAN,
1279 mean: f64::NAN,
1280 std_dev: f64::NAN,
1281 valid_count: 0,
1282 histogram: Some(bins),
1283 });
1284 }
1285
1286 let mean = sum / valid_count as f64;
1287 let variance = (sum_sq / valid_count as f64) - (mean * mean);
1288 let std_dev = variance.max(0.0).sqrt();
1289
1290 Ok(BufferStatistics {
1291 min,
1292 max,
1293 mean,
1294 std_dev,
1295 valid_count,
1296 histogram: Some(bins),
1297 })
1298 }
1299}
1300
1301impl fmt::Debug for RasterBuffer {
1302 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1303 f.debug_struct("RasterBuffer")
1304 .field("width", &self.width)
1305 .field("height", &self.height)
1306 .field("data_type", &self.data_type)
1307 .field("nodata", &self.nodata)
1308 .field("bytes", &self.data.len())
1309 .finish()
1310 }
1311}
1312
1313#[derive(Debug, Clone, PartialEq)]
1318pub struct BufferStatistics {
1319 pub min: f64,
1321 pub max: f64,
1323 pub mean: f64,
1325 pub std_dev: f64,
1327 pub valid_count: u64,
1329 pub histogram: Option<Vec<u64>>,
1334}
1335
1336#[cfg(feature = "arrow")]
1337mod arrow_support {
1338 use arrow_array::{Array, Float64Array};
1341
1342 use super::{OxiGeoError, RasterBuffer, Result};
1343
1344 impl RasterBuffer {
1345 pub fn from_arrow_array<A: Array>(_array: &A, _width: u64, _height: u64) -> Result<Self> {
1350 Err(OxiGeoError::NotSupported {
1353 operation: "Arrow array conversion".to_string(),
1354 })
1355 }
1356
1357 pub fn to_float64_array(&self) -> Result<Float64Array> {
1359 let mut values = Vec::with_capacity(self.pixel_count() as usize);
1360 for y in 0..self.height {
1361 for x in 0..self.width {
1362 values.push(self.get_pixel(x, y)?);
1363 }
1364 }
1365 Ok(Float64Array::from(values))
1366 }
1367 }
1368}
1369
1370#[cfg(test)]
1371mod tests {
1372 #![allow(clippy::expect_used)]
1373
1374 use super::*;
1375
1376 #[test]
1377 fn test_buffer_creation() {
1378 let buffer = RasterBuffer::zeros(100, 100, RasterDataType::UInt8);
1379 assert_eq!(buffer.width(), 100);
1380 assert_eq!(buffer.height(), 100);
1381 assert_eq!(buffer.pixel_count(), 10_000);
1382 assert_eq!(buffer.as_bytes().len(), 10_000);
1383 }
1384
1385 #[test]
1386 fn test_pixel_access() {
1387 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::Float32);
1388
1389 buffer.set_pixel(5, 5, 42.0).expect("set should work");
1390 let value = buffer.get_pixel(5, 5).expect("get should work");
1391 assert!((value - 42.0).abs() < f64::EPSILON);
1392
1393 assert!(buffer.get_pixel(100, 0).is_err());
1395 assert!(buffer.set_pixel(0, 100, 0.0).is_err());
1396 }
1397
1398 #[test]
1399 fn test_nodata() {
1400 let buffer = RasterBuffer::nodata_filled(
1401 10,
1402 10,
1403 RasterDataType::Float32,
1404 NoDataValue::Float(-9999.0),
1405 );
1406
1407 assert!(buffer.is_nodata(-9999.0));
1408 assert!(!buffer.is_nodata(0.0));
1409 }
1410
1411 #[test]
1412 fn test_statistics() {
1413 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::Float32);
1414
1415 for y in 0..10 {
1417 for x in 0..10 {
1418 let value = (y * 10 + x) as f64;
1419 buffer.set_pixel(x, y, value).expect("set should work");
1420 }
1421 }
1422
1423 let stats = buffer.compute_statistics().expect("stats should work");
1424 assert!((stats.min - 0.0).abs() < f64::EPSILON);
1425 assert!((stats.max - 99.0).abs() < f64::EPSILON);
1426 assert!((stats.mean - 49.5).abs() < 0.01);
1427 assert_eq!(stats.valid_count, 100);
1428 }
1429
1430 #[test]
1431 fn test_data_validation() {
1432 let result = RasterBuffer::new(
1434 vec![0u8; 100],
1435 10,
1436 10,
1437 RasterDataType::UInt16, NoDataValue::None,
1439 );
1440 assert!(result.is_err());
1441
1442 let result = RasterBuffer::new(
1444 vec![0u8; 200],
1445 10,
1446 10,
1447 RasterDataType::UInt16,
1448 NoDataValue::None,
1449 );
1450 assert!(result.is_ok());
1451 }
1452
1453 #[test]
1454 fn test_typed_get_u8() {
1455 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::UInt8);
1456 buffer.set_pixel(3, 4, 200.0).expect("set should work");
1457 assert_eq!(buffer.get_u8(3, 4).expect("get_u8"), 200);
1458 assert!(buffer.get_f32(3, 4).is_err());
1460 }
1461
1462 #[test]
1463 fn test_typed_get_i8() {
1464 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::Int8);
1465 buffer.set_pixel(0, 0, -42.0).expect("set should work");
1466 assert_eq!(buffer.get_i8(0, 0).expect("get_i8"), -42);
1467 }
1468
1469 #[test]
1470 fn test_typed_get_u16() {
1471 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::UInt16);
1472 buffer.set_pixel(5, 5, 60000.0).expect("set should work");
1473 assert_eq!(buffer.get_u16(5, 5).expect("get_u16"), 60000);
1474 }
1475
1476 #[test]
1477 fn test_typed_get_i16() {
1478 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::Int16);
1479 buffer.set_pixel(2, 3, -1234.0).expect("set should work");
1480 assert_eq!(buffer.get_i16(2, 3).expect("get_i16"), -1234);
1481 }
1482
1483 #[test]
1484 fn test_typed_get_u32() {
1485 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::UInt32);
1486 buffer.set_pixel(0, 0, 100_000.0).expect("set should work");
1487 assert_eq!(buffer.get_u32(0, 0).expect("get_u32"), 100_000);
1488 }
1489
1490 #[test]
1491 fn test_typed_get_i32() {
1492 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::Int32);
1493 buffer.set_pixel(1, 1, -50_000.0).expect("set should work");
1494 assert_eq!(buffer.get_i32(1, 1).expect("get_i32"), -50_000);
1495 }
1496
1497 #[test]
1498 fn test_typed_get_u64() {
1499 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::UInt64);
1500 buffer
1501 .set_pixel(0, 0, 1_000_000.0)
1502 .expect("set should work");
1503 assert_eq!(buffer.get_u64(0, 0).expect("get_u64"), 1_000_000);
1504 }
1505
1506 #[test]
1507 fn test_typed_get_i64() {
1508 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::Int64);
1509 buffer.set_pixel(0, 0, -999_999.0).expect("set should work");
1510 assert_eq!(buffer.get_i64(0, 0).expect("get_i64"), -999_999);
1511 }
1512
1513 #[test]
1514 fn test_typed_get_f32() {
1515 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::Float32);
1516 buffer
1517 .set_f32(7, 8, core::f32::consts::PI)
1518 .expect("set_f32 should work");
1519 let val = buffer.get_f32(7, 8).expect("get_f32");
1520 assert!((val - core::f32::consts::PI).abs() < 1e-5);
1521 }
1522
1523 #[test]
1524 fn test_typed_get_f64() {
1525 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::Float64);
1526 buffer
1527 .set_f64(9, 9, core::f64::consts::E)
1528 .expect("set_f64 should work");
1529 let val = buffer.get_f64(9, 9).expect("get_f64");
1530 assert!((val - core::f64::consts::E).abs() < 1e-9);
1531 }
1532
1533 #[test]
1534 fn test_typed_set_u8() {
1535 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::UInt8);
1536 buffer.set_u8(0, 0, 255).expect("set_u8 should work");
1537 assert_eq!(buffer.get_u8(0, 0).expect("get_u8"), 255);
1538 }
1539
1540 #[test]
1541 fn test_typed_out_of_bounds() {
1542 let buffer = RasterBuffer::zeros(10, 10, RasterDataType::Float32);
1543 assert!(buffer.get_f32(10, 0).is_err());
1544 assert!(buffer.get_f32(0, 10).is_err());
1545 }
1546
1547 #[test]
1548 fn test_typed_wrong_type() {
1549 let buffer = RasterBuffer::zeros(10, 10, RasterDataType::UInt8);
1550 assert!(buffer.get_f32(0, 0).is_err());
1551 assert!(buffer.get_u16(0, 0).is_err());
1552 assert!(buffer.get_i32(0, 0).is_err());
1553 assert!(buffer.get_f64(0, 0).is_err());
1554 }
1555
1556 #[test]
1557 fn test_row_slice() {
1558 let mut buffer = RasterBuffer::zeros(5, 3, RasterDataType::Float32);
1559 for x in 0..5 {
1560 buffer
1561 .set_pixel(x, 1, (x + 10) as f64)
1562 .expect("set should work");
1563 }
1564
1565 match buffer.row_slice::<f32>(1) {
1571 Ok(row) => {
1572 assert_eq!(row.len(), 5);
1573 assert!((row[0] - 10.0).abs() < 1e-5);
1574 assert!((row[4] - 14.0).abs() < 1e-5);
1575 }
1576 Err(err) => {
1577 let text = format!("{err:?}");
1578 assert!(text.contains("misaligned"), "unexpected error: {text}");
1579 }
1580 }
1581
1582 let all: Vec<f32> = buffer.to_typed_vec().expect("to_typed_vec should work");
1584 assert!((all[5] - 10.0).abs() < 1e-5);
1585 assert!((all[9] - 14.0).abs() < 1e-5);
1586 }
1587
1588 #[test]
1589 fn test_row_slice_out_of_bounds() {
1590 let buffer = RasterBuffer::zeros(5, 3, RasterDataType::Float32);
1591 assert!(buffer.row_slice::<f32>(3).is_err());
1592 }
1593
1594 #[test]
1595 fn test_window() {
1596 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::UInt8);
1597 buffer.set_pixel(5, 5, 42.0).expect("set should work");
1598 buffer.set_pixel(6, 6, 99.0).expect("set should work");
1599
1600 let win = buffer.window(4, 4, 4, 4).expect("window should work");
1601 assert_eq!(win.width(), 4);
1602 assert_eq!(win.height(), 4);
1603 let val = win.get_pixel(1, 1).expect("get should work");
1605 assert!((val - 42.0).abs() < f64::EPSILON);
1606 let val = win.get_pixel(2, 2).expect("get should work");
1608 assert!((val - 99.0).abs() < f64::EPSILON);
1609 }
1610
1611 #[test]
1612 fn test_window_out_of_bounds() {
1613 let buffer = RasterBuffer::zeros(10, 10, RasterDataType::UInt8);
1614 assert!(buffer.window(8, 8, 4, 4).is_err());
1615 }
1616
1617 #[test]
1618 fn test_fill_value_cfloat32_writes_real_zero_imag() {
1619 let fill: f64 = 1.23456789;
1621 let mut buf = RasterBuffer::zeros(2, 2, RasterDataType::CFloat32);
1622 buf.fill_value(fill);
1623 let v = buf.get_pixel(0, 0).expect("pixel access");
1625 assert!(
1626 (v - fill).abs() < 1e-5,
1627 "real part should be {fill}, got {v}"
1628 );
1629 let raw = buf.as_bytes();
1631 let imag_bytes: [u8; 4] = raw[4..8].try_into().expect("slice");
1632 let imag = f32::from_ne_bytes(imag_bytes);
1633 assert_eq!(imag, 0.0f32, "imaginary part should be 0.0");
1634 }
1635
1636 #[test]
1637 fn test_fill_value_cfloat64_writes_real_zero_imag() {
1638 let fill: f64 = 9.876_543_21_f64;
1640 let mut buf = RasterBuffer::zeros(2, 2, RasterDataType::CFloat64);
1641 buf.fill_value(fill);
1642 let v = buf.get_pixel(0, 0).expect("pixel access");
1643 assert!((v - fill).abs() < 1e-9, "real part mismatch, got {v}");
1644 let raw = buf.as_bytes();
1646 let imag_bytes: [u8; 8] = raw[8..16].try_into().expect("slice");
1647 let imag = f64::from_ne_bytes(imag_bytes);
1648 assert_eq!(imag, 0.0f64, "imaginary part should be 0.0");
1649 }
1650}