Using GeoDjango to Filter by Points
Just recently I found myself playing with GeoDjango , I’ve been using it on both a Ubuntu 14.04 cloud server and a Macbook Pro (OS X El Capitan).
GeoDjango allows us to query by geographic points directly on the data model. We are then able to extend the model, and add a custom method to search by zipcode.
Using the Django shell we can easily check data in our favorite interpreter :
$ ./manage.py shell
In [1]: from hub.models import Vendor
In [2]: Vendor.get_vendors(zipcode='78664', miles=5)
Out[2]: [<Vendor: Starbucks>]
In [3]: Vendor.get_vendors(zipcode='78664', miles=10)
Out[3]: [<Vendor: Starbucks>, <Vendor: Starbucks>,
<Vendor: Starbucks>, <Vendor: Starbucks>,
<Vendor: Starbucks>, <Vendor: Starbucks>, <Vendor: Starbucks>]
It’s then pretty easy to take that data and present it on a Google Map ( using the Django application’s views and templates ):
If you find any of this exciting; read on, I’m going to go over setting the environment up from scratch ( using a Macbook as the development environment).
Prerequisites
Setup
It is a good idea to add Postgres.app ’s bin path to your $PATH .
You should run the following command (changing the version to match your install), and add it to the bottom of your ~/.bash_profile :
export PATH=$PATH:/Applications/Postgres.app/Contents/Versions/9.4/bin
Next lets create our PostgreSQL database, and enable the GIS extension.
Start the Postgres.app OSX application. Next click the elephant from your upper task bar, and select Open psql .
nessy=# create database geoapp;
CREATE DATABASE
nessy=# \c geoapp
You are now connected to database "geoapp" as user "nessy".
geoapp=# CREATE EXTENSION postgis;
CREATE EXTENSION
You can now close the psql shell.
Next lets install Django into a virtualenv
# create and change to new app directory
mkdir ~/geoapp && cd ~/geoapp/
# create a fresh virtual environment
virtualenv env
# activate the virtual environment
source env/bin/activate
# install Django inside the virtual environment
pip install Django
To use PostgreSQL with Python we will need the adapter installed, be sure you added Postgres.app’s bin path to your $PATH:
pip install psycopg2
GeoDjango requires the geos server to be available, we can install this with homebrew :
brew install geos
We are now ready to create the Django project and application.
# create a new project using Django admin tool
django-admin startproject geoproject
# change to the newly created project directory
cd geoproject/
# create a new application
./manage.py startapp hub
Now you need to configure your Django application to use PostgreSQL and GIS, open geoproject/settings.py with your favorite text editor.
vim geoproject/settings.py
Append django.contrib.gis and hub to your INSTALLED_APPS:
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.gis',
'hub',
)
Next find the DATABASES portion and set it to the postgis engine:
DATABASES = {
'default': {
'ENGINE': 'django.contrib.gis.db.backends.postgis',
'NAME': 'geoapp',
'PASSWORD': '',
'HOST': 'localhost',
'PORT': ''
}
}
The next step will be to create our model using GIS points, add the following to hub/models.py :
from django.contrib.gis.db import models
from django.contrib.gis.geos import Point, fromstr
from django.contrib.gis.measure import D
class Vendor(models.Model):
def __unicode__(self):
return unicode(self.name)
def save(self, *args, **kwargs):
if self.latitude and self.longitude:
self.location = Point(float(self.longitude), float(self.latitude))
super(Vendor, self).save(*args, **kwargs)
name = models.CharField(max_length=100)
longitude = models.FloatField()
latitude = models.FloatField()
location = models.PointField(blank=True, null=True)
You will also want to add this model to the admin page, so update hub/admin.py :
from django.contrib import admin
from hub.models import Vendor
class VendorAdmin(admin.ModelAdmin):
list_display = ('name', 'longitude', 'latitude')
exclude = ('location',)
admin.site.register(Vendor, VendorAdmin)
At this point you are ready to create the database tables, use the provided manage.py script:
./manage.py syncdb
I’m going to now jump into the Django shell to add data, but this can also be done using the admin ( http://127.0.0.1:8000/admin ):
./manage.py shell
In [1]: from hub.models import Vendor
In [2]: Vendor.objects.create(longitude=-97.677580, latitude=30.483176,
...: name='Starbucks')
Out[2]: <Vendor: Starbucks>
In [3]: Vendor.objects.create(longitude=-97.709085, latitude=30.518423,
...: name='Starbucks')
Out[3]: <Vendor: Starbucks>
In [4]: Vendor.objects.create(longitude=-97.658976, latitude=30.481517,
...: name='Starbucks')
Out[4]: <Vendor: Starbucks>
In [5]: Vendor.objects.create(longitude=-97.654141, latitude=30.494810,
...: name='Starbucks')
Out[5]: <Vendor: Starbucks>
I can then define a point in the center of the city, and filter by locations within a 5 mile radius:
In [6]: from django.contrib.gis.geos import fromstr
In [7]: from django.contrib.gis.measure import D
In [8]: point = fromstr('POINT(-97.6786111 30.5080556)')
In [9]: Vendor.objects.filter(location__distance_lte=(point, D(mi=5)))
Out[9]: [<Vendor: Starbucks>, <Vendor: Starbucks>, <Vendor: Starbucks>,
<Vendor: Starbucks>]
Hope you found this article helpful; if you did, please share with friends and coworkers.