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 class WKTWriter_st(Structure): pass class WKBReader_st(Structure): pass class WKBWriter_st(Structure): pass WKT_READ_PTR = POINTER(WKTReader_st) WKT_WRITE_PTR = POINTER(WKTWriter_st) WKB_READ_PTR = POINTER(WKBReader_st) WKB_WRITE_PTR = POINTER(WKBReader_st) ### WKTReader routines ### wkt_reader_create = GEOSFunc('GEOSWKTReader_create') wkt_reader_create.restype = WKT_READ_PTR wkt_reader_destroy = GEOSFunc('GEOSWKTReader_destroy') wkt_reader_destroy.argtypes = [WKT_READ_PTR] 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 = GEOSFunc('GEOSWKTWriter_create') wkt_writer_create.restype = WKT_WRITE_PTR wkt_writer_destroy = GEOSFunc('GEOSWKTWriter_destroy') wkt_writer_destroy.argtypes = [WKT_WRITE_PTR] 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 = GEOSFunc('GEOSWKBReader_create') wkb_reader_create.restype = WKB_READ_PTR wkb_reader_destroy = GEOSFunc('GEOSWKBReader_destroy') wkb_reader_destroy.argtypes = [WKB_READ_PTR] def wkb_read_func(func): # Although the function definitions take `const unsigned char *` # as their parameter, we use c_char_p here so the function may # take Python strings directly as parameters. Inside Python there # is not a difference between signed and unsigned characters, so # it is not a problem. func.argtypes = [WKB_READ_PTR, c_char_p, c_size_t] func.restype = GEOM_PTR func.errcheck = check_geom return func wkb_reader_read = wkb_read_func(GEOSFunc('GEOSWKBReader_read')) wkb_reader_read_hex = wkb_read_func(GEOSFunc('GEOSWKBReader_readHEX')) ### WKBWriter routines ### wkb_writer_create = GEOSFunc('GEOSWKBWriter_create') wkb_writer_create.restype = WKB_WRITE_PTR wkb_writer_destroy = GEOSFunc('GEOSWKBWriter_destroy') wkb_writer_destroy.argtypes = [WKB_WRITE_PTR] # WKB Writing prototypes. def wkb_write_func(func): func.argtypes = [WKB_WRITE_PTR, GEOM_PTR, POINTER(c_size_t)] func.restype = c_uchar_p func.errcheck = check_sized_string return func 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): func.argtypes = [WKB_WRITE_PTR] func.restype = restype return func def wkb_writer_set(func, argtype=c_int): func.argtypes = [WKB_WRITE_PTR, argtype] return func 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