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.

field.py 6.5KB

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