Skip to content

Commit

Permalink
gis: gdal: added support exporting/creating geometries to/from WKB an…
Browse files Browse the repository at this point in the history
…d HEX.

git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@6464 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
jbronn committed Oct 7, 2007
1 parent ad3e9e9 commit ddc9361
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 40 deletions.
4 changes: 2 additions & 2 deletions django/contrib/gis/gdal/feature.py
@@ -1,6 +1,6 @@
# types and ctypes
from types import StringType
from ctypes import c_char_p, c_int, string_at
from ctypes import c_char_p, c_int, c_void_p, string_at

# The GDAL C library, OGR exception, and the Field object
from django.contrib.gis.gdal.libgdal import lgdal
Expand Down Expand Up @@ -98,7 +98,7 @@ def geom(self):
srs = None

# Geometry is cloned so the feature isn't invalidated.
return OGRGeometry(lgdal.OGR_G_Clone(geom_ptr), srs)
return OGRGeometry(c_void_p(lgdal.OGR_G_Clone(geom_ptr)), srs)

@property
def geom_type(self):
Expand Down
117 changes: 80 additions & 37 deletions django/contrib/gis/gdal/geometries.py
Expand Up @@ -38,9 +38,11 @@
>>> print gt1 == 3, gt1 == 'Polygon' # Equivalence works w/non-OGRGeomType objects
True
"""
# types & ctypes
from types import IntType, StringType, UnicodeType
from ctypes import byref, string_at, c_char_p, c_double, c_int, c_void_p
# Python library imports
import re, sys
from binascii import a2b_hex, b2a_hex
from ctypes import byref, create_string_buffer, string_at, c_char_p, c_double, c_int, c_void_p
from types import BufferType, IntType, StringType, UnicodeType

# Getting GDAL prerequisites
from django.contrib.gis.gdal.libgdal import lgdal
Expand Down Expand Up @@ -70,14 +72,26 @@ def pnt_func(f):
get_area.restype = c_double
get_area.argtypes = [c_void_p]

# Regular expression for determining whether the input is HEXEWKB.
hex_regex = re.compile(r'^[0-9A-F]+$', re.I)

#### OGRGeometry Class ####
class OGRGeometry(object):
"Generally encapsulates an OGR geometry."

def __init__(self, geom_input, srs=None):
"Initializes Geometry on either WKT or an OGR pointer as input."

self._g = 0 # Initially NULL
self._g = c_void_p(None) # Initially NULL

# Checking if unicode
if isinstance(geom_input, UnicodeType):
# Encoding to ASCII, WKT or HEX doesn't need any more.
geo_input = geo_input.encode('ascii')

# If HEX, unpack input to to a binary buffer.
if isinstance(geom_input, StringType) and hex_regex.match(geom_input):
geom_input = buffer(a2b_hex(geom_input.upper()))

if isinstance(geom_input, StringType):
# First, trying the input as WKT
Expand All @@ -93,10 +107,15 @@ def __init__(self, geom_input, srs=None):
g = lgdal.OGR_G_CreateGeometry(ogr_t.num)
except:
raise OGRException('Could not initialize OGR Geometry from: %s' % geom_input)
elif isinstance(geom_input, BufferType):
# WKB was passed in
g = c_void_p()
check_err(lgdal.OGR_G_CreateFromWkb(c_char_p(str(geom_input)), c_void_p(), byref(g), len(geom_input)))
elif isinstance(geom_input, OGRGeomType):
# OGRGeomType was passed in, an empty geometry will be created.
g = lgdal.OGR_G_CreateGeometry(geom_input.num)
elif isinstance(geom_input, IntType):
# OGR Pointer (integer) was the input
elif isinstance(geom_input, c_void_p):
# OGR pointer (c_void_p) was the input.
g = geom_input
else:
raise OGRException('Type of input cannot be determined!')
Expand Down Expand Up @@ -181,6 +200,30 @@ def num_coords(self):
"Returns the number of Points in this Geometry."
return self.point_count

@property
def geom_type(self):
"Returns the Type for this Geometry."
return OGRGeomType(lgdal.OGR_G_GetGeometryType(self._g))

@property
def geom_name(self):
"Returns the Name of this Geometry."
return string_at(lgdal.OGR_G_GetGeometryName(self._g))

@property
def area(self):
"Returns the area for a LinearRing, Polygon, or MultiPolygon; 0 otherwise."
return get_area(self._g)

@property
def envelope(self):
"Returns the envelope for this Geometry."
env = OGREnvelope()
lgdal.OGR_G_GetEnvelope(self._g, byref(env))
return Envelope(env)

#### SpatialReference-related Properties ####

# The SRS property
def get_srs(self):
"Returns the Spatial Reference for this Geometry."
Expand Down Expand Up @@ -216,28 +259,6 @@ def set_srid(self, srid):

srid = property(get_srid, set_srid)

@property
def geom_type(self):
"Returns the Type for this Geometry."
return OGRGeomType(lgdal.OGR_G_GetGeometryType(self._g))

@property
def geom_name(self):
"Returns the Name of this Geometry."
return string_at(lgdal.OGR_G_GetGeometryName(self._g))

@property
def area(self):
"Returns the area for a LinearRing, Polygon, or MultiPolygon; 0 otherwise."
return get_area(self._g)

@property
def envelope(self):
"Returns the envelope for this Geometry."
env = OGREnvelope()
lgdal.OGR_G_GetEnvelope(self._g, byref(env))
return Envelope(env)

#### Output Methods ####
@property
def gml(self):
Expand All @@ -246,6 +267,30 @@ def gml(self):
if buf: return string_at(buf)
else: return None

@property
def hex(self):
"Returns the hexadecimal representation of the WKB (a string)."
return b2a_hex(self.wkb).upper()

@property
def wkb_size(self):
"Returns the size of the WKB buffer."
return lgdal.OGR_G_WkbSize(self._g)

@property
def wkb(self):
"Returns the WKB representation of the Geometry."
if sys.byteorder == 'little':
byteorder = c_int(1) # wkbNDR (from ogr_core.h)
else:
byteorder = c_int(0) # wkbXDR (from ogr_core.h)
# Creating a mutable string buffer of the given size, exporting
# to WKB, and returning a Python buffer of the WKB.
sz = self.wkb_size
wkb = create_string_buffer(sz)
check_err(lgdal.OGR_G_ExportToWkb(self._g, byteorder, byref(wkb)))
return buffer(string_at(wkb, sz))

@property
def wkt(self):
"Returns the WKT representation of the Geometry."
Expand All @@ -256,7 +301,7 @@ def wkt(self):
#### Geometry Methods ####
def clone(self):
"Clones this OGR Geometry."
return OGRGeometry(lgdal.OGR_G_Clone(self._g))
return OGRGeometry(c_void_p(lgdal.OGR_G_Clone(self._g)))

def close_rings(self):
"""If there are any rings within this geometry that have not been
Expand Down Expand Up @@ -327,9 +372,9 @@ def overlaps(self, other):
def _geomgen(self, gen_func, other=None):
"A helper routine for the OGR routines that generate geometries."
if isinstance(other, OGRGeometry):
return OGRGeometry(gen_func(self._g, other._g))
return OGRGeometry(c_void_p(gen_func(self._g, other._g)))
else:
return OGRGeometry(gen_func(self._g))
return OGRGeometry(c_void_p(gen_func(self._g)))

