1use super::{LASExtendedVariableLengthRecord, LASHeader, LASPoint};
2use crate::{
3 parsers::{FeatureReader, Reader},
4 proj::Transformer,
5 readers::{
6 FieldTagNames, GeoStore, GeoTIFFTypes, build_transform_from_geo_keys,
7 parse_geotiff_raw_geokeys,
8 },
9};
10use alloc::{collections::BTreeMap, string::String, vec::Vec};
11use s2json::{Properties, VectorFeature, VectorGeometry, VectorPoint};
12
13#[derive(Debug, Default, Clone)]
15pub struct LASReaderOptions {
16 pub dont_transform: bool,
18 pub epsg_codes: BTreeMap<String, String>,
20}
21
22pub type LASVectorFeature = VectorFeature<(), Properties, LASPoint>;
24
25#[derive(Debug, Clone)]
72pub struct LASReader<T: Reader> {
73 reader: T,
74 pub header: LASHeader,
76 pub variable_length_records: BTreeMap<u32, LASExtendedVariableLengthRecord>,
78 pub wkt: Option<String>,
80 pub geo_key_directory: GeoStore,
82 transformer: Transformer,
83 dont_transform: bool,
84}
85impl<T: Reader> LASReader<T> {
86 pub fn new(reader: T, options: Option<LASReaderOptions>) -> Self {
88 let options = options.unwrap_or_default();
89 let header = LASHeader::from_reader(&reader);
90 let variable_length_records = las_parse_variable_length_records(&header, &reader);
91 let mut transformer = Transformer::new();
92 for (epsg_code, wkt) in options.epsg_codes.iter() {
93 transformer.insert_epsg_code(epsg_code.clone(), wkt.clone());
94 }
95 let wkt = build_wkt(&header, &variable_length_records, &mut transformer);
96 let geo_key_directory =
97 build_geo_key_directory(&variable_length_records, &mut transformer, wkt.is_none());
98 Self {
99 reader,
100 header,
101 variable_length_records,
102 wkt,
103 geo_key_directory,
104 transformer,
105 dont_transform: options.dont_transform,
106 }
107 }
108
109 pub fn len(&self) -> u64 {
111 self.header.num_points as u64
112 }
113
114 pub fn is_empty(&self) -> bool {
116 self.len() == 0
117 }
118
119 pub fn get_feature(&self, index: u64) -> Option<LASVectorFeature> {
121 self.get_point(index).map(|point| {
122 VectorFeature::new_wm(
123 None,
124 Properties::default(),
125 VectorGeometry::new_point(point, None),
126 None,
127 )
128 })
129 }
130
131 pub fn get_point(&self, index: u64) -> Option<VectorPoint<LASPoint>> {
133 let Self { reader, header, dont_transform, .. } = self;
134 let LASHeader {
135 num_points,
136 offset_to_points,
137 point_data_format_id: format,
138 point_data_record_length,
139 ..
140 } = header;
141 if index + 1 > *num_points as u64 {
142 return None;
143 }
144 let offset_to_points = *offset_to_points as u64;
145 let point_data_record_length = *point_data_record_length as u64;
146 let offset = offset_to_points + index * point_data_record_length;
147 let point = if *format == 0 {
148 LASPoint::format0(reader, offset)
149 } else if *format == 1 {
150 LASPoint::format1(reader, offset)
151 } else if *format == 2 {
152 LASPoint::format2(reader, offset)
153 } else if *format == 3 {
154 LASPoint::format3(reader, offset)
155 } else if *format == 4 {
156 LASPoint::format4(reader, offset)
157 } else if *format == 5 {
158 LASPoint::format5(reader, offset)
159 } else if *format == 6 {
160 LASPoint::format6(reader, offset)
161 } else if *format == 7 {
162 LASPoint::format7(reader, offset)
163 } else if *format == 8 {
164 LASPoint::format8(reader, offset)
165 } else if *format == 9 {
166 LASPoint::format9(reader, offset)
167 } else if *format == 10 {
168 LASPoint::format10(reader, offset)
169 } else {
170 panic!("Unknown Point Data Format ID: {}", format);
171 };
172 let mut vp = point.to_vector_point(header);
173
174 if !*dont_transform {
175 self.transformer.forward_mut(&mut vp);
176 }
177
178 Some(vp)
179 }
180}
181
182#[derive(Debug)]
184pub struct LASIterator<'a, T: Reader> {
185 reader: &'a LASReader<T>,
186 index: u64,
187 len: u64,
188}
189impl<T: Reader> Iterator for LASIterator<'_, T> {
190 type Item = LASVectorFeature;
191
192 fn next(&mut self) -> Option<Self::Item> {
193 let las = &self.reader;
194 if self.index >= self.len {
195 None
196 } else if let Some(point) = las.get_feature(self.index) {
197 self.index += 1;
198 Some(point)
199 } else {
200 None
201 }
202 }
203}
204impl<T: Reader> FeatureReader<(), Properties, LASPoint> for LASReader<T> {
206 type FeatureIterator<'a>
207 = LASIterator<'a, T>
208 where
209 T: 'a;
210
211 fn iter(&self) -> Self::FeatureIterator<'_> {
212 LASIterator { reader: self, index: 0, len: self.len() }
213 }
214
215 fn par_iter(&self, pool_size: usize, thread_id: usize) -> Self::FeatureIterator<'_> {
216 let pool_size = pool_size as u64;
217 let thread_id = thread_id as u64;
218 let start = self.len() * thread_id / pool_size;
219 let end = self.len() * (thread_id + 1) / pool_size;
220 LASIterator { reader: self, index: start, len: end }
221 }
222}
223
224pub fn las_parse_variable_length_records<T: Reader>(
239 header: &LASHeader,
240 reader: &T,
241) -> BTreeMap<u32, LASExtendedVariableLengthRecord> {
242 let LASHeader { header_size, num_variable_length_records, .. } = header;
243 let mut res = BTreeMap::new();
244 let mut offset = *header_size as u64;
245 let mut i = 0;
246 while i < *num_variable_length_records {
247 let record = LASExtendedVariableLengthRecord::from_reader(reader, offset);
248 offset += 54 + record.record_length;
249 res.insert(record.record_id as u32, record);
250 i += 1;
251 }
252
253 res
254}
255
256pub fn build_wkt(
272 header: &LASHeader,
273 variable_length_records: &BTreeMap<u32, LASExtendedVariableLengthRecord>,
274 transformer: &mut Transformer,
275) -> Option<String> {
276 if (header.encoding & (1 << 3)) != 0 {
278 return None;
279 }
280 let wkt_math_ogc = variable_length_records.get(&2111).and_then(|v| v.data.clone());
282 let wkt_coord_system_data = variable_length_records.get(&2112).and_then(|v| v.data.clone());
284 if wkt_math_ogc.is_none() && wkt_coord_system_data.is_none() {
285 return None;
286 }
287 let wkt_coord_system = wkt_math_ogc.or(wkt_coord_system_data).unwrap();
288 let wkt_str: String = String::from_utf8_lossy(&wkt_coord_system).into();
289 transformer.set_source(wkt_str.clone());
290
291 Some(wkt_str)
292}
293
294pub fn build_geo_key_directory(
310 variable_length_records: &BTreeMap<u32, LASExtendedVariableLengthRecord>,
311 transformer: &mut Transformer,
312 wkt_is_none: bool,
313) -> GeoStore {
314 let mut file_dir = GeoStore::default();
315 let geokey_record = variable_length_records
317 .get(&(FieldTagNames::GeoKeyDirectory as u32))
318 .and_then(|v| v.data.clone());
319 if geokey_record.is_none() {
320 return GeoStore::default();
321 }
322 let raw_geo_keys: Vec<u16> = geokey_record
323 .unwrap()
324 .chunks_exact(2)
325 .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
326 .collect();
327 let double_record = variable_length_records
329 .get(&(FieldTagNames::GeoDoubleParams as u32))
330 .and_then(|v| v.data.clone());
331 if let Some(double_record) = double_record {
332 file_dir.set(
333 FieldTagNames::GeoDoubleParams as u16,
334 double_record.to_vec(),
335 GeoTIFFTypes::DOUBLE,
336 );
337 }
338 let ascii_record = variable_length_records
340 .get(&(FieldTagNames::GeoAsciiParams as u32))
341 .and_then(|v| v.data.clone());
342 if let Some(ascii_record) = ascii_record {
343 file_dir.set(
344 FieldTagNames::GeoAsciiParams as u16,
345 ascii_record.to_vec(),
346 GeoTIFFTypes::ASCII,
347 );
348 }
349 let gkd = parse_geotiff_raw_geokeys(&raw_geo_keys, &file_dir);
350 if wkt_is_none {
351 build_transform_from_geo_keys(transformer, &gkd);
352 }
353
354 gkd
355}