mirror of https://github.com/django/django.git
Fixed #18919 -- Stopped dropping Z attribute when transforming geometries
Previously, the wkb of geometries was dropping the Z attribute. Thanks luizvital for the report and tests and georger.silva@gmail.com for the tests.
This commit is contained in:
parent
82a74dce24
commit
ffdd6595ea
|
@ -25,7 +25,7 @@ from django.contrib.gis.geos import prototypes as capi
|
|||
|
||||
# These functions provide access to a thread-local instance
|
||||
# of their corresponding GEOS I/O class.
|
||||
from django.contrib.gis.geos.prototypes.io import wkt_r, wkt_w, wkb_r, wkb_w, ewkb_w, ewkb_w3d
|
||||
from django.contrib.gis.geos.prototypes.io import wkt_r, wkt_w, wkb_r, wkb_w, ewkb_w
|
||||
|
||||
# For recognizing geometry input.
|
||||
from django.contrib.gis.geometry.regex import hex_regex, wkt_regex, json_regex
|
||||
|
@ -388,28 +388,24 @@ class GEOSGeometry(GEOSBase, ListMixin):
|
|||
def hex(self):
|
||||
"""
|
||||
Returns the WKB of this Geometry in hexadecimal form. Please note
|
||||
that the SRID and Z values are not included in this representation
|
||||
because it is not a part of the OGC specification (use the `hexewkb`
|
||||
property instead).
|
||||
that the SRID is not included in this representation because it is not
|
||||
a part of the OGC specification (use the `hexewkb` property instead).
|
||||
"""
|
||||
# A possible faster, all-python, implementation:
|
||||
# str(self.wkb).encode('hex')
|
||||
return wkb_w().write_hex(self)
|
||||
return wkb_w(self.hasz and 3 or 2).write_hex(self)
|
||||
|
||||
@property
|
||||
def hexewkb(self):
|
||||
"""
|
||||
Returns the EWKB of this Geometry in hexadecimal form. This is an
|
||||
extension of the WKB specification that includes SRID and Z values
|
||||
that are a part of this geometry.
|
||||
extension of the WKB specification that includes SRID value that are
|
||||
a part of this geometry.
|
||||
"""
|
||||
if self.hasz:
|
||||
if not GEOS_PREPARE:
|
||||
if self.hasz and not GEOS_PREPARE:
|
||||
# See: http://trac.osgeo.org/geos/ticket/216
|
||||
raise GEOSException('Upgrade GEOS to 3.1 to get valid 3D HEXEWKB.')
|
||||
return ewkb_w3d().write_hex(self)
|
||||
else:
|
||||
return ewkb_w().write_hex(self)
|
||||
return ewkb_w(self.hasz and 3 or 2).write_hex(self)
|
||||
|
||||
@property
|
||||
def json(self):
|
||||
|
@ -429,22 +425,19 @@ class GEOSGeometry(GEOSBase, ListMixin):
|
|||
as a Python buffer. SRID and Z values are not included, use the
|
||||
`ewkb` property instead.
|
||||
"""
|
||||
return wkb_w().write(self)
|
||||
return wkb_w(self.hasz and 3 or 2).write(self)
|
||||
|
||||
@property
|
||||
def ewkb(self):
|
||||
"""
|
||||
Return the EWKB representation of this Geometry as a Python buffer.
|
||||
This is an extension of the WKB specification that includes any SRID
|
||||
and Z values that are a part of this geometry.
|
||||
value that are a part of this geometry.
|
||||
"""
|
||||
if self.hasz:
|
||||
if not GEOS_PREPARE:
|
||||
if self.hasz and not GEOS_PREPARE:
|
||||
# See: http://trac.osgeo.org/geos/ticket/216
|
||||
raise GEOSException('Upgrade GEOS to 3.1 to get valid 3D EWKB.')
|
||||
return ewkb_w3d().write(self)
|
||||
else:
|
||||
return ewkb_w().write(self)
|
||||
return ewkb_w(self.hasz and 3 or 2).write(self)
|
||||
|
||||
@property
|
||||
def kml(self):
|
||||
|
@ -516,7 +509,7 @@ class GEOSGeometry(GEOSBase, ListMixin):
|
|||
raise GEOSException("GDAL library is not available to transform() geometry.")
|
||||
|
||||
# Creating an OGR Geometry, which is then transformed.
|
||||
g = gdal.OGRGeometry(self.wkb, srid)
|
||||
g = self.ogr
|
||||
g.transform(ct)
|
||||
# Getting a new GEOS pointer
|
||||
ptr = wkb_r().read(g.wkb)
|
||||
|
|
|
@ -207,7 +207,6 @@ class ThreadLocalIO(threading.local):
|
|||
wkb_r = None
|
||||
wkb_w = None
|
||||
ewkb_w = None
|
||||
ewkb_w3d = None
|
||||
|
||||
thread_context = ThreadLocalIO()
|
||||
|
||||
|
@ -228,20 +227,15 @@ def wkb_r():
|
|||
thread_context.wkb_r = _WKBReader()
|
||||
return thread_context.wkb_r
|
||||
|
||||
def wkb_w():
|
||||
def wkb_w(dim=2):
|
||||
if not thread_context.wkb_w:
|
||||
thread_context.wkb_w = WKBWriter()
|
||||
thread_context.wkb_w.outdim = dim
|
||||
return thread_context.wkb_w
|
||||
|
||||
def ewkb_w():
|
||||
def ewkb_w(dim=2):
|
||||
if not thread_context.ewkb_w:
|
||||
thread_context.ewkb_w = WKBWriter()
|
||||
thread_context.ewkb_w.srid = True
|
||||
thread_context.ewkb_w.outdim = dim
|
||||
return thread_context.ewkb_w
|
||||
|
||||
def ewkb_w3d():
|
||||
if not thread_context.ewkb_w3d:
|
||||
thread_context.ewkb_w3d = WKBWriter()
|
||||
thread_context.ewkb_w3d.srid = True
|
||||
thread_context.ewkb_w3d.outdim = 3
|
||||
return thread_context.ewkb_w3d
|
||||
|
|
|
@ -92,6 +92,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
|
|||
"Testing (HEX)EWKB output."
|
||||
# For testing HEX(EWKB).
|
||||
ogc_hex = b'01010000000000000000000000000000000000F03F'
|
||||
ogc_hex_3d = b'01010000800000000000000000000000000000F03F0000000000000040'
|
||||
# `SELECT ST_AsHEXEWKB(ST_GeomFromText('POINT(0 1)', 4326));`
|
||||
hexewkb_2d = b'0101000020E61000000000000000000000000000000000F03F'
|
||||
# `SELECT ST_AsHEXEWKB(ST_GeomFromEWKT('SRID=4326;POINT(0 1 2)'));`
|
||||
|
@ -100,9 +101,9 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
|
|||
pnt_2d = Point(0, 1, srid=4326)
|
||||
pnt_3d = Point(0, 1, 2, srid=4326)
|
||||
|
||||
# OGC-compliant HEX will not have SRID nor Z value.
|
||||
# OGC-compliant HEX will not have SRID value.
|
||||
self.assertEqual(ogc_hex, pnt_2d.hex)
|
||||
self.assertEqual(ogc_hex, pnt_3d.hex)
|
||||
self.assertEqual(ogc_hex_3d, pnt_3d.hex)
|
||||
|
||||
# HEXEWKB should be appropriate for its dimension -- have to use an
|
||||
# a WKBWriter w/dimension set accordingly, else GEOS will insert
|
||||
|
@ -830,12 +831,17 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
|
|||
def test_gdal(self):
|
||||
"Testing `ogr` and `srs` properties."
|
||||
g1 = fromstr('POINT(5 23)')
|
||||
self.assertEqual(True, isinstance(g1.ogr, gdal.OGRGeometry))
|
||||
self.assertEqual(g1.srs, None)
|
||||
self.assertIsInstance(g1.ogr, gdal.OGRGeometry)
|
||||
self.assertIsNone(g1.srs)
|
||||
|
||||
if GEOS_PREPARE:
|
||||
g1_3d = fromstr('POINT(5 23 8)')
|
||||
self.assertIsInstance(g1_3d.ogr, gdal.OGRGeometry)
|
||||
self.assertEqual(g1_3d.ogr.z, 8)
|
||||
|
||||
g2 = fromstr('LINESTRING(0 0, 5 5, 23 23)', srid=4326)
|
||||
self.assertEqual(True, isinstance(g2.ogr, gdal.OGRGeometry))
|
||||
self.assertEqual(True, isinstance(g2.srs, gdal.SpatialReference))
|
||||
self.assertIsInstance(g2.ogr, gdal.OGRGeometry)
|
||||
self.assertIsInstance(g2.srs, gdal.SpatialReference)
|
||||
self.assertEqual(g2.hex, g2.ogr.hex)
|
||||
self.assertEqual('WGS 84', g2.srs.name)
|
||||
|
||||
|
@ -848,7 +854,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
|
|||
self.assertNotEqual(poly._ptr, cpy1._ptr)
|
||||
self.assertNotEqual(poly._ptr, cpy2._ptr)
|
||||
|
||||
@unittest.skipUnless(gdal.HAS_GDAL, "gdal is required")
|
||||
@unittest.skipUnless(gdal.HAS_GDAL, "gdal is required to transform geometries")
|
||||
def test_transform(self):
|
||||
"Testing `transform` method."
|
||||
orig = GEOSGeometry('POINT (-104.609 38.255)', 4326)
|
||||
|
@ -873,6 +879,15 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
|
|||
self.assertAlmostEqual(trans.x, p.x, prec)
|
||||
self.assertAlmostEqual(trans.y, p.y, prec)
|
||||
|
||||
@unittest.skipUnless(gdal.HAS_GDAL, "gdal is required to transform geometries")
|
||||
def test_transform_3d(self):
|
||||
p3d = GEOSGeometry('POINT (5 23 100)', 4326)
|
||||
p3d.transform(2774)
|
||||
if GEOS_PREPARE:
|
||||
self.assertEqual(p3d.z, 100)
|
||||
else:
|
||||
self.assertIsNone(p3d.z)
|
||||
|
||||
def test_transform_noop(self):
|
||||
""" Testing `transform` method (SRID match) """
|
||||
# transform() should no-op if source & dest SRIDs match,
|
||||
|
|
|
@ -4,7 +4,7 @@ import os
|
|||
import re
|
||||
|
||||
from django.contrib.gis.db.models import Union, Extent3D
|
||||
from django.contrib.gis.geos import GEOSGeometry, Point, Polygon
|
||||
from django.contrib.gis.geos import GEOSGeometry, LineString, Point, Polygon
|
||||
from django.contrib.gis.utils import LayerMapping, LayerMapError
|
||||
from django.test import TestCase
|
||||
|
||||
|
@ -67,8 +67,7 @@ class Geo3DTest(TestCase):
|
|||
# Interstate (2D / 3D and Geographic/Projected variants)
|
||||
for name, line, exp_z in interstate_data:
|
||||
line_3d = GEOSGeometry(line, srid=4269)
|
||||
# Using `hex` attribute because it omits 3D.
|
||||
line_2d = GEOSGeometry(line_3d.hex, srid=4269)
|
||||
line_2d = LineString([l[:2] for l in line_3d.coords], srid=4269)
|
||||
|
||||
# Creating a geographic and projected version of the
|
||||
# interstate in both 2D and 3D.
|
||||
|
|
|
@ -273,14 +273,18 @@ Essentially the SRID is prepended to the WKT representation, for example
|
|||
.. attribute:: GEOSGeometry.hex
|
||||
|
||||
Returns the WKB of this Geometry in hexadecimal form. Please note
|
||||
that the SRID and Z values are not included in this representation
|
||||
that the SRID value is not included in this representation
|
||||
because it is not a part of the OGC specification (use the
|
||||
:attr:`GEOSGeometry.hexewkb` property instead).
|
||||
|
||||
.. versionchanged:: 1.5
|
||||
|
||||
Prior to Django 1.5, the Z value of the geometry was dropped.
|
||||
|
||||
.. attribute:: GEOSGeometry.hexewkb
|
||||
|
||||
Returns the EWKB of this Geometry in hexadecimal form. This is an
|
||||
extension of the WKB specification that includes SRID and Z values
|
||||
extension of the WKB specification that includes the SRID value
|
||||
that are a part of this geometry.
|
||||
|
||||
.. note::
|
||||
|
@ -319,16 +323,20 @@ correspondg to the GEOS geometry.
|
|||
.. attribute:: GEOSGeometry.wkb
|
||||
|
||||
Returns the WKB (Well-Known Binary) representation of this Geometry
|
||||
as a Python buffer. SRID and Z values are not included, use the
|
||||
as a Python buffer. SRID value is not included, use the
|
||||
:attr:`GEOSGeometry.ewkb` property instead.
|
||||
|
||||
.. versionchanged:: 1.5
|
||||
|
||||
Prior to Django 1.5, the Z value of the geometry was dropped.
|
||||
|
||||
.. _ewkb:
|
||||
|
||||
.. attribute:: GEOSGeometry.ewkb
|
||||
|
||||
Return the EWKB representation of this Geometry as a Python buffer.
|
||||
This is an extension of the WKB specification that includes any SRID
|
||||
and Z values that are a part of this geometry.
|
||||
value that are a part of this geometry.
|
||||
|
||||
.. note::
|
||||
|
||||
|
@ -822,7 +830,7 @@ Writer Objects
|
|||
All writer objects have a ``write(geom)`` method that returns either the
|
||||
WKB or WKT of the given geometry. In addition, :class:`WKBWriter` objects
|
||||
also have properties that may be used to change the byte order, and or
|
||||
include the SRID and 3D values (in other words, EWKB).
|
||||
include the SRID value (in other words, EWKB).
|
||||
|
||||
.. class:: WKBWriter
|
||||
|
||||
|
@ -884,7 +892,7 @@ so that the Z value is included in the WKB.
|
|||
Outdim Value Description
|
||||
============ ===========================
|
||||
2 The default, output 2D WKB.
|
||||
3 Output 3D EWKB.
|
||||
3 Output 3D WKB.
|
||||
============ ===========================
|
||||
|
||||
Example::
|
||||
|
|
|
@ -117,6 +117,8 @@ GeoDjango
|
|||
:meth:`~django.contrib.gis.geos.GEOSGeometry.project()` methods
|
||||
(so-called linear referencing).
|
||||
|
||||
* The wkb and hex properties of `GEOSGeometry` objects preserve the Z dimension.
|
||||
|
||||
* Support for GDAL < 1.5 has been dropped.
|
||||
|
||||
Minor features
|
||||
|
|
Loading…
Reference in New Issue