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

How to deserialize a GeoServer WFS GeoJSON?


TL:DR
I want to deserialize a GeoServer WFS FeatureCollection in GeoJSON format into a GeometryField/GeometryCollection.


Let's start with the model:

class Layer(models.Model):
    name = models.CharField(max_length=50)
    layer = GeometryCollectionField(null=True)

and the serializer:

class LayerSerializer(GeoFeatureModelSerializer):

    class Meta:
        model = Layer
        geo_field = 'layer'
        fields = ('id', 'name', 'layer')

Now a sample WFS GeoJSON looks like this:

{
  "type": "FeatureCollection",
  "totalFeatures": 1,
    "features": [
      {
        "type": "Feature",
        "id": "some_id",
        "geometry": {
          "type": "MultiLineString",
          "coordinates": [
            [
              [4.538638998513776, 50.4674721021459],
              [4.5436667765043754, 50.47258379613634],
              [4.548444318495443, 50.47744374212726], 
              ...     
        },
        "geometry_name": "the_geom",
        "properties": {
          ...
        }
      }
    ],
    "crs": {
      "type": "name",
      "properties": {
        "name": "urn:ogc:def:crs:EPSG::4326"
      }
    }
  }
}

On trying to deserialize the above I get the following error:

"layer": [
  "Unable to convert to python object: 
  Invalid geometry pointer returned from \"OGR_G_CreateGeometryFromJson\"."
]

PS: I prefer a solution (if one exists) that doesn't need to modify the GeoJSON in order to transform it into a GeometryCollection, as I have done that with success.


Solution

  • I ran into some similar situation. I think the problem was parsing the GeoJSON since it got converted into Python dictionary. I used json.dump to bring it back to JSON format.

    This is how I solved mine. First is making a field serializer for the GIS field. In my case, I used GEOSGeometry:

    #serializers.py
    from django.contrib.gis.geos import GEOSGeometry
    import json
    
    class GeometryFieldSerializer(serializers.Field):
        def to_representation(self, instance):
            if instance.footprint:
                instance = json.loads(instance.footprint.geojson)
            else:
                instance = None
            return instance
    
        def to_internal_value(self, data):
            data = str(json.dumps(data))
            meta = {"footprint": GEOSGeometry(data)}
            return meta
    

    After this, you can incorporate this into your main serializer. For example:

    #serializers.py
    class ModelSerializer(serializers.ModelSerializer):
        footprint = GeometryFieldSerializer(source='*')
    
        class Meta:
            model = Model
    

    This was my sample JSON to POST:

    {"name": "test",
    "footprint": {"type": "Polygon",
                  "coordinates": [[ [121.37, 14.02], [121.62, 14.02], 
                                    [121.62, 14.26], [121.37, 14.26], 
                                    [121.37, 14.02] ]]}
    }