1use crate::vector::geometry::Geometry;
6use core::fmt;
7use serde::{Deserialize, Serialize};
8use serde_json::Value as JsonValue;
9
10#[cfg(feature = "std")]
11use std::collections::HashMap;
12#[cfg(feature = "std")]
13use std::string::String;
14
15#[cfg(all(not(feature = "std"), feature = "alloc"))]
16use alloc::{
17 collections::BTreeMap as HashMap,
18 string::{String, ToString},
19};
20
21#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
23pub struct Feature {
24 pub id: Option<FeatureId>,
26 pub geometry: Option<Geometry>,
28 pub properties: HashMap<String, FieldValue>,
30}
31
32impl Feature {
33 #[must_use]
35 pub fn new(geometry: Geometry) -> Self {
36 Self {
37 id: None,
38 geometry: Some(geometry),
39 properties: HashMap::new(),
40 }
41 }
42
43 #[must_use]
45 pub fn with_id(id: FeatureId, geometry: Geometry) -> Self {
46 Self {
47 id: Some(id),
48 geometry: Some(geometry),
49 properties: HashMap::new(),
50 }
51 }
52
53 #[must_use]
55 pub fn new_attribute_only() -> Self {
56 Self {
57 id: None,
58 geometry: None,
59 properties: HashMap::new(),
60 }
61 }
62
63 pub fn set_property<S: Into<String>>(&mut self, key: S, value: FieldValue) {
65 self.properties.insert(key.into(), value);
66 }
67
68 #[must_use]
70 pub fn get_property(&self, key: &str) -> Option<&FieldValue> {
71 self.properties.get(key)
72 }
73
74 pub fn remove_property(&mut self, key: &str) -> Option<FieldValue> {
76 self.properties.remove(key)
77 }
78
79 #[must_use]
81 pub const fn has_geometry(&self) -> bool {
82 self.geometry.is_some()
83 }
84
85 #[must_use]
87 pub fn bounds(&self) -> Option<(f64, f64, f64, f64)> {
88 self.geometry
89 .as_ref()
90 .and_then(super::geometry::Geometry::bounds)
91 }
92}
93
94#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
96#[serde(untagged)]
97pub enum FeatureId {
98 Integer(i64),
100 String(String),
102}
103
104impl From<i64> for FeatureId {
105 fn from(id: i64) -> Self {
106 Self::Integer(id)
107 }
108}
109
110impl From<String> for FeatureId {
111 fn from(id: String) -> Self {
112 Self::String(id)
113 }
114}
115
116impl From<&str> for FeatureId {
117 fn from(id: &str) -> Self {
118 Self::String(id.to_string())
119 }
120}
121
122#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
135#[serde(untagged)]
136pub enum FieldValue {
137 Null,
139 Bool(bool),
141 Integer(i64),
143 UInteger(u64),
145 Float(f64),
147 String(String),
149 Array(Vec<FieldValue>),
151 Object(HashMap<String, FieldValue>),
153 #[cfg(feature = "std")]
157 Date(time::Date),
158 Blob(Vec<u8>),
160}
161
162impl FieldValue {
163 #[must_use]
165 pub const fn is_null(&self) -> bool {
166 matches!(self, Self::Null)
167 }
168
169 #[cfg(feature = "std")]
174 #[must_use]
175 pub fn to_json_value(&self) -> JsonValue {
176 match self {
177 Self::Null => JsonValue::Null,
178 Self::Bool(b) => JsonValue::Bool(*b),
179 Self::Integer(i) => JsonValue::Number((*i).into()),
180 Self::UInteger(u) => JsonValue::Number((*u).into()),
181 Self::Float(f) => {
182 JsonValue::Number(serde_json::Number::from_f64(*f).unwrap_or_else(|| 0.into()))
183 }
184 Self::String(s) => JsonValue::String(s.clone()),
185 Self::Array(arr) => JsonValue::Array(arr.iter().map(Self::to_json_value).collect()),
186 Self::Object(obj) => JsonValue::Object(
187 obj.iter()
188 .map(|(k, v)| (k.clone(), v.to_json_value()))
189 .collect(),
190 ),
191 #[cfg(feature = "std")]
192 Self::Date(d) => JsonValue::String(d.to_string()),
193 Self::Blob(bytes) => JsonValue::Array(
194 bytes
195 .iter()
196 .map(|b| JsonValue::Number(u64::from(*b).into()))
197 .collect(),
198 ),
199 }
200 }
201
202 #[cfg(feature = "std")]
206 #[must_use]
207 pub fn to_json(&self) -> JsonValue {
208 self.to_json_value()
209 }
210
211 #[cfg(feature = "std")]
217 #[must_use]
218 pub fn from_json(value: &JsonValue) -> Self {
219 match value {
220 JsonValue::Null => Self::Null,
221 JsonValue::Bool(b) => Self::Bool(*b),
222 JsonValue::Number(n) => {
223 if let Some(i) = n.as_i64() {
224 Self::Integer(i)
225 } else if let Some(u) = n.as_u64() {
226 Self::UInteger(u)
227 } else if let Some(f) = n.as_f64() {
228 Self::Float(f)
229 } else {
230 Self::Null
231 }
232 }
233 JsonValue::String(s) => Self::String(s.clone()),
234 JsonValue::Array(arr) => Self::Array(arr.iter().map(Self::from_json).collect()),
235 JsonValue::Object(obj) => Self::Object(
236 obj.iter()
237 .map(|(k, v)| (k.clone(), Self::from_json(v)))
238 .collect(),
239 ),
240 }
241 }
242
243 #[must_use]
245 pub fn as_string(&self) -> Option<&str> {
246 match self {
247 Self::String(s) => Some(s),
248 _ => None,
249 }
250 }
251
252 #[must_use]
254 pub const fn as_i64(&self) -> Option<i64> {
255 match self {
256 Self::Integer(i) => Some(*i),
257 _ => None,
258 }
259 }
260
261 #[must_use]
263 pub const fn as_u64(&self) -> Option<u64> {
264 match self {
265 Self::UInteger(u) => Some(*u),
266 _ => None,
267 }
268 }
269
270 #[must_use]
274 pub fn as_f64(&self) -> Option<f64> {
275 match self {
276 Self::Float(f) => Some(*f),
277 Self::Integer(i) => Some(*i as f64),
278 Self::UInteger(u) => Some(*u as f64),
279 _ => None,
280 }
281 }
282
283 #[must_use]
285 pub const fn as_bool(&self) -> Option<bool> {
286 match self {
287 Self::Bool(b) => Some(*b),
288 _ => None,
289 }
290 }
291
292 #[must_use]
294 pub fn as_blob(&self) -> Option<&[u8]> {
295 match self {
296 Self::Blob(b) => Some(b),
297 _ => None,
298 }
299 }
300
301 #[cfg(feature = "std")]
303 #[must_use]
304 pub const fn as_date(&self) -> Option<time::Date> {
305 match self {
306 Self::Date(d) => Some(*d),
307 _ => None,
308 }
309 }
310}
311
312impl fmt::Display for FieldValue {
313 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314 match self {
315 Self::Null => f.write_str("null"),
316 Self::Bool(b) => write!(f, "{b}"),
317 Self::Integer(i) => write!(f, "{i}"),
318 Self::UInteger(u) => write!(f, "{u}"),
319 Self::Float(v) => write!(f, "{v}"),
320 Self::String(s) => write!(f, "\"{s}\""),
321 Self::Array(arr) => {
322 write!(f, "[")?;
323 for (i, v) in arr.iter().enumerate() {
324 if i > 0 {
325 write!(f, ", ")?;
326 }
327 write!(f, "{v}")?;
328 }
329 write!(f, "]")
330 }
331 Self::Object(obj) => {
332 write!(f, "{{")?;
333 let mut first = true;
334 for (k, v) in obj.iter() {
335 if !first {
336 write!(f, ", ")?;
337 }
338 first = false;
339 write!(f, "\"{k}\": {v}")?;
340 }
341 write!(f, "}}")
342 }
343 #[cfg(feature = "std")]
344 Self::Date(d) => write!(f, "{d}"),
345 Self::Blob(b) => write!(f, "Blob({} bytes)", b.len()),
346 }
347 }
348}
349
350impl From<serde_json::Value> for FieldValue {
351 fn from(v: serde_json::Value) -> Self {
352 Self::from_json(&v)
353 }
354}
355
356impl From<bool> for FieldValue {
357 fn from(b: bool) -> Self {
358 Self::Bool(b)
359 }
360}
361
362impl From<i64> for FieldValue {
363 fn from(i: i64) -> Self {
364 Self::Integer(i)
365 }
366}
367
368impl From<i32> for FieldValue {
369 fn from(i: i32) -> Self {
370 Self::Integer(i64::from(i))
371 }
372}
373
374impl From<u64> for FieldValue {
375 fn from(u: u64) -> Self {
376 Self::UInteger(u)
377 }
378}
379
380impl From<u32> for FieldValue {
381 fn from(u: u32) -> Self {
382 Self::UInteger(u64::from(u))
383 }
384}
385
386impl From<f64> for FieldValue {
387 fn from(f: f64) -> Self {
388 Self::Float(f)
389 }
390}
391
392impl From<f32> for FieldValue {
393 fn from(f: f32) -> Self {
394 Self::Float(f64::from(f))
395 }
396}
397
398impl From<String> for FieldValue {
399 fn from(s: String) -> Self {
400 Self::String(s)
401 }
402}
403
404impl From<&str> for FieldValue {
405 fn from(s: &str) -> Self {
406 Self::String(s.to_string())
407 }
408}
409
410#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
412pub struct FeatureCollection {
413 pub features: Vec<Feature>,
415 #[serde(skip_serializing_if = "Option::is_none")]
417 pub metadata: Option<HashMap<String, FieldValue>>,
418}
419
420impl FeatureCollection {
421 #[must_use]
423 pub const fn new(features: Vec<Feature>) -> Self {
424 Self {
425 features,
426 metadata: None,
427 }
428 }
429
430 #[must_use]
432 pub const fn empty() -> Self {
433 Self {
434 features: Vec::new(),
435 metadata: None,
436 }
437 }
438
439 pub fn push(&mut self, feature: Feature) {
441 self.features.push(feature);
442 }
443
444 #[must_use]
446 pub fn len(&self) -> usize {
447 self.features.len()
448 }
449
450 #[must_use]
452 pub fn is_empty(&self) -> bool {
453 self.features.is_empty()
454 }
455
456 #[must_use]
458 pub fn bounds(&self) -> Option<(f64, f64, f64, f64)> {
459 if self.features.is_empty() {
460 return None;
461 }
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
468 for feature in &self.features {
469 if let Some((x_min, y_min, x_max, y_max)) = feature.bounds() {
470 min_x = min_x.min(x_min);
471 min_y = min_y.min(y_min);
472 max_x = max_x.max(x_max);
473 max_y = max_y.max(y_max);
474 }
475 }
476
477 if min_x.is_infinite() {
478 None
479 } else {
480 Some((min_x, min_y, max_x, max_y))
481 }
482 }
483}
484
485impl Default for FeatureCollection {
486 fn default() -> Self {
487 Self::empty()
488 }
489}
490
491#[cfg(test)]
492mod tests {
493 #![allow(clippy::expect_used)]
494
495 use super::*;
496 use crate::vector::geometry::Point;
497
498 #[test]
499 fn test_feature_creation() {
500 let point = Point::new(1.0, 2.0);
501 let mut feature = Feature::new(Geometry::Point(point));
502
503 feature.set_property("name", FieldValue::String("Test Point".to_string()));
504 feature.set_property("value", FieldValue::Integer(42));
505
506 assert!(feature.has_geometry());
507 assert_eq!(feature.properties.len(), 2);
508
509 let name = feature.get_property("name");
510 assert!(name.is_some());
511 assert_eq!(name.and_then(|v| v.as_string()), Some("Test Point"));
512
513 let value = feature.get_property("value");
514 assert!(value.is_some());
515 assert_eq!(value.and_then(|v| v.as_i64()), Some(42));
516 }
517
518 #[test]
519 fn test_feature_id() {
520 let point = Point::new(1.0, 2.0);
521 let feature = Feature::with_id(FeatureId::Integer(123), Geometry::Point(point));
522
523 assert_eq!(feature.id, Some(FeatureId::Integer(123)));
524 }
525
526 #[test]
527 fn test_field_value_conversions() {
528 let pv_int = FieldValue::from(42_i64);
529 assert_eq!(pv_int.as_i64(), Some(42));
530
531 let pv_float = FieldValue::from(2.78_f64);
532 assert_eq!(pv_float.as_f64(), Some(2.78));
533
534 let pv_bool = FieldValue::from(true);
535 assert_eq!(pv_bool.as_bool(), Some(true));
536
537 let pv_str = FieldValue::from("hello");
538 assert_eq!(pv_str.as_string(), Some("hello"));
539 }
540
541 #[test]
542 fn test_feature_collection() {
543 let mut collection = FeatureCollection::empty();
544 assert!(collection.is_empty());
545
546 let point1 = Point::new(1.0, 2.0);
547 let feature1 = Feature::new(Geometry::Point(point1));
548 collection.push(feature1);
549
550 let point2 = Point::new(3.0, 4.0);
551 let feature2 = Feature::new(Geometry::Point(point2));
552 collection.push(feature2);
553
554 assert_eq!(collection.len(), 2);
555 assert!(!collection.is_empty());
556
557 let bounds = collection.bounds();
558 assert!(bounds.is_some());
559 let (min_x, min_y, max_x, max_y) = bounds.expect("bounds calculation failed");
560 assert_eq!(min_x, 1.0);
561 assert_eq!(min_y, 2.0);
562 assert_eq!(max_x, 3.0);
563 assert_eq!(max_y, 4.0);
564 }
565
566 #[test]
567 fn test_fieldvalue_variants_exhaustive() {
568 let _ = FieldValue::Null;
569 let _ = FieldValue::Bool(true);
570 let _ = FieldValue::Integer(-1);
571 let _ = FieldValue::UInteger(1u64);
572 let _ = FieldValue::Float(1.5);
573 #[cfg(feature = "std")]
574 let _ = FieldValue::Date(
575 time::Date::from_calendar_date(2024, time::Month::January, 1).expect("valid date"),
576 );
577 let _ = FieldValue::Blob(vec![0u8]);
578 let _ = FieldValue::String("x".into());
579 let _ = FieldValue::Array(vec![]);
580 let _ = FieldValue::Object(Default::default());
581 }
582
583 #[test]
584 #[cfg(feature = "std")]
585 fn test_fieldvalue_to_json_value_all_variants() {
586 assert_eq!(FieldValue::Null.to_json_value(), serde_json::Value::Null);
587 assert_eq!(
588 FieldValue::Bool(true).to_json_value(),
589 serde_json::Value::Bool(true)
590 );
591 let blob_json = FieldValue::Blob(vec![1, 2]).to_json_value();
592 assert!(matches!(blob_json, serde_json::Value::Array(_)));
593 if let serde_json::Value::Array(a) = blob_json {
594 assert_eq!(a.len(), 2);
595 }
596 let date =
598 time::Date::from_calendar_date(2024, time::Month::March, 15).expect("valid date");
599 let json = FieldValue::Date(date).to_json_value();
600 assert!(matches!(json, serde_json::Value::String(_)));
601 if let serde_json::Value::String(s) = json {
602 assert!(s.contains("2024"), "date string should contain year");
603 }
604 }
605
606 #[test]
607 #[cfg(feature = "std")]
608 fn test_from_json_value() {
609 let json = serde_json::json!({
610 "name": "test",
611 "count": 42,
612 "flag": true,
613 "score": 3.5,
614 "items": [1, 2, 3]
615 });
616 let fv = FieldValue::from(json);
617 assert!(matches!(fv, FieldValue::Object(_)));
618 if let FieldValue::Object(map) = fv {
619 assert!(map.contains_key("name"));
620 assert!(map.contains_key("count"));
621 }
622 }
623
624 #[test]
625 #[cfg(feature = "std")]
626 fn test_blob_accessor() {
627 let fv = FieldValue::Blob(vec![0xDE, 0xAD, 0xBE, 0xEF]);
628 assert_eq!(fv.as_blob(), Some([0xDE, 0xAD, 0xBE, 0xEFu8].as_ref()));
629 }
630
631 #[test]
632 fn test_display_fieldvalue_each_variant() {
633 assert_eq!(FieldValue::Null.to_string(), "null");
634 assert_eq!(FieldValue::Bool(true).to_string(), "true");
635 assert_eq!(FieldValue::Bool(false).to_string(), "false");
636 assert_eq!(FieldValue::Integer(-42).to_string(), "-42");
637 assert_eq!(FieldValue::UInteger(99).to_string(), "99");
638 assert_eq!(FieldValue::String("hello".into()).to_string(), "\"hello\"");
639 assert_eq!(FieldValue::Blob(vec![1, 2, 3]).to_string(), "Blob(3 bytes)");
640 assert_eq!(
641 FieldValue::Array(vec![FieldValue::Integer(1)]).to_string(),
642 "[1]"
643 );
644 }
645}