Django GIS
Introduction to GeoDjango
GeoDjango is a powerful extension to Django that turns it into a world-class geographic web framework. It allows you to work with spatial data - data that represents objects in geographic space - just as easily as you work with traditional data types in Django.
Geographic Information Systems (GIS) enable developers to create applications that can:
- Store and retrieve geographic information
- Display interactive maps
- Perform spatial queries (like "find all stores within 5 miles of a user's location")
- Analyze spatial data (distance calculations, area measurements, etc.)
Whether you're building a location-based service, a mapping application, or any project that deals with geographic data, GeoDjango provides the tools you need.
Prerequisites
Before diving into GeoDjango, you should have:
- Basic understanding of Django models, views, and templates
- Familiarity with Python
- A working Django development environment
- Spatial database (PostgreSQL with PostGIS is recommended)
Setting Up GeoDjango
1. Install Required Dependencies
GeoDjango relies on several spatial libraries. For Ubuntu/Debian systems:
sudo apt-get install binutils libproj-dev gdal-bin python3-gdal
For macOS using Homebrew:
brew install postgresql
brew install postgis
brew install gdal
brew install libgeoip
2. Configure Database
GeoDjango requires a spatial database backend. PostgreSQL with PostGIS extension is highly recommended:
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.contrib.gis.db.backends.postgis',
'NAME': 'geodjango',
'USER': 'geo_user',
'PASSWORD': 'geo_password',
'HOST': 'localhost',
'PORT': '5432',
}
}
3. Add GeoDjango to Installed Apps
# settings.py
INSTALLED_APPS = [
# ... other apps
'django.contrib.gis',
# your project apps
]
Creating Spatial Models
GeoDjango provides spatial field types for representing geographic data:
from django.contrib.gis.db import models
class City(models.Model):
name = models.CharField(max_length=100)
population = models.IntegerField()
# Here's where the magic happens - a spatial field
location = models.PointField()
def __str__(self):
return self.name
Common GeoDjango field types include:
PointField
: A single point (longitude, latitude)LineStringField
: A sequence of points forming a linePolygonField
: A closed sequence of points forming an areaMultiPointField
: A collection of pointsMultiLineStringField
: A collection of line stringsMultiPolygonField
: A collection of polygonsGeometryCollectionField
: A collection of geometry objects
Working with Spatial Data
Creating and Saving Spatial Objects
You can create spatial objects using Well-Known Text (WKT) format:
from django.contrib.gis.geos import Point
from myapp.models import City
# Create a point representing New York City
point = Point(-74.0060, 40.7128) # (longitude, latitude)
# Create and save a new City object
nyc = City(name="New York", population=8800000, location=point)
nyc.save()
Spatial Queries
GeoDjango provides powerful spatial query methods:
from django.contrib.gis.geos import Point
from django.contrib.gis.measure import D
from myapp.models import City
# Find all cities within 50 miles of a point
user_location = Point(-73.9857, 40.7484) # Times Square
nearby_cities = City.objects.filter(
location__distance_lte=(user_location, D(mi=50))
)
# Order cities by distance from a point
cities_by_distance = City.objects.distance(user_location).order_by('distance')
# Find cities within a bounding box
cities_in_area = City.objects.filter(
location__within=some_polygon
)
Common spatial lookups include:
contains
: checks if geometry contains anotherwithin
: checks if geometry is within anotherintersects
: checks if geometries intersectdistance_lt
,distance_gt
: distance comparisonsdwithin
: checks if within a specified distance
Displaying Maps with GeoDjango
Let's create a simple map view to display our cities:
1. Create a view
from django.views.generic import TemplateView
from .models import City
class CityMapView(TemplateView):
template_name = 'map.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['cities'] = City.objects.all()
return context
2. Create a template with map using Leaflet.js
{% extends "base.html" %}
{% load static %}
{% block content %}
<div id="map" style="width: 800px; height: 600px;"></div>
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
<script>
// Initialize map
var map = L.map('map').setView([40, -95], 4);
// Add base map layer
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
// Add markers for cities
{% for city in cities %}
L.marker([{{ city.location.y }}, {{ city.location.x }}])
.addTo(map)
.bindPopup('{{ city.name }} - Population: {{ city.population }}');
{% endfor %}
</script>
{% endblock %}
Advanced: GeoDjango and REST APIs
To build a GIS web application, you'll often need to serve spatial data via an API. Django REST Framework with djangorestframework-gis
makes this easy:
1. Install required packages
pip install djangorestframework djangorestframework-gis
2. Create serializers for your spatial models
from rest_framework import serializers
from rest_framework_gis.serializers import GeoFeatureModelSerializer
from .models import City
class CitySerializer(GeoFeatureModelSerializer):
class Meta:
model = City
geo_field = 'location'
fields = ('id', 'name', 'population', 'location')
3. Create API views
from rest_framework import viewsets
from .models import City
from .serializers import CitySerializer
class CityViewSet(viewsets.ReadOnlyModelViewSet):
queryset = City.objects.all()
serializer_class = CitySerializer
4. Configure URLs
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import CityViewSet
router = DefaultRouter()
router.register(r'cities', CityViewSet)
urlpatterns = [
path('api/', include(router.urls)),
# other URL patterns
]
This API will serve your city data in GeoJSON format, which can be directly consumed by most mapping libraries like Leaflet or Mapbox.
Real-World Example: Store Locator Application
Let's build a simple store locator application using GeoDjango:
Models
from django.contrib.gis.db import models
class StoreCategory(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Store(models.Model):
name = models.CharField(max_length=100)
address = models.CharField(max_length=255)
location = models.PointField()
category = models.ForeignKey(StoreCategory, on_delete=models.CASCADE)
opening_hours = models.CharField(max_length=100, blank=True, null=True)
website = models.URLField(blank=True, null=True)
phone = models.CharField(max_length=20, blank=True, null=True)
def __str__(self):
return self.name
View for finding nearby stores
from django.contrib.gis.geos import Point
from django.contrib.gis.db.models.functions import Distance
from django.shortcuts import render
from .models import Store
def find_nearby_stores(request):
# Get parameters from request
latitude = request.GET.get('lat', None)
longitude = request.GET.get('lng', None)
radius = request.GET.get('radius', 5) # default 5km
category_id = request.GET.get('category', None)
if latitude and longitude:
user_location = Point(float(longitude), float(latitude), srid=4326)
# Base queryset
queryset = Store.objects.all()
# Filter by category if provided
if category_id:
queryset = queryset.filter(category_id=category_id)
# Annotate with distance and filter by radius
stores = queryset.annotate(
distance=Distance('location', user_location)
).filter(
distance__lte=radius * 1000 # Convert km to meters
).order_by('distance')
return render(request, 'stores/nearby.html', {
'stores': stores,
'latitude': latitude,
'longitude': longitude,
'radius': radius,
})
else:
# Handle case where coordinates aren't provided
return render(request, 'stores/search.html')
Template showing stores on a map
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-4">
<h2>Nearby Stores</h2>
<ul class="store-list">
{% for store in stores %}
<li class="store-item" data-id="{{ store.id }}">
<h3>{{ store.name }}</h3>
<p>{{ store.address }}</p>
<p><strong>Distance:</strong> {{ store.distance.km|floatformat:2 }} km</p>
{% if store.phone %}
<p><strong>Phone:</strong> {{ store.phone }}</p>
{% endif %}
</li>
{% empty %}
<li>No stores found in this area.</li>
{% endfor %}
</ul>
</div>
<div class="col-md-8">
<div id="map" style="width: 100%; height: 600px;"></div>
</div>
</div>
</div>
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
<script>
// Initialize map centered on user location
var map = L.map('map').setView([{{ latitude }}, {{ longitude }}], 12);
// Add OpenStreetMap layer
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
// Add user location marker
var userIcon = L.icon({
iconUrl: '{% static "img/user-marker.png" %}',
iconSize: [32, 32],
iconAnchor: [16, 32],
popupAnchor: [0, -32]
});
L.marker([{{ latitude }}, {{ longitude }}], {icon: userIcon})
.addTo(map)
.bindPopup('Your Location')
.openPopup();
// Add store markers
{% for store in stores %}
L.marker([{{ store.location.y }}, {{ store.location.x }}])
.addTo(map)
.bindPopup(
'<strong>{{ store.name }}</strong><br>' +
'{{ store.address }}<br>' +
'Distance: {{ store.distance.km|floatformat:2 }} km'
);
{% endfor %}
// Draw radius circle
L.circle([{{ latitude }}, {{ longitude }}], {
radius: {{ radius }} * 1000,
fillColor: '#0078D7',
fillOpacity: 0.1,
color: '#0078D7',
weight: 1
}).addTo(map);
</script>
{% endblock %}
Summary
GeoDjango transforms Django into a powerful framework for building geospatial web applications. In this tutorial, we've covered:
- Setting up GeoDjango with PostGIS
- Creating spatial models with geometry fields
- Performing spatial queries
- Displaying maps using Leaflet.js
- Building a RESTful API for spatial data
- Creating a real-world store locator application
With GeoDjango, you can build sophisticated location-based applications including:
- Store locators
- Real estate mapping applications
- Asset tracking systems
- Delivery and logistics platforms
- Social applications with location features
- And much more!
Additional Resources
- Official GeoDjango Documentation
- PostGIS Documentation
- Leaflet.js Documentation
- djangorestframework-gis Documentation
Exercises
- Create a model for national parks that includes their boundaries as polygon data.
- Implement a "find parks within X miles" feature using spatial queries.
- Add a feature to your store locator to filter stores by type.
- Extend the store locator to calculate and show directions from the user's location to a selected store.
- Create a heatmap visualization showing the density of stores across a region.
GeoDjango opens up a world of possibilities for spatial data applications. With the foundation provided in this tutorial, you're ready to start building your own location-based services.
If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)