How to deserialize a GeoServer WFS GeoJSON?

1.8k views Asked by At

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.

2

There are 2 answers

1
Nikko On BEST ANSWER

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] ]]}
}
0
John Moutafis On

@Nikko Has a very good and very DRF oriented solution!

I actually solved this in a quite similar fashion, by creating a parser for the specific response of the geoserver, that returns a geos.GeometryCollection from the features:

def parse_feature_collection(feature_collection: dict):
    """
    Parses a WFS response from GeoServer and creates a GeometryCollection from it.
    """
    geometries = [
        GEOSGeometry(json.dumps(feature.get('geometry')))
        for feature in feature_collection.get('features')
    ]
    return GeometryCollection(tuple(geometries), srid=4326)

That was then used inside a celery task to update the corresponding model field and to execute some other time-consuming methods upon the instance as well (rasterizing the data and apply some calculations).