web/lib/django/contrib/gis/geos/libgeos.py
changeset 38 77b6da96e6f1
equal deleted inserted replaced
37:8d941af65caf 38:77b6da96e6f1
       
     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 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 #### GEOS Geometry C data structures, and utility functions. ####
       
    75 
       
    76 # Opaque GEOS geometry structures, used for GEOM_PTR and CS_PTR
       
    77 class GEOSGeom_t(Structure): pass
       
    78 class GEOSPrepGeom_t(Structure): pass
       
    79 class GEOSCoordSeq_t(Structure): pass
       
    80 class GEOSContextHandle_t(Structure): pass
       
    81 
       
    82 # Pointers to opaque GEOS geometry structures.
       
    83 GEOM_PTR = POINTER(GEOSGeom_t)
       
    84 PREPGEOM_PTR = POINTER(GEOSPrepGeom_t)
       
    85 CS_PTR = POINTER(GEOSCoordSeq_t)
       
    86 CONTEXT_PTR  = POINTER(GEOSContextHandle_t)
       
    87 
       
    88 # Used specifically by the GEOSGeom_createPolygon and GEOSGeom_createCollection
       
    89 #  GEOS routines
       
    90 def get_pointer_arr(n):
       
    91     "Gets a ctypes pointer array (of length `n`) for GEOSGeom_t opaque pointer."
       
    92     GeomArr = GEOM_PTR * n
       
    93     return GeomArr()
       
    94 
       
    95 # Returns the string version of the GEOS library. Have to set the restype
       
    96 # explicitly to c_char_p to ensure compatibility accross 32 and 64-bit platforms.
       
    97 geos_version = lgeos.GEOSversion
       
    98 geos_version.argtypes = None
       
    99 geos_version.restype = c_char_p
       
   100 
       
   101 # Regular expression should be able to parse version strings such as
       
   102 # '3.0.0rc4-CAPI-1.3.3', or '3.0.0-CAPI-1.4.1'
       
   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+)$')
       
   104 def geos_version_info():
       
   105     """
       
   106     Returns a dictionary containing the various version metadata parsed from
       
   107     the GEOS version string, including the version number, whether the version
       
   108     is a release candidate (and what number release candidate), and the C API
       
   109     version.
       
   110     """
       
   111     ver = geos_version()
       
   112     m = version_regex.match(ver)
       
   113     if not m: raise GEOSException('Could not parse version info string "%s"' % ver)
       
   114     return dict((key, m.group(key)) for key in ('version', 'release_candidate', 'capi_version', 'major', 'minor', 'subminor'))
       
   115 
       
   116 # Version numbers and whether or not prepared geometry support is available.
       
   117 _verinfo = geos_version_info()
       
   118 GEOS_MAJOR_VERSION = int(_verinfo['major'])
       
   119 GEOS_MINOR_VERSION = int(_verinfo['minor'])
       
   120 GEOS_SUBMINOR_VERSION = int(_verinfo['subminor'])
       
   121 del _verinfo
       
   122 GEOS_VERSION = (GEOS_MAJOR_VERSION, GEOS_MINOR_VERSION, GEOS_SUBMINOR_VERSION)
       
   123 GEOS_PREPARE = GEOS_VERSION >= (3, 1, 0)
       
   124 
       
   125 if GEOS_PREPARE:
       
   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)