field.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  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://www.gdal.org/ogr__api_8h.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 capi.get_field_as_double(self._feat.ptr, self._index) if self.is_set else None
  37. def as_int(self, is_64=False):
  38. "Retrieve the Field's value as an integer."
  39. if is_64:
  40. return capi.get_field_as_integer64(self._feat.ptr, self._index) if self.is_set else None
  41. else:
  42. return capi.get_field_as_integer(self._feat.ptr, self._index) if self.is_set else None
  43. def as_string(self):
  44. "Retrieve the Field's value as a string."
  45. if not self.is_set:
  46. return None
  47. string = capi.get_field_as_string(self._feat.ptr, self._index)
  48. return force_str(string, encoding=self._feat.encoding, strings_only=True)
  49. def as_datetime(self):
  50. "Retrieve the Field's value as a tuple of date & time components."
  51. if not self.is_set:
  52. return None
  53. yy, mm, dd, hh, mn, ss, tz = [c_int() for i in range(7)]
  54. status = capi.get_field_as_datetime(
  55. self._feat.ptr, self._index, byref(yy), byref(mm), byref(dd),
  56. byref(hh), byref(mn), byref(ss), byref(tz))
  57. if status:
  58. return (yy, mm, dd, hh, mn, ss, tz)
  59. else:
  60. raise GDALException('Unable to retrieve date & time information from the field.')
  61. # #### Field Properties ####
  62. @property
  63. def is_set(self):
  64. "Return True if the value of this field isn't null, False otherwise."
  65. return capi.is_field_set(self._feat.ptr, self._index)
  66. @property
  67. def name(self):
  68. "Return the name of this Field."
  69. name = capi.get_field_name(self.ptr)
  70. return force_str(name, encoding=self._feat.encoding, strings_only=True)
  71. @property
  72. def precision(self):
  73. "Return the precision of this Field."
  74. return capi.get_field_precision(self.ptr)
  75. @property
  76. def type(self):
  77. "Return the OGR type of this Field."
  78. return capi.get_field_type(self.ptr)
  79. @property
  80. def type_name(self):
  81. "Return the OGR field type name for this Field."
  82. return capi.get_field_type_name(self.type)
  83. @property
  84. def value(self):
  85. "Return the value of this Field."
  86. # Default is to get the field as a string.
  87. return self.as_string()
  88. @property
  89. def width(self):
  90. "Return the width of this Field."
  91. return capi.get_field_width(self.ptr)
  92. # ### The Field sub-classes for each OGR Field type. ###
  93. class OFTInteger(Field):
  94. _bit64 = False
  95. @property
  96. def value(self):
  97. "Return an integer contained in this field."
  98. return self.as_int(self._bit64)
  99. @property
  100. def type(self):
  101. """
  102. GDAL uses OFTReals to represent OFTIntegers in created
  103. shapefiles -- forcing the type here since the underlying field
  104. type may actually be OFTReal.
  105. """
  106. return 0
  107. class OFTReal(Field):
  108. @property
  109. def value(self):
  110. "Return a float contained in this field."
  111. return self.as_double()
  112. # String & Binary fields, just subclasses
  113. class OFTString(Field):
  114. pass
  115. class OFTWideString(Field):
  116. pass
  117. class OFTBinary(Field):
  118. pass
  119. # OFTDate, OFTTime, OFTDateTime fields.
  120. class OFTDate(Field):
  121. @property
  122. def value(self):
  123. "Return a Python `date` object for the OFTDate field."
  124. try:
  125. yy, mm, dd, hh, mn, ss, tz = self.as_datetime()
  126. return date(yy.value, mm.value, dd.value)
  127. except (TypeError, ValueError, GDALException):
  128. return None
  129. class OFTDateTime(Field):
  130. @property
  131. def value(self):
  132. "Return a Python `datetime` object for this OFTDateTime field."
  133. # TODO: Adapt timezone information.
  134. # See https://lists.osgeo.org/pipermail/gdal-dev/2006-February/007990.html
  135. # The `tz` variable has values of: 0=unknown, 1=localtime (ambiguous),
  136. # 100=GMT, 104=GMT+1, 80=GMT-5, etc.
  137. try:
  138. yy, mm, dd, hh, mn, ss, tz = self.as_datetime()
  139. return datetime(yy.value, mm.value, dd.value, hh.value, mn.value, ss.value)
  140. except (TypeError, ValueError, GDALException):
  141. return None
  142. class OFTTime(Field):
  143. @property
  144. def value(self):
  145. "Return a Python `time` object for this OFTTime field."
  146. try:
  147. yy, mm, dd, hh, mn, ss, tz = self.as_datetime()
  148. return time(hh.value, mn.value, ss.value)
  149. except (ValueError, GDALException):
  150. return None
  151. class OFTInteger64(OFTInteger):
  152. _bit64 = True
  153. # List fields are also just subclasses
  154. class OFTIntegerList(Field):
  155. pass
  156. class OFTRealList(Field):
  157. pass
  158. class OFTStringList(Field):
  159. pass
  160. class OFTWideStringList(Field):
  161. pass
  162. class OFTInteger64List(Field):
  163. pass
  164. # Class mapping dictionary for OFT Types and reverse mapping.
  165. OGRFieldTypes = {
  166. 0: OFTInteger,
  167. 1: OFTIntegerList,
  168. 2: OFTReal,
  169. 3: OFTRealList,
  170. 4: OFTString,
  171. 5: OFTStringList,
  172. 6: OFTWideString,
  173. 7: OFTWideStringList,
  174. 8: OFTBinary,
  175. 9: OFTDate,
  176. 10: OFTTime,
  177. 11: OFTDateTime,
  178. # New 64-bit integer types in GDAL 2
  179. 12: OFTInteger64,
  180. 13: OFTInteger64List,
  181. }
  182. ROGRFieldTypes = {cls: num for num, cls in OGRFieldTypes.items()}