Skip to main content

oxigdal_vrt/
xml.rs

1//! VRT XML format parser and writer
2
3use crate::band::{PixelFunction, VrtBand};
4use crate::dataset::{VrtDataset, VrtSubclass};
5use crate::error::{Result, VrtError};
6use crate::source::{PixelRect, SourceFilename, SourceWindow, VrtSource};
7use oxigdal_core::types::{ColorInterpretation, GeoTransform, NoDataValue, RasterDataType};
8use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
9use quick_xml::{Reader, Writer};
10use std::io::{BufRead, Write};
11use std::path::Path;
12
13/// VRT XML parser
14pub struct VrtXmlParser;
15
16impl VrtXmlParser {
17    /// Parses VRT from XML string
18    ///
19    /// # Errors
20    /// Returns an error if parsing fails
21    pub fn parse(xml: &str) -> Result<VrtDataset> {
22        let mut reader = Reader::from_str(xml);
23        reader.config_mut().trim_text(true);
24
25        let mut dataset = None;
26        let mut buf = Vec::new();
27
28        loop {
29            match reader.read_event_into(&mut buf) {
30                Ok(Event::Start(ref e)) if e.name().as_ref() == b"VRTDataset" => {
31                    dataset = Some(Self::parse_dataset(&mut reader, e)?);
32                }
33                Ok(Event::Eof) => break,
34                Err(e) => {
35                    return Err(VrtError::xml_parse(format!(
36                        "XML parsing error at position {}: {}",
37                        reader.buffer_position(),
38                        e
39                    )));
40                }
41                _ => {}
42            }
43            buf.clear();
44        }
45
46        dataset.ok_or_else(|| VrtError::xml_parse("No VRTDataset element found"))
47    }
48
49    /// Parses VRT from a file
50    ///
51    /// # Errors
52    /// Returns an error if file reading or parsing fails
53    pub fn parse_file<P: AsRef<Path>>(path: P) -> Result<VrtDataset> {
54        let xml = std::fs::read_to_string(&path)?;
55        let mut dataset = Self::parse(&xml)?;
56        dataset.vrt_path = Some(path.as_ref().to_path_buf());
57        Ok(dataset)
58    }
59
60    fn parse_dataset<R: BufRead>(reader: &mut Reader<R>, start: &BytesStart) -> Result<VrtDataset> {
61        let mut raster_x_size = 0u64;
62        let mut raster_y_size = 0u64;
63        let mut subclass = None;
64
65        // Parse attributes
66        for attr in start.attributes() {
67            let attr = attr.map_err(|e| VrtError::xml_parse(format!("Attribute error: {}", e)))?;
68            match attr.key.as_ref() {
69                b"rasterXSize" => {
70                    raster_x_size = Self::parse_u64(&attr.value)?;
71                }
72                b"rasterYSize" => {
73                    raster_y_size = Self::parse_u64(&attr.value)?;
74                }
75                b"subClass" => {
76                    let s = Self::parse_string(&attr.value)?;
77                    subclass = Some(match s.as_str() {
78                        "VRTWarpedDataset" => VrtSubclass::Warped,
79                        "VRTPansharpenedDataset" => VrtSubclass::Pansharpened,
80                        "VRTProcessedDataset" => VrtSubclass::Processed,
81                        _ => VrtSubclass::Standard,
82                    });
83                }
84                _ => {}
85            }
86        }
87
88        let mut dataset = VrtDataset::new(raster_x_size, raster_y_size);
89        if let Some(sc) = subclass {
90            dataset = dataset.with_subclass(sc);
91        }
92
93        let mut buf = Vec::new();
94
95        loop {
96            match reader.read_event_into(&mut buf) {
97                Ok(Event::Start(ref e)) => match e.name().as_ref() {
98                    b"SRS" => {
99                        dataset.srs = Some(Self::parse_text_element(reader, "SRS")?);
100                    }
101                    b"GeoTransform" => {
102                        let text = Self::parse_text_element(reader, "GeoTransform")?;
103                        dataset.geo_transform = Some(Self::parse_geotransform(&text)?);
104                    }
105                    b"VRTRasterBand" => {
106                        let band = Self::parse_band(reader, e)?;
107                        dataset.add_band(band);
108                    }
109                    b"BlockXSize" => {
110                        let text = Self::parse_text_element(reader, "BlockXSize")?;
111                        let x_size = text.parse::<u32>().map_err(|e| {
112                            VrtError::xml_parse(format!("Invalid BlockXSize: {}", e))
113                        })?;
114                        let (_, y_size) = dataset.block_size.unwrap_or((0, 0));
115                        dataset.block_size = Some((x_size, y_size));
116                    }
117                    b"BlockYSize" => {
118                        let text = Self::parse_text_element(reader, "BlockYSize")?;
119                        let y_size = text.parse::<u32>().map_err(|e| {
120                            VrtError::xml_parse(format!("Invalid BlockYSize: {}", e))
121                        })?;
122                        let (x_size, _) = dataset.block_size.unwrap_or((0, 0));
123                        dataset.block_size = Some((x_size, y_size));
124                    }
125                    _ => {
126                        Self::skip_element(reader)?;
127                    }
128                },
129                Ok(Event::End(ref e)) if e.name().as_ref() == b"VRTDataset" => break,
130                Ok(Event::Eof) => {
131                    return Err(VrtError::xml_parse("Unexpected EOF in VRTDataset"));
132                }
133                Err(e) => {
134                    return Err(VrtError::xml_parse(format!("XML error: {}", e)));
135                }
136                _ => {}
137            }
138            buf.clear();
139        }
140
141        Ok(dataset)
142    }
143
144    fn parse_band<R: BufRead>(reader: &mut Reader<R>, start: &BytesStart) -> Result<VrtBand> {
145        let mut band_num = 0usize;
146        let mut data_type = RasterDataType::UInt8;
147
148        // Parse attributes
149        for attr in start.attributes() {
150            let attr = attr.map_err(|e| VrtError::xml_parse(format!("Attribute error: {}", e)))?;
151            match attr.key.as_ref() {
152                b"band" => {
153                    band_num = Self::parse_usize(&attr.value)?;
154                }
155                b"dataType" => {
156                    let s = Self::parse_string(&attr.value)?;
157                    data_type = Self::parse_data_type(&s)?;
158                }
159                _ => {}
160            }
161        }
162
163        let mut band = VrtBand::new(band_num, data_type);
164        let mut buf = Vec::new();
165
166        loop {
167            match reader.read_event_into(&mut buf) {
168                Ok(Event::Start(ref e)) => match e.name().as_ref() {
169                    b"NoDataValue" => {
170                        let text = Self::parse_text_element(reader, "NoDataValue")?;
171                        band.nodata = Self::parse_nodata(&text)?;
172                    }
173                    b"ColorInterp" => {
174                        let text = Self::parse_text_element(reader, "ColorInterp")?;
175                        band.color_interp = Self::parse_color_interp(&text);
176                    }
177                    b"SimpleSource" | b"ComplexSource" => {
178                        let source = Self::parse_source(reader, e)?;
179                        band.add_source(source);
180                    }
181                    b"Offset" => {
182                        let text = Self::parse_text_element(reader, "Offset")?;
183                        band.offset = text.parse::<f64>().ok();
184                    }
185                    b"Scale" => {
186                        let text = Self::parse_text_element(reader, "Scale")?;
187                        band.scale = text.parse::<f64>().ok();
188                    }
189                    b"PixelFunctionType" => {
190                        let text = Self::parse_text_element(reader, "PixelFunctionType")?;
191                        band.pixel_function = Some(Self::parse_pixel_function(&text));
192                    }
193                    _ => {
194                        Self::skip_element(reader)?;
195                    }
196                },
197                Ok(Event::End(ref e)) if e.name().as_ref() == b"VRTRasterBand" => break,
198                Ok(Event::Eof) => {
199                    return Err(VrtError::xml_parse("Unexpected EOF in VRTRasterBand"));
200                }
201                Err(e) => {
202                    return Err(VrtError::xml_parse(format!("XML error: {}", e)));
203                }
204                _ => {}
205            }
206            buf.clear();
207        }
208
209        Ok(band)
210    }
211
212    fn parse_source<R: BufRead>(reader: &mut Reader<R>, start: &BytesStart) -> Result<VrtSource> {
213        let mut filename = None;
214        let mut source_band = 1usize;
215        let mut src_rect = None;
216        let mut dst_rect = None;
217        let mut buf = Vec::new();
218        let element_name = start.name().as_ref().to_vec();
219
220        loop {
221            match reader.read_event_into(&mut buf) {
222                Ok(Event::Start(ref e)) => match e.name().as_ref() {
223                    b"SourceFilename" => {
224                        let text = Self::parse_text_element(reader, "SourceFilename")?;
225                        filename = Some(SourceFilename::absolute(text));
226                    }
227                    b"SourceBand" => {
228                        let text = Self::parse_text_element(reader, "SourceBand")?;
229                        source_band = text.parse::<usize>().map_err(|e| {
230                            VrtError::xml_parse(format!("Invalid SourceBand: {}", e))
231                        })?;
232                    }
233                    b"SrcRect" => {
234                        src_rect = Some(Self::parse_rect_from_start(reader, e)?);
235                    }
236                    b"DstRect" => {
237                        dst_rect = Some(Self::parse_rect_from_start(reader, e)?);
238                    }
239                    _ => {
240                        Self::skip_element(reader)?;
241                    }
242                },
243                // Handle self-closing elements like <SrcRect ... />
244                Ok(Event::Empty(ref e)) => match e.name().as_ref() {
245                    b"SrcRect" => {
246                        src_rect = Some(Self::parse_rect_from_empty(e)?);
247                    }
248                    b"DstRect" => {
249                        dst_rect = Some(Self::parse_rect_from_empty(e)?);
250                    }
251                    _ => {}
252                },
253                Ok(Event::End(ref e)) if e.name().as_ref() == element_name => break,
254                Ok(Event::Eof) => {
255                    return Err(VrtError::xml_parse("Unexpected EOF in source element"));
256                }
257                Err(e) => {
258                    return Err(VrtError::xml_parse(format!("XML error: {}", e)));
259                }
260                _ => {}
261            }
262            buf.clear();
263        }
264
265        let filename = filename.ok_or_else(|| VrtError::xml_parse("Missing SourceFilename"))?;
266        let mut source = VrtSource::new(filename, source_band);
267
268        if let (Some(src), Some(dst)) = (src_rect, dst_rect) {
269            source = source.with_window(SourceWindow::new(src, dst));
270        }
271
272        Ok(source)
273    }
274
275    /// Parses PixelRect from attributes only (for self-closing tags like `<SrcRect ... />`)
276    fn parse_rect_from_empty(start: &BytesStart) -> Result<PixelRect> {
277        let mut x_off = 0u64;
278        let mut y_off = 0u64;
279        let mut x_size = 0u64;
280        let mut y_size = 0u64;
281
282        for attr in start.attributes() {
283            let attr = attr.map_err(|e| VrtError::xml_parse(format!("Attribute error: {}", e)))?;
284            match attr.key.as_ref() {
285                b"xOff" => x_off = Self::parse_u64(&attr.value)?,
286                b"yOff" => y_off = Self::parse_u64(&attr.value)?,
287                b"xSize" => x_size = Self::parse_u64(&attr.value)?,
288                b"ySize" => y_size = Self::parse_u64(&attr.value)?,
289                _ => {}
290            }
291        }
292
293        Ok(PixelRect::new(x_off, y_off, x_size, y_size))
294    }
295
296    /// Parses PixelRect from a Start event (needs to consume End event)
297    fn parse_rect_from_start<R: BufRead>(
298        reader: &mut Reader<R>,
299        start: &BytesStart,
300    ) -> Result<PixelRect> {
301        let rect = Self::parse_rect_from_empty(start)?;
302        Self::skip_element(reader)?;
303        Ok(rect)
304    }
305
306    fn parse_geotransform(text: &str) -> Result<GeoTransform> {
307        let parts: Vec<&str> = text.split(',').map(|s| s.trim()).collect();
308        if parts.len() != 6 {
309            return Err(VrtError::xml_parse("GeoTransform must have 6 values"));
310        }
311
312        let values: Result<Vec<f64>> = parts
313            .iter()
314            .map(|s| {
315                s.parse::<f64>()
316                    .map_err(|e| VrtError::xml_parse(format!("Invalid GeoTransform value: {}", e)))
317            })
318            .collect();
319
320        let v = values?;
321        Ok(GeoTransform {
322            origin_x: v[0],
323            pixel_width: v[1],
324            row_rotation: v[2],
325            origin_y: v[3],
326            col_rotation: v[4],
327            pixel_height: v[5],
328        })
329    }
330
331    fn parse_data_type(s: &str) -> Result<RasterDataType> {
332        match s {
333            "Byte" => Ok(RasterDataType::UInt8),
334            "UInt16" => Ok(RasterDataType::UInt16),
335            "Int16" => Ok(RasterDataType::Int16),
336            "UInt32" => Ok(RasterDataType::UInt32),
337            "Int32" => Ok(RasterDataType::Int32),
338            "Float32" => Ok(RasterDataType::Float32),
339            "Float64" => Ok(RasterDataType::Float64),
340            _ => Err(VrtError::xml_parse(format!("Unknown data type: {}", s))),
341        }
342    }
343
344    fn parse_nodata(s: &str) -> Result<NoDataValue> {
345        if let Ok(val) = s.parse::<f64>() {
346            Ok(NoDataValue::Float(val))
347        } else {
348            Ok(NoDataValue::None)
349        }
350    }
351
352    fn parse_color_interp(s: &str) -> ColorInterpretation {
353        match s {
354            "Red" => ColorInterpretation::Red,
355            "Green" => ColorInterpretation::Green,
356            "Blue" => ColorInterpretation::Blue,
357            "Alpha" => ColorInterpretation::Alpha,
358            "Gray" => ColorInterpretation::Gray,
359            "Palette" => ColorInterpretation::PaletteIndex,
360            _ => ColorInterpretation::Undefined,
361        }
362    }
363
364    fn parse_pixel_function(s: &str) -> PixelFunction {
365        match s {
366            "average" | "Average" => PixelFunction::Average,
367            "min" | "Min" => PixelFunction::Min,
368            "max" | "Max" => PixelFunction::Max,
369            "sum" | "Sum" => PixelFunction::Sum,
370            _ => PixelFunction::Custom {
371                name: s.to_string(),
372            },
373        }
374    }
375
376    fn parse_text_element<R: BufRead>(reader: &mut Reader<R>, name: &str) -> Result<String> {
377        let mut text = String::new();
378        let mut buf = Vec::new();
379
380        loop {
381            match reader.read_event_into(&mut buf) {
382                Ok(Event::Text(e)) => {
383                    text.push_str(
384                        &e.decode().map_err(|e| {
385                            VrtError::xml_parse(format!("Text decode error: {}", e))
386                        })?,
387                    );
388                }
389                Ok(Event::End(_)) => break,
390                Ok(Event::Eof) => {
391                    return Err(VrtError::xml_parse(format!("Unexpected EOF in {}", name)));
392                }
393                Err(e) => {
394                    return Err(VrtError::xml_parse(format!("XML error: {}", e)));
395                }
396                _ => {}
397            }
398            buf.clear();
399        }
400
401        Ok(text.trim().to_string())
402    }
403
404    fn skip_element<R: BufRead>(reader: &mut Reader<R>) -> Result<()> {
405        let mut depth = 1;
406        let mut buf = Vec::new();
407
408        while depth > 0 {
409            match reader.read_event_into(&mut buf) {
410                Ok(Event::Start(_)) => depth += 1,
411                Ok(Event::End(_)) => depth -= 1,
412                Ok(Event::Eof) => {
413                    return Err(VrtError::xml_parse("Unexpected EOF while skipping element"));
414                }
415                Err(e) => {
416                    return Err(VrtError::xml_parse(format!("XML error: {}", e)));
417                }
418                _ => {}
419            }
420            buf.clear();
421        }
422
423        Ok(())
424    }
425
426    fn parse_string(bytes: &[u8]) -> Result<String> {
427        String::from_utf8(bytes.to_vec())
428            .map_err(|e| VrtError::xml_parse(format!("UTF-8 error: {}", e)))
429    }
430
431    fn parse_u64(bytes: &[u8]) -> Result<u64> {
432        let s = Self::parse_string(bytes)?;
433        s.parse::<u64>()
434            .map_err(|e| VrtError::xml_parse(format!("Invalid u64: {}", e)))
435    }
436
437    fn parse_usize(bytes: &[u8]) -> Result<usize> {
438        let s = Self::parse_string(bytes)?;
439        s.parse::<usize>()
440            .map_err(|e| VrtError::xml_parse(format!("Invalid usize: {}", e)))
441    }
442}
443
444/// VRT XML writer
445pub struct VrtXmlWriter;
446
447impl VrtXmlWriter {
448    /// Writes VRT dataset to XML string
449    ///
450    /// # Errors
451    /// Returns an error if writing fails
452    pub fn write(dataset: &VrtDataset) -> Result<String> {
453        let mut buffer = Vec::new();
454        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 2);
455
456        Self::write_dataset(&mut writer, dataset)?;
457
458        String::from_utf8(buffer).map_err(|e| VrtError::xml_parse(format!("UTF-8 error: {}", e)))
459    }
460
461    /// Writes VRT dataset to a file
462    ///
463    /// # Errors
464    /// Returns an error if file writing fails
465    pub fn write_file<P: AsRef<Path>>(dataset: &VrtDataset, path: P) -> Result<()> {
466        let xml = Self::write(dataset)?;
467        std::fs::write(path, xml)?;
468        Ok(())
469    }
470
471    fn write_dataset<W: Write>(writer: &mut Writer<W>, dataset: &VrtDataset) -> Result<()> {
472        let mut elem = BytesStart::new("VRTDataset");
473        elem.push_attribute(("rasterXSize", dataset.raster_x_size.to_string().as_str()));
474        elem.push_attribute(("rasterYSize", dataset.raster_y_size.to_string().as_str()));
475
476        if let Some(ref subclass) = dataset.subclass {
477            let subclass_str = match subclass {
478                VrtSubclass::Warped => "VRTWarpedDataset",
479                VrtSubclass::Pansharpened => "VRTPansharpenedDataset",
480                VrtSubclass::Processed => "VRTProcessedDataset",
481                VrtSubclass::Standard => "VRTDataset",
482            };
483            elem.push_attribute(("subClass", subclass_str));
484        }
485
486        writer
487            .write_event(Event::Start(elem))
488            .map_err(|e| VrtError::xml_parse(format!("Write error: {}", e)))?;
489
490        if let Some(ref srs) = dataset.srs {
491            Self::write_text_element(writer, "SRS", srs)?;
492        }
493
494        if let Some(ref gt) = dataset.geo_transform {
495            let text = format!(
496                "{}, {}, {}, {}, {}, {}",
497                gt.origin_x,
498                gt.pixel_width,
499                gt.row_rotation,
500                gt.origin_y,
501                gt.col_rotation,
502                gt.pixel_height
503            );
504            Self::write_text_element(writer, "GeoTransform", &text)?;
505        }
506
507        for band in &dataset.bands {
508            Self::write_band(writer, band)?;
509        }
510
511        writer
512            .write_event(Event::End(BytesEnd::new("VRTDataset")))
513            .map_err(|e| VrtError::xml_parse(format!("Write error: {}", e)))?;
514
515        Ok(())
516    }
517
518    fn write_band<W: Write>(writer: &mut Writer<W>, band: &VrtBand) -> Result<()> {
519        let mut elem = BytesStart::new("VRTRasterBand");
520        elem.push_attribute(("band", band.band.to_string().as_str()));
521        elem.push_attribute(("dataType", Self::data_type_name(band.data_type)));
522
523        writer
524            .write_event(Event::Start(elem))
525            .map_err(|e| VrtError::xml_parse(format!("Write error: {}", e)))?;
526
527        if let Some(nodata) = Self::nodata_value(band.nodata) {
528            Self::write_text_element(writer, "NoDataValue", &nodata)?;
529        }
530
531        if band.color_interp != ColorInterpretation::Undefined {
532            Self::write_text_element(
533                writer,
534                "ColorInterp",
535                Self::color_interp_name(band.color_interp),
536            )?;
537        }
538
539        for source in &band.sources {
540            Self::write_source(writer, source)?;
541        }
542
543        if let Some(offset) = band.offset {
544            Self::write_text_element(writer, "Offset", &offset.to_string())?;
545        }
546
547        if let Some(scale) = band.scale {
548            Self::write_text_element(writer, "Scale", &scale.to_string())?;
549        }
550
551        writer
552            .write_event(Event::End(BytesEnd::new("VRTRasterBand")))
553            .map_err(|e| VrtError::xml_parse(format!("Write error: {}", e)))?;
554
555        Ok(())
556    }
557
558    fn write_source<W: Write>(writer: &mut Writer<W>, source: &VrtSource) -> Result<()> {
559        let elem = BytesStart::new("SimpleSource");
560        writer
561            .write_event(Event::Start(elem))
562            .map_err(|e| VrtError::xml_parse(format!("Write error: {}", e)))?;
563
564        Self::write_text_element(
565            writer,
566            "SourceFilename",
567            &source.filename.path.display().to_string(),
568        )?;
569        Self::write_text_element(writer, "SourceBand", &source.source_band.to_string())?;
570
571        if let Some(ref window) = source.window {
572            Self::write_rect(writer, "SrcRect", &window.src_rect)?;
573            Self::write_rect(writer, "DstRect", &window.dst_rect)?;
574        }
575
576        writer
577            .write_event(Event::End(BytesEnd::new("SimpleSource")))
578            .map_err(|e| VrtError::xml_parse(format!("Write error: {}", e)))?;
579
580        Ok(())
581    }
582
583    fn write_rect<W: Write>(writer: &mut Writer<W>, name: &str, rect: &PixelRect) -> Result<()> {
584        let mut elem = BytesStart::new(name);
585        elem.push_attribute(("xOff", rect.x_off.to_string().as_str()));
586        elem.push_attribute(("yOff", rect.y_off.to_string().as_str()));
587        elem.push_attribute(("xSize", rect.x_size.to_string().as_str()));
588        elem.push_attribute(("ySize", rect.y_size.to_string().as_str()));
589
590        writer
591            .write_event(Event::Empty(elem))
592            .map_err(|e| VrtError::xml_parse(format!("Write error: {}", e)))?;
593
594        Ok(())
595    }
596
597    fn write_text_element<W: Write>(writer: &mut Writer<W>, name: &str, text: &str) -> Result<()> {
598        writer
599            .write_event(Event::Start(BytesStart::new(name)))
600            .map_err(|e| VrtError::xml_parse(format!("Write error: {}", e)))?;
601        writer
602            .write_event(Event::Text(BytesText::new(text)))
603            .map_err(|e| VrtError::xml_parse(format!("Write error: {}", e)))?;
604        writer
605            .write_event(Event::End(BytesEnd::new(name)))
606            .map_err(|e| VrtError::xml_parse(format!("Write error: {}", e)))?;
607        Ok(())
608    }
609
610    fn data_type_name(dt: RasterDataType) -> &'static str {
611        match dt {
612            RasterDataType::UInt8 => "Byte",
613            RasterDataType::UInt16 => "UInt16",
614            RasterDataType::Int16 => "Int16",
615            RasterDataType::UInt32 => "UInt32",
616            RasterDataType::Int32 => "Int32",
617            RasterDataType::Float32 => "Float32",
618            RasterDataType::Float64 => "Float64",
619            _ => "Byte",
620        }
621    }
622
623    fn nodata_value(nd: NoDataValue) -> Option<String> {
624        match nd {
625            NoDataValue::None => None,
626            NoDataValue::Integer(v) => Some(v.to_string()),
627            NoDataValue::Float(v) => Some(v.to_string()),
628        }
629    }
630
631    fn color_interp_name(ci: ColorInterpretation) -> &'static str {
632        match ci {
633            ColorInterpretation::Red => "Red",
634            ColorInterpretation::Green => "Green",
635            ColorInterpretation::Blue => "Blue",
636            ColorInterpretation::Alpha => "Alpha",
637            ColorInterpretation::Gray => "Gray",
638            ColorInterpretation::PaletteIndex => "Palette",
639            ColorInterpretation::Undefined => "Undefined",
640            _ => "Undefined",
641        }
642    }
643}
644
645#[cfg(test)]
646mod tests {
647    use super::*;
648
649    #[test]
650    fn test_parse_simple_vrt() {
651        let xml = r#"
652<VRTDataset rasterXSize="512" rasterYSize="512">
653  <SRS>EPSG:4326</SRS>
654  <GeoTransform>0.0, 1.0, 0.0, 0.0, 0.0, -1.0</GeoTransform>
655  <VRTRasterBand band="1" dataType="Byte">
656    <NoDataValue>0</NoDataValue>
657    <SimpleSource>
658      <SourceFilename>/path/to/file.tif</SourceFilename>
659      <SourceBand>1</SourceBand>
660    </SimpleSource>
661  </VRTRasterBand>
662</VRTDataset>
663"#;
664
665        let dataset = VrtXmlParser::parse(xml);
666        assert!(dataset.is_ok());
667        let ds = dataset.expect("Should parse");
668        assert_eq!(ds.raster_x_size, 512);
669        assert_eq!(ds.raster_y_size, 512);
670        assert_eq!(ds.band_count(), 1);
671    }
672
673    #[test]
674    fn test_write_simple_vrt() {
675        let mut dataset = VrtDataset::new(512, 512);
676        let source = VrtSource::simple("/test.tif", 1);
677        let band = VrtBand::simple(1, RasterDataType::UInt8, source);
678        dataset.add_band(band);
679
680        let xml = VrtXmlWriter::write(&dataset);
681        assert!(xml.is_ok());
682        let xml_str = xml.expect("Should write");
683        assert!(xml_str.contains("VRTDataset"));
684        assert!(xml_str.contains("rasterXSize=\"512\""));
685        assert!(xml_str.contains("VRTRasterBand"));
686    }
687
688    #[test]
689    fn test_roundtrip() {
690        let mut dataset = VrtDataset::new(1024, 768);
691        dataset = dataset.with_srs("EPSG:4326");
692        let source = VrtSource::simple("/test.tif", 1);
693        let band = VrtBand::simple(1, RasterDataType::UInt8, source);
694        dataset.add_band(band);
695
696        let xml = VrtXmlWriter::write(&dataset).expect("Should write");
697        let parsed = VrtXmlParser::parse(&xml).expect("Should parse");
698
699        assert_eq!(parsed.raster_x_size, 1024);
700        assert_eq!(parsed.raster_y_size, 768);
701        assert_eq!(parsed.srs, Some("EPSG:4326".to_string()));
702    }
703}