Quantcast
Channel: Krzysztof Żuraw blog's RSS Feed
Viewing all articles
Browse latest Browse all 205

GeoDjango and Leaflet.js- part two

$
0
0

This is the second post from GeoDjango i Leaflet.js series. You can find the previous post under thislink.

After loading data to GeoDjango application now, it’s time to present it to the user. You can use django template tag like {{object}} but I think it’s better to provide api endpoints. I will be using GeoDjango builtin GeoJSON serializer. To do this declare new views in views.py:

from django.http import HttpResponse
from django.core.serializers import serialize
from.models import Point, Voivodeship

defpoints_view(request):
    points_as_geojson = serialize('geojson', Point.objects.all())return HttpResponse(points_as_geojson, content_type='json')defwojewodztwa_view(request):
    voivodeships_as_geojson = serialize('geojson', Voivodeship.objects.all())return HttpResponse(voivodeships_as_geojson, content_type='json')

Also add following setting into settings.py:

SERIALIZATION_MODULES ={"geojson":"django.contrib.gis.serializers.geojson",}

GeoJSON is open format for encoding geographical data. It’s based on JSON.

Then add lines to urls.py:

from django.conf.urls import include, url
from django.contrib import admin
from voivodeships.views import points_view, voivodeships_view

urlpatterns =[
    url(r'^admin/', include(admin.site.urls)),
    url(r'^points.data/', points_view, name='points'),
    url(r'^voivodeships.data/', voivodeships_view, name='voivodeships'),]

As you can see GeoDjango displays data from database in GeoJSON:

GeoJSON from GeoDjango

It’s nice but end user need to see results on the map not in JSON format so I use Leaflet.js.

You can download leaflet.js from the web page but there is a better way: django-leaflet. It’s django application with allows you embed leaflet to django project. Install it by:

$ pip install django-leaflet

Then make sure that leaflet is added to INSTALLED_APPS in settings.py:

INSTALLED_APPS =(# other applications
  leaflet
)

Let’s add main page view to GeoDjango application in views.py:

from django.views.generic import TemplateView

classMainPageView(TemplateView):
    template_name ='voivodeships/index.html'

And to urls.py:

from voivodeships.views import MainPageView

urlpatterns =[# rest of urls
               url(r'^$', MainPageView.as_view()),]

After this add new index.html under voivodeships/templates/voivodeships/index.html with this content:

<html>
{% load leaflet_tags %}
    <head>
        {% leaflet_js %}
        {% leaflet_css %}
    </head><body>
        {% leaflet_map "poland" %}
    </body></html>

And going to the web page with running GeoDjango application you can see map:

Basic Leaflet.js map

Thanks to django-leaflet you can control behavior of all maps. Let add the following content to end of settings.py:

LEAFLET_CONFIG ={'DEFAULT_CENTER':(52.00,20.00),'DEFAULT_ZOOM':6,'MIN_ZOOM':1,'MAX_ZOOM':20,}

But still map is not taking full space in the web page so let’s add more CSS lines to fix that in index.html:

<head><stylemedia="screen">#poland{width:100%;height:100% }</style><!-- Rest of html -->

One of the Leaflet.js strong points is huge extensions database. In this project I will use few of them including: leaflet-ajax, leaflet-spin, markercluster. It’s up to you how you want to install it. I will use bower for that:

$ bower install leaflet-ajax leaflet-spin leaflet.markerculster

Add STATICFILES_DIRS to settings.py:

STATICFILES_DIRS =(
    os.path.join(BASE_DIR,'static'),)

After installation got to index.html and use these plugins:

{% load static %}
   <head><!-- style tag and django-leaflet tag here --><scriptsrc="{% static 'leaflet-ajax/dist/leaflet.ajax.min.js' %}"></script><scriptsrc="{% static 'spin.js/spin.min.js' %}"></script><scriptsrc="{% static 'leaflet-spin/leaflet.spin.js' %}"></script></head><body><scripttype="text/javascript">functionmap_init_basic(map, options){var geojsonPointLayer =newL.GeoJSON.AJAX("{% url 'points' %}",{onEachFeature:function(feature, layer){
                        layer.bindPopup(feature.properties.name.toString());}});
               geojsonPointLayer.addTo(map);var geojsonVoivodeshipsLayer =newL.GeoJSON.AJAX("{% url 'voivodeships' %}",{onEachFeature:function(feature, layer){
                       layer.bindPopup(feature.properties.jpt_nazwa_field.toString());}});
               geojsonVoivodeshipsLayer.addTo(map);}</script>
      {% leaflet_map "poland" callback="window.map_init_basic" %}
   </body>

I added new function map_init_basic which is a callback for django-leaflet tag. Then thanks to leaflet-ajax I get points and voivodeships GeoJSONs from GeoDjango. Moreover, I use function from leaflet.js: onEachFeature. This function add popup with the name of point or voivodeship.

There is one problem. GeoJSON with voivodeship is so accurate that deserializing takes a lot of time (about 41 sec). So one of the solution is to dump GeoJSON to cache, I will use Redis as a cache database.

First, install and check if Redis is working by:

$ sudoapt-getinstall redis-server
$ redis-cli ping PONG

Then it’s time to install python bindings:

$ pip install redis
$ pip install django-redis-cache

After this adjust some settings in settings.py:

MIDDLEWARE_CLASSES =['django.middleware.cache.UpdateCacheMiddleware',# ... another middlewares'django.middleware.common.CommonMiddleware',# ... rest of middlewares'django.middleware.cache.FetchFromCacheMiddleware',]

CACHES ={'default':{'BACKEND':'redis_cache.RedisCache','LOCATION':'127.0.0.1:6379',},}

What is important in MIDDLEWARE_CLASSES is order: UpdateCacheMiddleware should go before CommonMiddleware and FetchFromCacheMiddleware is supposed to be last.

Lastly, add cache to voivodeships_view in views.py:

from django.core.cache import cache

defvoivodeships_view(request):
    redis_key ='voivodeships'
    voivodeships = cache.get(redis_key)# getting value for given key from redisifnot voivodeships:
       voivodeships = serialize('geojson', Voivodeship.objects.all())
       cache.set(redis_key, voivodeships)# if not GeoJSON is not in cache set itreturn HttpResponse(voivodeships, content_type='json')

Right now GeoJSON will be loaded from the database. After reloading the web page, django will get results from cache.

That’s all: you have working GeoDjango application. The github repo is under this link

  • Update 28.06.18:

If you want your views to work with Django 2.0 you can use this snippet:

import json
from django.http import JsonResponse
from django.core.serializers import serialize
from.models import Point

defpoints_view(request):
    points_as_geojson = serialize('geojson',Point.objects.all())return JsonResponse(json.loads(points_as_geojson))

Thanks to Phyo Min Htwe for providing this piece of code!


Viewing all articles
Browse latest Browse all 205

Trending Articles