Maps
with GeoDjango
PostGIS
and Leaflet
Paolo Melchiorre
@pauloxnet
paulox.net
20tab.com
Slide 2
Slide 2 text
paulox.net
20tab.com
Paolo Melchiorre @pauloxnet
2
● Computer Science Engineer
● Python Developer since 2006
● PostgreSQL user (not a DBA)
● Django Developer since 2011
● Remote Worker since 2015
● Senior Developer at 20tab
Slide 3
Slide 3 text
paulox.net
20tab.com
www.20tab.com
3
● Rome based with remote workers
● Meetup and conferences
● Agile and Lean methodologies
● Growth marketing
● Software development
● Python, Django, React JS, uWSGI
Slide 4
Slide 4 text
paulox.net
20tab.com
Goal
4
Find a simple way
to integrate a web map
in a Django project.
Slide 5
Slide 5 text
paulox.net
20tab.com
Outline
5
Basic map
GeoDjango
Leaflet JS
PostGIS
Use case
Slide 6
Slide 6 text
paulox.net
20tab.com
Web map
6
● Map delivered by GIS
● Static and Dynamic
● Interactive and view only
● Raster or Vector tiles
● Spatial databases
● Javascript library
paulox.net
20tab.com
PostGIS
8
● Best GeoDjango backend
● PostgreSQL extension
● Integrated spatial data
● Spatial data types
● Spatial indexing
● Spatial functions
Slide 9
Slide 9 text
paulox.net
20tab.com
Leaflet
9
● JavaScript library for maps
● Free Software
● Desktop & Mobile friendly
● Light (> 40 KB of gizp JS)
● Well documented
● Simple, performing, usable
Slide 10
Slide 10 text
paulox.net
20tab.com
Basic map example
10
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
class Author(models.Model):
name = models.CharField(max_length=200)
class Entry(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
authors = models.ManyToManyField(Author)
headline = models.CharField(max_length=255)
paulox.net
20tab.com
Migrations
12
from django.contrib.postgres import operations
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [('blog', '0001_initial')]
operations = [
operations.CreateExtension('postgis')
]
Slide 13
Slide 13 text
paulox.net
20tab.com
Models
13
from django.contrib.gis.db.models import PointField
from django.db import models
class Entry(models.Model):
# …
point = PointField()
@property
def lat_lng(self):
return list(getattr(self.point, 'coords', [])[::-1])
Slide 14
Slide 14 text
paulox.net
20tab.com
Admin
14
from django.contrib import admin
from django.contrib.gis.admin import OSMGeoAdmin
from .models import Entry
@admin.register(Entry)
class EntryAdmin(OSMGeoAdmin):
default_lon = 1400000
default_lat = 7495000
default_zoom = 12
# …
Slide 15
Slide 15 text
paulox.net
20tab.com
Admin page
15
Slide 16
Slide 16 text
paulox.net
20tab.com
Views and urls
16
from django.urls import path
from django.views.generic import ListView
from .models import Entry
class EntryList(ListView):
queryset = Entry.objects.filter(point__isnull=False)
urlpatterns = [
path(map/', EntryList.as_view()),
]
Slide 17
Slide 17 text
paulox.net
20tab.com
Template
17
DjangoCon Europe 2019 Venues
Slide 18
Slide 18 text
paulox.net
20tab.com
Javascript
18
var m = L.map('m').setView([55.67, 12.55], 13); # CPH
L.tileLayer('//{s}.tile.osm.org/{z}/{x}/{y}.png').addTo(m);
{% for e in object_list %}
L.marker({{e.lat_lng}}).addTo(m).bindPopup('{{e}}');
{% endfor %}
Slide 19
Slide 19 text
paulox.net
20tab.com
Basic map page
19
Slide 20
Slide 20 text
paulox.net
20tab.com
Mer et Demeures
20
● Coastal properties
● Active since 2014
● 8 Languages
● ~ 100k active advertisements
● ~ 40 Countries
● 6 Continents
paulox.net
20tab.com
Serializer
25
from rest_framework_gis.serializers import (
GeoFeatureModelSerializer
)
from .models import Ad
class AdSerializer(GeoFeatureModelSerializer):
class Meta:
model = Ad
geo_field = 'location'
fields = ('id',)
Slide 26
Slide 26 text
paulox.net
20tab.com
Views
from rest_framework.viewsets import ReadOnlyModelViewSet
from rest_framework_gis.filters import InBBoxFilter
from .models import Ad
from .serializers import AdSerializer
class AdViewSet(ReadOnlyModelViewSet):
bbox_filter_field = 'location'
filter_backends = (InBBoxFilter,)
queryset = Ad.objects.filter(location__isnull=False)
serializer_class = AdSerializer
26