1use crate::band::PixelFunction;
4use crate::dataset::VrtDataset;
5use crate::error::{Result, VrtError};
6use crate::mosaic::MosaicCompositor;
7use crate::source::{PixelRect, VrtSource};
8use crate::xml::VrtXmlParser;
9use lru::LruCache;
10use oxigdal_core::buffer::RasterBuffer;
11use oxigdal_core::io::FileDataSource;
12use oxigdal_core::types::{GeoTransform, NoDataValue, RasterDataType, RasterMetadata};
13use oxigdal_geotiff::GeoTiffReader;
14use std::num::NonZeroUsize;
15use std::path::{Path, PathBuf};
16use std::sync::{Arc, Mutex};
17
18pub struct VrtReader {
20 dataset: VrtDataset,
22 source_cache: Arc<Mutex<LruCache<PathBuf, Arc<SourceDataset>>>>,
24 compositor: MosaicCompositor,
26}
27
28impl VrtReader {
29 pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
34 let dataset = VrtXmlParser::parse_file(&path)?;
35 Self::from_dataset(dataset)
36 }
37
38 pub fn from_dataset(dataset: VrtDataset) -> Result<Self> {
43 dataset.validate()?;
44
45 let cache_size =
47 NonZeroUsize::new(32).ok_or_else(|| VrtError::cache_error("Invalid cache size"))?;
48 let source_cache = Arc::new(Mutex::new(LruCache::new(cache_size)));
49
50 let compositor = MosaicCompositor::new();
51
52 Ok(Self {
53 dataset,
54 source_cache,
55 compositor,
56 })
57 }
58
59 #[must_use]
61 pub fn width(&self) -> u64 {
62 self.dataset.raster_x_size
63 }
64
65 #[must_use]
67 pub fn height(&self) -> u64 {
68 self.dataset.raster_y_size
69 }
70
71 #[must_use]
73 pub fn band_count(&self) -> usize {
74 self.dataset.band_count()
75 }
76
77 #[must_use]
79 pub fn geo_transform(&self) -> Option<&GeoTransform> {
80 self.dataset.geo_transform.as_ref()
81 }
82
83 #[must_use]
85 pub fn srs(&self) -> Option<&str> {
86 self.dataset.srs.as_deref()
87 }
88
89 #[must_use]
91 pub fn block_size(&self) -> (u32, u32) {
92 self.dataset.effective_block_size()
93 }
94
95 #[must_use]
97 pub fn metadata(&self) -> RasterMetadata {
98 let (tile_width, tile_height) = self.block_size();
99 RasterMetadata {
100 width: self.dataset.raster_x_size,
101 height: self.dataset.raster_y_size,
102 band_count: self.dataset.band_count() as u32,
103 data_type: self
104 .dataset
105 .primary_data_type()
106 .unwrap_or(RasterDataType::UInt8),
107 geo_transform: self.dataset.geo_transform,
108 crs_wkt: self.dataset.srs.clone(),
109 nodata: NoDataValue::None,
110 color_interpretation: Vec::new(),
111 layout: oxigdal_core::types::PixelLayout::Tiled {
112 tile_width,
113 tile_height,
114 },
115 driver_metadata: Vec::new(),
116 }
117 }
118
119 pub fn read_window(&self, band: usize, window: PixelRect) -> Result<RasterBuffer> {
124 let band_idx = band - 1;
125 let vrt_band = self
126 .dataset
127 .get_band(band_idx)
128 .ok_or_else(|| VrtError::band_out_of_range(band, self.dataset.band_count()))?;
129
130 let contributing_sources: Vec<&VrtSource> = vrt_band
132 .sources
133 .iter()
134 .filter(|s| s.dst_rect().map(|r| r.intersects(&window)).unwrap_or(false))
135 .collect();
136
137 if contributing_sources.is_empty() {
138 return Err(VrtError::invalid_window(
139 "No sources contribute to this window",
140 ));
141 }
142
143 let data_size = (window.x_size * window.y_size) as usize * vrt_band.data_type.size_bytes();
145 let mut data = vec![0u8; data_size];
146
147 if let Some(ref pixel_func) = vrt_band.pixel_function {
149 self.apply_pixel_function(
150 &contributing_sources,
151 &window,
152 vrt_band.data_type,
153 vrt_band.nodata,
154 pixel_func,
155 &mut data,
156 )?;
157 } else {
158 for source in &contributing_sources {
160 self.read_source_contribution(source, &window, vrt_band.data_type, &mut data)?;
161 }
162 }
163
164 RasterBuffer::new(
165 data,
166 window.x_size,
167 window.y_size,
168 vrt_band.data_type,
169 vrt_band.nodata,
170 )
171 .map_err(|e| e.into())
172 }
173
174 pub fn read_band(&self, band: usize) -> Result<RasterBuffer> {
179 let window = PixelRect::new(0, 0, self.width(), self.height());
180 self.read_window(band, window)
181 }
182
183 fn read_source_contribution(
185 &self,
186 source: &VrtSource,
187 dst_window: &PixelRect,
188 data_type: RasterDataType,
189 output: &mut [u8],
190 ) -> Result<()> {
191 let source_dst_rect = source
192 .dst_rect()
193 .ok_or_else(|| VrtError::invalid_source("Source has no destination rectangle"))?;
194
195 let intersection = source_dst_rect
197 .intersect(dst_window)
198 .ok_or_else(|| VrtError::invalid_window("Source does not intersect window"))?;
199
200 let dataset = self.open_source(source)?;
202
203 let src_window = source
205 .window
206 .as_ref()
207 .ok_or_else(|| VrtError::invalid_source("Source has no window configuration"))?;
208
209 let src_x_off = src_window.src_rect.x_off + (intersection.x_off - source_dst_rect.x_off);
211 let src_y_off = src_window.src_rect.y_off + (intersection.y_off - source_dst_rect.y_off);
212
213 let src_rect = PixelRect::new(
214 src_x_off,
215 src_y_off,
216 intersection.x_size,
217 intersection.y_size,
218 );
219
220 let source_data = dataset.read_window(source.source_band, src_rect)?;
222
223 let dst_x_off = intersection.x_off - dst_window.x_off;
225 let dst_y_off = intersection.y_off - dst_window.y_off;
226
227 let params = crate::mosaic::CompositeParams::new(
228 dst_x_off,
229 dst_y_off,
230 intersection.x_size,
231 intersection.y_size,
232 dst_window.x_size,
233 data_type,
234 );
235 self.compositor
236 .composite(source_data.as_bytes(), output, ¶ms)?;
237
238 Ok(())
239 }
240
241 fn apply_pixel_function(
243 &self,
244 sources: &[&VrtSource],
245 window: &PixelRect,
246 data_type: RasterDataType,
247 nodata: NoDataValue,
248 pixel_func: &PixelFunction,
249 output: &mut [u8],
250 ) -> Result<()> {
251 let pixel_count = (window.x_size * window.y_size) as usize;
252 let _bytes_per_pixel = data_type.size_bytes();
253
254 let mut source_buffers = Vec::new();
256 for source in sources {
257 let source_dst_rect = source
258 .dst_rect()
259 .ok_or_else(|| VrtError::invalid_source("Source has no destination rectangle"))?;
260
261 let intersection = source_dst_rect
262 .intersect(window)
263 .ok_or_else(|| VrtError::invalid_window("Source does not intersect window"))?;
264
265 let dataset = self.open_source(source)?;
266
267 let src_window = source
268 .window
269 .as_ref()
270 .ok_or_else(|| VrtError::invalid_source("Source has no window configuration"))?;
271
272 let src_x_off =
273 src_window.src_rect.x_off + (intersection.x_off - source_dst_rect.x_off);
274 let src_y_off =
275 src_window.src_rect.y_off + (intersection.y_off - source_dst_rect.y_off);
276
277 let src_rect = PixelRect::new(
278 src_x_off,
279 src_y_off,
280 intersection.x_size,
281 intersection.y_size,
282 );
283
284 let source_data = dataset.read_window(source.source_band, src_rect)?;
285 source_buffers.push((source_data, intersection));
286 }
287
288 for pixel_idx in 0..pixel_count {
290 let y = pixel_idx as u64 / window.x_size;
291 let x = pixel_idx as u64 % window.x_size;
292 let global_x = window.x_off + x;
293 let global_y = window.y_off + y;
294
295 let mut values = Vec::new();
297 for (source_buffer, intersection) in &source_buffers {
298 if global_x >= intersection.x_off
299 && global_x < intersection.x_off + intersection.x_size
300 && global_y >= intersection.y_off
301 && global_y < intersection.y_off + intersection.y_size
302 {
303 let local_x = global_x - intersection.x_off;
304 let local_y = global_y - intersection.y_off;
305 let local_idx = (local_y * intersection.x_size + local_x) as usize;
306
307 let value = self.read_pixel_value(
309 source_buffer.as_bytes(),
310 local_idx,
311 data_type,
312 nodata,
313 )?;
314 values.push(value);
315 } else {
316 values.push(None);
317 }
318 }
319
320 let result = pixel_func.apply(&values)?;
322
323 self.write_pixel_value(output, pixel_idx, result, data_type)?;
325 }
326
327 Ok(())
328 }
329
330 fn read_pixel_value(
332 &self,
333 buffer: &[u8],
334 pixel_idx: usize,
335 data_type: RasterDataType,
336 nodata: NoDataValue,
337 ) -> Result<Option<f64>> {
338 let bytes_per_pixel = data_type.size_bytes();
339 let offset = pixel_idx * bytes_per_pixel;
340
341 if offset + bytes_per_pixel > buffer.len() {
342 return Ok(None);
343 }
344
345 let value = match data_type {
346 RasterDataType::UInt8 => buffer[offset] as f64,
347 RasterDataType::Int8 => buffer[offset] as i8 as f64,
348 RasterDataType::UInt16 => {
349 let val = u16::from_le_bytes([buffer[offset], buffer[offset + 1]]);
350 val as f64
351 }
352 RasterDataType::Int16 => {
353 let val = i16::from_le_bytes([buffer[offset], buffer[offset + 1]]);
354 val as f64
355 }
356 RasterDataType::UInt32 => {
357 let val = u32::from_le_bytes([
358 buffer[offset],
359 buffer[offset + 1],
360 buffer[offset + 2],
361 buffer[offset + 3],
362 ]);
363 val as f64
364 }
365 RasterDataType::Int32 => {
366 let val = i32::from_le_bytes([
367 buffer[offset],
368 buffer[offset + 1],
369 buffer[offset + 2],
370 buffer[offset + 3],
371 ]);
372 val as f64
373 }
374 RasterDataType::Float32 => {
375 let val = f32::from_le_bytes([
376 buffer[offset],
377 buffer[offset + 1],
378 buffer[offset + 2],
379 buffer[offset + 3],
380 ]);
381 val as f64
382 }
383 RasterDataType::Float64 => f64::from_le_bytes([
384 buffer[offset],
385 buffer[offset + 1],
386 buffer[offset + 2],
387 buffer[offset + 3],
388 buffer[offset + 4],
389 buffer[offset + 5],
390 buffer[offset + 6],
391 buffer[offset + 7],
392 ]),
393 _ => return Err(VrtError::invalid_source("Unsupported data type")),
394 };
395
396 let is_nodata = match nodata {
398 NoDataValue::None => false,
399 NoDataValue::Integer(nd) => (value - nd as f64).abs() < f64::EPSILON,
400 NoDataValue::Float(nd) => (value - nd).abs() < f64::EPSILON,
401 };
402
403 if is_nodata { Ok(None) } else { Ok(Some(value)) }
404 }
405
406 fn write_pixel_value(
408 &self,
409 buffer: &mut [u8],
410 pixel_idx: usize,
411 value: Option<f64>,
412 data_type: RasterDataType,
413 ) -> Result<()> {
414 let bytes_per_pixel = data_type.size_bytes();
415 let offset = pixel_idx * bytes_per_pixel;
416
417 if offset + bytes_per_pixel > buffer.len() {
418 return Err(VrtError::invalid_window("Pixel offset out of bounds"));
419 }
420
421 let write_val = value.unwrap_or(0.0);
422
423 match data_type {
424 RasterDataType::UInt8 => {
425 buffer[offset] = write_val.clamp(0.0, 255.0) as u8;
426 }
427 RasterDataType::Int8 => {
428 buffer[offset] = write_val.clamp(-128.0, 127.0) as i8 as u8;
429 }
430 RasterDataType::UInt16 => {
431 let val = write_val.clamp(0.0, 65535.0) as u16;
432 buffer[offset..offset + 2].copy_from_slice(&val.to_le_bytes());
433 }
434 RasterDataType::Int16 => {
435 let val = write_val.clamp(-32768.0, 32767.0) as i16;
436 buffer[offset..offset + 2].copy_from_slice(&val.to_le_bytes());
437 }
438 RasterDataType::UInt32 => {
439 let val = write_val.clamp(0.0, u32::MAX as f64) as u32;
440 buffer[offset..offset + 4].copy_from_slice(&val.to_le_bytes());
441 }
442 RasterDataType::Int32 => {
443 let val = write_val.clamp(i32::MIN as f64, i32::MAX as f64) as i32;
444 buffer[offset..offset + 4].copy_from_slice(&val.to_le_bytes());
445 }
446 RasterDataType::Float32 => {
447 let val = write_val as f32;
448 buffer[offset..offset + 4].copy_from_slice(&val.to_le_bytes());
449 }
450 RasterDataType::Float64 => {
451 buffer[offset..offset + 8].copy_from_slice(&write_val.to_le_bytes());
452 }
453 _ => return Err(VrtError::invalid_source("Unsupported data type")),
454 }
455
456 Ok(())
457 }
458
459 fn open_source(&self, source: &VrtSource) -> Result<Arc<SourceDataset>> {
461 let path = if let Some(ref vrt_path) = self.dataset.vrt_path {
462 source.filename.resolve(vrt_path)?
463 } else {
464 source.filename.path.clone()
465 };
466
467 {
469 let mut cache = self
470 .source_cache
471 .lock()
472 .map_err(|_| VrtError::cache_error("Failed to lock source cache"))?;
473
474 if let Some(dataset) = cache.get(&path) {
475 return Ok(Arc::clone(dataset));
476 }
477 }
478
479 let dataset = SourceDataset::open(&path)?;
481 let arc_dataset = Arc::new(dataset);
482
483 {
485 let mut cache = self
486 .source_cache
487 .lock()
488 .map_err(|_| VrtError::cache_error("Failed to lock source cache"))?;
489 cache.put(path, Arc::clone(&arc_dataset));
490 }
491
492 Ok(arc_dataset)
493 }
494
495 pub fn clear_cache(&mut self) {
497 if let Ok(mut cache) = self.source_cache.lock() {
498 cache.clear();
499 }
500 }
501
502 pub fn cache_size(&self) -> usize {
504 self.source_cache
505 .lock()
506 .map(|cache| cache.len())
507 .unwrap_or(0)
508 }
509}
510
511pub struct SourceDataset {
513 geotiff: Option<GeoTiffReader<FileDataSource>>,
515}
516
517impl SourceDataset {
518 pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
523 match FileDataSource::open(path.as_ref()) {
525 Ok(source) => match GeoTiffReader::open(source) {
526 Ok(reader) => Ok(Self {
527 geotiff: Some(reader),
528 }),
529 Err(e) => Err(VrtError::source_error(
530 path.as_ref().display().to_string(),
531 format!("Failed to open as GeoTIFF: {}", e),
532 )),
533 },
534 Err(e) => Err(VrtError::source_error(
535 path.as_ref().display().to_string(),
536 format!("Failed to open file: {}", e),
537 )),
538 }
539 }
540
541 pub fn read_window(&self, band: usize, window: PixelRect) -> Result<RasterBuffer> {
546 if let Some(ref geotiff) = self.geotiff {
547 let full_band = geotiff.read_band(0, band - 1).map_err(|e| {
550 VrtError::source_error("source", format!("Failed to read band: {}", e))
551 })?;
552
553 let width = geotiff.width() as usize;
555 let height = geotiff.height() as usize;
556 let data_type = geotiff.data_type().unwrap_or(RasterDataType::UInt8);
557 let bytes_per_pixel = data_type.size_bytes();
558
559 let mut window_data = Vec::new();
560
561 for y in 0..window.y_size {
562 let src_y = (window.y_off + y) as usize;
563 if src_y >= height {
564 break;
565 }
566
567 let src_offset = (src_y * width + window.x_off as usize) * bytes_per_pixel;
568 let copy_width = window.x_size.min((width as u64) - window.x_off) as usize;
569 let copy_bytes = copy_width * bytes_per_pixel;
570
571 if src_offset + copy_bytes <= full_band.len() {
572 window_data.extend_from_slice(&full_band[src_offset..src_offset + copy_bytes]);
573 }
574 }
575
576 RasterBuffer::new(
577 window_data,
578 window.x_size,
579 window.y_size,
580 data_type,
581 geotiff.nodata(),
582 )
583 .map_err(|e| e.into())
584 } else {
585 Err(VrtError::source_error(
586 "unknown",
587 "Unsupported source format",
588 ))
589 }
590 }
591}
592
593#[cfg(test)]
594mod tests {
595 use super::*;
596 use crate::band::VrtBand;
597 use crate::source::VrtSource;
598
599 #[test]
600 fn test_vrt_reader_creation() {
601 let mut dataset = VrtDataset::new(512, 512);
602 let source = VrtSource::simple("/test.tif", 1);
603 let band = VrtBand::simple(1, RasterDataType::UInt8, source);
604 dataset.add_band(band);
605
606 let reader = VrtReader::from_dataset(dataset);
607 assert!(reader.is_ok());
608 let r = reader.expect("Should create reader");
609 assert_eq!(r.width(), 512);
610 assert_eq!(r.height(), 512);
611 assert_eq!(r.band_count(), 1);
612 }
613
614 #[test]
615 fn test_cache() {
616 let mut dataset = VrtDataset::new(512, 512);
617 let source = VrtSource::simple("/test.tif", 1);
618 let band = VrtBand::simple(1, RasterDataType::UInt8, source);
619 dataset.add_band(band);
620
621 let mut reader = VrtReader::from_dataset(dataset).expect("Should create reader");
622 assert_eq!(reader.cache_size(), 0);
623
624 reader.clear_cache();
625 assert_eq!(reader.cache_size(), 0);
626 }
627}