1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use serde_repr::{Deserialize_repr, Serialize_repr};
use serde_with::DisplayFromStr;

use crate::common::Links;

use super::{BoundingBox2D, Crs, Point2D, TitleDescriptionKeywords};

#[serde_with::serde_as]
#[serde_with::skip_serializing_none]
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TileSets {
    pub tilesets: Vec<TileSetItem>,
    pub links: Option<Links>,
}

/// A minimal tileset element for use within a list of tilesets linking to
/// full description of those tilesets.
#[serde_with::serde_as]
#[serde_with::skip_serializing_none]
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TileSetItem {
    pub title: Option<String>,
    pub data_type: DataType,
    #[serde_as(as = "DisplayFromStr")]
    pub crs: Crs,
    #[serde(rename = "tileMatrixSetURI")]
    pub tile_matrix_set_uri: Option<String>,
    pub links: Links,
}

/// A resource describing a tileset based on the OGC TileSet Metadata Standard.
/// At least one of the 'TileMatrixSet',  or a link with 'rel' tiling-scheme"
#[serde_with::serde_as]
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TileSet {
    #[serde(flatten)]
    pub title_description_keywords: TitleDescriptionKeywords,
    /// Type of data represented in the tileset
    pub data_type: DataType,
    /// Reference to a Tile Matrix Set on the OGC NA definition server
    /// (<http://www.opengis.net/def/tms/>). Required if the tile matrix set is
    /// registered on the definition server.
    #[serde(rename = "tileMatrixSetURI")]
    pub tile_matrix_set_uri: Option<String>,
    /// Limits for the TileRow and TileCol values for each TileMatrix in the
    /// TileMatrixSet. If missing, there are no limits other that the ones
    /// imposed by the TileMatrixSet. If present the TileMatrices listed are
    /// limited and the rest not available at all
    pub tile_matrix_set_limits: Option<Vec<TileMatrixLimits>>,
    /// Coordinate Reference System (CRS)
    #[serde_as(as = "DisplayFromStr")]
    pub crs: Crs,
    /// Epoch of the Coordinate Reference System (CRS)
    pub epoch: Option<f64>,
    /// Links to related resources. Possible link 'rel' values are: 'dataset'
    /// for a URL pointing to the dataset, 'tiles' for a URL template to get
    /// the tiles; 'alternate' for a URL pointing to another representation of
    /// the TileSetMetadata (e.g a TileJSON file); 'tiling-scheme' for a
    /// definition of the TileMatrixSet
    pub links: Links,
    pub layers: Option<Vec<GeospatialData>>,
    /// Minimum bounding rectangle surrounding the tile matrix set, in the supported CRS
    pub bounding_box: Option<BoundingBox2D>,
    /// Style involving all layers used to generate the tileset
    pub style: Option<Style>,
    /// Location of a tile that nicely represents the tileset. Implementations
    /// may use this center value to set the default location or to present a
    /// representative tile in a user interface
    pub center_point: Option<TilePoint>,
    /// License applicable to the tiles
    pub license: Option<String>,
    /// Restrictions on the availability of the Tile Set that the user needs to
    /// be aware of before using or redistributing the Tile Set
    pub access_constraints: Option<AccessConstraints>,
    /// Version of the Tile Set. Changes if the data behind the tiles has been changed
    pub version: Option<String>,
    /// When the Tile Set was first produced
    pub created: Option<DateTime<Utc>>,
    /// Last Tile Set change/revision
    pub updated: Option<DateTime<Utc>>,
    /// Useful information to contact the authors or custodians for the Tile Set
    pub point_of_contact: Option<String>,
    /// Media types available for the tiles
    pub media_types: Option<Vec<String>>,
}

