1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
use crate::error::Converter;
use crate::paged_reader::PagedReader;
use crate::root::root_from_document;
use crate::root::Root;
use crate::Blob;
use crate::DateTime;
use crate::Error;
use crate::Extension;
use crate::Header;
use crate::Image;
use crate::PointCloud;
use crate::PointCloudReaderRaw;
use crate::PointCloudReaderSimple;
use crate::Result;
use roxmltree::Document;
use std::fs::File;
use std::io::BufReader;
use std::io::Read;
use std::io::Seek;
use std::io::Write;
use std::path::Path;
const MAX_XML_SIZE: usize = 1024 * 1024 * 50;
/// Main interface for reading E57 files.
pub struct E57Reader<T: Read + Seek> {
reader: PagedReader<T>,
header: Header,
xml: String,
root: Root,
pointclouds: Vec<PointCloud>,
images: Vec<Image>,
extensions: Vec<Extension>,
}
impl<T: Read + Seek> E57Reader<T> {
/// Creates a new E57 instance for from a reader.
pub fn new(mut reader: T) -> Result<Self> {
// Read, parse and validate E57 header
let header = Header::read(&mut reader)?;
// Set up paged reader for the CRC page layer
let mut reader = PagedReader::new(reader, header.page_size)
.read_err("Failed creating paged CRC reader")?;
// Read and parse XML data
let xml_raw = Self::extract_xml(
&mut reader,
header.phys_xml_offset,
header.xml_length as usize,
)?;
let xml = String::from_utf8(xml_raw).read_err("Failed to parse XML as UTF8")?;
let document = Document::parse(&xml).invalid_err("Failed to parse XML data")?;
let root = root_from_document(&document)?;
let pointclouds = PointCloud::vec_from_document(&document)?;
let images = Image::vec_from_document(&document)?;
let extensions = Extension::vec_from_document(&document);
Ok(Self {
reader,
header,
xml,
root,
pointclouds,
images,
extensions,
})
}
/// Returns the contents of E57 binary file header structure.
pub fn header(&self) -> Header {
self.header.clone()
}
/// Returns the XML section of the E57 file.
pub fn xml(&self) -> &str {
&self.xml
}
/// Returns format name stored in the XML section.
pub fn format_name(&self) -> &str {
&self.root.format
}
/// Returns GUID stored in the XML section.
pub fn guid(&self) -> &str {
&self.root.guid
}
/// Returns the library version string of the root XML section.
pub fn library_version(&self) -> Option<&str> {
self.root.library_version.as_deref()
}
/// Returns a list of all extensions defined in this file.
pub fn extensions(&self) -> Vec<Extension> {
self.extensions.clone()
}
/// Returns a list of all point cloud descriptors in the file.
pub fn pointclouds(&self) -> Vec<PointCloud> {
self.pointclouds.clone()
}
/// Returns an iterator for reading point cloud data.
/// The data provided by this interface is already normalized for convenience.
/// There is also a raw iterator for advanced use-cases that require direct access.
pub fn pointcloud_simple(&mut self, pc: &PointCloud) -> Result<PointCloudReaderSimple<'_, T>> {
PointCloudReaderSimple::new(pc, &mut self.reader)
}
/// Returns an iterator for reading raw low level point cloud data.
/// This provides access to the original values stored in the E57 file.
/// This interface is only recommended for advanced use-cases.
/// In most scenarios the simple iterator is the better choice.
pub fn pointcloud_raw(&mut self, pc: &PointCloud) -> Result<PointCloudReaderRaw<'_, T>> {
PointCloudReaderRaw::new(pc, &mut self.reader)
}
/// Returns a list of all image descriptors in the file.
pub fn images(&self) -> Vec<Image> {
self.images.clone()
}
/// Reads the content of a blob and copies it into the supplied writer.
/// Returns the number of written bytes.
pub fn blob(&mut self, blob: &Blob, writer: &mut dyn Write) -> Result<u64> {
blob.read(&mut self.reader, writer)
}
/// Returns the optional creation date and time of the file.
pub fn creation(&self) -> Option<DateTime> {
self.root.creation.clone()
}
/// Returns the optional coordinate system metadata of the file.
///
/// This should contain a Coordinate Reference System that is specified by
/// a string in a well-known text format for a spatial reference system,
/// as defined by the Coordinate Transformation Service specification
/// developed by the Open Geospatial Consortium.
/// See also: <https://www.ogc.org/standard/wkt-crs/>
pub fn coordinate_metadata(&self) -> Option<&str> {
self.root.coordinate_metadata.as_deref()
}
/// Iterate over an reader to check an E57 file for CRC errors.
///
/// This standalone function does only the minimal parsing required
/// to get the E57 page size and without any other checks or validation.
/// After that it will CRC-validate the whole file.
/// It will not read or check any other file header and XML data!
/// This method returns the page size of the E57 file.
pub fn validate_crc(mut reader: T) -> Result<u64> {
let page_size = Self::get_u64(&mut reader, 40, "page size")?;
let mut paged_reader =
PagedReader::new(reader, page_size).read_err("Failed creating paged CRC reader")?;
let mut buffer = vec![0_u8; page_size as usize];
let mut page = 0;
while paged_reader
.read(&mut buffer)
.read_err(format!("Failed to validate CRC for page {page}"))?
!= 0
{
page += 1;
}
Ok(page_size)
}
/// Returns the raw unparsed binary XML data of the E57 file as bytes.
///
/// This standalone function does only the minimal parsing required
/// to get the XML section without any other checks or any other
/// validation than basic CRC ckecking for the XML section itself.
pub fn raw_xml(mut reader: T) -> Result<Vec<u8>> {
let page_size = Self::get_u64(&mut reader, 40, "page size")?;
let xml_offset = Self::get_u64(&mut reader, 24, "XML offset")?;
let xml_length = Self::get_u64(&mut reader, 32, "XML length")?;
// Create paged CRC reader
let mut paged_reader =
PagedReader::new(reader, page_size).read_err("Failed creating paged CRC reader")?;
// Read XML data
Self::extract_xml(&mut paged_reader, xml_offset, xml_length as usize)
}
fn get_u64(reader: &mut T, offset: u64, name: &str) -> Result<u64> {
reader
.seek(std::io::SeekFrom::Start(offset))
.read_err(format!("Cannot seek to {name} offset"))?;
let mut buf = [0_u8; 8];
reader
.read_exact(&mut buf)
.read_err(format!("Cannot read {name} bytes"))?;
Ok(u64::from_le_bytes(buf))
}
fn extract_xml(reader: &mut PagedReader<T>, offset: u64, length: usize) -> Result<Vec<u8>> {
if length > MAX_XML_SIZE {
Error::not_implemented(format!(
"XML sections larger than {MAX_XML_SIZE} bytes are not supported"
))?
}
reader
.seek_physical(offset)
.read_err("Cannot seek to XML offset")?;
let mut xml = vec![0_u8; length];
reader
.read_exact(&mut xml)
.read_err("Failed to read XML data")?;
Ok(xml)
}
}
impl E57Reader<BufReader<File>> {
/// Creates an E57 instance from a Path.
pub fn from_file(path: impl AsRef<Path>) -> Result<Self> {
let file = File::open(path).read_err("Unable to open file")?;
let reader = BufReader::new(file);
Self::new(reader)
}
}