1use 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
13pub struct VrtXmlParser;
15
16impl VrtXmlParser {
17 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 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 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 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 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 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 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
444pub struct VrtXmlWriter;
446
447impl VrtXmlWriter {
448 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 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}