#[serde_with::serde_as]
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GeospatialData {
    #[serde(flatten)]
    pub title_description_keywords: TitleDescriptionKeywords,
    /// Unique identifier of the Layer. Implemetion of 'identifier'
    pub id: String,
    /// Type of data represented in the layer
    pub data_type: DataType,
    /// The geometry type of the features shown in this layer
    pub geometry_dimension: Option<GeometryDimension>,
    /// Feature type identifier. Only applicable to layers of datatype 'geometries'
    pub feature_type: Option<String>,
    /// Useful information to contact the authors or custodians for the layer
    /// (e.g. e-mail address, a physical address,  phone numbers, etc)
    pub point_of_contact: Option<String>,
    /// Organization or individual responsible for making the layer available
    pub publisher: Option<String>,
    /// Category where the layer can be grouped
    pub theme: Option<String>,
    /// Coordinate Reference System (CRS)
    #[serde_as(as = "Option<DisplayFromStr>")]
    pub crs: Option<Crs>,
    /// Epoch of the Coordinate Reference System (CRS)
    pub epoch: Option<f64>,
    /// Minimum scale denominator for usage of the layer
    pub min_scale_denominator: Option<f64>,
    /// aximum scale denominator for usage of the layer
    pub max_scale_denominator: Option<f64>,
    /// Minimum cell size for usage of the layer
    pub min_cell_size: Option<f64>,
    /// Maximum cell size for usage of the layer
    pub max_cell_size: Option<f64>,
    /// TileMatrix identifier associated with the minScaleDenominator
    pub max_tile_matrix: Option<String>,
    /// TileMatrix identifier associated with the maxScaleDenominator
    pub min_tile_matrix: Option<String>,
    /// Minimum bounding rectangle surrounding the layer
    pub bounding_box: Option<BoundingBox2D>,
    /// When the layer was first produced
    pub created: Option<DateTime<Utc>>,
    /// Last layer change/revision
    pub updated: Option<DateTime<Utc>>,
    /// Style used to generate the layer in the tileset
    pub style: Option<Style>,
    /// URI identifying a class of data contained in this layer (useful to
    /// determine compatibility with styles or processes)
    pub geo_data_classes: Option<Vec<String>>,
    /// Properties represented by the features in this layer. Can be the
    /// attributes of a feature dataset (datatype=geometries) or the rangeType
    /// of a coverage (datatype=coverage)
    pub properties_schema: Option<Value>,
    /// Links related to this layer. Possible link 'rel' values are:
    /// 'geodata' for a URL pointing to the collection of geospatial data.
    pub links: Option<Links>,
}

#[serde_with::serde_as]
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TilePoint {
    pub coordinates: Option<Point2D>,
    // Coordinate Reference System (CRS) of the coordinates
    #[serde_as(as = "Option<DisplayFromStr>")]
    pub crs: Option<Crs>,
    /// TileMatrix identifier associated with the scaleDenominator
    pub tile_matrix: Option<String>,
    /// Scale denominator of the tile matrix selected
    pub scale_denominator: Option<f64>,
    /// Cell size of the tile matrix selected
    pub cell_size: Option<f64>,
}

#[serde_with::serde_as]
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Style {
    #[serde(flatten)]
    pub title_description_keywords: TitleDescriptionKeywords,
    /// An identifier for this style. Implementation of 'identifier'
    pub id: String,
    /// Links to style related resources. Possible link 'rel' values are:
    /// 'style' for a URL pointing to the style description, 'styleSpec' for a
    /// URL pointing to the specification or standard used to define the style.
    pub links: Option<Links>,
}

/// A resource describing useful to create an array that describes the limits
/// for a tile set [TileMatrixSet] based on the OGC TileSet Metadata Standard
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TileMatrixLimits {
    pub tile_matrix: String,
    pub min_tile_row: u64,
    pub max_tile_row: u64,
    pub min_tile_col: u64,
    pub max_tile_col: u64,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
#[non_exhaustive]
pub enum DataType {
    Map,
    Vector,
    Coverage,
}

#[repr(u8)]
#[derive(Serialize_repr, Deserialize_repr, PartialEq, Eq, Debug)]
pub enum GeometryDimension {
    Points = 0,
    Curves = 1,
    Surfaces = 2,
    Solids = 3,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum AccessConstraints {
    Unclassified,
    Restricted,
    Confidential,
    Secret,
    TopSecret,
}

impl Default for AccessConstraints {
    fn default() -> Self {
        AccessConstraints::Unclassified
    }
}

#[cfg(test)]
mod test {
    use super::GeometryDimension;

    #[test]
    fn geometry_dimension() {
        assert_eq!(
            serde_json::from_str::<GeometryDimension>("0").unwrap(),
            GeometryDimension::Points
        );
        assert_eq!(
            serde_json::to_value(&GeometryDimension::Points)
                .unwrap()
                .as_u64(),
            Some(0)
        );
    }
}