Python & Web Maps

A Guide to Interactive Mapping with Python and GeoDjango

What is a web map?

A web map is the representation of a geographical systemproviding spatial data and information.

The map can have different features, depending on what is needed:

  • it can be static or dynamic;
  • may be interactive or not;
  • can be designed using photographic images or vector models.

In general, in order to create one, we need a spatial database for data-storage and a Javascript library to design it on the webpage.

GeoDjango

This one is not an external package, on the contrary it has been part of Django, as a contrib package, since version 1.0: it turns Django, as we were saying, into a geographic spatial framework.

How does it work? First of all, GeoDjango provides us with spatial field types - there is quite a few of them - and allows us to perform spatial queries directly in Django’s ORM. Also, the fields are already integrated into the admin.

GeoDjango currently supports 4 geospatial databases.

PostGIS

We used PostGIS in this project: we opted for it because we were already using PostgreSQL, but also because it represents GeoDjango’s backend with the most extensive support.

This is a PostgreSQL extension which directly integrates spatial data.

Alongside with GeoDjango, PostGIS provides us with specific data types so as to store our spatial information. In addition, there are indexes optimized for spatial data, as well as specific functions that we can use to search for this kind of data.

Leaflet JS

We added Leaflet to our stack, for the frontend map, for a number of reasons.

Firstly, it is one of the most used Javascript libraries for rendering maps on a browser.

Another aspect of primary importance, both for the client and for us developers, consists in the fact that it is a Free Software, with a large community of developers participating in its development. Moreover, it is mobile friendly, which is essential in the present era, and it is also very light.

Plus, it is extremely easy to use: the documentation is accessible and the result you get has excellent performance.

A basic map example

Let's take Django’s Blog application, which can be found in the documentation, and test it with these three models.

from django.db import modelsclass 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)    headline = models.CharField(max_length=255)    authors = models.ManyToManyField(Author)

We are going to add spatial data to this application and render them in a web map. Starting from this example, the first step is to change the settings:

INSTALLED_APPS = [    # …    'django.contrib.gis',]DATABASES = {     'default' : {        'ENGINE' : 'django.contrib.gis.db.backends.postgis',        # …    } }

We should also activate the PostGIS extension with a migration:

from django.contrib.postgres import operationsfrom django.db import migrationsclass Migration (migrations.Migration) :    dependencies = [ ('blog',  '0001_initial') ]    operations = [        operations.CreateExtension ('postgis')    ]

[Need help from our Python experts to create advanced maps? Contact us.]

The other change we need to make is adding a PointField, importing it from GeoDjango. We have also added a property, which will then be useful in Leaflet, so as to render longitude and latitude.

from django.contrib.gis.db.models import PointFieldfrom django.db import modelsclass Entry (models.Model) :    # …    point = PointField()    @property    def lat_lng(self):        return list(getattr(self.point, 'coords', [])[::-1])

The easiest way to change our entries consists in changing Admin in order to use a specific one. We can also indicate distinct attributes and zoom by default.

The end result is a map that you can interact with: you can zoom in and out and move around, all using a few lines of code.

Once we have edited the entries and added points to the admin, we can show them on a real map. We use views and urls to do this:

from django.urls import pathfrom django.views.generic import ListViewfrom .models import Entryclass EntryList (ListView) :    queryset = Entry.objects.filter(point__isnull=False)urlpatterns = [    path ('map/', EntryList.as_view()),]

After defining the EntryList, we map it to a url: and here is where the backend work ends. The following step concerns writing the template.

We start by including JS Leaflet’s style and the JavaScript library in the head. Then we write a div which will allow us to render the map in the html part:

<script type="text/javascript">  var m = L.map('m').setView([43.77, 11.26], 15); // Florence  L.tyleLayer ('//{s}.tile.osm.org/{z}/{x}/{y}.png').addTo(m);  {% for e in object_list %}    L.marker({{e.lat_lng}}).addTo(m);  {% endfor %}</script>

As to what concerns the Javascript part: L stands for Leaflet, we render the map in our Div and set the map view, latitude, longitude and zoom. We also choose the tile which needs to be inserted: we use Open Street Map for the graphic part in this case, otherwise we would not be able to see anything.

We then insert a loop with the list of markers and we ask Leaflet to print one with latitude and longitude for each marker.

This code is actually working: you can try using it too, by following all the steps!

WARNING: PostGIS first asks for longitude and then latitude, while Leaflet, on the contrary, first asks for latitude and then longitude. That is why we have created a property.

Paolo Melchiorre

CTO & Developer @ 20tab

Need help from our Python experts to create advanced maps?

Get in touch today to discuss your requirements and explore how we can assist you!

WHO WE ARE

We are a digital product management and development company.

Our mission is to create products that have a tangible positive impact on users, businesses and communities, while minimizing the resources used.


We are working on improving the UX of the djangoproject.com website.

Read more

© 20tab srl | VAT Nr. 11955811002 | Privacy Policy | Cookie Policy