Skip to content

Commit

Permalink
better handling of CRS, not there yet
Browse files Browse the repository at this point in the history
  • Loading branch information
3nids committed Sep 29, 2023
1 parent 70f677c commit 0e842d1
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 38 deletions.
67 changes: 39 additions & 28 deletions docker/integration-tests/test_integration_qgis.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from qgis.core import (
QgsDataSourceUri,
QgsFeature,
QgsGeometry,
QgsPoint,
QgsProject,
QgsVectorDataProvider,
QgsVectorLayer,
Expand Down Expand Up @@ -59,31 +61,40 @@ def test_load_layer(self):

self.assertFalse(bool(layer.dataProvider().capabilities() & QgsVectorDataProvider.Capability.AddFeatures))

def test_load_with_basic_auth(self):
uri = QgsDataSourceUri()
uri.setParam("service", "wfs")
uri.setParam("typename", "tests.point_2056_10fields")
uri.setParam("url", ROOT_URL)
uri.setPassword(self.password)
uri.setUsername(self.user)

layer = QgsVectorLayer(uri.uri(), "point", "OAPIF")
self.assertTrue(layer.isValid())
layer = self.project.addMapLayer(layer)
self.assertIsNotNone(layer)

self.assertTrue(bool(layer.dataProvider().capabilities() & QgsVectorDataProvider.Capability.AddFeatures))

f = None
for f in layer.getFeatures():
pass
self.assertIsInstance(f, QgsFeature)

f["field_1"] = "xyz"
with edit(layer):
layer.updateFeature(f)

f = None
for f in layer.getFeatures("field_1='xyz'"):
pass
self.assertIsInstance(f, QgsFeature)
def test_load_and_edit_with_basic_auth(self):
for layer in ("tests.point_2056_10fields_local_json", "tests.point_2056_10fields"):
uri = QgsDataSourceUri()
uri.setParam("service", "wfs")
uri.setParam("typename", layer)
uri.setParam("url", ROOT_URL)
uri.setPassword(self.password)
uri.setUsername(self.user)

layer = QgsVectorLayer(uri.uri(), layer, "OAPIF")
self.assertTrue(layer.isValid())
layer = self.project.addMapLayer(layer)
self.assertIsNotNone(layer)

self.assertTrue(bool(layer.dataProvider().capabilities() & QgsVectorDataProvider.Capability.AddFeatures))

f = next(layer.getFeatures())
self.assertIsInstance(f, QgsFeature)

f["field_0"] = "xyz"
with edit(layer):
layer.updateFeature(f)

f = next(layer.getFeatures("field_0='xyz'"))
self.assertIsInstance(f, QgsFeature)

# create with geometry
f = QgsFeature()
f.setFields(layer.fields())
f["field_0"] = "Super Green"
geom = QgsGeometry.fromPoint(QgsPoint(2345678.0, 1234567.0))
f.setGeometry(geom)
with edit(layer):
layer.addFeature(f)
f = next(layer.getFeatures("field_0='Super Green'"))
self.assertIsInstance(f, QgsFeature)
self.assertEquals(geom, f.geometry())
28 changes: 24 additions & 4 deletions src/django_oapif/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def register_oapif_viewset(
key: Optional[str] = None,
geom_db_serializer: Optional[bool] = True,
geom_field: [str] = "geom",
crs: Optional[int] = None,
custom_serializer_attrs: Dict[str, Any] = None,
custom_viewset_attrs: Dict[str, Any] = None,
) -> Callable[[Any], models.Model]:
Expand All @@ -32,6 +33,7 @@ def register_oapif_viewset(
- key: allows to pass a custom name for the collection (defaults to the model's label)
- geom_db_serializer: delegate the geometry serialization to the DB
- geom_field: the geometry field name. If None, a null geometry is produced
- crs: the EPSG code, if empty CRS84 is assumed
- custom_serializer_attrs: allows to pass custom attributes to set to the serializer's Meta (e.g. custom fields)
- custom_viewset_attrs: allows to pass custom attributes to set to the viewset (e.g. custom pagination class)
"""
Expand All @@ -45,8 +47,6 @@ def register_oapif_viewset(
def inner(Model):
"""
Create the serializers
1 for viewsets for models with a geometry and
1 for viewsets for models without (aka 'non-geometric features').
"""

if geom_db_serializer and geom_field:
Expand All @@ -61,9 +61,15 @@ class Meta:

def to_internal_value(self, data):
# TODO: this needs improvement!!!
geo = data["geometry"]
geo = None
if "geometry" in data:
geo = data["geometry"]
if crs not in geo:
geo["crs"] = {"type": "name", "properties": {"name": f"urn:ogc:def:crs:EPSG::{Model.crs}"}}
data = super().to_internal_value(data)
data[geom_field] = GEOSGeometry(json.dumps(geo))
print(543534534, data)
if geo:
data[geom_field] = GEOSGeometry(json.dumps(geo))
return data

else:
Expand All @@ -74,6 +80,18 @@ class Meta:
fields = "__all__"
geo_field = geom_field

def to_internal_value(self, data):
# TODO: this needs improvement!!!
if "geometry" in data and "crs" not in data["geometry"]:
data["geometry"]["crs"] = {
"type": "name",
"properties": {"name": f"urn:ogc:def:crs:EPSG::{Model.crs}"},
}
print(data)
data = super().to_internal_value(data)
print(12344555, data)
return data

# Create the viewset
class Viewset(OAPIFDescribeModelViewSetMixin, viewsets.ModelViewSet):
queryset = Model.objects.all()
Expand Down Expand Up @@ -109,6 +127,8 @@ def get_queryset(self):

return qs

setattr(Model, "crs", crs)

# Apply custom serializer attributes
for k, v in custom_serializer_attrs.items():
setattr(AutoSerializer.Meta, k, v)
Expand Down
4 changes: 1 addition & 3 deletions src/django_oapif/filters.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from os import getenv

from django.contrib.gis.geos import Polygon
from pyproj import CRS, Transformer
from rest_framework.filters import BaseFilterBackend
Expand All @@ -19,7 +17,7 @@ def filter_queryset(self, request, queryset, view):

if user_crs:
user_crs = get_crs_from_uri(user_crs)
api_crs = CRS.from_epsg(int(getenv("GEOMETRY_SRID", "2056")))
api_crs = CRS.from_epsg(queryset.model.crs) # TODO support CRS84, not only EPSG codes
transformer = Transformer.from_crs(user_crs, api_crs)
LL = transformer.transform(coords[0], coords[1])
UR = transformer.transform(coords[2], coords[3])
Expand Down
2 changes: 1 addition & 1 deletion src/tests/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 4.2.5 on 2023-09-29 08:38
# Generated by Django 4.2.5 on 2023-09-29 13:15

import uuid

Expand Down
5 changes: 3 additions & 2 deletions src/tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
logger = logging.getLogger(__name__)


@register_oapif_viewset()
@register_oapif_viewset(crs=2056)
class Point_2056_10fields(ComputedFieldsModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
geom = models.PointField(srid=2056, verbose_name=_("Geometry"))
Expand All @@ -27,7 +27,7 @@ class Point_2056_10fields(ComputedFieldsModel):
field_9 = models.CharField(max_length=255, verbose_name=_("Field 9"), null=True, blank=True)


@register_oapif_viewset(geom_db_serializer=False)
@register_oapif_viewset(crs=2056, geom_db_serializer=False)
class Point_2056_10fields_local_json(ComputedFieldsModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
geom = models.PointField(srid=2056, verbose_name=_("Geometry"))
Expand Down Expand Up @@ -59,6 +59,7 @@ class NoGeom_10fields(ComputedFieldsModel):


@register_oapif_viewset(
crs=2056,
custom_viewset_attrs={"permission_classes": (permissions.DjangoModelPermissions,)},
)
class Line_2056_10fields(ComputedFieldsModel):
Expand Down

0 comments on commit 0e842d1

Please sign in to comment.