Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed #10923 -- The GEOS bindings now use the thread-safe API, when a…

…pplicable. Thanks, Tuure Laurinolli, for assistance in developing this patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@12214 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 32e0117abb205da87a6d6251b9ce3490d4dd8bda 1 parent 418bfa2
@jbronn jbronn authored
View
31 django/contrib/gis/geos/geometry.py
@@ -21,6 +21,10 @@
# the underlying GEOS library.
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
+
# Regular expression for recognizing HEXEWKB and WKT. A prophylactic measure
# to prevent potentially malicious input from reaching the underlying C
# library. Not a substitute for good web security programming practices.
@@ -61,13 +65,13 @@ def __init__(self, geo_input, srid=None):
if wkt_m:
# Handling WKT input.
if wkt_m.group('srid'): srid = int(wkt_m.group('srid'))
- g = wkt_r.read(wkt_m.group('wkt'))
+ g = wkt_r().read(wkt_m.group('wkt'))
elif hex_regex.match(geo_input):
# Handling HEXEWKB input.
- g = wkb_r.read(geo_input)
+ g = wkb_r().read(geo_input)
elif gdal.GEOJSON and gdal.geometries.json_regex.match(geo_input):
# Handling GeoJSON input.
- g = wkb_r.read(gdal.OGRGeometry(geo_input).wkb)
+ g = wkb_r().read(gdal.OGRGeometry(geo_input).wkb)
else:
raise ValueError('String or unicode input unrecognized as WKT EWKT, and HEXEWKB.')
elif isinstance(geo_input, GEOM_PTR):
@@ -75,7 +79,7 @@ def __init__(self, geo_input, srid=None):
g = geo_input
elif isinstance(geo_input, buffer):
# When the input is a buffer (WKB).
- g = wkb_r.read(geo_input)
+ g = wkb_r().read(geo_input)
elif isinstance(geo_input, GEOSGeometry):
g = capi.geom_clone(geo_input.ptr)
else:
@@ -368,7 +372,7 @@ def ewkt(self):
@property
def wkt(self):
"Returns the WKT (Well-Known Text) representation of this Geometry."
- return wkt_w.write(self)
+ return wkt_w().write(self)
@property
def hex(self):
@@ -380,7 +384,7 @@ def hex(self):
"""
# A possible faster, all-python, implementation:
# str(self.wkb).encode('hex')
- return wkb_w.write_hex(self)
+ return wkb_w().write_hex(self)
@property
def hexewkb(self):
@@ -393,9 +397,9 @@ def hexewkb(self):
if 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)
+ return ewkb_w3d().write_hex(self)
else:
- return ewkb_w.write_hex(self)
+ return ewkb_w().write_hex(self)
@property
def json(self):
@@ -416,7 +420,7 @@ def wkb(self):
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):
@@ -429,9 +433,9 @@ def ewkb(self):
if 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)
+ return ewkb_w3d().write(self)
else:
- return ewkb_w.write(self)
+ return ewkb_w().write(self)
@property
def kml(self):
@@ -493,7 +497,7 @@ def transform(self, ct, clone=False):
g = gdal.OGRGeometry(self.wkb, srid)
g.transform(ct)
# Getting a new GEOS pointer
- ptr = wkb_r.read(g.wkb)
+ ptr = wkb_r().read(g.wkb)
if clone:
# User wants a cloned transformed geometry returned.
return GEOSGeometry(ptr, srid=g.srid)
@@ -655,9 +659,6 @@ def clone(self):
7 : GeometryCollection,
}
-# 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, ewkb_w, ewkb_w3d
-
# If supported, import the PreparedGeometry class.
if GEOS_PREPARE:
from django.contrib.gis.geos.prepared import PreparedGeometry
View
122 django/contrib/gis/geos/io.py
@@ -3,128 +3,18 @@
objects. Specifically, this has Python implementations of WKB/WKT
reader and writer classes.
"""
-from ctypes import byref, c_size_t
-from django.contrib.gis.geos.base import GEOSBase
-from django.contrib.gis.geos.error import GEOSException
from django.contrib.gis.geos.geometry import GEOSGeometry
-from django.contrib.gis.geos.libgeos import GEOM_PTR
-from django.contrib.gis.geos.prototypes import io as capi
-
-class IOBase(GEOSBase):
- "Base class for GEOS I/O objects."
- def __init__(self):
- # Getting the pointer with the constructor.
- self.ptr = self._constructor()
-
- def __del__(self):
- # Cleaning up with the appropriate destructor.
- if self._ptr: self._destructor(self._ptr)
-
-### WKT Reading and Writing objects ###
-
-# Non-public class for internal use because its `read` method returns
-# _pointers_ instead of a GEOSGeometry object.
-class _WKTReader(IOBase):
- _constructor = capi.wkt_reader_create
- _destructor = capi.wkt_reader_destroy
- ptr_type = capi.WKT_READ_PTR
-
- def read(self, wkt):
- if not isinstance(wkt, basestring): raise TypeError
- return capi.wkt_reader_read(self.ptr, wkt)
-
-class WKTReader(_WKTReader):
- def read(self, wkt):
- "Returns a GEOSGeometry for the given WKT string."
- return GEOSGeometry(super(WKTReader, self).read(wkt))
-
-class WKTWriter(IOBase):
- _constructor = capi.wkt_writer_create
- _destructor = capi.wkt_writer_destroy
- ptr_type = capi.WKT_WRITE_PTR
-
- def write(self, geom):
- "Returns the WKT representation of the given geometry."
- return capi.wkt_writer_write(self.ptr, geom.ptr)
-
-### WKB Reading and Writing objects ###
-
-# Non-public class for the same reason as _WKTReader above.
-class _WKBReader(IOBase):
- _constructor = capi.wkb_reader_create
- _destructor = capi.wkb_reader_destroy
- ptr_type = capi.WKB_READ_PTR
-
- def read(self, wkb):
- "Returns a _pointer_ to C GEOS Geometry object from the given WKB."
- if isinstance(wkb, buffer):
- wkb_s = str(wkb)
- return capi.wkb_reader_read(self.ptr, wkb_s, len(wkb_s))
- elif isinstance(wkb, basestring):
- return capi.wkb_reader_read_hex(self.ptr, wkb, len(wkb))
- else:
- raise TypeError
+from django.contrib.gis.geos.prototypes.io import _WKTReader, _WKBReader, WKBWriter, WKTWriter
+# Public classes for (WKB|WKT)Reader, which return GEOSGeometry
class WKBReader(_WKBReader):
def read(self, wkb):
"Returns a GEOSGeometry for the given WKB buffer."
return GEOSGeometry(super(WKBReader, self).read(wkb))
-class WKBWriter(IOBase):
- _constructor = capi.wkb_writer_create
- _destructor = capi.wkb_writer_destroy
- ptr_type = capi.WKB_WRITE_PTR
-
- def write(self, geom):
- "Returns the WKB representation of the given geometry."
- return buffer(capi.wkb_writer_write(self.ptr, geom.ptr, byref(c_size_t())))
-
- def write_hex(self, geom):
- "Returns the HEXEWKB representation of the given geometry."
- return capi.wkb_writer_write_hex(self.ptr, geom.ptr, byref(c_size_t()))
-
- ### WKBWriter Properties ###
-
- # Property for getting/setting the byteorder.
- def _get_byteorder(self):
- return capi.wkb_writer_get_byteorder(self.ptr)
-
- def _set_byteorder(self, order):
- if not order in (0, 1): raise ValueError('Byte order parameter must be 0 (Big Endian) or 1 (Little Endian).')
- capi.wkb_writer_set_byteorder(self.ptr, order)
-
- byteorder = property(_get_byteorder, _set_byteorder)
-
- # Property for getting/setting the output dimension.
- def _get_outdim(self):
- return capi.wkb_writer_get_outdim(self.ptr)
-
- def _set_outdim(self, new_dim):
- if not new_dim in (2, 3): raise ValueError('WKB output dimension must be 2 or 3')
- capi.wkb_writer_set_outdim(self.ptr, new_dim)
-
- outdim = property(_get_outdim, _set_outdim)
-
- # Property for getting/setting the include srid flag.
- def _get_include_srid(self):
- return bool(ord(capi.wkb_writer_get_include_srid(self.ptr)))
-
- def _set_include_srid(self, include):
- if bool(include): flag = chr(1)
- else: flag = chr(0)
- capi.wkb_writer_set_include_srid(self.ptr, flag)
-
- srid = property(_get_include_srid, _set_include_srid)
+class WKTReader(_WKTReader):
+ def read(self, wkt):
+ "Returns a GEOSGeometry for the given WKT string."
+ return GEOSGeometry(super(WKTReader, self).read(wkt))
-# Instances of the WKT and WKB reader/writer objects.
-wkt_r = _WKTReader()
-wkt_w = WKTWriter()
-wkb_r = _WKBReader()
-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
View
35 django/contrib/gis/geos/libgeos.py
@@ -6,7 +6,7 @@
This module also houses GEOS Pointer utilities, including
get_pointer_arr(), and GEOM_PTR.
"""
-import atexit, os, re, sys
+import os, re, sys
from ctypes import c_char_p, Structure, CDLL, CFUNCTYPE, POINTER
from ctypes.util import find_library
from django.contrib.gis.geos.error import GEOSException
@@ -45,14 +45,14 @@
'", "'.join(lib_names))
# Getting the GEOS C library. The C interface (CDLL) is used for
-# both *NIX and Windows.
+# both *NIX and Windows.
# See the GEOS C API source code for more details on the library function calls:
# http://geos.refractions.net/ro/doxygen_docs/html/geos__c_8h-source.html
lgeos = CDLL(lib_path)
# The notice and error handler C function callback definitions.
-# Supposed to mimic the GEOS message handler (C below):
-# "typedef void (*GEOSMessageHandler)(const char *fmt, ...);"
+# Supposed to mimic the GEOS message handler (C below):
+# typedef void (*GEOSMessageHandler)(const char *fmt, ...);
NOTICEFUNC = CFUNCTYPE(None, c_char_p, c_char_p)
def notice_h(fmt, lst, output_h=sys.stdout):
try:
@@ -71,23 +71,19 @@ def error_h(fmt, lst, output_h=sys.stderr):
output_h.write('GEOS_ERROR: %s\n' % err_msg)
error_h = ERRORFUNC(error_h)
-# The initGEOS routine should be called first, however, that routine takes
-# the notice and error functions as parameters. Here is the C code that
-# is wrapped:
-# "extern void GEOS_DLL initGEOS(GEOSMessageHandler notice_function, GEOSMessageHandler error_function);"
-lgeos.initGEOS(notice_h, error_h)
-
#### GEOS Geometry C data structures, and utility functions. ####
# Opaque GEOS geometry structures, used for GEOM_PTR and CS_PTR
class GEOSGeom_t(Structure): pass
class GEOSPrepGeom_t(Structure): pass
class GEOSCoordSeq_t(Structure): pass
+class GEOSContextHandle_t(Structure): pass
# Pointers to opaque GEOS geometry structures.
GEOM_PTR = POINTER(GEOSGeom_t)
PREPGEOM_PTR = POINTER(GEOSPrepGeom_t)
CS_PTR = POINTER(GEOSCoordSeq_t)
+CONTEXT_PTR = POINTER(GEOSContextHandle_t)
# Used specifically by the GEOSGeom_createPolygon and GEOSGeom_createCollection
# GEOS routines
@@ -126,5 +122,20 @@ def geos_version_info():
GEOS_VERSION = (GEOS_MAJOR_VERSION, GEOS_MINOR_VERSION, GEOS_SUBMINOR_VERSION)
GEOS_PREPARE = GEOS_VERSION >= (3, 1, 0)
-# Calling the finishGEOS() upon exit of the interpreter.
-atexit.register(lgeos.finishGEOS)
+if GEOS_PREPARE:
+ # Here we set up the prototypes for the initGEOS_r and finishGEOS_r
+ # routines. These functions aren't actually called until they are
+ # attached to a GEOS context handle -- this actually occurs in
+ # geos/prototypes/threadsafe.py.
+ lgeos.initGEOS_r.restype = CONTEXT_PTR
+ lgeos.finishGEOS_r.argtypes = [CONTEXT_PTR]
+else:
+ # When thread-safety isn't available, the initGEOS routine must be called
+ # first. This function takes the notice and error functions, defined
+ # as Python callbacks above, as parameters. Here is the C code that is
+ # wrapped:
+ # extern void GEOS_DLL initGEOS(GEOSMessageHandler notice_function, GEOSMessageHandler error_function);
+ lgeos.initGEOS(notice_h, error_h)
+ # Calling finishGEOS() upon exit of the interpreter.
+ import atexit
+ atexit.register(lgeos.finishGEOS)
View
29 django/contrib/gis/geos/prototypes/coordseq.py
@@ -1,6 +1,7 @@
from ctypes import c_double, c_int, c_uint, POINTER
-from django.contrib.gis.geos.libgeos import lgeos, GEOM_PTR, CS_PTR
+from django.contrib.gis.geos.libgeos import GEOM_PTR, CS_PTR
from django.contrib.gis.geos.prototypes.errcheck import last_arg_byref, GEOSException
+from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc
## Error-checking routines specific to coordinate sequences. ##
def check_cs_ptr(result, func, cargs):
@@ -59,24 +60,24 @@ def cs_output(func, argtypes):
## Coordinate Sequence ctypes prototypes ##
# Coordinate Sequence constructors & cloning.
-cs_clone = cs_output(lgeos.GEOSCoordSeq_clone, [CS_PTR])
-create_cs = cs_output(lgeos.GEOSCoordSeq_create, [c_uint, c_uint])
-get_cs = cs_output(lgeos.GEOSGeom_getCoordSeq, [GEOM_PTR])
+cs_clone = cs_output(GEOSFunc('GEOSCoordSeq_clone'), [CS_PTR])
+create_cs = cs_output(GEOSFunc('GEOSCoordSeq_create'), [c_uint, c_uint])
+get_cs = cs_output(GEOSFunc('GEOSGeom_getCoordSeq'), [GEOM_PTR])
# Getting, setting ordinate
-cs_getordinate = cs_operation(lgeos.GEOSCoordSeq_getOrdinate, ordinate=True, get=True)
-cs_setordinate = cs_operation(lgeos.GEOSCoordSeq_setOrdinate, ordinate=True)
+cs_getordinate = cs_operation(GEOSFunc('GEOSCoordSeq_getOrdinate'), ordinate=True, get=True)
+cs_setordinate = cs_operation(GEOSFunc('GEOSCoordSeq_setOrdinate'), ordinate=True)
# For getting, x, y, z
-cs_getx = cs_operation(lgeos.GEOSCoordSeq_getX, get=True)
-cs_gety = cs_operation(lgeos.GEOSCoordSeq_getY, get=True)
-cs_getz = cs_operation(lgeos.GEOSCoordSeq_getZ, get=True)
+cs_getx = cs_operation(GEOSFunc('GEOSCoordSeq_getX'), get=True)
+cs_gety = cs_operation(GEOSFunc('GEOSCoordSeq_getY'), get=True)
+cs_getz = cs_operation(GEOSFunc('GEOSCoordSeq_getZ'), get=True)
# For setting, x, y, z
-cs_setx = cs_operation(lgeos.GEOSCoordSeq_setX)
-cs_sety = cs_operation(lgeos.GEOSCoordSeq_setY)
-cs_setz = cs_operation(lgeos.GEOSCoordSeq_setZ)
+cs_setx = cs_operation(GEOSFunc('GEOSCoordSeq_setX'))
+cs_sety = cs_operation(GEOSFunc('GEOSCoordSeq_setY'))
+cs_setz = cs_operation(GEOSFunc('GEOSCoordSeq_setZ'))
# These routines return size & dimensions.
-cs_getsize = cs_int(lgeos.GEOSCoordSeq_getSize)
-cs_getdims = cs_int(lgeos.GEOSCoordSeq_getDimensions)
+cs_getsize = cs_int(GEOSFunc('GEOSCoordSeq_getSize'))
+cs_getdims = cs_int(GEOSFunc('GEOSCoordSeq_getDimensions'))
View
5 django/contrib/gis/geos/prototypes/errcheck.py
@@ -4,14 +4,15 @@
import os
from ctypes import c_void_p, string_at, CDLL
from django.contrib.gis.geos.error import GEOSException
-from django.contrib.gis.geos.libgeos import lgeos, GEOS_VERSION
+from django.contrib.gis.geos.libgeos import GEOS_VERSION
+from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc
# Getting the `free` routine used to free the memory allocated for
# string pointers returned by GEOS.
if GEOS_VERSION >= (3, 1, 1):
# In versions 3.1.1 and above, `GEOSFree` was added to the C API
# because `free` isn't always available on all platforms.
- free = lgeos.GEOSFree
+ free = GEOSFunc('GEOSFree')
free.argtypes = [c_void_p]
free.restype = None
else:
View
58 django/contrib/gis/geos/prototypes/geom.py
@@ -1,7 +1,8 @@
from ctypes import c_char_p, c_int, c_size_t, c_ubyte, c_uint, POINTER
-from django.contrib.gis.geos.libgeos import lgeos, CS_PTR, GEOM_PTR, PREPGEOM_PTR, GEOS_PREPARE
+from django.contrib.gis.geos.libgeos import CS_PTR, GEOM_PTR, PREPGEOM_PTR, GEOS_PREPARE
from django.contrib.gis.geos.prototypes.errcheck import \
check_geom, check_minus_one, check_sized_string, check_string, check_zero
+from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc
# This is the return type used by binary output (WKB, HEX) routines.
c_uchar_p = POINTER(c_ubyte)
@@ -62,56 +63,57 @@ def string_from_geom(func):
### ctypes prototypes ###
-# Deprecated creation and output routines from WKB, HEX, WKT
-from_hex = bin_constructor(lgeos.GEOSGeomFromHEX_buf)
-from_wkb = bin_constructor(lgeos.GEOSGeomFromWKB_buf)
-from_wkt = geom_output(lgeos.GEOSGeomFromWKT, [c_char_p])
+# Deprecated creation routines from WKB, HEX, WKT
+from_hex = bin_constructor(GEOSFunc('GEOSGeomFromHEX_buf'))
+from_wkb = bin_constructor(GEOSFunc('GEOSGeomFromWKB_buf'))
+from_wkt = geom_output(GEOSFunc('GEOSGeomFromWKT'), [c_char_p])
-to_hex = bin_output(lgeos.GEOSGeomToHEX_buf)
-to_wkb = bin_output(lgeos.GEOSGeomToWKB_buf)
-to_wkt = string_from_geom(lgeos.GEOSGeomToWKT)
+# Deprecated output routines
+to_hex = bin_output(GEOSFunc('GEOSGeomToHEX_buf'))
+to_wkb = bin_output(GEOSFunc('GEOSGeomToWKB_buf'))
+to_wkt = string_from_geom(GEOSFunc('GEOSGeomToWKT'))
-# The GEOS geometry type, typeid, num_coordinates and number of geometries
-geos_normalize = int_from_geom(lgeos.GEOSNormalize)
-geos_type = string_from_geom(lgeos.GEOSGeomType)
-geos_typeid = int_from_geom(lgeos.GEOSGeomTypeId)
-get_dims = int_from_geom(lgeos.GEOSGeom_getDimensions, zero=True)
-get_num_coords = int_from_geom(lgeos.GEOSGetNumCoordinates)
-get_num_geoms = int_from_geom(lgeos.GEOSGetNumGeometries)
+# The GEOS geometry type, typeid, num_coordites and number of geometries
+geos_normalize = int_from_geom(GEOSFunc('GEOSNormalize'))
+geos_type = string_from_geom(GEOSFunc('GEOSGeomType'))
+geos_typeid = int_from_geom(GEOSFunc('GEOSGeomTypeId'))
+get_dims = int_from_geom(GEOSFunc('GEOSGeom_getDimensions'), zero=True)
+get_num_coords = int_from_geom(GEOSFunc('GEOSGetNumCoordinates'))
+get_num_geoms = int_from_geom(GEOSFunc('GEOSGetNumGeometries'))
# Geometry creation factories
-create_point = geom_output(lgeos.GEOSGeom_createPoint, [CS_PTR])
-create_linestring = geom_output(lgeos.GEOSGeom_createLineString, [CS_PTR])
-create_linearring = geom_output(lgeos.GEOSGeom_createLinearRing, [CS_PTR])
+create_point = geom_output(GEOSFunc('GEOSGeom_createPoint'), [CS_PTR])
+create_linestring = geom_output(GEOSFunc('GEOSGeom_createLineString'), [CS_PTR])
+create_linearring = geom_output(GEOSFunc('GEOSGeom_createLinearRing'), [CS_PTR])
# Polygon and collection creation routines are special and will not
# have their argument types defined.
-create_polygon = geom_output(lgeos.GEOSGeom_createPolygon, None)
-create_collection = geom_output(lgeos.GEOSGeom_createCollection, None)
+create_polygon = geom_output(GEOSFunc('GEOSGeom_createPolygon'), None)
+create_collection = geom_output(GEOSFunc('GEOSGeom_createCollection'), None)
# Ring routines
-get_extring = geom_output(lgeos.GEOSGetExteriorRing, [GEOM_PTR])
-get_intring = geom_index(lgeos.GEOSGetInteriorRingN)
-get_nrings = int_from_geom(lgeos.GEOSGetNumInteriorRings)
+get_extring = geom_output(GEOSFunc('GEOSGetExteriorRing'), [GEOM_PTR])
+get_intring = geom_index(GEOSFunc('GEOSGetInteriorRingN'))
+get_nrings = int_from_geom(GEOSFunc('GEOSGetNumInteriorRings'))
# Collection Routines
-get_geomn = geom_index(lgeos.GEOSGetGeometryN)
+get_geomn = geom_index(GEOSFunc('GEOSGetGeometryN'))
# Cloning
-geom_clone = lgeos.GEOSGeom_clone
+geom_clone = GEOSFunc('GEOSGeom_clone')
geom_clone.argtypes = [GEOM_PTR]
geom_clone.restype = GEOM_PTR
# Destruction routine.
-destroy_geom = lgeos.GEOSGeom_destroy
+destroy_geom = GEOSFunc('GEOSGeom_destroy')
destroy_geom.argtypes = [GEOM_PTR]
destroy_geom.restype = None
# SRID routines
-geos_get_srid = lgeos.GEOSGetSRID
+geos_get_srid = GEOSFunc('GEOSGetSRID')
geos_get_srid.argtypes = [GEOM_PTR]
geos_get_srid.restype = c_int
-geos_set_srid = lgeos.GEOSSetSRID
+geos_set_srid = GEOSFunc('GEOSSetSRID')
geos_set_srid.argtypes = [GEOM_PTR, c_int]
geos_set_srid.restype = None
View
192 django/contrib/gis/geos/prototypes/io.py
@@ -1,7 +1,10 @@
-from ctypes import c_char_p, c_int, c_char, c_size_t, Structure, POINTER
-from django.contrib.gis.geos.libgeos import lgeos, GEOM_PTR
+import threading
+from ctypes import byref, c_char_p, c_int, c_char, c_size_t, Structure, POINTER
+from django.contrib.gis.geos.base import GEOSBase
+from django.contrib.gis.geos.libgeos import GEOM_PTR
from django.contrib.gis.geos.prototypes.errcheck import check_geom, check_string, check_sized_string
from django.contrib.gis.geos.prototypes.geom import c_uchar_p, geos_char_p
+from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc
### The WKB/WKT Reader/Writer structures and pointers ###
class WKTReader_st(Structure): pass
@@ -15,34 +18,34 @@ class WKBWriter_st(Structure): pass
WKB_WRITE_PTR = POINTER(WKBReader_st)
### WKTReader routines ###
-wkt_reader_create = lgeos.GEOSWKTReader_create
+wkt_reader_create = GEOSFunc('GEOSWKTReader_create')
wkt_reader_create.restype = WKT_READ_PTR
-wkt_reader_destroy = lgeos.GEOSWKTReader_destroy
+wkt_reader_destroy = GEOSFunc('GEOSWKTReader_destroy')
wkt_reader_destroy.argtypes = [WKT_READ_PTR]
-wkt_reader_read = lgeos.GEOSWKTReader_read
+wkt_reader_read = GEOSFunc('GEOSWKTReader_read')
wkt_reader_read.argtypes = [WKT_READ_PTR, c_char_p]
wkt_reader_read.restype = GEOM_PTR
wkt_reader_read.errcheck = check_geom
### WKTWriter routines ###
-wkt_writer_create = lgeos.GEOSWKTWriter_create
+wkt_writer_create = GEOSFunc('GEOSWKTWriter_create')
wkt_writer_create.restype = WKT_WRITE_PTR
-wkt_writer_destroy = lgeos.GEOSWKTWriter_destroy
+wkt_writer_destroy = GEOSFunc('GEOSWKTWriter_destroy')
wkt_writer_destroy.argtypes = [WKT_WRITE_PTR]
-wkt_writer_write = lgeos.GEOSWKTWriter_write
+wkt_writer_write = GEOSFunc('GEOSWKTWriter_write')
wkt_writer_write.argtypes = [WKT_WRITE_PTR, GEOM_PTR]
wkt_writer_write.restype = geos_char_p
wkt_writer_write.errcheck = check_string
### WKBReader routines ###
-wkb_reader_create = lgeos.GEOSWKBReader_create
+wkb_reader_create = GEOSFunc('GEOSWKBReader_create')
wkb_reader_create.restype = WKB_READ_PTR
-wkb_reader_destroy = lgeos.GEOSWKBReader_destroy
+wkb_reader_destroy = GEOSFunc('GEOSWKBReader_destroy')
wkb_reader_destroy.argtypes = [WKB_READ_PTR]
def wkb_read_func(func):
@@ -56,14 +59,14 @@ def wkb_read_func(func):
func.errcheck = check_geom
return func
-wkb_reader_read = wkb_read_func(lgeos.GEOSWKBReader_read)
-wkb_reader_read_hex = wkb_read_func(lgeos.GEOSWKBReader_readHEX)
+wkb_reader_read = wkb_read_func(GEOSFunc('GEOSWKBReader_read'))
+wkb_reader_read_hex = wkb_read_func(GEOSFunc('GEOSWKBReader_readHEX'))
### WKBWriter routines ###
-wkb_writer_create = lgeos.GEOSWKBWriter_create
+wkb_writer_create = GEOSFunc('GEOSWKBWriter_create')
wkb_writer_create.restype = WKB_WRITE_PTR
-wkb_writer_destroy = lgeos.GEOSWKBWriter_destroy
+wkb_writer_destroy = GEOSFunc('GEOSWKBWriter_destroy')
wkb_writer_destroy.argtypes = [WKB_WRITE_PTR]
# WKB Writing prototypes.
@@ -73,8 +76,8 @@ def wkb_write_func(func):
func.errcheck = check_sized_string
return func
-wkb_writer_write = wkb_write_func(lgeos.GEOSWKBWriter_write)
-wkb_writer_write_hex = wkb_write_func(lgeos.GEOSWKBWriter_writeHEX)
+wkb_writer_write = wkb_write_func(GEOSFunc('GEOSWKBWriter_write'))
+wkb_writer_write_hex = wkb_write_func(GEOSFunc('GEOSWKBWriter_writeHEX'))
# WKBWriter property getter/setter prototypes.
def wkb_writer_get(func, restype=c_int):
@@ -86,9 +89,154 @@ def wkb_writer_set(func, argtype=c_int):
func.argtypes = [WKB_WRITE_PTR, argtype]
return func
-wkb_writer_get_byteorder = wkb_writer_get(lgeos.GEOSWKBWriter_getByteOrder)
-wkb_writer_set_byteorder = wkb_writer_set(lgeos.GEOSWKBWriter_setByteOrder)
-wkb_writer_get_outdim = wkb_writer_get(lgeos.GEOSWKBWriter_getOutputDimension)
-wkb_writer_set_outdim = wkb_writer_set(lgeos.GEOSWKBWriter_setOutputDimension)
-wkb_writer_get_include_srid = wkb_writer_get(lgeos.GEOSWKBWriter_getIncludeSRID, restype=c_char)
-wkb_writer_set_include_srid = wkb_writer_set(lgeos.GEOSWKBWriter_setIncludeSRID, argtype=c_char)
+wkb_writer_get_byteorder = wkb_writer_get(GEOSFunc('GEOSWKBWriter_getByteOrder'))
+wkb_writer_set_byteorder = wkb_writer_set(GEOSFunc('GEOSWKBWriter_setByteOrder'))
+wkb_writer_get_outdim = wkb_writer_get(GEOSFunc('GEOSWKBWriter_getOutputDimension'))
+wkb_writer_set_outdim = wkb_writer_set(GEOSFunc('GEOSWKBWriter_setOutputDimension'))
+wkb_writer_get_include_srid = wkb_writer_get(GEOSFunc('GEOSWKBWriter_getIncludeSRID'), restype=c_char)
+wkb_writer_set_include_srid = wkb_writer_set(GEOSFunc('GEOSWKBWriter_setIncludeSRID'), argtype=c_char)
+
+### Base I/O Class ###
+class IOBase(GEOSBase):
+ "Base class for GEOS I/O objects."
+ def __init__(self):
+ # Getting the pointer with the constructor.
+ self.ptr = self._constructor()
+
+ def __del__(self):
+ # Cleaning up with the appropriate destructor.
+ if self._ptr: self._destructor(self._ptr)
+
+### Base WKB/WKT Reading and Writing objects ###
+
+# Non-public WKB/WKT reader classes for internal use because
+# their `read` methods return _pointers_ instead of GEOSGeometry
+# objects.
+class _WKTReader(IOBase):
+ _constructor = wkt_reader_create
+ _destructor = wkt_reader_destroy
+ ptr_type = WKT_READ_PTR
+
+ def read(self, wkt):
+ if not isinstance(wkt, basestring): raise TypeError
+ return wkt_reader_read(self.ptr, wkt)
+
+class _WKBReader(IOBase):
+ _constructor = wkb_reader_create
+ _destructor = wkb_reader_destroy
+ ptr_type = WKB_READ_PTR
+
+ def read(self, wkb):
+ "Returns a _pointer_ to C GEOS Geometry object from the given WKB."
+ if isinstance(wkb, buffer):
+ wkb_s = str(wkb)
+ return wkb_reader_read(self.ptr, wkb_s, len(wkb_s))
+ elif isinstance(wkb, basestring):
+ return wkb_reader_read_hex(self.ptr, wkb, len(wkb))
+ else:
+ raise TypeError
+
+### WKB/WKT Writer Classes ###
+class WKTWriter(IOBase):
+ _constructor = wkt_writer_create
+ _destructor = wkt_writer_destroy
+ ptr_type = WKT_WRITE_PTR
+
+ def write(self, geom):
+ "Returns the WKT representation of the given geometry."
+ return wkt_writer_write(self.ptr, geom.ptr)
+
+class WKBWriter(IOBase):
+ _constructor = wkb_writer_create
+ _destructor = wkb_writer_destroy
+ ptr_type = WKB_WRITE_PTR
+
+ def write(self, geom):
+ "Returns the WKB representation of the given geometry."
+ return buffer(wkb_writer_write(self.ptr, geom.ptr, byref(c_size_t())))
+
+ def write_hex(self, geom):
+ "Returns the HEXEWKB representation of the given geometry."
+ return wkb_writer_write_hex(self.ptr, geom.ptr, byref(c_size_t()))
+
+ ### WKBWriter Properties ###
+
+ # Property for getting/setting the byteorder.
+ def _get_byteorder(self):
+ return wkb_writer_get_byteorder(self.ptr)
+
+ def _set_byteorder(self, order):
+ if not order in (0, 1): raise ValueError('Byte order parameter must be 0 (Big Endian) or 1 (Little Endian).')
+ wkb_writer_set_byteorder(self.ptr, order)
+
+ byteorder = property(_get_byteorder, _set_byteorder)
+
+ # Property for getting/setting the output dimension.
+ def _get_outdim(self):
+ return wkb_writer_get_outdim(self.ptr)
+
+ def _set_outdim(self, new_dim):
+ if not new_dim in (2, 3): raise ValueError('WKB output dimension must be 2 or 3')
+ wkb_writer_set_outdim(self.ptr, new_dim)
+
+ outdim = property(_get_outdim, _set_outdim)
+
+ # Property for getting/setting the include srid flag.
+ def _get_include_srid(self):
+ return bool(ord(wkb_writer_get_include_srid(self.ptr)))
+
+ def _set_include_srid(self, include):
+ if bool(include): flag = chr(1)
+ else: flag = chr(0)
+ wkb_writer_set_include_srid(self.ptr, flag)
+
+ srid = property(_get_include_srid, _set_include_srid)
+
+# `ThreadLocalIO` object holds instances of the WKT and WKB reader/writer
+# objects that are local to the thread. The `GEOSGeometry` internals
+# access these instances by calling the module-level functions, defined
+# below.
+class ThreadLocalIO(threading.local):
+ wkt_r = None
+ wkt_w = None
+ wkb_r = None
+ wkb_w = None
+ ewkb_w = None
+ ewkb_w3d = None
+
+thread_context = ThreadLocalIO()
+
+# These module-level routines return the I/O object that is local to the
+# the thread. If the I/O object does not exist yet it will be initialized.
+def wkt_r():
+ if not thread_context.wkt_r:
+ thread_context.wkt_r = _WKTReader()
+ return thread_context.wkt_r
+
+def wkt_w():
+ if not thread_context.wkt_w:
+ thread_context.wkt_w = WKTWriter()
+ return thread_context.wkt_w
+
+def wkb_r():
+ if not thread_context.wkb_r:
+ thread_context.wkb_r = _WKBReader()
+ return thread_context.wkb_r
+
+def wkb_w():
+ if not thread_context.wkb_w:
+ thread_context.wkb_w = WKBWriter()
+ return thread_context.wkb_w
+
+def ewkb_w():
+ if not thread_context.ewkb_w:
+ thread_context.ewkb_w = WKBWriter()
+ thread_context.ewkb_w.srid = True
+ 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
View
9 django/contrib/gis/geos/prototypes/misc.py
@@ -3,8 +3,9 @@
ones that return the area, distance, and length.
"""
from ctypes import c_int, c_double, POINTER
-from django.contrib.gis.geos.libgeos import lgeos, GEOM_PTR
+from django.contrib.gis.geos.libgeos import GEOM_PTR
from django.contrib.gis.geos.prototypes.errcheck import check_dbl
+from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc
### ctypes generator function ###
def dbl_from_geom(func, num_geom=1):
@@ -22,6 +23,6 @@ def dbl_from_geom(func, num_geom=1):
### ctypes prototypes ###
# Area, distance, and length prototypes.
-geos_area = dbl_from_geom(lgeos.GEOSArea)
-geos_distance = dbl_from_geom(lgeos.GEOSDistance, num_geom=2)
-geos_length = dbl_from_geom(lgeos.GEOSLength)
+geos_area = dbl_from_geom(GEOSFunc('GEOSArea'))
+geos_distance = dbl_from_geom(GEOSFunc('GEOSDistance'), num_geom=2)
+geos_length = dbl_from_geom(GEOSFunc('GEOSLength'))
View
33 django/contrib/gis/geos/prototypes/predicates.py
@@ -3,8 +3,9 @@
unary and binary predicate operations on geometries.
"""
from ctypes import c_char, c_char_p, c_double
-from django.contrib.gis.geos.libgeos import lgeos, GEOM_PTR
+from django.contrib.gis.geos.libgeos import GEOM_PTR
from django.contrib.gis.geos.prototypes.errcheck import check_predicate
+from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc
## Binary & unary predicate functions ##
def binary_predicate(func, *args):
@@ -24,20 +25,20 @@ def unary_predicate(func):
return func
## Unary Predicates ##
-geos_hasz = unary_predicate(lgeos.GEOSHasZ)
-geos_isempty = unary_predicate(lgeos.GEOSisEmpty)
-geos_isring = unary_predicate(lgeos.GEOSisRing)
-geos_issimple = unary_predicate(lgeos.GEOSisSimple)
-geos_isvalid = unary_predicate(lgeos.GEOSisValid)
+geos_hasz = unary_predicate(GEOSFunc('GEOSHasZ'))
+geos_isempty = unary_predicate(GEOSFunc('GEOSisEmpty'))
+geos_isring = unary_predicate(GEOSFunc('GEOSisRing'))
+geos_issimple = unary_predicate(GEOSFunc('GEOSisSimple'))
+geos_isvalid = unary_predicate(GEOSFunc('GEOSisValid'))
## Binary Predicates ##
-geos_contains = binary_predicate(lgeos.GEOSContains)
-geos_crosses = binary_predicate(lgeos.GEOSCrosses)
-geos_disjoint = binary_predicate(lgeos.GEOSDisjoint)
-geos_equals = binary_predicate(lgeos.GEOSEquals)
-geos_equalsexact = binary_predicate(lgeos.GEOSEqualsExact, c_double)
-geos_intersects = binary_predicate(lgeos.GEOSIntersects)
-geos_overlaps = binary_predicate(lgeos.GEOSOverlaps)
-geos_relatepattern = binary_predicate(lgeos.GEOSRelatePattern, c_char_p)
-geos_touches = binary_predicate(lgeos.GEOSTouches)
-geos_within = binary_predicate(lgeos.GEOSWithin)
+geos_contains = binary_predicate(GEOSFunc('GEOSContains'))
+geos_crosses = binary_predicate(GEOSFunc('GEOSCrosses'))
+geos_disjoint = binary_predicate(GEOSFunc('GEOSDisjoint'))
+geos_equals = binary_predicate(GEOSFunc('GEOSEquals'))
+geos_equalsexact = binary_predicate(GEOSFunc('GEOSEqualsExact'), c_double)
+geos_intersects = binary_predicate(GEOSFunc('GEOSIntersects'))
+geos_overlaps = binary_predicate(GEOSFunc('GEOSOverlaps'))
+geos_relatepattern = binary_predicate(GEOSFunc('GEOSRelatePattern'), c_char_p)
+geos_touches = binary_predicate(GEOSFunc('GEOSTouches'))
+geos_within = binary_predicate(GEOSFunc('GEOSWithin'))
View
15 django/contrib/gis/geos/prototypes/prepared.py
@@ -1,13 +1,14 @@
from ctypes import c_char
-from django.contrib.gis.geos.libgeos import lgeos, GEOM_PTR, PREPGEOM_PTR
+from django.contrib.gis.geos.libgeos import GEOM_PTR, PREPGEOM_PTR
from django.contrib.gis.geos.prototypes.errcheck import check_predicate
+from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc
# Prepared geometry constructor and destructors.
-geos_prepare = lgeos.GEOSPrepare
+geos_prepare = GEOSFunc('GEOSPrepare')
geos_prepare.argtypes = [GEOM_PTR]
geos_prepare.restype = PREPGEOM_PTR
-prepared_destroy = lgeos.GEOSPreparedGeom_destroy
+prepared_destroy = GEOSFunc('GEOSPreparedGeom_destroy')
prepared_destroy.argtpes = [PREPGEOM_PTR]
prepared_destroy.restype = None
@@ -18,7 +19,7 @@ def prepared_predicate(func):
func.errcheck = check_predicate
return func
-prepared_contains = prepared_predicate(lgeos.GEOSPreparedContains)
-prepared_contains_properly = prepared_predicate(lgeos.GEOSPreparedContainsProperly)
-prepared_covers = prepared_predicate(lgeos.GEOSPreparedCovers)
-prepared_intersects = prepared_predicate(lgeos.GEOSPreparedIntersects)
+prepared_contains = prepared_predicate(GEOSFunc('GEOSPreparedContains'))
+prepared_contains_properly = prepared_predicate(GEOSFunc('GEOSPreparedContainsProperly'))
+prepared_covers = prepared_predicate(GEOSFunc('GEOSPreparedCovers'))
+prepared_intersects = prepared_predicate(GEOSFunc('GEOSPreparedIntersects'))
View
90 django/contrib/gis/geos/prototypes/threadsafe.py
@@ -0,0 +1,90 @@
+import threading
+from django.contrib.gis.geos.libgeos import lgeos, notice_h, error_h, CONTEXT_PTR
+
+class GEOSContextHandle(object):
+ """
+ Python object representing a GEOS context handle.
+ """
+ def __init__(self):
+ # Initializing the context handler for this thread with
+ # the notice and error handler.
+ self.ptr = lgeos.initGEOS_r(notice_h, error_h)
+
+ def __del__(self):
+ if self.ptr: lgeos.finishGEOS_r(self.ptr)
+
+# Defining a thread-local object and creating an instance
+# to hold a reference to GEOSContextHandle for this thread.
+class GEOSContext(threading.local):
+ handle = None
+
+thread_context = GEOSContext()
+
+def call_geos_threaded(cfunc, args):
+ """
+ This module-level routine calls the specified GEOS C thread-safe
+ function with the context for this current thread.
+ """
+ # If a context handle does not exist for this thread, initialize one.
+ if not thread_context.handle:
+ thread_context.handle = GEOSContextHandle()
+ # Call the threaded GEOS routine with pointer of the context handle
+ # as the first argument.
+ return cfunc(thread_context.handle.ptr, *args)
+
+class GEOSFunc(object):
+ """
+ Class that serves as a wrapper for GEOS C Functions, and will
+ use thread-safe function variants when available.
+ """
+ def __init__(self, func_name):
+ try:
+ # GEOS thread-safe function signatures end with '_r', and
+ # take an additional context handle parameter.
+ self.cfunc = getattr(lgeos, func_name + '_r')
+ self.threaded = True
+ except AttributeError:
+ # Otherwise, use usual function.
+ self.cfunc = getattr(lgeos, func_name)
+ self.threaded = False
+
+ def __call__(self, *args):
+ if self.threaded:
+ return call_geos_threaded(self.cfunc, args)
+ else:
+ return self.cfunc(*args)
+
+ def __str__(self):
+ return self.cfunc.__name__
+
+ # argtypes property
+ def _get_argtypes(self):
+ return self.cfunc.argtypes
+
+ def _set_argtypes(self, argtypes):
+ if self.threaded:
+ new_argtypes = [CONTEXT_PTR]
+ new_argtypes.extend(argtypes)
+ self.cfunc.argtypes = new_argtypes
+ else:
+ self.cfunc.argtypes = argtypes
+
+ argtypes = property(_get_argtypes, _set_argtypes)
+
+ # restype property
+ def _get_restype(self):
+ return self.cfunc.restype
+
+ def _set_restype(self, restype):
+ self.cfunc.restype = restype
+
+ restype = property(_get_restype, _set_restype)
+
+ # errcheck property
+ def _get_errcheck(self):
+ return self.cfunc.errcheck
+
+ def _set_errcheck(self, errcheck):
+ self.cfunc.errcheck = errcheck
+
+ errcheck = property(_get_errcheck, _set_errcheck)
View
33 django/contrib/gis/geos/prototypes/topology.py
@@ -8,9 +8,10 @@
'geos_simplify', 'geos_symdifference', 'geos_union', 'geos_relate']
from ctypes import c_char_p, c_double, c_int
-from django.contrib.gis.geos.libgeos import lgeos, GEOM_PTR, GEOS_PREPARE
+from django.contrib.gis.geos.libgeos import GEOM_PTR, GEOS_PREPARE
from django.contrib.gis.geos.prototypes.errcheck import check_geom, check_string
from django.contrib.gis.geos.prototypes.geom import geos_char_p
+from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc
def topology(func, *args):
"For GEOS unary topology functions."
@@ -22,29 +23,29 @@ def topology(func, *args):
return func
### Topology Routines ###
-geos_boundary = topology(lgeos.GEOSBoundary)
-geos_buffer = topology(lgeos.GEOSBuffer, c_double, c_int)
-geos_centroid = topology(lgeos.GEOSGetCentroid)
-geos_convexhull = topology(lgeos.GEOSConvexHull)
-geos_difference = topology(lgeos.GEOSDifference, GEOM_PTR)
-geos_envelope = topology(lgeos.GEOSEnvelope)
-geos_intersection = topology(lgeos.GEOSIntersection, GEOM_PTR)
-geos_linemerge = topology(lgeos.GEOSLineMerge)
-geos_pointonsurface = topology(lgeos.GEOSPointOnSurface)
-geos_preservesimplify = topology(lgeos.GEOSTopologyPreserveSimplify, c_double)
-geos_simplify = topology(lgeos.GEOSSimplify, c_double)
-geos_symdifference = topology(lgeos.GEOSSymDifference, GEOM_PTR)
-geos_union = topology(lgeos.GEOSUnion, GEOM_PTR)
+geos_boundary = topology(GEOSFunc('GEOSBoundary'))
+geos_buffer = topology(GEOSFunc('GEOSBuffer'), c_double, c_int)
+geos_centroid = topology(GEOSFunc('GEOSGetCentroid'))
+geos_convexhull = topology(GEOSFunc('GEOSConvexHull'))
+geos_difference = topology(GEOSFunc('GEOSDifference'), GEOM_PTR)
+geos_envelope = topology(GEOSFunc('GEOSEnvelope'))
+geos_intersection = topology(GEOSFunc('GEOSIntersection'), GEOM_PTR)
+geos_linemerge = topology(GEOSFunc('GEOSLineMerge'))
+geos_pointonsurface = topology(GEOSFunc('GEOSPointOnSurface'))
+geos_preservesimplify = topology(GEOSFunc('GEOSTopologyPreserveSimplify'), c_double)
+geos_simplify = topology(GEOSFunc('GEOSSimplify'), c_double)
+geos_symdifference = topology(GEOSFunc('GEOSSymDifference'), GEOM_PTR)
+geos_union = topology(GEOSFunc('GEOSUnion'), GEOM_PTR)
# GEOSRelate returns a string, not a geometry.
-geos_relate = lgeos.GEOSRelate
+geos_relate = GEOSFunc('GEOSRelate')
geos_relate.argtypes = [GEOM_PTR, GEOM_PTR]
geos_relate.restype = geos_char_p
geos_relate.errcheck = check_string
# Routines only in GEOS 3.1+
if GEOS_PREPARE:
- geos_cascaded_union = lgeos.GEOSUnionCascaded
+ geos_cascaded_union = GEOSFunc('GEOSUnionCascaded')
geos_cascaded_union.argtypes = [GEOM_PTR]
geos_cascaded_union.restype = GEOM_PTR
__all__.append('geos_cascaded_union')
Please sign in to comment.
Something went wrong with that request. Please try again.