Mapswith GeoDjango
PostGISand Leaflet
Paolo Melchiorre@pauloxnet
paulox.net20tab.com
paulox.net20tab.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
paulox.net20tab.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
paulox.net20tab.com
Goal
4
Find a simple way
to integrate a web map
in a Django project.
paulox.net20tab.com
Outline
5
Basic map
GeoDjangoLeaflet JS
PostGISUse case
paulox.net20tab.com
Web map
6
● Map delivered by GIS
● Static and Dynamic
● Interactive and view only
● Raster or Vector tiles
● Spatial databases
● Javascript library
paulox.net20tab.com
GeoDjango
7
● django.contrib.gis
● Geographic framework
● Spatial Field Types
● Spatial ORM queries
● Admin geometry fields
● Four database backends
paulox.net20tab.com
PostGIS
8
● Best GeoDjango backend
● PostgreSQL extension
● Integrated spatial data
● Spatial data types
● Spatial indexing
● Spatial functions
paulox.net20tab.com
Leaflet
9
● JavaScript library for maps
● Free Software
● Desktop & Mobile friendly
● Light (> 40 KB of gizp JS)
● Well documented
● Simple, performing, usable
paulox.net20tab.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.net20tab.com
Settings
11
INSTALLED_APPS = [ # … 'django.contrib.gis',]
DATABASES = {'default': { 'ENGINE': 'django.contrib.gis.db.backends.postgis', # …}}
paulox.net20tab.com
Migrations
12
from django.contrib.postgres import operationsfrom django.db import migrations
class Migration(migrations.Migration): dependencies = [('blog', '0001_initial')] operations = [ operations.CreateExtension('postgis') ]
paulox.net20tab.com
Models
13
from django.contrib.gis.db.models import PointFieldfrom django.db import models
class Entry(models.Model): # … point = PointField()
@property def lat_lng(self): return list(getattr(self.point, 'coords', [])[::-1])
paulox.net20tab.com
Admin
14
from django.contrib import adminfrom django.contrib.gis.admin import OSMGeoAdminfrom .models import Entry
@admin.register(Entry)class EntryAdmin(OSMGeoAdmin): default_lon = 1400000 default_lat = 7495000 default_zoom = 12 # …
paulox.net20tab.com
Admin page
15
paulox.net20tab.com
Views and urls
16
from django.urls import pathfrom django.views.generic import ListViewfrom .models import Entry
class EntryList(ListView): queryset = Entry.objects.filter(point__isnull=False)
urlpatterns = [ path(map/', EntryList.as_view()),]
paulox.net20tab.com
Template
17
<html><head> <link rel="stylesheet" href="//unpkg.com/leaflet/dist/leaflet.css"/> <script src="//unpkg.com/leaflet/dist/leaflet.js"></script></head>
<body><h1>DjangoCon Europe 2019 Venues</h1> <div id="m" style="width: 1920px; height: 1080px;"></div> <!-- add javascript here --></body></html>
paulox.net20tab.com
Javascript
18
<script type="text/javascript">
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 %}
</script>
paulox.net20tab.com
Basic map page
19
paulox.net20tab.com
Mer et Demeures
20
● Coastal properties
● Active since 2014
● 8 Languages
● ~ 100k active advertisements
● ~ 40 Countries
● 6 Continents
paulox.net20tab.com
Version 1.0
21
● Django 1.6
● Python 2.7
● PostgreSQL 9.3
● Textual Spatial Fields
● Leaflet 1.0
● Static/View-only map
paulox.net20tab.com
Version 2.0
22
● Django 2.1 / GeoDjango
● Python 3.6
● PostgreSQL 10
● PostGIS 2.4 / Spatial data
● Leaflet 1.4
● Dynamic/Interactive map
paulox.net20tab.com
Models
23
from django.db import modelsfrom django.contrib.gis.db.models import ( MultiPolygonField, PointField)
class City(models.Model): borders = MultiPolygonField()
class Ad(models.Model): city = models.ForeignKey(City, on_delete=models.CASCADE) location = PointField()
paulox.net20tab.com
Setup
24
$ pip install djangorestframework # RESTful API$ pip install djangorestframework-gis # Geographic add-on$ pip install django-filter # Filtering support
INSTALLED_APPS = [ # … 'django.contrib.gis', 'rest_framework', 'rest_framework_gis', 'django_filters',]
paulox.net20tab.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',)
paulox.net20tab.com
Views
from rest_framework.viewsets import ReadOnlyModelViewSetfrom rest_framework_gis.filters import InBBoxFilterfrom .models import Adfrom .serializers import AdSerializer
class AdViewSet(ReadOnlyModelViewSet): bbox_filter_field = 'location' filter_backends = (InBBoxFilter,) queryset = Ad.objects.filter(location__isnull=False) serializer_class = AdSerializer
26
paulox.net20tab.com
Urls
from rest_framework.routers import DefaultRouterfrom .views import AdViewSet
router = DefaultRouter()router.register(r'markers', AdViewSet, basename='marker')urlpatterns = router.urls
27
paulox.net20tab.com
GeoJSON
{"type": "FeatureCollection", "features": [{ "id": 1, "type": "Feature", "geometry": { "type": "Point", "coordinates": [12.54, 55.66] }, "properties": {}}]}
28
paulox.net20tab.com
Demo
29
paulox.net20tab.com
Conclusion
30
● Out-of-the-box features
● Spatial & Relational queries
● Django/PostgreSQL
● Backend clusterization
● Administrative levels
● Dynamic spatial entity
paulox.net20tab.com
Resources
31
● docs.djangoproject.com/en/
● github.com/django/django
● postgis.net/docs/
● github.com/postgis/postgis
● leafletjs.com/reference.html
● github.com/leaflet/leaflet
paulox.net20tab.com
Acknowledgments
32
Mer et Demeuresmeretdemeures.com
> Hiring a Django developer !
paulox.net20tab.com
Info
33
● www.paulox.net/talks
● Slides
● Code samples
● Resource URLs
● Questions and comments
● License (CC BY-SA)