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