From a413ef2155c4f3e5bf6954608d65a96631feb7e6 Mon Sep 17 00:00:00 2001 From: Sergey Fedoseev Date: Fri, 2 Dec 2016 13:18:41 +0500 Subject: [PATCH] Refs #27472 -- Fixed OGRGeometry('POINT EMPTY').geos crash. --- django/contrib/gis/gdal/geometries.py | 14 +++++++++++++- django/contrib/gis/gdal/prototypes/generation.py | 4 +++- django/contrib/gis/gdal/prototypes/geom.py | 1 + django/contrib/gis/geos/geometry.py | 4 ++++ django/contrib/gis/geos/point.py | 7 ++++++- tests/gis_tests/gdal_tests/test_geom.py | 8 ++++++++ 6 files changed, 35 insertions(+), 3 deletions(-) diff --git a/django/contrib/gis/gdal/geometries.py b/django/contrib/gis/gdal/geometries.py index 5a9fc2796d..ef3dbb10d2 100644 --- a/django/contrib/gis/gdal/geometries.py +++ b/django/contrib/gis/gdal/geometries.py @@ -254,6 +254,10 @@ class OGRGeometry(GDALBase): # TODO: Fix Envelope() for Point geometries. return Envelope(capi.get_envelope(self.ptr, byref(OGREnvelope()))) + @property + def empty(self): + return capi.is_empty(self.ptr) + @property def extent(self): "Returns the envelope as a 4-tuple, instead of as an Envelope object." @@ -305,11 +309,15 @@ class OGRGeometry(GDALBase): srid = property(_get_srid, _set_srid) # #### Output Methods #### + def _geos_ptr(self): + from django.contrib.gis.geos import GEOSGeometry + return GEOSGeometry._from_wkb(self.wkb) + @property def geos(self): "Returns a GEOSGeometry object from this OGRGeometry." from django.contrib.gis.geos import GEOSGeometry - return GEOSGeometry(self.wkb, self.srid) + return GEOSGeometry(self._geos_ptr(), self.srid) @property def gml(self): @@ -504,6 +512,10 @@ class OGRGeometry(GDALBase): # The subclasses for OGR Geometry. class Point(OGRGeometry): + def _geos_ptr(self): + from django.contrib.gis import geos + return geos.Point._create_empty() if self.empty else super(Point, self)._geos_ptr() + @classmethod def _create_empty(cls): return capi.create_geom(OGRGeomType('point').num) diff --git a/django/contrib/gis/gdal/prototypes/generation.py b/django/contrib/gis/gdal/prototypes/generation.py index 04b89be6a6..18246cfc63 100644 --- a/django/contrib/gis/gdal/prototypes/generation.py +++ b/django/contrib/gis/gdal/prototypes/generation.py @@ -49,10 +49,12 @@ def geom_output(func, argtypes, offset=None): return func -def int_output(func, argtypes): +def int_output(func, argtypes, errcheck=None): "Generates a ctypes function that returns an integer value." func.argtypes = argtypes func.restype = c_int + if errcheck: + func.errcheck = errcheck return func diff --git a/django/contrib/gis/gdal/prototypes/geom.py b/django/contrib/gis/gdal/prototypes/geom.py index a66b18842d..f7745f552f 100644 --- a/django/contrib/gis/gdal/prototypes/geom.py +++ b/django/contrib/gis/gdal/prototypes/geom.py @@ -79,6 +79,7 @@ get_centroid = void_output(lgdal.OGR_G_Centroid, [c_void_p, c_void_p]) get_dims = int_output(lgdal.OGR_G_GetDimension, [c_void_p]) get_coord_dim = int_output(lgdal.OGR_G_GetCoordinateDimension, [c_void_p]) set_coord_dim = void_output(lgdal.OGR_G_SetCoordinateDimension, [c_void_p, c_int], errcheck=False) +is_empty = int_output(lgdal.OGR_G_IsEmpty, [c_void_p], errcheck=lambda result, func, cargs: bool(result)) get_geom_count = int_output(lgdal.OGR_G_GetGeometryCount, [c_void_p]) get_geom_name = const_string_output(lgdal.OGR_G_GetGeometryName, [c_void_p], decoding='ascii') diff --git a/django/contrib/gis/geos/geometry.py b/django/contrib/gis/geos/geometry.py index ac16184469..29589855d0 100644 --- a/django/contrib/gis/geos/geometry.py +++ b/django/contrib/gis/geos/geometry.py @@ -168,6 +168,10 @@ class GEOSGeometry(GEOSBase, ListMixin): self.ptr = ptr self._post_init(srid) + @classmethod + def _from_wkb(cls, wkb): + return wkb_r().read(wkb) + @classmethod def from_gml(cls, gml_string): return gdal.OGRGeometry.from_gml(gml_string).geos diff --git a/django/contrib/gis/geos/point.py b/django/contrib/gis/geos/point.py index ccb292cecc..c7b91f9bb5 100644 --- a/django/contrib/gis/geos/point.py +++ b/django/contrib/gis/geos/point.py @@ -47,7 +47,12 @@ class Point(GEOSGeometry): def _ogr_ptr(self): return gdal.geometries.Point._create_empty() if self.empty else super(Point, self)._ogr_ptr() - def _create_point(self, ndim, coords): + @classmethod + def _create_empty(cls): + return cls._create_point(None, None) + + @classmethod + def _create_point(cls, ndim, coords): """ Create a coordinate sequence, set X, Y, [Z], and create point """ diff --git a/tests/gis_tests/gdal_tests/test_geom.py b/tests/gis_tests/gdal_tests/test_geom.py index c205a31092..bc8219772c 100644 --- a/tests/gis_tests/gdal_tests/test_geom.py +++ b/tests/gis_tests/gdal_tests/test_geom.py @@ -549,3 +549,11 @@ class OGRGeomTest(unittest.TestCase, TestDataMixin): '' ), ) + + def test_empty(self): + self.assertIs(OGRGeometry('POINT (0 0)').empty, False) + self.assertIs(OGRGeometry('POINT EMPTY').empty, True) + + def test_empty_point_to_geos(self): + p = OGRGeometry('POINT EMPTY', srs=4326) + self.assertEqual(p.geos.ewkt, p.ewkt)