You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

libgeos.py 5.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  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. This module also houses GEOS Pointer utilities, including
  6. get_pointer_arr(), and GEOM_PTR.
  7. """
  8. import logging
  9. import os
  10. from ctypes import CDLL, CFUNCTYPE, POINTER, Structure, c_char_p
  11. from ctypes.util import find_library
  12. from django.core.exceptions import ImproperlyConfigured
  13. from django.utils.functional import SimpleLazyObject, cached_property
  14. from django.utils.version import get_version_tuple
  15. logger = logging.getLogger('django.contrib.gis')
  16. def load_geos():
  17. # Custom library path set?
  18. try:
  19. from django.conf import settings
  20. lib_path = settings.GEOS_LIBRARY_PATH
  21. except (AttributeError, EnvironmentError,
  22. ImportError, ImproperlyConfigured):
  23. lib_path = None
  24. # Setting the appropriate names for the GEOS-C library.
  25. if lib_path:
  26. lib_names = None
  27. elif os.name == 'nt':
  28. # Windows NT libraries
  29. lib_names = ['geos_c', 'libgeos_c-1']
  30. elif os.name == 'posix':
  31. # *NIX libraries
  32. lib_names = ['geos_c', 'GEOS']
  33. else:
  34. raise ImportError('Unsupported OS "%s"' % os.name)
  35. # Using the ctypes `find_library` utility to find the path to the GEOS
  36. # shared library. This is better than manually specifying each library name
  37. # and extension (e.g., libgeos_c.[so|so.1|dylib].).
  38. if lib_names:
  39. for lib_name in lib_names:
  40. lib_path = find_library(lib_name)
  41. if lib_path is not None:
  42. break
  43. # No GEOS library could be found.
  44. if lib_path is None:
  45. raise ImportError(
  46. 'Could not find the GEOS library (tried "%s"). '
  47. 'Try setting GEOS_LIBRARY_PATH in your settings.' %
  48. '", "'.join(lib_names)
  49. )
  50. # Getting the GEOS C library. The C interface (CDLL) is used for
  51. # both *NIX and Windows.
  52. # See the GEOS C API source code for more details on the library function calls:
  53. # http://geos.refractions.net/ro/doxygen_docs/html/geos__c_8h-source.html
  54. _lgeos = CDLL(lib_path)
  55. # Here we set up the prototypes for the initGEOS_r and finishGEOS_r
  56. # routines. These functions aren't actually called until they are
  57. # attached to a GEOS context handle -- this actually occurs in
  58. # geos/prototypes/threadsafe.py.
  59. _lgeos.initGEOS_r.restype = CONTEXT_PTR
  60. _lgeos.finishGEOS_r.argtypes = [CONTEXT_PTR]
  61. # Set restype for compatibility across 32 and 64-bit platforms.
  62. _lgeos.GEOSversion.restype = c_char_p
  63. return _lgeos
  64. # The notice and error handler C function callback definitions.
  65. # Supposed to mimic the GEOS message handler (C below):
  66. # typedef void (*GEOSMessageHandler)(const char *fmt, ...);
  67. NOTICEFUNC = CFUNCTYPE(None, c_char_p, c_char_p)
  68. def notice_h(fmt, lst):
  69. fmt, lst = fmt.decode(), lst.decode()
  70. try:
  71. warn_msg = fmt % lst
  72. except TypeError:
  73. warn_msg = fmt
  74. logger.warning('GEOS_NOTICE: %s\n', warn_msg)
  75. notice_h = NOTICEFUNC(notice_h)
  76. ERRORFUNC = CFUNCTYPE(None, c_char_p, c_char_p)
  77. def error_h(fmt, lst):
  78. fmt, lst = fmt.decode(), lst.decode()
  79. try:
  80. err_msg = fmt % lst
  81. except TypeError:
  82. err_msg = fmt
  83. logger.error('GEOS_ERROR: %s\n', err_msg)
  84. error_h = ERRORFUNC(error_h)
  85. # #### GEOS Geometry C data structures, and utility functions. ####
  86. # Opaque GEOS geometry structures, used for GEOM_PTR and CS_PTR
  87. class GEOSGeom_t(Structure):
  88. pass
  89. class GEOSPrepGeom_t(Structure):
  90. pass
  91. class GEOSCoordSeq_t(Structure):
  92. pass
  93. class GEOSContextHandle_t(Structure):
  94. pass
  95. # Pointers to opaque GEOS geometry structures.
  96. GEOM_PTR = POINTER(GEOSGeom_t)
  97. PREPGEOM_PTR = POINTER(GEOSPrepGeom_t)
  98. CS_PTR = POINTER(GEOSCoordSeq_t)
  99. CONTEXT_PTR = POINTER(GEOSContextHandle_t)
  100. lgeos = SimpleLazyObject(load_geos)
  101. class GEOSFuncFactory:
  102. """
  103. Lazy loading of GEOS functions.
  104. """
  105. argtypes = None
  106. restype = None
  107. errcheck = None
  108. def __init__(self, func_name, *args, restype=None, errcheck=None, argtypes=None, **kwargs):
  109. self.func_name = func_name
  110. if restype is not None:
  111. self.restype = restype
  112. if errcheck is not None:
  113. self.errcheck = errcheck
  114. if argtypes is not None:
  115. self.argtypes = argtypes
  116. self.args = args
  117. self.kwargs = kwargs
  118. def __call__(self, *args, **kwargs):
  119. return self.func(*args, **kwargs)
  120. @cached_property
  121. def func(self):
  122. from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc
  123. func = GEOSFunc(self.func_name)
  124. func.argtypes = self.argtypes or []
  125. func.restype = self.restype
  126. if self.errcheck:
  127. func.errcheck = self.errcheck
  128. return func
  129. def geos_version():
  130. """Return the string version of the GEOS library."""
  131. return lgeos.GEOSversion()
  132. def geos_version_tuple():
  133. """Return the GEOS version as a tuple (major, minor, subminor)."""
  134. return get_version_tuple(geos_version().decode())