Working with Geo🌎Django

lyamaa

Lyamaa

Posted on June 5, 2021

Working with Geo🌎Django

GeoDjango is a built-in application that is included as a contrib module in Django. It’s actually a complete framework itself that can also be used separately from Django. It provides a toolbox of utilities for building GIS web applications.

Purpose of this tutorial

Nothing much fancy, here we are getting co-ordinates(Longitude, Latitude) of location by inputting names of country and city.

Requirements :

Note: above requirements need to be install on your machine seperately before continuing

Project setup:


$ mkdir dj_gis && cd dj_gis
$ python3 -m venv env
$ source env/bin/activate
$ pip install django djangorestframework django-leaflet geopy psycopg2-binary 
$ django-admin.py startproject config
$ python manage.py startapp location

Enter fullscreen mode Exit fullscreen mode

config/settings.py

#config/settings.py

INSTALLED_APPS = [
    "django.contrib.gis",
    "location",
    "rest_framework",
]

Enter fullscreen mode Exit fullscreen mode

config/urls.py

#config/urls.py

from django.contrib import admin
from django.urls import path
from django.urls.conf import include


urlpatterns = [
    path("admin/", admin.site.urls),
    path("api/v1/", include("location.urls")),
]

Enter fullscreen mode Exit fullscreen mode

include url.py file on location app

#location/urls.py

from django.urls import path

urlpatterns = []
Enter fullscreen mode Exit fullscreen mode

So we finished basic setups

Let's create a model location/models.py

from django.db import models
from django.contrib.gis.db import models  # GeoDjango Model
from django.utils.translation import gettext_lazy as _


class Hotel(models.Model):
    name = models.CharField(max_length=255)
    street_1 = models.CharField(max_length=200)
    street_2 = models.CharField(max_length=200, null=True, blank=True)
    city = models.CharField(max_length=100)
    state = models.CharField(max_length=100)
    zip_code = models.CharField(max_length=100)
    country = models.CharField(max_length=50)
    location = models.PointField(null=True)  # Spatial Field
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = _("Hotel")
        verbose_name_plural = _("Hotels")

    def __str__(self):
        return f"{self.street_1}, {self.city}, {self.state}, {self.country}"

Enter fullscreen mode Exit fullscreen mode

here, we include a pointfield spatial field of geo django model api.

let's create a serializer and views for our model.

# location/serializers.py

from location.models import Hotel
from rest_framework import serializers


class HotelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Hotel

        fields = (
            "id",
            "name",
            "street_1",
            "street_2",
            "city",
            "state",
            "zip_code",
            "country",
            "location",
        )

        extra_kwargs = {"location": {"read_only": True}}    
Enter fullscreen mode Exit fullscreen mode

location/views.py

from location.serializers import HotelSerializer
from django.shortcuts import render
from rest_framework import generics
from .models import Hotel

from django.contrib.gis.geos import Point
from geopy.geocoders import Nominatim

geolocator = Nominatim(user_agent="location")


class ListCreateGenericViews(generics.ListCreateAPIView):
    queryset = Hotel.objects.all()
    serializer_class = HotelSerializer

    def perform_create(self, serializer):
        street_1 = serializer.initial_data["street_1"]
        address = serializer.initial_data["city"]
        state = serializer.initial_data["state"]
        country = serializer.initial_data["city"]
        data = [street_1, address, state, country]
        " ".join(data)

        g = geolocator.geocode(data)
        lat = g.latitude
        lng = g.longitude
        pnt = Point(lng, lat)
        print(pnt)
        serializer.save(location=pnt)


class HotelUpdateRetreiveView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Hotel.objects.all()
    serializer_class = HotelSerializer

    def perform_update(self, serializer):
        street_1 = serializer.initial_data["street_1"]
        address = serializer.initial_data["city"]
        state = serializer.initial_data["state"]
        country = serializer.initial_data["city"]
        data = [street_1, address, state, country]
        " ".join(data)

        g = geolocator.geocode(data)
        lat = g.latitude
        lng = g.longitude
        pnt = Point(lng, lat)
        print(pnt)
        serializer.save(location=pnt)

Enter fullscreen mode Exit fullscreen mode

Note: above view can be further refactor using viewsets or your desired ones.

so, geopy library comes in handy, which is is a Python client for several popular geocoding web services. geopy makes it easy for Python developers to locate the coordinates of addresses, cities, countries, and landmarks across the globe using third-party geocoders and other data sources.

let's update our urls:

#location/urls.py

from django.urls import path
from .views import HotelUpdateRetreiveView, ListCreateGenericViews

urlpatterns = [
    path("hotels", ListCreateGenericViews.as_view()),
    path(
        "hotels/<str:pk>",
        HotelUpdateRetreiveView.as_view(),
    ),
]
Enter fullscreen mode Exit fullscreen mode

Creating a Database:

sudo -u postgres psql

CREATE DATABASE locator;

CREATE USER locator WITH PASSWORD 'locator';

CREATE EXTENSION postgis;

ALTER ROLE locator SET client_encoding TO 'utf8';

ALTER ROLE locator SET default_transaction_isolation TO 'read committed';

ALTER ROLE locator SET timezone TO 'UTC';

ALTER ROLE locator SUPERUSER;

GRANT ALL PRIVILEGES ON DATABASE locator TO locator;
Enter fullscreen mode Exit fullscreen mode

let's make some changes on our settings.py

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "django.contrib.gis",
    "location",
    "rest_framework",
    "leaflet",
]

DATABASES = {
    "default": {
        "ENGINE": "django.contrib.gis.db.backends.postgis",
        "NAME": "locator",
        "USER": "locator",
        "PASSWORD": "locator",
        "HOST": "localhost",
        "PORT": "5432",
    }
}

LEAFLET_CONFIG = {
    # "SPATIAL_EXTENT": (5.0, 44.0, 7.5, 46),
    "DEFAULT_CENTER": (13.3888599 52.5170365), #set your corordinate
    "DEFAULT_ZOOM": 16,
    "MIN_ZOOM": 3,
    "MAX_ZOOM": 20,
    "DEFAULT_PRECISION": 6,
    "SCALE": "both",
    "ATTRIBUTION_PREFIX": "powered by me",
}

Enter fullscreen mode Exit fullscreen mode

Registering model on admin

from django.contrib import admin
from leaflet.admin import LeafletGeoAdmin

from .models import Hotel


@admin.register(Hotel)
class HotelAdmin(LeafletGeoAdmin):
    list_display = ("id", "name", "location", "created_at", "updated_at")
Enter fullscreen mode Exit fullscreen mode

so, we have added leaflet, leaflet_config and database.
For more about Leaflet you can visit Read the docs

let's run our app:

python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
python manage.py runserver
Enter fullscreen mode Exit fullscreen mode

here, what you get on admin panel.
admin

Let's add some data using browsable api or you can use postman too.

Try it out yourself for udate and delete

browsable api

You can find the code in github repo.

GitHub logo lyamaa / dj_gis

Examples on GIS using django

💖 💪 🙅 🚩
lyamaa
Lyamaa

Posted on June 5, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Working with Geo🌎Django
django Working with Geo🌎Django

June 5, 2021