1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub enum FieldType {
10 Integer,
12 Real,
14 Text,
16 Blob,
18 Boolean,
20 Date,
22 DateTime,
24 Null,
26}
27
28impl FieldType {
29 pub fn from_sql_type(type_str: &str) -> Self {
34 match type_str.to_ascii_uppercase().trim() {
35 "INTEGER" | "INT" | "TINYINT" | "SMALLINT" | "MEDIUMINT" | "BIGINT"
36 | "UNSIGNED BIG INT" | "INT2" | "INT8" => Self::Integer,
37 "REAL" | "DOUBLE" | "DOUBLE PRECISION" | "FLOAT" | "NUMERIC" | "DECIMAL" => Self::Real,
38 "BLOB" => Self::Blob,
39 "BOOLEAN" | "BOOL" => Self::Boolean,
40 "DATE" => Self::Date,
41 "DATETIME" | "TIMESTAMP" => Self::DateTime,
42 "NULL" => Self::Null,
43 _ => Self::Text,
44 }
45 }
46
47 pub fn as_str(self) -> &'static str {
49 match self {
50 Self::Integer => "INTEGER",
51 Self::Real => "REAL",
52 Self::Text => "TEXT",
53 Self::Blob => "BLOB",
54 Self::Boolean => "BOOLEAN",
55 Self::Date => "DATE",
56 Self::DateTime => "DATETIME",
57 Self::Null => "NULL",
58 }
59 }
60}
61
62#[derive(Debug, Clone, PartialEq)]
68pub enum FieldValue {
69 Integer(i64),
71 Real(f64),
73 Text(String),
75 Blob(Vec<u8>),
77 Boolean(bool),
79 Null,
81}
82
83impl FieldValue {
84 pub fn as_integer(&self) -> Option<i64> {
86 match self {
87 Self::Integer(v) => Some(*v),
88 _ => None,
89 }
90 }
91
92 pub fn as_real(&self) -> Option<f64> {
94 match self {
95 Self::Real(v) => Some(*v),
96 _ => None,
97 }
98 }
99
100 pub fn as_text(&self) -> Option<&str> {
102 match self {
103 Self::Text(s) => Some(s.as_str()),
104 _ => None,
105 }
106 }
107
108 pub fn as_bool(&self) -> Option<bool> {
110 match self {
111 Self::Boolean(b) => Some(*b),
112 _ => None,
113 }
114 }
115
116 pub fn is_null(&self) -> bool {
118 matches!(self, Self::Null)
119 }
120
121 pub fn field_type(&self) -> FieldType {
123 match self {
124 Self::Integer(_) => FieldType::Integer,
125 Self::Real(_) => FieldType::Real,
126 Self::Text(_) => FieldType::Text,
127 Self::Blob(_) => FieldType::Blob,
128 Self::Boolean(_) => FieldType::Boolean,
129 Self::Null => FieldType::Null,
130 }
131 }
132
133 pub(crate) fn to_json(&self) -> String {
135 match self {
136 Self::Integer(v) => v.to_string(),
137 Self::Real(v) => {
138 if v.is_finite() {
139 format!("{v}")
140 } else {
141 "null".into()
142 }
143 }
144 Self::Text(s) => json_string_escape(s),
145 Self::Blob(b) => {
146 let hex: String = b.iter().map(|byte| format!("{byte:02x}")).collect();
148 json_string_escape(&format!("0x{hex}"))
149 }
150 Self::Boolean(b) => if *b { "true" } else { "false" }.into(),
151 Self::Null => "null".into(),
152 }
153 }
154}
155
156#[derive(Debug, Clone, PartialEq)]
162pub struct FieldDefinition {
163 pub name: String,
165 pub field_type: FieldType,
167 pub not_null: bool,
169 pub primary_key: bool,
171 pub default_value: Option<String>,
173}
174
175#[derive(Debug, Clone, Copy, PartialEq, Eq)]
183pub enum CoordDim {
184 XY,
186 XYZ,
188 XYM,
190 XYZM,
192}
193
194#[derive(Debug, Clone, PartialEq)]
199pub struct Point4D {
200 pub x: f64,
202 pub y: f64,
204 pub z: Option<f64>,
206 pub m: Option<f64>,
208}
209
210impl Point4D {
211 pub fn to_xy(&self) -> (f64, f64) {
213 (self.x, self.y)
214 }
215
216 pub fn to_xyz(&self) -> (f64, f64, f64) {
218 (self.x, self.y, self.z.unwrap_or(0.0))
219 }
220}
221
222#[derive(Debug, Clone, PartialEq)]
232pub enum GpkgGeometry {
233 Point {
235 x: f64,
237 y: f64,
239 },
240 LineString {
242 coords: Vec<(f64, f64)>,
244 },
245 Polygon {
247 rings: Vec<Vec<(f64, f64)>>,
249 },
250 MultiPoint {
252 points: Vec<(f64, f64)>,
254 },
255 MultiLineString {
257 lines: Vec<Vec<(f64, f64)>>,
259 },
260 MultiPolygon {
262 polygons: Vec<Vec<Vec<(f64, f64)>>>,
264 },
265 GeometryCollection(Vec<GpkgGeometry>),
267 PointZ {
269 x: f64,
271 y: f64,
273 z: f64,
275 },
276 LineStringZ {
278 coords: Vec<(f64, f64, f64)>,
280 },
281 PolygonZ {
283 rings: Vec<Vec<(f64, f64, f64)>>,
285 },
286 MultiPointZ {
288 points: Vec<(f64, f64, f64)>,
290 },
291 MultiLineStringZ {
293 lines: Vec<Vec<(f64, f64, f64)>>,
295 },
296 MultiPolygonZ {
298 polygons: Vec<Vec<Vec<(f64, f64, f64)>>>,
300 },
301 GeometryCollectionZ(Vec<GpkgGeometry>),
303 PointM {
305 x: f64,
307 y: f64,
309 m: f64,
311 },
312 LineStringM {
314 coords: Vec<(f64, f64, f64)>,
316 },
317 PolygonM {
319 rings: Vec<Vec<(f64, f64, f64)>>,
321 },
322 MultiPointM {
324 points: Vec<(f64, f64, f64)>,
326 },
327 MultiLineStringM {
329 lines: Vec<Vec<(f64, f64, f64)>>,
331 },
332 MultiPolygonM {
334 polygons: Vec<Vec<Vec<(f64, f64, f64)>>>,
336 },
337 GeometryCollectionM(Vec<GpkgGeometry>),
339 PointZM(Point4D),
341 LineStringZM {
343 coords: Vec<Point4D>,
345 },
346 PolygonZM {
348 rings: Vec<Vec<Point4D>>,
350 },
351 MultiPointZM {
353 points: Vec<Point4D>,
355 },
356 MultiLineStringZM {
358 lines: Vec<Vec<Point4D>>,
360 },
361 MultiPolygonZM {
363 polygons: Vec<Vec<Vec<Point4D>>>,
365 },
366 GeometryCollectionZM(Vec<GpkgGeometry>),
368 Empty,
370}
371
372impl GpkgGeometry {
373 pub fn geometry_type(&self) -> &'static str {
375 match self {
376 Self::Point { .. } => "Point",
377 Self::LineString { .. } => "LineString",
378 Self::Polygon { .. } => "Polygon",
379 Self::MultiPoint { .. } => "MultiPoint",
380 Self::MultiLineString { .. } => "MultiLineString",
381 Self::MultiPolygon { .. } => "MultiPolygon",
382 Self::GeometryCollection(_) => "GeometryCollection",
383 Self::PointZ { .. } => "PointZ",
384 Self::LineStringZ { .. } => "LineStringZ",
385 Self::PolygonZ { .. } => "PolygonZ",
386 Self::MultiPointZ { .. } => "MultiPointZ",
387 Self::MultiLineStringZ { .. } => "MultiLineStringZ",
388 Self::MultiPolygonZ { .. } => "MultiPolygonZ",
389 Self::GeometryCollectionZ(_) => "GeometryCollectionZ",
390 Self::PointM { .. } => "PointM",
391 Self::LineStringM { .. } => "LineStringM",
392 Self::PolygonM { .. } => "PolygonM",
393 Self::MultiPointM { .. } => "MultiPointM",
394 Self::MultiLineStringM { .. } => "MultiLineStringM",
395 Self::MultiPolygonM { .. } => "MultiPolygonM",
396 Self::GeometryCollectionM(_) => "GeometryCollectionM",
397 Self::PointZM(_) => "PointZM",
398 Self::LineStringZM { .. } => "LineStringZM",
399 Self::PolygonZM { .. } => "PolygonZM",
400 Self::MultiPointZM { .. } => "MultiPointZM",
401 Self::MultiLineStringZM { .. } => "MultiLineStringZM",
402 Self::MultiPolygonZM { .. } => "MultiPolygonZM",
403 Self::GeometryCollectionZM(_) => "GeometryCollectionZM",
404 Self::Empty => "Empty",
405 }
406 }
407
408 pub fn point_count(&self) -> usize {
410 match self {
411 Self::Point { .. } | Self::PointZ { .. } | Self::PointM { .. } | Self::PointZM(_) => 1,
412 Self::LineString { coords } => coords.len(),
413 Self::LineStringZ { coords } => coords.len(),
414 Self::LineStringM { coords } => coords.len(),
415 Self::LineStringZM { coords } => coords.len(),
416 Self::Polygon { rings } => rings.iter().map(|r| r.len()).sum(),
417 Self::PolygonZ { rings } => rings.iter().map(|r| r.len()).sum(),
418 Self::PolygonM { rings } => rings.iter().map(|r| r.len()).sum(),
419 Self::PolygonZM { rings } => rings.iter().map(|r| r.len()).sum(),
420 Self::MultiPoint { points } => points.len(),
421 Self::MultiPointZ { points } => points.len(),
422 Self::MultiPointM { points } => points.len(),
423 Self::MultiPointZM { points } => points.len(),
424 Self::MultiLineString { lines } => lines.iter().map(|l| l.len()).sum(),
425 Self::MultiLineStringZ { lines } => lines.iter().map(|l| l.len()).sum(),
426 Self::MultiLineStringM { lines } => lines.iter().map(|l| l.len()).sum(),
427 Self::MultiLineStringZM { lines } => lines.iter().map(|l| l.len()).sum(),
428 Self::MultiPolygon { polygons } => polygons
429 .iter()
430 .flat_map(|poly| poly.iter())
431 .map(|ring| ring.len())
432 .sum(),
433 Self::MultiPolygonZ { polygons } => polygons
434 .iter()
435 .flat_map(|poly| poly.iter())
436 .map(|ring| ring.len())
437 .sum(),
438 Self::MultiPolygonM { polygons } => polygons
439 .iter()
440 .flat_map(|poly| poly.iter())
441 .map(|ring| ring.len())
442 .sum(),
443 Self::MultiPolygonZM { polygons } => polygons
444 .iter()
445 .flat_map(|poly| poly.iter())
446 .map(|ring| ring.len())
447 .sum(),
448 Self::GeometryCollection(geoms)
449 | Self::GeometryCollectionZ(geoms)
450 | Self::GeometryCollectionM(geoms)
451 | Self::GeometryCollectionZM(geoms) => geoms.iter().map(|g| g.point_count()).sum(),
452 Self::Empty => 0,
453 }
454 }
455
456 pub fn bbox(&self) -> Option<(f64, f64, f64, f64)> {
459 let coords: Vec<(f64, f64)> = self.collect_coords();
460 if coords.is_empty() {
461 return None;
462 }
463 let mut min_x = f64::INFINITY;
464 let mut min_y = f64::INFINITY;
465 let mut max_x = f64::NEG_INFINITY;
466 let mut max_y = f64::NEG_INFINITY;
467 for (x, y) in &coords {
468 if *x < min_x {
469 min_x = *x;
470 }
471 if *y < min_y {
472 min_y = *y;
473 }
474 if *x > max_x {
475 max_x = *x;
476 }
477 if *y > max_y {
478 max_y = *y;
479 }
480 }
481 if min_x.is_finite() {
482 Some((min_x, min_y, max_x, max_y))
483 } else {
484 None
485 }
486 }
487
488 fn collect_coords(&self) -> Vec<(f64, f64)> {
490 match self {
491 Self::Point { x, y } => vec![(*x, *y)],
492 Self::PointZ { x, y, .. } => vec![(*x, *y)],
493 Self::PointM { x, y, .. } => vec![(*x, *y)],
494 Self::PointZM(p) => vec![(p.x, p.y)],
495 Self::LineString { coords } => coords.clone(),
496 Self::LineStringZ { coords } => coords.iter().map(|(x, y, _)| (*x, *y)).collect(),
497 Self::LineStringM { coords } => coords.iter().map(|(x, y, _)| (*x, *y)).collect(),
498 Self::LineStringZM { coords } => coords.iter().map(|p| (p.x, p.y)).collect(),
499 Self::Polygon { rings } => rings.iter().flatten().copied().collect(),
500 Self::PolygonZ { rings } => rings.iter().flatten().map(|(x, y, _)| (*x, *y)).collect(),
501 Self::PolygonM { rings } => rings.iter().flatten().map(|(x, y, _)| (*x, *y)).collect(),
502 Self::PolygonZM { rings } => rings.iter().flatten().map(|p| (p.x, p.y)).collect(),
503 Self::MultiPoint { points } => points.clone(),
504 Self::MultiPointZ { points } => points.iter().map(|(x, y, _)| (*x, *y)).collect(),
505 Self::MultiPointM { points } => points.iter().map(|(x, y, _)| (*x, *y)).collect(),
506 Self::MultiPointZM { points } => points.iter().map(|p| (p.x, p.y)).collect(),
507 Self::MultiLineString { lines } => lines.iter().flatten().copied().collect(),
508 Self::MultiLineStringZ { lines } => {
509 lines.iter().flatten().map(|(x, y, _)| (*x, *y)).collect()
510 }
511 Self::MultiLineStringM { lines } => {
512 lines.iter().flatten().map(|(x, y, _)| (*x, *y)).collect()
513 }
514 Self::MultiLineStringZM { lines } => {
515 lines.iter().flatten().map(|p| (p.x, p.y)).collect()
516 }
517 Self::MultiPolygon { polygons } => polygons
518 .iter()
519 .flat_map(|poly| poly.iter().flatten())
520 .copied()
521 .collect(),
522 Self::MultiPolygonZ { polygons } => polygons
523 .iter()
524 .flat_map(|poly| poly.iter().flatten())
525 .map(|(x, y, _)| (*x, *y))
526 .collect(),
527 Self::MultiPolygonM { polygons } => polygons
528 .iter()
529 .flat_map(|poly| poly.iter().flatten())
530 .map(|(x, y, _)| (*x, *y))
531 .collect(),
532 Self::MultiPolygonZM { polygons } => polygons
533 .iter()
534 .flat_map(|poly| poly.iter().flatten())
535 .map(|p| (p.x, p.y))
536 .collect(),
537 Self::GeometryCollection(geoms)
538 | Self::GeometryCollectionZ(geoms)
539 | Self::GeometryCollectionM(geoms)
540 | Self::GeometryCollectionZM(geoms) => {
541 geoms.iter().flat_map(|g| g.collect_coords()).collect()
542 }
543 Self::Empty => vec![],
544 }
545 }
546
547 pub(crate) fn to_geojson_geometry(&self) -> String {
551 match self {
552 Self::Point { x, y } => {
553 format!(r#"{{"type":"Point","coordinates":[{x},{y}]}}"#)
554 }
555 Self::PointZ { x, y, z } => {
556 format!(r#"{{"type":"Point","coordinates":[{x},{y},{z}]}}"#)
557 }
558 Self::LineString { coords } => {
559 let pts = coords_to_json_array(coords);
560 format!(r#"{{"type":"LineString","coordinates":{pts}}}"#)
561 }
562 Self::LineStringZ { coords } => {
563 let pts = coords_z_to_json_array(coords);
564 format!(r#"{{"type":"LineString","coordinates":{pts}}}"#)
565 }
566 Self::Polygon { rings } => {
567 let rings_json = rings
568 .iter()
569 .map(|r| coords_to_json_array(r))
570 .collect::<Vec<_>>()
571 .join(",");
572 format!(r#"{{"type":"Polygon","coordinates":[{rings_json}]}}"#)
573 }
574 Self::PolygonZ { rings } => {
575 let rings_json = rings
576 .iter()
577 .map(|r| coords_z_to_json_array(r))
578 .collect::<Vec<_>>()
579 .join(",");
580 format!(r#"{{"type":"Polygon","coordinates":[{rings_json}]}}"#)
581 }
582 Self::MultiPoint { points } => {
583 let pts = coords_to_json_array(points);
584 format!(r#"{{"type":"MultiPoint","coordinates":{pts}}}"#)
585 }
586 Self::MultiPointZ { points } => {
587 let pts = coords_z_to_json_array(points);
588 format!(r#"{{"type":"MultiPoint","coordinates":{pts}}}"#)
589 }
590 Self::MultiLineString { lines } => {
591 let lines_json = lines
592 .iter()
593 .map(|l| coords_to_json_array(l))
594 .collect::<Vec<_>>()
595 .join(",");
596 format!(r#"{{"type":"MultiLineString","coordinates":[{lines_json}]}}"#)
597 }
598 Self::MultiLineStringZ { lines } => {
599 let lines_json = lines
600 .iter()
601 .map(|l| coords_z_to_json_array(l))
602 .collect::<Vec<_>>()
603 .join(",");
604 format!(r#"{{"type":"MultiLineString","coordinates":[{lines_json}]}}"#)
605 }
606 Self::MultiPolygon { polygons } => {
607 let polys_json = polygons
608 .iter()
609 .map(|poly| {
610 let rings_json = poly
611 .iter()
612 .map(|r| coords_to_json_array(r))
613 .collect::<Vec<_>>()
614 .join(",");
615 format!("[{rings_json}]")
616 })
617 .collect::<Vec<_>>()
618 .join(",");
619 format!(r#"{{"type":"MultiPolygon","coordinates":[{polys_json}]}}"#)
620 }
621 Self::MultiPolygonZ { polygons } => {
622 let polys_json = polygons
623 .iter()
624 .map(|poly| {
625 let rings_json = poly
626 .iter()
627 .map(|r| coords_z_to_json_array(r))
628 .collect::<Vec<_>>()
629 .join(",");
630 format!("[{rings_json}]")
631 })
632 .collect::<Vec<_>>()
633 .join(",");
634 format!(r#"{{"type":"MultiPolygon","coordinates":[{polys_json}]}}"#)
635 }
636 Self::GeometryCollection(geoms)
637 | Self::GeometryCollectionZ(geoms)
638 | Self::GeometryCollectionM(geoms)
639 | Self::GeometryCollectionZM(geoms) => {
640 let geom_json = geoms
641 .iter()
642 .map(|g| g.to_geojson_geometry())
643 .collect::<Vec<_>>()
644 .join(",");
645 format!(r#"{{"type":"GeometryCollection","geometries":[{geom_json}]}}"#)
646 }
647 Self::PointM { x, y, .. } => {
649 format!(r#"{{"type":"Point","coordinates":[{x},{y}]}}"#)
650 }
651 Self::LineStringM { coords } => {
652 let pts = coords_to_json_array(
653 &coords.iter().map(|(x, y, _)| (*x, *y)).collect::<Vec<_>>(),
654 );
655 format!(r#"{{"type":"LineString","coordinates":{pts}}}"#)
656 }
657 Self::PolygonM { rings } => {
658 let rings_json = rings
659 .iter()
660 .map(|r| {
661 coords_to_json_array(
662 &r.iter().map(|(x, y, _)| (*x, *y)).collect::<Vec<_>>(),
663 )
664 })
665 .collect::<Vec<_>>()
666 .join(",");
667 format!(r#"{{"type":"Polygon","coordinates":[{rings_json}]}}"#)
668 }
669 Self::MultiPointM { points } => {
670 let pts = coords_to_json_array(
671 &points.iter().map(|(x, y, _)| (*x, *y)).collect::<Vec<_>>(),
672 );
673 format!(r#"{{"type":"MultiPoint","coordinates":{pts}}}"#)
674 }
675 Self::MultiLineStringM { lines } => {
676 let lines_json = lines
677 .iter()
678 .map(|l| {
679 coords_to_json_array(
680 &l.iter().map(|(x, y, _)| (*x, *y)).collect::<Vec<_>>(),
681 )
682 })
683 .collect::<Vec<_>>()
684 .join(",");
685 format!(r#"{{"type":"MultiLineString","coordinates":[{lines_json}]}}"#)
686 }
687 Self::MultiPolygonM { polygons } => {
688 let polys_json = polygons
689 .iter()
690 .map(|poly| {
691 let rings_json = poly
692 .iter()
693 .map(|r| {
694 coords_to_json_array(
695 &r.iter().map(|(x, y, _)| (*x, *y)).collect::<Vec<_>>(),
696 )
697 })
698 .collect::<Vec<_>>()
699 .join(",");
700 format!("[{rings_json}]")
701 })
702 .collect::<Vec<_>>()
703 .join(",");
704 format!(r#"{{"type":"MultiPolygon","coordinates":[{polys_json}]}}"#)
705 }
706 Self::PointZM(p) => {
708 let (x, y, z) = p.to_xyz();
709 format!(r#"{{"type":"Point","coordinates":[{x},{y},{z}]}}"#)
710 }
711 Self::LineStringZM { coords } => {
712 let pts =
713 coords_z_to_json_array(&coords.iter().map(|p| p.to_xyz()).collect::<Vec<_>>());
714 format!(r#"{{"type":"LineString","coordinates":{pts}}}"#)
715 }
716 Self::PolygonZM { rings } => {
717 let rings_json = rings
718 .iter()
719 .map(|r| {
720 coords_z_to_json_array(&r.iter().map(|p| p.to_xyz()).collect::<Vec<_>>())
721 })
722 .collect::<Vec<_>>()
723 .join(",");
724 format!(r#"{{"type":"Polygon","coordinates":[{rings_json}]}}"#)
725 }
726 Self::MultiPointZM { points } => {
727 let pts =
728 coords_z_to_json_array(&points.iter().map(|p| p.to_xyz()).collect::<Vec<_>>());
729 format!(r#"{{"type":"MultiPoint","coordinates":{pts}}}"#)
730 }
731 Self::MultiLineStringZM { lines } => {
732 let lines_json = lines
733 .iter()
734 .map(|l| {
735 coords_z_to_json_array(&l.iter().map(|p| p.to_xyz()).collect::<Vec<_>>())
736 })
737 .collect::<Vec<_>>()
738 .join(",");
739 format!(r#"{{"type":"MultiLineString","coordinates":[{lines_json}]}}"#)
740 }
741 Self::MultiPolygonZM { polygons } => {
742 let polys_json = polygons
743 .iter()
744 .map(|poly| {
745 let rings_json = poly
746 .iter()
747 .map(|r| {
748 coords_z_to_json_array(
749 &r.iter().map(|p| p.to_xyz()).collect::<Vec<_>>(),
750 )
751 })
752 .collect::<Vec<_>>()
753 .join(",");
754 format!("[{rings_json}]")
755 })
756 .collect::<Vec<_>>()
757 .join(",");
758 format!(r#"{{"type":"MultiPolygon","coordinates":[{polys_json}]}}"#)
759 }
760 Self::Empty => "null".into(),
761 }
762 }
763}
764
765pub(crate) fn json_string_escape(s: &str) -> String {
771 let mut out = String::with_capacity(s.len() + 2);
772 out.push('"');
773 for ch in s.chars() {
774 match ch {
775 '"' => out.push_str("\\\""),
776 '\\' => out.push_str("\\\\"),
777 '\n' => out.push_str("\\n"),
778 '\r' => out.push_str("\\r"),
779 '\t' => out.push_str("\\t"),
780 c if (c as u32) < 0x20 => {
781 out.push_str(&format!("\\u{:04x}", c as u32));
782 }
783 c => out.push(c),
784 }
785 }
786 out.push('"');
787 out
788}
789
790pub(crate) fn coords_to_json_array(coords: &[(f64, f64)]) -> String {
792 let inner: String = coords
793 .iter()
794 .map(|(x, y)| format!("[{x},{y}]"))
795 .collect::<Vec<_>>()
796 .join(",");
797 format!("[{inner}]")
798}
799
800pub(crate) fn coords_z_to_json_array(coords: &[(f64, f64, f64)]) -> String {
802 let inner: String = coords
803 .iter()
804 .map(|(x, y, z)| format!("[{x},{y},{z}]"))
805 .collect::<Vec<_>>()
806 .join(",");
807 format!("[{inner}]")
808}