|
0
|
1 |
""" |
|
|
2 |
This module houses the ctypes initialization procedures, as well |
|
|
3 |
as the notice and error handler function callbacks (get called |
|
|
4 |
when an error occurs in GEOS). |
|
|
5 |
|
|
|
6 |
This module also houses GEOS Pointer utilities, including |
|
|
7 |
get_pointer_arr(), and GEOM_PTR. |
|
|
8 |
""" |
|
|
9 |
import atexit, os, re, sys |
|
|
10 |
from ctypes import c_char_p, Structure, CDLL, CFUNCTYPE, POINTER |
|
|
11 |
from ctypes.util import find_library |
|
|
12 |
from django.contrib.gis.geos.error import GEOSException |
|
|
13 |
|
|
|
14 |
# Custom library path set? |
|
|
15 |
try: |
|
|
16 |
from django.conf import settings |
|
|
17 |
lib_path = settings.GEOS_LIBRARY_PATH |
|
|
18 |
except (AttributeError, EnvironmentError, ImportError): |
|
|
19 |
lib_path = None |
|
|
20 |
|
|
|
21 |
# Setting the appropriate names for the GEOS-C library. |
|
|
22 |
if lib_path: |
|
|
23 |
lib_names = None |
|
|
24 |
elif os.name == 'nt': |
|
|
25 |
# Windows NT libraries |
|
|
26 |
lib_names = ['libgeos_c-1'] |
|
|
27 |
elif os.name == 'posix': |
|
|
28 |
# *NIX libraries |
|
|
29 |
lib_names = ['geos_c', 'GEOS'] |
|
|
30 |
else: |
|
|
31 |
raise ImportError('Unsupported OS "%s"' % os.name) |
|
|
32 |
|
|
|
33 |
# Using the ctypes `find_library` utility to find the path to the GEOS |
|
|
34 |
# shared library. This is better than manually specifiying each library name |
|
|
35 |
# and extension (e.g., libgeos_c.[so|so.1|dylib].). |
|
|
36 |
if lib_names: |
|
|
37 |
for lib_name in lib_names: |
|
|
38 |
lib_path = find_library(lib_name) |
|
|
39 |
if not lib_path is None: break |
|
|
40 |
|
|
|
41 |
# No GEOS library could be found. |
|
|
42 |
if lib_path is None: |
|
|
43 |
raise ImportError('Could not find the GEOS library (tried "%s"). ' |
|
|
44 |
'Try setting GEOS_LIBRARY_PATH in your settings.' % |
|
|
45 |
'", "'.join(lib_names)) |
|
|
46 |
|
|
|
47 |
# Getting the GEOS C library. The C interface (CDLL) is used for |
|
|
48 |
# both *NIX and Windows. |
|
|
49 |
# See the GEOS C API source code for more details on the library function calls: |
|
|
50 |
# http://geos.refractions.net/ro/doxygen_docs/html/geos__c_8h-source.html |
|
|
51 |
lgeos = CDLL(lib_path) |
|
|
52 |
|
|
|
53 |
# The notice and error handler C function callback definitions. |
|
|
54 |
# Supposed to mimic the GEOS message handler (C below): |
|
|
55 |
# "typedef void (*GEOSMessageHandler)(const char *fmt, ...);" |
|
|
56 |
NOTICEFUNC = CFUNCTYPE(None, c_char_p, c_char_p) |
|
|
57 |
def notice_h(fmt, lst, output_h=sys.stdout): |
|
|
58 |
try: |
|
|
59 |
warn_msg = fmt % lst |
|
|
60 |
except: |
|
|
61 |
warn_msg = fmt |
|
|
62 |
output_h.write('GEOS_NOTICE: %s\n' % warn_msg) |
|
|
63 |
notice_h = NOTICEFUNC(notice_h) |
|
|
64 |
|
|
|
65 |
ERRORFUNC = CFUNCTYPE(None, c_char_p, c_char_p) |
|
|
66 |
def error_h(fmt, lst, output_h=sys.stderr): |
|
|
67 |
try: |
|
|
68 |
err_msg = fmt % lst |
|
|
69 |
except: |
|
|
70 |
err_msg = fmt |
|
|
71 |
output_h.write('GEOS_ERROR: %s\n' % err_msg) |
|
|
72 |
error_h = ERRORFUNC(error_h) |
|
|
73 |
|
|
|
74 |
# The initGEOS routine should be called first, however, that routine takes |
|
|
75 |
# the notice and error functions as parameters. Here is the C code that |
|
|
76 |
# is wrapped: |
|
|
77 |
# "extern void GEOS_DLL initGEOS(GEOSMessageHandler notice_function, GEOSMessageHandler error_function);" |
|
|
78 |
lgeos.initGEOS(notice_h, error_h) |
|
|
79 |
|
|
|
80 |
#### GEOS Geometry C data structures, and utility functions. #### |
|
|
81 |
|
|
|
82 |
# Opaque GEOS geometry structures, used for GEOM_PTR and CS_PTR |
|
|
83 |
class GEOSGeom_t(Structure): pass |
|
|
84 |
class GEOSPrepGeom_t(Structure): pass |
|
|
85 |
class GEOSCoordSeq_t(Structure): pass |
|
|
86 |
|
|
|
87 |
# Pointers to opaque GEOS geometry structures. |
|
|
88 |
GEOM_PTR = POINTER(GEOSGeom_t) |
|
|
89 |
PREPGEOM_PTR = POINTER(GEOSPrepGeom_t) |
|
|
90 |
CS_PTR = POINTER(GEOSCoordSeq_t) |
|
|
91 |
|
|
|
92 |
# Used specifically by the GEOSGeom_createPolygon and GEOSGeom_createCollection |
|
|
93 |
# GEOS routines |
|
|
94 |
def get_pointer_arr(n): |
|
|
95 |
"Gets a ctypes pointer array (of length `n`) for GEOSGeom_t opaque pointer." |
|
|
96 |
GeomArr = GEOM_PTR * n |
|
|
97 |
return GeomArr() |
|
|
98 |
|
|
|
99 |
# Returns the string version of the GEOS library. Have to set the restype |
|
|
100 |
# explicitly to c_char_p to ensure compatibility accross 32 and 64-bit platforms. |
|
|
101 |
geos_version = lgeos.GEOSversion |
|
|
102 |
geos_version.argtypes = None |
|
|
103 |
geos_version.restype = c_char_p |
|
|
104 |
|
|
|
105 |
# Regular expression should be able to parse version strings such as |
|
|
106 |
# '3.0.0rc4-CAPI-1.3.3', or '3.0.0-CAPI-1.4.1' |
|
|
107 |
version_regex = re.compile(r'^(?P<version>(?P<major>\d+)\.(?P<minor>\d+)\.\d+)(rc(?P<release_candidate>\d+))?-CAPI-(?P<capi_version>\d+\.\d+\.\d+)$') |
|
|
108 |
def geos_version_info(): |
|
|
109 |
""" |
|
|
110 |
Returns a dictionary containing the various version metadata parsed from |
|
|
111 |
the GEOS version string, including the version number, whether the version |
|
|
112 |
is a release candidate (and what number release candidate), and the C API |
|
|
113 |
version. |
|
|
114 |
""" |
|
|
115 |
ver = geos_version() |
|
|
116 |
m = version_regex.match(ver) |
|
|
117 |
if not m: raise GEOSException('Could not parse version info string "%s"' % ver) |
|
|
118 |
return dict((key, m.group(key)) for key in ('version', 'release_candidate', 'capi_version', 'major', 'minor')) |
|
|
119 |
|
|
|
120 |
# Version numbers and whether or not prepared geometry support is available. |
|
|
121 |
_verinfo = geos_version_info() |
|
|
122 |
GEOS_MAJOR_VERSION = int(_verinfo['major']) |
|
|
123 |
GEOS_MINOR_VERSION = int(_verinfo['minor']) |
|
|
124 |
del _verinfo |
|
|
125 |
GEOS_PREPARE = GEOS_MAJOR_VERSION > 3 or GEOS_MAJOR_VERSION == 3 and GEOS_MINOR_VERSION >= 1 |
|
|
126 |
|
|
|
127 |
# Calling the finishGEOS() upon exit of the interpreter. |
|
|
128 |
atexit.register(lgeos.finishGEOS) |