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
use crate::common::{Crs, Links};
use crate::{BoundingBox2D, OrderedAxes, Point2D, TitleDescriptionKeywords};
use serde::{Deserialize, Serialize};
use serde_with::DisplayFromStr;
use std::num::{NonZeroU16, NonZeroU64};
use std::path::PathBuf;
// #[derive(Serialize)]
// #[serde(rename_all = "camelCase")]
// pub struct TileMatrixSets {
// pub tile_matrix_sets: Vec<TileMatrixSetItem>,
// }
/// A minimal tile matrix set element for use within a list of tile matrix
/// sets linking to a full definition.
#[serde_with::serde_as]
#[serde_with::skip_serializing_none]
#[derive(Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct TileMatrixSetItem {
/// Optional local tile matrix set identifier, e.g. for use as unspecified
/// `{tileMatrixSetId}` parameter. Implementation of 'identifier'
pub id: Option<String>,
/// Title of this tile matrix set, normally used for display to a human
pub title: Option<String>,
/// Reference to an official source for this tileMatrixSet
pub uri: Option<String>,
#[serde(default)]
#[serde_as(as = "Option<DisplayFromStr>")]
pub crs: Option<Crs>,
/// Links to related resources. A 'self' link to the tile matrix set definition is required.
pub links: Links,
}
/// A definition of a tile matrix set following the Tile Matrix Set standard.
/// For tileset metadata, such a description (in `tileMatrixSet` property) is
/// only required for offline use, as an alternative to a link with a
/// `http://www.opengis.net/def/rel/ogc/1.0/tiling-scheme` relation type.
#[serde_with::serde_as]
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct TileMatrixSet {
#[serde(flatten)]
pub title_description_keywords: TitleDescriptionKeywords,
/// Tile matrix set identifier. Implementation of 'identifier'
pub id: String,
/// Reference to an official source for this TileMatrixSet
pub uri: Option<String>,
/// Coordinate Reference System (CRS)
#[serde_as(as = "DisplayFromStr")]
pub crs: Crs,
pub ordered_axes: Option<Vec<String>>,
/// Reference to a well-known scale set
pub well_known_scale_set: Option<String>,
/// Minimum bounding rectangle surrounding the tile matrix set, in the
/// supported CRS
pub bounding_box: Option<BoundingBox2D>,
/// Describes scale levels and its tile matrices
pub tile_matrices: Vec<TileMatrix>,
}
#[serde_with::serde_as]
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct TileMatrix {
#[serde(flatten)]
pub title_description_keywords: TitleDescriptionKeywords,
/// Identifier selecting one of the scales defined in the [TileMatrixSet]
/// and representing the scaleDenominator the tile. Implementation of 'identifier'
pub id: String,
/// Scale denominator of this tile matrix
pub scale_denominator: f64,
/// Cell size of this tile matrix
pub cell_size: f64,
/// description": "The corner of the tile matrix (_topLeft_ or
/// _bottomLeft_) used as the origin for numbering tile rows and columns.
/// This corner is also a corner of the (0, 0) tile.
pub corner_of_origin: Option<CornerOfOrigin>,
/// Precise position in CRS coordinates of the corner of origin (e.g. the
/// top-left corner) for this tile matrix. This position is also a corner
/// of the (0, 0) tile. In previous version, this was 'topLeftCorner' and
/// 'cornerOfOrigin' did not exist.
pub point_of_origin: Point2D,
/// Width of each tile of this tile matrix in pixels
pub tile_width: NonZeroU16,
/// Height of each tile of this tile matrix in pixels
pub tile_height: NonZeroU16,
/// Width of the matrix (number of tiles in width)
pub matrix_width: NonZeroU64,
/// Height of the matrix (number of tiles in height)
pub matrix_height: NonZeroU64,
/// Describes the rows that has variable matrix width
pub variable_matrix_widths: Option<Vec<VariableMatrixWidth>>,
}
/// Variable Matrix Width data structure
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct VariableMatrixWidth {
/// Number of tiles in width that coalesce in a single tile for these rows
pub coalesc: NonZeroU64,
/// First tile row where the coalescence factor applies for this tilematrix
pub min_tile_row: u64,
/// Last tile row where the coalescence factor applies for this tilematrix
pub smax_tile_row: u64,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub enum CornerOfOrigin {
TopLeft,
BottomLeft,
}
impl Default for CornerOfOrigin {
fn default() -> Self {
CornerOfOrigin::TopLeft
}
}
#[derive(thiserror::Error, Debug)]
pub enum TileMatrixSetError {
#[error(transparent)]
JsonError(#[from] serde_json::Error),
#[error("{0}: {1}")]
FileError(PathBuf, #[source] std::io::Error),
}
impl TileMatrixSet {
pub fn from_json_file(json_path: &str) -> Result<Self, TileMatrixSetError> {
let content = std::fs::read_to_string(json_path)
.map_err(|e| TileMatrixSetError::FileError(json_path.into(), e))?;
TileMatrixSet::from_json(&content)
}
pub fn from_json(json: &str) -> Result<Self, TileMatrixSetError> {
serde_json::from_str(&json).map_err(Into::into)
}
/// Check if CRS has inverted AXIS (lat,lon) instead of (lon,lat).
pub(crate) fn crs_axis_inverted(&self) -> bool {
if let Some(axes) = &self.ordered_axes {
ordered_axes_inverted(&axes)
} else {
false // TODO: Check CRS axis ordering
}
}
}
pub(crate) fn ordered_axes_inverted(axes: &OrderedAxes) -> bool {
first_axes_inverted(&axes[0].to_uppercase())
}
fn first_axes_inverted(first: &str) -> bool {
first == "Y" || first == "LAT" || first == "N"
}
#[cfg(test)]
mod test {
use super::TileMatrixSet;
#[test]
fn parse_tms_example() {
let tms = TileMatrixSet::from_json_file("./data/WebMercatorQuad.json").unwrap();
println!("{}", serde_json::to_string_pretty(&tms).unwrap());
}
}