Login

Updated: GeoJSON Serializer for GeoDjango (gis)

Author:
danielsokolowski
Posted:
November 9, 2011
Language:
Python
Version:
1.3
Score:
1 (after 1 ratings)

Unfortunately the built in Django JSON serialzer encodes GeoDjango GeometyrField as WKT text. This snippet extends django's serializer and adds support for GEOJson format.

Built in JSON serializer output:

[{"pk": 1, ... "geopoint": "POINT (-76.5060419999999937 44.2337040000000030)" ... }]

GeoJSON serializer ouput:

[{"pk": 1, ...  "geopoint": {"type": "Point", 
    "coordinates": [-76.503296000000006, 44.230956999999997],
    "__GEOSGeometry__": [
                "__init__",
                [
                    "SRID=4326;POINT (-75.5129950000000036          44.2442360000000008)"
                ]
            ]
}]

Note: the "__GEOSGeometry__" is a class hint as defined by JSON-RCP
and used during deserilization.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
'''
Created on 2011-05-12
Updated on 2011-11-09 -- added desrializer support

@author: Daniel Sokolowski

Extends django's built in JSON serializer to support GEOJSON encoding

Requirements:
    Install and setup geodjango (django.contrib.gis)

Install:
    Add ``SERIALIZATION_MODULES = { 'geojson' : 'path.to.geojson_serializer' }`` to your 
    project ``settings.py`` file.
    
Usage:
    from django.core import serializers
    geojson = serializers.serialize("geojson", <Model>.objects.all())
    
Console Usage: 
    python manage.py dumpdata advertisements.advertiserlocation --format geojson --indent 4 --settings=settings_dev > fixture.geojson
         --- check the file and verify that no extra characters were added at top and end of the dump
    python manage.py loaddata fixture.geojson --settings=settings_dev
    
    **Note:** however using plain JSON serializer/deserializer in the console will work just as fine.

'''
from django.core.serializers.json import DjangoJSONEncoder
from django.core.serializers.json import Serializer as OverloadedSerializer
from django.core.serializers.json import Deserializer
#from wadofstuff.django.serializers.python import Serializer as OverloadedSerializer
from django.utils import simplejson
from django.contrib.gis.db.models.fields import GeometryField
from django.contrib.gis.geos.geometry import GEOSGeometry
from django.utils import simplejson as json
from django.core.serializers.python import Deserializer as PythonDeserializer


class Serializer(OverloadedSerializer):
    def handle_field(self, obj, field):
        """
        If field is of GeometryField than encode otherwise call parent's method
        """
        value = field._get_val_from_obj(obj)
        if isinstance(field, GeometryField):
            self._current[field.name] = value
        else:
            super(Serializer, self).handle_field(obj, field)

    
    def end_serialization(self):
        simplejson.dump(self.objects, self.stream, cls=DjangoGEOJSONEncoder, **self.options)

class DjangoGEOJSONEncoder(DjangoJSONEncoder):
    """
    DjangoGEOJSONEncoder subclass that knows how to encode GEOSGeometry value
    """
    
    def default(self, o):
        """ overload the default method to process any GEOSGeometry objects otherwise call original method """ 
        if isinstance(o, GEOSGeometry):
            dictval = json.loads(o.geojson)
            #raise Exception(o.ewkt)
            dictval['__GEOSGeometry__'] = ['__init__', [o.ewkt]] #json class hint; see http://json-rpc.org/wiki/specification
            return dictval
        else:
            super(DjangoGEOJSONEncoder, self).default(o)

def Deserializer(stream_or_string, **options):
    """
    Deserialize a stream or string of JSON data.
    """
    def GEOJsonToEWKT(dict):
        """ 
        Convert to a string that GEOSGeometry class constructor can accept. 
        
        The default decoder would pass our geo dict object to the constructor which 
        would result in a TypeError; using the below hook we are forcing it into a 
        ewkt format. This is accomplished with a class hint as per JSON-RPC 
        """ 
        if '__GEOSGeometry__' in dict: # using class hint catch a GEOSGeometry definition 
            return dict['__GEOSGeometry__'][1][0]
        
        return dict
    if isinstance(stream_or_string, basestring):
        stream = StringIO(stream_or_string)
    else:
        stream = stream_or_string
    for obj in PythonDeserializer(simplejson.load(stream, object_hook=GEOJsonToEWKT), **options):
        yield obj

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
  5. Help text hyperlinks by sa2812 1 year, 7 months ago

Comments

Please login first before commenting.