From 5e80571bf92d93c177bea9e5ee7d89153ecadbad Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Fri, 21 Sep 2012 20:22:54 +0200 Subject: [PATCH] Fixed #16594 -- Added wkt 3D support for GEOS geometries This requires GEOS >= 3.3.0 to function properly. On previous versions, the Z dimension will simply not appear in the wkt. Disabled OpenLayers editing for 3D geometries (unsupported). --- django/contrib/gis/admin/options.py | 5 ++-- django/contrib/gis/geos/geometry.py | 2 +- django/contrib/gis/geos/prototypes/io.py | 26 ++++++++++++++++++- django/contrib/gis/geos/tests/test_geos.py | 3 ++- django/contrib/gis/tests/data/geometries.json | 4 ++- 5 files changed, 34 insertions(+), 6 deletions(-) diff --git a/django/contrib/gis/admin/options.py b/django/contrib/gis/admin/options.py index 6913bf58700..6bdceb77229 100644 --- a/django/contrib/gis/admin/options.py +++ b/django/contrib/gis/admin/options.py @@ -51,9 +51,10 @@ class GeoModelAdmin(ModelAdmin): def formfield_for_dbfield(self, db_field, **kwargs): """ Overloaded from ModelAdmin so that an OpenLayersWidget is used - for viewing/editing GeometryFields. + for viewing/editing 2D GeometryFields (OpenLayers 2 does not support + 3D editing). """ - if isinstance(db_field, models.GeometryField): + if isinstance(db_field, models.GeometryField) and db_field.dim < 3: request = kwargs.pop('request', None) # Setting the widget with the newly defined widget. kwargs['widget'] = self.get_map_widget(db_field) diff --git a/django/contrib/gis/geos/geometry.py b/django/contrib/gis/geos/geometry.py index df396bdbd3a..7ae57883e36 100644 --- a/django/contrib/gis/geos/geometry.py +++ b/django/contrib/gis/geos/geometry.py @@ -382,7 +382,7 @@ class GEOSGeometry(GEOSBase, ListMixin): @property def wkt(self): "Returns the WKT (Well-Known Text) representation of this Geometry." - return wkt_w().write(self).decode() + return wkt_w(self.hasz and 3 or 2).write(self).decode() @property def hex(self): diff --git a/django/contrib/gis/geos/prototypes/io.py b/django/contrib/gis/geos/prototypes/io.py index 1be7da88451..1e80351dbf1 100644 --- a/django/contrib/gis/geos/prototypes/io.py +++ b/django/contrib/gis/geos/prototypes/io.py @@ -45,6 +45,18 @@ wkt_writer_write.argtypes = [WKT_WRITE_PTR, GEOM_PTR] wkt_writer_write.restype = geos_char_p wkt_writer_write.errcheck = check_string +try: + wkt_writer_get_outdim = GEOSFunc('GEOSWKTWriter_getOutputDimension') + wkt_writer_get_outdim.argtypes = [WKT_WRITE_PTR] + wkt_writer_get_outdim.restype = c_int + wkt_writer_set_outdim = GEOSFunc('GEOSWKTWriter_setOutputDimension') + wkt_writer_set_outdim.argtypes = [WKT_WRITE_PTR, c_int] +except AttributeError: + # GEOSWKTWriter_get/setOutputDimension has been introduced in GEOS 3.3.0 + # Always return 2 if not available + wkt_writer_get_outdim = lambda ptr: 2 + wkt_writer_set_outdim = lambda ptr, dim: None + ### WKBReader routines ### wkb_reader_create = GEOSFunc('GEOSWKBReader_create') wkb_reader_create.restype = WKB_READ_PTR @@ -151,6 +163,17 @@ class WKTWriter(IOBase): "Returns the WKT representation of the given geometry." return wkt_writer_write(self.ptr, geom.ptr) + @property + def outdim(self): + return wkt_writer_get_outdim(self.ptr) + + @outdim.setter + def outdim(self, new_dim): + if not new_dim in (2, 3): + raise ValueError('WKT output dimension must be 2 or 3') + wkt_writer_set_outdim(self.ptr, new_dim) + + class WKBWriter(IOBase): _constructor = wkb_writer_create _destructor = wkb_writer_destroy @@ -217,9 +240,10 @@ def wkt_r(): thread_context.wkt_r = _WKTReader() return thread_context.wkt_r -def wkt_w(): +def wkt_w(dim=2): if not thread_context.wkt_w: thread_context.wkt_w = WKTWriter() + thread_context.wkt_w.outdim = dim return thread_context.wkt_w def wkb_r(): diff --git a/django/contrib/gis/geos/tests/test_geos.py b/django/contrib/gis/geos/tests/test_geos.py index c15833fb5b5..0081c7f728f 100644 --- a/django/contrib/gis/geos/tests/test_geos.py +++ b/django/contrib/gis/geos/tests/test_geos.py @@ -80,7 +80,8 @@ class GEOSTest(unittest.TestCase, TestDataMixin): "Testing WKT output." for g in self.geometries.wkt_out: geom = fromstr(g.wkt) - self.assertEqual(g.ewkt, geom.wkt) + if geom.hasz and geos_version_info()['version'] >= '3.3.0': + self.assertEqual(g.ewkt, geom.wkt) def test_hex(self): "Testing HEX output." diff --git a/django/contrib/gis/tests/data/geometries.json b/django/contrib/gis/tests/data/geometries.json index 46de4d61829..b1a6ad14bf1 100644 --- a/django/contrib/gis/tests/data/geometries.json +++ b/django/contrib/gis/tests/data/geometries.json @@ -20,6 +20,7 @@ ], "wkt_out": [ {"wkt": "POINT (110 130)", "ewkt": "POINT (110.0000000000000000 130.0000000000000000)", "kml": "110.0,130.0,0", "gml": "110,130"}, + {"wkt": "POINT (110 130 85)", "ewkt": "POINT Z (110.0000000000000000 130.0000000000000000 85.0000000000000000)", "kml": "110.0,130.0,85.0", "gml": "110,130,85"}, {"wkt": "LINESTRING (40 40,50 130,130 130)", "ewkt": "LINESTRING (40.0000000000000000 40.0000000000000000, 50.0000000000000000 130.0000000000000000, 130.0000000000000000 130.0000000000000000)", "kml": "40.0,40.0,0 50.0,130.0,0 130.0,130.0,0", "gml": "40,40 50,130 130,130"}, {"wkt": "POLYGON ((150 150,410 150,280 20,20 20,150 150),(170 120,330 120,260 50,100 50,170 120))", "ewkt": "POLYGON ((150.0000000000000000 150.0000000000000000, 410.0000000000000000 150.0000000000000000, 280.0000000000000000 20.0000000000000000, 20.0000000000000000 20.0000000000000000, 150.0000000000000000 150.0000000000000000), (170.0000000000000000 120.0000000000000000, 330.0000000000000000 120.0000000000000000, 260.0000000000000000 50.0000000000000000, 100.0000000000000000 50.0000000000000000, 170.0000000000000000 120.0000000000000000))", "kml": "150.0,150.0,0 410.0,150.0,0 280.0,20.0,0 20.0,20.0,0 150.0,150.0,0170.0,120.0,0 330.0,120.0,0 260.0,50.0,0 100.0,50.0,0 170.0,120.0,0", "gml": "150,150 410,150 280,20 20,20 150,150170,120 330,120 260,50 100,50 170,120"}, {"wkt": "MULTIPOINT (10 80,110 170,110 120)", "ewkt": "MULTIPOINT (10.0000000000000000 80.0000000000000000, 110.0000000000000000 170.0000000000000000, 110.0000000000000000 120.0000000000000000)", "kml": "10.0,80.0,0110.0,170.0,0110.0,120.0,0", "gml": "10,80110,170110,120"}, @@ -29,6 +30,7 @@ ], "hex_wkt": [ {"wkt": "POINT(0 1)", "hex": "01010000000000000000000000000000000000F03F"}, + {"wkt": "POINT(0 1 5)", "hex": "01010000800000000000000000000000000000F03F0000000000001440"}, {"wkt": "LINESTRING(0 1, 2 3, 4 5)", "hex": "0102000000030000000000000000000000000000000000F03F0000000000000040000000000000084000000000000010400000000000001440"}, {"wkt": "POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))", "hex": "010300000001000000050000000000000000000000000000000000000000000000000024400000000000000000000000000000244000000000000024400000000000000000000000000000244000000000000000000000000000000000"}, {"wkt": "MULTIPOINT(0 0, 10 0, 10 10, 0 10, 0 0)", "hex": "010400000005000000010100000000000000000000000000000000000000010100000000000000000024400000000000000000010100000000000000000024400000000000002440010100000000000000000000000000000000002440010100000000000000000000000000000000000000"}, @@ -118,4 +120,4 @@ {"wkt": "POLYGON ((-5 0,-5 10,5 10,5 5,10 5,10 -5,0 -5,0 0,-5 0))"}, {"wkt": "POLYGON ((2 0, 2 15, 18 15, 18 0, 2 0))"} ] -} \ No newline at end of file +}