1
0
mirror of https://github.com/django/django.git synced 2025-07-04 17:59:13 +00:00

gis: gdal: added support exporting/creating geometries to/from WKB and HEX.

git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@6464 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2007-10-07 20:29:59 +00:00
parent ad3e9e99a2
commit ddc9361d93
3 changed files with 103 additions and 40 deletions

View File

@ -1,6 +1,6 @@
# types and ctypes # types and ctypes
from types import StringType 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 # The GDAL C library, OGR exception, and the Field object
from django.contrib.gis.gdal.libgdal import lgdal from django.contrib.gis.gdal.libgdal import lgdal
@ -98,7 +98,7 @@ class Feature(object):
srs = None srs = None
# Geometry is cloned so the feature isn't invalidated. # 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 @property
def geom_type(self): def geom_type(self):

View File

@ -38,9 +38,11 @@
>>> print gt1 == 3, gt1 == 'Polygon' # Equivalence works w/non-OGRGeomType objects >>> print gt1 == 3, gt1 == 'Polygon' # Equivalence works w/non-OGRGeomType objects
True True
""" """
# types & ctypes # Python library imports
from types import IntType, StringType, UnicodeType import re, sys
from ctypes import byref, string_at, c_char_p, c_double, c_int, c_void_p 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 # Getting GDAL prerequisites
from django.contrib.gis.gdal.libgdal import lgdal from django.contrib.gis.gdal.libgdal import lgdal
@ -70,6 +72,9 @@ get_area = lgdal.OGR_G_GetArea
get_area.restype = c_double get_area.restype = c_double
get_area.argtypes = [c_void_p] 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 #### #### OGRGeometry Class ####
class OGRGeometry(object): class OGRGeometry(object):
"Generally encapsulates an OGR geometry." "Generally encapsulates an OGR geometry."
@ -77,7 +82,16 @@ class OGRGeometry(object):
def __init__(self, geom_input, srs=None): def __init__(self, geom_input, srs=None):
"Initializes Geometry on either WKT or an OGR pointer as input." "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): if isinstance(geom_input, StringType):
# First, trying the input as WKT # First, trying the input as WKT
@ -93,10 +107,15 @@ class OGRGeometry(object):
g = lgdal.OGR_G_CreateGeometry(ogr_t.num) g = lgdal.OGR_G_CreateGeometry(ogr_t.num)
except: except:
raise OGRException('Could not initialize OGR Geometry from: %s' % geom_input) 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): elif isinstance(geom_input, OGRGeomType):
# OGRGeomType was passed in, an empty geometry will be created.
g = lgdal.OGR_G_CreateGeometry(geom_input.num) g = lgdal.OGR_G_CreateGeometry(geom_input.num)
elif isinstance(geom_input, IntType): elif isinstance(geom_input, c_void_p):
# OGR Pointer (integer) was the input # OGR pointer (c_void_p) was the input.
g = geom_input g = geom_input
else: else:
raise OGRException('Type of input cannot be determined!') raise OGRException('Type of input cannot be determined!')
@ -181,6 +200,30 @@ class OGRGeometry(object):
"Returns the number of Points in this Geometry." "Returns the number of Points in this Geometry."
return self.point_count 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 # The SRS property
def get_srs(self): def get_srs(self):
"Returns the Spatial Reference for this Geometry." "Returns the Spatial Reference for this Geometry."
@ -216,28 +259,6 @@ class OGRGeometry(object):
srid = property(get_srid, set_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 #### #### Output Methods ####
@property @property
def gml(self): def gml(self):
@ -246,6 +267,30 @@ class OGRGeometry(object):
if buf: return string_at(buf) if buf: return string_at(buf)
else: return None 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 @property
def wkt(self): def wkt(self):
"Returns the WKT representation of the Geometry." "Returns the WKT representation of the Geometry."
@ -256,7 +301,7 @@ class OGRGeometry(object):
#### Geometry Methods #### #### Geometry Methods ####
def clone(self): def clone(self):
"Clones this OGR Geometry." "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): def close_rings(self):
"""If there are any rings within this geometry that have not been """If there are any rings within this geometry that have not been
@ -327,9 +372,9 @@ class OGRGeometry(object):
def _geomgen(self, gen_func, other=None): def _geomgen(self, gen_func, other=None):
"A helper routine for the OGR routines that generate geometries." "A helper routine for the OGR routines that generate geometries."
if isinstance(other, OGRGeometry): if isinstance(other, OGRGeometry):
return OGRGeometry(gen_func(self._g, other._g)) return OGRGeometry(c_void_p(gen_func(self._g, other._g)))
else: else:
return OGRGeometry(gen_func(self._g)) return OGRGeometry(c_void_p(gen_func(self._g)))
@property @property
def boundary(self): def boundary(self):
@ -382,9 +427,7 @@ class Point(OGRGeometry):
@property @property
def tuple(self): def tuple(self):
"Returns the tuple of this point." "Returns the tuple of this point."
if self.coord_dim == 1: if self.coord_dim == 2:
return (self.x,)
elif self.coord_dim == 2:
return (self.x, self.y) return (self.x, self.y)
elif self.coord_dim == 3: elif self.coord_dim == 3:
return (self.x, self.y, self.z) return (self.x, self.y, self.z)
@ -441,7 +484,7 @@ class Polygon(OGRGeometry):
if index < 0 or index >= self.geom_count: if index < 0 or index >= self.geom_count:
raise OGRIndexError('index out of range: %s' % str(index)) raise OGRIndexError('index out of range: %s' % str(index))
else: 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 # Polygon Properties
@property @property
@ -477,7 +520,7 @@ class GeometryCollection(OGRGeometry):
if index < 0 or index >= self.geom_count: if index < 0 or index >= self.geom_count:
raise OGRIndexError('index out of range: %s' % str(index)) raise OGRIndexError('index out of range: %s' % str(index))
else: 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): def __iter__(self):
"Iterates over each Geometry." "Iterates over each Geometry."
@ -492,7 +535,7 @@ class GeometryCollection(OGRGeometry):
"Add the geometry to this Geometry Collection." "Add the geometry to this Geometry Collection."
if isinstance(geom, OGRGeometry): if isinstance(geom, OGRGeometry):
ptr = geom._g ptr = geom._g
elif isinstance(geom, StringType): elif isinstance(geom, (StringType, UnicodeType)):
tmp = OGRGeometry(geom) tmp = OGRGeometry(geom)
ptr = tmp._g ptr = tmp._g
else: else:

View File

@ -1,6 +1,6 @@
import unittest import unittest
from django.contrib.gis.gdal import OGRGeometry, OGRGeomType, OGRException, SpatialReference from django.contrib.gis.gdal import OGRGeometry, OGRGeomType, OGRException, SpatialReference
from geometries import * from django.contrib.gis.tests.geometries import *
class OGRGeomTest(unittest.TestCase): class OGRGeomTest(unittest.TestCase):
"This tests the OGR Geometry." "This tests the OGR Geometry."
@ -42,6 +42,26 @@ class OGRGeomTest(unittest.TestCase):
geom = OGRGeometry(g.wkt) geom = OGRGeometry(g.wkt)
self.assertEqual(g.gml, geom.gml) 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): def test02_points(self):
"Testing Point objects." "Testing Point objects."