Geo๐ŸŒŽDjango์™€ ํ•จ๊ป˜ ์ผํ•˜๊ธฐ

9665 ๋‹จ์–ด leafletdjangogeodjangopostgis
GeoDjango์€ Django์— contrib ๋ชจ๋“ˆ๋กœ ํฌํ•จ๋œ ๋‚ด์žฅ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ž…๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ Django์™€ ๋ณ„๋„๋กœ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋Š” ์™„์ „ํ•œ ํ”„๋ ˆ์ž„์›Œํฌ ๊ทธ ์ž์ฒด์ž…๋‹ˆ๋‹ค. GIS ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์ถ•์„ ์œ„ํ•œ ์œ ํ‹ธ๋ฆฌํ‹ฐ ๋„๊ตฌ ์ƒ์ž๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์ด ํŠœํ† ๋ฆฌ์–ผ์˜ ๋ชฉ์ 



๋ณ„๊ฑฐ ์•„๋‹Œ๋ฐ, ์—ฌ๊ธฐ์—์„œ๋Š” ๊ตญ๊ฐ€์™€ ๋„์‹œ ์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์—ฌ ์œ„์น˜ ์ขŒํ‘œ(๊ฒฝ๋„, ์œ„๋„)๋ฅผ ์–ป์Šต๋‹ˆ๋‹ค.

์š”๊ตฌ ์‚ฌํ•ญ:


  • Postgres

  • postgis ํ™•์žฅ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
    postgres์— ์„ค์น˜๋จ

  • QGIS ์ง€์˜ค์žฅ๊ณ 
    ์ข…์†์„ฑ ๋˜๋Š” ์ง€๋ฆฌ ๊ณต๊ฐ„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜GEOS, GDAL, and PROJ.4 ,

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



    ํ”„๋กœ์ ํŠธ ์„ค์ •:




    
    $ 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
    
    

    ๊ตฌ์„ฑ/settings.py

    #config/settings.py
    
    INSTALLED_APPS = [
        "django.contrib.gis",
        "location",
        "rest_framework",
    ]
    
    

    ๊ตฌ์„ฑ/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")),
    ]
    
    

    ์œ„์น˜ ์•ฑ์— url.py ํŒŒ์ผ ํฌํ•จ

    #location/urls.py
    
    from django.urls import path
    
    urlpatterns = []
    

    So we finished basic setups



    ๋ชจ๋ธ 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}"
    
    

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



    ์šฐ๋ฆฌ ๋ชจ๋ธ์— ๋Œ€ํ•œ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ์™€ ๋ทฐ๋ฅผ ๋งŒ๋“ค์–ด ๋ด…์‹œ๋‹ค.

    # 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}}    
    

    ์œ„์น˜/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)
    
    

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



    ๊ทธ๋ž˜์„œ geopy ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๋Š” ์—ฌ๋Ÿฌ ์ง€์˜ค์ฝ”๋”ฉ ์›น ์„œ๋น„์Šค๋ฅผ ์œ„ํ•œ Python ํด๋ผ์ด์–ธํŠธ์ž…๋‹ˆ๋‹ค. geopy๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Python ๊ฐœ๋ฐœ์ž๊ฐ€ ํƒ€์‚ฌ ์ง€์˜ค์ฝ”๋” ๋ฐ ๊ธฐํƒ€ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ „ ์„ธ๊ณ„์˜ ์ฃผ์†Œ, ๋„์‹œ, ๊ตญ๊ฐ€ ๋ฐ ๋žœ๋“œ๋งˆํฌ์˜ ์ขŒํ‘œ๋ฅผ ์‰ฝ๊ฒŒ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    URL์„ ์—…๋ฐ์ดํŠธํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

    #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(),
        ),
    ]
    

    ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ƒ์„ฑ:

    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;
    

    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",
    }
    
    

    ๊ด€๋ฆฌ์ž์— ๋ชจ๋ธ ๋“ฑ๋ก

    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")
    

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



    ์•ฑ์„ ์‹คํ–‰ํ•ด ๋ด…์‹œ๋‹ค:

    python manage.py makemigrations
    python manage.py migrate
    python manage.py createsuperuser
    python manage.py runserver
    

    ์—ฌ๊ธฐ, ๊ด€๋ฆฌ์ž ํŒจ๋„์—์„œ ์–ป๋Š” ๊ฒƒ.


    ํƒ์ƒ‰ ๊ฐ€๋Šฅํ•œ API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ผ๋ถ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์šฐํŽธ ๋ฐฐ๋‹ฌ๋ถ€๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    Try it out yourself for udate and delete





    github repo์—์„œ ์ฝ”๋“œ๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


    ๋ฆฌ์•ผ๋งˆ / dj_gis


    django๋ฅผ ์‚ฌ์šฉํ•˜๋Š” GIS์˜ ์˜ˆ

    ์ข‹์€ ์›นํŽ˜์ด์ง€ ์ฆ๊ฒจ์ฐพ๊ธฐ