4 when an error occurs in GEOS). |
4 when an error occurs in GEOS). |
5 |
5 |
6 This module also houses GEOS Pointer utilities, including |
6 This module also houses GEOS Pointer utilities, including |
7 get_pointer_arr(), and GEOM_PTR. |
7 get_pointer_arr(), and GEOM_PTR. |
8 """ |
8 """ |
9 import atexit, os, re, sys |
9 import os, re, sys |
10 from ctypes import c_char_p, Structure, CDLL, CFUNCTYPE, POINTER |
10 from ctypes import c_char_p, Structure, CDLL, CFUNCTYPE, POINTER |
11 from ctypes.util import find_library |
11 from ctypes.util import find_library |
12 from django.contrib.gis.geos.error import GEOSException |
12 from django.contrib.gis.geos.error import GEOSException |
13 |
13 |
14 # Custom library path set? |
14 # Custom library path set? |
43 raise ImportError('Could not find the GEOS library (tried "%s"). ' |
43 raise ImportError('Could not find the GEOS library (tried "%s"). ' |
44 'Try setting GEOS_LIBRARY_PATH in your settings.' % |
44 'Try setting GEOS_LIBRARY_PATH in your settings.' % |
45 '", "'.join(lib_names)) |
45 '", "'.join(lib_names)) |
46 |
46 |
47 # Getting the GEOS C library. The C interface (CDLL) is used for |
47 # Getting the GEOS C library. The C interface (CDLL) is used for |
48 # both *NIX and Windows. |
48 # both *NIX and Windows. |
49 # See the GEOS C API source code for more details on the library function calls: |
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 |
50 # http://geos.refractions.net/ro/doxygen_docs/html/geos__c_8h-source.html |
51 lgeos = CDLL(lib_path) |
51 lgeos = CDLL(lib_path) |
52 |
52 |
53 # The notice and error handler C function callback definitions. |
53 # The notice and error handler C function callback definitions. |
54 # Supposed to mimic the GEOS message handler (C below): |
54 # Supposed to mimic the GEOS message handler (C below): |
55 # "typedef void (*GEOSMessageHandler)(const char *fmt, ...);" |
55 # typedef void (*GEOSMessageHandler)(const char *fmt, ...); |
56 NOTICEFUNC = CFUNCTYPE(None, c_char_p, c_char_p) |
56 NOTICEFUNC = CFUNCTYPE(None, c_char_p, c_char_p) |
57 def notice_h(fmt, lst, output_h=sys.stdout): |
57 def notice_h(fmt, lst, output_h=sys.stdout): |
58 try: |
58 try: |
59 warn_msg = fmt % lst |
59 warn_msg = fmt % lst |
60 except: |
60 except: |
69 except: |
69 except: |
70 err_msg = fmt |
70 err_msg = fmt |
71 output_h.write('GEOS_ERROR: %s\n' % err_msg) |
71 output_h.write('GEOS_ERROR: %s\n' % err_msg) |
72 error_h = ERRORFUNC(error_h) |
72 error_h = ERRORFUNC(error_h) |
73 |
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. #### |
74 #### GEOS Geometry C data structures, and utility functions. #### |
81 |
75 |
82 # Opaque GEOS geometry structures, used for GEOM_PTR and CS_PTR |
76 # Opaque GEOS geometry structures, used for GEOM_PTR and CS_PTR |
83 class GEOSGeom_t(Structure): pass |
77 class GEOSGeom_t(Structure): pass |
84 class GEOSPrepGeom_t(Structure): pass |
78 class GEOSPrepGeom_t(Structure): pass |
85 class GEOSCoordSeq_t(Structure): pass |
79 class GEOSCoordSeq_t(Structure): pass |
|
80 class GEOSContextHandle_t(Structure): pass |
86 |
81 |
87 # Pointers to opaque GEOS geometry structures. |
82 # Pointers to opaque GEOS geometry structures. |
88 GEOM_PTR = POINTER(GEOSGeom_t) |
83 GEOM_PTR = POINTER(GEOSGeom_t) |
89 PREPGEOM_PTR = POINTER(GEOSPrepGeom_t) |
84 PREPGEOM_PTR = POINTER(GEOSPrepGeom_t) |
90 CS_PTR = POINTER(GEOSCoordSeq_t) |
85 CS_PTR = POINTER(GEOSCoordSeq_t) |
|
86 CONTEXT_PTR = POINTER(GEOSContextHandle_t) |
91 |
87 |
92 # Used specifically by the GEOSGeom_createPolygon and GEOSGeom_createCollection |
88 # Used specifically by the GEOSGeom_createPolygon and GEOSGeom_createCollection |
93 # GEOS routines |
89 # GEOS routines |
94 def get_pointer_arr(n): |
90 def get_pointer_arr(n): |
95 "Gets a ctypes pointer array (of length `n`) for GEOSGeom_t opaque pointer." |
91 "Gets a ctypes pointer array (of length `n`) for GEOSGeom_t opaque pointer." |
102 geos_version.argtypes = None |
98 geos_version.argtypes = None |
103 geos_version.restype = c_char_p |
99 geos_version.restype = c_char_p |
104 |
100 |
105 # Regular expression should be able to parse version strings such as |
101 # 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' |
102 # '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+)$') |
103 version_regex = re.compile(r'^(?P<version>(?P<major>\d+)\.(?P<minor>\d+)\.(?P<subminor>\d+))(rc(?P<release_candidate>\d+))?-CAPI-(?P<capi_version>\d+\.\d+\.\d+)$') |
108 def geos_version_info(): |
104 def geos_version_info(): |
109 """ |
105 """ |
110 Returns a dictionary containing the various version metadata parsed from |
106 Returns a dictionary containing the various version metadata parsed from |
111 the GEOS version string, including the version number, whether the version |
107 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 |
108 is a release candidate (and what number release candidate), and the C API |
113 version. |
109 version. |
114 """ |
110 """ |
115 ver = geos_version() |
111 ver = geos_version() |
116 m = version_regex.match(ver) |
112 m = version_regex.match(ver) |
117 if not m: raise GEOSException('Could not parse version info string "%s"' % ver) |
113 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')) |
114 return dict((key, m.group(key)) for key in ('version', 'release_candidate', 'capi_version', 'major', 'minor', 'subminor')) |
119 |
115 |
120 # Version numbers and whether or not prepared geometry support is available. |
116 # Version numbers and whether or not prepared geometry support is available. |
121 _verinfo = geos_version_info() |
117 _verinfo = geos_version_info() |
122 GEOS_MAJOR_VERSION = int(_verinfo['major']) |
118 GEOS_MAJOR_VERSION = int(_verinfo['major']) |
123 GEOS_MINOR_VERSION = int(_verinfo['minor']) |
119 GEOS_MINOR_VERSION = int(_verinfo['minor']) |
|
120 GEOS_SUBMINOR_VERSION = int(_verinfo['subminor']) |
124 del _verinfo |
121 del _verinfo |
125 GEOS_PREPARE = GEOS_MAJOR_VERSION > 3 or GEOS_MAJOR_VERSION == 3 and GEOS_MINOR_VERSION >= 1 |
122 GEOS_VERSION = (GEOS_MAJOR_VERSION, GEOS_MINOR_VERSION, GEOS_SUBMINOR_VERSION) |
|
123 GEOS_PREPARE = GEOS_VERSION >= (3, 1, 0) |
126 |
124 |
127 # Calling the finishGEOS() upon exit of the interpreter. |
125 if GEOS_PREPARE: |
128 atexit.register(lgeos.finishGEOS) |
126 # Here we set up the prototypes for the initGEOS_r and finishGEOS_r |
|
127 # routines. These functions aren't actually called until they are |
|
128 # attached to a GEOS context handle -- this actually occurs in |
|
129 # geos/prototypes/threadsafe.py. |
|
130 lgeos.initGEOS_r.restype = CONTEXT_PTR |
|
131 lgeos.finishGEOS_r.argtypes = [CONTEXT_PTR] |
|
132 else: |
|
133 # When thread-safety isn't available, the initGEOS routine must be called |
|
134 # first. This function takes the notice and error functions, defined |
|
135 # as Python callbacks above, as parameters. Here is the C code that is |
|
136 # wrapped: |
|
137 # extern void GEOS_DLL initGEOS(GEOSMessageHandler notice_function, GEOSMessageHandler error_function); |
|
138 lgeos.initGEOS(notice_h, error_h) |
|
139 # Calling finishGEOS() upon exit of the interpreter. |
|
140 import atexit |
|
141 atexit.register(lgeos.finishGEOS) |