Developing geospatial webapps with Python and Django - Tutorial
/There are many ways to display spatial data, from the traditional maps, GIS desktop software and most recently on the web. We face certain challenges to display spatial data on the web because we will require certain functionalities that we find on a desktop software and certain spatial analysis tools implemented as buttons. Django is a web framework written in Python that allows to create web applications with moderate simplicity and this framework is capable of displaying vector spatial data using libraries such as Folium.
We have created a tutorial with an applied case of displaying hydrological spatial information on a webapp in Django. The tutorial creates a Django project, creates an app for the spatial data representation, imports the vector data (geojson or shp) and displays spatial data with defined styles.
Tutorial
Input data
You can download the input data from this link.
Procedure
This is whole procedure in Windows with Anaconda
1 - Open your Anaconda prompt and install Django and folium
pip install Django
pip install folium
2 - Go to the location where you want your project to be created
3 - Create the project “geo”
4 - Move to the created directory
cd geo
5 - Create the Django app “geoApp”
python manage.py startapp geoApp
6 - Create the file “urls.py” inside geoApp
7 - Inside “geoApp/urls.py”, paste the following lines
from django.urls import path
from django.contrib.auth import views as auth_views
from . import views
urlpatterns = [
path('', views.home,name='home'),
]
8 - Now we have to register this “geoApp/urls.py” inside “geo/urls.py”, add “include” to “Django.urls”
from django.urls import path, include
add the following line to “urlspatterns”
path('',include('geoApp.urls')),
You now have something like this:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('',include('geoApp.urls')),
]
9 - Inside “geoApp”:
Create a folder called “templates”
Inside “templates” create a folder called “geoApp”
10 - Before configuring the “Html” files, let´s define the “static” files and “path”. In the “/geo/settings.py”
First, at the top of the file add
import os
search “STATIC_URL” it is located at the end of the file and replace with the following commands
STATIC_URL = '/static/'
MEDIA_URL = '/media/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
11 - Add the folders “static” and “media” in the root folder:
12 - Inside the “static” folder, paste the folders that are inside the “static” folder given, it contains a “bootstrap” folder” and a “jquery” folder
13 - Return to “settings.py”, and in “INSTALLED_APPS” add:
'geoApp' ,
14 - Continuing in the “settings.py” file, in “DIRS” inside “TEMPLATES” add
os.path.join(BASE_DIR, 'geoApp','templates')
You would have something like the following:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(BASE_DIR, 'geoApp','templates'),
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
15 - Edit “/geoApp/views.py”, here we create a home function that returns to our “home.html” template, this template is created in the next step
from django.shortcuts import render, redirect
# Create your views here.
def home(request):
context={}
return render(request,'geoApp/home.html',context)
16 - Create a new file inside the new folder called “home.html”, paste this html code inside:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>GeoGida</title>
<!-- Bootstrap core CSS -->
<link rel="stylesheet" href="{% static '/bootstrap/css/bootstrap.min.css' %}">
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark static-top">
<div class="container">
<a class="navbar-brand" href="#">GeoGida</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ml-auto">
<li class="nav-item active">
<a class="nav-link" href="">Welcome</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Page Content -->
<div class="container">
<div class="row">
<div class="col-lg-12 text-center">
<h1 class="mt-5">GeoApp developed by Hatarilabs</h1>
<p class="lead">You´ll be seeing shapefiles storaged in Database</p>
<ul class="list-unstyled">
<li>Developed by Hatarilabs</li>
<li>2020</li>
</ul>
</div>
</div>
</div>
<!-- Bootstrap core JavaScript -->
<script src="{% static '/jquery/jquery.slim.min.js' %}"></script>
<script src="{% static '/bootstrap/js/bootstrap.bundle.min.js' %}"></script>
</body>
</html>
17 - Till this point go to the kernel and run your app
python manage.py runserver
Go to localhost and you should be able to see the next interface:
http://127.0.0.1:8000/
18 - Now, we will add the geospatial view, to do this copy the “shp” folder of the given data to “media”, this contains two “.geojson” files
19 - Modify the “geoApp/views.py” file as follows:
from django.shortcuts import render, redirect
import os
import folium
# Create your views here.
def home(request):
shp_dir = os.path.join(os.getcwd(),'media','shp')
# folium
m = folium.Map(location=[-16.22,-71.59],zoom_start=10#)
## style
style_basin = {'fillColor': '#228B22', 'color': '#228B22'}
style_rivers = { 'color': 'blue'}
## adding to view
folium.GeoJson(os.path.join(shp_dir,'basin.geojson'),name='basin',style_function=lambda x:style_basin).add_to(m)
folium.GeoJson(os.path.join(shp_dir,'rivers.geojson'),name='rivers',style_function=lambda x:style_rivers).add_to(m)
folium.LayerControl().add_to(m)
## exporting
m=m._repr_html_()
context = {'my_map': m}
## rendering
return render(request,'geoApp/home.html',context)
20 - Now, add the map to the html code, open “home.html” an add after “<p class="lead">You´ll be seeing shapefiles storaged in Database</p>”, the following command
{{ my_map|safe }}
The complete “home.html” would be as follows:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>GeoGida</title>
<!-- Bootstrap core CSS -->
<link rel="stylesheet" href="{% static '/bootstrap/css/bootstrap.min.css' %}">
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark static-top">
<div class="container">
<a class="navbar-brand" href="#">GeoGida</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ml-auto">
<li class="nav-item active">
<a class="nav-link" href="">Welcome</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Page Content -->
<div class="container">
<div class="row">
<div class="col-lg-12 text-center">
<h1 class="mt-5">GeoApp developed by Hatarilabs</h1>
<p class="lead">You´ll be seeing shapefiles storaged in Database</p>
{{ my_map|safe }}
<ul class="list-unstyled">
<li>Developed by Hatarilabs</li>
<li>2020</li>
</ul>
</div>
</div>
</div>
<!-- Bootstrap core JavaScript -->
<script src="{% static '/jquery/jquery.slim.min.js' %}"></script>
<script src="{% static '/bootstrap/js/bootstrap.bundle.min.js' %}"></script>
</body>
</html>