/ django

Storing, querying & serializing location data with Django using PostGIS

If you are building location features chances are you have struggled with managing geospatial data on your server. It is a complex task that requires a few steps. Firstly, you would set up your web project and database. Secondly, you would use open source libraries to store this data process it and and make it available to consume through APIs. Here is guide in order to achieve these objectives with Django using Postgres and PostGIS.

You would find this post relevant if you are building location features:

Prerequisites:

  • Django project
  • PostgreSQL database

You will need:

Store, query and serialize location data using PostGIS

Set up

  1. Install postgis: If you have installed Postgres.app you would already have postgis installed. If not for any reason, run:
brew install postgis
  1. Install additional geolibs that Django needs. Run:
brew install gdal brew install libgeoip
  1. Add postgis extension in Postgres database as a root user of the database. Run the following in the Postgres shell:
\c DATABASE_NAME; CREATE EXTENSION IF NOT EXISTS postgis;
  1. Configure Django to use postgis (1/2): In your project's settings.py file, use django.contrib.gis.db.backends.postgis as the database engine like such:
DATABASES = {  
    'default': {
        'ENGINE': 'django.contrib.gis.db.backends.postgis',
        ...
    }
}
  1. Configure Django to use postgis (2/2): In your project's settings.py file, add django.contrib.gis to your INSTALLED_APPS array like such:
INSTALLED_APPS = [  
    ...
    'django.contrib.gis',
    ...
]

Store, query and serialize

  1. Store location in a model:

    • Add PointField in the model. Note that we are using django.contrib.gis.db.models instead of django.db.modelsPointField stores location as a tuple of latitude and longitude. The spatial_index=True parameter creates an index on the field and geography=True parameter enables geospatial queries on the field.
    from django.contrib.gis.db import models
    
    class Place(models.model):
        point = models.PointField(null=True, spatial_index=True, geography=True)
        ...
    
    • Save location to the field given an address string. You can use a geocoder library to get geocoded location from a service like Google Maps. The result is a json like this.
    import geocoder, json
    from django.contrib.gis.geos import GEOSGeometry
    
    geocoded = geocoder.google(address)
    place.point = GEOSGeometry(json.dumps(geocoded.geometry))
    place.save()
    
    import json
    from django.contrib.gis.geos import GEOSGeometry
    
    place.point = GEOSGeometry(json.dumps(location["geojson"]))
    place.save()
    
  2. Query location from the model:

    from django.contrib.gis.measure import D
    
    place = Place.objects.filter(point__dwithin=(point, D(m=100)))
    
    • Get [latitude, longitude] from the model:
    import json
    
    geojson = point.json
    coordinates = json.loads(geojson).get('coordinates')
    return [coordinates[1], coordinates[0]]
    
  3. Serialize location from the model: Django Rest Framework sugar handles this automatically and serializes the location in the field to json as below.

    "point": {
        "type": "Point",
        "coordinates": [72.870264, 19.068789] 
    }
    

Serialize location data without storing it using djangorestframework-gis

Set up

Follow the instructions here[1].

Serialize location

If you do not want to store location data in a model and only want to serialize it, use this code snippet. It returns a json like the one you got using postgis.

from rest_framework_gis.serializers import GeometryField 

point = GeometryField()

[1] Reference: Making spatial querys in django using Postgres and PostGIS