Coverage for pygeoapi/models/provider/base.py: 98%
127 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 08:15 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 08:15 +0000
1# =================================================================
2#
3# Authors: Antonio Cerciello <anto.nio.cerciello@gmail.com>
4# Francesco Bartoli <xbartolone@gmail.com>
5# Tom Kralidis <tomkralidis@gmail.com>
6#
7# Copyright (c) 2022 Antonio Cerciello
8# Copyright (c) 2025 Francesco Bartoli
9# Copyright (c) 2025 Tom Kralidis
10#
11# Permission is hereby granted, free of charge, to any person
12# obtaining a copy of this software and associated documentation
13# files (the "Software"), to deal in the Software without
14# restriction, including without limitation the rights to use,
15# copy, modify, merge, publish, distribute, sublicense, and/or sell
16# copies of the Software, and to permit persons to whom the
17# Software is furnished to do so, subject to the following
18# conditions:
19#
20# The above copyright notice and this permission notice shall be
21# included in all copies or substantial portions of the Software.
22#
23# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
25# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
27# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
28# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
30# OTHER DEALINGS IN THE SOFTWARE.
31#
32# =================================================================
34from datetime import datetime
35from enum import Enum
36import json
37from pathlib import Path
38from typing import List, Optional
40import pydantic
41from pydantic import BaseModel
43from pygeoapi.util import DEFINITIONSDIR
45TMS_DIR = DEFINITIONSDIR / 'tiles'
48class TilesMetadataFormat(str, Enum):
49 # Tile Set Metadata
50 JSON = "JSON"
51 JSONLD = "JSONLD"
52 # TileJSON 3.0
53 TILEJSON = "TILEJSON"
54 # HTML (default)
55 HTML = "HTML"
58# Tile Set Metadata Enums
59class AccessConstraintsEnum(str, Enum):
60 UNCLASSIFIED = "unclassified"
61 RESTRICTED = "restricted"
62 CONFIDENTIAL = "confidential"
63 SECRET = "secret"
64 TOPSECRET = "topSecret"
67class DataTypeEnum(str, Enum):
68 MAP = "map"
69 VECTOR = "vector"
70 COVERAGE = "coverage"
73class GeometryDimensionEnum(int, Enum):
74 POINTS = 0
75 CURVES = 1
76 SURFACES = 2
77 SOLIDS = 3
80class TileMatrixSetEnumType(BaseModel):
81 tileMatrixSet: str
82 tileMatrixSetURI: str
83 crs: str
84 title: str
85 orderedAxes: List[str]
86 wellKnownScaleSet: str
87 tileMatrices: List[dict]
90class TileMatrixSetLoader:
91 def __init__(self, directory: Path):
92 self.directory = directory
94 def load_from_file(self, filename: str) -> TileMatrixSetEnumType:
95 """
96 Load a single TMS JSON file.
98 :param filename: filename of TMS
100 :returns: `TileMatrixSetEnumType` of TMS
101 """
103 filepath = self.directory / filename
105 with filepath.open(encoding='utf-8') as fh:
106 data = json.load(fh)
108 return TileMatrixSetEnumType(
109 tileMatrixSet=data["id"],
110 tileMatrixSetURI=data["uri"],
111 crs=data["crs"],
112 title=data["title"],
113 orderedAxes=data["orderedAxes"],
114 wellKnownScaleSet=data.get("wellKnownScaleSet", ""),
115 tileMatrices=data["tileMatrices"]
116 )
118 def create_enum(self) -> Enum:
119 """
120 Create an Enum with all TileMatrixSets in the directory.
122 :returns: `Enum` of `TileMatrixSetEnum`
123 """
125 members = {}
126 for filepath in self.directory.glob("*.json"):
127 tms = self.load_from_file(filepath.name)
128 enum_name = tms.tileMatrixSet.upper().replace("-", "").replace(" ", "") # noqa
129 members[enum_name] = tms
131 return Enum("TileMatrixSetEnum", members)
134tms_loader = TileMatrixSetLoader(TMS_DIR)
135TileMatrixSetEnum = tms_loader.create_enum()
138# Tile Set Metadata Sub Types
139class TileMatrixLimitsType(BaseModel):
140 tileMatrix: str
141 minTileRow: int
142 maxTileRow: int
143 minTileCol: int
144 maxTileCol: int
147class TwoDBoundingBoxType(BaseModel):
148 lowerLeft: List[float]
149 upperRight: List[float]
150 crs: Optional[str] = None
153class LinkType(BaseModel):
154 href: str
155 rel: Optional[str] = None
156 type_: Optional[str] = None
157 hreflang: Optional[str] = None
158 title: Optional[str] = None
159 length: Optional[int] = None
162class GeospatialDataType(BaseModel):
163 id: Optional[str] = None
164 title: Optional[str] = None
165 description: Optional[str] = None
166 keywords: Optional[List[str]] = None
167 dataType: DataTypeEnum = DataTypeEnum.VECTOR
168 geometryDimension: Optional[GeometryDimensionEnum] = None
169 maxTileMatrix: Optional[str] = None
170 minTileMatrix: Optional[str] = None
171 minScaleDenominator: Optional[float] = None
172 maxScaleDenominator: Optional[float] = None
173 minCellSize: Optional[float] = None
174 maxCellSize: Optional[float] = None
175 boundingBox: Optional[TwoDBoundingBoxType] = None
176 links: Optional[LinkType] = None
177 propertiesSchema: Optional[dict] = None
180class StyleType(BaseModel):
181 id: Optional[str] = None
182 title: Optional[str] = None
183 description: Optional[str] = None
184 keywords: Optional[List[str]] = None
185 links: Optional[LinkType] = None
188class TilePointType(BaseModel):
189 crs: str
190 coordinates: Optional[List[float]] = None
191 scaleDenominator: Optional[float] = None
192 cellSize: Optional[float] = None
193 # CodeType as adaptation of MD_Identifier class ISO 19115
194 tileMatrix: str
195 cellSize: Optional[str] = None
198class TileSetMetadata(BaseModel):
199 # A title for this tileset
200 title: Optional[str] = None
201 # Brief narrative description of this tile set
202 description: Optional[str] = None
203 # keywords about this tileset
204 keywords: Optional[List[str]] = None
205 # Version of the Tile Set. Changes if the data behind the tiles
206 # has been changed
207 version: Optional[str] = None
208 # Useful information to contact the authors or custodians for the Tile Set
209 pointOfContact: Optional[str] = None
210 # Short reference to recognize the author or provider
211 attribution: Optional[str] = None
212 # License applicable to the tiles
213 license_: Optional[str] = None
214 # Restrictions on the availability of the Tile Set that the user needs to
215 # be aware of before using or redistributing the Tile Set
216 accessConstraints: Optional[AccessConstraintsEnum] = AccessConstraintsEnum.UNCLASSIFIED # noqa
217 # Media types available for the tiles
218 mediaTypes: Optional[List[str]] = None
219 # Type of data represented in the tileset
220 dataType: DataTypeEnum = DataTypeEnum.VECTOR
221 # Limits for the TileRow and TileCol values for each TileMatrix in the
222 # tileMatrixSet. If missing, there are no limits other that the ones
223 # imposed by the TileMatrixSet. If present the TileMatrices listed are
224 # limited and the rest not available at all
225 tileMatrixSetLimits: Optional[TileMatrixLimitsType] = None
226 # Coordinate Reference System (CRS)
227 crs: Optional[str] = None
228 # Epoch of the Coordinate Reference System (CRS)
229 epoch: Optional[int] = None
230 # Minimum bounding rectangle surrounding the tile matrix set, in the
231 # supported CRS
232 boundingBox: Optional[TwoDBoundingBoxType] = None
233 # When the Tile Set was first produced
234 created: Optional[datetime] = None
235 # Last Tile Set change/revision
236 updated: Optional[datetime] = None
237 layers: Optional[GeospatialDataType] = None
238 # Style involving all layers used to generate the tileset
239 style: Optional[StyleType] = None
240 # Location of a tile that nicely represents the tileset.
241 # Implementations may use this center value to set the default location
242 # or to present a representative tile in a user interface
243 centerPoint: Optional[TilePointType] = None
244 # Tile matrix set definition
245 tileMatrixSet: Optional[str] = None
246 # Reference to a Tile Matrix Set on an official source
247 tileMatrixSetURI: Optional[str] = None
248 # Links to related resources.
249 links: Optional[List[LinkType]] = None
252if pydantic.VERSION.startswith('1'): 252 ↛ exitline 252 didn't exit the module because the condition on line 252 was always true
253 def _dump(self, *, exclude_none: bool = False, **kwargs):
254 return self.dict(exclude_none=exclude_none, **kwargs)
256 TileSetMetadata.model_dump = _dump