Skip to content

Commit

Permalink
Merge 9e2a2a5 into ce07e7d
Browse files Browse the repository at this point in the history
  • Loading branch information
JesseCrocker committed Aug 22, 2014
2 parents ce07e7d + 9e2a2a5 commit 003a941
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 2 deletions.
26 changes: 24 additions & 2 deletions rest_framework_gis/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from rest_framework.filters import BaseFilterBackend
from rest_framework.exceptions import ParseError

from tilenames import tileEdges

try:
import django_filters
except ImportError:
Expand All @@ -19,7 +21,8 @@
__all__ = [
'InBBOXFilter',
'GeometryFilter',
'GeoFilterSet'
'GeoFilterSet',
'TMSTileFilter'
]


Expand Down Expand Up @@ -72,4 +75,23 @@ class GeoFilterSet(django_filters.FilterSet):
def __new__(cls, *args, **kwargs):
cls.filter_overrides.update(cls.GEOFILTER_FOR_DBFIELD_DEFAULTS)
cls.LOOKUP_TYPES = sorted(models.sql.query.ALL_TERMS)
return super(GeoFilterSet, cls).__new__(cls)
return super(GeoFilterSet, cls).__new__(cls)


class TMSTileFilter(InBBOXFilter):

tile_param = 'tile' # The URL query paramater which contains the tile address

def get_filter_bbox(self, request):
tile_string = request.QUERY_PARAMS.get(self.tile_param, None)
if not tile_string:
return None

try:
z, x, y = (int(n) for n in tile_string.split('/'))
except ValueError:
raise ParseError("Not valid tile string in parameter %s."
% self.tile_param)

bbox = Polygon.from_bbox(tileEdges(x, y, z))
return bbox
38 changes: 38 additions & 0 deletions rest_framework_gis/tilenames.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env python
#-------------------------------------------------------
# Translates between lat/long and the slippy-map tile
# numbering scheme
#
# http://wiki.openstreetmap.org/index.php/Slippy_map_tilenames
#
# Written by Oliver White, 2007
# This file is public-domain
#-------------------------------------------------------
from math import *

def numTiles(z):
return(pow(2,z))

def latEdges(y,z):
n = numTiles(z)
unit = 1 / n
relY1 = y * unit
relY2 = relY1 + unit
lat1 = mercatorToLat(pi * (1 - 2 * relY1))
lat2 = mercatorToLat(pi * (1 - 2 * relY2))
return(lat1,lat2)

def lonEdges(x,z):
n = numTiles(z)
unit = 360 / n
lon1 = -180 + x * unit
lon2 = lon1 + unit
return(lon1,lon2)

def tileEdges(x,y,z):
lat1,lat2 = latEdges(y,z)
lon1,lon2 = lonEdges(x,z)
return((lon1, lat2, lon2, lat1)) # w, s, e, n

def mercatorToLat(mercatorY):
return(degrees(atan(sinh(mercatorY))))
52 changes: 52 additions & 0 deletions tests/django_restframework_gis_tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ def setUp(self):
self.geojson_location_list_url = reverse('api_geojson_location_list')
self.location_contained_in_bbox_list_url = reverse('api_geojson_location_list_contained_in_bbox_filter')
self.location_overlaps_bbox_list_url = reverse('api_geojson_location_list_overlaps_bbox_filter')
self.location_contained_in_tile_list_url = reverse('api_geojson_location_list_contained_in_tile_filter')
self.location_overlaps_tile_list_url = reverse('api_geojson_location_list_overlaps_tile_filter')
self.geos_error_message = 'Invalid format: string or unicode input unrecognized as WKT EWKT, and HEXEWKB.'
self.geojson_contained_in_geometry = reverse('api_geojson_contained_in_geometry')

Expand Down Expand Up @@ -466,6 +468,56 @@ def test_inBBOXFilter_filtering(self):
self.assertEqual(len(response.data['features']), 3)
for result in response.data['features']:
self.assertEqual(result['properties']['name'] in ('isContained', 'isEqualToBounds', 'overlaps'), True)

