django-gisserver 2.1__tar.gz → 2.1.2__tar.gz
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-2.1 → django_gisserver-2.1.2}/CHANGES.md +19 -23
- {django_gisserver-2.1/django_gisserver.egg-info → django_gisserver-2.1.2}/PKG-INFO +2 -1
- {django_gisserver-2.1 → django_gisserver-2.1.2/django_gisserver.egg-info}/PKG-INFO +2 -1
- {django_gisserver-2.1 → django_gisserver-2.1.2}/django_gisserver.egg-info/SOURCES.txt +1 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/django_gisserver.egg-info/requires.txt +1 -0
- django_gisserver-2.1.2/gisserver/__init__.py +1 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/conf.py +23 -1
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/crs.py +84 -33
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/db.py +7 -1
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/features.py +18 -13
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/geometries.py +1 -1
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/output/gml32.py +47 -8
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/ast.py +8 -5
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/fes20/operators.py +1 -1
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/gml/geometries.py +7 -1
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/wfs20/adhoc.py +3 -1
- django_gisserver-2.1.2/gisserver/static/gisserver/bootstrap-5.3.6.min.css +6 -0
- django_gisserver-2.1.2/gisserver/static/gisserver/index.css +36 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/templates/gisserver/base.html +4 -1
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/templates/gisserver/index.html +1 -1
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/templates/gisserver/service_description.html +1 -1
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/templates/gisserver/wfs/feature_type.html +11 -2
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/types.py +2 -1
- {django_gisserver-2.1 → django_gisserver-2.1.2}/setup.py +1 -0
- django_gisserver-2.1/gisserver/__init__.py +0 -1
- django_gisserver-2.1/gisserver/static/gisserver/index.css +0 -20
- {django_gisserver-2.1 → django_gisserver-2.1.2}/LICENSE +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/MANIFEST.in +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/README.md +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/django_gisserver.egg-info/dependency_links.txt +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/django_gisserver.egg-info/not-zip-safe +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/django_gisserver.egg-info/top_level.txt +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/compat.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/exceptions.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/extensions/__init__.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/extensions/functions.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/extensions/queries.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/management/__init__.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/management/commands/__init__.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/management/commands/loadgeojson.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/operations/__init__.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/operations/base.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/operations/wfs20.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/output/__init__.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/output/base.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/output/csv.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/output/geojson.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/output/iters.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/output/results.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/output/stored.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/output/utils.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/output/xmlschema.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/__init__.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/fes20/__init__.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/fes20/expressions.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/fes20/filters.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/fes20/identifiers.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/fes20/lookups.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/fes20/sorting.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/gml/__init__.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/gml/base.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/ows/__init__.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/ows/kvp.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/ows/requests.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/query.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/values.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/wfs20/__init__.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/wfs20/base.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/wfs20/projection.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/wfs20/requests.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/wfs20/stored.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/parsers/xml.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/projection.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/templates/gisserver/wfs/2.0.0/get_capabilities.xml +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/templates/gisserver/wfs/feature_field.html +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/templatetags/__init__.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/templatetags/gisserver_tags.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/gisserver/views.py +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/pyproject.toml +0 -0
- {django_gisserver-2.1 → django_gisserver-2.1.2}/setup.cfg +0 -0
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# 2025-09-10 (2.1.2)
|
|
2
|
+
|
|
3
|
+
* Stop using CDN for bootstrap stylesheet, instead use a static local file.
|
|
4
|
+
This in order for the server to work in an airgapped environment (PR#72).
|
|
5
|
+
|
|
6
|
+
# 2025-06-06 (2.1.1)
|
|
7
|
+
|
|
8
|
+
* Fixed using legacy longitude/latitude rendering for `EPSG:4326` and `:http://www.opengis.net/gml/srs/epsg.xml#xxxx` SRS names.
|
|
9
|
+
* Improved styling for the default HTML page.
|
|
10
|
+
|
|
11
|
+
This increases interoperability with web-based clients and legacy libraries. GeoServer applies the same heuristic.
|
|
12
|
+
Clients that use the official OGC notations such as `urn:ogc:def:crs:EPSG::4326` and `http://www.opengis.net/def/crs/epsg/0/4326`
|
|
13
|
+
are not affected. If needed, this behavior can be disabled using `GISSERVER_FORCE_XY_EPSG_4326=False` and `GISSERVER_FORCE_XY_OLD_CRS=False`.
|
|
14
|
+
|
|
1
15
|
# 2025-05-29 (2.1)
|
|
2
16
|
|
|
3
17
|
* Added support for `ArrayField` field in CSV exports.
|
|
@@ -16,7 +30,6 @@
|
|
|
16
30
|
* Fixed crash when receiving WFS 1.0 POST requests.
|
|
17
31
|
* Fixed building readthedocs.
|
|
18
32
|
|
|
19
|
-
|
|
20
33
|
# 2025-04-28 (2.0)
|
|
21
34
|
|
|
22
35
|
* Added support for XML POST requests.
|
|
@@ -86,7 +99,6 @@ the notable API changes are:
|
|
|
86
99
|
* `XsdElement.is_geometry` was unneeded, use `XsdElement.type.is_geometry` now.
|
|
87
100
|
* `XsdElement.orm_path` points to the absolute path, and `XsdElement.local_orm_path` to the relative path.
|
|
88
101
|
|
|
89
|
-
|
|
90
102
|
# 2024-11-25 (1.5.0)
|
|
91
103
|
|
|
92
104
|
* Added `PROPERTYNAME` support
|
|
@@ -120,10 +132,11 @@ the notable API changes are:
|
|
|
120
132
|
* Django 5 support added.
|
|
121
133
|
|
|
122
134
|
## Contributors
|
|
135
|
+
|
|
123
136
|
We would like to thank the following contributors
|
|
124
137
|
for their work on this release.
|
|
125
138
|
|
|
126
|
-
|
|
139
|
+
* [tomdtp](https://github.com/tomdtp)
|
|
127
140
|
|
|
128
141
|
# 2023-06-08 (1.2.7)
|
|
129
142
|
|
|
@@ -143,25 +156,21 @@ for their work on this release.
|
|
|
143
156
|
|
|
144
157
|
* Fixed type assertion when `django.contrib.postgres` was not installed.
|
|
145
158
|
|
|
146
|
-
|
|
147
159
|
# 2022-09-07 (1.2.3)
|
|
148
160
|
|
|
149
161
|
* Added "geojson" as output format alias in `GetCapabilities` for ESRI ArcGIS online.
|
|
150
162
|
|
|
151
|
-
|
|
152
163
|
# 2022-07-28 (1.2.2)
|
|
153
164
|
|
|
154
165
|
* Optimized response writing, buffering provement gave ~12-15% speedup.
|
|
155
166
|
* Optimized GML response, reduced response size by ~9% by removing whitespace.
|
|
156
167
|
|
|
157
|
-
|
|
158
168
|
# 2022-04-13 (1.2.1)
|
|
159
169
|
|
|
160
170
|
* Fixed regression for auto-correcting xmlns for `<Filter>` tags that have leading whitespace.
|
|
161
171
|
* Fixed weird crashes when geometry field is not provided.
|
|
162
172
|
* Simplify `FeatureType.geometry_field` logic.
|
|
163
173
|
|
|
164
|
-
|
|
165
174
|
# 2022-04-11 (1.2)
|
|
166
175
|
|
|
167
176
|
* Added support for `maxOccurs=unbounded` elements (M2M, reverse foreign key, array fields).
|
|
@@ -182,7 +191,6 @@ for their work on this release.
|
|
|
182
191
|
* Various code cleanups.
|
|
183
192
|
* Dropped Python 3.6 support.
|
|
184
193
|
|
|
185
|
-
|
|
186
194
|
# 2021-05-17 (1.1.3)
|
|
187
195
|
|
|
188
196
|
* Included Django 3.2 in test matrix.
|
|
@@ -191,21 +199,18 @@ for their work on this release.
|
|
|
191
199
|
* Updated pre-commit hooks.
|
|
192
200
|
* Drop universal wheel declaration (Python 2 is no longer supported)
|
|
193
201
|
|
|
194
|
-
|
|
195
202
|
# 2020-12-22 (1.1.2)
|
|
196
203
|
|
|
197
204
|
* Fixed double ``>`` sign in ``<Filter xml..>>`` code when namespaces were auto-corrected.
|
|
198
205
|
* Fixed basic ``<Beyond>`` support for distance queries.
|
|
199
206
|
* Fixed parameter name for ``round()`` function.
|
|
200
207
|
|
|
201
|
-
|
|
202
208
|
# 2020-08-19 (1.1.1)
|
|
203
209
|
|
|
204
210
|
* Improve HTML page with "Using This WFS" section.
|
|
205
211
|
* Fixed Django 3.1 compatibility.
|
|
206
212
|
* Improved error message for `<fes:PropertyIsLike>` operator when comparing against a `<fes:ValueReference>` instead of `<fes:Literal>`.
|
|
207
213
|
|
|
208
|
-
|
|
209
214
|
# 2020-08-13 (1.1)
|
|
210
215
|
|
|
211
216
|
* Added browsable HTML views for WFS views, which can be extended/overwritten.
|
|
@@ -213,7 +218,6 @@ for their work on this release.
|
|
|
213
218
|
* Added "abstract" property to field classes to provide a description.
|
|
214
219
|
* Consider `?SERVICE=WFS` as default for `WFSView` views.
|
|
215
220
|
|
|
216
|
-
|
|
217
221
|
# 2020-07-21 (1.0)
|
|
218
222
|
|
|
219
223
|
* Added `FeatureType.show_name_field` to show/hide `<gml:name>` and GeoJSON `geometry_name`.
|
|
@@ -235,12 +239,10 @@ for their work on this release.
|
|
|
235
239
|
* Fixed exception message for bad SRID's.
|
|
236
240
|
* Internal code reorganizations.
|
|
237
241
|
|
|
238
|
-
|
|
239
242
|
# 2020-07-09 (0.9.1)
|
|
240
243
|
|
|
241
244
|
* Fixed `GetPropertyValue` calls for non-db optimized rendering.
|
|
242
245
|
|
|
243
|
-
|
|
244
246
|
# 2020-07-09 (0.9)
|
|
245
247
|
|
|
246
248
|
* Added support for nested, flattened fields.
|
|
@@ -260,7 +262,6 @@ for their work on this release.
|
|
|
260
262
|
* Optimize feature retrieval, only fetch the actual fields being displayed.
|
|
261
263
|
* Compactified XML output headers.
|
|
262
264
|
|
|
263
|
-
|
|
264
265
|
# 2020-07-06 (0.8.4)
|
|
265
266
|
|
|
266
267
|
* Added field renaming support with `field(..., model_attribute=...)`.
|
|
@@ -280,7 +281,6 @@ for their work on this release.
|
|
|
280
281
|
* Fix exposed WFS capabilities to pass cite WFS Simple conformance
|
|
281
282
|
* Fixed SORTBY parameter to handle renamed fields.
|
|
282
283
|
|
|
283
|
-
|
|
284
284
|
# 2020-06-30 (0.8.3)
|
|
285
285
|
|
|
286
286
|
* Added `GISSERVER_CAPABILITIES_BOUNDING_BOX` setting.
|
|
@@ -288,13 +288,11 @@ for their work on this release.
|
|
|
288
288
|
* Fixed `DescribeFeatureType` to return all types when TYPENAMES is not provided.
|
|
289
289
|
* Advertise `ImplementsMinimumXPath` in `GetCapabilities` for cite testing (other servers also do this while being incomplete).
|
|
290
290
|
|
|
291
|
-
|
|
292
291
|
# 2020-06-30 (0.8.2)
|
|
293
292
|
|
|
294
293
|
* Improve XPath matching support, allow "app:" prefix and root elements.
|
|
295
294
|
* Refactored `FeatureType.resolve_element()` to return an `XPathMatch` object
|
|
296
295
|
|
|
297
|
-
|
|
298
296
|
# 2020-06-29 (0.8.1)
|
|
299
297
|
|
|
300
298
|
* Added unpaginated GeoJSON support (performance is good enough).
|
|
@@ -305,7 +303,6 @@ for their work on this release.
|
|
|
305
303
|
* Optimized GeoJSON output by no longer selecting the other geometry fields.
|
|
306
304
|
* Added shortcut properties to `XsdComplexType`: `gml_elements` and `complex_elements`.
|
|
307
305
|
|
|
308
|
-
|
|
309
306
|
# 2020-06-25 (0.8)
|
|
310
307
|
|
|
311
308
|
* Added preliminary support to render complex field types (e.g. relations).
|
|
@@ -328,7 +325,6 @@ for their work on this release.
|
|
|
328
325
|
* Enforce using keyword-arguments on `FeatureType(...)`
|
|
329
326
|
* Internal code reorganizations/cleanups.
|
|
330
327
|
|
|
331
|
-
|
|
332
328
|
# 2020-06-08 (0.7)
|
|
333
329
|
|
|
334
330
|
* Added database-based rendering for GML/GeoJSON (disable with `GISSERVER_USE_DB_RENDERING=False`).
|
|
@@ -400,6 +396,6 @@ for their work on this release.
|
|
|
400
396
|
* First basic release that works in QGis.
|
|
401
397
|
* Features:
|
|
402
398
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
399
|
+
* WFS `GetCapabilities`
|
|
400
|
+
* WFS `DescribeFeatureType`
|
|
401
|
+
* WFS `GetFeature` with bbox and pagination support.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-gisserver
|
|
3
|
-
Version: 2.1
|
|
3
|
+
Version: 2.1.2
|
|
4
4
|
Summary: Django speaking WFS 2.0 (exposing GeoDjango model fields)
|
|
5
5
|
Home-page: https://github.com/amsterdam/django-gisserver
|
|
6
6
|
Author: Diederik van der Boor
|
|
@@ -46,6 +46,7 @@ Requires-Dist: pytest-cov>=2.11.1; extra == "tests"
|
|
|
46
46
|
Provides-Extra: docs
|
|
47
47
|
Requires-Dist: Django~=5.0; extra == "docs"
|
|
48
48
|
Requires-Dist: sphinxcontrib-django>=2.5; extra == "docs"
|
|
49
|
+
Requires-Dist: myst-parser>=3.0.1; extra == "docs"
|
|
49
50
|
Requires-Dist: psycopg2-binary>=2.8.4; extra == "docs"
|
|
50
51
|
Dynamic: author
|
|
51
52
|
Dynamic: author-email
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-gisserver
|
|
3
|
-
Version: 2.1
|
|
3
|
+
Version: 2.1.2
|
|
4
4
|
Summary: Django speaking WFS 2.0 (exposing GeoDjango model fields)
|
|
5
5
|
Home-page: https://github.com/amsterdam/django-gisserver
|
|
6
6
|
Author: Diederik van der Boor
|
|
@@ -46,6 +46,7 @@ Requires-Dist: pytest-cov>=2.11.1; extra == "tests"
|
|
|
46
46
|
Provides-Extra: docs
|
|
47
47
|
Requires-Dist: Django~=5.0; extra == "docs"
|
|
48
48
|
Requires-Dist: sphinxcontrib-django>=2.5; extra == "docs"
|
|
49
|
+
Requires-Dist: myst-parser>=3.0.1; extra == "docs"
|
|
49
50
|
Requires-Dist: psycopg2-binary>=2.8.4; extra == "docs"
|
|
50
51
|
Dynamic: author
|
|
51
52
|
Dynamic: author-email
|
|
@@ -64,6 +64,7 @@ gisserver/parsers/wfs20/base.py
|
|
|
64
64
|
gisserver/parsers/wfs20/projection.py
|
|
65
65
|
gisserver/parsers/wfs20/requests.py
|
|
66
66
|
gisserver/parsers/wfs20/stored.py
|
|
67
|
+
gisserver/static/gisserver/bootstrap-5.3.6.min.css
|
|
67
68
|
gisserver/static/gisserver/index.css
|
|
68
69
|
gisserver/templates/gisserver/base.html
|
|
69
70
|
gisserver/templates/gisserver/index.html
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.1.2" # follows PEP440
|
|
@@ -4,6 +4,8 @@ from django.conf import settings
|
|
|
4
4
|
from django.core.signals import setting_changed
|
|
5
5
|
from django.dispatch import receiver
|
|
6
6
|
|
|
7
|
+
_originals = {}
|
|
8
|
+
|
|
7
9
|
# -- feature flags
|
|
8
10
|
|
|
9
11
|
# Configure whether the <ows:WGS84BoundingBox> should be included in GetCapabilities.
|
|
@@ -30,6 +32,17 @@ GISSERVER_COUNT_NUMBER_MATCHED = getattr(settings, "GISSERVER_COUNT_NUMBER_MATCH
|
|
|
30
32
|
|
|
31
33
|
# -- output rendering
|
|
32
34
|
|
|
35
|
+
# Following https://docs.geoserver.org/stable/en/user/services/wfs/axis_order.html here:
|
|
36
|
+
# Whether the older EPSG:4326 notation (instead of the OGC recommended styles)
|
|
37
|
+
# should render in legacy longitude/latitude (x/y) ordering.
|
|
38
|
+
# This increases interoperability with legacy web clients,
|
|
39
|
+
# as others use urn:ogc:def:crs:EPSG::4326 or http://www.opengis.net/def/crs/epsg/0/4326.
|
|
40
|
+
GISSERVER_FORCE_XY_EPSG_4326 = getattr(settings, "GISSERVER_FORCE_XY_EPSG_4326", True)
|
|
41
|
+
|
|
42
|
+
# Whether the legacy CRS notation http://www.opengis.net/gml/srs/epsg.xml# should render in X/Y
|
|
43
|
+
GISSERVER_FORCE_XY_OLD_CRS = getattr(settings, "GISSERVER_FORCE_XY_OLD_CRS", True)
|
|
44
|
+
|
|
45
|
+
# Extra output formats for GetFeature (see documentation for details)
|
|
33
46
|
GISSERVER_EXTRA_OUTPUT_FORMATS = getattr(settings, "GISSERVER_EXTRA_OUTPUT_FORMATS", {})
|
|
34
47
|
GISSERVER_GET_FEATURE_OUTPUT_FORMATS = getattr(
|
|
35
48
|
settings, "GISSERVER_GET_FEATURE_OUTPUT_FORMATS", {}
|
|
@@ -59,4 +72,13 @@ def _on_settings_change(setting, value, enter, **kwargs):
|
|
|
59
72
|
if not setting.startswith("GISSERVER_"):
|
|
60
73
|
return
|
|
61
74
|
|
|
62
|
-
globals()
|
|
75
|
+
conf_module = globals()
|
|
76
|
+
if value is None and not enter:
|
|
77
|
+
# override_settings().disable() returns what the django settings module had.
|
|
78
|
+
# Revert to our defaults here instead.
|
|
79
|
+
value = _originals.get(setting)
|
|
80
|
+
else:
|
|
81
|
+
# Track defaults of this file for reverting to them
|
|
82
|
+
_originals.setdefault(setting, conf_module[setting])
|
|
83
|
+
|
|
84
|
+
conf_module[setting] = value
|
|
@@ -15,6 +15,7 @@ import pyproj
|
|
|
15
15
|
from django.contrib.gis.gdal import AxisOrder, CoordTransform, OGRGeometry, SpatialReference
|
|
16
16
|
from django.contrib.gis.geos import GEOSGeometry
|
|
17
17
|
|
|
18
|
+
from gisserver import conf
|
|
18
19
|
from gisserver.exceptions import ExternalValueError
|
|
19
20
|
|
|
20
21
|
CRS_URN_REGEX = re.compile(
|
|
@@ -36,7 +37,7 @@ __all__ = [
|
|
|
36
37
|
]
|
|
37
38
|
|
|
38
39
|
# Caches to avoid reinitializing WGS84 each time.
|
|
39
|
-
|
|
40
|
+
_COMMON_CRS_BY_STR = {}
|
|
40
41
|
_COMMON_CRS_BY_SRID = {}
|
|
41
42
|
|
|
42
43
|
|
|
@@ -74,7 +75,7 @@ _get_proj_crs_from_string = lru_cache(maxsize=10)(pyproj.CRS.from_string)
|
|
|
74
75
|
_get_proj_crs_from_authority = lru_cache(maxsize=10)(pyproj.CRS.from_authority)
|
|
75
76
|
|
|
76
77
|
|
|
77
|
-
@dataclass(frozen=True)
|
|
78
|
+
@dataclass(frozen=True, eq=False)
|
|
78
79
|
class CRS:
|
|
79
80
|
"""
|
|
80
81
|
Represents a CRS (Coordinate Reference System), which preferably follows the URN format
|
|
@@ -110,6 +111,9 @@ class CRS:
|
|
|
110
111
|
#: Original input
|
|
111
112
|
origin: str = field(init=False, default=None)
|
|
112
113
|
|
|
114
|
+
#: Tell whether the input format used the legacy notation.
|
|
115
|
+
force_xy: bool = False
|
|
116
|
+
|
|
113
117
|
@classmethod
|
|
114
118
|
def from_string(cls, uri: str | int) -> CRS:
|
|
115
119
|
"""
|
|
@@ -123,12 +127,15 @@ class CRS:
|
|
|
123
127
|
* A legacy CRS URI ("epsg:<SRID>", or "http://www.opengis.net/...").
|
|
124
128
|
* A numeric SRID (which calls :meth:`from_srid()`)
|
|
125
129
|
"""
|
|
130
|
+
if known_crs := _COMMON_CRS_BY_STR.get(uri):
|
|
131
|
+
return known_crs # Avoid object re-creation
|
|
132
|
+
|
|
126
133
|
if isinstance(uri, int) or uri.isdigit():
|
|
127
134
|
return cls.from_srid(int(uri))
|
|
128
135
|
elif uri.startswith("urn:"):
|
|
129
136
|
return cls._from_urn(uri)
|
|
130
137
|
else:
|
|
131
|
-
return cls.
|
|
138
|
+
return cls._from_prefix(uri)
|
|
132
139
|
|
|
133
140
|
@classmethod
|
|
134
141
|
def from_srid(cls, srid: int):
|
|
@@ -141,22 +148,19 @@ class CRS:
|
|
|
141
148
|
if common_crs := _COMMON_CRS_BY_SRID.get(srid):
|
|
142
149
|
return common_crs # Avoid object re-creation
|
|
143
150
|
|
|
144
|
-
|
|
151
|
+
return cls(
|
|
145
152
|
domain="ogc",
|
|
146
153
|
authority="EPSG",
|
|
147
154
|
version="",
|
|
148
155
|
crsid=str(srid),
|
|
149
156
|
srid=int(srid),
|
|
150
157
|
)
|
|
151
|
-
crs.__dict__["origin"] = srid
|
|
152
|
-
return crs
|
|
153
158
|
|
|
154
159
|
@classmethod
|
|
155
160
|
def _from_urn(cls, urn): # noqa: C901
|
|
156
|
-
"""Instantiate this class using a URN format.
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
161
|
+
"""Instantiate this class using a URN format.
|
|
162
|
+
This format is defined in https://portal.ogc.org/files/?artifact_id=30575.
|
|
163
|
+
"""
|
|
160
164
|
urn_match = CRS_URN_REGEX.match(urn)
|
|
161
165
|
if not urn_match:
|
|
162
166
|
raise ExternalValueError(f"Unknown CRS URN [{urn}] specified: {CRS_URN_REGEX.pattern}")
|
|
@@ -187,7 +191,7 @@ class CRS:
|
|
|
187
191
|
crs = cls(
|
|
188
192
|
domain=domain,
|
|
189
193
|
authority=authority,
|
|
190
|
-
version=urn_match.group(3),
|
|
194
|
+
version=urn_match.group(3) or "",
|
|
191
195
|
crsid=crsid,
|
|
192
196
|
srid=srid,
|
|
193
197
|
)
|
|
@@ -195,16 +199,30 @@ class CRS:
|
|
|
195
199
|
return crs
|
|
196
200
|
|
|
197
201
|
@classmethod
|
|
198
|
-
def
|
|
199
|
-
"""Instantiate this class from a
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
202
|
+
def _from_prefix(cls, uri):
|
|
203
|
+
"""Instantiate this class from a non-URI notation.
|
|
204
|
+
|
|
205
|
+
The modern URL format (:samp:`http://www.opengis.net/def/crs/epsg/0/{xxxx}`)
|
|
206
|
+
is defined in https://portal.ogc.org/files/?artifact_id=46361.
|
|
207
|
+
|
|
208
|
+
Older notations like :samp:`EPSG:{xxxx}` or legacy XML URLs like
|
|
209
|
+
and :samp`:http://www.opengis.net/gml/srs/epsg.xml#{xxxx}` are also supported.
|
|
210
|
+
"""
|
|
211
|
+
# Make sure origin uses the expected upper/lowercasing.
|
|
212
|
+
origin = uri.lower() if "://" in uri else uri.upper()
|
|
213
|
+
for prefix, force_xy in (
|
|
214
|
+
(
|
|
215
|
+
"EPSG:",
|
|
216
|
+
(conf.GISSERVER_FORCE_XY_EPSG_4326 and origin == "EPSG:4326"),
|
|
217
|
+
),
|
|
218
|
+
(
|
|
219
|
+
"http://www.opengis.net/gml/srs/epsg.xml#",
|
|
220
|
+
conf.GISSERVER_FORCE_XY_OLD_CRS,
|
|
221
|
+
),
|
|
222
|
+
("http://www.opengis.net/def/crs/epsg/0/", False),
|
|
205
223
|
):
|
|
206
|
-
if
|
|
207
|
-
crsid =
|
|
224
|
+
if origin.startswith(prefix):
|
|
225
|
+
crsid = origin[len(prefix) :]
|
|
208
226
|
try:
|
|
209
227
|
srid = int(crsid)
|
|
210
228
|
except ValueError:
|
|
@@ -218,16 +236,19 @@ class CRS:
|
|
|
218
236
|
version="",
|
|
219
237
|
crsid=crsid,
|
|
220
238
|
srid=srid,
|
|
239
|
+
force_xy=force_xy,
|
|
221
240
|
)
|
|
222
|
-
crs.__dict__["origin"] =
|
|
241
|
+
crs.__dict__["origin"] = origin
|
|
223
242
|
return crs
|
|
224
243
|
|
|
225
244
|
raise ExternalValueError(f"Unknown CRS URI [{uri}] specified")
|
|
226
245
|
|
|
227
246
|
@property
|
|
228
247
|
def legacy(self):
|
|
229
|
-
"""Return a legacy string in the format :samp:`
|
|
230
|
-
|
|
248
|
+
"""Return a legacy string in the format :samp:`http://www.opengis.net/gml/srs/epsg.xml#{srid}`."""
|
|
249
|
+
# This mirrors what GeoSever does, as this notation always has an axis ordering defined.
|
|
250
|
+
# Notations like EPSG:xxxx notation don't have such consistent usage.
|
|
251
|
+
return f"http://www.opengis.net/gml/srs/epsg.xml#{self.srid:d}"
|
|
231
252
|
|
|
232
253
|
@cached_property
|
|
233
254
|
def urn(self):
|
|
@@ -254,16 +275,29 @@ class CRS:
|
|
|
254
275
|
return [axis.direction for axis in proj_crs.axis_info]
|
|
255
276
|
|
|
256
277
|
def __str__(self):
|
|
257
|
-
return self.urn
|
|
278
|
+
return self.legacy if self.force_xy else self.urn
|
|
258
279
|
|
|
259
280
|
def __eq__(self, other):
|
|
260
281
|
if isinstance(other, CRS):
|
|
261
|
-
|
|
262
|
-
# EPSG:4326 specifies coordinates in lat/long order and CRS84 in long/lat order.
|
|
263
|
-
return self.authority == other.authority and self.srid == other.srid
|
|
282
|
+
return self.matches(other, compare_legacy=True)
|
|
264
283
|
else:
|
|
265
284
|
return NotImplemented
|
|
266
285
|
|
|
286
|
+
def matches(self, other, compare_legacy=True) -> bool:
|
|
287
|
+
"""Tell whether this CRS is identical to another one."""
|
|
288
|
+
return (
|
|
289
|
+
# "urn:ogc:def:crs:EPSG::4326" != "urn:ogc:def:crs:OGC::CRS84"
|
|
290
|
+
# even through they both share the same srid.
|
|
291
|
+
self.srid == other.srid
|
|
292
|
+
and self.authority == other.authority
|
|
293
|
+
and (
|
|
294
|
+
# Also, legacy notations like EPSG:4326 are treated differently,
|
|
295
|
+
# unless this is configured to not make a difference.
|
|
296
|
+
not compare_legacy
|
|
297
|
+
or self.force_xy == other.force_xy
|
|
298
|
+
)
|
|
299
|
+
)
|
|
300
|
+
|
|
267
301
|
def __hash__(self):
|
|
268
302
|
"""Used to match objects in a set."""
|
|
269
303
|
return hash((self.authority, self.srid))
|
|
@@ -272,7 +306,8 @@ class CRS:
|
|
|
272
306
|
"""Generate the GDAL Spatial Reference object."""
|
|
273
307
|
if self.backends[axis_order] is None:
|
|
274
308
|
backends = list(self.backends)
|
|
275
|
-
if self.origin:
|
|
309
|
+
if self.origin and "://" not in self.origin: # avoid downloads and OGR errors
|
|
310
|
+
# Passing the origin helps to detect CRS84 strings
|
|
276
311
|
backends[axis_order] = _get_spatial_reference(self.origin, "user", axis_order)
|
|
277
312
|
else:
|
|
278
313
|
backends[axis_order] = _get_spatial_reference(self.srid, "epsg", axis_order)
|
|
@@ -284,7 +319,12 @@ class CRS:
|
|
|
284
319
|
|
|
285
320
|
def _as_proj(self) -> pyproj.CRS:
|
|
286
321
|
"""Generate the PROJ CRS object"""
|
|
287
|
-
if
|
|
322
|
+
if (
|
|
323
|
+
isinstance(self.origin, str)
|
|
324
|
+
and not self.origin.isdigit()
|
|
325
|
+
and "epsg.xml#" not in self.origin # not supported
|
|
326
|
+
):
|
|
327
|
+
# Passing the origin helps to detect CRS84 strings
|
|
288
328
|
return _get_proj_crs_from_string(self.origin)
|
|
289
329
|
else:
|
|
290
330
|
return _get_proj_crs_from_authority(self.authority, self.srid)
|
|
@@ -293,7 +333,7 @@ class CRS:
|
|
|
293
333
|
self,
|
|
294
334
|
geometry: AnyGeometry,
|
|
295
335
|
clone=False,
|
|
296
|
-
axis_order: AxisOrder =
|
|
336
|
+
axis_order: AxisOrder | None = None,
|
|
297
337
|
) -> AnyGeometry | None:
|
|
298
338
|
"""Transform the geometry using this coordinate reference.
|
|
299
339
|
|
|
@@ -322,6 +362,12 @@ class CRS:
|
|
|
322
362
|
* PostGIS stores the data in x/y.
|
|
323
363
|
* WFS 1.0 used x/y, WFS 1.3 used y/x except for ``EPSG:4326``.
|
|
324
364
|
|
|
365
|
+
When receiving legacy notations (e.g. ``EPSG:4326`` instead of ``urn:ogc:def:crs:EPSG::4326``),
|
|
366
|
+
the data is still projected in legacy ordering, unless ``GISSERVER_FORCE_XY_...`` is disabled.
|
|
367
|
+
This reflects the design of `GeoServer Axis Ordering
|
|
368
|
+
<https://docs.geoserver.org/stable/en/user/services/wfs/axis_order.html>`_
|
|
369
|
+
to have maximum interoperability with legacy/JavaScript clients.
|
|
370
|
+
|
|
325
371
|
After GDAL/OGR changed the axis orientation, that information is
|
|
326
372
|
lost when the return value is loaded back into GEOS.
|
|
327
373
|
To address this, :meth:`tag_geometry` is called on the result.
|
|
@@ -331,6 +377,11 @@ class CRS:
|
|
|
331
377
|
For GEOS->GDAL->GEOS conversions, this makes no difference in efficiency.
|
|
332
378
|
:param axis_order: Which axis ordering to convert the geometry into (depends on the use-case).
|
|
333
379
|
"""
|
|
380
|
+
if axis_order is None:
|
|
381
|
+
# This transforms by default to WFS 2 axis ordering (e.g. latitude/longitude),
|
|
382
|
+
# unless a legacy notation is used (e.g. EPSG:4326).
|
|
383
|
+
axis_order = AxisOrder.TRADITIONAL if self.force_xy else AxisOrder.AUTHORITY
|
|
384
|
+
|
|
334
385
|
if isinstance(geometry, OGRGeometry):
|
|
335
386
|
transform = _get_coord_transform(geometry.srs, self._as_gdal(axis_order=axis_order))
|
|
336
387
|
return geometry.transform(transform, clone=clone)
|
|
@@ -377,12 +428,12 @@ class CRS:
|
|
|
377
428
|
This also makes sure that requests which use the same URN will get our CRS object
|
|
378
429
|
version, instead of a fresh new one.
|
|
379
430
|
"""
|
|
380
|
-
if self.authority == "EPSG":
|
|
431
|
+
if self.authority == "EPSG" and self.srid != 4326:
|
|
381
432
|
# Only register for EPSG to avoid conflicting axis ordering issues.
|
|
382
|
-
# (WGS84 and CRS84 both use srid 4326)
|
|
433
|
+
# (WGS84 and CRS84 both use srid 4326, and the 'EPSG:4326' notation is treated as legacy)
|
|
383
434
|
_COMMON_CRS_BY_SRID[self.srid] = self
|
|
384
435
|
|
|
385
|
-
|
|
436
|
+
_COMMON_CRS_BY_STR[str(self)] = self
|
|
386
437
|
|
|
387
438
|
|
|
388
439
|
#: Worldwide GPS, latitude/longitude (y/x). https://epsg.io/4326
|
|
@@ -42,16 +42,22 @@ class AsGML(functions.AsGML):
|
|
|
42
42
|
precision=conf.GISSERVER_DB_PRECISION,
|
|
43
43
|
envelope=False,
|
|
44
44
|
is_latlon=False,
|
|
45
|
+
long_urn=False,
|
|
45
46
|
**extra,
|
|
46
47
|
):
|
|
47
48
|
# Note that Django's AsGml the defaults are: version=2, precision=8
|
|
48
49
|
super().__init__(expression, version, precision, **extra)
|
|
49
50
|
self.envelope = envelope
|
|
50
51
|
self.is_latlon = is_latlon
|
|
52
|
+
self.long_urn = long_urn
|
|
51
53
|
|
|
52
54
|
def as_postgresql(self, compiler, connection, **extra_context):
|
|
53
55
|
# Fill options parameter (https://postgis.net/docs/ST_AsGML.html)
|
|
54
|
-
options =
|
|
56
|
+
options = 0
|
|
57
|
+
if self.long_urn:
|
|
58
|
+
options |= 1 # long CRS urn
|
|
59
|
+
if self.envelope:
|
|
60
|
+
options |= 32 # bbox
|
|
55
61
|
if self.is_latlon:
|
|
56
62
|
# PostGIS provides the data in longitude/latitude format (east/north to look like x/y).
|
|
57
63
|
# However, WFS 2.0 fixed their axis by following the authority. The ST_AsGML() doesn't
|
|
@@ -846,19 +846,24 @@ class FeatureType:
|
|
|
846
846
|
|
|
847
847
|
def resolve_crs(self, crs: CRS, locator="") -> CRS:
|
|
848
848
|
"""Check a parsed CRS against the list of supported types."""
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
849
|
+
for candidate in self.supported_crs:
|
|
850
|
+
# Not using self.supported_crs.index(crs), as that depends on CRS.__eq__():
|
|
851
|
+
if candidate.matches(crs, compare_legacy=False):
|
|
852
|
+
if candidate.force_xy != crs.force_xy:
|
|
853
|
+
# user provided legacy CRS, allow output in legacy CRS
|
|
854
|
+
return crs
|
|
855
|
+
else:
|
|
856
|
+
# Replace the parsed CRS with the declared one.
|
|
857
|
+
return candidate
|
|
858
|
+
|
|
859
|
+
# No match found
|
|
860
|
+
if conf.GISSERVER_SUPPORTED_CRS_ONLY:
|
|
861
|
+
raise InvalidParameterValue(
|
|
862
|
+
f"Feature '{self.name}' does not support CRS '{crs}'.",
|
|
863
|
+
locator=locator,
|
|
864
|
+
) from None
|
|
865
|
+
else:
|
|
866
|
+
return crs
|
|
862
867
|
|
|
863
868
|
|
|
864
869
|
class HDict(dict):
|
|
@@ -27,7 +27,7 @@ class BoundingBox:
|
|
|
27
27
|
Due to the overlap between 2 types, this element is used for 2 cases:
|
|
28
28
|
|
|
29
29
|
* The ``<ows:WGS84BoundingBox>`` element for ``GetCapabilities``.
|
|
30
|
-
* The ``<gml:Envelope>`` inside an``<gml:boundedBy>`` single feature.
|
|
30
|
+
* The ``<gml:Envelope>`` inside an ``<gml:boundedBy>`` single feature.
|
|
31
31
|
|
|
32
32
|
While both classes have no common base class (and exist in different schema's),
|
|
33
33
|
their properties are identical.
|