1use std::io::Cursor;
12
13use serde::Deserialize;
14use serde_json::{Map, Number, Value};
15
16use shapefile::record::traits::{HasM, HasXY, HasZ};
17use shapefile::{Multipatch, Patch, Point, PointM, PointZ, Shape, ShapeReader, NO_DATA};
18
19use crate::error::{Result, ShapefileError};
20
21#[derive(Debug, Default, Deserialize)]
25#[serde(default, rename_all = "camelCase")]
26pub struct ReadOptions {
27 pub encoding: Option<String>,
29 pub include_m: bool,
32 pub prj: Option<String>,
35}
36
37impl ReadOptions {
38 fn reader_builder(&self) -> dbase::ReaderBuilder {
41 let builder = dbase::ReaderBuilder::new();
42
43 let requested = self
44 .encoding
45 .as_deref()
46 .unwrap_or("utf-8")
47 .trim()
48 .to_ascii_lowercase()
49 .replace(['-', '_', ' '], "");
50
51 match requested.as_str() {
53 "utf8" | "utf8bom" | "65001" | "" => builder.with_encoding(dbase::UnicodeLossy),
54 "cp1252" | "windows1252" | "iso88591" | "latin1" | "ansi" | "1252" => {
55 builder.with_encoding(yore::code_pages::CP1252)
56 }
57 "cp1250" | "windows1250" | "1250" => builder.with_encoding(yore::code_pages::CP1250),
58 "cp1251" | "windows1251" | "1251" => builder.with_encoding(yore::code_pages::CP1251),
59 "cp1253" | "windows1253" | "1253" => builder.with_encoding(yore::code_pages::CP1253),
60 "cp1254" | "windows1254" | "1254" => builder.with_encoding(yore::code_pages::CP1254),
61 "cp1255" | "windows1255" | "1255" => builder.with_encoding(yore::code_pages::CP1255),
62 "cp1256" | "windows1256" | "1256" => builder.with_encoding(yore::code_pages::CP1256),
63 "cp437" | "437" | "oem" => builder.with_encoding(yore::code_pages::CP437),
64 "cp850" | "850" => builder.with_encoding(yore::code_pages::CP850),
65 "cp852" | "852" => builder.with_encoding(yore::code_pages::CP852),
66 "cp865" | "865" => builder.with_encoding(yore::code_pages::CP865),
67 "cp866" | "866" => builder.with_encoding(yore::code_pages::CP866),
68 "cp874" | "874" => builder.with_encoding(yore::code_pages::CP874),
69 _ => builder.with_encoding(dbase::UnicodeLossy),
72 }
73 }
74}
75
76pub fn read(shp: &[u8], dbf: Option<&[u8]>, options: &ReadOptions) -> Result<Value> {
78 let reader = ShapeReader::new(Cursor::new(shp))?;
79 let shapes = reader.read()?;
80
81 let records = match dbf {
85 Some(bytes) => read_records(bytes, options)?,
86 None => Vec::new(),
87 };
88
89 let mut features = Vec::with_capacity(shapes.len());
90 for (index, shape) in shapes.iter().enumerate() {
91 let mut feature = Map::new();
92 feature.insert("type".into(), Value::String("Feature".into()));
93 feature.insert(
94 "geometry".into(),
95 shape_to_geometry(shape, options.include_m, index)?,
96 );
97 feature.insert(
98 "properties".into(),
99 records
100 .get(index)
101 .cloned()
102 .map(Value::Object)
103 .unwrap_or(Value::Object(Map::new())),
104 );
105 features.push(Value::Object(feature));
106 }
107
108 let mut collection = Map::new();
109 collection.insert("type".into(), Value::String("FeatureCollection".into()));
110 collection.insert("features".into(), Value::Array(features));
111 if let Some(prj) = &options.prj {
112 collection.insert("wkt".into(), Value::String(prj.clone()));
114 }
115
116 Ok(Value::Object(collection))
117}
118
119fn read_records(dbf: &[u8], options: &ReadOptions) -> Result<Vec<Map<String, Value>>> {
120 let mut reader = options.reader_builder().build(Cursor::new(dbf))?;
121
122 let field_names: Vec<String> = reader
125 .fields()
126 .iter()
127 .map(|field| field.name().to_string())
128 .filter(|name| name != "DeletionFlag")
129 .collect();
130
131 let records = reader.read()?;
132
133 Ok(records
134 .into_iter()
135 .map(|record| {
136 let mut properties = Map::new();
137 for name in &field_names {
138 let value = record.get(name).map(field_to_json).unwrap_or(Value::Null);
139 properties.insert(name.clone(), value);
140 }
141 properties
142 })
143 .collect())
144}
145
146fn field_to_json(value: &dbase::FieldValue) -> Value {
149 use dbase::FieldValue;
150
151 let text = |value: &String| Value::String(value.clone());
152
153 match value {
154 FieldValue::Character(Some(value)) => text(value),
155 FieldValue::Memo(value) => text(value),
156 FieldValue::Character(None) => Value::Null,
157 FieldValue::Numeric(Some(value)) => number(*value),
158 FieldValue::Numeric(None) => Value::Null,
159 FieldValue::Float(Some(value)) => number(*value as f64),
160 FieldValue::Float(None) => Value::Null,
161 FieldValue::Logical(Some(value)) => Value::Bool(*value),
162 FieldValue::Logical(None) => Value::Null,
163 FieldValue::Integer(value) => Value::Number(Number::from(*value)),
164 FieldValue::Currency(value) | FieldValue::Double(value) => number(*value),
165 FieldValue::Date(Some(date)) => Value::String(format!(
166 "{:04}-{:02}-{:02}",
167 date.year(),
168 date.month(),
169 date.day()
170 )),
171 FieldValue::Date(None) => Value::Null,
172 FieldValue::DateTime(stamp) => {
173 let date = stamp.date();
174 let time = stamp.time();
175 Value::String(format!(
176 "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z",
177 date.year(),
178 date.month(),
179 date.day(),
180 time.hours(),
181 time.minutes(),
182 time.seconds()
183 ))
184 }
185 }
186}
187
188fn number(value: f64) -> Value {
189 Number::from_f64(value)
190 .map(Value::Number)
191 .unwrap_or(Value::Null)
192}
193
194fn has_measure(m: f64) -> bool {
196 m > NO_DATA && m.is_finite()
197}
198
199fn position_xy(point: &Point) -> Value {
200 Value::Array(vec![number(point.x), number(point.y)])
201}
202
203fn position_m(point: &PointM, include_m: bool) -> Value {
204 let mut ordinates = vec![number(point.x()), number(point.y())];
205 if include_m && has_measure(point.m()) {
206 ordinates.push(number(point.m()));
207 }
208 Value::Array(ordinates)
209}
210
211fn position_z(point: &PointZ, include_m: bool) -> Value {
212 let mut ordinates = vec![number(point.x()), number(point.y()), number(point.z())];
213 if include_m && has_measure(point.m()) {
214 ordinates.push(number(point.m()));
215 }
216 Value::Array(ordinates)
217}
218
219fn geometry(kind: &str, coordinates: Value) -> Value {
220 let mut object = Map::new();
221 object.insert("type".into(), Value::String(kind.into()));
222 object.insert("coordinates".into(), coordinates);
223 Value::Object(object)
224}
225
226fn lines(parts: Vec<Value>) -> Value {
229 if parts.len() == 1 {
230 geometry("LineString", parts.into_iter().next().unwrap())
231 } else {
232 geometry("MultiLineString", Value::Array(parts))
233 }
234}
235
236fn points(points: Vec<Value>) -> Value {
237 geometry("MultiPoint", Value::Array(points))
238}
239
240fn shape_to_geometry(shape: &Shape, include_m: bool, index: usize) -> Result<Value> {
241 let geometry = match shape {
242 Shape::NullShape => Value::Null,
243
244 Shape::Point(point) => geometry("Point", position_xy(point)),
245 Shape::PointM(point) => geometry("Point", position_m(point, include_m)),
246 Shape::PointZ(point) => geometry("Point", position_z(point, include_m)),
247
248 Shape::Multipoint(shape) => points(shape.points().iter().map(position_xy).collect()),
249 Shape::MultipointM(shape) => points(
250 shape
251 .points()
252 .iter()
253 .map(|point| position_m(point, include_m))
254 .collect(),
255 ),
256 Shape::MultipointZ(shape) => points(
257 shape
258 .points()
259 .iter()
260 .map(|point| position_z(point, include_m))
261 .collect(),
262 ),
263
264 Shape::Polyline(shape) => lines(
265 shape
266 .parts()
267 .iter()
268 .map(|part| Value::Array(part.iter().map(position_xy).collect()))
269 .collect(),
270 ),
271 Shape::PolylineM(shape) => lines(
272 shape
273 .parts()
274 .iter()
275 .map(|part| Value::Array(part.iter().map(|p| position_m(p, include_m)).collect()))
276 .collect(),
277 ),
278 Shape::PolylineZ(shape) => lines(
279 shape
280 .parts()
281 .iter()
282 .map(|part| Value::Array(part.iter().map(|p| position_z(p, include_m)).collect()))
283 .collect(),
284 ),
285
286 Shape::Polygon(shape) => rings_to_geometry(
287 shape
288 .rings()
289 .iter()
290 .map(|ring| Ring::from_points(ring, |point| (point.x, point.y), position_xy))
291 .collect(),
292 ),
293 Shape::PolygonM(shape) => rings_to_geometry(
294 shape
295 .rings()
296 .iter()
297 .map(|ring| {
298 Ring::from_points(
299 ring,
300 |point| (point.x(), point.y()),
301 |point| position_m(point, include_m),
302 )
303 })
304 .collect(),
305 ),
306 Shape::PolygonZ(shape) => rings_to_geometry(
307 shape
308 .rings()
309 .iter()
310 .map(|ring| {
311 Ring::from_points(
312 ring,
313 |point| (point.x(), point.y()),
314 |point| position_z(point, include_m),
315 )
316 })
317 .collect(),
318 ),
319
320 Shape::Multipatch(shape) => multipatch_to_geometry(shape, include_m, index)?,
321 };
322
323 Ok(geometry)
324}
325
326struct Ring {
329 outer: bool,
330 xy: Vec<(f64, f64)>,
331 positions: Vec<Value>,
332}
333
334impl Ring {
335 fn from_points<P, F, G>(ring: &shapefile::PolygonRing<P>, to_xy: F, to_position: G) -> Self
336 where
337 F: Fn(&P) -> (f64, f64),
338 G: Fn(&P) -> Value,
339 {
340 let (outer, points) = match ring {
341 shapefile::PolygonRing::Outer(points) => (true, points),
342 shapefile::PolygonRing::Inner(points) => (false, points),
343 };
344 Self {
345 outer,
346 xy: points.iter().map(&to_xy).collect(),
347 positions: points.iter().map(&to_position).collect(),
348 }
349 }
350
351 fn signed_area(&self) -> f64 {
353 let mut total = 0.0;
354 for window in self.xy.windows(2) {
355 let (x1, y1) = window[0];
356 let (x2, y2) = window[1];
357 total += (x2 - x1) * (y2 + y1);
358 }
359 -total
360 }
361
362 fn contains(&self, point: (f64, f64)) -> bool {
363 let (px, py) = point;
365 let mut inside = false;
366 for window in self.xy.windows(2) {
367 let (x1, y1) = window[0];
368 let (x2, y2) = window[1];
369 if (y1 > py) != (y2 > py) {
370 let slope = (x2 - x1) / (y2 - y1);
371 if px < x1 + (py - y1) * slope {
372 inside = !inside;
373 }
374 }
375 }
376 inside
377 }
378
379 fn oriented(mut self, counter_clockwise: bool) -> Vec<Value> {
382 let is_ccw = self.signed_area() > 0.0;
383 if is_ccw != counter_clockwise {
384 self.positions.reverse();
385 }
386 self.positions
387 }
388}
389
390fn rings_to_geometry(rings: Vec<Ring>) -> Value {
392 let (outers, inners): (Vec<Ring>, Vec<Ring>) = rings.into_iter().partition(|ring| ring.outer);
393
394 if outers.is_empty() {
395 let polygons: Vec<Value> = inners
398 .into_iter()
399 .map(|ring| Value::Array(vec![Value::Array(ring.oriented(true))]))
400 .collect();
401 return finish_polygons(polygons);
402 }
403
404 let mut assignments: Vec<Vec<Value>> = outers.iter().map(|_| Vec::new()).collect();
407 for inner in inners {
408 let probe = match inner.xy.first() {
409 Some(point) => *point,
410 None => continue,
411 };
412
413 let best = outers
414 .iter()
415 .enumerate()
416 .filter(|(_, outer)| outer.contains(probe))
417 .min_by(|(_, a), (_, b)| {
418 a.signed_area()
419 .abs()
420 .partial_cmp(&b.signed_area().abs())
421 .unwrap_or(std::cmp::Ordering::Equal)
422 })
423 .map(|(index, _)| index);
424
425 let target = best.unwrap_or(0);
428 assignments[target].push(Value::Array(inner.oriented(false)));
429 }
430
431 let polygons: Vec<Value> = outers
432 .into_iter()
433 .zip(assignments)
434 .map(|(outer, holes)| {
435 let mut ring_list = vec![Value::Array(outer.oriented(true))];
436 ring_list.extend(holes);
437 Value::Array(ring_list)
438 })
439 .collect();
440
441 finish_polygons(polygons)
442}
443
444fn finish_polygons(polygons: Vec<Value>) -> Value {
445 if polygons.len() == 1 {
446 geometry("Polygon", polygons.into_iter().next().unwrap())
447 } else {
448 geometry("MultiPolygon", Value::Array(polygons))
449 }
450}
451
452fn multipatch_to_geometry(shape: &Multipatch, include_m: bool, index: usize) -> Result<Value> {
456 let mut polygons: Vec<Value> = Vec::new();
457 let mut pending_outer: Option<Vec<Value>> = None;
458
459 let close = |points: &[PointZ]| -> Vec<Value> {
460 let mut positions: Vec<Value> = points.iter().map(|p| position_z(p, include_m)).collect();
461 if positions.len() > 2 && positions.first() != positions.last() {
462 positions.push(positions[0].clone());
463 }
464 positions
465 };
466
467 for patch in shape.patches() {
468 match patch {
469 Patch::TriangleStrip(points) => {
470 for triangle in points.windows(3) {
471 polygons.push(Value::Array(vec![Value::Array(close(triangle))]));
472 }
473 }
474 Patch::TriangleFan(points) => {
475 if points.len() >= 3 {
476 for pair in points[1..].windows(2) {
477 let triangle = [points[0], pair[0], pair[1]];
478 polygons.push(Value::Array(vec![Value::Array(close(&triangle))]));
479 }
480 }
481 }
482 Patch::OuterRing(points) => {
483 if let Some(previous) = pending_outer.take() {
484 polygons.push(Value::Array(vec![Value::Array(previous)]));
485 }
486 pending_outer = Some(close(points));
487 }
488 Patch::InnerRing(points) => {
489 let hole = Value::Array(close(points));
491 match polygons.last_mut() {
492 Some(Value::Array(rings)) if pending_outer.is_none() => rings.push(hole),
493 _ => {
494 if let Some(outer) = pending_outer.take() {
495 polygons.push(Value::Array(vec![Value::Array(outer), hole]));
496 }
497 }
498 }
499 }
500 Patch::FirstRing(points) | Patch::Ring(points) => {
501 if let Some(previous) = pending_outer.take() {
502 polygons.push(Value::Array(vec![Value::Array(previous)]));
503 }
504 polygons.push(Value::Array(vec![Value::Array(close(points))]));
505 }
506 }
507 }
508
509 if let Some(outer) = pending_outer.take() {
510 polygons.push(Value::Array(vec![Value::Array(outer)]));
511 }
512
513 if polygons.is_empty() {
514 return Err(ShapefileError::Feature {
515 index,
516 message: "multipatch contained no renderable patches".into(),
517 });
518 }
519
520 Ok(finish_polygons(polygons))
521}