Fixed #25141 -- Diminished GDAL dependence during geojson serialization
Only require GDAL if contained geometries need coordinate transformations. Thanks drepo for the report and Tim Graham for the review.
This commit is contained in:
parent
774c16d16e
commit
1da170a203
|
@ -2,6 +2,7 @@
|
||||||
This module houses the Geometry Collection objects:
|
This module houses the Geometry Collection objects:
|
||||||
GeometryCollection, MultiPoint, MultiLineString, and MultiPolygon
|
GeometryCollection, MultiPoint, MultiLineString, and MultiPolygon
|
||||||
"""
|
"""
|
||||||
|
import json
|
||||||
from ctypes import byref, c_int, c_uint
|
from ctypes import byref, c_int, c_uint
|
||||||
|
|
||||||
from django.contrib.gis.geos import prototypes as capi
|
from django.contrib.gis.geos import prototypes as capi
|
||||||
|
@ -83,6 +84,19 @@ class GeometryCollection(GEOSGeometry):
|
||||||
_set_single = GEOSGeometry._set_single_rebuild
|
_set_single = GEOSGeometry._set_single_rebuild
|
||||||
_assign_extended_slice = GEOSGeometry._assign_extended_slice_rebuild
|
_assign_extended_slice = GEOSGeometry._assign_extended_slice_rebuild
|
||||||
|
|
||||||
|
@property
|
||||||
|
def json(self):
|
||||||
|
if self.__class__.__name__ == 'GeometryCollection':
|
||||||
|
return json.dumps({
|
||||||
|
'type': self.__class__.__name__,
|
||||||
|
'geometries': [
|
||||||
|
{'type': geom.__class__.__name__, 'coordinates': geom.coords}
|
||||||
|
for geom in self
|
||||||
|
],
|
||||||
|
})
|
||||||
|
return super(GeometryCollection, self).json
|
||||||
|
geojson = json
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def kml(self):
|
def kml(self):
|
||||||
"Returns the KML for this Geometry Collection."
|
"Returns the KML for this Geometry Collection."
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import json
|
||||||
from ctypes import addressof, byref, c_double
|
from ctypes import addressof, byref, c_double
|
||||||
|
|
||||||
from django.contrib.gis import gdal
|
from django.contrib.gis import gdal
|
||||||
|
@ -411,12 +412,9 @@ class GEOSGeometry(GEOSBase, ListMixin):
|
||||||
@property
|
@property
|
||||||
def json(self):
|
def json(self):
|
||||||
"""
|
"""
|
||||||
Returns GeoJSON representation of this Geometry if GDAL is installed.
|
Returns GeoJSON representation of this Geometry.
|
||||||
"""
|
"""
|
||||||
if gdal.HAS_GDAL:
|
return json.dumps({'type': self.__class__.__name__, 'coordinates': self.coords})
|
||||||
return self.ogr.json
|
|
||||||
else:
|
|
||||||
raise GEOSException('GeoJSON output only supported when GDAL is installed.')
|
|
||||||
geojson = json
|
geojson = json
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -17,17 +17,14 @@ class Serializer(JSONSerializer):
|
||||||
def _init_options(self):
|
def _init_options(self):
|
||||||
super(Serializer, self)._init_options()
|
super(Serializer, self)._init_options()
|
||||||
self.geometry_field = self.json_kwargs.pop('geometry_field', None)
|
self.geometry_field = self.json_kwargs.pop('geometry_field', None)
|
||||||
self.srs = SpatialReference(self.json_kwargs.pop('srid', 4326))
|
self.srid = self.json_kwargs.pop('srid', 4326)
|
||||||
|
|
||||||
def start_serialization(self):
|
def start_serialization(self):
|
||||||
if not HAS_GDAL:
|
|
||||||
# GDAL is needed for the geometry.geojson call
|
|
||||||
raise SerializationError("The geojson serializer requires the GDAL library.")
|
|
||||||
self._init_options()
|
self._init_options()
|
||||||
self._cts = {} # cache of CoordTransform's
|
self._cts = {} # cache of CoordTransform's
|
||||||
self.stream.write(
|
self.stream.write(
|
||||||
'{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "EPSG:%d"}},'
|
'{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "EPSG:%d"}},'
|
||||||
' "features": [' % self.srs.srid)
|
' "features": [' % self.srid)
|
||||||
|
|
||||||
def end_serialization(self):
|
def end_serialization(self):
|
||||||
self.stream.write(']}')
|
self.stream.write(']}')
|
||||||
|
@ -48,10 +45,15 @@ class Serializer(JSONSerializer):
|
||||||
"properties": self._current,
|
"properties": self._current,
|
||||||
}
|
}
|
||||||
if self._geometry:
|
if self._geometry:
|
||||||
if self._geometry.srid != self.srs.srid:
|
if self._geometry.srid != self.srid:
|
||||||
# If needed, transform the geometry in the srid of the global geojson srid
|
# If needed, transform the geometry in the srid of the global geojson srid
|
||||||
|
if not HAS_GDAL:
|
||||||
|
raise SerializationError(
|
||||||
|
'Unable to convert geometry to SRID %s when GDAL is not installed.' % self.srid
|
||||||
|
)
|
||||||
if self._geometry.srid not in self._cts:
|
if self._geometry.srid not in self._cts:
|
||||||
self._cts[self._geometry.srid] = CoordTransform(self._geometry.srs, self.srs)
|
srs = SpatialReference(self.srid)
|
||||||
|
self._cts[self._geometry.srid] = CoordTransform(self._geometry.srs, srs)
|
||||||
self._geometry.transform(self._cts[self._geometry.srid])
|
self._geometry.transform(self._cts[self._geometry.srid])
|
||||||
data["geometry"] = eval(self._geometry.geojson)
|
data["geometry"] = eval(self._geometry.geojson)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -7,9 +7,17 @@ GeoJSON Serializer
|
||||||
.. module:: django.contrib.gis.serializers.geojson
|
.. module:: django.contrib.gis.serializers.geojson
|
||||||
:synopsis: Serialization of GeoDjango models in the GeoJSON format.
|
:synopsis: Serialization of GeoDjango models in the GeoJSON format.
|
||||||
|
|
||||||
GeoDjango provides a specific serializer for the `GeoJSON`__ format. The GDAL
|
GeoDjango provides a specific serializer for the `GeoJSON`__ format. See
|
||||||
library is required for this serializer. See :doc:`/topics/serialization` for
|
:doc:`/topics/serialization` for more information on serialization.
|
||||||
more information on serialization.
|
|
||||||
|
The GDAL library is required if any of the serialized geometries need
|
||||||
|
coordinate transformations (that is if the geometry's spatial reference system
|
||||||
|
differs from the ``srid`` serializer option).
|
||||||
|
|
||||||
|
.. versionchanged:: 1.9
|
||||||
|
|
||||||
|
The GeoJSON serializer no longer needs GDAL if all geometries are in the
|
||||||
|
same coordinate system as the ``srid`` serializer option.
|
||||||
|
|
||||||
__ http://geojson.org/
|
__ http://geojson.org/
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,8 @@ import json
|
||||||
|
|
||||||
from django.contrib.gis.geos import LinearRing, Point, Polygon
|
from django.contrib.gis.geos import LinearRing, Point, Polygon
|
||||||
from django.core import serializers
|
from django.core import serializers
|
||||||
from django.test import TestCase, skipUnlessDBFeature
|
from django.test import TestCase, mock, skipUnlessDBFeature
|
||||||
|
from django.utils import six
|
||||||
|
|
||||||
from .models import City, MultiFields, PennsylvaniaCity
|
from .models import City, MultiFields, PennsylvaniaCity
|
||||||
|
|
||||||
|
@ -70,6 +71,14 @@ class GeoJSONSerializerTests(TestCase):
|
||||||
[int(c) for c in geodata['features'][0]['geometry']['coordinates']],
|
[int(c) for c in geodata['features'][0]['geometry']['coordinates']],
|
||||||
[1564802, 5613214])
|
[1564802, 5613214])
|
||||||
|
|
||||||
|
@mock.patch('django.contrib.gis.serializers.geojson.HAS_GDAL', False)
|
||||||
|
def test_without_gdal(self):
|
||||||
|
# Without coordinate transformation, the serialization should succeed:
|
||||||
|
serializers.serialize('geojson', City.objects.all())
|
||||||
|
with six.assertRaisesRegex(self, serializers.base.SerializationError, '.*GDAL is not installed'):
|
||||||
|
# Coordinate transformations need GDAL
|
||||||
|
serializers.serialize('geojson', City.objects.all(), srid=2847)
|
||||||
|
|
||||||
def test_deserialization_exception(self):
|
def test_deserialization_exception(self):
|
||||||
"""
|
"""
|
||||||
GeoJSON cannot be deserialized.
|
GeoJSON cannot be deserialized.
|
||||||
|
|
Loading…
Reference in New Issue