django-gisserver 1.5.0__py3-none-any.whl → 2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {django_gisserver-1.5.0.dist-info → django_gisserver-2.1.dist-info}/METADATA +34 -8
- django_gisserver-2.1.dist-info/RECORD +68 -0
- {django_gisserver-1.5.0.dist-info → django_gisserver-2.1.dist-info}/WHEEL +1 -1
- gisserver/__init__.py +1 -1
- gisserver/compat.py +23 -0
- gisserver/conf.py +7 -0
- gisserver/crs.py +401 -0
- gisserver/db.py +126 -51
- gisserver/exceptions.py +132 -4
- gisserver/extensions/__init__.py +4 -0
- gisserver/{parsers/fes20 → extensions}/functions.py +131 -31
- gisserver/extensions/queries.py +266 -0
- gisserver/features.py +253 -181
- gisserver/geometries.py +64 -311
- gisserver/management/__init__.py +0 -0
- gisserver/management/commands/__init__.py +0 -0
- gisserver/management/commands/loadgeojson.py +311 -0
- gisserver/operations/base.py +130 -312
- gisserver/operations/wfs20.py +399 -375
- gisserver/output/__init__.py +14 -49
- gisserver/output/base.py +198 -144
- gisserver/output/csv.py +78 -75
- gisserver/output/geojson.py +37 -37
- gisserver/output/gml32.py +287 -259
- gisserver/output/iters.py +207 -0
- gisserver/output/results.py +73 -61
- gisserver/output/stored.py +143 -0
- gisserver/output/utils.py +81 -169
- gisserver/output/xmlschema.py +85 -46
- gisserver/parsers/__init__.py +10 -10
- gisserver/parsers/ast.py +426 -0
- gisserver/parsers/fes20/__init__.py +89 -31
- gisserver/parsers/fes20/expressions.py +172 -58
- gisserver/parsers/fes20/filters.py +116 -45
- gisserver/parsers/fes20/identifiers.py +66 -28
- gisserver/parsers/fes20/lookups.py +146 -0
- gisserver/parsers/fes20/operators.py +417 -161
- gisserver/parsers/fes20/sorting.py +113 -34
- gisserver/parsers/gml/__init__.py +17 -25
- gisserver/parsers/gml/base.py +36 -15
- gisserver/parsers/gml/geometries.py +105 -44
- gisserver/parsers/ows/__init__.py +25 -0
- gisserver/parsers/ows/kvp.py +198 -0
- gisserver/parsers/ows/requests.py +160 -0
- gisserver/parsers/query.py +179 -0
- gisserver/parsers/values.py +87 -4
- gisserver/parsers/wfs20/__init__.py +39 -0
- gisserver/parsers/wfs20/adhoc.py +253 -0
- gisserver/parsers/wfs20/base.py +148 -0
- gisserver/parsers/wfs20/projection.py +103 -0
- gisserver/parsers/wfs20/requests.py +483 -0
- gisserver/parsers/wfs20/stored.py +193 -0
- gisserver/parsers/xml.py +261 -0
- gisserver/projection.py +367 -0
- gisserver/static/gisserver/index.css +20 -4
- gisserver/templates/gisserver/base.html +12 -0
- gisserver/templates/gisserver/index.html +9 -15
- gisserver/templates/gisserver/service_description.html +12 -6
- gisserver/templates/gisserver/wfs/2.0.0/get_capabilities.xml +9 -9
- gisserver/templates/gisserver/wfs/feature_field.html +3 -3
- gisserver/templates/gisserver/wfs/feature_type.html +35 -13
- gisserver/templatetags/gisserver_tags.py +20 -0
- gisserver/types.py +445 -313
- gisserver/views.py +227 -62
- django_gisserver-1.5.0.dist-info/RECORD +0 -54
- gisserver/parsers/base.py +0 -149
- gisserver/parsers/fes20/query.py +0 -285
- gisserver/parsers/tags.py +0 -102
- gisserver/queries/__init__.py +0 -37
- gisserver/queries/adhoc.py +0 -185
- gisserver/queries/base.py +0 -186
- gisserver/queries/projection.py +0 -240
- gisserver/queries/stored.py +0 -206
- gisserver/templates/gisserver/wfs/2.0.0/describe_stored_queries.xml +0 -20
- gisserver/templates/gisserver/wfs/2.0.0/list_stored_queries.xml +0 -14
- {django_gisserver-1.5.0.dist-info → django_gisserver-2.1.dist-info/licenses}/LICENSE +0 -0
- {django_gisserver-1.5.0.dist-info → django_gisserver-2.1.dist-info}/top_level.txt +0 -0
gisserver/geometries.py
CHANGED
|
@@ -1,338 +1,86 @@
|
|
|
1
1
|
"""Helper classes to handle geometry data types.
|
|
2
2
|
|
|
3
|
-
This includes the CRS parsing, coordinate transforms and bounding box object.
|
|
4
3
|
The bounding box can be calculated within Python, or read from a database result.
|
|
5
4
|
"""
|
|
6
5
|
|
|
7
6
|
from __future__ import annotations
|
|
8
7
|
|
|
9
|
-
import
|
|
10
|
-
from dataclasses import dataclass
|
|
11
|
-
from decimal import Decimal
|
|
12
|
-
from functools import lru_cache
|
|
8
|
+
import math
|
|
9
|
+
from dataclasses import dataclass
|
|
13
10
|
|
|
14
|
-
from django.contrib.gis.
|
|
15
|
-
from django.contrib.gis.geos import GEOSGeometry, Polygon
|
|
11
|
+
from django.contrib.gis.geos import GEOSGeometry
|
|
16
12
|
|
|
17
|
-
from gisserver.
|
|
13
|
+
from gisserver.crs import CRS, CRS84, WEB_MERCATOR, WGS84 # noqa: F401 (keep old exports)
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
r":def:crs:(?P<authority>[a-z]+)"
|
|
22
|
-
r":(?P<version>[0-9]+\.[0-9]+(\.[0-9]+)?)?"
|
|
23
|
-
r":(?P<id>[0-9]+|crs84)"
|
|
24
|
-
r"$",
|
|
25
|
-
re.IGNORECASE,
|
|
26
|
-
)
|
|
15
|
+
#: The CRS for the ``<ows:WGS84BoundingBox>`` element:
|
|
16
|
+
WGS84_BOUNDING_BOX_CRS = CRS.from_string("urn:ogc:def:crs:OGC:2:84")
|
|
27
17
|
|
|
28
18
|
__all__ = [
|
|
29
|
-
"CRS",
|
|
30
|
-
"WGS84",
|
|
31
19
|
"BoundingBox",
|
|
20
|
+
"WGS84BoundingBox",
|
|
32
21
|
]
|
|
33
22
|
|
|
34
23
|
|
|
35
|
-
@
|
|
36
|
-
|
|
37
|
-
"""
|
|
38
|
-
|
|
39
|
-
return SpatialReference(srs_input, srs_type=srs_type, axis_order=axis_order)
|
|
24
|
+
@dataclass
|
|
25
|
+
class BoundingBox:
|
|
26
|
+
"""A bounding box.
|
|
27
|
+
Due to the overlap between 2 types, this element is used for 2 cases:
|
|
40
28
|
|
|
29
|
+
* The ``<ows:WGS84BoundingBox>`` element for ``GetCapabilities``.
|
|
30
|
+
* The ``<gml:Envelope>`` inside an``<gml:boundedBy>`` single feature.
|
|
41
31
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
source: int | SpatialReference, target: int | SpatialReference
|
|
45
|
-
) -> CoordTransform:
|
|
46
|
-
"""Get an efficient coordinate transformation object.
|
|
32
|
+
While both classes have no common base class (and exist in different schema's),
|
|
33
|
+
their properties are identical.
|
|
47
34
|
|
|
48
|
-
The
|
|
49
|
-
coordinate transformation repeatedly on different geometries.
|
|
35
|
+
The X/Y coordinates can be either latitude or longitude, depending on the CRS.
|
|
50
36
|
|
|
51
|
-
|
|
52
|
-
|
|
37
|
+
Note this isn't using the GDAL/OGR "Envelope" object, as that doesn't expose the CRS,
|
|
38
|
+
and requires constant copies to merge geometries.
|
|
53
39
|
"""
|
|
54
|
-
if isinstance(source, int):
|
|
55
|
-
source = _get_spatial_reference(source, srs_type="epsg")
|
|
56
|
-
if isinstance(target, int):
|
|
57
|
-
target = _get_spatial_reference(target, srs_type="epsg")
|
|
58
|
-
|
|
59
|
-
return CoordTransform(source, target)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
@dataclass(frozen=True)
|
|
63
|
-
class CRS:
|
|
64
|
-
"""
|
|
65
|
-
Represents a CRS (Coordinate Reference System), which preferably follows the URN format
|
|
66
|
-
as specified by `the OGC consortium <http://www.opengeospatial.org/ogcUrnPolicy>`_.
|
|
67
|
-
"""
|
|
68
|
-
|
|
69
|
-
# CRS logic, based upon https://github.com/wglas85/django-wfs/blob/master/wfs/helpers.py
|
|
70
|
-
# Copyright (c) 2006 Wolfgang Glas - Apache 2.0 licensed
|
|
71
|
-
# Ported to Python 3.6 style.
|
|
72
|
-
|
|
73
|
-
#: Either "ogc" or "opengis", whereas "ogc" is highly recommended.
|
|
74
|
-
domain: str
|
|
75
|
-
|
|
76
|
-
#: Either "OGC" or "EPSG".
|
|
77
|
-
authority: str
|
|
78
|
-
|
|
79
|
-
#: The version of the authorities' SRS registry, which is empty or
|
|
80
|
-
#: contains two or three numeric components separated by dots like "6.9" or "6.11.9".
|
|
81
|
-
#: For WFS 2.0 this is typically empty.
|
|
82
|
-
version: str
|
|
83
|
-
|
|
84
|
-
#: A string representation of the coordinate system reference ID.
|
|
85
|
-
#: For OGC, only "CRS84" is supported as crsid. For EPSG, this is the formatted CRSID.
|
|
86
|
-
crsid: str
|
|
87
|
-
|
|
88
|
-
#: The integer representing the numeric spatial reference ID as
|
|
89
|
-
#: used by the EPSG and GIS database backends.
|
|
90
|
-
srid: int
|
|
91
|
-
|
|
92
|
-
#: GDAL SpatialReference with PROJ.4 / WKT content to describe the exact transformation.
|
|
93
|
-
backend: SpatialReference | None = None
|
|
94
|
-
|
|
95
|
-
#: Original input
|
|
96
|
-
origin: str = field(init=False, default=None)
|
|
97
|
-
|
|
98
|
-
has_custom_backend: bool = field(init=False)
|
|
99
|
-
|
|
100
|
-
def __post_init__(self):
|
|
101
|
-
# Using __dict__ because of frozen=True
|
|
102
|
-
self.__dict__["has_custom_backend"] = self.backend is not None
|
|
103
|
-
|
|
104
|
-
@classmethod
|
|
105
|
-
def from_string(cls, uri: str | int, backend: SpatialReference | None = None) -> CRS:
|
|
106
|
-
"""
|
|
107
|
-
Parse an CRS (Coordinate Reference System) URI, which preferably follows the URN format
|
|
108
|
-
as specified by `the OGC consortium <http://www.opengeospatial.org/ogcUrnPolicy>`_
|
|
109
|
-
and construct a new CRS instance.
|
|
110
|
-
|
|
111
|
-
The value can be 3 things:
|
|
112
|
-
|
|
113
|
-
* A URI in OGC URN format.
|
|
114
|
-
* A legacy CRS URI ("epsg:<SRID>", or "http://www.opengis.net/...").
|
|
115
|
-
* A numeric SRID (which calls `from_srid()`)
|
|
116
|
-
"""
|
|
117
|
-
if isinstance(uri, int) or uri.isdigit():
|
|
118
|
-
return cls.from_srid(int(uri), backend=backend)
|
|
119
|
-
elif uri.startswith("urn:"):
|
|
120
|
-
return cls._from_urn(uri, backend=backend)
|
|
121
|
-
else:
|
|
122
|
-
return cls._from_legacy(uri, backend=backend)
|
|
123
|
-
|
|
124
|
-
@classmethod
|
|
125
|
-
def from_srid(cls, srid: int, backend=None):
|
|
126
|
-
"""Instantiate this class using a numeric spatial reference ID
|
|
127
|
-
|
|
128
|
-
This is logically identical to calling::
|
|
129
|
-
|
|
130
|
-
CRS.from_string("urn:ogc:def:crs:EPSG:6.9:<SRID>")
|
|
131
|
-
"""
|
|
132
|
-
crs = cls(
|
|
133
|
-
domain="ogc",
|
|
134
|
-
authority="EPSG",
|
|
135
|
-
version="",
|
|
136
|
-
crsid=str(srid),
|
|
137
|
-
srid=int(srid),
|
|
138
|
-
backend=backend,
|
|
139
|
-
)
|
|
140
|
-
crs.__dict__["origin"] = srid
|
|
141
|
-
return crs
|
|
142
|
-
|
|
143
|
-
@classmethod
|
|
144
|
-
def _from_urn(cls, urn, backend=None): # noqa: C901
|
|
145
|
-
"""Instantiate this class using a URN format."""
|
|
146
|
-
urn_match = CRS_URN_REGEX.match(urn)
|
|
147
|
-
if not urn_match:
|
|
148
|
-
raise ExternalValueError(f"Unknown CRS URN [{urn}] specified: {CRS_URN_REGEX.pattern}")
|
|
149
|
-
|
|
150
|
-
domain = urn_match.group("domain")
|
|
151
|
-
authority = urn_match.group("authority").upper()
|
|
152
|
-
|
|
153
|
-
if domain not in ("ogc", "opengis"):
|
|
154
|
-
raise ExternalValueError(f"CRS URI [{urn}] contains unknown domain [{domain}]")
|
|
155
|
-
|
|
156
|
-
if authority == "EPSG":
|
|
157
|
-
crsid = urn_match.group("id")
|
|
158
|
-
try:
|
|
159
|
-
srid = int(crsid)
|
|
160
|
-
except ValueError:
|
|
161
|
-
raise ExternalValueError(
|
|
162
|
-
f"CRS URI [{urn}] should contain a numeric SRID value."
|
|
163
|
-
) from None
|
|
164
|
-
elif authority == "OGC":
|
|
165
|
-
crsid = urn_match.group("id").upper()
|
|
166
|
-
if crsid != "CRS84":
|
|
167
|
-
raise ExternalValueError(f"OGC CRS URI from [{urn}] contains unknown id [{id}]")
|
|
168
|
-
srid = 4326
|
|
169
|
-
else:
|
|
170
|
-
raise ExternalValueError(f"CRS URI [{urn}] contains unknown authority [{authority}]")
|
|
171
|
-
|
|
172
|
-
crs = cls(
|
|
173
|
-
domain=domain,
|
|
174
|
-
authority=authority,
|
|
175
|
-
version=urn_match.group(3),
|
|
176
|
-
crsid=crsid,
|
|
177
|
-
srid=srid,
|
|
178
|
-
backend=backend,
|
|
179
|
-
)
|
|
180
|
-
crs.__dict__["origin"] = urn
|
|
181
|
-
return crs
|
|
182
|
-
|
|
183
|
-
@classmethod
|
|
184
|
-
def _from_legacy(cls, uri, backend=None):
|
|
185
|
-
"""Instantiate this class from a legacy URL"""
|
|
186
|
-
luri = uri.lower()
|
|
187
|
-
for head in (
|
|
188
|
-
"epsg:",
|
|
189
|
-
"http://www.opengis.net/def/crs/epsg/0/",
|
|
190
|
-
"http://www.opengis.net/gml/srs/epsg.xml#",
|
|
191
|
-
):
|
|
192
|
-
if luri.startswith(head):
|
|
193
|
-
crsid = luri[len(head) :]
|
|
194
|
-
try:
|
|
195
|
-
srid = int(crsid)
|
|
196
|
-
except ValueError:
|
|
197
|
-
raise ExternalValueError(
|
|
198
|
-
f"CRS URI [{uri}] should contain a numeric SRID value."
|
|
199
|
-
) from None
|
|
200
40
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
crsid=crsid,
|
|
206
|
-
srid=srid,
|
|
207
|
-
backend=backend,
|
|
208
|
-
)
|
|
209
|
-
crs.__dict__["origin"] = uri
|
|
210
|
-
return crs
|
|
211
|
-
|
|
212
|
-
raise ExternalValueError(f"Unknown CRS URI [{uri}] specified")
|
|
213
|
-
|
|
214
|
-
@property
|
|
215
|
-
def legacy(self):
|
|
216
|
-
"""Return a legacy string in the format "EPSG:<srid>"""
|
|
217
|
-
return f"EPSG:{self.srid:d}"
|
|
218
|
-
|
|
219
|
-
@property
|
|
220
|
-
def urn(self):
|
|
221
|
-
"""Return The OGC URN corresponding to this CRS."""
|
|
222
|
-
return f"urn:{self.domain}:def:crs:{self.authority}:{self.version or ''}:{self.crsid}"
|
|
223
|
-
|
|
224
|
-
def __str__(self):
|
|
225
|
-
return self.urn
|
|
226
|
-
|
|
227
|
-
def __eq__(self, other):
|
|
228
|
-
if isinstance(other, CRS):
|
|
229
|
-
# CRS84 is NOT equivalent to EPSG:4326.
|
|
230
|
-
# EPSG:4326 specifies coordinates in lat/long order and CRS:84 in long/lat order.
|
|
231
|
-
return self.authority == other.authority and self.srid == other.srid
|
|
232
|
-
else:
|
|
233
|
-
return NotImplemented
|
|
234
|
-
|
|
235
|
-
def __hash__(self):
|
|
236
|
-
"""Used to match objects in a set."""
|
|
237
|
-
return hash((self.authority, self.srid))
|
|
238
|
-
|
|
239
|
-
def _as_gdal(self) -> SpatialReference:
|
|
240
|
-
"""Generate the GDAL Spatial Reference object"""
|
|
241
|
-
if self.backend is None:
|
|
242
|
-
# Avoid repeated construction, reuse the object from cache if possible.
|
|
243
|
-
# Note that the original data is used, as it also defines axis orientation.
|
|
244
|
-
if self.origin:
|
|
245
|
-
self.__dict__["backend"] = _get_spatial_reference(self.origin)
|
|
246
|
-
else:
|
|
247
|
-
self.__dict__["backend"] = _get_spatial_reference(self.srid, srs_type="epsg")
|
|
248
|
-
return self.backend
|
|
249
|
-
|
|
250
|
-
def apply_to(self, geometry: GEOSGeometry, clone=False) -> GEOSGeometry | None:
|
|
251
|
-
"""Transform the geometry using this coordinate reference.
|
|
252
|
-
|
|
253
|
-
This method caches the used CoordTransform object
|
|
254
|
-
|
|
255
|
-
Every transformation within this package happens through this method,
|
|
256
|
-
giving full control over coordinate transformations.
|
|
257
|
-
"""
|
|
258
|
-
if self.srid == geometry.srid:
|
|
259
|
-
# Avoid changes if spatial reference system is identical.
|
|
260
|
-
if clone:
|
|
261
|
-
return geometry.clone()
|
|
262
|
-
else:
|
|
263
|
-
return None
|
|
264
|
-
else:
|
|
265
|
-
# Convert using GDAL / proj
|
|
266
|
-
transform = _get_coord_transform(geometry.srid, self._as_gdal())
|
|
267
|
-
return geometry.transform(transform, clone=clone)
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
WGS84 = CRS.from_srid(4326) # aka EPSG:4326
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
@dataclass
|
|
274
|
-
class BoundingBox:
|
|
275
|
-
"""A bounding box that describes the extent of a map layer"""
|
|
276
|
-
|
|
277
|
-
south: Decimal # longitude
|
|
278
|
-
west: Decimal # latitude
|
|
279
|
-
north: Decimal # longitude
|
|
280
|
-
east: Decimal # latitude
|
|
41
|
+
min_x: float
|
|
42
|
+
min_y: float
|
|
43
|
+
max_x: float
|
|
44
|
+
max_y: float
|
|
281
45
|
crs: CRS | None = None
|
|
282
46
|
|
|
283
47
|
@classmethod
|
|
284
|
-
def
|
|
285
|
-
"""
|
|
48
|
+
def from_geometries(cls, geometries: list[GEOSGeometry], crs: CRS) -> BoundingBox | None:
|
|
49
|
+
"""Calculate the extent of a collection of geometries."""
|
|
50
|
+
if not geometries:
|
|
51
|
+
return None
|
|
52
|
+
elif len(geometries) == 1:
|
|
53
|
+
# Common case: feature has a single geometry
|
|
54
|
+
ogr_geometry = geometries[0].ogr
|
|
55
|
+
crs.apply_to(ogr_geometry, clone=False)
|
|
56
|
+
return cls(*ogr_geometry.extent, crs=crs)
|
|
57
|
+
else:
|
|
58
|
+
# Feature has multiple geometries.
|
|
59
|
+
# Start with an obviously invalid bbox,
|
|
60
|
+
# which corrects at the first extend_to_geometry call.
|
|
61
|
+
result = cls(math.inf, math.inf, -math.inf, -math.inf, crs=crs)
|
|
286
62
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
raise ExternalParsingError(
|
|
292
|
-
f"Input does not contain bounding box, expected 4 or 5 values, not {bbox}."
|
|
293
|
-
)
|
|
294
|
-
return cls(
|
|
295
|
-
Decimal(bbox[0]),
|
|
296
|
-
Decimal(bbox[1]),
|
|
297
|
-
Decimal(bbox[2]),
|
|
298
|
-
Decimal(bbox[3]),
|
|
299
|
-
CRS.from_string(bbox[4]) if len(bbox) == 5 else None,
|
|
300
|
-
)
|
|
63
|
+
for geometry in geometries:
|
|
64
|
+
ogr_geometry = geometry.ogr
|
|
65
|
+
crs.apply_to(ogr_geometry, clone=False)
|
|
66
|
+
result.extend_to(*ogr_geometry.extent)
|
|
301
67
|
|
|
302
|
-
|
|
303
|
-
def from_geometry(cls, geometry: GEOSGeometry, crs: CRS | None = None):
|
|
304
|
-
"""Construct the bounding box for a geometry"""
|
|
305
|
-
if crs is None:
|
|
306
|
-
crs = CRS.from_srid(geometry.srid)
|
|
307
|
-
elif geometry.srid != crs.srid:
|
|
308
|
-
geometry = crs.apply_to(geometry, clone=True)
|
|
309
|
-
|
|
310
|
-
return cls(*geometry.extent, crs=crs)
|
|
68
|
+
return result
|
|
311
69
|
|
|
312
70
|
@property
|
|
313
71
|
def lower_corner(self):
|
|
314
|
-
return [self.
|
|
72
|
+
return [self.min_x, self.min_y]
|
|
315
73
|
|
|
316
74
|
@property
|
|
317
75
|
def upper_corner(self):
|
|
318
|
-
return [self.
|
|
319
|
-
|
|
320
|
-
def __repr__(self):
|
|
321
|
-
return f"BoundingBox({self.south}, {self.west}, {self.north}, {self.east})"
|
|
76
|
+
return [self.max_x, self.max_y]
|
|
322
77
|
|
|
323
|
-
def extend_to(self,
|
|
78
|
+
def extend_to(self, min_x: float, min_y: float, max_x: float, max_y: float):
|
|
324
79
|
"""Expand the bounding box in-place"""
|
|
325
|
-
self.
|
|
326
|
-
self.
|
|
327
|
-
self.
|
|
328
|
-
self.
|
|
329
|
-
|
|
330
|
-
def extend_to_geometry(self, geometry: GEOSGeometry):
|
|
331
|
-
"""Extend this bounding box with the coordinates of a given geometry."""
|
|
332
|
-
if self.crs is not None and geometry.srid != self.crs.srid:
|
|
333
|
-
geometry = self.crs.apply_to(geometry, clone=True)
|
|
334
|
-
|
|
335
|
-
self.extend_to(*geometry.extent)
|
|
80
|
+
self.min_x = min(self.min_x, min_x)
|
|
81
|
+
self.min_y = min(self.min_y, min_y)
|
|
82
|
+
self.max_x = max(self.max_x, max_x)
|
|
83
|
+
self.max_y = max(self.max_y, max_y)
|
|
336
84
|
|
|
337
85
|
def __add__(self, other):
|
|
338
86
|
"""Combine both extents into a larger box."""
|
|
@@ -341,18 +89,23 @@ class BoundingBox:
|
|
|
341
89
|
raise ValueError(
|
|
342
90
|
"Can't combine instances with different spatial reference systems"
|
|
343
91
|
)
|
|
344
|
-
return
|
|
345
|
-
min(self.
|
|
346
|
-
min(self.
|
|
347
|
-
max(self.
|
|
348
|
-
max(self.
|
|
92
|
+
return self.__class__(
|
|
93
|
+
min(self.min_x, other.min_x),
|
|
94
|
+
min(self.min_y, other.min_y),
|
|
95
|
+
max(self.max_x, other.max_x),
|
|
96
|
+
max(self.max_y, other.max_y),
|
|
97
|
+
crs=self.crs,
|
|
349
98
|
)
|
|
350
99
|
else:
|
|
351
100
|
return NotImplemented
|
|
352
101
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
102
|
+
|
|
103
|
+
class WGS84BoundingBox(BoundingBox):
|
|
104
|
+
"""The ``<ows:WGS84BoundingBox>`` element for the ``GetCapabilities`` element.
|
|
105
|
+
|
|
106
|
+
This always has coordinates are always in longitude/latitude axis ordering,
|
|
107
|
+
the CRS is fixed to ``urn:ogc:def:crs:OGC:2:84``.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
def __init__(self, min_x: float, min_y: float, max_x: float, max_y: float):
|
|
111
|
+
super().__init__(min_x, min_y, max_x, max_y, crs=WGS84_BOUNDING_BOX_CRS)
|
|
File without changes
|
|
File without changes
|