1pub mod header;
7pub mod shapes;
8
9pub use header::{BoundingBox, HEADER_SIZE, ShapefileHeader};
10pub use shapes::{
11 Box2D, MultiPartShape, MultiPartShapeM, MultiPartShapeZ, Point, PointM, PointZ, ShapeType,
12};
13
14use crate::error::{Result, ShapefileError};
15use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
16use std::io::{Read, Seek, Write};
17
18pub const RECORD_HEADER_SIZE: usize = 8;
20
21#[derive(Debug, Clone)]
23pub struct ShapeRecord {
24 pub record_number: i32,
26 pub shape: Shape,
28}
29
30#[derive(Debug, Clone, PartialEq)]
32pub enum Shape {
33 Null,
35 Point(Point),
37 PointZ(PointZ),
39 PointM(PointM),
41 PolyLine(MultiPartShape),
43 Polygon(MultiPartShape),
45 MultiPoint(MultiPartShape),
47 PolyLineZ(MultiPartShapeZ),
49 PolygonZ(MultiPartShapeZ),
51 MultiPointZ(MultiPartShapeZ),
53 PolyLineM(MultiPartShapeM),
55 PolygonM(MultiPartShapeM),
57 MultiPointM(MultiPartShapeM),
59}
60
61impl Shape {
62 pub fn shape_type(&self) -> ShapeType {
64 match self {
65 Self::Null => ShapeType::Null,
66 Self::Point(_) => ShapeType::Point,
67 Self::PointZ(_) => ShapeType::PointZ,
68 Self::PointM(_) => ShapeType::PointM,
69 Self::PolyLine(_) => ShapeType::PolyLine,
70 Self::Polygon(_) => ShapeType::Polygon,
71 Self::MultiPoint(_) => ShapeType::MultiPoint,
72 Self::PolyLineZ(_) => ShapeType::PolyLineZ,
73 Self::PolygonZ(_) => ShapeType::PolygonZ,
74 Self::MultiPointZ(_) => ShapeType::MultiPointZ,
75 Self::PolyLineM(_) => ShapeType::PolyLineM,
76 Self::PolygonM(_) => ShapeType::PolygonM,
77 Self::MultiPointM(_) => ShapeType::MultiPointM,
78 }
79 }
80
81 pub fn read<R: Read>(reader: &mut R) -> Result<Self> {
83 let shape_type_code = reader
84 .read_i32::<LittleEndian>()
85 .map_err(|_| ShapefileError::unexpected_eof("reading shape type"))?;
86
87 let shape_type = ShapeType::from_code(shape_type_code)?;
88
89 match shape_type {
90 ShapeType::Null => Ok(Self::Null),
91 ShapeType::Point => {
92 let point = Point::read(reader)?;
93 Ok(Self::Point(point))
94 }
95 ShapeType::PointZ => {
96 let point = PointZ::read(reader)?;
97 Ok(Self::PointZ(point))
98 }
99 ShapeType::PointM => {
100 let point = PointM::read(reader)?;
101 Ok(Self::PointM(point))
102 }
103 ShapeType::PolyLine => {
104 let shape = MultiPartShape::read(reader)?;
105 Ok(Self::PolyLine(shape))
106 }
107 ShapeType::Polygon => {
108 let shape = MultiPartShape::read(reader)?;
109 Ok(Self::Polygon(shape))
110 }
111 ShapeType::MultiPoint => {
112 let shape = MultiPartShape::read(reader)?;
113 Ok(Self::MultiPoint(shape))
114 }
115 ShapeType::PolyLineZ => {
116 let shape = MultiPartShapeZ::read(reader)?;
117 Ok(Self::PolyLineZ(shape))
118 }
119 ShapeType::PolygonZ => {
120 let shape = MultiPartShapeZ::read(reader)?;
121 Ok(Self::PolygonZ(shape))
122 }
123 ShapeType::MultiPointZ => {
124 let shape = MultiPartShapeZ::read(reader)?;
125 Ok(Self::MultiPointZ(shape))
126 }
127 ShapeType::PolyLineM => {
128 let shape = MultiPartShapeM::read(reader)?;
129 Ok(Self::PolyLineM(shape))
130 }
131 ShapeType::PolygonM => {
132 let shape = MultiPartShapeM::read(reader)?;
133 Ok(Self::PolygonM(shape))
134 }
135 ShapeType::MultiPointM => {
136 let shape = MultiPartShapeM::read(reader)?;
137 Ok(Self::MultiPointM(shape))
138 }
139 _ => Err(ShapefileError::UnsupportedShapeType {
140 shape_type: shape_type_code,
141 }),
142 }
143 }
144
145 pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
147 let shape_type = self.shape_type();
148 writer
149 .write_i32::<LittleEndian>(shape_type.to_code())
150 .map_err(ShapefileError::Io)?;
151
152 match self {
153 Self::Null => Ok(()),
154 Self::Point(point) => point.write(writer),
155 Self::PointZ(point) => point.write(writer),
156 Self::PointM(point) => point.write(writer),
157 Self::PolyLine(shape) => shape.write(writer),
158 Self::Polygon(shape) => shape.write(writer),
159 Self::MultiPoint(shape) => shape.write(writer),
160 Self::PolyLineZ(shape) | Self::PolygonZ(shape) | Self::MultiPointZ(shape) => {
161 shape.write(writer)
162 }
163 Self::PolyLineM(shape) | Self::PolygonM(shape) | Self::MultiPointM(shape) => {
164 shape.write(writer)
165 }
166 }
167 }
168
169 pub fn content_length(&self) -> i32 {
171 match self {
172 Self::Null => 0,
173 Self::Point(_) => 8,
175 Self::PointZ(_) => 16,
177 Self::PointM(_) => 12,
179 Self::PolyLine(shape) | Self::Polygon(shape) | Self::MultiPoint(shape) => {
181 let bbox_bytes = 32; let counts_bytes = 8; let parts_bytes = shape.num_parts * 4;
184 let points_bytes = shape.num_points * 16; (bbox_bytes + counts_bytes + parts_bytes + points_bytes) / 2
186 }
187 Self::PolyLineZ(shape) | Self::PolygonZ(shape) | Self::MultiPointZ(shape) => {
189 shape.content_length_words()
190 }
191 Self::PolyLineM(shape) | Self::PolygonM(shape) | Self::MultiPointM(shape) => {
193 shape.content_length_words()
194 }
195 }
196 }
197}
198
199impl ShapeRecord {
200 pub fn new(record_number: i32, shape: Shape) -> Self {
202 Self {
203 record_number,
204 shape,
205 }
206 }
207
208 pub fn read<R: Read>(reader: &mut R) -> Result<Self> {
210 let record_number = reader
212 .read_i32::<BigEndian>()
213 .map_err(|_| ShapefileError::unexpected_eof("reading record number"))?;
214
215 let _content_length = reader
216 .read_i32::<BigEndian>()
217 .map_err(|_| ShapefileError::unexpected_eof("reading content length"))?;
218
219 let shape = Shape::read(reader)?;
221
222 Ok(Self {
223 record_number,
224 shape,
225 })
226 }
227
228 pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
230 writer
232 .write_i32::<BigEndian>(self.record_number)
233 .map_err(ShapefileError::Io)?;
234
235 let content_length = 2 + self.shape.content_length(); writer
238 .write_i32::<BigEndian>(content_length)
239 .map_err(ShapefileError::Io)?;
240
241 self.shape.write(writer)?;
243
244 Ok(())
245 }
246}
247
248pub struct ShpReader<R: Read> {
250 reader: R,
251 header: ShapefileHeader,
252}
253
254impl<R: Read> ShpReader<R> {
255 pub fn new(mut reader: R) -> Result<Self> {
257 let header = ShapefileHeader::read(&mut reader)?;
258 Ok(Self { reader, header })
259 }
260
261 pub fn header(&self) -> &ShapefileHeader {
263 &self.header
264 }
265
266 pub fn read_record(&mut self) -> Result<Option<ShapeRecord>> {
268 match ShapeRecord::read(&mut self.reader) {
269 Ok(record) => Ok(Some(record)),
270 Err(ShapefileError::Io(ref e)) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
271 Ok(None)
272 }
273 Err(ShapefileError::UnexpectedEof { .. }) => {
274 Ok(None)
276 }
277 Err(e) => Err(e),
278 }
279 }
280
281 pub fn read_all_records(&mut self) -> Result<Vec<ShapeRecord>> {
283 let mut records = Vec::new();
284 while let Some(record) = self.read_record()? {
285 records.push(record);
286 }
287 Ok(records)
288 }
289}
290
291pub struct ShpWriter<W: Write> {
293 writer: W,
294 header: ShapefileHeader,
295 record_count: i32,
296 file_length_words: i32,
298}
299
300impl<W: Write> ShpWriter<W> {
301 pub fn new(writer: W, shape_type: ShapeType, bbox: BoundingBox) -> Self {
303 let header = ShapefileHeader::new(shape_type, bbox);
304 Self {
305 writer,
306 header,
307 record_count: 0,
308 file_length_words: 50, }
310 }
311
312 pub fn write_header(&mut self) -> Result<()> {
314 self.header.write(&mut self.writer)
315 }
316
317 pub fn write_record(&mut self, shape: Shape) -> Result<()> {
319 self.record_count += 1;
320
321 let content_length = 2 + shape.content_length(); self.file_length_words += 4 + content_length; let record = ShapeRecord::new(self.record_count, shape);
326 record.write(&mut self.writer)
327 }
328
329 pub fn flush(&mut self) -> Result<()> {
331 self.writer.flush().map_err(ShapefileError::Io)
332 }
333
334 pub fn finalize<S: Write + Seek>(self, _seekable_writer: S) -> Result<()> {
336 Ok(())
341 }
342}
343
344impl<W: Write + Seek> ShpWriter<W> {
345 pub fn update_file_length(&mut self) -> Result<()> {
347 self.writer
349 .seek(std::io::SeekFrom::Start(24))
350 .map_err(ShapefileError::Io)?;
351
352 self.writer
354 .write_i32::<BigEndian>(self.file_length_words)
355 .map_err(ShapefileError::Io)?;
356
357 self.writer
359 .seek(std::io::SeekFrom::End(0))
360 .map_err(ShapefileError::Io)?;
361
362 Ok(())
363 }
364}
365
366#[cfg(test)]
367#[allow(clippy::panic)]
368mod tests {
369 use super::*;
370 use std::io::Cursor;
371
372 #[test]
373 fn test_shape_content_length() {
374 let null_shape = Shape::Null;
375 assert_eq!(null_shape.content_length(), 0);
376
377 let point_shape = Shape::Point(Point::new(10.0, 20.0));
378 assert_eq!(point_shape.content_length(), 8);
379 }
380
381 #[test]
382 fn test_record_round_trip() {
383 let shape = Shape::Point(Point::new(10.5, 20.3));
384 let record = ShapeRecord::new(1, shape.clone());
385
386 let mut buffer = Vec::new();
387 record.write(&mut buffer).expect("write record to buffer");
388
389 let mut cursor = Cursor::new(buffer);
390 let read_record = ShapeRecord::read(&mut cursor).expect("read record from cursor");
391
392 assert_eq!(read_record.record_number, 1);
393 assert_eq!(read_record.shape, shape);
394 }
395
396 #[test]
397 fn test_shp_reader_writer() {
398 let bbox = BoundingBox::new_2d(-180.0, -90.0, 180.0, 90.0).expect("valid bbox");
399 let mut buffer = Cursor::new(Vec::new());
400
401 {
403 let mut writer = ShpWriter::new(&mut buffer, ShapeType::Point, bbox);
404 writer.write_header().expect("write header");
405 writer
406 .write_record(Shape::Point(Point::new(10.0, 20.0)))
407 .expect("write record 1");
408 writer
409 .write_record(Shape::Point(Point::new(30.0, 40.0)))
410 .expect("write record 2");
411 }
412
413 buffer.set_position(0);
415 let mut reader = ShpReader::new(buffer).expect("create reader");
416
417 assert_eq!(reader.header().shape_type, ShapeType::Point);
418
419 let records = reader.read_all_records().expect("read records");
420 assert_eq!(records.len(), 2);
421 assert_eq!(records[0].record_number, 1);
422 assert_eq!(records[1].record_number, 2);
423 }
424
425 #[test]
426 fn test_polyline_z_round_trip() {
427 let points = vec![
428 Point::new(0.0, 0.0),
429 Point::new(10.0, 10.0),
430 Point::new(20.0, 5.0),
431 ];
432 let z_values = vec![100.0, 200.0, 150.0];
433 let m_values = Some(vec![0.0, 0.5, 1.0]);
434
435 let shape_z = MultiPartShapeZ::new(vec![0], points, z_values.clone(), m_values.clone())
436 .expect("valid shape");
437 let shape = Shape::PolyLineZ(shape_z);
438
439 let mut buffer = Vec::new();
440 let record = ShapeRecord::new(1, shape.clone());
441 record.write(&mut buffer).expect("write record");
442
443 let mut cursor = Cursor::new(buffer);
444 let read_record = ShapeRecord::read(&mut cursor).expect("read record");
445
446 assert_eq!(read_record.record_number, 1);
447 if let Shape::PolyLineZ(ref sz) = read_record.shape {
448 assert_eq!(sz.base.num_points, 3);
449 assert_eq!(sz.z_values.len(), 3);
450 assert!((sz.z_values[0] - 100.0).abs() < f64::EPSILON);
451 assert!((sz.z_values[1] - 200.0).abs() < f64::EPSILON);
452 assert!((sz.z_values[2] - 150.0).abs() < f64::EPSILON);
453 assert!(sz.m_values.is_some());
454 let mv = sz.m_values.as_ref().expect("m_values");
455 assert!((mv[0] - 0.0).abs() < f64::EPSILON);
456 assert!((mv[1] - 0.5).abs() < f64::EPSILON);
457 assert!((mv[2] - 1.0).abs() < f64::EPSILON);
458 } else {
459 panic!("Expected PolyLineZ shape");
460 }
461 }
462
463 #[test]
464 fn test_polygon_m_round_trip() {
465 let points = vec![
466 Point::new(0.0, 0.0),
467 Point::new(10.0, 0.0),
468 Point::new(10.0, 10.0),
469 Point::new(0.0, 0.0),
470 ];
471 let m_values = vec![0.0, 1.0, 2.0, 0.0];
472
473 let shape_m = MultiPartShapeM::new(vec![0], points, m_values.clone()).expect("valid shape");
474 let shape = Shape::PolygonM(shape_m);
475
476 let mut buffer = Vec::new();
477 let record = ShapeRecord::new(1, shape.clone());
478 record.write(&mut buffer).expect("write record");
479
480 let mut cursor = Cursor::new(buffer);
481 let read_record = ShapeRecord::read(&mut cursor).expect("read record");
482
483 assert_eq!(read_record.record_number, 1);
484 if let Shape::PolygonM(ref sm) = read_record.shape {
485 assert_eq!(sm.base.num_points, 4);
486 assert_eq!(sm.m_values.len(), 4);
487 assert!((sm.m_values[0] - 0.0).abs() < f64::EPSILON);
488 assert!((sm.m_values[1] - 1.0).abs() < f64::EPSILON);
489 } else {
490 panic!("Expected PolygonM shape");
491 }
492 }
493
494 #[test]
495 fn test_z_content_length() {
496 let points = vec![Point::new(0.0, 0.0), Point::new(10.0, 10.0)];
497 let z_values = vec![100.0, 200.0];
498
499 let shape_z = MultiPartShapeZ::new(vec![0], points, z_values, None).expect("valid shape");
500 let shape = Shape::PolyLineZ(shape_z);
501 let cl = shape.content_length();
503 assert!(cl > 0);
504 }
505}