field.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. from ctypes import byref, c_int
  2. from datetime import date, datetime, time
  3. from django.contrib.gis.gdal.base import GDALBase
  4. from django.contrib.gis.gdal.error import GDALException
  5. from django.contrib.gis.gdal.prototypes import ds as capi
  6. from django.utils.encoding import force_str
  7. # For more information, see the OGR C API source code:
  8. # https://gdal.org/api/vector_c_api.html
  9. #
  10. # The OGR_Fld_* routines are relevant here.
  11. class Field(GDALBase):
  12. """
  13. Wrap an OGR Field. Needs to be instantiated from a Feature object.
  14. """
  15. def __init__(self, feat, index):
  16. """
  17. Initialize on the feature object and the integer index of
  18. the field within the feature.
  19. """
  20. # Setting the feature pointer and index.
  21. self._feat = feat
  22. self._index = index
  23. # Getting the pointer for this field.
  24. fld_ptr = capi.get_feat_field_defn(feat.ptr, index)
  25. if not fld_ptr:
  26. raise GDALException("Cannot create OGR Field, invalid pointer given.")
  27. self.ptr = fld_ptr
  28. # Setting the class depending upon the OGR Field Type (OFT)
  29. self.__class__ = OGRFieldTypes[self.type]
  30. def __str__(self):
  31. "Return the string representation of the Field."
  32. return str(self.value).strip()
  33. # #### Field Methods ####
  34. def as_double(self):
  35. "Retrieve the Field's value as a double (float)."
  36. return (
  37. capi.get_field_as_double(self._feat.ptr, self._index)
  38. if self.is_set
  39. else None
  40. )
  41. def as_int(self, is_64=False):
  42. "Retrieve the Field's value as an integer."
  43. if is_64:
  44. return (
  45. capi.get_field_as_integer64(self._feat.ptr, self._index)
  46. if self.is_set
  47. else None
  48. )
  49. else:
  50. return (
  51. capi.get_field_as_integer(self._feat.ptr, self._index)
  52. if self.is_set
  53. else None
  54. )
  55. def as_string(self):
  56. "Retrieve the Field's value as a string."
  57. if not self.is_set:
  58. return None
  59. string = capi.get_field_as_string(self._feat.ptr, self._index)
  60. return force_str(string, encoding=self._feat.encoding, strings_only=True)
  61. def as_datetime(self):
  62. "Retrieve the Field's value as a tuple of date & time components."
  63. if not self.is_set:
  64. return None
  65. yy, mm, dd, hh, mn, ss, tz = [c_int() for i in range(7)]
  66. status = capi.get_field_as_datetime(
  67. self._feat.ptr,
  68. self._index,
  69. byref(yy),
  70. byref(mm),
  71. byref(dd),
  72. byref(hh),
  73. byref(mn),
  74. byref(ss),
  75. byref(tz),
  76. )
  77. if status:
  78. return (yy, mm, dd, hh, mn, ss, tz)
  79. else:
  80. raise GDALException(
  81. "Unable to retrieve date & time information from the field."
  82. )
  83. # #### Field Properties ####
  84. @property
  85. def is_set(self):
  86. "Return True if the value of this field isn't null, False otherwise."
  87. return capi.is_field_set(self._feat.ptr, self._index)
  88. @property
  89. def name(self):
  90. "Return the name of this Field."
  91. name = capi.get_field_name(self.ptr)
  92. return force_str(name, encoding=self._feat.encoding, strings_only=True)
  93. @property
  94. def precision(self):
  95. "Return the precision of this Field."
  96. return capi.get_field_precision(self.ptr)
  97. @property
  98. def type(self):
  99. "Return the OGR type of this Field."
  100. return capi.get_field_type(self.ptr)
  101. @property
  102. def type_name(self):
  103. "Return the OGR field type name for this Field."
  104. return capi.get_field_type_name(self.type)
  105. @property
  106. def value(self):
  107. "Return the value of this Field."
  108. # Default is to get the field as a string.
  109. return self.as_string()
  110. @property
  111. def width(self):
  112. "Return the width of this Field."
  113. return capi.get_field_width(self.ptr)
  114. # ### The Field sub-classes for each OGR Field type. ###
  115. class OFTInteger(Field):
  116. _bit64 = False
  117. @property
  118. def value(self):
  119. "Return an integer contained in this field."
  120. return self.as_int(self._bit64)
  121. @property
  122. def type(self):
  123. """
  124. GDAL uses OFTReals to represent OFTIntegers in created
  125. shapefiles -- forcing the type here since the underlying field
  126. type may actually be OFTReal.
  127. """
  128. return 0
  129. class OFTReal(Field):
  130. @property
  131. def value(self):
  132. "Return a float contained in this field."
  133. return self.as_double()
  134. # String & Binary fields, just subclasses
  135. class OFTString(Field):
  136. pass
  137. class OFTWideString(Field):
  138. pass
  139. class OFTBinary(Field):
  140. pass
  141. # OFTDate, OFTTime, OFTDateTime fields.
  142. class OFTDate(Field):
  143. @property
  144. def value(self):
  145. "Return a Python `date` object for the OFTDate field."
  146. try:
  147. yy, mm, dd, hh, mn, ss, tz = self.as_datetime()
  148. return date(yy.value, mm.value, dd.value)
  149. except (TypeError, ValueError, GDALException):
  150. return None
  151. class OFTDateTime(Field):
  152. @property
  153. def value(self):
  154. "Return a Python `datetime` object for this OFTDateTime field."
  155. # TODO: Adapt timezone information.
  156. # See https://lists.osgeo.org/pipermail/gdal-dev/2006-February/007990.html
  157. # The `tz` variable has values of: 0=unknown, 1=localtime (ambiguous),
  158. # 100=GMT, 104=GMT+1, 80=GMT-5, etc.
  159. try:
  160. yy, mm, dd, hh, mn, ss, tz = self.as_datetime()
  161. return datetime(yy.value, mm.value, dd.value, hh.value, mn.value, ss.value)
  162. except (TypeError, ValueError, GDALException):
  163. return None
  164. class OFTTime(Field):
  165. @property
  166. def value(self):
  167. "Return a Python `time` object for this OFTTime field."
  168. try:
  169. yy, mm, dd, hh, mn, ss, tz = self.as_datetime()
  170. return time(hh.value, mn.value, ss.value)
  171. except (ValueError, GDALException):
  172. return None
  173. class OFTInteger64(OFTInteger):
  174. _bit64 = True
  175. # List fields are also just subclasses
  176. class OFTIntegerList(Field):
  177. pass
  178. class OFTRealList(Field):
  179. pass
  180. class OFTStringList(Field):
  181. pass
  182. class OFTWideStringList(Field):
  183. pass
  184. class OFTInteger64List(Field):
  185. pass
  186. # Class mapping dictionary for OFT Types and reverse mapping.
  187. OGRFieldTypes = {
  188. 0: OFTInteger,
  189. 1: OFTIntegerList,
  190. 2: OFTReal,
  191. 3: OFTRealList,
  192. 4: OFTString,
  193. 5: OFTStringList,
  194. 6: OFTWideString,
  195. 7: OFTWideStringList,
  196. 8: OFTBinary,
  197. 9: OFTDate,
  198. 10: OFTTime,
  199. 11: OFTDateTime,
  200. 12: OFTInteger64,
  201. 13: OFTInteger64List,
  202. }
  203. ROGRFieldTypes = {cls: num for num, cls in OGRFieldTypes.items()}