1use std::fmt;
7use std::io::{Read, Seek, SeekFrom, Write};
8use std::mem::size_of;
9use std::ops::Index;
10use std::slice::SliceIndex;
11
12use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
13
14use super::io::*;
15use super::traits::{GrowablePoint, ShrinkablePoint};
16use super::EsriShape;
17use super::{ConcreteReadableShape, GenericBBox};
18use super::{Error, ShapeType};
19use super::{HasShapeType, WritableShape};
20use super::{Point, PointM, PointZ};
21
22#[cfg(feature = "geo-types")]
23use geo_types;
24
25#[derive(Debug, Clone, PartialEq)]
65pub struct GenericMultipoint<PointType> {
66 pub(crate) bbox: GenericBBox<PointType>,
67 pub(crate) points: Vec<PointType>,
68}
69
70impl<PointType: ShrinkablePoint + GrowablePoint + Copy> GenericMultipoint<PointType> {
71 pub fn new(points: Vec<PointType>) -> Self {
105 let bbox = GenericBBox::<PointType>::from_points(&points);
106 Self { bbox, points }
107 }
108}
109
110impl<PointType> GenericMultipoint<PointType> {
111 #[inline]
128 pub fn bbox(&self) -> &GenericBBox<PointType> {
129 &self.bbox
130 }
131
132 #[inline]
134 pub fn points(&self) -> &[PointType] {
135 &self.points
136 }
137
138 #[inline]
153 pub fn point(&self, index: usize) -> Option<&PointType> {
154 self.points.get(index)
155 }
156
157 #[inline]
159 pub fn into_inner(self) -> Vec<PointType> {
160 self.points
161 }
162}
163
164impl<PointType> From<Vec<PointType>> for GenericMultipoint<PointType>
165where
166 PointType: ShrinkablePoint + GrowablePoint + Copy,
167{
168 fn from(points: Vec<PointType>) -> Self {
169 Self::new(points)
170 }
171}
172
173impl<PointType, I: SliceIndex<[PointType]>> Index<I> for GenericMultipoint<PointType> {
174 type Output = I::Output;
175
176 #[inline]
177 fn index(&self, index: I) -> &Self::Output {
178 Index::index(&self.points, index)
179 }
180}
181
182macro_rules! impl_from_multipoint_to_vec_for_point_type {
186 ($PointType:ty) => {
187 impl From<GenericMultipoint<$PointType>> for Vec<$PointType> {
188 fn from(multipoints: GenericMultipoint<$PointType>) -> Self {
189 multipoints.points
190 }
191 }
192 };
193}
194
195impl_from_multipoint_to_vec_for_point_type!(Point);
196impl_from_multipoint_to_vec_for_point_type!(PointM);
197impl_from_multipoint_to_vec_for_point_type!(PointZ);
198
199#[cfg(feature = "geo-types")]
200impl<PointType> From<GenericMultipoint<PointType>> for geo_types::MultiPoint<f64>
201where
202 geo_types::Point<f64>: From<PointType>,
203{
204 fn from(multi_points: GenericMultipoint<PointType>) -> Self {
205 multi_points
206 .points
207 .into_iter()
208 .map(geo_types::Point::from)
209 .collect::<Vec<geo_types::Point<f64>>>()
210 .into()
211 }
212}
213
214#[cfg(feature = "geo-types")]
215impl<PointType> From<geo_types::MultiPoint<f64>> for GenericMultipoint<PointType>
216where
217 PointType: From<geo_types::Point<f64>> + ShrinkablePoint + GrowablePoint + Copy,
218{
219 fn from(mp: geo_types::MultiPoint<f64>) -> Self {
220 let points = mp.into_iter().map(|p| p.into()).collect();
221 Self::new(points)
222 }
223}
224
225pub type Multipoint = GenericMultipoint<Point>;
232
233impl Multipoint {
234 pub(crate) fn size_of_record(num_points: i32) -> usize {
235 let mut size = 0usize;
236 size += 4 * size_of::<f64>(); size += size_of::<i32>(); size += size_of::<Point>() * num_points as usize;
239 size
240 }
241}
242
243impl fmt::Display for Multipoint {
244 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
245 write!(f, "Multipoint({} points)", self.points.len())
246 }
247}
248
249impl HasShapeType for Multipoint {
250 fn shapetype() -> ShapeType {
251 ShapeType::Multipoint
252 }
253}
254
255impl ConcreteReadableShape for Multipoint {
256 fn read_shape_content<T: Read + Seek>(source: &mut T, record_size: i32) -> Result<Self, Error> {
257 let mut bbox = GenericBBox::<Point>::default();
258 bbox_read_xy_from(&mut bbox, source)?;
259
260 let num_points = source.read_i32::<LittleEndian>()?;
261 let expected = Self::size_of_record(num_points) as i32;
262 let diff = record_size - expected;
263
264 if diff < 0 {
265 return Err(Error::InvalidShapeRecordSize);
266 }
267
268 let points = read_xy_in_vec_of::<Point, T>(source, num_points)?;
269
270 if diff > 0 {
271 source.seek(SeekFrom::Current(i64::from(diff)))?;
272 }
273
274 Ok(Self { bbox, points })
275 }
276}
277
278impl WritableShape for Multipoint {
279 fn size_in_bytes(&self) -> usize {
280 let mut size = 0usize;
281 size += 4 * size_of::<f64>(); size += size_of::<i32>(); size += 2 * size_of::<f64>() * self.points.len();
284 size
285 }
286
287 fn write_to<T: Write>(&self, dest: &mut T) -> Result<(), Error> {
288 bbox_write_xy_to(&self.bbox, dest)?;
289 dest.write_i32::<LittleEndian>(self.points.len() as i32)?;
290 for point in self.points.iter() {
291 dest.write_f64::<LittleEndian>(point.x)?;
292 dest.write_f64::<LittleEndian>(point.y)?;
293 }
294 Ok(())
295 }
296}
297
298impl EsriShape for Multipoint {
299 fn x_range(&self) -> [f64; 2] {
300 self.bbox.x_range()
301 }
302
303 fn y_range(&self) -> [f64; 2] {
304 self.bbox.y_range()
305 }
306}
307
308pub type MultipointM = GenericMultipoint<PointM>;
315
316impl MultipointM {
317 pub(crate) fn size_of_record(num_points: i32, is_m_used: bool) -> usize {
318 let mut size = Multipoint::size_of_record(num_points);
319 if is_m_used {
320 size += 2 * size_of::<f64>(); size += size_of::<f64>() * num_points as usize; }
323 size
324 }
325}
326
327impl fmt::Display for MultipointM {
328 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
329 write!(f, "MultipointM({} points)", self.points.len())
330 }
331}
332
333impl HasShapeType for MultipointM {
334 fn shapetype() -> ShapeType {
335 ShapeType::MultipointM
336 }
337}
338
339impl ConcreteReadableShape for MultipointM {
340 fn read_shape_content<T: Read + Seek>(source: &mut T, record_size: i32) -> Result<Self, Error> {
341 let mut bbox = GenericBBox::<PointM>::default();
342 bbox_read_xy_from(&mut bbox, source)?;
343
344 let num_points = source.read_i32::<LittleEndian>()?;
345
346 let size_with_m = Self::size_of_record(num_points, true) as i32;
347 let size_without_m = Self::size_of_record(num_points, false) as i32;
348
349 let size_read = if record_size >= size_with_m {
350 size_with_m
351 } else {
352 size_without_m
353 };
354
355 let diff = record_size - size_read;
356 if diff < 0 {
357 return Err(Error::InvalidShapeRecordSize);
358 }
359
360 let mut points = read_xy_in_vec_of::<PointM, T>(source, num_points)?;
361
362 if record_size >= size_with_m {
363 bbox_read_m_range_from(&mut bbox, source)?;
364 read_ms_into(source, &mut points)?;
365 }
366
367 if diff > 0 {
368 source.seek(SeekFrom::Current(i64::from(diff)))?;
369 }
370
371 Ok(Self { bbox, points })
372 }
373}
374
375impl WritableShape for MultipointM {
376 fn size_in_bytes(&self) -> usize {
377 let mut size = 0usize;
378 size += 4 * size_of::<f64>();
379 size += size_of::<i32>();
380 size += 3 * size_of::<f64>() * self.points.len();
381 size += 2 * size_of::<f64>();
382 size
383 }
384
385 fn write_to<T: Write>(&self, mut dest: &mut T) -> Result<(), Error> {
386 bbox_write_xy_to(&self.bbox, dest)?;
387 dest.write_i32::<LittleEndian>(self.points.len() as i32)?;
388
389 write_points(&mut dest, &self.points)?;
390
391 bbox_write_m_range_to(&self.bbox, dest)?;
392 write_ms(&mut dest, &self.points)?;
393 Ok(())
394 }
395}
396
397impl EsriShape for MultipointM {
398 fn x_range(&self) -> [f64; 2] {
399 self.bbox.x_range()
400 }
401
402 fn y_range(&self) -> [f64; 2] {
403 self.bbox.y_range()
404 }
405
406 fn m_range(&self) -> [f64; 2] {
407 self.bbox.m_range()
408 }
409}
410
411pub type MultipointZ = GenericMultipoint<PointZ>;
418
419impl fmt::Display for MultipointZ {
420 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
421 write!(f, "MultipointZ({} points)", self.points.len())
422 }
423}
424impl MultipointZ {
425 pub(crate) fn size_of_record(num_points: i32, is_m_used: bool) -> usize {
426 let mut size = Multipoint::size_of_record(num_points);
427 size += 2 * size_of::<f64>(); size += size_of::<f64>() * num_points as usize; if is_m_used {
431 size += 2 * size_of::<f64>(); size += size_of::<f64>() * num_points as usize; }
434
435 size
436 }
437}
438
439impl HasShapeType for MultipointZ {
440 fn shapetype() -> ShapeType {
441 ShapeType::MultipointZ
442 }
443}
444
445impl ConcreteReadableShape for MultipointZ {
446 fn read_shape_content<T: Read + Seek>(source: &mut T, record_size: i32) -> Result<Self, Error> {
447 let mut bbox = GenericBBox::<PointZ>::default();
448 bbox_read_xy_from(&mut bbox, source)?;
449 let num_points = source.read_i32::<LittleEndian>()?;
450
451 let size_with_m = Self::size_of_record(num_points, true) as i32;
452 let size_without_m = Self::size_of_record(num_points, false) as i32;
453
454 let size_read = if record_size >= size_with_m {
455 size_with_m
456 } else {
457 size_without_m
458 };
459
460 let diff = record_size - size_read;
461 if diff < 0 {
462 return Err(Error::InvalidShapeRecordSize);
463 }
464
465 let mut points = read_xy_in_vec_of::<PointZ, T>(source, num_points)?;
466
467 bbox_read_z_range_from(&mut bbox, source)?;
468 read_zs_into(source, &mut points)?;
469
470 if record_size >= size_with_m {
471 bbox_read_m_range_from(&mut bbox, source)?;
472 read_ms_into(source, &mut points)?;
473 }
474
475 if diff > 0 {
476 source.seek(SeekFrom::Current(i64::from(diff)))?;
477 }
478
479 Ok(Self { bbox, points })
480 }
481}
482
483impl WritableShape for MultipointZ {
484 fn size_in_bytes(&self) -> usize {
485 let mut size = 0usize;
486 size += 4 * size_of::<f64>();
487 size += size_of::<i32>();
488 size += 4 * size_of::<f64>() * self.points.len();
489 size += 2 * size_of::<f64>();
490 size += 2 * size_of::<f64>();
491 size
492 }
493
494 fn write_to<T: Write>(&self, mut dest: &mut T) -> Result<(), Error> {
495 bbox_write_xy_to(&self.bbox, dest)?;
496 dest.write_i32::<LittleEndian>(self.points.len() as i32)?;
497
498 write_points(&mut dest, &self.points)?;
499
500 bbox_write_z_range_to(&self.bbox, dest)?;
501 write_zs(&mut dest, &self.points)?;
502
503 bbox_write_m_range_to(&self.bbox, dest)?;
504 write_ms(&mut dest, &self.points)?;
505
506 Ok(())
507 }
508}
509
510impl EsriShape for MultipointZ {
511 fn x_range(&self) -> [f64; 2] {
512 self.bbox.x_range()
513 }
514
515 fn y_range(&self) -> [f64; 2] {
516 self.bbox.y_range()
517 }
518
519 fn z_range(&self) -> [f64; 2] {
520 self.bbox.z_range()
521 }
522
523 fn m_range(&self) -> [f64; 2] {
524 self.bbox.m_range()
525 }
526}
527
528#[cfg(test)]
529#[cfg(feature = "geo-types")]
530mod test_geo_types_conversions {
531 use super::*;
532 use crate::{geo_types, NO_DATA};
533 use geo_types::Coord;
534
535 #[test]
536 fn test_multipoint_to_geo_types_multipoint() {
537 let shapefile_points = vec![Point::new(1.0, 1.0), Point::new(2.0, 2.0)];
538 let geo_types_coords = shapefile_points
539 .iter()
540 .copied()
541 .map(Coord::<f64>::from)
542 .collect::<Vec<Coord<f64>>>();
543
544 let expected_shapefile_multipoint = Multipoint::new(shapefile_points);
545 let expected_geo_types_multipoint = geo_types::MultiPoint::from(geo_types_coords);
546
547 let geo_types_multipoint: geo_types::MultiPoint<f64> =
548 expected_shapefile_multipoint.clone().into();
549 let shapefile_multipoint: Multipoint = expected_geo_types_multipoint.clone().into();
550
551 assert_eq!(geo_types_multipoint, expected_geo_types_multipoint);
552 assert_eq!(shapefile_multipoint, expected_shapefile_multipoint);
553 }
554
555 #[test]
556 fn test_multipoint_m_to_geo_types_multipoint() {
557 let points = vec![
558 PointM::new(120.0, 56.0, 42.2),
559 PointM::new(6.0, 18.7, 462.54),
560 ];
561 let shapefile_multipoint = MultipointM::new(points);
562 let geo_types_multipoint = geo_types::MultiPoint::from(shapefile_multipoint);
563
564 let mut iter = geo_types_multipoint.into_iter();
565 let p1 = iter.next().unwrap();
566 let p2 = iter.next().unwrap();
567 assert_eq!(p1.x(), 120.0);
568 assert_eq!(p1.y(), 56.0);
569
570 assert_eq!(p2.x(), 6.0);
571 assert_eq!(p2.y(), 18.7);
572
573 let geo_types_multipoint: geo_types::MultiPoint<_> = vec![p1, p2].into();
574 let shapefile_multipoint = MultipointM::from(geo_types_multipoint);
575
576 assert_eq!(shapefile_multipoint.points[0].x, 120.0);
577 assert_eq!(shapefile_multipoint.points[0].y, 56.0);
578 assert_eq!(shapefile_multipoint.points[0].m, NO_DATA);
579
580 assert_eq!(shapefile_multipoint.points[1].x, 6.0);
581 assert_eq!(shapefile_multipoint.points[1].y, 18.7);
582 assert_eq!(shapefile_multipoint.points[0].m, NO_DATA);
583 }
584
585 #[test]
586 fn test_multipoint_z_to_geo_types_multipoint() {
587 let points = vec![
588 PointZ::new(1.0, 1.0, 17.0, 18.0),
589 PointZ::new(2.0, 2.0, 15.0, 16.0),
590 ];
591 let shapefile_multipoint = MultipointZ::new(points);
592 let geo_types_multipoint = geo_types::MultiPoint::from(shapefile_multipoint);
593
594 let mut iter = geo_types_multipoint.into_iter();
595 let p1 = iter.next().unwrap();
596 let p2 = iter.next().unwrap();
597 assert_eq!(p1.x(), 1.0);
598 assert_eq!(p1.y(), 1.0);
599
600 assert_eq!(p2.x(), 2.0);
601 assert_eq!(p2.y(), 2.0);
602
603 let geo_types_multipoint: geo_types::MultiPoint<_> = vec![p1, p2].into();
604 let shapefile_multipoint = MultipointZ::from(geo_types_multipoint);
605
606 assert_eq!(shapefile_multipoint.points[0].x, 1.0);
607 assert_eq!(shapefile_multipoint.points[0].y, 1.0);
608 assert_eq!(shapefile_multipoint.points[0].z, 0.0);
609 assert_eq!(shapefile_multipoint.points[0].m, NO_DATA);
610
611 assert_eq!(shapefile_multipoint.points[1].x, 2.0);
612 assert_eq!(shapefile_multipoint.points[1].y, 2.0);
613 assert_eq!(shapefile_multipoint.points[0].z, 0.0);
614 assert_eq!(shapefile_multipoint.points[0].m, NO_DATA);
615 }
616}
617
618#[cfg(test)]
619mod tests {
620 use super::{MultipointZ, PointZ};
621
622 #[test]
623 fn test_multipoint_index() {
624 let points = vec![
625 PointZ::new(1.0, 1.0, 17.0, 18.0),
626 PointZ::new(2.0, 2.0, 15.0, 16.0),
627 ];
628 let multipoint = MultipointZ::new(points.clone());
629
630 assert_eq!(multipoint[0], points[0]);
631 assert_eq!(multipoint[1], points[1]);
632
633 assert_eq!(multipoint[..1], points[..1]);
634 }
635}