@property
def boundary(self):
Expand Down Expand Up @@ -382,9 +427,7 @@ def z(self):
@property
def tuple(self):
"Returns the tuple of this point."
if self.coord_dim == 1:
return (self.x,)
elif self.coord_dim == 2:
if self.coord_dim == 2:
return (self.x, self.y)
elif self.coord_dim == 3:
return (self.x, self.y, self.z)
Expand Down Expand Up @@ -441,7 +484,7 @@ def __getitem__(self, index):
if index < 0 or index >= self.geom_count:
raise OGRIndexError('index out of range: %s' % str(index))
else:
return OGRGeometry(lgdal.OGR_G_Clone(lgdal.OGR_G_GetGeometryRef(self._g, c_int(index))), self.srs)
return OGRGeometry(c_void_p(lgdal.OGR_G_Clone(lgdal.OGR_G_GetGeometryRef(self._g, c_int(index)))), self.srs)

# Polygon Properties
@property
Expand Down Expand Up @@ -477,7 +520,7 @@ def __getitem__(self, index):
if index < 0 or index >= self.geom_count:
raise OGRIndexError('index out of range: %s' % str(index))
else:
return OGRGeometry(lgdal.OGR_G_Clone(lgdal.OGR_G_GetGeometryRef(self._g, c_int(index))), self.srs)
return OGRGeometry(c_void_p(lgdal.OGR_G_Clone(lgdal.OGR_G_GetGeometryRef(self._g, c_int(index)))), self.srs)

def __iter__(self):
"Iterates over each Geometry."
Expand All @@ -492,7 +535,7 @@ def add(self, geom):
"Add the geometry to this Geometry Collection."
if isinstance(geom, OGRGeometry):
ptr = geom._g
elif isinstance(geom, StringType):
elif isinstance(geom, (StringType, UnicodeType)):
tmp = OGRGeometry(geom)
ptr = tmp._g
else:
Expand Down
22 changes: 21 additions & 1 deletion django/contrib/gis/tests/test_gdal_geom.py
@@ -1,6 +1,6 @@
import unittest
from django.contrib.gis.gdal import OGRGeometry, OGRGeomType, OGRException, SpatialReference
from geometries import *
from django.contrib.gis.tests.geometries import *

class OGRGeomTest(unittest.TestCase):
"This tests the OGR Geometry."
Expand Down Expand Up @@ -42,6 +42,26 @@ def test01b_gml(self):
geom = OGRGeometry(g.wkt)
self.assertEqual(g.gml, geom.gml)

def test01c_hex(self):
"Testing HEX input/output."
for g in hex_wkt:
geom1 = OGRGeometry(g.wkt)
self.assertEqual(g.hex, geom1.hex)
# Constructing w/HEX
geom2 = OGRGeometry(g.hex)
self.assertEqual(geom1, geom2)

def test01d_wkb(self):
"Testing WKB input/output."
from binascii import b2a_hex
for g in hex_wkt:
geom1 = OGRGeometry(g.wkt)
wkb = geom1.wkb
self.assertEqual(b2a_hex(wkb).upper(), g.hex)
# Constructing w/WKB.
geom2 = OGRGeometry(wkb)
self.assertEqual(geom1, geom2)

def test02_points(self):
"Testing Point objects."

Expand Down

0 comments on commit ddc9361

Please sign in to comment.