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 statistics: None,
117 }
118 }
119
120 pub fn read_window(&self, band: usize, window: PixelRect) -> Result<RasterBuffer> {
125 let band_idx = band - 1;
126 let vrt_band = self
127 .dataset
128 .get_band(band_idx)
129 .ok_or_else(|| VrtError::band_out_of_range(band, self.dataset.band_count()))?;
130
131 let contributing_sources: Vec<&VrtSource> = vrt_band
133 .sources
134 .iter()
135 .filter(|s| s.dst_rect().map(|r| r.intersects(&window)).unwrap_or(false))
136 .collect();
137
138 if contributing_sources.is_empty() {
139 return Err(VrtError::invalid_window(
140 "No sources contribute to this window",
141 ));
142 }
143
144 let data_size = (window.x_size * window.y_size) as usize * vrt_band.data_type.size_bytes();
146 let mut data = vec![0u8; data_size];
147
148 if let Some(ref pixel_func) = vrt_band.pixel_function {
150 self.apply_pixel_function(
151 &contributing_sources,
152 &window,
153 vrt_band.data_type,
154 vrt_band.nodata,
155 pixel_func,
156 &mut data,
157 )?;
158 } else {
159 for source in &contributing_sources {
161 self.read_source_contribution(source, &window, vrt_band.data_type, &mut data)?;
162 }
163 }
164
165 RasterBuffer::new(
166 data,
167 window.x_size,
168 window.y_size,
169 vrt_band.data_type,
170 vrt_band.nodata,
171 )
172 .map_err(|e| e.into())
173 }
174
175 pub fn read_band(&self, band: usize) -> Result<RasterBuffer> {
180 let window = PixelRect::new(0, 0, self.width(), self.height());
181 self.read_window(band, window)
182 }
183
184 fn read_source_contribution(
186 &self,
187 source: &VrtSource,
188 dst_window: &PixelRect,
189 data_type: RasterDataType,
190 output: &mut [u8],
191 ) -> Result<()> {
192 let source_dst_rect = source
193 .dst_rect()
194 .ok_or_else(|| VrtError::invalid_source("Source has no destination rectangle"))?;
195
196 let intersection = source_dst_rect
198 .intersect(dst_window)
199 .ok_or_else(|| VrtError::invalid_window("Source does not intersect window"))?;
200
201 let dataset = self.open_source(source)?;
203
204 let src_window = source
206 .window
207 .as_ref()
208 .ok_or_else(|| VrtError::invalid_source("Source has no window configuration"))?;
209
210 let src_x_off = src_window.src_rect.x_off + (intersection.x_off - source_dst_rect.x_off);
212 let src_y_off = src_window.src_rect.y_off + (intersection.y_off - source_dst_rect.y_off);
213
214 let src_rect = PixelRect::new(
215 src_x_off,
216 src_y_off,
217 intersection.x_size,
218 intersection.y_size,
219 );
220
221 let source_data = dataset.read_window(source.source_band, src_rect)?;
223
224 let dst_x_off = intersection.x_off - dst_window.x_off;
226 let dst_y_off = intersection.y_off - dst_window.y_off;
227
228 let params = crate::mosaic::CompositeParams::new(
229 dst_x_off,
230 dst_y_off,
231 intersection.x_size,
232 intersection.y_size,
233 dst_window.x_size,
234 data_type,
235 );
236 self.compositor
237 .composite(source_data.as_bytes(), output, ¶ms)?;
238
239 Ok(())
240 }
241
242 fn apply_pixel_function(
244 &self,
245 sources: &[&VrtSource],
246 window: &PixelRect,
247 data_type: RasterDataType,
248 nodata: NoDataValue,
249 pixel_func: &PixelFunction,
250 output: &mut [u8],
251 ) -> Result<()> {
252 let pixel_count = (window.x_size * window.y_size) as usize;
253 let _bytes_per_pixel = data_type.size_bytes();
254
255 let mut source_buffers = Vec::new();
257 for source in sources {
258 let source_dst_rect = source
259 .dst_rect()
260 .ok_or_else(|| VrtError::invalid_source("Source has no destination rectangle"))?;
261
262 let intersection = source_dst_rect
263 .intersect(window)
264 .ok_or_else(|| VrtError::invalid_window("Source does not intersect window"))?;
265
266 let dataset = self.open_source(source)?;
267
268 let src_window = source
269 .window
270 .as_ref()
271 .ok_or_else(|| VrtError::invalid_source("Source has no window configuration"))?;
272
273 let src_x_off =
274 src_window.src_rect.x_off + (intersection.x_off - source_dst_rect.x_off);
275 let src_y_off =
276 src_window.src_rect.y_off + (intersection.y_off - source_dst_rect.y_off);
277
278 let src_rect = PixelRect::new(
279 src_x_off,
280 src_y_off,
281 intersection.x_size,
282 intersection.y_size,
283 );
284
285 let source_data = dataset.read_window(source.source_band, src_rect)?;
286 source_buffers.push((source_data, intersection));
287 }
288
289 for pixel_idx in 0..pixel_count {
291 let y = pixel_idx as u64 / window.x_size;
292 let x = pixel_idx as u64 % window.x_size;
293 let global_x = window.x_off + x;
294 let global_y = window.y_off + y;
295
296 let mut values = Vec::new();
298 for (source_buffer, intersection) in &source_buffers {
299 if global_x >= intersection.x_off
300 && global_x < intersection.x_off + intersection.x_size
301 && global_y >= intersection.y_off
302 && global_y < intersection.y_off + intersection.y_size
303 {
304 let local_x = global_x - intersection.x_off;
305 let local_y = global_y - intersection.y_off;
306 let local_idx = (local_y * intersection.x_size + local_x) as usize;
307
308 let value = self.read_pixel_value(
310 source_buffer.as_bytes(),
311 local_idx,
312 data_type,
313 nodata,
314 )?;
315 values.push(value);
316 } else {
317 values.push(None);
318 }
319 }
320
321 let result = pixel_func.apply(&values)?;
323
324 self.write_pixel_value(output, pixel_idx, result, data_type)?;
326 }
327
328 Ok(())
329 }
330
331 fn read_pixel_value(
333 &self,
334 buffer: &[u8],
335 pixel_idx: usize,
336 data_type: RasterDataType,
337 nodata: NoDataValue,
338 ) -> Result<Option<f64>> {
339 let bytes_per_pixel = data_type.size_bytes();
340 let offset = pixel_idx * bytes_per_pixel;
341
342 if offset + bytes_per_pixel > buffer.len() {
343 return Ok(None);
344 }
345
346 let value = match data_type {
347 RasterDataType::UInt8 => buffer[offset] as f64,
348 RasterDataType::Int8 => buffer[offset] as i8 as f64,
349 RasterDataType::UInt16 => {
350 let val = u16::from_le_bytes([buffer[offset], buffer[offset + 1]]);
351 val as f64
352 }
353 RasterDataType::Int16 => {
354 let val = i16::from_le_bytes([buffer[offset], buffer[offset + 1]]);
355 val as f64
356 }
357 RasterDataType::UInt32 => {
358 let val = u32::from_le_bytes([
359 buffer[offset],
360 buffer[offset + 1],
361 buffer[offset + 2],
362 buffer[offset + 3],
363 ]);
364 val as f64
365 }
366 RasterDataType::Int32 => {
367 let val = i32::from_le_bytes([
368 buffer[offset],
369 buffer[offset + 1],
370 buffer[offset + 2],
371 buffer[offset + 3],
372 ]);
373 val as f64
374 }
375 RasterDataType::Float32 => {
376 let val = f32::from_le_bytes([
377 buffer[offset],
378 buffer[offset + 1],
379 buffer[offset + 2],
380 buffer[offset + 3],
381 ]);
382 val as f64
383 }
384 RasterDataType::Float64 => f64::from_le_bytes([
385 buffer[offset],
386 buffer[offset + 1],
387 buffer[offset + 2],
388 buffer[offset + 3],
389 buffer[offset + 4],
390 buffer[offset + 5],
391 buffer[offset + 6],
392 buffer[offset + 7],
393 ]),
394 _ => return Err(VrtError::invalid_source("Unsupported data type")),
395 };
396
397 let is_nodata = match nodata {
399 NoDataValue::None => false,
400 NoDataValue::Integer(nd) => (value - nd as f64).abs() < f64::EPSILON,
401 NoDataValue::Float(nd) => (value - nd).abs() < f64::EPSILON,
402 };
403
404 if is_nodata { Ok(None) } else { Ok(Some(value)) }
405 }
406
407 fn write_pixel_value(
409 &self,
410 buffer: &mut [u8],
411 pixel_idx: usize,
412 value: Option<f64>,
413 data_type: RasterDataType,
414 ) -> Result<()> {
415 let bytes_per_pixel = data_type.size_bytes();
416 let offset = pixel_idx * bytes_per_pixel;
417
418 if offset + bytes_per_pixel > buffer.len() {
419 return Err(VrtError::invalid_window("Pixel offset out of bounds"));
420 }
421
422 let write_val = value.unwrap_or(0.0);
423
424 match data_type {
425 RasterDataType::UInt8 => {
426 buffer[offset] = write_val.clamp(0.0, 255.0) as u8;
427 }
428 RasterDataType::Int8 => {
429 buffer[offset] = write_val.clamp(-128.0, 127.0) as i8 as u8;
430 }
431 RasterDataType::UInt16 => {
432 let val = write_val.clamp(0.0, 65535.0) as u16;
433 buffer[offset..offset + 2].copy_from_slice(&val.to_le_bytes());
434 }
435 RasterDataType::Int16 => {
436 let val = write_val.clamp(-32768.0, 32767.0) as i16;
437 buffer[offset..offset + 2].copy_from_slice(&val.to_le_bytes());
438 }
439 RasterDataType::UInt32 => {
440 let val = write_val.clamp(0.0, u32::MAX as f64) as u32;
441 buffer[offset..offset + 4].copy_from_slice(&val.to_le_bytes());
442 }
443 RasterDataType::Int32 => {
444 let val = write_val.clamp(i32::MIN as f64, i32::MAX as f64) as i32;
445 buffer[offset..offset + 4].copy_from_slice(&val.to_le_bytes());
446 }
447 RasterDataType::Float32 => {
448 let val = write_val as f32;
449 buffer[offset..offset + 4].copy_from_slice(&val.to_le_bytes());
450 }
451 RasterDataType::Float64 => {
452 buffer[offset..offset + 8].copy_from_slice(&write_val.to_le_bytes());
453 }
454 _ => return Err(VrtError::invalid_source("Unsupported data type")),
455 }
456
457 Ok(())
458 }
459
460 fn open_source(&self, source: &VrtSource) -> Result<Arc<SourceDataset>> {
462 let path = if let Some(ref vrt_path) = self.dataset.vrt_path {
463 source.filename.resolve(vrt_path)?
464 } else {
465 source.filename.path.clone()
466 };
467
468 {
470 let mut cache = self
471 .source_cache
472 .lock()
473 .map_err(|_| VrtError::cache_error("Failed to lock source cache"))?;
474
475 if let Some(dataset) = cache.get(&path) {
476 return Ok(Arc::clone(dataset));
477 }
478 }
479
480 let dataset = SourceDataset::open(&path)?;
482 let arc_dataset = Arc::new(dataset);
483
484 {
486 let mut cache = self
487 .source_cache
488 .lock()
489 .map_err(|_| VrtError::cache_error("Failed to lock source cache"))?;
490 cache.put(path, Arc::clone(&arc_dataset));
491 }
492
493 Ok(arc_dataset)
494 }
495
496 pub fn clear_cache(&mut self) {
498 if let Ok(mut cache) = self.source_cache.lock() {
499 cache.clear();
500 }
501 }
502
503 pub fn cache_size(&self) -> usize {
505 self.source_cache
506 .lock()
507 .map(|cache| cache.len())
508 .unwrap_or(0)
509 }
510}
511
512pub struct SourceDataset {
514 geotiff: Option<GeoTiffReader<FileDataSource>>,
516}
517
518impl SourceDataset {
519 pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
524 match FileDataSource::open(path.as_ref()) {
526 Ok(source) => match GeoTiffReader::open(source) {
527 Ok(reader) => Ok(Self {
528 geotiff: Some(reader),
529 }),
530 Err(e) => Err(VrtError::source_error(
531 path.as_ref().display().to_string(),
532 format!("Failed to open as GeoTIFF: {}", e),
533 )),
534 },
535 Err(e) => Err(VrtError::source_error(
536 path.as_ref().display().to_string(),
537 format!("Failed to open file: {}", e),
538 )),
539 }
540 }
541
542 pub fn read_window(&self, band: usize, window: PixelRect) -> Result<RasterBuffer> {
547 if let Some(ref geotiff) = self.geotiff {
548 let full_band = geotiff.read_band(0, band - 1).map_err(|e| {
551 VrtError::source_error("source", format!("Failed to read band: {}", e))
552 })?;
553
554 let width = geotiff.width() as usize;
556 let height = geotiff.height() as usize;
557 let data_type = geotiff.data_type().unwrap_or(RasterDataType::UInt8);
558 let bytes_per_pixel = data_type.size_bytes();
559
560 let mut window_data = Vec::new();
561
562 for y in 0..window.y_size {
563 let src_y = (window.y_off + y) as usize;
564 if src_y >= height {
565 break;
566 }
567
568 let src_offset = (src_y * width + window.x_off as usize) * bytes_per_pixel;
569 let copy_width = window.x_size.min((width as u64) - window.x_off) as usize;
570 let copy_bytes = copy_width * bytes_per_pixel;
571
572 if src_offset + copy_bytes <= full_band.len() {
573 window_data.extend_from_slice(&full_band[src_offset..src_offset + copy_bytes]);
574 }
575 }
576
577 RasterBuffer::new(
578 window_data,
579 window.x_size,
580 window.y_size,
581 data_type,
582 geotiff.nodata(),
583 )
584 .map_err(|e| e.into())
585 } else {
586 Err(VrtError::source_error(
587 "unknown",
588 "Unsupported source format",
589 ))
590 }
591 }
592}
593
594#[cfg(test)]
595mod tests {
596 use super::*;
597 use crate::band::VrtBand;
598 use crate::source::VrtSource;
599
600 #[test]
601 fn test_vrt_reader_creation() {
602 let mut dataset = VrtDataset::new(512, 512);
603 let source = VrtSource::simple("/test.tif", 1);
604 let band = VrtBand::simple(1, RasterDataType::UInt8, source);
605 dataset.add_band(band);
606
607 let reader = VrtReader::from_dataset(dataset);
608 assert!(reader.is_ok());
609 let r = reader.expect("Should create reader");
610 assert_eq!(r.width(), 512);
611 assert_eq!(r.height(), 512);
612 assert_eq!(r.band_count(), 1);
613 }
614
615 #[test]
616 fn test_cache() {
617 let mut dataset = VrtDataset::new(512, 512);
618 let source = VrtSource::simple("/test.tif", 1);
619 let band = VrtBand::simple(1, RasterDataType::UInt8, source);
620 dataset.add_band(band);
621
622 let mut reader = VrtReader::from_dataset(dataset).expect("Should create reader");
623 assert_eq!(reader.cache_size(), 0);
624
625 reader.clear_cache();
626 assert_eq!(reader.cache_size(), 0);
627 }
628}