def test_TileFilter_filtering(self):
"""
Checks that the TMSTileFilter returns only objects strictly contained
in the bounding box given by the tile URL parameter
"""
self.assertEqual(Location.objects.count(), 0)

# Bounding box
z = 1
x = 1
y = 0

url_params = '?tile=%d/%d/%d&format=json' % (z, x, y)

# Square with bottom left at (1,1), top right at (9,9)
isContained = Location()
isContained.name = 'isContained'
isContained.geometry = Polygon(((1,1),(9,1),(9,9),(1,9),(1,1)))
isContained.save()

isEqualToBounds = Location()
isEqualToBounds.name = 'isEqualToBounds'
isEqualToBounds.geometry = Polygon(((0,0),(0,85.05113),(180,85.05113),(180,0),(0,0)))
isEqualToBounds.save()

# Rectangle with bottom left at (-1,1), top right at (5,5)
overlaps = Location()
overlaps.name = 'overlaps'
overlaps.geometry = Polygon(((-1,1),(5,1),(5,5),(-1,5),(-1,1)))
overlaps.save()

# Rectangle with bottom left at (-3,-3), top right at (-1,2)
nonIntersecting = Location()
nonIntersecting.name = 'nonIntersecting'
nonIntersecting.geometry = Polygon(((-3,-3),(-1,-3),(-1,2),(-3,2),(-3,-3)))
nonIntersecting.save()

# Make sure we only get back the ones strictly contained in the bounding box
response = self.client.get(self.location_contained_in_tile_list_url + url_params)
self.assertEqual(len(response.data['features']), 2)
for result in response.data['features']:
self.assertEqual(result['properties']['name'] in ('isContained', 'isEqualToBounds'), True)

# Make sure we get overlapping results for the view which allows bounding box overlaps.
response = self.client.get(self.location_overlaps_tile_list_url + url_params)
self.assertEqual(len(response.data['features']), 3)
for result in response.data['features']:
self.assertEqual(result['properties']['name'] in ('isContained', 'isEqualToBounds', 'overlaps'), True)


def test_GeometryField_filtering(self):
""" Checks that the GeometryField allows sane filtering. """
Expand Down
2 changes: 2 additions & 0 deletions tests/django_restframework_gis_tests/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@
url(r'^filters/contained_in_bbox$', 'geojson_location_contained_in_bbox_list', name='api_geojson_location_list_contained_in_bbox_filter'),
url(r'^filters/overlaps_bbox$', 'geojson_location_overlaps_bbox_list', name='api_geojson_location_list_overlaps_bbox_filter'),
url(r'^filters/contained_in_geometry$', 'geojson_contained_in_geometry', name='api_geojson_contained_in_geometry'),
url(r'^filters/contained_in_tile$', 'geojson_location_contained_in_tile_list', name='api_geojson_location_list_contained_in_tile_filter'),
url(r'^filters/overlaps_tile$', 'geojson_location_overlaps_tile_list', name='api_geojson_location_list_overlaps_tile_filter'),
)
13 changes: 13 additions & 0 deletions tests/django_restframework_gis_tests/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ class GeojsonLocationOverlapsBBoxList(GeojsonLocationContainedInBBoxList):

geojson_location_overlaps_bbox_list = GeojsonLocationOverlapsBBoxList.as_view()

class GeojsonLocationContainedInTileList(generics.ListAPIView):
model = Location
serializer_class = LocationGeoFeatureSerializer
queryset = Location.objects.all()
bbox_filter_field = 'geometry'
filter_backends = (TMSTileFilter,)

geojson_location_contained_in_tile_list = GeojsonLocationContainedInTileList.as_view()

class GeojsonLocationOverlapsTileList(GeojsonLocationContainedInTileList):
bbox_filter_include_overlapping = True

geojson_location_overlaps_tile_list = GeojsonLocationOverlapsTileList.as_view()

class GeojsonLocationDetails(generics.RetrieveUpdateDestroyAPIView):
model = Location
Expand Down

0 comments on commit 003a941

Please sign in to comment.