Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A custom serializer that outputs object_detail and object_list endpoints in KML, GeoJSON and SHP #36

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 27 additions & 13 deletions boundaryservice/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,50 @@
from django.contrib.gis.measure import D
from tastypie import fields
from tastypie.serializers import Serializer
from boundaryservice.serializers import BoundaryGeoSerializer
from boundaryservice.serializers import BoundarySetGeoSerializer

from boundaryservice.authentication import NoOpApiKeyAuthentication
from boundaryservice.models import BoundarySet, Boundary
from boundaryservice.tastyhacks import SluggedResource
from boundaryservice.throttle import AnonymousThrottle


class BoundarySetResource(SluggedResource):
boundaries = fields.ToManyField('boundaryservice.resources.BoundaryResource', 'boundaries')

class Meta:
queryset = BoundarySet.objects.all()
serializer = Serializer(formats=['json', 'jsonp'], content_types = {'json': 'application/json', 'jsonp': 'text/javascript'})
serializer = BoundarySetGeoSerializer()
resource_name = 'boundary-set'
excludes = ['id', 'singular', 'kind_first']
allowed_methods = ['get']
authentication = NoOpApiKeyAuthentication()
#throttle = AnonymousThrottle(throttle_at=100)

def alter_list_data_to_serialize(self, request, data):
"""
Allow the selection of simple, full or no shapes using a query parameter.
"""
data['shape_type'] = request.GET.get('shape_type', 'simple')
return data

def alter_detail_data_to_serialize(self, request, bundle):
"""
Allow the selection of simple, full or no shapes using a query parameter.
"""
bundle.shape_type = request.GET.get('shape_type', 'simple')
return bundle


class BoundaryResource(SluggedResource):
set = fields.ForeignKey(BoundarySetResource, 'set')

class Meta:
queryset = Boundary.objects.all()
serializer = Serializer(formats=['json', 'jsonp'], content_types = {'json': 'application/json', 'jsonp': 'text/javascript'})
serializer = BoundaryGeoSerializer()
resource_name = 'boundary'
excludes = ['id', 'display_name']
excludes = ['id', 'display_name', 'centroid']
allowed_methods = ['get']
authentication = NoOpApiKeyAuthentication()
#throttle = AnonymousThrottle(throttle_at=100)
Expand All @@ -38,28 +56,24 @@ def alter_list_data_to_serialize(self, request, data):
Allow the selection of simple, full or no shapes using a query parameter.
"""
shape_type = request.GET.get('shape_type', 'simple')

data['shape_type'] = 'shape'
for obj in data['objects']:
if shape_type != 'simple':
if shape_type == 'full':
del obj.data['simple_shape']

if shape_type != 'full':
else:
del obj.data['shape']

return data

def alter_detail_data_to_serialize(self, request, bundle):
"""
Allow the selection of simple, full or no shapes using a query parameter.
"""
shape_type = request.GET.get('shape_type', 'simple')

if shape_type != 'simple':
bundle.shape_type = shape_type
if shape_type == 'full':
del bundle.data['simple_shape']

if shape_type != 'full':
else:
del bundle.data['shape']

return bundle

def build_filters(self, filters=None):
Expand Down
259 changes: 259 additions & 0 deletions boundaryservice/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
import json
from tastypie.bundle import Bundle
from tastypie.serializers import Serializer
from boundaryservice.shp import ShpSerializer
from django.template.loader import render_to_string
from django.core.serializers.json import DjangoJSONEncoder


class BaseGeoSerializer(Serializer):
"""
Adds some common geospatial outputs to the standard serializer.

Supported formats:

* JSON (Standard issue)
* JSONP (Standard issue)
* KML
* GeoJSON

