djangorestframework-gis-0.10.1/0000775000175000017500000000000012643203115017556 5ustar nemesisnemesis00000000000000djangorestframework-gis-0.10.1/requirements.txt0000664000175000017500000000003112643201066023037 0ustar nemesisnemesis00000000000000djangorestframework>=3.3 djangorestframework-gis-0.10.1/djangorestframework_gis.egg-info/0000775000175000017500000000000012643203115026170 5ustar nemesisnemesis00000000000000djangorestframework-gis-0.10.1/djangorestframework_gis.egg-info/SOURCES.txt0000664000175000017500000000225112643203115030054 0ustar nemesisnemesis00000000000000AUTHORS CHANGES.rst LICENSE MANIFEST.in README.rst requirements.txt runtests.py setup.cfg setup.py djangorestframework_gis.egg-info/PKG-INFO djangorestframework_gis.egg-info/SOURCES.txt djangorestframework_gis.egg-info/dependency_links.txt djangorestframework_gis.egg-info/requires.txt djangorestframework_gis.egg-info/top_level.txt rest_framework_gis/__init__.py rest_framework_gis/apps.py rest_framework_gis/fields.py rest_framework_gis/filters.py rest_framework_gis/filterset.py rest_framework_gis/pagination.py rest_framework_gis/serializers.py rest_framework_gis/tilenames.py tests/__init__.py tests/local_settings.example.py tests/local_settings.py tests/manage.py tests/settings.py tests/urls.py tests/django_restframework_gis_tests/__init__.py tests/django_restframework_gis_tests/admin.py tests/django_restframework_gis_tests/models.py tests/django_restframework_gis_tests/serializers.py tests/django_restframework_gis_tests/test_bbox.py tests/django_restframework_gis_tests/test_filters.py tests/django_restframework_gis_tests/test_performance.py tests/django_restframework_gis_tests/tests.py tests/django_restframework_gis_tests/urls.py tests/django_restframework_gis_tests/views.pydjangorestframework-gis-0.10.1/djangorestframework_gis.egg-info/top_level.txt0000664000175000017500000000002312643203115030715 0ustar nemesisnemesis00000000000000rest_framework_gis djangorestframework-gis-0.10.1/djangorestframework_gis.egg-info/requires.txt0000664000175000017500000000003112643203115030562 0ustar nemesisnemesis00000000000000djangorestframework>=3.3 djangorestframework-gis-0.10.1/djangorestframework_gis.egg-info/dependency_links.txt0000664000175000017500000000000112643203115032236 0ustar nemesisnemesis00000000000000 djangorestframework-gis-0.10.1/djangorestframework_gis.egg-info/PKG-INFO0000664000175000017500000000203412643203115027264 0ustar nemesisnemesis00000000000000Metadata-Version: 1.1 Name: djangorestframework-gis Version: 0.10.1 Summary: Geographic add-ons for Django Rest Framework Home-page: https://github.com/djangonauts/django-rest-framework-gis Author: Douglas Meehan Author-email: django-rest-framework-gis@googlegroups.com License: BSD Download-URL: https://github.com/djangonauts/django-rest-framework-gis/releases Description: UNKNOWN Keywords: django,rest-framework,gis,geojson Platform: Platform Indipendent Classifier: Development Status :: 4 - Beta Classifier: Environment :: Web Environment Classifier: Topic :: Internet :: WWW/HTTP Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Framework :: Django Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 djangorestframework-gis-0.10.1/README.rst0000664000175000017500000005267512643201525021267 0ustar nemesisnemesis00000000000000django-rest-framework-gis ========================= |Build Status| |Coverage Status| |Requirements Status| |PyPI version| |PyPI downloads| Geographic add-ons for Django Rest Framework - `Mailing List `__. Install last stable version from pypi ------------------------------------- .. code-block:: bash pip install djangorestframework-gis Install development version --------------------------- .. code-block:: bash pip install https://github.com/djangonauts/django-rest-framework-gis/tarball/master Setup ----- Add ``rest_framework_gis`` in ``settings.INSTALLED_APPS``, after ``rest_framework``: .. code-block:: python INSTALLED_APPS = [ # ... 'rest_framework', 'rest_framework_gis', # ... ] Compatibility with DRF, Django and Python ----------------------------------------- =============== ============================ ==================== ================================== DRF-gis version DRF version Django version Python version **0.10.x** **3.1** to **3.3** **1.7** to **1.9** **2.7** to **3.5** **0.9.6** **3.1** to **3.2** **1.5** to **1.8** **2.6** to **3.5** **0.9.5** **3.1** to **3.2** **1.5** to **1.8** **2.6** to **3.4** **0.9.4** **3.1** to **3.2** **1.5** to **1.8** **2.6** to **3.4** **0.9.3** **3.1** **1.5** to **1.8** **2.6** to **3.4** **0.9.2** **3.1** **1.5** to **1.8** **2.6** to **3.4** **0.9.1** **3.1** **1.5** to **1.8** **2.6** to **3.4** **0.9** **3.1** **1.5** to **1.8** **2.6**, **2.7**, **3.3**, **3.4** **0.9** **3.1** **1.5** to **1.8** **2.6**, **2.7**, **3.3**, **3.4** **0.9** **3.1** **1.5** to **1.8** **2.6**, **2.7**, **3.3**, **3.4** **0.8.2** **3.0.4** to **3.1.1** **1.5** to **1.8** **2.6**, **2.7**, **3.3**, **3.4** **0.8.1** **3.0.4** to **3.1.1** **1.5** to **1.8** **2.6**, **2.7**, **3.3**, **3.4** **0.8** **3.0.4** **1.5** to **1.7** **2.6**, **2.7**, **3.3**, **3.4** **0.7** **2.4.3** **1.5** to **1.7** **2.6**, **2.7**, **3.3**, **3.4** **0.6** **2.4.3** **1.5** to **1.7** **2.6**, **2.7**, **3.3**, **3.4** **0.5** from **2.3.14** to **2.4.2** **1.5** to **1.7** **2.6**, **2.7**, **3.3**, **3.4** **0.4** from **2.3.14** to **2.4.2** **1.5** to **1.7** **2.6**, **2.7**, **3.3**, **3.4** **0.3** from **2.3.14** to **2.4.2** **1.5**, **1.6** **2.6**, **2.7** **0.2** from **2.2.2** to **2.3.13** **1.5**, **1.6** **2.6**, **2.7** =============== ============================ ==================== ================================== Fields ------ GeometryField ~~~~~~~~~~~~~ Provides a ``GeometryField``, which is a subclass of Django Rest Framework (from now on **DRF**) ``WritableField``. This field handles GeoDjango geometry fields, providing custom ``to_native`` and ``from_native`` methods for GeoJSON input/output. **New in 0.9.3:** there is no need to define this field explicitly in your serializer, it's mapped automatically during initialization in ``rest_framework_gis.apps.AppConfig.ready()``. GeometrySerializerMethodField ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Provides a ``GeometrySerializerMethodField``, which is a subclass of DRF ``SerializerMethodField`` and handles values which are computed with a serializer method and are used as a ``geo_field``. `See example below `__. Serializers ----------- GeoModelSerializer (DEPRECATED) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Deprecated, will be removed in 1.0**: Using this serializer is not needed anymore since 0.9.3 if you add ``rest_framework_gis`` in ``settings.INSTALLED_APPS`` Provides a ``GeoModelSerializer``, which is a sublass of DRF ``ModelSerializer``. This serializer updates the field\_mapping dictionary to include field mapping of GeoDjango geometry fields to the above ``GeometryField``. For example, the following model: .. code-block:: python class Location(models.Model): """ A model which holds information about a particular location """ address = models.Charfield(max_length=255) city = models.CharField(max_length=100) state = models.CharField(max_length=100) point = models.PointField() By default, the DRF ModelSerializer will output: .. code-block:: javascript { "id": 1, "address": "742 Evergreen Terrace", "city": "Springfield", "state": "Oregon", "point": "POINT(-123.0208 44.0464)" } In contrast, the ``GeoModelSerializer`` will output: .. code-block:: javascript { "id": 1, "address": "742 Evergreen Terrace", "city": "Springfield", "state": "Oregon", "point": { "type": "Point", "coordinates": [-123.0208, 44.0464], } } GeoFeatureModelSerializer ~~~~~~~~~~~~~~~~~~~~~~~~~ ``GeoFeatureModelSerializer`` is a subclass of ``rest_framework.ModelSerializer`` which will output data in a format that is **GeoJSON** compatible. Using the above example, the ``GeoFeatureModelSerializer`` will output: .. code-block:: javascript { "id": 1, "type": "Feature", "geometry": { "point": { "type": "Point", "coordinates": [-123.0208, 44.0464], }, }, "properties": { "address": "742 Evergreen Terrace", "city": "Springfield", "state": "Oregon" } } If you are serializing an object list, ``GeoFeatureModelSerializer`` will create a ``FeatureCollection``: .. code-block:: javascript { "type": "FeatureCollection", "features": [ { "id": 1 "type": "Feature", "geometry": { "point": { "type": "Point", "coordinates": [-123.0208, 44.0464], } }, "properties": { "address": "742 Evergreen Terrace", "city": "Springfield", "state": "Oregon", } } { "id": 2, "type": "Feature", "geometry": { "point": { "type": "Point", "coordinates": [-123.0208, 44.0489], }, }, "properties": { "address": "744 Evergreen Terrace", "city": "Springfield", "state": "Oregon" } } } Specifying the geometry field: "geo_field" ########################################## ``GeoFeatureModelSerializer`` requires you to define a ``geo_field`` to be serialized as the "geometry". For example: .. code-block:: python from rest_framework_gis.serializers import GeoFeatureModelSerializer class LocationSerializer(GeoFeatureModelSerializer): """ A class to serialize locations as GeoJSON compatible data """ class Meta: model = Location geo_field = "point" # you can also explicitly declare which fields you want to include # as with a ModelSerializer. fields = ('id', 'address', 'city', 'state') Using GeometrySerializerMethodField as "geo_field" ################################################## ``geo_field`` may also be an instance of ``GeometrySerializerMethodField``. In this case you can compute its value during serialization. For example: .. code-block:: python from django.contrib.gis.geos import Point from rest_framework_gis.serializers import GeoFeatureModelSerializer, GeometrySerializerMethodField class LocationSerializer(GeoFeatureModelSerializer): """ A class to serialize locations as GeoJSON compatible data """ # a field which contains a geometry value and can be used as geo_field other_point = GeometrySerializerMethodField() def get_other_point(self, obj): return Point(obj.point.lat / 2, obj.point.lon / 2) class Meta: model = Location geo_field = 'other_point' Serializer for ``geo_field`` may also return ``None`` value, which will translate to ``null`` value for geojson ``geometry`` field. Specifying the ID: "id_field" ############################# The primary key of the model (usually the "id" attribute) is automatically used as the ``id`` field of each `GeoJSON Feature Object `_. The default behaviour follows the `GeoJSON RFC `_, but it can be disbaled by setting ``id_field`` to ``False``: .. code-block:: python from rest_framework_gis.serializers import GeoFeatureModelSerializer class LocationSerializer(GeoFeatureModelSerializer): class Meta: model = Location geo_field = "point" id_field = False fields = ('id', 'address', 'city', 'state') The ``id_field`` can also be set to use some other unique field in your model, eg: ``slug``: .. code-block:: python from rest_framework_gis.serializers import GeoFeatureModelSerializer class LocationSerializer(GeoFeatureModelSerializer): class Meta: model = Location geo_field = 'point' id_field = 'slug' fields = ('slug', 'address', 'city', 'state') Bounding Box: "auto_bbox" and "bbox_geo_field" ############################################## The GeoJSON specification allows a feature to contain a `boundingbox of a feature `__. ``GeoFeatureModelSerializer`` allows two different ways to fill this property. The first is using the ``geo_field`` to calculate the bounding box of a feature. This only allows read access for a REST client and can be achieved using ``auto_bbox``. Example: .. code-block:: python from rest_framework_gis.serializers import GeoFeatureModelSerializer class LocationSerializer(GeoFeatureModelSerializer): class Meta: model = Location geo_field = 'geometry' auto_bbox = True The second approach uses the ``bbox_geo_field`` to specify an addional ``GeometryField`` of the model which will be used to calculate the bounding box. This allows boundingboxes differ from the exact extent of a features geometry. Additionally this enables read and write access for the REST client. Bounding boxes send from the client will be saved as Polygons. Example: .. code-block:: python from rest_framework_gis.serializers import GeoFeatureModelSerializer class LocationSerializer(GeoFeatureModelSerializer): class Meta: model = BoxedLocation geo_field = 'geometry' bbox_geo_field = 'bbox_geometry' Custom GeoJSON properties source ################################ In GeoJSON each feature can have a ``properties`` member containing the attributes of the feature. By default this field is filled with the attributes from your Django model, excluding the id, geometry and bounding box fields. It's possible to override this behaviour and implement a custom source for the ``properties`` member. The following example shows how to use a PostgreSQL HStore field as a source for the ``properties`` member: .. code-block:: python # models.py class Link(models.Model): """ Metadata is stored in a PostgreSQL HStore field, which allows us to store arbitrary key-value pairs with a link record. """ metadata = HStoreField(blank=True, null=True, default={}) geo = models.LineStringField() objects = models.GeoManager() # serializers.py class NetworkGeoSerializer(GeoFeatureModelSerializer): class Meta: model = models.Link geo_field = 'geo' auto_bbox = True def get_properties(self, instance, fields): # This is a PostgreSQL HStore field, which django maps to a dict return instance.metadata def unformat_geojson(self, feature): attrs = { self.Meta.geo_field: feature["geometry"], "metadata": feature["properties"] } if self.Meta.bbox_geo_field and "bbox" in feature: attrs[self.Meta.bbox_geo_field] = Polygon.from_bbox(feature["bbox"]) return attrs When the serializer renders GeoJSON, it calls the method ``get_properties`` for each object in the database. This function should return a dictionary containing the attributes for the feature. In the case of a HStore field, this function is easily implemented. The reverse is also required: mapping a GeoJSON formatted structure to attributes of your model. This task is done by ``unformat_geojson``. It should return a dictionary with your model attributes as keys, and the corresponding values retrieved from the GeoJSON feature data. Pagination ---------- We provide a ``GeoJsonPagination`` class. GeoJsonPagination ~~~~~~~~~~~~~~~~~ Based on ``rest_framework.pagination.PageNumberPagination``. Code example: .. code-block:: python from rest_framework_gis.pagination import GeoJsonPagination # --- other omitted imports --- # class GeojsonLocationList(generics.ListCreateAPIView): # -- other omitted view attributes --- # pagination_class = GeoJsonPagination Example result response (cut to one element only instead of 10): .. code-block:: javascript { "type": "FeatureCollection", "count": 25, "next": "http://localhost:8000/geojson/?page=2", "previous": null, "features": [ { "type": "Feature", "geometry": { "type": "Point", "coordinates": [ 42.0, 50.0 ] }, "properties": { "name": "test" } } ] } Filters ------- We provide a ``GeometryFilter`` field as well as a ``GeoFilterSet`` for usage with ``django_filter``. You simply provide, in the query string, one of the textual types supported by ``GEOSGeometry``. By default, this includes WKT, HEXEWKB, WKB (in a buffer), and GeoJSON. GeometryFilter ~~~~~~~~~~~~~~ .. code-block:: python from rest_framework_gis.filterset import GeoFilterSet class RegionFilter(GeoFilterSet): slug = filters.CharFilter(name='slug', lookup_type='istartswith') contains_geom = filters.GeometryFilter(name='geom', lookup_type='contains') class Meta: model = Region We can then filter in the URL, using GeoJSON, and we will perform a ``__contains`` geometry lookup, e.g. ``/region/?contains_geom={ "type": "Point", "coordinates": [ -123.26436996459961, 44.564178042345375 ] }``. GeoFilterSet ~~~~~~~~~~~~ The ``GeoFilterSet`` provides a ``django_filter`` compatible ``FilterSet`` that will automatically create ``GeometryFilters`` for ``GeometryFields``. InBBoxFilter ~~~~~~~~~~~~ Provides a ``InBBoxFilter``, which is a subclass of DRF ``BaseFilterBackend``. Filters a queryset to only those instances within a certain bounding box. ``views.py:`` .. code-block:: python from rest_framework_gis.filters import InBBoxFilter class LocationList(ListAPIView): queryset = models.Location.objects.all() serializer_class = serializers.LocationSerializer bbox_filter_field = 'point' filter_backends = (InBBoxFilter, ) bbox_filter_include_overlapping = True # Optional We can then filter in the URL, using Bounding Box format (min Lon, min Lat, max Lon, max Lat), and we can search for instances within the bounding box, e.g.: ``/location/?in_bbox=-90,29,-89,35``. By default, InBBoxFilter will only return those instances entirely within the stated bounding box. To include those instances which overlap the bounding box, include ``bbox_filter_include_overlapping = True`` in your view. Note that if you are using other filters, you'll want to include your other filter backend in your view. For example: ``filter_backends = (InBBoxFilter, DjangoFilterBackend,)`` TMSTileFilter ~~~~~~~~~~~~~ Provides a ``TMSTileFilter``, which is a subclass of ``InBBoxFilter``. Filters a queryset to only those instances within a bounding box defined by a `TMS tile `__ address. ``views.py:`` .. code-block:: python from rest_framework_gis.filters import TMSTileFilter class LocationList(ListAPIView): queryset = models.Location.objects.all() serializer_class = serializers.LocationSerializer bbox_filter_field = 'point' filter_backends = (TMSTileFilter, ) bbox_filter_include_overlapping = True # Optional We can then filter in the URL, using TMS tile addresses in the zoom/x/y format, eg:. ``/location/?tile=8/100/200`` which is equivalant to filtering on the bbox (-39.37500,-71.07406,-37.96875,-70.61261). For more information on configuration options see InBBoxFilter. Note that the tile address start in the upper left, not the lower left origin used by some implementations. DistanceToPointFilter ~~~~~~~~~~~~~~~~~~~~~ Provides a ``DistanceToPointFilter``, which is a subclass of DRF ``BaseFilterBackend``. Filters a queryset to only those instances within a certain distance of a given point. ``views.py:`` .. code-block:: python from rest_framework_gis.filters import DistanceToPointFilter class LocationList(ListAPIView): queryset = models.Location.objects.all() serializer_class = serializers.LocationSerializer distance_filter_field = 'geometry' filter_backends = (DistanceToPointFilter, ) bbox_filter_include_overlapping = True # Optional We can then filter in the URL, using a distance and a point in (lon, lat) format. The distance can be given in meters or in degrees. eg:. ``/location/?dist=4000&point=-122.4862,37.7694&format=json`` which is equivalant to filtering within 4000 meters of the point (-122.4862, 37.7694). By default, DistanceToPointFilter will pass the 'distance' in the URL directly to the database for the search. The effect depends on the srid of the database in use. If geo data is indexed in meters (srid 3875, aka 900913), a distance in meters can be passed in directly without conversion. For lat-lon databases such as srid 4326, which is indexed in degrees, the 'distance' will be interpreted as degrees. Set the flag, 'distance_filter_convert_meters' to 'True' in order to convert an input distance in meters to degrees. This conversion is approximate, and the errors at latitudes > 60 degrees are > 25%. Projects using this package --------------------------- - `Nodeshot `__: Extensible Django web application for management of community-led georeferenced data Running the tests ----------------- Assuming one has the dependencies installed (restframework and restframework\_gis), and one of the `Spatial Database server supported by GeoDjango `__ is up and running: .. code-block:: bash ./runtests.py You might need to tweak the DB settings according to your DB configuration. You can copy the file ``local_settings.example.py`` to ``local_settings.py`` and change the ``DATABASES`` and/or ``INSTALLED_APPS`` directives there. If you want to contribute you need to install the test app in a proper development environment. These steps should do the trick: - create a spatial database named "django\_restframework\_gis" - create ``local_settings.py``, eg: ``cp local_settings.example.py local_settings.py`` - tweak the ``DATABASES`` configuration directive according to your DB settings - optionally install ``olwidget`` with ``pip install olwidget`` - uncomment ``INSTALLED_APPS`` (remove olwidget if you did not install it) - run ``python manage.py syncdb`` - run ``python manage.py collectstatic`` - run ``python manage.py runserver`` Contributing ------------ 1. Join the `Django REST Framework GIS Mailing List `__ and announce your intentions 2. Follow the `PEP8 Style Guide for Python Code `__ 3. Fork this repo 4. Write code 5. Write tests for your code 6. Ensure all tests pass 7. Ensure test coverage is not under 90% 8. Document your changes 9. Send pull request .. |Build Status| image:: https://travis-ci.org/djangonauts/django-rest-framework-gis.svg?branch=master :target: https://travis-ci.org/djangonauts/django-rest-framework-gis .. |Coverage Status| image:: https://coveralls.io/repos/djangonauts/django-rest-framework-gis/badge.svg :target: https://coveralls.io/r/djangonauts/django-rest-framework-gis .. |Requirements Status| image:: https://requires.io/github/djangonauts/django-rest-framework-gis/requirements.svg?branch=master :target: https://requires.io/github/djangonauts/django-rest-framework-gis/requirements/?branch=master .. |PyPI version| image:: https://badge.fury.io/py/djangorestframework-gis.svg :target: http://badge.fury.io/py/djangorestframework-gis .. |PyPI downloads| image:: https://img.shields.io/pypi/dm/djangorestframework-gis.svg :target: http://badge.fury.io/py/djangorestframework-gis djangorestframework-gis-0.10.1/setup.cfg0000664000175000017500000000013012643203115021371 0ustar nemesisnemesis00000000000000[bdist_wheel] universal = 1 [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 djangorestframework-gis-0.10.1/AUTHORS0000664000175000017500000000045612544034142020635 0ustar nemesisnemesis00000000000000Original Author --------------- Douglas Meehan https://github.com/dmeehan Contributors ------------ Christoph Heer https://github.com/jarus Federico Capoano https://github.com/nemesisdesign/ Shanto https://github.com/Shanto Eric Theise https://github.com/erictheise djangorestframework-gis-0.10.1/LICENSE0000664000175000017500000000204612544034142020567 0ustar nemesisnemesis00000000000000Copyright (C) 2013-2014 Douglas Meehan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.djangorestframework-gis-0.10.1/rest_framework_gis/0000775000175000017500000000000012643203115023452 5ustar nemesisnemesis00000000000000djangorestframework-gis-0.10.1/rest_framework_gis/pagination.py0000664000175000017500000000115412643201066026161 0ustar nemesisnemesis00000000000000from collections import OrderedDict from rest_framework import pagination from rest_framework.response import Response class GeoJsonPagination(pagination.PageNumberPagination): """ A geoJSON implementation of a pagination serializer. """ page_size_query_param = 'page_size' def get_paginated_response(self, data): return Response(OrderedDict([ ('type', 'FeatureCollection'), ('count', self.page.paginator.count), ('next', self.get_next_link()), ('previous', self.get_previous_link()), ('features', data['features']) ])) djangorestframework-gis-0.10.1/rest_framework_gis/__init__.py0000664000175000017500000000070712643201510025564 0ustar nemesisnemesis00000000000000VERSION = (0, 10, 1, 'final') __version__ = VERSION # alias def get_version(): version = '%s.%s' % (VERSION[0], VERSION[1]) if VERSION[2]: version = '%s.%s' % (version, VERSION[2]) if VERSION[3:] == ('alpha', 0): version = '%s pre-alpha' % version else: if VERSION[3] != 'final': version = '%s %s' % (version, VERSION[3]) return version default_app_config = 'rest_framework_gis.apps.AppConfig' djangorestframework-gis-0.10.1/rest_framework_gis/filterset.py0000664000175000017500000000005212544034142026024 0ustar nemesisnemesis00000000000000from .filters import GeoFilterSet # noqa djangorestframework-gis-0.10.1/rest_framework_gis/apps.py0000664000175000017500000000212012643201066024765 0ustar nemesisnemesis00000000000000from django.apps import AppConfig as BaseConfig class AppConfig(BaseConfig): name = 'rest_framework_gis' def ready(self): """ update Django Rest Framework serializer mappings """ from django.contrib.gis.db import models from rest_framework.serializers import ModelSerializer from .fields import GeometryField try: # drf 3.0 field_mapping = ModelSerializer._field_mapping.mapping except AttributeError: # drf 3.1 field_mapping = ModelSerializer.serializer_field_mapping # map GeoDjango fields to drf-gis GeometryField field_mapping.update({ models.GeometryField: GeometryField, models.PointField: GeometryField, models.LineStringField: GeometryField, models.PolygonField: GeometryField, models.MultiPointField: GeometryField, models.MultiLineStringField: GeometryField, models.MultiPolygonField: GeometryField, models.GeometryCollectionField: GeometryField }) djangorestframework-gis-0.10.1/rest_framework_gis/tilenames.py0000664000175000017500000000200512544034142026004 0ustar nemesisnemesis00000000000000#!/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 pi, atan, sinh, degrees, pow as math_pow def num_tiles(z): return(math_pow(2, z)) def lat_edges(y, z): n = num_tiles(z) unit = 1 / n relY1 = y * unit relY2 = relY1 + unit lat1 = mercator_to_lat(pi * (1 - 2 * relY1)) lat2 = mercator_to_lat(pi * (1 - 2 * relY2)) return(lat1, lat2) def lon_edges(x, z): n = num_tiles(z) unit = 360 / n lon1 = -180 + x * unit lon2 = lon1 + unit return(lon1, lon2) def tile_edges(x, y, z): lat1, lat2 = lat_edges(y, z) lon1, lon2 = lon_edges(x, z) return((lon1, lat2, lon2, lat1)) # w, s, e, n def mercator_to_lat(mercatorY): return(degrees(atan(sinh(mercatorY)))) djangorestframework-gis-0.10.1/rest_framework_gis/serializers.py0000664000175000017500000001632312643201066026370 0ustar nemesisnemesis00000000000000from collections import OrderedDict from django.core.exceptions import ImproperlyConfigured from django.contrib.gis.geos import Polygon from rest_framework.serializers import ModelSerializer, ListSerializer, LIST_SERIALIZER_KWARGS from .fields import GeometryField, GeometrySerializerMethodField # noqa class GeoModelSerializer(ModelSerializer): """ Deprecated, will be removed in django-rest-framework-gis 1.0 """ class GeoFeatureModelListSerializer(ListSerializer): @property def data(self): return super(ListSerializer, self).data def to_representation(self, data): """ Add GeoJSON compatible formatting to a serialized queryset list """ return OrderedDict(( ("type", "FeatureCollection"), ("features", super(GeoFeatureModelListSerializer, self).to_representation(data)) )) class GeoFeatureModelSerializer(ModelSerializer): """ A subclass of ModelSerializer that outputs geojson-ready data as features and feature collections """ @classmethod def many_init(cls, *args, **kwargs): child_serializer = cls(*args, **kwargs) list_kwargs = {'child': child_serializer} list_kwargs.update(dict([ (key, value) for key, value in kwargs.items() if key in LIST_SERIALIZER_KWARGS ])) meta = getattr(cls, 'Meta', None) list_serializer_class = getattr(meta, 'list_serializer_class', GeoFeatureModelListSerializer) return list_serializer_class(*args, **list_kwargs) def __init__(self, *args, **kwargs): super(GeoFeatureModelSerializer, self).__init__(*args, **kwargs) meta = getattr(self, 'Meta') default_id_field = None primary_key = self.Meta.model._meta.pk.name # use primary key as id_field when possible if not hasattr(meta, 'fields') or primary_key in meta.fields: default_id_field = primary_key meta.id_field = getattr(meta, 'id_field', default_id_field) if not hasattr(meta, 'geo_field') or not meta.geo_field: raise ImproperlyConfigured("You must define a 'geo_field'.") def check_excludes(field_name, field_role): """make sure the field is not excluded""" if hasattr(meta, 'exclude') and field_name in meta.exclude: raise ImproperlyConfigured("You cannot exclude your '{0}'.".format(field_role)) def add_to_fields(field_name): """Make sure the field is included in the fields""" if hasattr(meta, 'fields'): if field_name not in meta.fields: if type(meta.fields) is tuple: additional_fields = (field_name,) else: additional_fields = [field_name] meta.fields += additional_fields check_excludes(meta.geo_field, 'geo_field') add_to_fields(meta.geo_field) meta.bbox_geo_field = getattr(meta, 'bbox_geo_field', None) if meta.bbox_geo_field: check_excludes(meta.bbox_geo_field, 'bbox_geo_field') add_to_fields(meta.bbox_geo_field) meta.auto_bbox = getattr(meta, 'auto_bbox', False) if meta.bbox_geo_field and meta.auto_bbox: raise ImproperlyConfigured("You must eiher define a 'bbox_geo_field' or " "'auto_bbox', but you can not set both") def to_representation(self, instance): """ Serialize objects -> primitives. """ # prepare OrderedDict geojson structure feature = OrderedDict() # the list of fields that will be processed by get_properties # we will remove fields that have been already processed # to increase performance on large numbers fields = list(self.fields.values()) # optional id attribute if self.Meta.id_field: field = self.fields[self.Meta.id_field] value = field.get_attribute(instance) feature["id"] = field.to_representation(value) fields.remove(field) # required type attribute # must be "Feature" according to GeoJSON spec feature["type"] = "Feature" # required geometry attribute # MUST be present in output according to GeoJSON spec field = self.fields[self.Meta.geo_field] geo_value = field.get_attribute(instance) feature["geometry"] = field.to_representation(geo_value) fields.remove(field) # Bounding Box # if auto_bbox feature is enabled # bbox will be determined automatically automatically if self.Meta.auto_bbox and geo_value: feature["bbox"] = geo_value.extent # otherwise it can be determined via another field elif self.Meta.bbox_geo_field: field = self.fields[self.Meta.bbox_geo_field] value = field.get_attribute(instance) feature["bbox"] = value.extent if hasattr(value, 'extent') else None fields.remove(field) # GeoJSON properties feature["properties"] = self.get_properties(instance, fields) return feature def get_properties(self, instance, fields): """ Get the feature metadata which will be used for the GeoJSON "properties" key. By default it returns all serializer fields excluding those used for the ID, the geometry and the bounding box. :param instance: The current Django model instance :param fields: The list of fields to process (fields already processed have been removed) :return: OrderedDict containing the properties of the current feature :rtype: OrderedDict """ properties = OrderedDict() for field in fields: if field.write_only: continue value = field.get_attribute(instance) representation = None if value is not None: representation = field.to_representation(value) properties[field.field_name] = representation return properties def to_internal_value(self, data): """ Override the parent method to first remove the GeoJSON formatting """ if 'properties' in data: data = self.unformat_geojson(data) return super(GeoFeatureModelSerializer, self).to_internal_value(data) def unformat_geojson(self, feature): """ This function should return a dictionary containing keys which maps to serializer fields. Remember that GeoJSON contains a key "properties" which contains the feature metadata. This should be flattened to make sure this metadata is stored in the right serializer fields. :param feature: The dictionary containing the feature data directly from the GeoJSON data. :return: A new dictionary which maps the GeoJSON values to serializer fields """ attrs = feature["properties"] if 'geometry' in feature: attrs[self.Meta.geo_field] = feature['geometry'] if self.Meta.bbox_geo_field and 'bbox' in feature: attrs[self.Meta.bbox_geo_field] = Polygon.from_bbox(feature['bbox']) return attrs djangorestframework-gis-0.10.1/rest_framework_gis/fields.py0000664000175000017500000000507612643201066025305 0ustar nemesisnemesis00000000000000import json from collections import OrderedDict from django.contrib.gis.geos import GEOSGeometry, GEOSException from django.contrib.gis.gdal import OGRException from django.core.exceptions import ValidationError from django.utils.translation import ugettext_lazy as _ from rest_framework.fields import Field, SerializerMethodField __all__ = ['GeometryField', 'GeometrySerializerMethodField'] class GeometryField(Field): """ A field to handle GeoDjango Geometry fields """ type_name = 'GeometryField' def __init__(self, **kwargs): super(GeometryField, self).__init__(**kwargs) self.style = {'base_template': 'textarea.html'} def to_representation(self, value): if isinstance(value, dict) or value is None: return value # we expect value to be a GEOSGeometry instance return GeoJsonDict(( ('type', value.geom_type), ('coordinates', value.coords), )) def to_internal_value(self, value): if value == '' or value is None: return value if isinstance(value, GEOSGeometry): # value already has the correct representation return value if isinstance(value, dict): value = json.dumps(value) try: return GEOSGeometry(value) except (ValueError, GEOSException, OGRException, TypeError): raise ValidationError(_('Invalid format: string or unicode input unrecognized as GeoJSON, WKT EWKT or HEXEWKB.')) def validate_empty_values(self, data): if data == '': self.fail('required') return super(GeometryField, self).validate_empty_values(data) class GeometrySerializerMethodField(SerializerMethodField): def to_representation(self, value): value = super(GeometrySerializerMethodField, self).to_representation(value) if value is not None: # we expect value to be a GEOSGeometry instance return GeoJsonDict(( ('type', value.geom_type), ('coordinates', value.coords), )) else: return None class GeoJsonDict(OrderedDict): """ Used for serializing GIS values to GeoJSON values TODO: remove this when support for python 2.6/2.7 will be dropped """ def __str__(self): """ Avoid displaying strings like ``{ 'type': u'Point', 'coordinates': [12, 32] }`` in DRF browsable UI inputs (python 2.6/2.7) see: https://github.com/djangonauts/django-rest-framework-gis/pull/60 """ return json.dumps(self) djangorestframework-gis-0.10.1/rest_framework_gis/filters.py0000664000175000017500000001403412552233565025511 0ustar nemesisnemesis00000000000000from math import cos, pi from django.db.models import Q from django.core.exceptions import ImproperlyConfigured from django.contrib.gis.db import models from django.contrib.gis.geos import Polygon, Point from django.contrib.gis import forms from rest_framework.filters import BaseFilterBackend from rest_framework.exceptions import ParseError from .tilenames import tile_edges try: import django_filters except ImportError: # pragma: no cover raise ImproperlyConfigured( 'restframework-gis filters depend on package "django-filter" ' 'which is missing. Install with "pip install django-filter".' ) try: # pragma: no cover # django >= 1.8 from django.contrib.gis.db.models.lookups import gis_lookups except ImportError: # pragma: no cover # django <= 1.7 gis_lookups = models.sql.query.ALL_TERMS __all__ = [ 'InBBoxFilter', 'InBBOXFilter', 'GeometryFilter', 'GeoFilterSet', 'TMSTileFilter', 'DistanceToPointFilter' ] class InBBoxFilter(BaseFilterBackend): bbox_param = 'in_bbox' # The URL query parameter which contains the bbox. def get_filter_bbox(self, request): bbox_string = request.query_params.get(self.bbox_param, None) if not bbox_string: return None try: p1x, p1y, p2x, p2y = (float(n) for n in bbox_string.split(',')) except ValueError: raise ParseError('Invalid bbox string supplied for parameter {0}'.format(self.bbox_param)) x = Polygon.from_bbox((p1x, p1y, p2x, p2y)) return x def filter_queryset(self, request, queryset, view): filter_field = getattr(view, 'bbox_filter_field', None) include_overlapping = getattr(view, 'bbox_filter_include_overlapping', False) if include_overlapping: geoDjango_filter = 'bboverlaps' else: geoDjango_filter = 'contained' if not filter_field: return queryset bbox = self.get_filter_bbox(request) if not bbox: return queryset return queryset.filter(Q(**{'%s__%s' % (filter_field, geoDjango_filter): bbox})) # backward compatibility InBBOXFilter = InBBoxFilter class GeometryFilter(django_filters.Filter): field_class = forms.GeometryField class GeoFilterSet(django_filters.FilterSet): GEOFILTER_FOR_DBFIELD_DEFAULTS = { models.GeometryField: { 'filter_class': GeometryFilter }, } def __new__(cls, *args, **kwargs): cls.filter_overrides.update(cls.GEOFILTER_FOR_DBFIELD_DEFAULTS) cls.LOOKUP_TYPES = sorted(gis_lookups) 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('Invalid tile string supplied for parameter {0}'.format(self.tile_param)) bbox = Polygon.from_bbox(tile_edges(x, y, z)) return bbox class DistanceToPointFilter(BaseFilterBackend): dist_param = 'dist' point_param = 'point' # The URL query parameter which contains the def get_filter_point(self, request): point_string = request.query_params.get(self.point_param, None) if not point_string: return None try: (x, y) = (float(n) for n in point_string.split(',')) except ValueError: raise ParseError('Invalid geometry string supplied for parameter {0}'.format(self.point_param)) p = Point(x, y) return p def dist_to_deg(self, distance, latitude): """ distance = distance in meters latitude = latitude in degrees at the equator, the distance of one degree is equal in latitude and longitude. at higher latitudes, a degree longitude is shorter in length, proportional to cos(latitude) http://en.wikipedia.org/wiki/Decimal_degrees This function is part of a distance filter where the database 'distance' is in degrees. There's no good single-valued answer to this problem. The distance/ degree is quite constant N/S around the earth (latitude), but varies over a huge range E/W (longitude). Split the difference: I'm going to average the the degrees latitude and degrees longitude corresponding to the given distance. At high latitudes, this will be too short N/S and too long E/W. It splits the errors between the two axes. Errors are < 25 percent for latitudes < 60 degrees N/S. """ # d * (180 / pi) / earthRadius ==> degrees longitude # (degrees longitude) / cos(latitude) ==> degrees latitude lat = latitude if latitude >= 0 else -1 * latitude rad2deg = 180 / pi earthRadius = 6378160.0 latitudeCorrection = 0.5 * (1 + cos(lat * pi / 180)) return (distance / (earthRadius * latitudeCorrection) * rad2deg) def filter_queryset(self, request, queryset, view): filter_field = getattr(view, 'distance_filter_field', None) convert_distance_input = getattr(view, 'distance_filter_convert_meters', False) geoDjango_filter = 'dwithin' # use dwithin for points if not filter_field: return queryset point = self.get_filter_point(request) if not point: return queryset # distance in meters dist_string = request.query_params.get(self.dist_param, 1000) try: dist = float(dist_string) except ValueError: raise ParseError('Invalid distance string supplied for parameter {0}'.format(self.dist_param)) if (convert_distance_input): # Warning: assumes that the point is (lon,lat) dist = self.dist_to_deg(dist, point[1]) return queryset.filter(Q(**{'%s__%s' % (filter_field, geoDjango_filter): (point, dist)})) djangorestframework-gis-0.10.1/runtests.py0000755000175000017500000000060212526372137022031 0ustar nemesisnemesis00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import os import sys sys.path.insert(0, "tests") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") if __name__ == "__main__": from django.core.management import execute_from_command_line args = sys.argv args.insert(1, "test") args.insert(2, "django_restframework_gis_tests") execute_from_command_line(args) djangorestframework-gis-0.10.1/setup.py0000664000175000017500000000406512643201066021300 0ustar nemesisnemesis00000000000000#!/usr/bin/env python import sys import os from setuptools import setup, find_packages from rest_framework_gis import get_version def get_install_requires(): """ parse requirements.txt, ignore links, exclude comments """ requirements = [] for line in open('requirements.txt').readlines(): # skip to next iteration if comment or empty line if line.startswith('#') or line == '' or line.startswith('http') or line.startswith('git'): continue # add line to requirements requirements.append(line) return requirements if sys.argv[-1] == 'publish': os.system("python setup.py sdist bdist_wheel upload -s") args = {'version': get_version()} print("You probably want to also tag the version now:") print(" git tag -a %(version)s -m 'version %(version)s'" % args) print(" git push --tags") sys.exit() setup( name='djangorestframework-gis', version=get_version(), license='BSD', author='Douglas Meehan', author_email='django-rest-framework-gis@googlegroups.com', description='Geographic add-ons for Django Rest Framework', url='https://github.com/djangonauts/django-rest-framework-gis', download_url='https://github.com/djangonauts/django-rest-framework-gis/releases', platforms=['Platform Indipendent'], keywords=['django', 'rest-framework', 'gis', 'geojson'], packages=find_packages(exclude=['tests', 'tests.*']), install_requires=get_install_requires(), classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: Web Environment', 'Topic :: Internet :: WWW/HTTP', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Framework :: Django', 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', ] ) djangorestframework-gis-0.10.1/PKG-INFO0000664000175000017500000000203412643203115020652 0ustar nemesisnemesis00000000000000Metadata-Version: 1.1 Name: djangorestframework-gis Version: 0.10.1 Summary: Geographic add-ons for Django Rest Framework Home-page: https://github.com/djangonauts/django-rest-framework-gis Author: Douglas Meehan Author-email: django-rest-framework-gis@googlegroups.com License: BSD Download-URL: https://github.com/djangonauts/django-rest-framework-gis/releases Description: UNKNOWN Keywords: django,rest-framework,gis,geojson Platform: Platform Indipendent Classifier: Development Status :: 4 - Beta Classifier: Environment :: Web Environment Classifier: Topic :: Internet :: WWW/HTTP Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Framework :: Django Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 djangorestframework-gis-0.10.1/MANIFEST.in0000664000175000017500000000022112643200322021304 0ustar nemesisnemesis00000000000000include AUTHORS include LICENSE include README.rst include CHANGES.rst include requirements.txt include runtests.py recursive-include tests *.py djangorestframework-gis-0.10.1/tests/0000775000175000017500000000000012643203115020720 5ustar nemesisnemesis00000000000000djangorestframework-gis-0.10.1/tests/settings.py0000664000175000017500000000252312566637441023155 0ustar nemesisnemesis00000000000000import os TEST_PERFORMANCE = False DEBUG = True TEMPLATE_DEBUG = DEBUG DATABASES = { 'default': { 'ENGINE': 'django.contrib.gis.db.backends.postgis', 'NAME': 'django_restframework_gis', 'USER': 'postgres', 'PASSWORD': 'postgres', 'HOST': '', 'PORT': '' }, } SECRET_KEY = 'fn)t*+$)ugeyip6-#txyy$5wf2ervc0d2n#h)qb)y5@ly$t*@w' INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.admin', # rest framework 'rest_framework', 'rest_framework_gis', # test app 'django_restframework_gis_tests' ) MIDDLEWARE_CLASSES = [ 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', ] ROOT_URLCONF = 'urls' TIME_ZONE = 'Europe/Rome' LANGUAGE_CODE = 'en-gb' USE_TZ = True USE_I18N = False USE_L10N = False SITE_ROOT = os.path.dirname(os.path.realpath(__file__)) MEDIA_ROOT = '%s/media/' % SITE_ROOT MEDIA_URL = '/media/' STATIC_ROOT = '%s/static/' % SITE_ROOT STATIC_URL = '/static/' TEMPLATE_STRING_IF_INVALID = 'INVALID_TEMPLATE: %s END_INVALID_TEMPLATE' # local settings must be imported before test runner otherwise they'll be ignored try: from local_settings import * except ImportError: pass djangorestframework-gis-0.10.1/tests/django_restframework_gis_tests/0000775000175000017500000000000012643203115027221 5ustar nemesisnemesis00000000000000djangorestframework-gis-0.10.1/tests/django_restframework_gis_tests/__init__.py0000664000175000017500000000000012552217421031324 0ustar nemesisnemesis00000000000000djangorestframework-gis-0.10.1/tests/django_restframework_gis_tests/views.py0000664000175000017500000001324712611406014030735 0ustar nemesisnemesis00000000000000from rest_framework import generics from rest_framework.filters import DjangoFilterBackend from rest_framework_gis.filters import * from rest_framework_gis.pagination import GeoJsonPagination from .models import * from .serializers import * class LocationList(generics.ListCreateAPIView): model = Location serializer_class = LocationGeoSerializer queryset = Location.objects.all() pagination_class = PaginatedLocationGeoSerializer location_list = LocationList.as_view() class LocationDetails(generics.RetrieveUpdateDestroyAPIView): model = Location serializer_class = LocationGeoSerializer queryset = Location.objects.all() location_details = LocationDetails.as_view() class GeojsonLocationList(generics.ListCreateAPIView): model = Location serializer_class = LocationGeoFeatureSerializer queryset = Location.objects.all() pagination_class = GeoJsonPagination geojson_location_list = GeojsonLocationList.as_view() class GeojsonLocationContainedInBBoxList(generics.ListAPIView): model = Location serializer_class = LocationGeoFeatureSerializer queryset = Location.objects.all() bbox_filter_field = 'geometry' filter_backends = (InBBoxFilter,) geojson_location_contained_in_bbox_list = GeojsonLocationContainedInBBoxList.as_view() class GeojsonLocationOverlapsBBoxList(GeojsonLocationContainedInBBoxList): bbox_filter_include_overlapping = True 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 GeojsonLocationWithinDistanceOfPointList(generics.ListAPIView): model = Location serializer_class = LocationGeoFeatureSerializer distance_filter_convert_meters = True queryset = Location.objects.all() distance_filter_field = 'geometry' filter_backends = (DistanceToPointFilter,) geojson_location_within_distance_of_point_list = GeojsonLocationWithinDistanceOfPointList.as_view() class GeojsonLocationWithinDegreesOfPointList(GeojsonLocationWithinDistanceOfPointList): distance_filter_convert_meters = False #Default setting geojson_location_within_degrees_of_point_list = GeojsonLocationWithinDegreesOfPointList.as_view() class GeojsonLocationDetails(generics.RetrieveUpdateDestroyAPIView): model = Location serializer_class = LocationGeoFeatureSerializer queryset = Location.objects.all() geojson_location_details = GeojsonLocationDetails.as_view() class GeojsonLocationDetailsHidden(generics.RetrieveUpdateDestroyAPIView): model = Location serializer_class = LocationGeoFeatureMethodSerializer queryset = Location.objects.all() geojson_location_details_hidden = GeojsonLocationDetailsHidden.as_view() class GeojsonLocationDetailsNone(generics.RetrieveUpdateDestroyAPIView): model = Location serializer_class = NoneGeoFeatureMethodSerializer queryset = Location.objects.all() geojson_location_details_none = GeojsonLocationDetailsNone.as_view() class GeojsonLocationSlugDetails(generics.RetrieveUpdateDestroyAPIView): model = Location lookup_field = 'slug' serializer_class = LocationGeoFeatureSlugSerializer queryset = Location.objects.all() geojson_location_slug_details = GeojsonLocationSlugDetails.as_view() class GeojsonLocationFalseIdDetails(generics.RetrieveUpdateDestroyAPIView): model = Location serializer_class = LocationGeoFeatureFalseIdSerializer queryset = Location.objects.all() geojson_location_falseid_details = GeojsonLocationFalseIdDetails.as_view() class GeojsonLocationNoIdDetails(generics.RetrieveUpdateDestroyAPIView): model = Location serializer_class = LocationGeoFeatureNoIdSerializer queryset = Location.objects.all() geojson_location_noid_details = GeojsonLocationNoIdDetails.as_view() class LocationFilter(GeoFilterSet): contains_properly = GeometryFilter(name='geometry', lookup_type='contains_properly') class Meta: model = Location class GeojsonLocationContainedInGeometry(generics.ListAPIView): queryset = Location.objects.all() serializer_class = LocationGeoSerializer filter_class = LocationFilter filter_backends = (DjangoFilterBackend,) geojson_contained_in_geometry = GeojsonLocationContainedInGeometry.as_view() class GeojsonLocatedFileDetails(generics.RetrieveUpdateDestroyAPIView): model = LocatedFile serializer_class = LocatedFileGeoFeatureSerializer queryset = LocatedFile.objects.all() geojson_located_file_details = GeojsonLocatedFileDetails.as_view() class GeojsonBoxedLocationDetails(generics.RetrieveUpdateDestroyAPIView): model = BoxedLocation serializer_class = BoxedLocationGeoFeatureSerializer queryset = BoxedLocation.objects.all() geojson_boxedlocation_details = GeojsonBoxedLocationDetails.as_view() class GeojsonBoxedLocationList(generics.ListCreateAPIView): model = BoxedLocation serializer_class = BoxedLocationGeoFeatureSerializer queryset = BoxedLocation.objects.all() geojson_boxedlocation_list = GeojsonBoxedLocationList.as_view() class GeojsonLocationBboxList(generics.ListCreateAPIView): model = Location serializer_class = LocationGeoFeatureBboxSerializer queryset = Location.objects.all() geojson_location_bbox_list = GeojsonLocationBboxList.as_view() djangorestframework-gis-0.10.1/tests/django_restframework_gis_tests/test_filters.py0000664000175000017500000004407512643200214032311 0ustar nemesisnemesis00000000000000import json import urllib from unittest import skipIf from django.conf import settings from django.test import TestCase from django.contrib.gis.geos import GEOSGeometry, Polygon from django.core.urlresolvers import reverse from .models import Location has_spatialite = settings.DATABASES['default']['ENGINE'] == 'django.contrib.gis.db.backends.spatialite' class TestRestFrameworkGisFilters(TestCase): """ unit tests for filters feature in restframework_gis """ def setUp(self): 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.location_within_distance_of_point_list_url = reverse('api_geojson_location_list_within_distance_of_point_filter') self.location_within_degrees_of_point_list_url = reverse('api_geojson_location_list_within_degrees_of_point_filter') self.geojson_contained_in_geometry = reverse('api_geojson_contained_in_geometry') def test_inBBOXFilter_filtering(self): """ Checks that the inBBOXFilter returns only objects strictly contained in the bounding box given by the in_bbox URL parameter """ self.assertEqual(Location.objects.count(), 0) # Bounding box xmin = 0 ymin = 0 xmax = 10 ymax = 10 url_params = '?in_bbox=%d,%d,%d,%d&format=json' % (xmin, ymin, xmax, ymax) # 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),(10,0),(10,10),(0,10),(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_bbox_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_bbox_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) @skipIf(has_spatialite, 'Skipped test for spatialite backend: not accurate enough') 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) @skipIf(has_spatialite, 'Skipped test for spatialite backend: missing feature "dwithin"') def test_DistanceToPointFilter_filtering(self): """ Checks that the DistancFilter returns only objects within the given distance of the given geometry defined by the URL parameters """ self.assertEqual(Location.objects.count(), 0) # Filter parameters distance = 5000 # meters point_inside_ggpark = [-122.49034881591797, 37.76949349270407] point_on_golden_gate_bridge = [-122.47894, 37.8199] point_on_alcatraz = [-122.4222, 37.82667] point_on_treasure_island = [-122.3692, 37.8244] point_on_angel_island = [-122.4326, 37.86091] url_params = '?dist=%0.4f&point=hello&format=json' % (distance) response = self.client.get('%s%s' % (self.location_within_distance_of_point_list_url, url_params)) self.assertEqual(response.status_code, 400) url_params = '?dist=%0.4f&point=%0.4f,%0.4f&format=json' % (distance, point_on_alcatraz[0], point_on_alcatraz[1]) treasure_island_geojson = """{ "type": "Polygon", "coordinates": [ [ [ -122.44640350341795, 37.86103094116189 ], [ -122.44262695312501, 37.85506751416839 ], [ -122.43481636047363, 37.853305500228025 ], [ -122.42975234985352, 37.854660899304704 ], [ -122.41953849792479, 37.852627791344894 ], [ -122.41807937622069, 37.853305500228025 ], [ -122.41868019104004, 37.86211514878027 ], [ -122.42391586303711, 37.870584971740065 ], [ -122.43035316467285, 37.8723465726078 ], [ -122.43515968322752, 37.86963639998042 ], [ -122.43953704833984, 37.86882332875222 ], [ -122.44640350341795, 37.86103094116189 ] ] ] }""" treasure_island_geom = GEOSGeometry(treasure_island_geojson) treasure_island = Location() treasure_island.name = "Treasure Island" treasure_island.geometry = treasure_island_geom treasure_island.full_clean() treasure_island.save() ggpark_geojson = """{ "type": "Polygon", "coordinates": [ [ [ -122.5111198425293, 37.77125750792944 ], [ -122.51026153564452, 37.76447260365713 ], [ -122.45309829711913, 37.76677954095475 ], [ -122.45481491088867, 37.77424266859531 ], [ -122.5111198425293, 37.77125750792944 ] ] ] }""" ggpark_geom = GEOSGeometry(ggpark_geojson) ggpark = Location() ggpark.name = "Golden Gate Park" ggpark.geometry = ggpark_geom ggpark.save() # Make sure we only get back the ones within the distance response = self.client.get('%s%s' % (self.location_within_distance_of_point_list_url, url_params)) self.assertEqual(len(response.data['features']), 1) for result in response.data['features']: self.assertEqual(result['properties']['name'] in (treasure_island.name), True) # Make sure we get back all the ones within the distance distance = 7000 url_params = '?dist=%0.4f&point=%0.4f,%0.4f&format=json' % (distance, point_on_alcatraz[0], point_on_alcatraz[1]) response = self.client.get('%s%s' % (self.location_within_distance_of_point_list_url, url_params)) self.assertEqual(len(response.data['features']), 2) for result in response.data['features']: self.assertEqual(result['properties']['name'] in (ggpark.name, treasure_island.name), True) # Make sure we only get back the ones within the distance degrees = .05 url_params = '?dist=%0.4f&point=%0.4f,%0.4f&format=json' % (degrees, point_on_alcatraz[0], point_on_alcatraz[1]) response = self.client.get(self.location_within_degrees_of_point_list_url + url_params) self.assertEqual(len(response.data['features']), 1) for result in response.data['features']: self.assertEqual(result['properties']['name'] in (treasure_island.name), True) @skipIf(has_spatialite, 'Skipped test for spatialite backend: missing feature "contains_properly"') def test_GeometryField_filtering(self): """ Checks that the GeometryField allows sane filtering. """ self.assertEqual(Location.objects.count(), 0) treasure_island_geojson = """{ "type": "Polygon", "coordinates": [ [ [ -122.44640350341795, 37.86103094116189 ], [ -122.44262695312501, 37.85506751416839 ], [ -122.43481636047363, 37.853305500228025 ], [ -122.42975234985352, 37.854660899304704 ], [ -122.41953849792479, 37.852627791344894 ], [ -122.41807937622069, 37.853305500228025 ], [ -122.41868019104004, 37.86211514878027 ], [ -122.42391586303711, 37.870584971740065 ], [ -122.43035316467285, 37.8723465726078 ], [ -122.43515968322752, 37.86963639998042 ], [ -122.43953704833984, 37.86882332875222 ], [ -122.44640350341795, 37.86103094116189 ] ] ] }""" treasure_island_geom = GEOSGeometry(treasure_island_geojson) treasure_island = Location() treasure_island.name = "Treasure Island" treasure_island.geometry = treasure_island_geom treasure_island.full_clean() treasure_island.save() ggpark_geojson = """{ "type": "Polygon", "coordinates": [ [ [ -122.5111198425293, 37.77125750792944 ], [ -122.51026153564452, 37.76447260365713 ], [ -122.45309829711913, 37.76677954095475 ], [ -122.45481491088867, 37.77424266859531 ], [ -122.5111198425293, 37.77125750792944 ] ] ] }""" ggpark_geom = GEOSGeometry(ggpark_geojson) ggpark = Location() ggpark.name = "Golden Gate Park" ggpark.geometry = ggpark_geom ggpark.save() point_inside_ggpark_geojson = """{ "type": "Point", "coordinates": [ -122.49034881591797, 37.76949349270407 ] }""" try: quoted_param = urllib.quote(point_inside_ggpark_geojson) except AttributeError: quoted_param = urllib.parse.quote(point_inside_ggpark_geojson) url_params = "?contains_properly=%s" % quoted_param response = self.client.get('{0}{1}'.format(self.geojson_contained_in_geometry, url_params)) self.assertEqual(len(response.data), 1) geometry_response = GEOSGeometry(json.dumps(response.data[0]['geometry'])) self.assertTrue(geometry_response.equals_exact(ggpark_geom)) self.assertEqual(response.data[0]['name'], ggpark.name) # try without any param, should return both response = self.client.get(self.geojson_contained_in_geometry) self.assertEqual(len(response.data), 2) def test_inBBOXFilter_filtering_none(self): url_params = '?in_bbox=&format=json' response = self.client.get(self.location_contained_in_bbox_list_url + url_params) self.assertDictEqual(response.data, {'type':'FeatureCollection','features':[]}) def test_inBBOXFilter_ValueError(self): url_params = '?in_bbox=0&format=json' response = self.client.get(self.location_contained_in_bbox_list_url + url_params) self.assertEqual(response.data['detail'], 'Invalid bbox string supplied for parameter in_bbox') def test_inBBOXFilter_filter_field_none(self): from .views import GeojsonLocationContainedInBBoxList as view original_value = view.bbox_filter_field view.bbox_filter_field = None url_params = '?in_bbox=0,0,0,0&format=json' response = self.client.get(self.location_contained_in_bbox_list_url + url_params) self.assertDictEqual(response.data, {'type':'FeatureCollection','features':[]}) view.bbox_filter_field = original_value def test_TileFilter_filtering_none(self): url_params = '?tile=&format=json' response = self.client.get(self.location_contained_in_tile_list_url + url_params) self.assertEqual(response.data, {'type':'FeatureCollection','features':[]}) def test_TileFilter_ValueError(self): url_params = '?tile=1/0&format=json' response = self.client.get(self.location_contained_in_tile_list_url + url_params) self.assertEqual(response.data['detail'], 'Invalid tile string supplied for parameter tile') def test_DistanceToPointFilter_filtering_none(self): url_params = '?dist=%0.4f&point=&format=json' % 5000 response = self.client.get('%s%s' % (self.location_within_distance_of_point_list_url, url_params)) self.assertDictEqual(response.data, {'type':'FeatureCollection','features':[]}) def test_DistanceToPointFilter_filter_field_none(self): from .views import GeojsonLocationWithinDistanceOfPointList as view original_value = view.distance_filter_field view.distance_filter_field = None url_params = '?dist=%0.4f&point=&format=json' % 5000 response = self.client.get('%s%s' % (self.location_within_distance_of_point_list_url, url_params)) self.assertDictEqual(response.data, {'type':'FeatureCollection','features':[]}) view.distance_filter_field = original_value def test_DistanceToPointFilter_ValueError_point(self): url_params = '?dist=500.0&point=hello&format=json' response = self.client.get('%s%s' % (self.location_within_distance_of_point_list_url, url_params)) self.assertEqual(response.data['detail'], 'Invalid geometry string supplied for parameter point') def test_DistanceToPointFilter_ValueError_distance(self): url_params = '?dist=wrong&point=12.0,42.0&format=json' response = self.client.get('%s%s' % (self.location_within_distance_of_point_list_url, url_params)) self.assertEqual(response.data['detail'], 'Invalid distance string supplied for parameter dist') djangorestframework-gis-0.10.1/tests/django_restframework_gis_tests/test_bbox.py0000664000175000017500000000677612544054631031613 0ustar nemesisnemesis00000000000000import json from django.test import TestCase from django.core.urlresolvers import reverse from django.core.exceptions import ImproperlyConfigured from rest_framework_gis import serializers as gis_serializers from .models import BoxedLocation, Location from .serializers import LocationGeoSerializer class TestRestFrameworkGisBBox(TestCase): """ unit tests for bbox support in restframework_gis """ def setUp(self): self.geojson_boxedlocation_list_url = reverse('api_geojson_boxedlocation_list') self.geojson_location_bbox_list_url = reverse('api_geojson_location_bbox_list') def _create_locations(self): self.bl1 = BoxedLocation.objects.create(id=1, name='l1', slug='l1', geometry='POINT (13.007 42.423)', bbox_geometry='POLYGON((12.997 42.413,12.997 42.433,13.017 42.433,13.017 42.413,12.997 42.413))') self.bl2 = BoxedLocation.objects.create(id=2, name='l2', slug='l2', geometry='POINT (12.007 43.423)', bbox_geometry='POLYGON((11.997 43.413,11.997 43.433,12.017 43.433,12.017 43.413,11.997 43.413))') self.l1 = Location.objects.create(id=1, name='l1', slug='l1', geometry='POLYGON((12.997 42.413,12.997 42.433,13.017 42.433,13.017 42.413,12.997 42.413))') def test_list(self): self._create_locations() response = self.client.get(self.geojson_boxedlocation_list_url) self.assertEqual(response.status_code, 200) self.assertEqual(len(response.data['features']), 2) for feature in response.data['features']: self.assertIn('bbox', feature) fid = feature['id'] if fid==1: self.assertEqual(feature['bbox'], self.bl1.bbox_geometry.extent) elif fid==2: self.assertEqual(feature['bbox'], self.bl2.bbox_geometry.extent) else: self.fail("Unexpected id: {0}".format(fid)) BoxedLocation.objects.all().delete() def test_post_location_list_geojson(self): self.assertEqual(BoxedLocation.objects.count(), 0) data = { "properties": { "name": "geojson input test", }, "geometry": { "type": "Point", "coordinates": [ 12.49, 41.89 ] }, "bbox": [11.0, 40.0, 13.0, 42.0] } response = self.client.post(self.geojson_boxedlocation_list_url, data=json.dumps(data), content_type='application/json') self.assertEqual(response.status_code, 201) self.assertEqual(BoxedLocation.objects.count(), 1) self.assertEqual(BoxedLocation.objects.all()[0].bbox_geometry.extent, (11.0,40.0,13.0,42.0)) def test_get_autogenerated_location_bbox_geojson(self): self._create_locations() response = self.client.get(self.geojson_location_bbox_list_url) self.assertEqual(response.status_code, 200) self.assertEqual(len(response.data['features']), 1) self.assertEqual(response.data['features'][0]['bbox'], self.l1.geometry.extent) def test_bbox_improperly_configured(self): self._create_locations() class LocationGeoFeatureSerializer(gis_serializers.GeoFeatureModelSerializer): class Meta: model = Location geo_field = 'geometry' bbox_geo_field = 'geometry' auto_bbox = True with self.assertRaises(ImproperlyConfigured): LocationGeoFeatureSerializer(instance=self.l1) djangorestframework-gis-0.10.1/tests/django_restframework_gis_tests/models.py0000664000175000017500000000220612614363720031065 0ustar nemesisnemesis00000000000000from django.contrib.gis.db import models from django.utils.text import slugify __all__ = [ 'Location', 'LocatedFile', 'BoxedLocation' ] class BaseModel(models.Model): name = models.CharField(max_length=32) slug = models.SlugField(max_length=128, unique=True, blank=True) timestamp = models.DateTimeField(null=True, blank=True) geometry = models.GeometryField() objects = models.GeoManager() class Meta: abstract = True def __unicode__(self): return self.name def _generate_slug(self): if self.slug == '' or self.slug is None: try: name = unicode(self.name) except NameError: name = self.name self.slug = slugify(name) def clean(self): self._generate_slug() def save(self, *args, **kwargs): self._generate_slug() super(BaseModel, self).save(*args, **kwargs) class Location(BaseModel): pass class LocatedFile(BaseModel): file = models.FileField(upload_to='located_files', blank=True, null=True) class BoxedLocation(BaseModel): bbox_geometry = models.PolygonField() djangorestframework-gis-0.10.1/tests/django_restframework_gis_tests/admin.py0000664000175000017500000000067012544034142030670 0ustar nemesisnemesis00000000000000from django.contrib import admin from django.conf import settings GEODJANGO_IMPROVED_WIDGETS = 'olwidget' in settings.INSTALLED_APPS if GEODJANGO_IMPROVED_WIDGETS: from olwidget.admin import GeoModelAdmin else: from django.contrib.gis.admin import ModelAdmin as GeoModelAdmin from .models import Location class LocationAdmin(GeoModelAdmin): list_display = ('name', 'geometry') admin.site.register(Location, LocationAdmin)djangorestframework-gis-0.10.1/tests/django_restframework_gis_tests/tests.py0000664000175000017500000006244412614363720030756 0ustar nemesisnemesis00000000000000""" unit tests for restframework_gis """ import urllib import sys import json from django.test import TestCase from django.contrib.gis.geos import GEOSGeometry, Polygon, Point from django.core.urlresolvers import reverse from django.core.exceptions import ImproperlyConfigured from rest_framework_gis import serializers as gis_serializers from .models import Location, LocatedFile from .serializers import LocationGeoSerializer class TestRestFrameworkGis(TestCase): def setUp(self): self.location_list_url = reverse('api_location_list') self.geojson_location_list_url = reverse('api_geojson_location_list') self.geos_error_message = 'Invalid format: string or unicode input unrecognized as GeoJSON, WKT EWKT or HEXEWKB.' def _create_locations(self): self.l1 = Location.objects.create(id=1, name='l1', slug='l1', geometry='POINT (13.0078125000020002 42.4234565179379999)') self.l2 = Location.objects.create(id=2, name='l2', slug='l2', geometry='POINT (12.0078125000020002 43.4234565179379999)') def test_get_location_list(self): response = self.client.get(self.location_list_url) self.assertEqual(response.status_code, 200) def test_post_location_list_geojson(self): self.assertEqual(Location.objects.count(), 0) data = { "name": "geojson input test", "geometry": { "type": "GeometryCollection", "geometries": [ { "type": "Point", "coordinates": [ 12.492324113849, 41.890307434153 ] } ] } } response = self.client.post(self.location_list_url, data=json.dumps(data), content_type='application/json') self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) data = { "name": "geojson input test2", "geometry": { "type": "Point", "coordinates": [ 12.492324113849, 41.890307434153 ] } } response = self.client.post(self.location_list_url, data=json.dumps(data), content_type='application/json') self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 2) def test_post_location_list_geojson_as_multipartformdata(self): """ emulate sending geojson string in webform """ self.assertEqual(Location.objects.count(), 0) data = { "name": "geojson input test", "geometry": json.dumps({ "type": "GeometryCollection", "geometries": [ { "type": "Point", "coordinates": [ 12.492324113849, 41.890307434153 ] } ] }) } response = self.client.post(self.location_list_url, data) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) def test_post_HTML_browsable_api(self): self.assertEqual(Location.objects.count(), 0) data = { "name": "geojson input test2", "slug": "prova", "geometry": json.dumps({ "type": "GeometryCollection", "geometries": [ { "type": "Point", "coordinates": [ 12.492324113849, 41.890307434153 ] } ] }) } response = self.client.post(self.location_list_url, data, HTTP_ACCEPT='text/html') self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) location = Location.objects.all()[0] self.assertEqual(location.name, 'geojson input test2') self.assertEqual(location.slug, 'prova') def test_post_location_list_WKT(self): self.assertEqual(Location.objects.count(), 0) data = { 'name': 'WKT input test', 'geometry': 'POINT (12.492324113849 41.890307434153)' } response = self.client.post(self.location_list_url, data) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) def test_post_location_list_EWKT(self): self.assertEqual(Location.objects.count(), 0) data = { 'name': 'EWKT input test', 'geometry': 'SRID=28992;POINT(221160 600204)' } response = self.client.post(self.location_list_url, data) expected_coords = (6.381495826183805, 53.384066927384985) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) for l, e in zip(Location.objects.get(name='EWKT input test').geometry.coords, expected_coords): self.assertAlmostEqual(l, e, places=5) def test_post_location_list_WKT_as_json(self): self.assertEqual(Location.objects.count(), 0) data = { 'name': 'WKT input test', 'geometry': 'POINT (12.492324113849 41.890307434153)' } response = self.client.post(self.location_list_url, data=json.dumps(data), content_type='application/json') self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) def test_post_location_list_empty_geometry(self): data = { 'name': 'empty input test' } response = self.client.post(self.location_list_url, data) self.assertEqual(response.data['geometry'][0], 'This field is required.') data = { 'name': 'empty input test', 'geometry': '' } response = self.client.post(self.location_list_url, data) self.assertEqual(response.data['geometry'][0], 'This field is required.') data = { 'name': 'empty input test' } response = self.client.post(self.location_list_url, data=json.dumps(data), content_type='application/json') self.assertEqual(response.data['geometry'][0], 'This field is required.') data = { 'name': 'empty input test', 'geometry': '' } response = self.client.post(self.location_list_url, data=json.dumps(data), content_type='application/json') self.assertEqual(response.data['geometry'][0], 'This field is required.') def test_post_location_list_invalid_WKT(self): data = { 'name': 'WKT wrong input test', 'geometry': 'I AM OBVIOUSLY WRONG' } response = self.client.post(self.location_list_url, data=json.dumps(data), content_type='application/json') self.assertEqual(response.status_code, 400) self.assertEqual(Location.objects.count(), 0) self.assertEqual(response.data['geometry'][0], self.geos_error_message) # repeat as multipart form data response = self.client.post(self.location_list_url, data) self.assertEqual(response.data['geometry'][0], self.geos_error_message) data = { 'name': 'I AM MODERATELY WRONG', 'geometry': 'POINT (12.492324113849, 41.890307434153)' } response = self.client.post(self.location_list_url, data=json.dumps(data), content_type='application/json') self.assertEqual(response.data['geometry'][0], self.geos_error_message) # repeat as multipart form data response = self.client.post(self.location_list_url, data) self.assertEqual(response.data['geometry'][0], self.geos_error_message) def test_post_location_list_invalid_geojson(self): data = { "name": "quite wrong", "geometry": { "type": "ARRRR", "dasdas": [ { "STtype": "PTUAMAoint", "NNAare":"rgon" } ] } } response = self.client.post(self.location_list_url, data=json.dumps(data), content_type='application/json') self.assertEqual(response.data['geometry'][0], self.geos_error_message) data = { "name": "very wrong", "geometry": ['a', 'b', 'c'] } response = self.client.post(self.location_list_url, data=json.dumps(data), content_type='application/json') self.assertEqual(response.data['geometry'][0], self.geos_error_message) data = { "name": "very wrong", "geometry": False } response = self.client.post(self.location_list_url, data=json.dumps(data), content_type='application/json') self.assertEqual(response.data['geometry'][0], self.geos_error_message) data = { "name": "very wrong", "geometry": { "value": { "nested": ["yo"] } } } response = self.client.post(self.location_list_url, data=json.dumps(data), content_type='application/json') self.assertEqual(response.data['geometry'][0], self.geos_error_message) def test_geojson_format(self): """ test geojson format """ location = Location.objects.create(name='geojson test', geometry='POINT (135.0 45.0)') url = reverse('api_geojson_location_details', args=[location.id]) expected = { 'id': location.id, 'type': 'Feature', 'properties': { 'details': "http://testserver/geojson/%s/" % location.id, 'name': 'geojson test', 'fancy_name': 'Kool geojson test', 'timestamp': None, 'slug': 'geojson-test', }, 'geometry': { 'type': 'Point', 'coordinates': [ 135.0, 45.0, ], } } response = self.client.get(url) if sys.version_info>(3,0,0): self.assertCountEqual(json.dumps(response.data), json.dumps(expected)) else: self.assertItemsEqual(json.dumps(response.data), json.dumps(expected)) response = self.client.get(url, HTTP_ACCEPT='text/html') self.assertContains(response, "Kool geojson test") def test_geojson_id_attribute(self): location = Location.objects.create(name='geojson test', geometry='POINT (10.1 10.1)') url = reverse('api_geojson_location_details', args=[location.id]) response = self.client.get(url) self.assertEqual(response.data['id'], location.id) def test_geojson_id_attribute_slug(self): location = Location.objects.create(name='geojson test', geometry='POINT (10.1 10.1)') url = reverse('api_geojson_location_slug_details', args=[location.slug]) response = self.client.get(url) self.assertEqual(response.data['id'], location.slug) def test_geojson_false_id_attribute_slug(self): location = Location.objects.create(name='falseid test', geometry='POINT (10.1 10.1)') url = reverse('api_geojson_location_falseid_details', args=[location.id]) response = self.client.get(url) self.assertEqual(response.data['properties']['name'], 'falseid test') with self.assertRaises(KeyError): response.data['id'] def test_geojson_no_id_attribute_slug(self): location = Location.objects.create(name='noid test', geometry='POINT (10.1 10.1)') url = reverse('api_geojson_location_noid_details', args=[location.id]) response = self.client.get(url) self.assertEqual(response.data['properties']['name'], 'noid test') with self.assertRaises(KeyError): response.data['id'] def test_geojson_filefield_attribute(self): located_file = LocatedFile.objects.create(name='geojson filefield test', geometry='POINT (10.1 10.1)') url = reverse('api_geojson_located_file_details', args=[located_file.id]) response = self.client.get(url) self.assertEqual(response.data['properties']['file'], None) def test_post_geojson_location_list(self): self.assertEqual(Location.objects.count(), 0) data = { "type": "Feature", "properties": { "name": "point?", "details": "ignore this" }, "geometry": { "type": "Point", "coordinates": [ 10.1, 10.1 ] } } response = self.client.post(self.geojson_location_list_url, data=json.dumps(data), content_type='application/json') self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) url = reverse('api_geojson_location_details', args=[Location.objects.order_by('-id')[0].id]) response = self.client.get(url) self.assertEqual(response.data['properties']['name'], "point?") self.assertEqual(response.data['geometry']['type'], "Point") self.assertEqual(json.dumps(response.data['geometry']['coordinates']), "[10.1, 10.1]") self.assertNotEqual(response.data['properties']['details'], "ignore this") def test_post_geojson_location_list_HTML(self): self.assertEqual(Location.objects.count(), 0) data = { "type": "Feature", "properties": { "name": "point?", "details": "ignore this" }, "geometry": { "type": "Point", "coordinates": [ 10.1, 10.1 ] } } response = self.client.post(self.geojson_location_list_url, data=json.dumps(data), content_type='application/json', HTTP_ACCEPT='text/html') self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) url = reverse('api_geojson_location_details', args=[Location.objects.order_by('-id')[0].id]) response = self.client.get(url) self.assertEqual(response.data['properties']['name'], "point?") self.assertEqual(response.data['geometry']['type'], "Point") self.assertEqual(json.dumps(response.data['geometry']['coordinates']), "[10.1, 10.1]") self.assertNotEqual(response.data['properties']['details'], "ignore this") def test_post_invalid_geojson_location_list(self): data = { "type": "Feature", "properties": { "details": "ignore this" }, "geometry": { "type": "Point", "coordinates": [ 10.1, 10.1 ] } } response = self.client.post(self.geojson_location_list_url, data=json.dumps(data), content_type='application/json') self.assertEqual(response.status_code, 400) self.assertEqual(Location.objects.count(), 0) self.assertEqual(response.data['name'][0], "This field is required.") data = { "type": "Feature", "properties": { "name": "point?", }, "geometry": { "type": "Point", "WRONG": {} } } response = self.client.post(self.geojson_location_list_url, data=json.dumps(data), content_type='application/json') self.assertEqual(response.status_code, 400) self.assertEqual(Location.objects.count(), 0) self.assertEqual(response.data['geometry'][0], self.geos_error_message) def test_post_geojson_location_list_WKT(self): self.assertEqual(Location.objects.count(), 0) data = { "type": "Feature", "properties": { "name": "point?", }, "geometry": "POINT (10.1 10.1)" } response = self.client.post(self.geojson_location_list_url, data=json.dumps(data), content_type='application/json') self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) url = reverse('api_geojson_location_details', args=[Location.objects.order_by('-id')[0].id]) response = self.client.get(url) self.assertEqual(response.data['properties']['name'], "point?") self.assertEqual(response.data['geometry']['type'], "Point") self.assertEqual(json.dumps(response.data['geometry']['coordinates']), "[10.1, 10.1]") def test_geofeatured_model_serializer_compatible_with_geomodel_serializer(self): self.assertEqual(Location.objects.count(), 0) data = { "name": "geojson input test", "geometry": { "type": "GeometryCollection", "geometries": [ { "type": "Point", "coordinates": [ 12.492324113849, 41.890307434153 ] } ] } } response = self.client.post(self.geojson_location_list_url, data=json.dumps(data), content_type='application/json') self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) def test_geofeatured_model_post_as_multipartformdata(self): """ emulate sending geojson string in webform """ self.assertEqual(Location.objects.count(), 0) data = { "name": "geojson input test", "geometry": json.dumps({ "type": "Point", "coordinates": [ 12.492324113849, 41.890307434153 ] }) } response = self.client.post(self.location_list_url, data) self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) self.assertEqual(response.data['geometry']['type'], "Point") def test_HTML_browsable_geojson_location_list(self): response = self.client.get(self.geojson_location_list_url, HTTP_ACCEPT='text/html') self.assertEqual(response.status_code, 200) self._create_locations() response = self.client.get(self.geojson_location_list_url, HTTP_ACCEPT='text/html') self.assertContains(response, 'l1') self.assertContains(response, 'l2') def test_post_geojson_location_list_HTML_web_form(self): self.assertEqual(Location.objects.count(), 0) data = { "name": "HTML test", "geometry": json.dumps({ "type": "Point", "coordinates": [ 10.1, 10.1 ] }) } response = self.client.post(self.geojson_location_list_url, data, HTTP_ACCEPT='text/html') self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) location = Location.objects.all()[0] self.assertEqual(location.name, "HTML test") self.assertEqual(location.geometry.geom_type, "Point") def test_post_geojson_location_list_HTML_web_form_WKT(self): self.assertEqual(Location.objects.count(), 0) data = { "name": "HTML test WKT", "geometry": "POINT (10.1 10.1)" } response = self.client.post(self.geojson_location_list_url, data, HTTP_ACCEPT='text/html') self.assertEqual(response.status_code, 201) self.assertEqual(Location.objects.count(), 1) location = Location.objects.all()[0] self.assertEqual(location.name, "HTML test WKT") def test_geojson_HTML_widget_value(self): self._create_locations() response = self.client.get(self.geojson_location_list_url, HTTP_ACCEPT='text/html') self.assertContains(response, '