Search code examples
pythondjangogispolygongeodjango

How to translate a polygon in GeoDjango?


I have a Place model which has a polygon field:

from django.contrib.gis.db import models

class Place(models.Model):
    area = models.PolygonField()

I want to translate the area field of an instance of this Place model.

Here is how I am doing it right now:

from django.contrib.gis.db.models.functions import Translate

place = Place.objects.get(pk=1)
place.area = Place.objects.filter(pk=place.pk).annotate(new_area=Translate('area', 0.001, 0.001)).first().new_area
place.save()

This seems very hacky. I think that there should be a way of doing this in the following way:

place = Place.objects.get(pk=1)
place.area = Translate(place.area, 0.001, 0.001)
place.save()

But this throws an exception.

Should I be using some library? Which one works well with GeoDjango and its models/fields?

What is the better way of doing it?


Solution

  • Note the "db" in django.contrib.gis.db.models.functions. There aren't Python functions, these are classes (thus the initial capital in the names) that allow you to access the database system's spatial functions (usually provided by a spatial database extension like spatialite of SQLite or PostGIS for PostgreSQL). See the GeoDjango documentation

    The functions documented on this page allow users to access geographic database functions to be used in annotations, aggregations, or filters in Django.

    (Emphasis mine.)

    That is, these can only be used in queries, as the spatial calculation work of offloaded to the database.

    So what can you do instead of doing several queries?

    update query

    If you'd do this directly in SQL, you'd probably write an UPDATE query. The Django ORM allows you to do the same from Django, with the update() method.

    It'd probably look something like this (untested):

    Place.objects.filter(pk=1).update(area=Translate('area', 0.001, 0.001))
    

    If you're using GeoDjango anyway, this is probably the preferred way.

    external (Python) library

    As you suggested, you can use an external library instead of GeoDjango's Translate, e.g. shapely.affinity.translate from the shapely library. However, I doubt it will accept GeoDjango's django.contrib.gis.geos.GEOSGeometry as input, so you might have to go through GeoJSON or WKT to convert back and forth.

    Use this approach if you need your spatial calculations to be independent of the underlying database system.