Skip to content

Commit

Permalink
basic implementation
Browse files Browse the repository at this point in the history
sorted test
  • Loading branch information
why-not-try-calmer committed Aug 12, 2023
1 parent 51d76c6 commit 66e7a6d
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 1 deletion.
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
django>=4
django-computedfields
fiona
psycopg2-binary
transifex-client
djangorestframework
Expand Down
5 changes: 4 additions & 1 deletion src/django_oapif/decorators.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from typing import Any, Callable, Dict, Optional

from django.db.models import Model
from rest_framework import viewsets
from rest_framework import renderers, viewsets
from rest_framework_gis.serializers import GeoFeatureModelSerializer

from django_oapif.metadata import OAPIFMetadata
from django_oapif.mixins import OAPIFDescribeModelViewSetMixin
from django_oapif.renderers import FGBRenderer
from django_oapif.urls import oapif_router

from .filters import BboxFilterBackend
Expand Down Expand Up @@ -57,6 +58,7 @@ class Meta:
else:
"""
viewset_serializer_class = AutoSerializer
viewset_renderer_classes = [renderers.JSONRenderer, FGBRenderer]
viewset_oapif_geom_lookup = (
"geom" # one day this will be retrieved automatically from the serializer
)
Expand All @@ -65,6 +67,7 @@ class Meta:
class Viewset(OAPIFDescribeModelViewSetMixin, viewsets.ModelViewSet):
queryset = Model.objects.all()
serializer_class = viewset_serializer_class
renderer_classes = viewset_renderer_classes

# TODO: these should probably be moved to the mixin
oapif_title = Model._meta.verbose_name
Expand Down
29 changes: 29 additions & 0 deletions src/django_oapif/renderers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import io
from typing import OrderedDict

import fiona
from django.http import StreamingHttpResponse
from rest_framework import renderers


class FGBRenderer(renderers.BaseRenderer):
format = "fgb"
media_type = "application/octet-stream"
# FIXME: This should be sent by the model.
schema = {"geometry": "Point", "properties": {"name": "str", "_serialized": "str"}}

def render(
self, data: OrderedDict, accepted_media_type=None, renderer_context=None
) -> StreamingHttpResponse:
"""Renders pre-serialized Python objects as a flatgeobuf binary stream"""
features = (fiona.Feature.from_dict(obj) for obj in data["features"])
buffer_wrapper = io.BytesIO()

with fiona.open(
buffer_wrapper, mode="w", driver="FlatGeobuf", schema=self.schema
) as fh:
for feature in features:
fh.write(feature)

buffer_wrapper.seek(0)
return StreamingHttpResponse(buffer_wrapper)
48 changes: 48 additions & 0 deletions src/signalo/core/tests.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import cProfile
import io
import logging
import os
import pstats
from itertools import islice
from operator import itemgetter
from typing import Callable, Iterable, Tuple

import fiona
from django.contrib.auth.models import User
from django.core.management import call_command
from rest_framework.test import APITestCase

from django_oapif.renderers import FGBRenderer
from signalo.core.views import PoleSerializer

from .models import Azimuth, Pole, Sign
Expand Down Expand Up @@ -189,3 +193,47 @@ def test_admin_items_options(self):

self.assertEqual(allowed_body, expected)
self.assertEqual(allowed_headers, allowed_body)


class TestRenderers(APITestCase):
@classmethod
def setUpTestData(cls):
call_command("populate_vl")
call_command("populate_signs_poles")

cls.collection_url = collections_url + "/signalo_core.pole"
cls.items_url = cls.collection_url + "/items"

def test_flatgeobuf(self):
json_features = self.client.get(self.items_url, {"format": "json"}).json()[
"features"
]
json_coordinates = [
tuple(json_feature["geometry"]["coordinates"])
for json_feature in json_features
]
output_stream = io.BytesIO(
self.client.get(self.items_url, {"format": "fgb"}, streaming=True).content
)

with fiona.open(
output_stream, mode="r", driver="FlatGeobuf", schema=FGBRenderer.schema
) as fgb_features:
fgb_coordinates = [
fgb_feature.geometry["coordinates"] for fgb_feature in fgb_features
]

self.assertEqual(
set(fgb_coordinates),
set(json_coordinates),
msg="The two sources map to the same set of coordinates.",
)

fgb_coordinates.sort(key=itemgetter(0, 1))
json_coordinates.sort(key=itemgetter(0, 1))

self.assertEqual(
fgb_coordinates,
json_coordinates,
msg="Once sorted, the two sources map to the same list of coordinates.",
)

0 comments on commit 66e7a6d

Please sign in to comment.