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/output/csv.py
CHANGED
|
@@ -9,20 +9,14 @@ from io import StringIO
|
|
|
9
9
|
from django.db import models
|
|
10
10
|
|
|
11
11
|
from gisserver import conf
|
|
12
|
-
from gisserver.db import
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
get_db_annotation,
|
|
16
|
-
get_db_geometry_selects,
|
|
17
|
-
)
|
|
18
|
-
from gisserver.geometries import CRS
|
|
19
|
-
from gisserver.queries import FeatureProjection
|
|
20
|
-
from gisserver.types import XsdElement
|
|
12
|
+
from gisserver.db import AsEWKT, get_db_rendered_geometry, replace_queryset_geometries
|
|
13
|
+
from gisserver.projection import FeatureProjection, FeatureRelation
|
|
14
|
+
from gisserver.types import GeometryXsdElement, XsdElement, XsdTypes
|
|
21
15
|
|
|
22
|
-
from .base import
|
|
16
|
+
from .base import CollectionOutputRenderer
|
|
23
17
|
|
|
24
18
|
|
|
25
|
-
class CSVRenderer(
|
|
19
|
+
class CSVRenderer(CollectionOutputRenderer):
|
|
26
20
|
"""Fast CSV renderer, using a stream response.
|
|
27
21
|
|
|
28
22
|
The complex encoding bits are handled by the "csv" library.
|
|
@@ -37,32 +31,22 @@ class CSVRenderer(OutputRenderer):
|
|
|
37
31
|
#: or one of the registered names like: "unix", "excel", "excel-tab"
|
|
38
32
|
dialect = "unix"
|
|
39
33
|
|
|
40
|
-
@classmethod
|
|
41
34
|
def decorate_queryset(
|
|
42
|
-
|
|
43
|
-
projection: FeatureProjection,
|
|
44
|
-
queryset: models.QuerySet,
|
|
45
|
-
output_crs: CRS,
|
|
46
|
-
**params,
|
|
35
|
+
self, projection: FeatureProjection, queryset: models.QuerySet
|
|
47
36
|
) -> models.QuerySet:
|
|
48
37
|
"""Make sure relations are included with select-related to avoid N-queries.
|
|
49
38
|
Using prefetch_related() isn't possible with .iterator().
|
|
50
39
|
"""
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
if related:
|
|
63
|
-
queryset = queryset.select_related(*related)
|
|
64
|
-
|
|
65
|
-
return queryset
|
|
40
|
+
# First, make sure no array or m2m elements exist,
|
|
41
|
+
# as these are not possible to render in CSV.
|
|
42
|
+
projection.remove_fields(
|
|
43
|
+
lambda e: (e.is_many and not e.is_array)
|
|
44
|
+
or e.type == XsdTypes.gmlCodeType # gml:name
|
|
45
|
+
or e.type == XsdTypes.gmlBoundingShapeType # gml:boundedBy
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# All database optimizations
|
|
49
|
+
return super().decorate_queryset(projection, queryset)
|
|
66
50
|
|
|
67
51
|
def render_stream(self):
|
|
68
52
|
self.output = output = StringIO()
|
|
@@ -78,18 +62,12 @@ class CSVRenderer(OutputRenderer):
|
|
|
78
62
|
output.write("\n\n")
|
|
79
63
|
|
|
80
64
|
# Write the header
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
# By using .iterator(), the results are streamed with as little memory as
|
|
89
|
-
# possible. Doing prefetch_related() is not possible now. That could only
|
|
90
|
-
# be implemented with cursor pagination for large sets for 1000+ results.
|
|
91
|
-
for instance in sub_collection.iterator():
|
|
92
|
-
writer.writerow(self.get_row(instance, projection, fields))
|
|
65
|
+
xsd_elements = projection.xsd_root_elements
|
|
66
|
+
writer.writerow(self.get_header(projection, xsd_elements))
|
|
67
|
+
|
|
68
|
+
# Write all rows
|
|
69
|
+
for instance in self.read_features(sub_collection):
|
|
70
|
+
writer.writerow(self.get_row(instance, projection, xsd_elements))
|
|
93
71
|
|
|
94
72
|
# Only perform a 'yield' every once in a while,
|
|
95
73
|
# as it goes back-and-forth for writing it to the client.
|
|
@@ -108,18 +86,22 @@ class CSVRenderer(OutputRenderer):
|
|
|
108
86
|
return f"{buffer}\n\n{message}\n"
|
|
109
87
|
|
|
110
88
|
def get_header(
|
|
111
|
-
self, projection: FeatureProjection, xsd_elements: list[XsdElement]
|
|
89
|
+
self, projection: FeatureProjection, xsd_elements: list[XsdElement], prefix=""
|
|
112
90
|
) -> list[str]:
|
|
113
91
|
"""Return all field names."""
|
|
114
92
|
names = []
|
|
115
93
|
append = names.append
|
|
116
94
|
for xsd_element in xsd_elements:
|
|
117
95
|
if xsd_element.type.is_complex_type:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
96
|
+
names.extend(
|
|
97
|
+
self.get_header(
|
|
98
|
+
projection,
|
|
99
|
+
xsd_elements=projection.xsd_child_nodes[xsd_element],
|
|
100
|
+
prefix=f"{prefix}{xsd_element.name}.",
|
|
101
|
+
)
|
|
102
|
+
)
|
|
121
103
|
else:
|
|
122
|
-
append(xsd_element.name)
|
|
104
|
+
append(f"{prefix}{xsd_element.name}")
|
|
123
105
|
|
|
124
106
|
return names
|
|
125
107
|
|
|
@@ -130,14 +112,19 @@ class CSVRenderer(OutputRenderer):
|
|
|
130
112
|
values = []
|
|
131
113
|
append = values.append
|
|
132
114
|
for xsd_element in xsd_elements:
|
|
133
|
-
if xsd_element.is_geometry:
|
|
134
|
-
append(self.render_geometry(instance, xsd_element))
|
|
115
|
+
if xsd_element.type.is_geometry:
|
|
116
|
+
append(self.render_geometry(projection, instance, xsd_element))
|
|
135
117
|
continue
|
|
136
118
|
|
|
137
119
|
value = xsd_element.get_value(instance)
|
|
138
120
|
if xsd_element.type.is_complex_type:
|
|
139
|
-
|
|
140
|
-
|
|
121
|
+
values.extend(
|
|
122
|
+
self.get_row(
|
|
123
|
+
instance=value,
|
|
124
|
+
projection=projection,
|
|
125
|
+
xsd_elements=projection.xsd_child_nodes[xsd_element],
|
|
126
|
+
)
|
|
127
|
+
)
|
|
141
128
|
elif isinstance(value, list):
|
|
142
129
|
# Array field
|
|
143
130
|
append(",".join(map(str, value)))
|
|
@@ -147,9 +134,16 @@ class CSVRenderer(OutputRenderer):
|
|
|
147
134
|
append(value)
|
|
148
135
|
return values
|
|
149
136
|
|
|
150
|
-
def render_geometry(
|
|
137
|
+
def render_geometry(
|
|
138
|
+
self,
|
|
139
|
+
projection: FeatureProjection,
|
|
140
|
+
instance: models.Model,
|
|
141
|
+
geo_element: GeometryXsdElement,
|
|
142
|
+
) -> str:
|
|
151
143
|
"""Render the contents of a geometry value."""
|
|
152
|
-
|
|
144
|
+
geometry = geo_element.get_value(instance)
|
|
145
|
+
projection.output_crs.apply_to(geometry)
|
|
146
|
+
return geometry.ewkt
|
|
153
147
|
|
|
154
148
|
|
|
155
149
|
class DBCSVRenderer(CSVRenderer):
|
|
@@ -157,25 +151,34 @@ class DBCSVRenderer(CSVRenderer):
|
|
|
157
151
|
This is about 40% faster than calling the GEOS C-API from python.
|
|
158
152
|
"""
|
|
159
153
|
|
|
160
|
-
@classmethod
|
|
161
154
|
def decorate_queryset(
|
|
162
|
-
|
|
163
|
-
projection: FeatureProjection,
|
|
164
|
-
queryset: models.QuerySet,
|
|
165
|
-
output_crs: CRS,
|
|
166
|
-
**params,
|
|
155
|
+
self, projection: FeatureProjection, queryset: models.QuerySet
|
|
167
156
|
) -> models.QuerySet:
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
157
|
+
# Instead of reading the binary geometry data, let the database generate EWKT data.
|
|
158
|
+
# As annotations can't be done for select_related() objects, prefetches are used instead.
|
|
159
|
+
queryset = super().decorate_queryset(projection, queryset)
|
|
160
|
+
return replace_queryset_geometries(
|
|
161
|
+
queryset, projection.geometry_elements, projection.output_crs, AsEWKT
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
def get_prefetch_queryset(
|
|
165
|
+
self, projection: FeatureProjection, feature_relation: FeatureRelation
|
|
166
|
+
) -> models.QuerySet | None:
|
|
167
|
+
"""Perform DB annotations for prefetched relations too."""
|
|
168
|
+
queryset = super().get_prefetch_queryset(projection, feature_relation)
|
|
169
|
+
if queryset is None:
|
|
170
|
+
return None
|
|
171
|
+
|
|
172
|
+
# Find which fields are GML elements, annotate these too.
|
|
173
|
+
return replace_queryset_geometries(
|
|
174
|
+
queryset, feature_relation.geometry_elements, projection.output_crs, AsEWKT
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
def render_geometry(
|
|
178
|
+
self,
|
|
179
|
+
projection: FeatureProjection,
|
|
180
|
+
instance: models.Model,
|
|
181
|
+
geo_element: GeometryXsdElement,
|
|
182
|
+
):
|
|
183
|
+
"""Render the geometry using a database-rendered version."""
|
|
184
|
+
return get_db_rendered_geometry(instance, geo_element, AsEWKT)
|
gisserver/output/geojson.py
CHANGED
|
@@ -6,16 +6,17 @@ from io import BytesIO
|
|
|
6
6
|
|
|
7
7
|
import orjson
|
|
8
8
|
from django.contrib.gis.db.models.functions import AsGeoJSON
|
|
9
|
+
from django.contrib.gis.gdal import AxisOrder
|
|
9
10
|
from django.db import models
|
|
10
11
|
from django.utils.functional import Promise
|
|
11
12
|
|
|
12
13
|
from gisserver import conf
|
|
14
|
+
from gisserver.crs import CRS84, WGS84
|
|
13
15
|
from gisserver.db import get_db_geometry_target
|
|
14
|
-
from gisserver.
|
|
15
|
-
from gisserver.queries import FeatureProjection
|
|
16
|
+
from gisserver.projection import FeatureProjection
|
|
16
17
|
from gisserver.types import XsdElement
|
|
17
18
|
|
|
18
|
-
from .base import
|
|
19
|
+
from .base import CollectionOutputRenderer
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
def _json_default(obj):
|
|
@@ -25,13 +26,13 @@ def _json_default(obj):
|
|
|
25
26
|
raise TypeError(f"Unable to serialize {obj.__class__.__name__} to JSON")
|
|
26
27
|
|
|
27
28
|
|
|
28
|
-
class GeoJsonRenderer(
|
|
29
|
+
class GeoJsonRenderer(CollectionOutputRenderer):
|
|
29
30
|
"""Fast GeoJSON renderer, using a stream response.
|
|
30
31
|
|
|
31
32
|
The complex encoding bits are handled by the C-library "orjson"
|
|
32
33
|
and the geojson property of GEOSGeometry.
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
While Django has a GeoJSON serializer
|
|
35
36
|
(see https://docs.djangoproject.com/en/3.0/ref/contrib/gis/serializers/),
|
|
36
37
|
it does not offer streaming response handling.
|
|
37
38
|
"""
|
|
@@ -41,29 +42,26 @@ class GeoJsonRenderer(OutputRenderer):
|
|
|
41
42
|
max_page_size = conf.GISSERVER_GEOJSON_MAX_PAGE_SIZE
|
|
42
43
|
chunk_size = 40_000
|
|
43
44
|
|
|
44
|
-
@classmethod
|
|
45
45
|
def decorate_queryset(
|
|
46
|
-
|
|
46
|
+
self,
|
|
47
47
|
projection: FeatureProjection,
|
|
48
48
|
queryset: models.QuerySet,
|
|
49
|
-
output_crs: CRS,
|
|
50
|
-
**params,
|
|
51
49
|
):
|
|
52
50
|
"""Redefine which fields to query, always include geometry, but remove all others"""
|
|
53
|
-
|
|
54
|
-
projection.
|
|
55
|
-
|
|
51
|
+
# make sure output CRS matches the coordinate ordering that GEOSGeometry.json returns
|
|
52
|
+
if projection.output_crs == WGS84:
|
|
53
|
+
projection.output_crs = CRS84
|
|
56
54
|
|
|
55
|
+
# Make sure geometry is always queried.
|
|
57
56
|
# Other geometries can be excluded as these are not rendered by 'properties'
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if other_geometries:
|
|
64
|
-
queryset = queryset.defer(*other_geometries)
|
|
57
|
+
main_geo_element = projection.feature_type.main_geometry_element
|
|
58
|
+
projection.add_field(main_geo_element)
|
|
59
|
+
projection.remove_fields(
|
|
60
|
+
lambda element: element.type.is_geometry and element is not main_geo_element
|
|
61
|
+
)
|
|
65
62
|
|
|
66
|
-
|
|
63
|
+
# Apply the normal optimizations with this altered projection
|
|
64
|
+
return super().decorate_queryset(projection, queryset)
|
|
67
65
|
|
|
68
66
|
def render_stream(self):
|
|
69
67
|
self.output = output = BytesIO()
|
|
@@ -87,7 +85,7 @@ class GeoJsonRenderer(OutputRenderer):
|
|
|
87
85
|
output.write(b",\n")
|
|
88
86
|
|
|
89
87
|
is_first = True
|
|
90
|
-
for instance in sub_collection:
|
|
88
|
+
for instance in self.read_features(sub_collection):
|
|
91
89
|
if is_first:
|
|
92
90
|
is_first = False
|
|
93
91
|
else:
|
|
@@ -117,7 +115,7 @@ class GeoJsonRenderer(OutputRenderer):
|
|
|
117
115
|
def render_exception(self, exception: Exception):
|
|
118
116
|
"""Render the exception in a format that fits with the output."""
|
|
119
117
|
message = super().render_exception(exception)
|
|
120
|
-
buffer = self.output.getvalue()
|
|
118
|
+
buffer = self.output.getvalue().decode()
|
|
121
119
|
return f"{buffer}/* {message} */\n"
|
|
122
120
|
|
|
123
121
|
def render_feature(self, projection: FeatureProjection, instance: models.Model) -> bytes:
|
|
@@ -137,7 +135,7 @@ class GeoJsonRenderer(OutputRenderer):
|
|
|
137
135
|
return b' {"type":"Feature","id":%b,%b"geometry":%b,"properties":%b}' % (
|
|
138
136
|
orjson.dumps(f"{feature_type.name}.{instance.pk}"),
|
|
139
137
|
json_geometry_name,
|
|
140
|
-
self.render_geometry(
|
|
138
|
+
self.render_geometry(projection, instance),
|
|
141
139
|
orjson.dumps(properties, default=_json_default),
|
|
142
140
|
)
|
|
143
141
|
|
|
@@ -150,15 +148,17 @@ class GeoJsonRenderer(OutputRenderer):
|
|
|
150
148
|
else:
|
|
151
149
|
return value
|
|
152
150
|
|
|
153
|
-
def render_geometry(self,
|
|
151
|
+
def render_geometry(self, projection: FeatureProjection, instance: models.Model) -> bytes:
|
|
154
152
|
"""Generate the proper GeoJSON notation for a geometry.
|
|
155
153
|
This calls the GDAL C-API rendering found in 'GEOSGeometry.json'
|
|
156
154
|
"""
|
|
157
|
-
geometry =
|
|
155
|
+
geometry = projection.get_main_geometry_value(instance)
|
|
158
156
|
if geometry is None:
|
|
159
157
|
return b"null"
|
|
160
158
|
|
|
161
|
-
|
|
159
|
+
# The GeoJSON spec requires coordinates to be x,y (longitude,latitude),
|
|
160
|
+
# so web-based clients don't have to ship projection tables.
|
|
161
|
+
projection.output_crs.apply_to(geometry, axis_order=AxisOrder.TRADITIONAL)
|
|
162
162
|
return geometry.json.encode()
|
|
163
163
|
|
|
164
164
|
def get_header(self) -> dict:
|
|
@@ -167,11 +167,12 @@ class GeoJsonRenderer(OutputRenderer):
|
|
|
167
167
|
The format is based on the WFS 3.0 DRAFT. The count fields are moved
|
|
168
168
|
to the footer allowing them to be calculated without performing queries.
|
|
169
169
|
"""
|
|
170
|
+
output_crs = self.collection.results[0].projection.output_crs
|
|
170
171
|
return {
|
|
171
172
|
"type": "FeatureCollection",
|
|
172
173
|
"timeStamp": self._format_geojson_value(self.collection.timestamp),
|
|
173
174
|
# "numberReturned": is written at the end for better query performance.
|
|
174
|
-
"crs": {"type": "name", "properties": {"name": str(
|
|
175
|
+
"crs": {"type": "name", "properties": {"name": str(output_crs)}},
|
|
175
176
|
}
|
|
176
177
|
|
|
177
178
|
def get_footer(self) -> dict:
|
|
@@ -227,7 +228,7 @@ class GeoJsonRenderer(OutputRenderer):
|
|
|
227
228
|
"""
|
|
228
229
|
props = {}
|
|
229
230
|
for xsd_element in xsd_elements:
|
|
230
|
-
if not xsd_element.is_geometry:
|
|
231
|
+
if not xsd_element.type.is_geometry:
|
|
231
232
|
value = xsd_element.get_value(instance)
|
|
232
233
|
if xsd_element.type.is_complex_type:
|
|
233
234
|
# Nested object data
|
|
@@ -258,30 +259,29 @@ class DBGeoJsonRenderer(GeoJsonRenderer):
|
|
|
258
259
|
This is even more efficient than calling the C-API for each feature.
|
|
259
260
|
"""
|
|
260
261
|
|
|
261
|
-
|
|
262
|
-
def decorate_queryset(self, projection: FeatureProjection, queryset, output_crs, **params):
|
|
262
|
+
def decorate_queryset(self, projection: FeatureProjection, queryset):
|
|
263
263
|
"""Update the queryset to let the database render the GML output.
|
|
264
264
|
This is far more efficient than GeoDjango's logic, which performs a
|
|
265
265
|
C-API call for every single coordinate of a geometry.
|
|
266
266
|
"""
|
|
267
|
-
queryset = super().decorate_queryset(projection, queryset
|
|
267
|
+
queryset = super().decorate_queryset(projection, queryset)
|
|
268
268
|
# If desired, the entire FeatureCollection could be rendered
|
|
269
269
|
# in PostgreSQL as well: https://postgis.net/docs/ST_AsGeoJSON.html
|
|
270
|
-
|
|
271
|
-
if
|
|
272
|
-
queryset = queryset.defer(
|
|
270
|
+
main_geo_element = projection.feature_type.main_geometry_element
|
|
271
|
+
if main_geo_element is not None:
|
|
272
|
+
queryset = queryset.defer(main_geo_element.orm_path).annotate(
|
|
273
273
|
_as_db_geojson=AsGeoJSON(
|
|
274
|
-
get_db_geometry_target(
|
|
274
|
+
get_db_geometry_target(main_geo_element, projection.output_crs),
|
|
275
275
|
precision=conf.GISSERVER_DB_PRECISION,
|
|
276
276
|
)
|
|
277
277
|
)
|
|
278
278
|
|
|
279
279
|
return queryset
|
|
280
280
|
|
|
281
|
-
def render_geometry(self,
|
|
281
|
+
def render_geometry(self, projection: FeatureProjection, instance: models.Model) -> bytes:
|
|
282
282
|
"""Generate the proper GeoJSON notation for a geometry"""
|
|
283
283
|
# Database server rendering
|
|
284
|
-
if
|
|
284
|
+
if projection.main_geometry_element is None:
|
|
285
285
|
return b"null"
|
|
286
286
|
|
|
287
287
|
geojson = instance._as_db_geojson
|