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

gis: fixed bugs in GEOSCoordSeq (and added a test case). added preliminary support for Mac OSX compiled GEOS libraries.

git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@5023 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2007-04-18 00:13:25 +00:00
parent 39f9d48e4e
commit 522400d8ef
2 changed files with 55 additions and 26 deletions

View File

@ -30,7 +30,7 @@
# Trying not to pollute the namespace. # Trying not to pollute the namespace.
from ctypes import \ from ctypes import \
CDLL, CFUNCTYPE, byref, string_at, create_string_buffer, \ CDLL, CFUNCTYPE, byref, string_at, create_string_buffer, \
c_char_p, c_double, c_float, c_int, c_uint, c_size_t, c_ubyte c_char_p, c_double, c_float, c_int, c_uint, c_size_t
import os, sys import os, sys
""" """
@ -43,10 +43,10 @@ import os, sys
installation of SWIG. installation of SWIG.
(2) The PCL implementation is over 2K+ lines of C and would make (2) The PCL implementation is over 2K+ lines of C and would make
PCL a requisite package for the GeoDjango application stack. PCL a requisite package for the GeoDjango application stack.
(3) Windows compatibility becomes substantially easier, and does not require the (3) Windows and Mac compatibility becomes substantially easier, and does not
additional compilation of PCL or GEOS and SWIG -- all that is needed is require the additional compilation of PCL or GEOS and SWIG -- all that
a Win32 compiled GEOS C library (dll) in a location that Python can read is needed is a Win32 or Mac compiled GEOS C library (dll or dylib)
(e.g. C:\Python25). in a location that Python can read (e.g. 'C:\Python25').
In summary, I wanted to wrap GEOS in a more maintainable and portable way using In summary, I wanted to wrap GEOS in a more maintainable and portable way using
only Python and the excellent ctypes library (now standard in Python 2.5). only Python and the excellent ctypes library (now standard in Python 2.5).
@ -65,13 +65,22 @@ import os, sys
""" """
# Setting the appropriate name for the GEOS-C library, depending on which # Setting the appropriate name for the GEOS-C library, depending on which
# platform we're running. # OS and POSIX platform we're running.
if os.name == 'nt': if os.name == 'nt':
# Windows NT library # Windows NT library
lib_name = 'libgeos_c-1.dll' lib_name = 'libgeos_c-1.dll'
else: elif os.name == 'posix':
platform = os.uname()[0] # Using os.uname()
if platform == 'Linux':
# Linux shared library # Linux shared library
lib_name = 'libgeos_c.so' lib_name = 'libgeos_c.so'
elif platform == 'Darwin':
# Mac OSX Shared Library (Thanks Matt!)
lib_name = 'libgeos_c.dylib'
else:
raise GEOSException, 'Unknown POSIX platform "%s"' % platform
else:
raise GEOSException, 'Unsupported OS "%s"' % os.name
# Getting the GEOS C library. The C interface (CDLL) is used for # Getting the GEOS C library. The C interface (CDLL) is used for
# both *NIX and Windows. # both *NIX and Windows.
@ -79,7 +88,7 @@ else:
# http://geos.refractions.net/ro/doxygen_docs/html/geos__c_8h-source.html # http://geos.refractions.net/ro/doxygen_docs/html/geos__c_8h-source.html
lgeos = CDLL(lib_name) lgeos = CDLL(lib_name)
# The notice and error handlers # The notice and error handler C function callback definitions.
# Supposed to mimic the GEOS message handler (C below): # Supposed to mimic the GEOS message handler (C below):
# "typedef void (*GEOSMessageHandler)(const char *fmt, ...);" # "typedef void (*GEOSMessageHandler)(const char *fmt, ...);"
NOTICEFUNC = CFUNCTYPE(None, c_char_p, c_char_p) NOTICEFUNC = CFUNCTYPE(None, c_char_p, c_char_p)
@ -89,6 +98,7 @@ notice_h = NOTICEFUNC(notice_h)
ERRORFUNC = CFUNCTYPE(None, c_char_p, c_char_p) ERRORFUNC = CFUNCTYPE(None, c_char_p, c_char_p)
def error_h(fmt, list): def error_h(fmt, list):
# TODO: Figure out why improper format code given w/some GEOS errors
if not list: if not list:
sys.stderr.write(fmt) sys.stderr.write(fmt)
else: else:
@ -102,6 +112,7 @@ error_h = ERRORFUNC(error_h)
lgeos.initGEOS(notice_h, error_h) lgeos.initGEOS(notice_h, error_h)
class GEOSException: class GEOSException:
"Exception class for any GEOS-related errors."
def __init__(self, msg): def __init__(self, msg):
self.msg = msg self.msg = msg
def __str__(self): def __str__(self):
@ -289,7 +300,7 @@ class GEOSGeometry:
if s == 0: if s == 0:
return None return None
else: else:
return lgeos.GEOSGetSRID(self._g) return s
def set_srid(self, srid): def set_srid(self, srid):
"Sets the SRID for the geometry." "Sets the SRID for the geometry."
@ -408,14 +419,11 @@ class GEOSCoordSeq:
yield self.__getitem__(i) yield self.__getitem__(i)
def __len__(self): def __len__(self):
return self.size return int(self.size)
def __str__(self): def __str__(self):
"The string representation of the coordinate sequence." "The string representation of the coordinate sequence."
rep = [] return str(self.tuple)
for i in xrange(self.size):
rep.append(self.__getitem__(i))
return str(tuple(rep))
def _checkindex(self, index): def _checkindex(self, index):
"Checks the index." "Checks the index."
@ -445,9 +453,9 @@ class GEOSCoordSeq:
set_3d = False set_3d = False
if len(value) != n_args: if len(value) != n_args:
raise GEOSException, 'Improper value given!' raise GEOSException, 'Improper value given!'
self.setX(index, value) self.setX(index, value[0])
self.setY(index, value) self.setY(index, value[1])
if set_3d: self.setZ(index, value) if set_3d: self.setZ(index, value[2])
# Getting and setting the X coordinate for the given index. # Getting and setting the X coordinate for the given index.
def getX(self, index): def getX(self, index):
@ -460,15 +468,15 @@ class GEOSCoordSeq:
def getY(self, index): def getY(self, index):
return self.getOrdinate(1, index) return self.getOrdinate(1, index)
def setY(self, index): def setY(self, index, value):
self.setOrdinate(1, index) self.setOrdinate(1, index, value)
# Getting and setting the Z coordinate for the given index # Getting and setting the Z coordinate for the given index
def getZ(self, index): def getZ(self, index):
return self.getOrdinate(2, index) return self.getOrdinate(2, index)
def setZ(self, index): def setZ(self, index, value):
self.setOrdinate(2, index) self.setOrdinate(2, index, value)
def getOrdinate(self, dimension, index): def getOrdinate(self, dimension, index):
"Gets the value for the given dimension and index." "Gets the value for the given dimension and index."
@ -488,16 +496,15 @@ class GEOSCoordSeq:
def setOrdinate(self, dimension, index, value): def setOrdinate(self, dimension, index, value):
"Sets the value for the given dimension and index." "Sets the value for the given dimension and index."
self._checkindex(idnex) self._checkindex(index)
self._checkdim(dimension) self._checkdim(dimension)
# Wrapping the dimension, index # Wrapping the dimension, index
dim = c_uint(dimension) dim = c_uint(dimension)
idx = c_uint(index) idx = c_uint(index)
# 'd' is the value of the point # Setting the ordinate
d = c_double(value) status = lgeos.GEOSCoordSeq_setOrdinate(self._cs, idx, dim, c_double(value))
status = lgeos.GEOSCoordSeq_getOrdinate(self._cs, idx, dim, byref(d))
if status == 0: if status == 0:
raise GEOSException, 'Could not set the ordinate for (dim, index): (%d, %d)' % (dimension, index) raise GEOSException, 'Could not set the ordinate for (dim, index): (%d, %d)' % (dimension, index)

View File

@ -149,6 +149,28 @@ class GeosTest2(unittest.TestCase):
# "Testing LinearRing objects." # "Testing LinearRing objects."
# pass # pass
def test08_coord_seq(self):
"Testing Coordinate Sequence objects."
for p in polygons:
if p.ext_ring_cs:
poly = GEOSGeometry(p.wkt)
cs = poly.exterior_ring.coord_seq
self.assertEqual(p.ext_ring_cs, cs.tuple) # done in the Polygon test too.
self.assertEqual(len(p.ext_ring_cs), len(cs)) # Making sure __len__ works
# Checks __getitem__ and __setitem__
for i in xrange(len(p.ext_ring_cs)):
c1 = p.ext_ring_cs[i]
c2 = cs[i]
self.assertEqual(c1, c2)
if len(c1) == 2: tset = (5, 23)
else: tset = (5, 23, 8)
cs[i] = tset
for j in range(len(tset)):
cs[i] = tset
self.assertEqual(tset, cs[i])
def test09_relate_pattern(self): def test09_relate_pattern(self):
"Testing relate() and relate_pattern()." "Testing relate() and relate_pattern()."