Package: drf-gis
django-rest-framework-gisGeographic add-ons for Django Rest Framework -Mailing
List.Install last stable version from pypipipinstalldjangorestframework-gisInstall development versionpipinstallhttps://github.com/openwisp/django-rest-framework-gis/tarball/masterSetupAddrest_framework_gisinsettings.INSTALLED_APPS, afterrest_framework:INSTALLED_APPS=[# ...'rest_framework','rest_framework_gis',# ...]Compatibility with DRF, Django and PythonDRF-gis versionDRF versionDjango versionPython version1.1.x3.10up to3.142.2 to 4.03.6to3.111.0.x3.10up to3.132.2 to 4.03.6to3.90.18.x3.10up to3.132.2 to 4.03.6to3.90.17.x3.10up to3.122.2 to 3.13.6to3.80.16.x3.102.2 to 3.13.6to3.80.15.x3.101.11, 2.2 to 3.03.5to3.80.14.x3.3to3.91.11to2.13.4to3.70.13.x3.3to3.81.11to2.02.7to3.60.12.x3.1to3.71.11to2.02.7to3.60.11.x3.1to3.61.7to1.112.7to3.60.10.x3.1to3.31.7to1.92.7to3.50.9.63.1to3.21.5to1.82.6to3.50.9.53.1to3.21.5to1.82.6to3.40.9.43.1to3.21.5to1.82.6to3.40.9.33.11.5to1.82.6to3.40.9.23.11.5to1.82.6to3.40.9.13.11.5to1.82.6to3.40.93.11.5to1.82.6,2.7,3.3,3.40.93.11.5to1.82.6,2.7,3.3,3.40.93.11.5to1.82.6,2.7,3.3,3.40.8.23.0.4to3.1.11.5to1.82.6,2.7,3.3,3.40.8.13.0.4to3.1.11.5to1.82.6,2.7,3.3,3.40.83.0.41.5to1.72.6,2.7,3.3,3.40.72.4.31.5to1.72.6,2.7,3.3,3.40.62.4.31.5to1.72.6,2.7,3.3,3.40.5from2.3.14to2.4.21.5to1.72.6,2.7,3.3,3.40.4from2.3.14to2.4.21.5to1.72.6,2.7,3.3,3.40.3from2.3.14to2.4.21.5,1.62.6,2.70.2from2.2.2to2.3.131.5,1.62.6,2.7FieldsGeometryFieldProvides aGeometryField, which is a subclass of Django Rest Framework
(from now onDRF)WritableField. This field handles GeoDjango
geometry fields, providing customto_nativeandfrom_nativemethods for GeoJSON input/output.This field takes three optional arguments:precision: Passes coordinates through Python’s builtinround()function (docs), rounding values to
the provided level of precision. E.g. A Point with lat/lng of[51.0486,-114.0708]passed through aGeometryField(precision=2)would return a Point with a lat/lng of[51.05,-114.07].remove_duplicates: Remove sequential duplicate coordinates from line and
polygon geometries. This is particularly useful when used with theprecisionargument, as the likelihood of duplicate coordinates increase as precision of
coordinates are reduced.auto_bbox: IfTrue, the GeoJSON object will include
abounding box,
which is the smallest possible rectangle enclosing the geometry.Note:Whileprecisionandremove_duplicatesare designed to reduce the
byte size of the API response, they will also increase the processing time
required to render the response. This will likely be negligible for small GeoJSON
responses but may become an issue for large responses.New in 0.9.3:there is no need to define this field explicitly in your serializer,
it’s mapped automatically during initialization inrest_framework_gis.apps.AppConfig.ready().GeometrySerializerMethodFieldProvides aGeometrySerializerMethodField, which is a subclass of DRFSerializerMethodFieldand handles values which are computed with a serializer
method and are used as ageo_field.See example below.SerializersGeoModelSerializer (DEPRECATED)Deprecated, will be removed in 1.0: Using this serializer is not needed anymore since 0.9.3, if you addrest_framework_gisinsettings.INSTALLED_APPSthe serialization will work out of the box with DRF.
ReferIssue #156.Provides aGeoModelSerializer, which is a subclass of DRFModelSerializer. This serializer updates the field_mapping
dictionary to include field mapping of GeoDjango geometry fields to the
aboveGeometryField.For example, the following model:classLocation(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 ModelSerializerver < 0.9.3will output:{"id":1,"address":"742 Evergreen Terrace","city":"Springfield","state":"Oregon","point":"POINT(-123.0208 44.0464)"}In contrast, theGeoModelSerializerwill output:{"id":1,"address":"742 Evergreen Terrace","city":"Springfield","state":"Oregon","point":{"type":"Point","coordinates":[-123.0208,44.0464],}}Note:Forver>=0.9.3: The DRF model serializer will give the same output as above, if;rest_framework_gisis set insettings.INSTALLED_APPSorthe field in the serializer is set explicitly asGeometryField.GeoFeatureModelSerializerGeoFeatureModelSerializeris a subclass ofrest_framework.ModelSerializerwhich will output data in a format that isGeoJSONcompatible. Using
the above example, theGeoFeatureModelSerializerwill output:{"id":1,"type":"Feature","geometry":{"type":"Point","coordinates":[-123.0208,44.0464],},"properties":{"address":"742 Evergreen Terrace","city":"Springfield","state":"Oregon"}}If you are serializing an object list,GeoFeatureModelSerializerwill create aFeatureCollection:{"type":"FeatureCollection","features":[{"id":1"type":"Feature","geometry":{"type":"Point","coordinates":[-123.0208,44.0464],},"properties":{"address":"742 Evergreen Terrace","city":"Springfield","state":"Oregon",}}{"id":2,"type":"Feature","geometry":{"type":"Point","coordinates":[-123.0208,44.0489],},"properties":{"address":"744 Evergreen Terrace","city":"Springfield","state":"Oregon"}}}Specifying the geometry field: “geo_field”GeoFeatureModelSerializerrequires you to define ageo_fieldto be serialized as the “geometry”. For example:fromrest_framework_gis.serializersimportGeoFeatureModelSerializerclassLocationSerializer(GeoFeatureModelSerializer):""" A class to serialize locations as GeoJSON compatible data """classMeta:model=Locationgeo_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_fieldmay also be an instance ofGeometrySerializerMethodField.
In this case you can compute its value during serialization. For example:fromdjango.contrib.gis.geosimportPointfromrest_framework_gis.serializersimportGeoFeatureModelSerializer,GeometrySerializerMethodFieldclassLocationSerializer(GeoFeatureModelSerializer):""" A class to serialize locations as GeoJSON compatible data """# a field which contains a geometry value and can be used as geo_fieldother_point=GeometrySerializerMethodField()defget_other_point(self,obj):returnPoint(obj.point.lat/2,obj.point.lon/2)classMeta:model=Locationgeo_field='other_point'Serializer forgeo_fieldmay also returnNonevalue, which will translate tonullvalue for geojsongeometryfield.Specifying the ID: “id_field”The primary key of the model (usually the “id” attribute) is
automatically used as theidfield of eachGeoJSON Feature Object.The default behaviour follows theGeoJSON RFC,
but it can be disabled by settingid_fieldtoFalse:fromrest_framework_gis.serializersimportGeoFeatureModelSerializerclassLocationSerializer(GeoFeatureModelSerializer):classMeta:model=Locationgeo_field="point"id_field=Falsefields=('id','address','city','state')Theid_fieldcan also be set to use some other unique field in your model, eg:slug:fromrest_framework_gis.serializersimportGeoFeatureModelSerializerclassLocationSerializer(GeoFeatureModelSerializer):classMeta:model=Locationgeo_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 aboundingbox of a feature.GeoFeatureModelSerializerallows two different ways to fill this property. The first
is using thegeo_fieldto calculate the bounding box of a feature. This only allows
read access for a REST client and can be achieved usingauto_bbox. Example:fromrest_framework_gis.serializersimportGeoFeatureModelSerializerclassLocationSerializer(GeoFeatureModelSerializer):classMeta:model=Locationgeo_field='geometry'auto_bbox=TrueThe second approach uses thebbox_geo_fieldto specify an additionalGeometryFieldof 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:fromrest_framework_gis.serializersimportGeoFeatureModelSerializerclassLocationSerializer(GeoFeatureModelSerializer):classMeta:model=BoxedLocationgeo_field='geometry'bbox_geo_field='bbox_geometry'Custom GeoJSON properties sourceIn GeoJSON each feature can have apropertiesmember 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 thepropertiesmember.The following example shows how to use a PostgreSQL HStore field as a source for
thepropertiesmember:# models.pyclassLink(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=dict)geo=models.LineStringField()objects=models.GeoManager()# serializers.pyclassNetworkGeoSerializer(GeoFeatureModelSerializer):classMeta:model=models.Linkgeo_field='geo'auto_bbox=Truedefget_properties(self,instance,fields):# This is a PostgreSQL HStore field, which django maps to a dictreturninstance.metadatadefunformat_geojson(self,feature):attrs={self.Meta.geo_field:feature["geometry"],"metadata":feature["properties"]}ifself.Meta.bbox_geo_fieldand"bbox"infeature:attrs[self.Meta.bbox_geo_field]=Polygon.from_bbox(feature["bbox"])returnattrsWhen the serializer renders GeoJSON, it calls the methodget_propertiesfor 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 byunformat_geojson. It should
return a dictionary with your model attributes as keys, and the corresponding
values retrieved from the GeoJSON feature data.PaginationWe provide aGeoJsonPaginationclass.GeoJsonPaginationBased onrest_framework.pagination.PageNumberPagination.Code example:fromrest_framework_gis.paginationimportGeoJsonPagination# --- other omitted imports --- #classGeojsonLocationList(generics.ListCreateAPIView):# -- other omitted view attributes --- #pagination_class=GeoJsonPaginationExample result response (cut to one element only instead of 10):{"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"}}]}Filtersnote: this feature has been tested up to django-filter 1.0.We provide aGeometryFilterfield as well as aGeoFilterSetfor usage withdjango_filter. You simply provide, in the query
string, one of the textual types supported byGEOSGeometry. By
default, this includes WKT, HEXEWKB, WKB (in a buffer), and GeoJSON.GeometryFilterfromrest_framework_gis.filtersetimportGeoFilterSetfromrest_framework_gis.filtersimportGeometryFilterfromdjango_filtersimportfiltersclassRegionFilter(GeoFilterSet):slug=filters.CharFilter(name='slug',lookup_expr='istartswith')contains_geom=GeometryFilter(name='geom',lookup_expr='contains')classMeta:model=RegionWe can then filter in the URL, using GeoJSON, and we will perform a__containsgeometry lookup, e.g./region/?contains_geom={"type": "Point", "coordinates": [-123.26436996459961,44.564178042345375 ] }.GeoFilterSetTheGeoFilterSetprovides adjango_filtercompatibleFilterSetthat will automatically createGeometryFiltersforGeometryFields.InBBoxFilterProvides aInBBoxFilter, which is a subclass of DRFBaseFilterBackend. Filters a queryset to only those instances within
a certain bounding box.views.py:fromrest_framework_gis.filtersimportInBBoxFilterclassLocationList(ListAPIView):queryset=models.Location.objects.all()serializer_class=serializers.LocationSerializerbbox_filter_field='point'filter_backends=(InBBoxFilter,)bbox_filter_include_overlapping=True# OptionalWe 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, includebbox_filter_include_overlapping = Truein 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,)TMSTileFilterProvides aTMSTileFilter, which is a subclass ofInBBoxFilter.
Filters a queryset to only those instances within a bounding box defined
by aTMS tileaddress.views.py:fromrest_framework_gis.filtersimportTMSTileFilterclassLocationList(ListAPIView):queryset=models.Location.objects.all()serializer_class=serializers.LocationSerializerbbox_filter_field='point'filter_backends=(TMSTileFilter,)bbox_filter_include_overlapping=True# OptionalWe can then filter in the URL, using TMS tile addresses in the zoom/x/y format,
eg:./location/?tile=8/100/200which is equivalent 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.DistanceToPointFilterProvides aDistanceToPointFilter, which is a subclass of DRFBaseFilterBackend. Filters a queryset to only those instances within
a certain distance of a given point.views.py:fromrest_framework_gis.filtersimportDistanceToPointFilterclassLocationList(ListAPIView):queryset=models.Location.objects.all()serializer_class=serializers.LocationSerializerdistance_filter_field='geometry'filter_backends=(DistanceToPointFilter,)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=jsonwhich is equivalent 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%.DistanceToPointOrderingFilterProvides aDistanceToPointOrderingFilter,available on Django >= 3.0, which is a subclass ofDistanceToPointFilter.
Orders a queryset by distance to a given point, from the nearest to the most distant point.views.py:fromrest_framework_gis.filtersimportDistanceToPointOrderingFilterclassLocationList(ListAPIView):queryset=models.Location.objects.all()serializer_class=serializers.LocationSerializerdistance_ordering_filter_field='geometry'filter_backends=(DistanceToPointOrderingFilter,)We can then order the results by passing a point in (lon, lat) format in the URL.eg:./location/?point=-122.4862,37.7694&format=jsonwill order the results by the distance to the point (-122.4862, 37.7694).We can also reverse the order of the results by passingorder=desc:/location/?point=-122.4862,37.7694&order=desc&format=jsonSchema GenerationNote: Schema generation support is available only for DRF >= 3.12.Simplest Approach would be, changeDEFAULT_SCHEMA_CLASStorest_framework_gis.schema.GeoFeatureAutoSchema:REST_FRAMEWORK={...'DEFAULT_SCHEMA_CLASS':'rest_framework_gis.schema.GeoFeatureAutoSchema',...}If you do not want to change default schema generator class:You can pass this class as an argument toget_schema_viewfunction[Ref].You can pass this class as an argument to thegenerateschemacommand[Ref].Running the testsRequired setupYou need one of theSpatial Database servers supported by
GeoDjango,
and create a database for the tests.The following can be used with PostgreSQL:createdbdjango_restframework_gispsql-Upostgres-ddjango_restframework_gis-c"CREATE EXTENSION postgis"You might need to tweak the DB settings according to your DB
configuration. You can copy the filelocal_settings.example.pytolocal_settings.pyand change theDATABASESand/orINSTALLED_APPSdirectives there.This should allow you to run the tests already.For reference, the following steps will setup a development environment for
contributing to the project:create a spatial database named “django_restframework_gis”createlocal_settings.py, eg:cp local_settings.example.py local_settings.pytweak theDATABASESconfiguration directive according to your DB
settingsuncommentINSTALLED_APPSrunpython manage.py syncdbrunpython manage.py collectstaticrunpython manage.py runserverUsing toxThe recommended way to run the tests is by usingtox, which can be installed usingpip install tox.You can usetox-lto list the available environments, and then e.g. use
the following to run all tests with Python 3.6 and Django 1.11:tox-epy36-django111By default Django’s test runner is used, but there is a variation of tox’s
envlist to use pytest (using the-pytestsuffix).You can pass optional arguments to the test runner like this:tox-epy36-django111-pytest---ktest_fooRunning tests manuallyPlease refer to thetox.inifile for reference/help in case you want to run
tests manually / without tox.To run tests in docker usedocker-composebuilddocker-composerun--rmtestRunning QA-checksInstall the test requirements:pipinstall-rrequirements-test.txtReformat the code according toour coding style conventions with:openwisp-qa-formatRun the QA checks by using./run-qa-checksIn docker testing, QA checks are executed automatically.ContributingJoin theDjango REST Framework GIS Mailing
Listand announce your intentionsFollow thePEP8 Style Guide for Python
CodeFork this repoWrite codeWrite tests for your codeEnsure all tests passEnsure test coverage is not under 90%Document your changesSend pull request