Skip to content

Commit

Permalink
Added ewkb and hexewkb properties to GEOSGeometry. Refs #11433, #…
Browse files Browse the repository at this point in the history
…12010.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@11728 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
jbronn committed Nov 10, 2009
1 parent f3bb527 commit 36fe303
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 20 deletions.
46 changes: 39 additions & 7 deletions django/contrib/gis/geos/geometry.py
Expand Up @@ -357,43 +357,75 @@ def set_srid(self, srid):
#### Output Routines #### #### Output Routines ####
@property @property
def ewkt(self): def ewkt(self):
"Returns the EWKT (WKT + SRID) of the Geometry." """
Returns the EWKT (WKT + SRID) of the Geometry. Note that Z values
are *not* included in this representation because GEOS does not yet
support serializing them.
"""
if self.get_srid(): return 'SRID=%s;%s' % (self.srid, self.wkt) if self.get_srid(): return 'SRID=%s;%s' % (self.srid, self.wkt)
else: return self.wkt else: return self.wkt


@property @property
def wkt(self): def wkt(self):
"Returns the WKT (Well-Known Text) of the Geometry." "Returns the WKT (Well-Known Text) representation of this Geometry."
return wkt_w.write(self) return wkt_w.write(self)


@property @property
def hex(self): def hex(self):
""" """
Returns the HEX of the Geometry -- please note that the SRID is not Returns the HEX of the Geometry -- please note that the SRID is not
included in this representation, because the GEOS C library uses included in this representation, because it is not a part of the
-1 by default, even if the SRID is set. OGC specification (use the `hexewkb` property instead).
""" """
# A possible faster, all-python, implementation: # A possible faster, all-python, implementation:
# str(self.wkb).encode('hex') # str(self.wkb).encode('hex')
return wkb_w.write_hex(self) return wkb_w.write_hex(self)


@property
def hexewkb(self):
"""
Returns the HEXEWKB of this Geometry. This is an extension of the WKB
specification that includes SRID and Z values taht are a part of this
geometry.
"""
if self.hasz:
return ewkb_w3d.write_hex(self)
else:
return ewkb_w.write_hex(self)

@property @property
def json(self): def json(self):
""" """
Returns GeoJSON representation of this Geometry if GDAL 1.5+ Returns GeoJSON representation of this Geometry if GDAL 1.5+
is installed. is installed.
""" """
if gdal.GEOJSON: if gdal.GEOJSON:
return self.ogr.json return self.ogr.json
else: else:
raise GEOSException('GeoJSON output only supported on GDAL 1.5+.') raise GEOSException('GeoJSON output only supported on GDAL 1.5+.')
geojson = json geojson = json


