Skip to main content

shapefile/record/
io.rs

1use std::io::{Read, Write};
2
3use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
4
5use super::traits::{HasM, HasMutM, HasMutXY, HasMutZ, HasXY, HasZ};
6use super::{GenericBBox, PointZ, NO_DATA};
7use super::{Point, PointM};
8
9pub(crate) fn bbox_read_xy_from<PointType: HasMutXY, R: Read>(
10    bbox: &mut GenericBBox<PointType>,
11    src: &mut R,
12) -> std::io::Result<()> {
13    *bbox.min.x_mut() = src.read_f64::<LittleEndian>()?;
14    *bbox.min.y_mut() = src.read_f64::<LittleEndian>()?;
15    *bbox.max.x_mut() = src.read_f64::<LittleEndian>()?;
16    *bbox.max.y_mut() = src.read_f64::<LittleEndian>()?;
17    Ok(())
18}
19
20pub(crate) fn bbox_write_xy_to<PointType: HasXY, W: Write>(
21    bbox: &GenericBBox<PointType>,
22    dst: &mut W,
23) -> std::io::Result<()> {
24    dst.write_f64::<LittleEndian>(bbox.min.x())?;
25    dst.write_f64::<LittleEndian>(bbox.min.y())?;
26    dst.write_f64::<LittleEndian>(bbox.max.x())?;
27    dst.write_f64::<LittleEndian>(bbox.max.y())?;
28    Ok(())
29}
30
31pub(crate) fn bbox_read_m_range_from<PointType: HasMutM, R: Read>(
32    bbox: &mut GenericBBox<PointType>,
33    src: &mut R,
34) -> std::io::Result<()> {
35    *bbox.min.m_mut() = src.read_f64::<LittleEndian>()?;
36    *bbox.max.m_mut() = src.read_f64::<LittleEndian>()?;
37    Ok(())
38}
39
40pub(crate) fn bbox_read_z_range_from<PointType: HasMutZ, R: Read>(
41    bbox: &mut GenericBBox<PointType>,
42    src: &mut R,
43) -> std::io::Result<()> {
44    *bbox.min.z_mut() = src.read_f64::<LittleEndian>()?;
45    *bbox.max.z_mut() = src.read_f64::<LittleEndian>()?;
46    Ok(())
47}
48
49pub(crate) fn bbox_write_m_range_to<PointType: HasM, W: Write>(
50    bbox: &GenericBBox<PointType>,
51    dst: &mut W,
52) -> std::io::Result<()> {
53    dst.write_f64::<LittleEndian>(bbox.min.m())?;
54    dst.write_f64::<LittleEndian>(bbox.max.m())?;
55    Ok(())
56}
57
58pub(crate) fn bbox_write_z_range_to<PointType: HasZ, W: Write>(
59    bbox: &GenericBBox<PointType>,
60    dst: &mut W,
61) -> std::io::Result<()> {
62    dst.write_f64::<LittleEndian>(bbox.min.z())?;
63    dst.write_f64::<LittleEndian>(bbox.max.z())?;
64    Ok(())
65}
66
67pub(crate) fn read_xy_in_vec_of<PointType, T>(
68    source: &mut T,
69    num_points: i32,
70) -> Result<Vec<PointType>, std::io::Error>
71where
72    PointType: HasMutXY + Default,
73    T: Read,
74{
75    let mut points = Vec::<PointType>::with_capacity(num_points as usize);
76    for _ in 0..num_points {
77        let mut p = PointType::default();
78        *p.x_mut() = source.read_f64::<LittleEndian>()?;
79        *p.y_mut() = source.read_f64::<LittleEndian>()?;
80        points.push(p);
81    }
82    Ok(points)
83}
84
85pub(crate) fn read_ms_into<T: Read, D: HasMutM>(
86    source: &mut T,
87    points: &mut [D],
88) -> Result<(), std::io::Error> {
89    for point in points {
90        *point.m_mut() = f64::max(source.read_f64::<LittleEndian>()?, NO_DATA);
91    }
92    Ok(())
93}
94
95pub(crate) fn read_zs_into<T: Read>(
96    source: &mut T,
97    points: &mut [PointZ],
98) -> Result<(), std::io::Error> {
99    for point in points {
100        point.z = source.read_f64::<LittleEndian>()?;
101    }
102    Ok(())
103}
104
105pub(crate) fn read_parts<T: Read>(
106    source: &mut T,
107    num_parts: i32,
108) -> Result<Vec<i32>, std::io::Error> {
109    let mut parts = Vec::<i32>::with_capacity(num_parts as usize);
110    for _ in 0..num_parts {
111        parts.push(source.read_i32::<LittleEndian>()?);
112    }
113    Ok(parts)
114}
115
116pub(crate) fn write_points<T: Write, PointType: HasXY>(
117    dest: &mut T,
118    points: &[PointType],
119) -> Result<(), std::io::Error> {
120    for point in points {
121        dest.write_f64::<LittleEndian>(point.x())?;
122        dest.write_f64::<LittleEndian>(point.y())?;
123    }
124    Ok(())
125}
126
127pub(crate) fn write_ms<T: Write, PointType: HasM>(
128    dest: &mut T,
129    points: &[PointType],
130) -> Result<(), std::io::Error> {
131    for point in points {
132        dest.write_f64::<LittleEndian>(point.m())?;
133    }
134    Ok(())
135}
136
137pub(crate) fn write_zs<T: Write>(dest: &mut T, points: &[PointZ]) -> Result<(), std::io::Error> {
138    for point in points {
139        dest.write_f64::<LittleEndian>(point.z)?;
140    }
141    Ok(())
142}
143
144struct PartIndexIter<'a> {
145    parts_indices: &'a Vec<i32>,
146    current_part_index: usize,
147    num_points: i32,
148}
149
150impl<'a> PartIndexIter<'a> {
151    fn new(parts_indices: &'a Vec<i32>, num_points: i32) -> Self {
152        Self {
153            parts_indices,
154            current_part_index: 0,
155            num_points,
156        }
157    }
158}
159
160impl Iterator for PartIndexIter<'_> {
161    type Item = (i32, i32);
162
163    fn next(&mut self) -> Option<Self::Item> {
164        if self.current_part_index < self.parts_indices.len() {
165            let start_of_part_index = self.parts_indices[self.current_part_index];
166            let end_of_part_index = self
167                .parts_indices
168                .get(self.current_part_index + 1)
169                .copied()
170                .unwrap_or(self.num_points);
171            self.current_part_index += 1;
172            debug_assert!(end_of_part_index >= start_of_part_index);
173            Some((start_of_part_index, end_of_part_index))
174        } else {
175            None
176        }
177    }
178
179    fn size_hint(&self) -> (usize, Option<usize>) {
180        if self.num_points < 0 {
181            (0, None)
182        } else {
183            let remaining = self.parts_indices.len() - self.current_part_index;
184            (remaining, Some(remaining))
185        }
186    }
187}
188
189pub(crate) struct MultiPartShapeReader<'a, PointType, R: Read> {
190    pub(crate) num_points: i32,
191    pub(crate) num_parts: i32,
192    pub(crate) parts: Vec<Vec<PointType>>,
193    pub(crate) bbox: GenericBBox<PointType>,
194    pub(crate) source: &'a mut R,
195    parts_array: Vec<i32>,
196}
197
198impl<'a, PointType: Default + HasMutXY, R: Read> MultiPartShapeReader<'a, PointType, R> {
199    pub(crate) fn new(source: &'a mut R) -> std::io::Result<Self> {
200        let mut bbox = GenericBBox::<PointType>::default();
201        bbox_read_xy_from(&mut bbox, source)?;
202        let num_parts = source.read_i32::<LittleEndian>()?;
203        let num_points = source.read_i32::<LittleEndian>()?;
204        let parts_array = read_parts(source, num_parts)?;
205        let parts = Vec::<Vec<PointType>>::with_capacity(num_parts as usize);
206        Ok(Self {
207            num_points,
208            num_parts,
209            parts_array,
210            parts,
211            source,
212            bbox,
213        })
214    }
215
216    pub(crate) fn read_xy(mut self) -> std::io::Result<Self> {
217        for (start_index, end_index) in PartIndexIter::new(&self.parts_array, self.num_points) {
218            let num_points_in_part = end_index - start_index;
219            self.parts
220                .push(read_xy_in_vec_of(self.source, num_points_in_part)?);
221        }
222        Ok(self)
223    }
224}
225
226impl<PointType: HasMutM, R: Read> MultiPartShapeReader<'_, PointType, R> {
227    pub(crate) fn read_ms(mut self) -> std::io::Result<Self> {
228        bbox_read_m_range_from(&mut self.bbox, &mut self.source)?;
229        for part_points in self.parts.iter_mut() {
230            read_ms_into(self.source, part_points)?;
231        }
232        Ok(self)
233    }
234
235    pub(crate) fn read_ms_if(self, condition: bool) -> std::io::Result<Self> {
236        if condition {
237            self.read_ms()
238        } else {
239            Ok(self)
240        }
241    }
242}
243
244impl<R: Read> MultiPartShapeReader<'_, PointZ, R> {
245    pub(crate) fn read_zs(mut self) -> std::io::Result<Self> {
246        bbox_read_z_range_from(&mut self.bbox, &mut self.source)?;
247        for part_points in self.parts.iter_mut() {
248            read_zs_into(self.source, part_points)?;
249        }
250        Ok(self)
251    }
252}
253
254pub(crate) struct MultiPartShapeWriter<'a, PointType, T, W>
255where
256    T: Iterator<Item = &'a [PointType]> + Clone,
257    W: Write,
258{
259    pub(crate) dst: &'a mut W,
260    parts_iter: T,
261    bbox: &'a GenericBBox<PointType>,
262}
263
264impl<'a, PointType, T, W> MultiPartShapeWriter<'a, PointType, T, W>
265where
266    T: Iterator<Item = &'a [PointType]> + Clone,
267    W: Write,
268{
269    pub(crate) fn new(bbox: &'a GenericBBox<PointType>, parts_iter: T, dst: &'a mut W) -> Self {
270        Self {
271            parts_iter,
272            bbox,
273            dst,
274        }
275    }
276
277    pub(crate) fn write_num_points(self) -> std::io::Result<Self> {
278        let point_count: usize = self.parts_iter.clone().map(|points| points.len()).sum();
279        self.dst.write_i32::<LittleEndian>(point_count as i32)?;
280        Ok(self)
281    }
282
283    pub(crate) fn write_num_parts(self) -> std::io::Result<Self> {
284        let num_parts = self.parts_iter.clone().count();
285        self.dst.write_i32::<LittleEndian>(num_parts as i32)?;
286        Ok(self)
287    }
288
289    pub(crate) fn write_parts_array(self) -> std::io::Result<Self> {
290        let mut sum = 0;
291        for i in self.parts_iter.clone().map(|points| points.len() as i32) {
292            self.dst.write_i32::<LittleEndian>(sum)?;
293            sum += i;
294        }
295        Ok(self)
296    }
297}
298
299impl<'a, PointType, T, W> MultiPartShapeWriter<'a, PointType, T, W>
300where
301    T: Iterator<Item = &'a [PointType]> + Clone,
302    W: Write,
303    PointType: HasXY,
304{
305    pub(crate) fn write_bbox_xy(self) -> std::io::Result<Self> {
306        bbox_write_xy_to(self.bbox, self.dst)?;
307        Ok(self)
308    }
309
310    pub(crate) fn write_xy(self) -> std::io::Result<Self> {
311        for points in self.parts_iter.clone() {
312            write_points(self.dst, points)?;
313        }
314        Ok(self)
315    }
316}
317
318impl<'a, PointType, T, W> MultiPartShapeWriter<'a, PointType, T, W>
319where
320    T: Iterator<Item = &'a [PointType]> + Clone,
321    W: Write,
322    PointType: HasM,
323{
324    pub(crate) fn write_bbox_m_range(self) -> std::io::Result<Self> {
325        bbox_write_m_range_to(self.bbox, self.dst)?;
326        Ok(self)
327    }
328
329    pub(crate) fn write_ms(self) -> std::io::Result<Self> {
330        for points in self.parts_iter.clone() {
331            write_ms(self.dst, points)?;
332        }
333        Ok(self)
334    }
335}
336
337impl<'a, T, W> MultiPartShapeWriter<'a, PointZ, T, W>
338where
339    T: Iterator<Item = &'a [PointZ]> + Clone,
340    W: Write,
341{
342    pub(crate) fn write_bbox_z_range(self) -> std::io::Result<Self> {
343        bbox_write_z_range_to(self.bbox, self.dst)?;
344        Ok(self)
345    }
346
347    pub(crate) fn write_zs(self) -> std::io::Result<Self> {
348        for points in self.parts_iter.clone() {
349            write_zs(self.dst, points)?;
350        }
351        Ok(self)
352    }
353}
354
355impl<'a, T, W> MultiPartShapeWriter<'a, Point, T, W>
356where
357    T: Iterator<Item = &'a [Point]> + Clone,
358    W: Write,
359{
360    pub(crate) fn write_point_shape(self) -> std::io::Result<Self> {
361        self.write_bbox_xy()
362            .and_then(|wrt| wrt.write_num_parts())
363            .and_then(|wrt| wrt.write_num_points())
364            .and_then(|wrt| wrt.write_parts_array())
365            .and_then(|wrt| wrt.write_xy())
366    }
367}
368
369impl<'a, T, W> MultiPartShapeWriter<'a, PointM, T, W>
370where
371    T: Iterator<Item = &'a [PointM]> + Clone,
372    W: Write,
373{
374    pub(crate) fn write_point_m_shape(self) -> std::io::Result<Self> {
375        self.write_bbox_xy()
376            .and_then(|wrt| wrt.write_num_parts())
377            .and_then(|wrt| wrt.write_num_points())
378            .and_then(|wrt| wrt.write_parts_array())
379            .and_then(|wrt| wrt.write_xy())
380            .and_then(|wrt| wrt.write_bbox_m_range())
381            .and_then(|wrt| wrt.write_ms())
382    }
383}
384
385impl<'a, T, W> MultiPartShapeWriter<'a, PointZ, T, W>
386where
387    T: Iterator<Item = &'a [PointZ]> + Clone,
388    W: Write,
389{
390    pub(crate) fn write_point_z_shape(self) -> std::io::Result<Self> {
391        self.write_bbox_xy()
392            .and_then(|wrt| wrt.write_num_parts())
393            .and_then(|wrt| wrt.write_num_points())
394            .and_then(|wrt| wrt.write_parts_array())
395            .and_then(|wrt| wrt.write_xy())
396            .and_then(|wrt| wrt.write_bbox_z_range())
397            .and_then(|wrt| wrt.write_zs())
398            .and_then(|wrt| wrt.write_bbox_m_range())
399            .and_then(|wrt| wrt.write_ms())
400    }
401}