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.6 KiB

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