1#![allow(unsafe_code)]
4
5use core::fmt;
75
76use crate::error::{OxiGdalError, Result};
77use crate::types::{NoDataValue, RasterDataType};
78
79#[derive(Clone)]
81pub struct RasterBuffer {
82 data: Vec<u8>,
84 width: u64,
86 height: u64,
88 data_type: RasterDataType,
90 nodata: NoDataValue,
92}
93
94impl RasterBuffer {
95 pub fn new(
100 data: Vec<u8>,
101 width: u64,
102 height: u64,
103 data_type: RasterDataType,
104 nodata: NoDataValue,
105 ) -> Result<Self> {
106 let expected_size = width * height * data_type.size_bytes() as u64;
107 if data.len() as u64 != expected_size {
108 return Err(OxiGdalError::InvalidParameter {
109 parameter: "data",
110 message: format!(
111 "Data size mismatch: expected {} bytes for {}x{} {:?}, got {}",
112 expected_size,
113 width,
114 height,
115 data_type,
116 data.len()
117 ),
118 });
119 }
120
121 Ok(Self {
122 data,
123 width,
124 height,
125 data_type,
126 nodata,
127 })
128 }
129
130 #[must_use]
132 pub fn zeros(width: u64, height: u64, data_type: RasterDataType) -> Self {
133 let size = (width * height * data_type.size_bytes() as u64) as usize;
134 Self {
135 data: vec![0u8; size],
136 width,
137 height,
138 data_type,
139 nodata: NoDataValue::None,
140 }
141 }
142
143 #[must_use]
145 pub fn nodata_filled(
146 width: u64,
147 height: u64,
148 data_type: RasterDataType,
149 nodata: NoDataValue,
150 ) -> Self {
151 let mut buffer = Self::zeros(width, height, data_type);
152 buffer.nodata = nodata;
153
154 if let Some(value) = nodata.as_f64() {
156 buffer.fill_value(value);
157 }
158
159 buffer
160 }
161
162 pub fn fill_value(&mut self, value: f64) {
164 match self.data_type {
165 RasterDataType::UInt8 => {
166 let v = value as u8;
167 self.data.fill(v);
168 }
169 RasterDataType::Int8 => {
170 let v = value as i8;
171 self.data.fill(v as u8);
172 }
173 RasterDataType::UInt16 => {
174 let v = (value as u16).to_ne_bytes();
175 for chunk in self.data.chunks_exact_mut(2) {
176 chunk.copy_from_slice(&v);
177 }
178 }
179 RasterDataType::Int16 => {
180 let v = (value as i16).to_ne_bytes();
181 for chunk in self.data.chunks_exact_mut(2) {
182 chunk.copy_from_slice(&v);
183 }
184 }
185 RasterDataType::UInt32 => {
186 let v = (value as u32).to_ne_bytes();
187 for chunk in self.data.chunks_exact_mut(4) {
188 chunk.copy_from_slice(&v);
189 }
190 }
191 RasterDataType::Int32 => {
192 let v = (value as i32).to_ne_bytes();
193 for chunk in self.data.chunks_exact_mut(4) {
194 chunk.copy_from_slice(&v);
195 }
196 }
197 RasterDataType::Float32 => {
198 let v = (value as f32).to_ne_bytes();
199 for chunk in self.data.chunks_exact_mut(4) {
200 chunk.copy_from_slice(&v);
201 }
202 }
203 RasterDataType::Float64 => {
204 let v = value.to_ne_bytes();
205 for chunk in self.data.chunks_exact_mut(8) {
206 chunk.copy_from_slice(&v);
207 }
208 }
209 RasterDataType::UInt64 => {
210 let v = (value as u64).to_ne_bytes();
211 for chunk in self.data.chunks_exact_mut(8) {
212 chunk.copy_from_slice(&v);
213 }
214 }
215 RasterDataType::Int64 => {
216 let v = (value as i64).to_ne_bytes();
217 for chunk in self.data.chunks_exact_mut(8) {
218 chunk.copy_from_slice(&v);
219 }
220 }
221 RasterDataType::CFloat32 | RasterDataType::CFloat64 => {
222 }
225 }
226 }
227
228 #[must_use]
230 pub const fn width(&self) -> u64 {
231 self.width
232 }
233
234 #[must_use]
236 pub const fn height(&self) -> u64 {
237 self.height
238 }
239
240 #[must_use]
242 pub const fn data_type(&self) -> RasterDataType {
243 self.data_type
244 }
245
246 #[must_use]
248 pub const fn nodata(&self) -> NoDataValue {
249 self.nodata
250 }
251
252 #[must_use]
254 pub const fn pixel_count(&self) -> u64 {
255 self.width * self.height
256 }
257
258 #[must_use]
260 pub fn as_bytes(&self) -> &[u8] {
261 &self.data
262 }
263
264 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
266 &mut self.data
267 }
268
269 #[must_use]
271 pub fn into_bytes(self) -> Vec<u8> {
272 self.data
273 }
274
275 pub fn from_typed_vec<T: Copy + 'static>(
286 width: usize,
287 height: usize,
288 data: Vec<T>,
289 data_type: RasterDataType,
290 ) -> Result<Self> {
291 let expected_pixels = width * height;
292 if data.len() != expected_pixels {
293 return Err(OxiGdalError::InvalidParameter {
294 parameter: "data",
295 message: format!(
296 "Data length mismatch: expected {} pixels for {}x{}, got {}",
297 expected_pixels,
298 width,
299 height,
300 data.len()
301 ),
302 });
303 }
304
305 let type_size = core::mem::size_of::<T>();
307 let expected_type_size = data_type.size_bytes();
308 if type_size != expected_type_size {
309 return Err(OxiGdalError::InvalidParameter {
310 parameter: "data_type",
311 message: format!(
312 "Type size mismatch: provided type has {} bytes, {:?} expects {} bytes",
313 type_size, data_type, expected_type_size
314 ),
315 });
316 }
317
318 let byte_data: Vec<u8> = data
319 .iter()
320 .flat_map(|v| {
321 let ptr = v as *const T as *const u8;
323 unsafe { core::slice::from_raw_parts(ptr, type_size) }.to_vec()
324 })
325 .collect();
326
327 Self::new(
328 byte_data,
329 width as u64,
330 height as u64,
331 data_type,
332 NoDataValue::None,
333 )
334 }
335
336 pub fn as_slice<T: Copy + 'static>(&self) -> Result<&[T]> {
344 let type_size = core::mem::size_of::<T>();
345 let expected_size = self.data_type.size_bytes();
346
347 if type_size != expected_size {
348 return Err(OxiGdalError::InvalidParameter {
349 parameter: "T",
350 message: format!(
351 "Type size mismatch: requested type has {} bytes, buffer contains {:?} ({} bytes)",
352 type_size, self.data_type, expected_size
353 ),
354 });
355 }
356
357 let pixel_count = (self.width * self.height) as usize;
358 let slice =
361 unsafe { core::slice::from_raw_parts(self.data.as_ptr() as *const T, pixel_count) };
362 Ok(slice)
363 }
364
365 pub fn as_slice_mut<T: Copy + 'static>(&mut self) -> Result<&mut [T]> {
373 let type_size = core::mem::size_of::<T>();
374 let expected_size = self.data_type.size_bytes();
375
376 if type_size != expected_size {
377 return Err(OxiGdalError::InvalidParameter {
378 parameter: "T",
379 message: format!(
380 "Type size mismatch: requested type has {} bytes, buffer contains {:?} ({} bytes)",
381 type_size, self.data_type, expected_size
382 ),
383 });
384 }
385
386 let pixel_count = (self.width * self.height) as usize;
387 let slice = unsafe {
390 core::slice::from_raw_parts_mut(self.data.as_mut_ptr() as *mut T, pixel_count)
391 };
392 Ok(slice)
393 }
394
395 pub fn get_pixel(&self, x: u64, y: u64) -> Result<f64> {
400 if x >= self.width || y >= self.height {
401 return Err(OxiGdalError::OutOfBounds {
402 message: format!(
403 "Pixel ({}, {}) out of bounds for {}x{} buffer",
404 x, y, self.width, self.height
405 ),
406 });
407 }
408
409 let pixel_size = self.data_type.size_bytes();
410 let offset = (y * self.width + x) as usize * pixel_size;
411
412 let value = match self.data_type {
413 RasterDataType::UInt8 => f64::from(self.data[offset]),
414 RasterDataType::Int8 => f64::from(self.data[offset] as i8),
415 RasterDataType::UInt16 => {
416 let bytes: [u8; 2] = self.data[offset..offset + 2].try_into().map_err(|_| {
417 OxiGdalError::Internal {
418 message: "Invalid slice length".to_string(),
419 }
420 })?;
421 f64::from(u16::from_ne_bytes(bytes))
422 }
423 RasterDataType::Int16 => {
424 let bytes: [u8; 2] = self.data[offset..offset + 2].try_into().map_err(|_| {
425 OxiGdalError::Internal {
426 message: "Invalid slice length".to_string(),
427 }
428 })?;
429 f64::from(i16::from_ne_bytes(bytes))
430 }
431 RasterDataType::UInt32 => {
432 let bytes: [u8; 4] = self.data[offset..offset + 4].try_into().map_err(|_| {
433 OxiGdalError::Internal {
434 message: "Invalid slice length".to_string(),
435 }
436 })?;
437 f64::from(u32::from_ne_bytes(bytes))
438 }
439 RasterDataType::Int32 => {
440 let bytes: [u8; 4] = self.data[offset..offset + 4].try_into().map_err(|_| {
441 OxiGdalError::Internal {
442 message: "Invalid slice length".to_string(),
443 }
444 })?;
445 f64::from(i32::from_ne_bytes(bytes))
446 }
447 RasterDataType::Float32 => {
448 let bytes: [u8; 4] = self.data[offset..offset + 4].try_into().map_err(|_| {
449 OxiGdalError::Internal {
450 message: "Invalid slice length".to_string(),
451 }
452 })?;
453 f64::from(f32::from_ne_bytes(bytes))
454 }
455 RasterDataType::Float64 => {
456 let bytes: [u8; 8] = self.data[offset..offset + 8].try_into().map_err(|_| {
457 OxiGdalError::Internal {
458 message: "Invalid slice length".to_string(),
459 }
460 })?;
461 f64::from_ne_bytes(bytes)
462 }
463 RasterDataType::UInt64 => {
464 let bytes: [u8; 8] = self.data[offset..offset + 8].try_into().map_err(|_| {
465 OxiGdalError::Internal {
466 message: "Invalid slice length".to_string(),
467 }
468 })?;
469 u64::from_ne_bytes(bytes) as f64
470 }
471 RasterDataType::Int64 => {
472 let bytes: [u8; 8] = self.data[offset..offset + 8].try_into().map_err(|_| {
473 OxiGdalError::Internal {
474 message: "Invalid slice length".to_string(),
475 }
476 })?;
477 i64::from_ne_bytes(bytes) as f64
478 }
479 RasterDataType::CFloat32 | RasterDataType::CFloat64 => {
480 let bytes: [u8; 4] = self.data[offset..offset + 4].try_into().map_err(|_| {
482 OxiGdalError::Internal {
483 message: "Invalid slice length".to_string(),
484 }
485 })?;
486 f64::from(f32::from_ne_bytes(bytes))
487 }
488 };
489
490 Ok(value)
491 }
492
493 pub fn set_pixel(&mut self, x: u64, y: u64, value: f64) -> Result<()> {
498 if x >= self.width || y >= self.height {
499 return Err(OxiGdalError::OutOfBounds {
500 message: format!(
501 "Pixel ({}, {}) out of bounds for {}x{} buffer",
502 x, y, self.width, self.height
503 ),
504 });
505 }
506
507 let pixel_size = self.data_type.size_bytes();
508 let offset = (y * self.width + x) as usize * pixel_size;
509
510 match self.data_type {
511 RasterDataType::UInt8 => {
512 self.data[offset] = value as u8;
513 }
514 RasterDataType::Int8 => {
515 self.data[offset] = (value as i8) as u8;
516 }
517 RasterDataType::UInt16 => {
518 let bytes = (value as u16).to_ne_bytes();
519 self.data[offset..offset + 2].copy_from_slice(&bytes);
520 }
521 RasterDataType::Int16 => {
522 let bytes = (value as i16).to_ne_bytes();
523 self.data[offset..offset + 2].copy_from_slice(&bytes);
524 }
525 RasterDataType::UInt32 => {
526 let bytes = (value as u32).to_ne_bytes();
527 self.data[offset..offset + 4].copy_from_slice(&bytes);
528 }
529 RasterDataType::Int32 => {
530 let bytes = (value as i32).to_ne_bytes();
531 self.data[offset..offset + 4].copy_from_slice(&bytes);
532 }
533 RasterDataType::Float32 => {
534 let bytes = (value as f32).to_ne_bytes();
535 self.data[offset..offset + 4].copy_from_slice(&bytes);
536 }
537 RasterDataType::Float64 => {
538 let bytes = value.to_ne_bytes();
539 self.data[offset..offset + 8].copy_from_slice(&bytes);
540 }
541 RasterDataType::UInt64 => {
542 let bytes = (value as u64).to_ne_bytes();
543 self.data[offset..offset + 8].copy_from_slice(&bytes);
544 }
545 RasterDataType::Int64 => {
546 let bytes = (value as i64).to_ne_bytes();
547 self.data[offset..offset + 8].copy_from_slice(&bytes);
548 }
549 RasterDataType::CFloat32 => {
550 let bytes = (value as f32).to_ne_bytes();
552 self.data[offset..offset + 4].copy_from_slice(&bytes);
553 }
554 RasterDataType::CFloat64 => {
555 let bytes = value.to_ne_bytes();
557 self.data[offset..offset + 8].copy_from_slice(&bytes);
558 }
559 }
560
561 Ok(())
562 }
563
564 #[must_use]
566 pub fn is_nodata(&self, value: f64) -> bool {
567 match self.nodata.as_f64() {
568 Some(nd) => {
569 if nd.is_nan() && value.is_nan() {
570 true
571 } else {
572 (nd - value).abs() < f64::EPSILON
573 }
574 }
575 None => false,
576 }
577 }
578
579 pub fn convert_to(&self, target_type: RasterDataType) -> Result<Self> {
584 if target_type == self.data_type {
585 return Ok(self.clone());
586 }
587
588 let mut result = Self::zeros(self.width, self.height, target_type);
589 result.nodata = self.nodata;
590
591 for y in 0..self.height {
592 for x in 0..self.width {
593 let value = self.get_pixel(x, y)?;
594 result.set_pixel(x, y, value)?;
595 }
596 }
597
598 Ok(result)
599 }
600
601 pub fn compute_statistics(&self) -> Result<BufferStatistics> {
603 let mut min = f64::MAX;
604 let mut max = f64::MIN;
605 let mut sum = 0.0;
606 let mut sum_sq = 0.0;
607 let mut valid_count = 0u64;
608
609 for y in 0..self.height {
610 for x in 0..self.width {
611 let value = self.get_pixel(x, y)?;
612 if !self.is_nodata(value) && value.is_finite() {
613 min = min.min(value);
614 max = max.max(value);
615 sum += value;
616 sum_sq += value * value;
617 valid_count += 1;
618 }
619 }
620 }
621
622 if valid_count == 0 {
623 return Ok(BufferStatistics {
624 min: f64::NAN,
625 max: f64::NAN,
626 mean: f64::NAN,
627 std_dev: f64::NAN,
628 valid_count: 0,
629 });
630 }
631
632 let mean = sum / valid_count as f64;
633 let variance = (sum_sq / valid_count as f64) - (mean * mean);
634 let std_dev = variance.sqrt();
635
636 Ok(BufferStatistics {
637 min,
638 max,
639 mean,
640 std_dev,
641 valid_count,
642 })
643 }
644}
645
646impl fmt::Debug for RasterBuffer {
647 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
648 f.debug_struct("RasterBuffer")
649 .field("width", &self.width)
650 .field("height", &self.height)
651 .field("data_type", &self.data_type)
652 .field("nodata", &self.nodata)
653 .field("bytes", &self.data.len())
654 .finish()
655 }
656}
657
658#[derive(Debug, Clone, Copy, PartialEq)]
660pub struct BufferStatistics {
661 pub min: f64,
663 pub max: f64,
665 pub mean: f64,
667 pub std_dev: f64,
669 pub valid_count: u64,
671}
672
673#[cfg(feature = "arrow")]
674mod arrow_support {
675 use arrow_array::{Array, Float64Array};
678
679 use super::{OxiGdalError, RasterBuffer, Result};
680
681 impl RasterBuffer {
682 pub fn from_arrow_array<A: Array>(_array: &A, _width: u64, _height: u64) -> Result<Self> {
687 Err(OxiGdalError::NotSupported {
690 operation: "Arrow array conversion".to_string(),
691 })
692 }
693
694 pub fn to_float64_array(&self) -> Result<Float64Array> {
696 let mut values = Vec::with_capacity(self.pixel_count() as usize);
697 for y in 0..self.height {
698 for x in 0..self.width {
699 values.push(self.get_pixel(x, y)?);
700 }
701 }
702 Ok(Float64Array::from(values))
703 }
704 }
705}
706
707#[cfg(test)]
708mod tests {
709 #![allow(clippy::expect_used)]
710
711 use super::*;
712
713 #[test]
714 fn test_buffer_creation() {
715 let buffer = RasterBuffer::zeros(100, 100, RasterDataType::UInt8);
716 assert_eq!(buffer.width(), 100);
717 assert_eq!(buffer.height(), 100);
718 assert_eq!(buffer.pixel_count(), 10_000);
719 assert_eq!(buffer.as_bytes().len(), 10_000);
720 }
721
722 #[test]
723 fn test_pixel_access() {
724 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::Float32);
725
726 buffer.set_pixel(5, 5, 42.0).expect("set should work");
727 let value = buffer.get_pixel(5, 5).expect("get should work");
728 assert!((value - 42.0).abs() < f64::EPSILON);
729
730 assert!(buffer.get_pixel(100, 0).is_err());
732 assert!(buffer.set_pixel(0, 100, 0.0).is_err());
733 }
734
735 #[test]
736 fn test_nodata() {
737 let buffer = RasterBuffer::nodata_filled(
738 10,
739 10,
740 RasterDataType::Float32,
741 NoDataValue::Float(-9999.0),
742 );
743
744 assert!(buffer.is_nodata(-9999.0));
745 assert!(!buffer.is_nodata(0.0));
746 }
747
748 #[test]
749 fn test_statistics() {
750 let mut buffer = RasterBuffer::zeros(10, 10, RasterDataType::Float32);
751
752 for y in 0..10 {
754 for x in 0..10 {
755 let value = (y * 10 + x) as f64;
756 buffer.set_pixel(x, y, value).expect("set should work");
757 }
758 }
759
760 let stats = buffer.compute_statistics().expect("stats should work");
761 assert!((stats.min - 0.0).abs() < f64::EPSILON);
762 assert!((stats.max - 99.0).abs() < f64::EPSILON);
763 assert!((stats.mean - 49.5).abs() < 0.01);
764 assert_eq!(stats.valid_count, 100);
765 }
766
767 #[test]
768 fn test_data_validation() {
769 let result = RasterBuffer::new(
771 vec![0u8; 100],
772 10,
773 10,
774 RasterDataType::UInt16, NoDataValue::None,
776 );
777 assert!(result.is_err());
778
779 let result = RasterBuffer::new(
781 vec![0u8; 200],
782 10,
783 10,
784 RasterDataType::UInt16,
785 NoDataValue::None,
786 );
787 assert!(result.is_ok());
788 }
789}