@property @property
def wkb(self): def wkb(self):
"Returns the WKB of the Geometry as a buffer." """
Returns the WKB (Well-Known Binary) representation of this Geometry
as a Python buffer. SRID and Z values are not included, use the
`ewkb` property instead.
"""
return wkb_w.write(self) return wkb_w.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.
"""
if self.hasz:
return ewkb_w3d.write(self)
else:
return ewkb_w.write(self)

@property @property
def kml(self): def kml(self):
"Returns the KML representation of this Geometry." "Returns the KML representation of this Geometry."
Expand Down Expand Up @@ -617,7 +649,7 @@ def clone(self):
} }


# Similarly, import the GEOS I/O instances here to avoid conflicts. # Similarly, import the GEOS I/O instances here to avoid conflicts.
from django.contrib.gis.geos.io import wkt_r, wkt_w, wkb_r, wkb_w from django.contrib.gis.geos.io import wkt_r, wkt_w, wkb_r, wkb_w, ewkb_w, ewkb_w3d


# If supported, import the PreparedGeometry class. # If supported, import the PreparedGeometry class.
if GEOS_PREPARE: if GEOS_PREPARE:
Expand Down
27 changes: 17 additions & 10 deletions django/contrib/gis/geos/io.py
Expand Up @@ -14,19 +14,19 @@ class IOBase(GEOSBase):
"Base class for GEOS I/O objects." "Base class for GEOS I/O objects."
def __init__(self): def __init__(self):
# Getting the pointer with the constructor. # Getting the pointer with the constructor.
self.ptr = self.constructor() self.ptr = self._constructor()


def __del__(self): def __del__(self):
# Cleaning up with the appropriate destructor. # Cleaning up with the appropriate destructor.
if self._ptr: self.destructor(self._ptr) if self._ptr: self._destructor(self._ptr)


### WKT Reading and Writing objects ### ### WKT Reading and Writing objects ###


# Non-public class for internal use because its `read` method returns # Non-public class for internal use because its `read` method returns
# _pointers_ instead of a GEOSGeometry object. # _pointers_ instead of a GEOSGeometry object.
class _WKTReader(IOBase): class _WKTReader(IOBase):
constructor = capi.wkt_reader_create _constructor = capi.wkt_reader_create
destructor = capi.wkt_reader_destroy _destructor = capi.wkt_reader_destroy
ptr_type = capi.WKT_READ_PTR ptr_type = capi.WKT_READ_PTR


def read(self, wkt): def read(self, wkt):
Expand All @@ -39,8 +39,8 @@ def read(self, wkt):
return GEOSGeometry(super(WKTReader, self).read(wkt)) return GEOSGeometry(super(WKTReader, self).read(wkt))


class WKTWriter(IOBase): class WKTWriter(IOBase):
constructor = capi.wkt_writer_create _constructor = capi.wkt_writer_create
destructor = capi.wkt_writer_destroy _destructor = capi.wkt_writer_destroy
ptr_type = capi.WKT_WRITE_PTR ptr_type = capi.WKT_WRITE_PTR


def write(self, geom): def write(self, geom):
Expand All @@ -51,8 +51,8 @@ def write(self, geom):


# Non-public class for the same reason as _WKTReader above. # Non-public class for the same reason as _WKTReader above.
class _WKBReader(IOBase): class _WKBReader(IOBase):
constructor = capi.wkb_reader_create _constructor = capi.wkb_reader_create
destructor = capi.wkb_reader_destroy _destructor = capi.wkb_reader_destroy
ptr_type = capi.WKB_READ_PTR ptr_type = capi.WKB_READ_PTR


def read(self, wkb): def read(self, wkb):
Expand All @@ -71,8 +71,8 @@ def read(self, wkb):
return GEOSGeometry(super(WKBReader, self).read(wkb)) return GEOSGeometry(super(WKBReader, self).read(wkb))


class WKBWriter(IOBase): class WKBWriter(IOBase):
constructor = capi.wkb_writer_create _constructor = capi.wkb_writer_create
destructor = capi.wkb_writer_destroy _destructor = capi.wkb_writer_destroy
ptr_type = capi.WKB_WRITE_PTR ptr_type = capi.WKB_WRITE_PTR


def write(self, geom): def write(self, geom):
Expand Down Expand Up @@ -121,3 +121,10 @@ def _set_include_srid(self, include):
wkt_w = WKTWriter() wkt_w = WKTWriter()
wkb_r = _WKBReader() wkb_r = _WKBReader()
wkb_w = WKBWriter() wkb_w = WKBWriter()

# These instances are for writing EWKB in 2D and 3D.
ewkb_w = WKBWriter()
ewkb_w.srid = True
ewkb_w3d = WKBWriter()
ewkb_w3d.srid = True
ewkb_w3d.outdim = 3
5 changes: 2 additions & 3 deletions django/contrib/gis/geos/prototypes/geom.py
Expand Up @@ -62,17 +62,16 @@ def string_from_geom(func):


### ctypes prototypes ### ### ctypes prototypes ###


# Deprecated creation routines from WKB, HEX, WKT # Deprecated creation and output routines from WKB, HEX, WKT
from_hex = bin_constructor(lgeos.GEOSGeomFromHEX_buf) from_hex = bin_constructor(lgeos.GEOSGeomFromHEX_buf)
from_wkb = bin_constructor(lgeos.GEOSGeomFromWKB_buf) from_wkb = bin_constructor(lgeos.GEOSGeomFromWKB_buf)
from_wkt = geom_output(lgeos.GEOSGeomFromWKT, [c_char_p]) from_wkt = geom_output(lgeos.GEOSGeomFromWKT, [c_char_p])


# Output routines
to_hex = bin_output(lgeos.GEOSGeomToHEX_buf) to_hex = bin_output(lgeos.GEOSGeomToHEX_buf)
to_wkb = bin_output(lgeos.GEOSGeomToWKB_buf) to_wkb = bin_output(lgeos.GEOSGeomToWKB_buf)
to_wkt = string_from_geom(lgeos.GEOSGeomToWKT) to_wkt = string_from_geom(lgeos.GEOSGeomToWKT)


# The GEOS geometry type, typeid, num_coordites and number of geometries # The GEOS geometry type, typeid, num_coordinates and number of geometries
geos_normalize = int_from_geom(lgeos.GEOSNormalize) geos_normalize = int_from_geom(lgeos.GEOSNormalize)
geos_type = string_from_geom(lgeos.GEOSGeomType) geos_type = string_from_geom(lgeos.GEOSGeomType)
geos_typeid = int_from_geom(lgeos.GEOSGeomTypeId) geos_typeid = int_from_geom(lgeos.GEOSGeomTypeId)
Expand Down
25 changes: 25 additions & 0 deletions django/contrib/gis/geos/tests/test_geos.py
Expand Up @@ -71,6 +71,31 @@ def test01b_hex(self):
geom = fromstr(g.wkt) geom = fromstr(g.wkt)
self.assertEqual(g.hex, geom.hex) self.assertEqual(g.hex, geom.hex)


def test01b_hexewkb(self):
"Testing (HEX)EWKB output."
from binascii import a2b_hex

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.
self.assertEqual(ogc_hex, pnt_2d.hex)
self.assertEqual(ogc_hex, pnt_3d.hex)

# HEXEWKB should be appropriate for its dimension -- have to use an
# a WKBWriter w/dimension set accordingly, else GEOS will insert
# garbage into 3D coordinate if there is none.
self.assertEqual(hexewkb_2d, pnt_2d.hexewkb)
self.assertEqual(hexewkb_3d, pnt_3d.hexewkb)

# Same for EWKB.
self.assertEqual(buffer(a2b_hex(hexewkb_2d)), pnt_2d.ewkb)
self.assertEqual(buffer(a2b_hex(hexewkb_3d)), pnt_3d.ewkb)

# Redundant sanity check.
self.assertEqual(True, GEOSGeometry(hexewkb_3d).hasz)
self.assertEqual(4326, GEOSGeometry(hexewkb_2d).srid)

def test01c_kml(self): def test01c_kml(self):
"Testing KML output." "Testing KML output."
for tg in wkt_out: for tg in wkt_out:
Expand Down
7 changes: 7 additions & 0 deletions django/contrib/gis/tests/geometries.py
Expand Up @@ -171,3 +171,10 @@ def __init__(self, wkt, **kwargs):
not_equal=True, not_equal=True,
), ),
) )

# For testing HEX(EWKB).
ogc_hex = '01010000000000000000000000000000000000F03F'
# `SELECT ST_AsHEXEWKB(ST_GeomFromText('POINT(0 1)', 4326));`
hexewkb_2d = '0101000020E61000000000000000000000000000000000F03F'
# `SELECT ST_AsHEXEWKB(ST_GeomFromEWKT('SRID=4326;POINT(0 1 2)'));`
hexewkb_3d = '01010000A0E61000000000000000000000000000000000F03F0000000000000040'

0 comments on commit 36fe303

Please sign in to comment.