Skip to main content

cityjson_types/v2_0/
metadata.rs

1//! `CityJSON` metadata fields.
2//!
3//! [`Metadata`] corresponds to the optional `metadata` member of a `CityJSON` object.
4//! All fields are optional. The spec defines these fields, aligned with ISO 19115:
5//!
6//! | Field | Type | Notes |
7//! |---|---|---|
8//! | `geographicalExtent` | [`BBox`] | `[minx, miny, minz, maxx, maxy, maxz]` |
9//! | `identifier` | [`CityModelIdentifier`] | e.g. a UUID |
10//! | `referenceDate` | [`Date`] | `YYYY-MM-DD` (RFC 3339 full-date) |
11//! | `referenceSystem` | [`CRS`] | OGC CRS URL, e.g. `https://www.opengis.net/def/crs/EPSG/0/7415` |
12//! | `title` | `String` | Human-readable dataset name |
13//! | `pointOfContact` | [`Contact`] | Contact information |
14//!
15//! ```rust
16//! use cityjson_types::CityModelType;
17//! use cityjson_types::v2_0::{BBox, CRS, CityModelIdentifier, Date, OwnedCityModel};
18//!
19//! let mut model = OwnedCityModel::new(CityModelType::CityJSON);
20//! let meta = model.metadata_mut();
21//!
22//! meta.set_geographical_extent(BBox::new(84710.1, 446846.0, -5.3, 84757.1, 446944.0, 40.9));
23//! meta.set_identifier(CityModelIdentifier::new("eaeceeaa-3f66-429a-b81d-bbc6140b8c1c".to_string()));
24//! meta.set_reference_date(Date::new("1977-02-28".to_string()));
25//! meta.set_reference_system(CRS::new("https://www.opengis.net/def/crs/EPSG/0/7415".to_string()));
26//! meta.set_title("Amsterdam buildings LoD2".to_string());
27//! ```
28
29use crate::format_option;
30use crate::resources::storage::StringStorage;
31use crate::v2_0::attributes::Attributes;
32use std::fmt::{Display, Formatter};
33
34pub use crate::cityjson::core::metadata::{BBox, CRS, CityModelIdentifier, Date};
35
36/// Metadata for a `CityJSON` document. See the [module docs](self) for field descriptions.
37#[derive(Clone, Default, Debug, PartialEq)]
38pub struct Metadata<SS: StringStorage> {
39    geographical_extent: Option<BBox>,
40    identifier: Option<CityModelIdentifier<SS>>,
41    point_of_contact: Option<Contact<SS>>,
42    reference_date: Option<Date<SS>>,
43    reference_system: Option<CRS<SS>>,
44    title: Option<SS::String>,
45    extra: Option<Attributes<SS>>,
46}
47
48impl<SS: StringStorage> Metadata<SS> {
49    #[must_use]
50    pub fn new() -> Self {
51        Self::default()
52    }
53
54    pub fn geographical_extent(&self) -> Option<&BBox> {
55        self.geographical_extent.as_ref()
56    }
57
58    pub fn identifier(&self) -> Option<&CityModelIdentifier<SS>> {
59        self.identifier.as_ref()
60    }
61
62    pub fn reference_date(&self) -> Option<&Date<SS>> {
63        self.reference_date.as_ref()
64    }
65
66    pub fn reference_system(&self) -> Option<&CRS<SS>> {
67        self.reference_system.as_ref()
68    }
69
70    pub fn title(&self) -> Option<&str> {
71        self.title.as_deref()
72    }
73
74    pub fn extra(&self) -> Option<&Attributes<SS>> {
75        self.extra.as_ref()
76    }
77
78    /// Returns a mutable reference to the extra attributes, inserting an empty map if absent.
79    pub fn extra_mut(&mut self) -> &mut Attributes<SS> {
80        self.extra.get_or_insert_with(Attributes::new)
81    }
82
83    pub fn set_extra(&mut self, extra: Option<Attributes<SS>>) {
84        self.extra = extra;
85    }
86
87    pub fn set_geographical_extent(&mut self, bbox: BBox) {
88        self.geographical_extent = Some(bbox);
89    }
90
91    pub fn set_identifier(&mut self, identifier: CityModelIdentifier<SS>) {
92        self.identifier = Some(identifier);
93    }
94
95    pub fn set_reference_date(&mut self, date: Date<SS>) {
96        self.reference_date = Some(date);
97    }
98
99    pub fn set_reference_system(&mut self, crs: CRS<SS>) {
100        self.reference_system = Some(crs);
101    }
102
103    pub fn set_title(&mut self, title: SS::String) {
104        self.title = Some(title);
105    }
106
107    pub fn point_of_contact(&self) -> Option<&Contact<SS>> {
108        self.point_of_contact.as_ref()
109    }
110
111    pub fn set_point_of_contact(&mut self, contact: Option<Contact<SS>>) {
112        self.point_of_contact = contact;
113    }
114}
115
116impl<SS: StringStorage> std::fmt::Display for Metadata<SS> {
117    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118        write!(
119            f,
120            "geographical_extent: {}, identifier: {}, point_of_contact: {}, reference_date: {}, reference_system: {}, title: {}",
121            format_option(self.geographical_extent.as_ref()),
122            format_option(self.identifier.as_ref()),
123            format_option(self.point_of_contact.as_ref()),
124            format_option(self.reference_date.as_ref()),
125            format_option(self.reference_system.as_ref()),
126            format_option(self.title.as_ref())
127        )
128    }
129}
130
131/// Point-of-contact information within [`Metadata`].
132///
133/// Corresponds to the `pointOfContact` field in the `CityJSON` spec.
134#[derive(Clone, Default, Debug, PartialEq)]
135pub struct Contact<SS: StringStorage> {
136    name: SS::String,
137    email_address: SS::String,
138    role: Option<ContactRole>,
139    website: Option<SS::String>,
140    kind: Option<ContactType>,
141    address: Option<Attributes<SS>>,
142    phone: Option<SS::String>,
143    organization: Option<SS::String>,
144}
145
146impl<SS: StringStorage> Contact<SS> {
147    #[must_use]
148    pub fn new() -> Self {
149        Self::default()
150    }
151
152    #[must_use]
153    pub fn contact_name(&self) -> &str {
154        self.name.as_ref()
155    }
156
157    #[must_use]
158    pub fn email_address(&self) -> &str {
159        self.email_address.as_ref()
160    }
161
162    #[must_use]
163    pub fn role(&self) -> Option<ContactRole> {
164        self.role
165    }
166
167    #[must_use]
168    pub fn website(&self) -> &Option<SS::String> {
169        &self.website
170    }
171
172    #[must_use]
173    pub fn contact_type(&self) -> Option<ContactType> {
174        self.kind
175    }
176
177    #[must_use]
178    pub fn phone(&self) -> &Option<SS::String> {
179        &self.phone
180    }
181
182    #[must_use]
183    pub fn organization(&self) -> &Option<SS::String> {
184        &self.organization
185    }
186
187    pub fn set_contact_name(&mut self, contact_name: SS::String) {
188        self.name = contact_name;
189    }
190
191    pub fn set_email_address(&mut self, email_address: SS::String) {
192        self.email_address = email_address;
193    }
194
195    pub fn set_role(&mut self, role: Option<ContactRole>) {
196        self.role = role;
197    }
198
199    pub fn set_website(&mut self, website: Option<SS::String>) {
200        self.website = website;
201    }
202
203    pub fn set_contact_type(&mut self, contact_type: Option<ContactType>) {
204        self.kind = contact_type;
205    }
206
207    pub fn set_phone(&mut self, phone: Option<SS::String>) {
208        self.phone = phone;
209    }
210
211    pub fn set_organization(&mut self, organization: Option<SS::String>) {
212        self.organization = organization;
213    }
214
215    #[must_use]
216    pub fn address(&self) -> Option<&Attributes<SS>> {
217        self.address.as_ref()
218    }
219
220    pub fn address_mut(&mut self) -> &mut Attributes<SS> {
221        self.address.get_or_insert_with(Attributes::new)
222    }
223
224    pub fn set_address(&mut self, address: Option<Attributes<SS>>) {
225        self.address = address;
226    }
227}
228
229impl<SS: StringStorage> Display for Contact<SS> {
230    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
231        write!(
232            f,
233            "contact_name: {}, email_address: {}, role: {}, website: {}, contact_type: {}, address: {}, phone: {}, organization: {}",
234            self.name,
235            self.email_address,
236            format_option(self.role.as_ref()),
237            format_option(self.website.as_ref()),
238            format_option(self.kind.as_ref()),
239            format_option(self.address.as_ref()),
240            format_option(self.phone.as_ref()),
241            format_option(self.organization.as_ref())
242        )
243    }
244}
245
246/// Role of the point of contact, as defined in ISO 19115.
247#[repr(C)]
248#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
249pub enum ContactRole {
250    Author,
251    CoAuthor,
252    Processor,
253    PointOfContact,
254    Owner,
255    User,
256    Distributor,
257    Originator,
258    Custodian,
259    ResourceProvider,
260    RightsHolder,
261    Sponsor,
262    PrincipalInvestigator,
263    Stakeholder,
264    Publisher,
265}
266
267impl Display for ContactRole {
268    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
269        write!(f, "{self:?}")
270    }
271}
272
273/// Whether the point of contact is an individual or an organization.
274#[repr(C)]
275#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
276pub enum ContactType {
277    Individual,
278    Organization,
279}
280
281impl Display for ContactType {
282    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
283        write!(f, "{self:?}")
284    }
285}