Search code examples
pythondjangodjango-rest-frameworkpostgisdjango-rest-framework-gis

Django - Get centroid of polygon in geoJSON format


I'm building a REST API to manage geo-related data.
My front-end developer wants to retrieve the centroid of polygons, depending on zoom level, in geoJSON format.

My polygon model is as follows:

...
from django.contrib.gis.db import models as geomodels
class Polygon(geomodels.Model):
    fk_owner = models.ForeignKey(User, on_delete=models.DO_NOTHING, blank=True)
    external_id = models.CharField(max_length=25, unique=True) 
    func_type = models.CharField(max_length=15)
    coordinates = geomodels.PolygonField(srid=3857)
    properties = JSONField(default={}) 

The API currently returns things like this:

"type": "FeatureCollection",
"features": [
 {
     "type": "Feature",
     "geometry": {
         "type": "Polygon",
         "coordinates": [[[..]]]
      }
  }]

And I use rest_framework_gis.serializers.GeoFeatureModelSerializer to serialize my data.

I see the following ways to get the centroid:

  1. Add a column centroid to my model: I don't want to do this
  2. Create a database view of my model: Django does not manage database views and I don't want to write a custom migration
  3. Use the same model and add an extra(...) to my orm statement: I tried but things get hard in or before serialization, because in the model the type is Polygon, and the centroid is a Point. The error is the following:

    TypeError: 
        Cannot set Polygon SpatialProxy (POLYGON) with value of type:
        <class 'django.contrib.gis.geos.point.Point'>
    

The expected output should be:

"type": "FeatureCollection",
"features": [
 {
     "type": "Feature",
     "geometry": {
         "type": "Point",
         "coordinates": [..]
      }
  }]

What is your opinion ?


Solution

  • You can use a combination of the following methods:

    1. AsGeoJSON, which

      Accepts a single geographic field or expression and returns a GeoJSON representation of the geometry.

    2. Centroid() which

      Accepts a single geographic field or expression and returns the centroid value of the geometry.

    3. .annotate() which

      Annotates each object in the QuerySet with the provided list of query expressions.
      [...]
      Each argument to annotate() is an annotation that will be added to each object in the QuerySet that is returned.


    Example:

    The following query:

    Polygon.objects.annotate(geometry=AsGeoJSON(Centroid('coordinates')))
    

    will add a field named 'geometry' to the Polygon queryset which will contain the centroid calculated from the coordinates field of every Polygon object of your given model.