"""
formats = [
'json',
'jsonp',
'kml',
'geojson',
'shp',
]
content_types = {
'json': 'application/json',
'jsonp': 'text/javascript',
'kml': 'application/vnd.google-earth.kml+xml',
'geojson': 'application/geo+json',
'shp': 'application/zip',
}

def get_shape_attr(self, shape_type):
"""
Which shape attribute the user would like us to return.
"""
if shape_type == 'full':
return 'shape'
else:
return 'simple_shape'


class BoundarySetGeoSerializer(BaseGeoSerializer):
"""
Applies the geospatial serializer to the BoundarySet model.
"""
def to_shp(self, data, options=None):
"""
Converts the bundle to a SHP serialization.
"""
simple_obj = self.to_simple(data, options)
if isinstance(data, dict):
# List data
shape_attr = self.get_shape_attr(data['shape_type'])
boundary_list = []
for bset in data['objects']:
for boundary in bset.obj.boundaries.all():
boundary_list.append(boundary)
return ShpSerializer(
queryset=boundary_list,
geo_field=shape_attr,
excludes=['id', 'singular', 'kind_first', 'metadata'],
)()
elif isinstance(data, Bundle):
# Detail data
shape_attr = self.get_shape_attr(data.shape_type)
boundary_list = []
for boundary in data.obj.boundaries.all():
boundary_list.append(boundary)
return ShpSerializer(
queryset=boundary_list,
geo_field=shape_attr,
readme=simple_obj['notes'],
file_name=boundary_list[0].kind.lower(),
excludes=['id', 'singular', 'kind_first', 'metadata'],
)()

def to_geojson(self, data, options=None):
"""
Converts the bundle to a GeoJSON seralization.
"""
# Hook the GeoJSON output to the object
simple_obj = self.to_simple(data, options)
if isinstance(data, dict):
# List data
shape_attr = self.get_shape_attr(data['shape_type'])
boundary_list = []
for bset in data['objects']:
simple_bset = self.to_simple(bset, options)
for boundary in bset.obj.boundaries.all():
boundary.geojson = getattr(boundary, shape_attr).geojson
boundary.set_uri = simple_bset['resource_uri']
api_name = "".join(boundary.set_uri.split("/")[:2])
boundary.resource_uri = "/%s/boundary/%s/" % (api_name, boundary.slug)
boundary_list.append(boundary)
geojson = json.loads(render_to_string('object_list.geojson', {
'boundary_list': boundary_list,
}))
response_dict = dict(meta=simple_obj['meta'], geojson=geojson)
return json.dumps(
response_dict,
cls=DjangoJSONEncoder,
sort_keys=False,
ensure_ascii=False
)
elif isinstance(data, Bundle):
shape_attr = self.get_shape_attr(data.shape_type)
# Clean up the boundaries
boundary_list = []
for boundary in data.obj.boundaries.all():
boundary.geojson = getattr(boundary, shape_attr).geojson
boundary.set_uri = simple_obj['resource_uri']
api_name = "".join(boundary.set_uri.split("/")[:2])
boundary.resource_uri = "/%s/boundary/%s/" % (api_name, boundary.slug)
boundary_list.append(boundary)
# Render the result using a template and pass it out
return render_to_string('object_list.geojson', {
'boundary_list': boundary_list,
})

def to_kml(self, data, options=None):
"""
Converts the bundle to a KML serialization.
"""
# Hook the GeoJSON output to the object
simple_obj = self.to_simple(data, options)
if isinstance(data, dict):
# List data
shape_attr = self.get_shape_attr(data['shape_type'])
boundary_list = []
for bset in data['objects']:
simple_bset = self.to_simple(bset, options)
for boundary in bset.obj.boundaries.all():
boundary.kml = getattr(boundary, shape_attr).kml
boundary.set_uri = simple_bset['resource_uri']
api_name = "".join(boundary.set_uri.split("/")[:2])
boundary.resource_uri = "/%s/boundary/%s/" % (api_name, boundary.slug)
boundary_list.append(boundary)
return render_to_string('object_list.kml', {
'boundary_list': boundary_list,
})
elif isinstance(data, Bundle):
shape_attr = self.get_shape_attr(data.shape_type)
# Clean up the boundaries
boundary_list = []
for boundary in data.obj.boundaries.all():
boundary.kml = getattr(boundary, shape_attr).kml
boundary.set_uri = simple_obj['resource_uri']
api_name = "".join(boundary.set_uri.split("/")[:2])
boundary.resource_uri = "/%s/boundary/%s/" % (api_name, boundary.slug)
boundary_list.append(boundary)
# Render the result using a template and pass it out
return render_to_string('object_list.kml', {
'boundary_list': boundary_list,
})


class BoundaryGeoSerializer(BaseGeoSerializer):
"""
Applies the geospatial serializer to the Boundary model.
"""
def to_shp(self, data, options=None):
"""
Converts the bundle to a SHP serialization.
"""
# Hook the KML output to the object
simple_obj = self.to_simple(data, options)
# Figure out if it's list data or detail data
if isinstance(data, dict):
# List data
shape_attr = self.get_shape_attr(data['shape_type'])
boundary_list = []
for bundle in data['objects']:
boundary_list.append(bundle.obj)
return ShpSerializer(
queryset=boundary_list,
geo_field=shape_attr,
excludes=['id', 'singular', 'kind_first', 'metadata'],
)()

elif isinstance(data, Bundle):
# Detail data
shape_attr = self.get_shape_attr(data.shape_type)
simple_obj['kml'] = getattr(data.obj, shape_attr).kml
return ShpSerializer(
queryset=[data.obj],
geo_field=shape_attr,
file_name=data.obj.kind.lower(),
excludes=['id', 'singular', 'kind_first', 'metadata'],
)()

def to_geojson(self, data, options=None):
"""
Converts the bundle to a GeoJSON seralization.
"""
simple_obj = self.to_simple(data, options)
# Figure out if it's list data or detail data
if isinstance(data, dict):
# List data
shape_attr = self.get_shape_attr(data['shape_type'])
boundary_list = []
for bundle in data['objects']:
simple_boundary = self.to_simple(bundle, options)
simple_boundary['geojson'] = getattr(bundle.obj, shape_attr).geojson
simple_boundary['set_uri'] = simple_boundary['set']
boundary_list.append(simple_boundary)
geojson = json.loads(render_to_string('object_list.geojson', {
'boundary_list': boundary_list,
}))
response_dict = dict(meta=simple_obj['meta'], geojson=geojson)
return json.dumps(
response_dict,
cls=DjangoJSONEncoder,
sort_keys=False,
ensure_ascii=False
)

elif isinstance(data, Bundle):
# Detail data
shape_attr = self.get_shape_attr(data.shape_type)
simple_obj['geojson'] = getattr(data.obj, shape_attr).geojson
simple_obj['set_uri'] = simple_obj['set']
# Render the result using a template and pass it out
return render_to_string('object_detail.geojson', {
'obj': simple_obj,
})

def to_kml(self, data, options=None):
"""
Converts the bundle to a KML serialization.
"""
# Hook the KML output to the object
simple_obj = self.to_simple(data, options)
# Figure out if it's list data or detail data
if isinstance(data, dict):
# List data
shape_attr = self.get_shape_attr(data['shape_type'])
boundary_list = []
for bundle in data['objects']:
simple_boundary = self.to_simple(bundle, options)
simple_boundary['kml'] = getattr(bundle.obj, shape_attr).kml
simple_boundary['set_uri'] = simple_boundary['set']
boundary_list.append(simple_boundary)
return render_to_string('object_list.kml', {
'boundary_list': boundary_list,
})

elif isinstance(data, Bundle):
# Detail data
shape_attr = self.get_shape_attr(data.shape_type)
simple_obj['kml'] = getattr(data.obj, shape_attr).kml
simple_obj['set_uri'] = simple_obj['set']
# Render the result using a template and pass it out
return render_to_string('object_detail.kml', {
'obj': simple_obj,
})
Loading