Arthur de Jong

Open Source / Free Software developer

summaryrefslogtreecommitdiffstats
path: root/django/contrib/gis/gdal/field.py
blob: 38a72b6e19384c4b163c79d85ca5e6e7208de047 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
from ctypes import byref, c_int
from datetime import date, datetime, time

from django.contrib.gis.gdal.base import GDALBase
from django.contrib.gis.gdal.error import GDALException
from django.contrib.gis.gdal.prototypes import ds as capi
from django.utils.encoding import force_text


# For more information, see the OGR C API source code:
#  http://www.gdal.org/ogr/ogr__api_8h.html
#
# The OGR_Fld_* routines are relevant here.
class Field(GDALBase):
    """
    This class wraps an OGR Field, and needs to be instantiated
    from a Feature object.
    """

    def __init__(self, feat, index):
        """
        Initializes on the feature object and the integer index of
        the field within the feature.
        """
        # Setting the feature pointer and index.
        self._feat = feat
        self._index = index

        # Getting the pointer for this field.
        fld_ptr = capi.get_feat_field_defn(feat.ptr, index)
        if not fld_ptr:
            raise GDALException('Cannot create OGR Field, invalid pointer given.')
        self.ptr = fld_ptr

        # Setting the class depending upon the OGR Field Type (OFT)
        self.__class__ = OGRFieldTypes[self.type]

        # OFTReal with no precision should be an OFTInteger.
        if isinstance(self, OFTReal) and self.precision == 0:
            self.__class__ = OFTInteger
            self._double = True

    def __str__(self):
        "Returns the string representation of the Field."
        return str(self.value).strip()

    # #### Field Methods ####
    def as_double(self):
        "Retrieves the Field's value as a double (float)."
        return capi.get_field_as_double(self._feat.ptr, self._index)

    def as_int(self, is_64=False):
        "Retrieves the Field's value as an integer."
        if is_64:
            return capi.get_field_as_integer64(self._feat.ptr, self._index)
        else:
            return capi.get_field_as_integer(self._feat.ptr, self._index)

    def as_string(self):
        "Retrieves the Field's value as a string."
        string = capi.get_field_as_string(self._feat.ptr, self._index)
        return force_text(string, encoding=self._feat.encoding, strings_only=True)

    def as_datetime(self):
        "Retrieves the Field's value as a tuple of date & time components."
        yy, mm, dd, hh, mn, ss, tz = [c_int() for i in range(7)]
        status = capi.get_field_as_datetime(
            self._feat.ptr, self._index, byref(yy), byref(mm), byref(dd),
            byref(hh), byref(mn), byref(ss), byref(tz))
        if status:
            return (yy, mm, dd, hh, mn, ss, tz)
        else:
            raise GDALException('Unable to retrieve date & time information from the field.')

    # #### Field Properties ####
    @property
    def name(self):
        "Returns the name of this Field."
        name = capi.get_field_name(self.ptr)
        return force_text(name, encoding=self._feat.encoding, strings_only=True)

    @property
    def precision(self):
        "Returns the precision of this Field."
        return capi.get_field_precision(self.ptr)

    @property
    def type(self):
        "Returns the OGR type of this Field."
        return capi.get_field_type(self.ptr)

    @property
    def type_name(self):
        "Return the OGR field type name for this Field."
        return capi.get_field_type_name(self.type)

    @property
    def value(self):
        "Returns the value of this Field."
        # Default is to get the field as a string.
        return self.as_string()

    @property
    def width(self):
        "Returns the width of this Field."
        return capi.get_field_width(self.ptr)


# ### The Field sub-classes for each OGR Field type. ###
class OFTInteger(Field):
    _double = False
    _bit64 = False

    @property
    def value(self):
        "Returns an integer contained in this field."
        if self._double:
            # If this is really from an OFTReal field with no precision,
            # read as a double and cast as Python int (to prevent overflow).
            return int(self.as_double())
        else:
            return self.as_int(self._bit64)

    @property
    def type(self):
        """
        GDAL uses OFTReals to represent OFTIntegers in created
        shapefiles -- forcing the type here since the underlying field
        type may actually be OFTReal.
        """
        return 0


class OFTReal(Field):
    @property
    def value(self):
        "Returns a float contained in this field."
        return self.as_double()


# String & Binary fields, just subclasses
class OFTString(Field):
    pass


class OFTWideString(Field):
    pass


class OFTBinary(Field):
    pass


# OFTDate, OFTTime, OFTDateTime fields.
class OFTDate(Field):
    @property
    def value(self):
        "Returns a Python `date` object for the OFTDate field."
        try:
            yy, mm, dd, hh, mn, ss, tz = self.as_datetime()
            return date(yy.value, mm.value, dd.value)
        except (ValueError, GDALException):
            return None


class OFTDateTime(Field):
    @property
    def value(self):
        "Returns a Python `datetime` object for this OFTDateTime field."
        # TODO: Adapt timezone information.
        #  See http://lists.osgeo.org/pipermail/gdal-dev/2006-February/007990.html
        #  The `tz` variable has values of: 0=unknown, 1=localtime (ambiguous),
        #  100=GMT, 104=GMT+1, 80=GMT-5, etc.
        try:
            yy, mm, dd, hh, mn, ss, tz = self.as_datetime()
            return datetime(yy.value, mm.value, dd.value, hh.value, mn.value, ss.value)
        except (ValueError, GDALException):
            return None


class OFTTime(Field):
    @property
    def value(self):
        "Returns a Python `time` object for this OFTTime field."
        try:
            yy, mm, dd, hh, mn, ss, tz = self.as_datetime()
            return time(hh.value, mn.value, ss.value)
        except (ValueError, GDALException):
            return None


class OFTInteger64(OFTInteger):
    _bit64 = True


# List fields are also just subclasses
class OFTIntegerList(Field):
    pass


class OFTRealList(Field):
    pass


class OFTStringList(Field):
    pass


class OFTWideStringList(Field):
    pass


class OFTInteger64List(Field):
    pass


# Class mapping dictionary for OFT Types and reverse mapping.
OGRFieldTypes = {
    0: OFTInteger,
    1: OFTIntegerList,
    2: OFTReal,
    3: OFTRealList,
    4: OFTString,
    5: OFTStringList,
    6: OFTWideString,
    7: OFTWideStringList,
    8: OFTBinary,
    9: OFTDate,
    10: OFTTime,
    11: OFTDateTime,
    # New 64-bit integer types in GDAL 2
    12: OFTInteger64,
    13: OFTInteger64List,
}
ROGRFieldTypes = {cls: num for num, cls in OGRFieldTypes.items()}