root/MGET/Branches/Jason/PythonPackage/src/GeoEco/Datasets/__init__.py @ 897

Revision 897, 271.3 KB (checked in by jjr8, 17 months ago)

Fixed Python 2.4 build breaks.

Line 
1# Datasets/__init__.py - Base classes that define the interfaces used to access
2# table and grid datasets.
3#
4# Copyright (C) 2010 Jason J. Roberts
5#
6# This program is free software; you can redistribute it and/or
7# modify it under the terms of the GNU General Public License
8# as published by the Free Software Foundation; either version 2
9# of the License, or (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License (available in the file LICENSE.TXT)
15# for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
21import atexit
22import bisect
23import codecs
24import copy
25import datetime
26import math
27import locale
28import logging
29import os
30import re
31import shutil
32import types
33import weakref
34
35from GeoEco.DynamicDocString import DynamicDocString
36from GeoEco.Internationalization import _
37from GeoEco.Logging import Logger, ProgressReporter
38
39
40class CollectibleObject(object):
41    __doc__ = DynamicDocString()
42
43    # Public properties and methods
44
45    def _GetDisplayNameProp(self):
46        return self._GetDisplayName()
47
48    DisplayName = property(_GetDisplayNameProp, doc=DynamicDocString())
49
50    def _GetParentCollection(self):
51        return self._ParentCollection
52
53    ParentCollection = property(_GetParentCollection, doc=DynamicDocString())
54
55    def GetQueryableAttribute(self, name):
56        self.__class__.__doc__.Obj.ValidateMethodInvocation()
57
58        obj = self
59        while obj is not None:
60            if obj._QueryableAttributes is not None:
61                for attr in obj._QueryableAttributes:
62                    if attr.Name == name:
63                        return attr
64            obj = obj.ParentCollection
65
66        return None
67
68    def GetQueryableAttributesWithDataType(self, typeMetadata):
69        self.__class__.__doc__.Obj.ValidateMethodInvocation()
70
71        attrs = []
72        obj = self
73        while obj is not None:
74            if obj._QueryableAttributes is not None:
75                for attr in obj._QueryableAttributes:
76                    if issubclass(attr.DataType.__class__, typeMetadata):
77                        attrs.append(attr)
78            obj = obj.ParentCollection
79
80        return attrs
81
82    def GetAllQueryableAttributes(self):
83        self.__class__.__doc__.Obj.ValidateMethodInvocation()
84
85        attrs = []
86        obj = self
87        while obj is not None:
88            if obj._QueryableAttributes is not None:
89                for attr in obj._QueryableAttributes:
90                    attrs.append(attr)
91            obj = obj.ParentCollection
92
93        return attrs
94
95    def GetQueryableAttributeValue(self, name):
96        self.__class__.__doc__.Obj.ValidateMethodInvocation()
97
98        # Check whether the value has been explicitly assigned to us
99        # or our parents.
100
101        obj = self
102        while obj is not None:
103            if obj._QueryableAttributeValues is not None:
104                if name in obj._QueryableAttributeValues:
105                    return obj._QueryableAttributeValues[name]
106            obj = obj.ParentCollection
107
108        # Check whether the value is derived from another queryable
109        # attribute.
110
111        thisAttr = self.GetQueryableAttribute(name)
112        if thisAttr is not None and thisAttr.DerivedFromAttr is not None:
113            derivedFromValue = self.GetQueryableAttributeValue(thisAttr.DerivedFromAttr)
114            if derivedFromValue is not None:
115                if thisAttr.DerivedValueMap is not None:
116                    if derivedFromValue in thisAttr.DerivedValueMap:
117                        return thisAttr.DerivedValueMap[derivedFromValue]
118                else:
119                    return thisAttr.DerivedValueFunc(self, derivedFromValue)
120
121        # We did not find it. Return None.
122
123        return None
124
125    def GetLazyPropertyValue(self, name, allowPhysicalValue=True):
126
127        # Check whether value is cached in our dictionary or our
128        # parents' dictionaries.
129
130        obj = self
131        while obj is not None:
132            if obj._LazyPropertyValues is not None and name in obj._LazyPropertyValues:
133                return obj._LazyPropertyValues[name]
134            obj = obj.ParentCollection
135
136        # Check whether the value is assigned by a queryable
137        # attribute.
138
139        obj = self
140        while obj is not None:
141            if obj._QueryableAttributes is not None:
142                for attr in obj._QueryableAttributes:
143                    if attr.DerivedLazyDatasetProps is not None:
144                        attrValue = self.GetQueryableAttributeValue(attr.Name)
145                        if attrValue in attr.DerivedLazyDatasetProps and name in attr.DerivedLazyDatasetProps[attrValue]:
146                            value = attr.DerivedLazyDatasetProps[attrValue][name]
147                            self.SetLazyPropertyValue(name, value)
148                            return value
149            obj = obj.ParentCollection
150
151        # If allowed, retrieve the value from physical storage. This
152        # is typically slow and could involve downloading and/or
153        # decompressing the dataset into a cache directory, if this is
154        # the first time it is being physically accessed.
155
156        if not allowPhysicalValue:
157            return None
158
159        value = self._GetLazyPropertyPhysicalValue(name)
160        self.SetLazyPropertyValue(name, value)
161        return value
162
163    def SetLazyPropertyValue(self, name, value):
164        if self._LazyPropertyValues is None:
165            self._LazyPropertyValues = {}
166        self._LazyPropertyValues[name] = value
167
168    def HasLazyPropertyValue(self, name, allowPhysicalValue=True):
169        return self.GetLazyPropertyValue(name, allowPhysicalValue) is not None or self._LazyPropertyValues is not None and name in self._LazyPropertyValues
170
171    def Close(self):
172        try:
173            self._Close()
174        except:
175            pass
176
177    @classmethod
178    def TestCapability(cls, capability):
179        cls.__doc__.Obj.ValidateMethodInvocation()
180        return cls._TestCapability(capability)
181
182    # Private base class constructor. Do not invoke directly; use a
183    # derived class instead. If you override, be sure to call it from
184    # your derived class's __init__.
185
186    # TODO: Clean up comments like the one above. Either remove them
187    # entirely (put instructions on creating derived classes in the
188    # documentation) or make them completely clear and succinct.
189
190    def __init__(self, parentCollection=None, queryableAttributes=None, queryableAttributeValues=None, lazyPropertyValues=None):
191        self.__class__.__doc__.Obj.ValidateMethodInvocation()
192
193        # Initialize our properties.
194       
195        self._ParentCollection = parentCollection
196        self._QueryableAttributes = None
197
198        self._QueryableAttributeValues = {}
199        if queryableAttributeValues is not None:
200            self._QueryableAttributeValues.update(queryableAttributeValues)
201
202        self._LazyPropertyValues = {}
203        if lazyPropertyValues is not None:
204            self._LazyPropertyValues.update(lazyPropertyValues)
205
206        self._TempDirectories = None
207
208        # If the caller wants to define queryable attributes, perform
209        # additional validation.
210
211        if queryableAttributes is not None:
212
213            # Validate that the caller is not trying to define a
214            # queryable attribute that has already been defined by our
215            # chain of parent collections.
216
217            for qa in queryableAttributes:
218                if self.GetQueryableAttribute(qa.Name) is not None:
219                    raise ValueError(_(u'QueryableAttribute %(name)s has already been defined by a parent collection.') % {u'name': qa.Name})
220
221            # Validate that there is no more than one queryable
222            # attribute with data type DateTimeTypeMetadata among the
223            # attributes we define and our chain of parent
224            # collections.
225
226            foundDateTime = False
227
228            collection = parentCollection
229            while collection is not None:
230                if collection._QueryableAttributes is not None:
231                    for qa in collection._QueryableAttributes:
232                        if isinstance(qa.DataType, DateTimeTypeMetadata):
233                            foundDateTime = True
234                            break
235                collection = collection.ParentCollection
236           
237            for qa in queryableAttributes:
238                if isinstance(qa.DataType, DateTimeTypeMetadata):
239                    if foundDateTime:
240                        raise ValueError(_(u'Only one QueryableAttribute with data type DateTimeTypeMetadata may be defined for this object and its chain of parent collections.'))
241                    foundDateTime = True
242
243            self._QueryableAttributes = queryableAttributes
244
245    def __del__(self):
246        self.Close()
247
248    # Private methods that the derived class may override
249
250    def _GetDisplayName(self):
251        raise NotImplementedError(_(u'The _GetDisplayName method of class %s has not been implemented.') % self.__class__.__name__)
252
253    def _GetLazyPropertyPhysicalValue(self, name):
254        return None
255
256    def _Close(self):                               # If you override, be sure to call the base class at the end of your implementation!
257        if hasattr(self, '_TempDirectories') and self._TempDirectories is not None:
258            while len(self._TempDirectories) > 0:
259                self._LogDebug(_(u'%(class)s 0x%(id)08X: Deleting temporary directory %(dir)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'dir': self._Unicode(self._TempDirectories[0])})
260                try:
261                    shutil.rmtree(self._TempDirectories[0], onerror=CollectibleObject._LogFailedRemoval)
262                except:
263                    pass
264                del self._TempDirectories[0]
265
266        self._UnregisterForCloseAtExit()
267
268    @classmethod
269    def _TestCapability(cls, capability):
270        if isinstance(cls, CollectibleObject):
271            raise NotImplementedError(_(u'The _TestCapability method of class %s has not been implemented.') % cls.__class__.__name__)
272        else:
273            raise NotImplementedError(_(u'The _TestCapability method of class %s has not been implemented.') % cls.__name__)
274
275    # Private properties and methods that the derived class may access
276    # but generally does not override
277
278    def _RegisterForCloseAtExit(self):
279        if not hasattr(CollectibleObject, '_ObjectsToCloseAtExit'):
280            CollectibleObject._ObjectsToCloseAtExit = []
281
282        for r in CollectibleObject._ObjectsToCloseAtExit:
283            if r() == self:
284                return
285
286        CollectibleObject._ObjectsToCloseAtExit.insert(0, weakref.ref(self))
287
288    def _CreateTempDirectory(self):
289        import tempfile
290        tempDir = tempfile.mkdtemp(prefix='GeoEco_' + self.__class__.__name__ + '_Cache_')
291        self._LogDebug(_(u'%(class)s 0x%(id)08X: Created temporary directory %(dir)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'dir': self._Unicode(tempDir)})
292       
293        if self._TempDirectories is None:
294            self._TempDirectories = []
295        self._TempDirectories.insert(0, tempDir)
296
297        self._RegisterForCloseAtExit()
298
299        return tempDir
300
301    @classmethod
302    def _RequireCapability(cls, capability):
303        error = cls._TestCapability(capability.lower())
304        if error is not None:
305            raise error
306
307    @classmethod
308    def _ogr(cls):
309        if not hasattr(CollectibleObject, '_OGRModule'):
310            from GeoEco.Dependencies import ImportGDALModule
311            ogr = ImportGDALModule('osgeo.ogr')
312            ogr.UseExceptions()
313            setattr(CollectibleObject, '_OGRModule', ogr)
314           
315            setattr(CollectibleObject, '_GeometryTypeForOGRGeometry', {ogr.wkbNone: None,
316                                                                       ogr.wkbPoint: u'Point',
317                                                                       ogr.wkbLineString: u'LineString',
318                                                                       ogr.wkbPolygon: u'Polygon',
319                                                                       ogr.wkbMultiPoint: u'MultiPoint',
320                                                                       ogr.wkbMultiLineString: u'MultiLineString',
321                                                                       ogr.wkbMultiPolygon: u'MultiPolygon',
322                                                                       ogr.wkbGeometryCollection: u'GeometryCollection',
323                                                                       ogr.wkbPoint25D: u'Point25D',
324                                                                       ogr.wkbLineString25D: u'LineString25D',
325                                                                       ogr.wkbPolygon25D: u'Polygon25D',
326                                                                       ogr.wkbMultiPoint25D: u'MultiPoint25D',
327                                                                       ogr.wkbMultiLineString25D: u'MultiLineString25D',
328                                                                       ogr.wkbMultiPolygon25D: u'MultiPolygon25D',
329                                                                       ogr.wkbGeometryCollection25D: u'GeometryCollection25D'})
330           
331            setattr(CollectibleObject, '_OGRGeometryForGeometryType', {None: ogr.wkbNone,
332                                                                       u'Point': ogr.wkbPoint,
333                                                                       u'LineString': ogr.wkbLineString,
334                                                                       u'Polygon': ogr.wkbPolygon,
335                                                                       u'MultiPoint': ogr.wkbMultiPoint,
336                                                                       u'MultiLineString': ogr.wkbMultiLineString,
337                                                                       u'MultiPolygon': ogr.wkbMultiPolygon,
338                                                                       u'GeometryCollection': ogr.wkbGeometryCollection,
339                                                                       u'Point25D': ogr.wkbPoint25D,
340                                                                       u'LineString25D': ogr.wkbLineString25D,
341                                                                       u'Polygon25D': ogr.wkbPolygon25D,
342                                                                       u'MultiPoint25D': ogr.wkbMultiPoint25D,
343                                                                       u'MultiLineString25D': ogr.wkbMultiLineString25D,
344                                                                       u'MultiPolygon25D': ogr.wkbMultiPolygon25D,
345                                                                       u'GeometryCollection25D': ogr.wkbGeometryCollection25D})
346        return CollectibleObject._OGRModule
347
348    @classmethod
349    def _osr(cls):
350        if not hasattr(CollectibleObject, '_OSRModule'):
351            from GeoEco.Dependencies import ImportGDALModule
352            osr = ImportGDALModule('osgeo.osr')
353            osr.UseExceptions()
354            setattr(CollectibleObject, '_OSRModule', osr)
355        return CollectibleObject._OSRModule
356
357    @classmethod
358    def _gdal(cls):
359        if not hasattr(CollectibleObject, '_GDALModule'):
360            from GeoEco.Dependencies import ImportGDALModule
361            gdal = ImportGDALModule('osgeo.gdal')
362            gdal.UseExceptions()
363            setattr(CollectibleObject, '_GDALModule', gdal)
364        return CollectibleObject._GDALModule
365
366    @classmethod
367    def _gdalconst(cls):
368        if not hasattr(CollectibleObject, '_GDALConstModule'):
369            from GeoEco.Dependencies import ImportGDALModule
370            gdalconst = ImportGDALModule('osgeo.gdalconst')
371            setattr(CollectibleObject, '_GDALConstModule', gdalconst)
372        return CollectibleObject._GDALConstModule
373
374    @classmethod
375    def _Str(cls, s):
376        if isinstance(s, str):
377            return s
378        if not hasattr(CollectibleObject, '_Encoder'):
379            setattr(CollectibleObject, '_Encoder', codecs.getencoder(locale.getpreferredencoding()))
380        if isinstance(s, unicode):
381            return CollectibleObject._Encoder(s)[0]
382        return CollectibleObject._Encoder(str(s))[0]
383
384    @classmethod
385    def _Unicode(cls, s):
386        if isinstance(s, unicode):
387            return s
388        if not hasattr(CollectibleObject, '_Decoder'):
389            setattr(CollectibleObject, '_Decoder', codecs.getdecoder(locale.getpreferredencoding()))
390        return CollectibleObject._Decoder(str(s))[0]
391
392    _LoggingChannel = u'GeoEco.Datasets'
393
394    @classmethod
395    def _GetLogger(cls):
396        try:
397            return logging.getLogger(CollectibleObject._LoggingChannel)
398        except:
399            return None
400
401    @classmethod
402    def _LogDebug(cls, format, *args, **kwargs):
403        try:
404            logging.getLogger(CollectibleObject._LoggingChannel).debug(format, *args, **kwargs)
405        except:
406            pass
407
408    @classmethod
409    def _LogInfo(cls, format, *args, **kwargs):
410        try:
411            logging.getLogger(CollectibleObject._LoggingChannel).info(format, *args, **kwargs)
412        except:
413            pass
414
415    @classmethod
416    def _LogWarning(cls, format, *args, **kwargs):
417        try:
418            logging.getLogger(CollectibleObject._LoggingChannel).warning(format, *args, **kwargs)
419        except:
420            pass
421
422    @classmethod
423    def _LogError(cls, format, *args, **kwargs):
424        try:
425            logging.getLogger(CollectibleObject._LoggingChannel).error(format, *args, **kwargs)
426        except:
427            pass
428
429    @classmethod
430    def _DebugLoggingEnabled(cls):
431        return logging.getLogger(u'GeoEco.Datasets').isEnabledFor(logging.DEBUG)
432
433    # Private methods that the derived class should not use
434
435    def _UnregisterForCloseAtExit(self):
436        if hasattr(CollectibleObject, '_ObjectsToCloseAtExit'):
437            for i, r in enumerate(CollectibleObject._ObjectsToCloseAtExit):
438                if r() == self:
439                    del CollectibleObject._ObjectsToCloseAtExit[i]
440                    return
441
442    @staticmethod
443    def _CloseObjectsAtExit():
444        if hasattr(CollectibleObject, '_ObjectsToCloseAtExit'):
445            while len(CollectibleObject._ObjectsToCloseAtExit) > 0:
446                if CollectibleObject._ObjectsToCloseAtExit[0]() is None:        # Should never happen because CollectibleObject.__del__() removes the object from the list
447                    del CollectibleObject._ObjectsToCloseAtExit[0]
448                else:
449                    CollectibleObject._ObjectsToCloseAtExit[0]()._Close()       # This will remove it from the list.
450
451    @staticmethod
452    def _LogFailedRemoval(function, path, excinfo):
453        try:
454            logger = logging.getLogger(u'GeoEco.Datasets')
455            _unicode = codecs.getdecoder(locale.getpreferredencoding())
456            if function == os.remove:
457                logger.warning(_(u'Could not delete temporary file %(path)s due to %(error)s: %(msg)s') % {u'path' : _unicode(path)[0], u'error' : excinfo[0].__name__, u'msg' : _unicode(str(excinfo[1]))[0]})
458            elif function == os.rmdir:
459                logger.warning(_(u'Could not delete temporary directory %(path)s due to %(error)s: %(msg)s') % {u'path' : _unicode(path)[0], u'error' : excinfo[0].__name__, u'msg' : _unicode(str(excinfo[1]))[0]})
460            else:
461                logger.warning(_(u'Could not delete temporary directory %(path)s. The directory contents could not be listed due to %(error)s: %(msg)s') % {u'path' : _unicode(path)[0], u'error' : excinfo[0].__name__, u'msg' : _unicode(str(excinfo[1]))[0]})
462        except:
463            pass
464
465atexit.register(CollectibleObject._CloseObjectsAtExit)
466
467
468class QueryableAttribute(object):
469    __doc__ = DynamicDocString()
470
471    def _GetName(self):
472        return self._Name
473
474    Name = property(_GetName, doc=DynamicDocString())
475
476    def _GetDisplayName(self):
477        return self._DisplayName
478
479    DisplayName = property(_GetDisplayName, doc=DynamicDocString())
480
481    def _GetDataType(self):
482        return self._DataType
483
484    DataType = property(_GetDataType, doc=DynamicDocString())
485
486    def _GetDerivedLazyDatasetProps(self):
487        return self._DerivedLazyDatasetProps
488
489    DerivedLazyDatasetProps = property(_GetDerivedLazyDatasetProps, doc=DynamicDocString())
490
491    def _GetDerivedFromAttr(self):
492        return self._DerivedFromAttr
493
494    DerivedFromAttr = property(_GetDerivedFromAttr, doc=DynamicDocString())
495
496    def _GetDerivedValueMap(self):
497        return self._DerivedValueMap
498
499    DerivedValueMap = property(_GetDerivedValueMap, doc=DynamicDocString())
500
501    def _GetDerivedValueFunc(self):
502        return self._DerivedValueFunc
503
504    DerivedValueFunc = property(_GetDerivedValueFunc, doc=DynamicDocString())
505
506    def __init__(self, name, displayName, dataType, derivedLazyDatasetProps=None, derivedFromAttr=None, derivedValueMap=None, derivedValueFunc=None):
507        self.__class__.__doc__.Obj.ValidateMethodInvocation()
508
509        if not isinstance(dataType, (UnicodeStringTypeMetadata, IntegerTypeMetadata, FloatTypeMetadata, DateTimeTypeMetadata)):
510            raise TypeError(_(u'%(type)s is not an allowed data type for queryable attributes. Queryable attributes may only have the following data types: UnicodeStringTypeMetadata, IntegerTypeMetadata, FloatTypeMetadata, DateTimeTypeMetadata.') % {u'type': dataType.__class__.__name__})
511
512        if derivedFromAttr is not None and (derivedValueMap is None and derivedValueFunc is None or derivedValueMap is not None and derivedValueFunc is not None):
513            raise TypeError(_(u'If the derivedFromAttr parameter is not None, a value must be provided for either derivedValueMap or derivedValueFunc, but not for both.'))
514
515        if not isinstance(derivedValueFunc, (types.NoneType, types.FunctionType, types.MethodType)):
516            raise TypeError(_(u'The type of the derivedValueFunc parameter (%(type)s) is not an allowed type. It must be a function, a method, or None.') % {u'type': str(type(derivedValueFunc))})
517
518        self._Name = name
519        self._DisplayName = displayName
520        self._DataType = dataType
521        self._DerivedLazyDatasetProps = derivedLazyDatasetProps
522        self._DerivedFromAttr = derivedFromAttr
523        self._DerivedValueMap = derivedValueMap
524        self._DerivedValueFunc = derivedValueFunc
525
526
527class Dataset(CollectibleObject):
528    __doc__ = DynamicDocString()
529
530    # Public properties and methods
531
532    def GetSpatialReference(self, srType):
533        self.__class__.__doc__.Obj.ValidateMethodInvocation()
534
535        srRef = self.GetLazyPropertyValue('SpatialReference')
536
537        if srRef is None:
538            if srType == 'arcgis':
539                return u'{B286C06B-0879-11D2-AACA-00C04FA33C20}'        # This is ESRI's GUID for the "Unknown" coordinate system
540            return None
541       
542        if srType == 'obj':
543            return srRef
544       
545        if srType == 'wkt':
546            s = srRef.ExportToWkt()
547            if isinstance(s, basestring) and len(s) > 0:
548                s = self._Unicode(s)
549            return s
550       
551        if srType == 'proj4':
552            s = srRef.ExportToProj4()
553            if isinstance(s, basestring) and len(s) > 0:
554                s = self._Unicode(s)
555            return s
556       
557        if srType == 'arcgis':
558            s = srRef.ExportToWkt()
559            if isinstance(s, basestring) and len(s) > 0:
560                sr = self._osr().SpatialReference(s)        # TODO: Need to explicitly destroy temporary sr allocated here?
561                sr.MorphToESRI()
562                s = sr.ExportToWkt()
563                if isinstance(s, basestring) and len(s) > 0:
564                    s = self._Unicode(s)
565            return s
566       
567        raise ValueError(_(u'"%(srType)s" is an invalid value for the srType parameter.') % {u'srType': srType})
568
569    def SetSpatialReference(self, srType, sr):
570        self.__class__.__doc__.Obj.ValidateMethodInvocation()
571
572        # Verify that we have the required capabilities.
573
574        self._RequireCapability('SetSpatialReference')
575
576        # Convert the caller's spatial reference to the type required
577        # by the derived class and call the derived class to set it.
578
579        if srType != 'obj' and not isinstance(sr, (types.NoneType, basestring)):
580            raise TypeError(_(u'If the srType parameter is \'%(srType)s\', the sr parameter must be a string or None.') % {u'srType': srType})
581
582        if sr is None or srType != 'obj' and len(sr.strip()) <= 0 or srType == 'arcgis' and sr.upper() == '{B286C06B-0879-11D2-AACA-00C04FA33C20}':     # This is ESRI's GUID for the "Unknown" coordinate system
583            self._LogDebug(_(u'%(class)s 0x%(id)08X: Setting SpatialReference to None.'), {u'class': self.__class__.__name__, u'id': id(self)})
584        elif srType == 'obj':
585            self._LogDebug(_(u'%(class)s 0x%(id)08X: Setting SpatialReference from OSR SpatialReference object 0x%(id2)08X that has WKT \'%(srString)s\'.'), {u'class': self.__class__.__name__, u'id': id(self), u'id2': id(sr), u'srString': self._Unicode(sr.ExportToWkt())})
586        elif srType == 'wkt':
587            self._LogDebug(_(u'%(class)s 0x%(id)08X: Setting SpatialReference from WKT \'%(srString)s\'.'), {u'class': self.__class__.__name__, u'id': id(self), u'srString': self._Unicode(sr)})
588        elif srType == 'proj4':
589            self._LogDebug(_(u'%(class)s 0x%(id)08X: Setting SpatialReference from Proj4 string "%(srString)s".'), {u'class': self.__class__.__name__, u'id': id(self), u'srString': self._Unicode(sr)})
590        elif srType == 'arcgis':
591            self._LogDebug(_(u'%(class)s 0x%(id)08X: Setting SpatialReference from ArcGIS WKT \'%(srString)s\'.'), {u'class': self.__class__.__name__, u'id': id(self), u'srString': self._Unicode(sr)})
592        else:
593            raise ValueError(_(u'"%(srType)s" is an invalid value for the srType parameter.') % {u'srType': srType})
594
595        try:
596            self._SetSpatialReference(self.ConvertSpatialReference(srType, sr, self._GetSRTypeForSetting()))
597        except Exception, e:
598            raise RuntimeError(_(u'Failed to set the spatial reference of %(dn)s due to %(e)s: %(msg)s') % {u'dn': self.DisplayName, u'e': e.__class__.__name__, u'msg': unicode(e)})
599
600        # Set our SpatialReference property to a new OSR
601        # SpatialReference.
602           
603        self.SetLazyPropertyValue('SpatialReference', self.ConvertSpatialReference(srType, sr, 'obj'))
604
605    @classmethod
606    def ConvertSpatialReference(cls, srType, sr, outputSRType):
607        cls.__doc__.Obj.ValidateMethodInvocation()
608
609        if srType != 'obj' and not isinstance(sr, (types.NoneType, basestring)):
610            raise TypeError(_(u'If the srType parameter is \'%(srType)s\', the sr parameter must be a string or None.') % {u'srType': srType})
611
612        if outputSRType != 'obj' and outputSRType == srType:
613            return sr
614
615        # Create a SpatialReference object
616
617        if sr is None or srType != 'obj' and len(sr.strip()) <= 0 or srType == 'arcgis' and sr.upper() == '{B286C06B-0879-11D2-AACA-00C04FA33C20}':     # This is ESRI's GUID for the "Unknown" coordinate system
618            return None
619
620        elif srType == 'obj':
621            srObj = cls._osr().SpatialReference(sr.ExportToWkt())
622
623        elif srType == 'wkt':
624            srObj = cls._osr().SpatialReference(cls._Str(sr))
625
626        elif srType == 'proj4':
627            srObj = cls._osr().SpatialReference()
628            srObj.ImportFromProj4(cls._Str(sr))
629
630        elif srType == 'arcgis':
631            sr = re.sub("\\[\\'[^\\']*\\'", Dataset._FixESRIQuotes, sr)        # Convert ArcGIS's single quotes around WKT <name> tokens to double quotes, to conform to proper WKT syntax
632            srObj = cls._osr().SpatialReference(cls._Str(sr))
633            srObj.MorphFromESRI()
634
635        else:
636            raise ValueError(_(u'"%(srType)s" is an invalid value for the srType parameter.') % {u'srType': srType})
637
638        # Return the appropriate string or the SpatialReference
639        # object.
640
641        if outputSRType == 'obj':
642            return srObj
643
644        if outputSRType == 'wkt':
645            return cls._Unicode(srObj.ExportToWkt())
646
647        if outputSRType == 'proj4':
648            return cls._Unicode(srObj.ExportToProj4())
649
650        srObj.MorphToESRI()
651        return cls._Unicode(srObj.ExportToWkt())
652
653    # Private properties and methods that the derived class may access
654    # but generally does not override
655
656    @staticmethod
657    def _FixESRIQuotes(matchobj):
658        return '["' + matchobj.group(0)[2:-1] + '"'
659
660    # Private methods that the derived class is expected to override
661
662    @classmethod
663    def _GetSRTypeForSetting(cls):
664        if isinstance(cls, Dataset):
665            raise NotImplementedError(_(u'The _GetSRTypeForSetting method of class %s has not been implemented.') % cls.__class__.__name__)
666        else:
667            raise NotImplementedError(_(u'The _GetSRTypeForSetting method of class %s has not been implemented.') % cls.__name__)
668
669    def _SetSpatialReference(self, sr):
670        raise NotImplementedError(_(u'The _SetSpatialReference method of class %s has not been implemented.') % self.__class__.__name__)
671
672
673class DatasetCollection(CollectibleObject):
674    __doc__ = DynamicDocString()
675
676    def _GetCacheDirectory(self):
677        return self._CacheDirectory
678
679    def _SetCacheDirectory(self, value):
680        self.__doc__.Obj.ValidatePropertyAssignment()
681        self._CacheDirectory = value
682
683    CacheDirectory = property(_GetCacheDirectory, _SetCacheDirectory, doc=DynamicDocString())
684
685    def QueryDatasets(self, expression=None, reportProgress=True, **options):
686        self.__class__.__doc__.Obj.ValidateMethodInvocation()
687
688        # Parse the query.
689
690        parsedExpression, parentAttrValues = self._PrepareQuery(expression)
691
692        # Log a message and start a progress reporter, if requested.
693
694        if parsedExpression is not None:
695            if reportProgress:
696                self._LogInfo(_(u'Querying %(dn)s for datasets matching the expression "%(expr)s".'), {u'dn': self.DisplayName, u'expr': expression})
697            else:
698                self._LogDebug(_(u'%(class)s 0x%(id)08X: Executing query for datasets matching expression: "%(expr)s"'), {u'class': self.__class__.__name__, u'id': id(self), u'expr': expression})
699        else:
700            if reportProgress:
701                self._LogInfo(_(u'Querying %(dn)s.'), {u'dn': self.DisplayName})
702            else:
703                self._LogDebug(_(u'%(class)s 0x%(id)08X: Executing query for all datasets.'), {u'class': self.__class__.__name__, u'id': id(self)})
704
705        if reportProgress:
706            progressReporter = ProgressReporter(progressMessage2=_(u'Query in progress: %(elapsed)s elapsed, %(opsCompleted)i datasets found so far, %(perOp)s per dataset.'),
707                                                completionMessage=_(u'Query complete: %(elapsed)s elapsed, %(opsCompleted)i datasets found, %(perOp)s per dataset.'),
708                                                loggingChannel=DatasetCollection._LoggingChannel)
709            progressReporter.Start()
710        else:
711            progressReporter = None
712
713        # Execute the query.
714
715        try:
716            datasets = self._QueryDatasets(parsedExpression, progressReporter, options, parentAttrValues)
717        finally:
718            if reportProgress:
719                progressReporter.Stop()
720
721        if not reportProgress:
722            self._LogDebug(_(u'%(class)s 0x%(id)08X: Query complete. Returning %(i)i datasets.'), {u'class': self.__class__.__name__, u'id': id(self), u'i': len(datasets)})
723
724        # Return successfully.
725
726        return datasets
727
728    def GetOldestDataset(self, expression=None, **options):
729        self.__class__.__doc__.Obj.ValidateMethodInvocation()
730
731        # Validate that the collection has a queryable attribute with
732        # data type DateTimeTypeMetadata.
733
734        from GeoEco.Types import DateTimeTypeMetadata
735
736        attrs = self.GetQueryableAttributesWithDataType(DateTimeTypeMetadata)
737        if len(attrs) <= 0:
738            raise ValueError(_(u'This dataset collection does not have a queryable attribute defined with the data type DateTimeTypeMetadata. In order to retrieve the oldest dataset, a queryable attribute of that type must be defined.'))
739        if len(attrs) > 1:      # Should never happen; CollectibleObject.__init__ prevents it
740            raise ValueError(_(u'This dataset collection has multiple queryable attributes defined with the data type DateTimeTypeMetadata. In order to retrieve the oldest dataset, only one queryable attribute of that type must be defined.'))
741
742        dateTimeAttrName = attrs[0].Name
743
744        # Get the dataset.
745
746        parsedExpression, parentAttrValues = self._PrepareQuery(expression)
747
748        if parsedExpression is not None:
749            self._LogDebug(_(u'%(class)s 0x%(id)08X: Getting the oldest dataset matching expression: "%(expr)s"'), {u'class': self.__class__.__name__, u'id': id(self), u'expr': expression})
750        else:
751            self._LogDebug(_(u'%(class)s 0x%(id)08X: Getting the oldest dataset.'), {u'class': self.__class__.__name__, u'id': id(self)})
752
753        dataset = self._GetOldestDataset(parsedExpression, options, parentAttrValues, dateTimeAttrName)
754
755        if dataset is not None:
756            self._LogDebug(_(u'%(class)s 0x%(id)08X: The oldest dataset is dated %(dt)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'dt': dataset.GetQueryableAttributeValue(dateTimeAttrName).strftime('%Y-%m-%d %H:%M:%S')})
757        elif parsedExpression is not None:
758            self._LogDebug(_(u'%(class)s 0x%(id)08X: No datasets were found in this collection that match expression: "%(expr)s"'), {u'class': self.__class__.__name__, u'id': id(self), u'expr': expression})
759        else:
760            self._LogDebug(_(u'%(class)s 0x%(id)08X: No datasets were found in this collection.'), {u'class': self.__class__.__name__, u'id': id(self)})
761
762        return dataset
763
764    def GetNewestDataset(self, expression=None, **options):
765        self.__class__.__doc__.Obj.ValidateMethodInvocation()
766
767        # Validate that the collection has a queryable attribute with
768        # data type DateTimeTypeMetadata.
769
770        from GeoEco.Types import DateTimeTypeMetadata
771
772        attrs = self.GetQueryableAttributesWithDataType(DateTimeTypeMetadata)
773        if len(attrs) <= 0:
774            raise ValueError(_(u'This dataset collection does not have a queryable attribute defined with the data type DateTimeTypeMetadata. In order to retrieve the newest dataset, a queryable attribute of that type must be defined.'))
775        if len(attrs) > 1:      # Should never happen; CollectibleObject.__init__ prevents it
776            raise ValueError(_(u'This dataset collection has multiple queryable attributes defined with the data type DateTimeTypeMetadata. In order to retrieve the newest dataset, only one queryable attribute of that type must be defined.'))
777
778        dateTimeAttrName = attrs[0].Name
779
780        # Get the dataset.
781
782        parsedExpression, parentAttrValues = self._PrepareQuery(expression)
783
784        if parsedExpression is not None:
785            self._LogDebug(_(u'%(class)s 0x%(id)08X: Getting the newest dataset matching expression: "%(expr)s"'), {u'class': self.__class__.__name__, u'id': id(self), u'expr': expression})
786        else:
787            self._LogDebug(_(u'%(class)s 0x%(id)08X: Getting the newest dataset.'), {u'class': self.__class__.__name__, u'id': id(self)})
788
789        dataset = self._GetNewestDataset(parsedExpression, options, parentAttrValues, dateTimeAttrName)
790
791        if dataset is not None:
792            self._LogDebug(_(u'%(class)s 0x%(id)08X: The newest dataset is dated %(dt)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'dt': dataset.GetQueryableAttributeValue(dateTimeAttrName).strftime('%Y-%m-%d %H:%M:%S')})
793        elif parsedExpression is not None:
794            self._LogDebug(_(u'%(class)s 0x%(id)08X: No datasets were found in this collection that match expression: "%(expr)s"'), {u'class': self.__class__.__name__, u'id': id(self), u'expr': expression})
795        else:
796            self._LogDebug(_(u'%(class)s 0x%(id)08X: No datasets were found in this collection.'), {u'class': self.__class__.__name__, u'id': id(self)})
797
798        return dataset
799
800    def ImportDatasets(self, datasets, mode=u'Add', reportProgress=True, **options):
801        # TODO: self.__class__.__doc__.Obj.ValidateMethodInvocation()
802
803        # TODO: Test capabilities
804
805        # If the caller provided an empty list, return now.
806
807        if len(datasets) <= 0:
808            if reportProgress:
809                self._LogInfo(_(u'There are no datasets to import.'))
810            return
811
812        # Call the derived class to import the datasets.
813
814        try:
815            self._ImportDatasets(datasets, mode.lower(), reportProgress, options)
816
817        # Ensure all of the datasets are closed. The derived class
818        # should be closing them as it goes along, but we do it here
819        # as an additional convenince to the derived class.
820
821        finally:
822            for dataset in datasets:
823                dataset.Close()
824
825    def __init__(self, parentCollection=None, queryableAttributes=None, queryableAttributeValues=None, lazyPropertyValues=None, cacheDirectory=None):
826        self.__class__.__doc__.Obj.ValidateMethodInvocation()
827        super(DatasetCollection, self).__init__(parentCollection, queryableAttributes, queryableAttributeValues, lazyPropertyValues)
828        self._CacheDirectory = cacheDirectory
829
830    @classmethod
831    def _GetQueryExpressionParser(cls):
832
833        # If we have not built the parser yet, build it using the
834        # pyparsing module.
835       
836        if not hasattr(DatasetCollection, '_QueryExpressionParser'):
837
838            # Define helper classes and functions used to evaluate the
839            # parsed expression.
840            #
841            # Many thanks to Paul McGuire for pyparsing examples that
842            # inspired this code, particularly eval_arith.py.
843
844            class _EvalLiteral(object):
845                def __init__(self, tokens):
846                    self.value = tokens[0]
847                   
848                def eval(self, valuesDict=None):
849                    if self.value[0] in '0123456789.':
850                        if '.' in self.value or 'e' in self.value.lower():
851                            return float(self.value)
852                        return int(self.value)
853                   
854                    if self.value[0] == '"':
855                        return self.value[1:-1].replace('""', '"')
856                   
857                    if self.value[0] == "'":
858                        return self.value[1:-1].replace("''", "'")
859
860                    if self.value[0] == '#':
861                        if len(self.value) == 21:
862                            return datetime.datetime(int(self.value[1:5]), int(self.value[6:8]), int(self.value[9:11]), int(self.value[12:14]), int(self.value[15:17]), int(self.value[18:20]))     # The form of #YYYY-mm-dd HH:MM:SS#
863                        return datetime.datetime(int(self.value[1:5]), int(self.value[6:8]), int(self.value[9:11]))     # The form of #YYYY-mm-dd#
864
865                    if self.value.lower() in ['false', 'true']:
866                        return self.value[0] in ['t', 'T']
867
868                    raise ValueError(_(u'The token %(token)s cannot be parsed as a literal value.') % {u'token': token[0]})
869
870            class _EvalVariable(object):
871                def __init__(self, tokens):
872                    self.variable = tokens[0]
873                   
874                def eval(self, valuesDict=None):
875                    if valuesDict is not None and self.variable in valuesDict:
876                        return valuesDict[self.variable]
877                    return None
878
879            class _EvalSignOp(object):
880                def __init__(self, tokens):
881                    self.sign, self.value = tokens[0]
882                   
883                def eval(self, valuesDict=None):
884                    val = self.value.eval(valuesDict)
885                    if val is None:
886                        return None
887                    if self.sign == '-':
888                        return -1 * val
889                    return val
890
891            def _OperatorOperands(tokenlist):
892                it = iter(tokenlist)
893                while 1:
894                    try:
895                        yield (it.next(), it.next())
896                    except StopIteration:
897                        break
898
899            class _EvalMultOp(object):
900                def __init__(self, tokens):
901                    self.value = tokens[0]
902
903                def eval(self, valuesDict=None):
904                    prod = self.value[0].eval(valuesDict)
905                    if prod is None:
906                        return None
907                    for op, val in _OperatorOperands(self.value[1:]):
908                        val = val.eval(valuesDict)
909                        if val is None:
910                            return None
911                        elif op == '*':
912                            prod *= val
913                        elif op == '/':
914                            prod /= val
915                    return prod
916
917            class _EvalAddOp(object):
918                def __init__(self, tokens):
919                    self.value = tokens[0]
920
921                def eval(self, valuesDict=None):
922                    sum = self.value[0].eval(valuesDict)
923                    if sum is None:
924                        return None
925                    for op, val in _OperatorOperands(self.value[1:]):
926                        val = val.eval(valuesDict)
927                        if val is None:
928                            return None
929                        elif op == '+':
930                            sum += val
931                        elif op == '-':
932                            sum -= val
933                    return sum
934
935            class _EvalLiteralList(object):
936                def __init__(self, tokens):
937                    self.tokens = tokens
938                   
939                def eval(self, valuesDict=None):
940                    return map(lambda v: v.eval(valuesDict), self.tokens[1:-1])
941
942            class _EvalComparisonOp(object):
943                def __init__(self, tokens):
944                    self.tokens = tokens
945
946                def eval(self, valuesDict=None):
947                    val1 = self.tokens[0].eval(valuesDict)
948                    if val1 is None:
949                        return None
950
951                    if len(self.tokens) == 3:
952                        val2 = self.tokens[2].eval(valuesDict)
953                    else:
954                        val2 = self.tokens[3].eval(valuesDict)
955                    if val2 is None:
956                        return None
957
958                    if self.tokens[1] == '=':
959                        return val1 == val2
960                    if self.tokens[1] == '<':
961                        return val1 < val2
962                    if self.tokens[1] == '>':
963                        return val1 > val2
964                    if self.tokens[1] == '<=':
965                        return val1 <= val2
966                    if self.tokens[1] == '>=':
967                        return val1 >= val2
968                    if self.tokens[1] == '<>':
969                        return val1 != val2
970
971                    if self.tokens[1].lower() == 'matches':
972                        if not isinstance(val1, basestring):
973                            raise TypeError(_(u'%(value)s is not permitted as the left-hand operand of the matches operator; both operands of the matches operator must be strings.') % {u'value': repr(val1)})
974                        if not isinstance(val2, basestring):
975                            raise TypeError(_(u'%(value)s is not permitted as the right-hand operand of the matches operator; both operands of the matches operator must be strings.') % {u'value': repr(val2)})
976                        return re.match(val2, val1, re.IGNORECASE) is not None
977
978                    if self.tokens[1].lower() == 'in':
979                        return val1 in val2
980
981                    if self.tokens[1].lower() == 'not' and self.tokens[2].lower() == 'in':
982                        return val1 not in val2
983
984                    raise RuntimeError(_(u'Unknown comparison operator "%(op)s".') % {u'op': self.tokens[1]})
985
986            class _EvalNotOp(object):
987                def __init__(self, tokens):
988                    self.value = tokens[0]
989
990                def eval(self, valuesDict=None):
991                    val = self.value[1].eval(valuesDict)
992                    if val is None:
993                        return None
994                    return not val
995
996            class _EvalAndOp(object):
997                def __init__(self, tokens):
998                    self.value = tokens[0]
999
1000                def eval(self, valuesDict=None):
1001                    val = self.value[0].eval(valuesDict)
1002                    if val is not None and not val:
1003                        return False
1004                    gotNone = val is None
1005                    for op, val in _OperatorOperands(self.value[1:]):
1006                        val = val.eval(valuesDict)
1007                        if val is not None and not val:
1008                            return False
1009                        gotNone = gotNone or val is None
1010                    if gotNone:
1011                        return None
1012                    return True
1013
1014            class _EvalOrOp(object):
1015                def __init__(self, tokens):
1016                    self.value = tokens[0]
1017
1018                def eval(self, valuesDict=None):
1019                    val = self.value[0].eval(valuesDict)
1020                    if val:
1021                        return True
1022                    gotNone = val is None
1023                    for op, val in _OperatorOperands(self.value[1:]):
1024                        val = val.eval(valuesDict)
1025                        if val:
1026                            return True
1027                        gotNone = gotNone or val is None
1028                    if gotNone:
1029                        return None
1030                    return False
1031
1032            # Import pyparsing and enable "packrat" mode for higher
1033            # performance. Note that pyparsing is included in the
1034            # AggregatedModules sub-package of GeoEco. It can be
1035            # imported here without referencing the sub-package
1036            # because it was already imported by means of a
1037            # PythonAggregatedModuleDependency attached to the
1038            # DatasetCollection.QueryDatasets metadata.
1039
1040            from pyparsing import CaselessKeyword, CaselessLiteral, Combine, delimitedList, nums, oneOf, opAssoc, operatorPrecedence, Optional, ParserElement, QuotedString, Regex, Word
1041            ParserElement.enablePackrat()
1042
1043            # Define the parser using pyparsing.
1044            #
1045            # booleanExpr is the parser. It takes an expression
1046            # resembling a SQL where clause and a dictionary of
1047            # variable values and returns True or False. If the
1048            # dictionary does not contain all of the variables
1049            # referenced in the expression, the parser still works,
1050            # but if a missing variable causes the value of the
1051            # expression to be indeterminate--i.e. it the result
1052            # cannot be determined without knowing the value of the
1053            # variable, the parser returns None.
1054
1055            booleanLiteral = CaselessKeyword('false') | CaselessKeyword('true')
1056            integerLiteral = Word(nums)
1057            floatLiteral = Combine(((Word(nums) + '.' + Optional(Word(nums))) | ('.' + Word(nums))) + Optional(CaselessLiteral('E') + Optional(oneOf('+ -')) + Word(nums)))
1058            dateLiteral = Combine('#' + (Regex('[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}') | Regex('[0-9]{4}-[0-9]{2}-[0-9]{2}')) + '#')
1059            stringLiteral = QuotedString("'", "\\", "''", True, False) ^ QuotedString('"', "\\", '""', True, False)
1060
1061            literal = booleanLiteral | integerLiteral | floatLiteral | dateLiteral | stringLiteral
1062            literal.setParseAction(_EvalLiteral)
1063
1064            variable = Regex('[a-zA-Z][a-zA-Z0-9_]*')
1065            variable.setParseAction(_EvalVariable)
1066
1067            arithExpr = operatorPrecedence(literal | variable,
1068                                           [(oneOf('+ -'), 1, opAssoc.RIGHT, _EvalSignOp),
1069                                            (oneOf('* /'), 2, opAssoc.LEFT, _EvalMultOp),
1070                                            (oneOf('+ -'), 2, opAssoc.LEFT, _EvalAddOp)])
1071
1072            literalList = '(' + delimitedList(literal) + ')'
1073            literalList.setParseAction(_EvalLiteralList)
1074
1075            comparisonExpr = arithExpr + (oneOf("= < > >= <= <>") | CaselessKeyword("matches")) + arithExpr | arithExpr + CaselessKeyword("in") + literalList | arithExpr + CaselessKeyword("not") + CaselessKeyword("in") + literalList
1076            comparisonExpr.setParseAction(_EvalComparisonOp)
1077             
1078            booleanExpr = operatorPrecedence(comparisonExpr,
1079                                             [(CaselessKeyword("not"), 1, opAssoc.RIGHT, _EvalNotOp),
1080                                              (CaselessKeyword("and"), 2, opAssoc.LEFT, _EvalAndOp),
1081                                              (CaselessKeyword("or"), 2, opAssoc.LEFT, _EvalOrOp)])
1082
1083            # Store the parser as a class attribute.
1084
1085            setattr(DatasetCollection, '_QueryExpressionParser', booleanExpr)
1086
1087        return DatasetCollection._QueryExpressionParser
1088
1089    def _PrepareQuery(self, expression):
1090
1091        # Parse the expression.
1092
1093        if expression is not None and len(expression) > 0:
1094            parser = self._GetQueryExpressionParser()
1095            try:
1096                parsedExpression = parser.parseString(expression, parseAll=True)[0]
1097            except Exception, e:
1098                raise ValueError(_(u'Failed to parse the query expression "%(expr)s". The parser reported %(e)s: %(msg)s') % {u'expr': expression, u'e': e.__class__.__name__, u'msg': self._Unicode(e)})
1099        else:
1100            parsedExpression = None
1101
1102        # Build a dictionary of queryable attribute values defined by
1103        # us and our chain of parent collections, so that the derived
1104        # class can use it in evaluating the query.
1105
1106        parentAttrValues = {}
1107        collection = self
1108        while collection is not None:
1109            if collection._QueryableAttributeValues is not None:
1110                parentAttrValues.update(collection._QueryableAttributeValues)
1111            collection = collection.ParentCollection
1112
1113        # Return the parsed expression and dictionary.
1114
1115        return parsedExpression, parentAttrValues
1116
1117    # Private methods that the derived class should override.
1118
1119    def _QueryDatasets(self, parsedExpression, progressReporter, options, parentAttrValues):
1120        raise NotImplementedError(_(u'The _QueryDatasets method of class %s has not been implemented.') % self.__class__.__name__)
1121
1122    def _GetOldestDataset(self, parsedExpression, options, parentAttrValues, dateTimeAttrName):
1123
1124        # The base class implementation just retrieves all of the
1125        # datasets and iterates through them to find the oldest one.
1126        # The derived class should override this if it can implement
1127        # it more efficiently.
1128
1129        datasets = self._QueryDatasets(parsedExpression, None, options, parentAttrValues)
1130        if len(datasets) <= 0:
1131            return None
1132       
1133        oldest = 0
1134        for i in range(1, len(datasets)):
1135            if datasets[i].GetQueryableAttributeValue(dateTimeAttrName) < datasets[oldest].GetQueryableAttributeValue(dateTimeAttrName):
1136                oldest = i
1137
1138        return datasets[oldest]
1139
1140    def _GetNewestDataset(self, parsedExpression, options, parentAttrValues, dateTimeAttrName):
1141
1142        # The base class implementation just retrieves all of the
1143        # datasets and iterates through them to find the newest one.
1144        # The derived class should override this if it can implement
1145        # it more efficiently.
1146
1147        datasets = self._QueryDatasets(parsedExpression, None, options, parentAttrValues)
1148        if len(datasets) <= 0:
1149            return None
1150       
1151        newest = 0
1152        for i in range(1, len(datasets)):
1153            if datasets[i].GetQueryableAttributeValue(dateTimeAttrName) > datasets[newest].GetQueryableAttributeValue(dateTimeAttrName):
1154                newest = i
1155
1156        return datasets[newest]
1157
1158    def _ImportDatasets(self, datasets, mode, reportProgress, options):
1159        raise NotImplementedError(_(u'The _ImportDatasets method of class %s has not been implemented.') % self.__class__.__name__)
1160
1161
1162class Database(object):
1163    __doc__ = DynamicDocString()
1164
1165    # Public interface.
1166
1167    def TableExists(self, tableName):
1168        # TODO: Validation
1169        exists = self._TableExists(tableName)
1170
1171        if exists:
1172            self._LogDebug(_(u'%(class)s 0x%(id)08X: The %(objectType)s %(table)s exists in %(dn)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'objectType': self._GetObjectTypeDisplayName(tableName), u'table': tableName, u'dn': self.DisplayName})
1173        else:
1174            self._LogDebug(_(u'%(class)s 0x%(id)08X: The %(objectType)s %(table)s does not exist in %(dn)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'objectType': self._GetObjectTypeDisplayName(tableName), u'table': tableName, u'dn': self.DisplayName})
1175
1176        return exists
1177
1178    def CreateTable(self, tableName, geometryType=None, spatialReference=None, geometryFieldName=None):
1179        # TODO: Validation
1180
1181        self._RequireCapability('CreateTable')
1182        if geometryType is not None:
1183            self._RequireCapability('GeometryType %s %s' % (geometryType, tableName))
1184            if geometryType is not None:
1185                self._RequireCapability('GeometryFieldName')
1186       
1187        if self.TableExists(tableName):
1188            raise RuntimeError(_(u'Cannot create %(objectType)s %(table)s in %(dn)s because that %(objectType)s already exists.') % {u'objectType': self._GetObjectTypeDisplayName(tableName), u'table': tableName, u'dn': self.DisplayName})
1189
1190        if geometryType is None:       
1191            self._LogDebug(_(u'%(class)s 0x%(id)08X: Creating %(objectType)s %(table)s in %(dn)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'objectType': self._GetObjectTypeDisplayName(tableName), u'table': tableName, u'dn': self.DisplayName})
1192        else:
1193            if spatialReference is None:
1194                wkt = None
1195            else:
1196                wkt = spatialReference.ExportToWkt()
1197            self._LogDebug(_(u'%(class)s 0x%(id)08X: Creating %(objectType)s %(table)s in %(dn)s with geometryType=%(geometryType)s, spatialReference=%(wkt)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'objectType': self._GetObjectTypeDisplayName(tableName), u'table': tableName, u'dn': self.DisplayName, u'geometryType': geometryType, u'wkt': repr(wkt)})
1198
1199        try:
1200            return self._CreateTable(tableName, geometryType, spatialReference, geometryFieldName)
1201        except Exception, e:
1202            raise RuntimeError(_(u'Failed to create %(objectType)s %(tableName)s in %(dn)s due to %(e)s: %(msg)s.') % {u'objectType': self._GetObjectTypeDisplayName(tableName), u'tableName': tableName, u'dn': self._DisplayName, u'e': e.__class__.__name__, u'msg': self._Str(e)})
1203
1204    def CreateTableFromTemplate(self, tableName, templateTable, fields=None, allowSafeCoercions=True):
1205        # TODO: Validation
1206
1207        # If the caller specified a list of fields to use, make sure
1208        # all of those fields exist.
1209
1210        createGeometryField = False
1211
1212        if fields is not None:
1213            sourceFields = []
1214            for name in fields:
1215                if name == templateTable.OIDFieldName:          # Skip the OID field, if the caller specified it.
1216                    continue
1217               
1218                if name == templateTable.GeometryFieldName:     # Also skip the geometry field.
1219                    createGeometryField = True
1220                    continue
1221               
1222                field = templateTable.GetFieldByName(name)
1223                if field is None:
1224                    raise ValueError(_(u'Cannot create a field named %(field)s in new table %(table)s because that field does not exist in %(template)s.') % {u'field': name, u'table': tableName, u'template': templateTable.DisplayName})
1225               
1226                sourceFields.append(field)
1227
1228        # Otherwise, use all of the fields except the OID and geometry
1229        # field.
1230
1231        else:
1232            createGeometryField = True
1233            sourceFields = list(templateTable.Fields)
1234            i = 0
1235            while i < len(sourceFields):
1236                if sourceFields[i].Name in [templateTable.OIDFieldName, templateTable.GeometryFieldName]:
1237                    del sourceFields[i]
1238                else:
1239                    i += 1
1240
1241        # Create the table.
1242
1243        if createGeometryField:
1244            table = self.CreateTable(tableName, templateTable.GeometryType, templateTable.GetSpatialReference('Obj'), templateTable.GeometryFieldName)
1245        else:
1246            table = self.CreateTable(tableName)
1247
1248        # Add the fields.
1249
1250        for field in sourceFields:
1251            table.AddField(field.Name, field.DataType, field.Length, field.Precision, field.IsNullable, allowSafeCoercions, True)
1252
1253        # Return successfully.
1254
1255        return table
1256
1257    def ImportTable(self, destTableName, sourceTable, fields=None, where=None, orderBy=None, rowCount=None, reportProgress=True, rowDescriptionSingular=None, rowDescriptionPlural=None, copiedOIDFieldName=None, allowSafeCoercions=True):
1258        # TODO: Validation
1259
1260        # Create the destination table.
1261       
1262        destTable = self.CreateTableFromTemplate(destTableName, sourceTable, fields, allowSafeCoercions)
1263
1264        # If the caller requested that the OID field be copied from
1265        # the source table to a new field in the destination table,
1266        # add a field for that.
1267
1268        if copiedOIDFieldName is not None:
1269            destTable.AddField(copiedOIDFieldName, 'int32', None, None, False, allowSafeCoercions, True)
1270
1271        # If the caller specified a list of fields to copy, make sure
1272        # the list includes the OID and/or geometry fields, if they
1273        # are needed.
1274
1275        if fields is not None:
1276            if copiedOIDFieldName is not None and sourceTable.OIDFieldName not in fields:
1277                fields.append(sourceTable.OIDFieldName)
1278            if destTable.GeometryType is not None and sourceTable.GeometryFieldName not in fields:
1279                fields.append(sourceTable.GeometryFieldName)
1280
1281        # Otherwise create a list of fields to copy, removing the OID
1282        # and/or geometry fields, if they are not needed.
1283
1284        else:
1285            fields = [field.Name for field in sourceTable.Fields]
1286            if copiedOIDFieldName is None:
1287                fields.remove(sourceTable.OIDFieldName)
1288            if destTable.GeometryType is None:
1289                fields.remove(sourceTable.GeometryFieldName)
1290
1291        # Open a select cursor on the source table and an insert
1292        # cursor on the destination table, and copy the rows.
1293
1294        selectCursor = sourceTable.OpenSelectCursor(fields, where, orderBy, rowCount, False, rowDescriptionSingular, rowDescriptionPlural)
1295        try:
1296            insertCursor = destTable.OpenInsertCursor(rowCount, reportProgress, rowDescriptionSingular, rowDescriptionPlural)
1297            try:
1298                while selectCursor.NextRow():
1299                    for field in fields:
1300                        if field == sourceTable.OIDFieldName:
1301                            insertCursor.SetValue(copiedOIDFieldName, selectCursor.GetOID())
1302                        elif field == sourceTable.GeometryFieldName:
1303                            insertCursor.SetGeometry(selectCursor.GetGeometry())
1304                        else:
1305                            insertCursor.SetValue(field, selectCursor.GetValue(value))
1306            finally:
1307                del insertCursor
1308        finally:
1309            del selectCursor
1310
1311
1312    def DeleteTable(self, tableName, failIfNotExists=False):
1313        # TODO: Validation
1314
1315        self._RequireCapability('DeleteTable')
1316
1317        if not self.TableExists(tableName):
1318            if failIfNotExists:
1319                raise RuntimeError(_(u'Cannot create %(objectType)s %(table)s in %(dn)s because that %(objectType)s already exists.') % {u'objectType': self._GetObjectTypeDisplayName(tableName), u'table': tableName, u'dn': self.DisplayName})
1320            self._LogDebug(_(u'%(class)s 0x%(id)08X: Not deleting %(objectType)s %(table)s from %(dn)s because that %(objectType)s does not exist.'), {u'class': self.__class__.__name__, u'id': id(self), u'objectType': self._GetObjectTypeDisplayName(tableName), u'table': tableName, u'dn': self.DisplayName})
1321            return
1322
1323        self._LogDebug(_(u'%(class)s 0x%(id)08X: Deleting %(objectType)s %(table)s from %(dn)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'objectType': self._GetObjectTypeDisplayName(tableName), u'table': tableName, u'dn': self.DisplayName})
1324
1325        try:
1326            self._DeleteTable(tableName)
1327        except Exception, e:
1328            raise RuntimeError(_(u'Failed to delete %(objectType)s %(tableName)s from %(dn)s due to %(e)s: %(msg)s.') % {u'objectType': self._GetObjectTypeDisplayName(tableName), u'tableName': tableName, u'dn': self._DisplayName, u'e': e.__class__.__name__, u'msg': self._Str(e)})
1329           
1330
1331    # Private members that the derived class should override.
1332
1333    def _GetObjectTypeDisplayName(self, tableName):
1334        return _(u'table')
1335   
1336    def _TableExists(self, tableName):
1337        raise RuntimeError(_(u'Programming error in this tool: The %(cls)s class does not define the Database._TableExists method. Please contact the author of this tool for assistance.') % {u'cls': self.__class__.__name__})
1338
1339    def _CreateTable(self, tableName, geometryType=None, spatialReference=None, geometryFieldName=u'geometry'):
1340        raise RuntimeError(_(u'Programming error in this tool: The %(cls)s class does not define the Database._CreateTable method. Please contact the author of this tool for assistance.') % {u'cls': self.__class__.__name__})
1341
1342    def _DeleteTable(self, tableName):
1343        raise RuntimeError(_(u'Programming error in this tool: The %(cls)s class does not define the Database._DeleteTable method. Please contact the author of this tool for assistance.') % {u'cls': self.__class__.__name__})
1344
1345
1346class Table(Dataset):
1347    __doc__ = DynamicDocString()
1348   
1349    # Public properties and instance methods
1350
1351    def _GetHasOID(self):
1352        return self.GetLazyPropertyValue('HasOID')
1353
1354    HasOID = property(_GetHasOID, doc=DynamicDocString())
1355
1356    def _GetOIDFieldName(self):
1357        return self.GetLazyPropertyValue('OIDFieldName')
1358
1359    OIDFieldName = property(_GetOIDFieldName, doc=DynamicDocString())
1360
1361    def _GetGeometryType(self):
1362        return self.GetLazyPropertyValue('GeometryType')
1363
1364    GeometryType = property(_GetGeometryType, doc=DynamicDocString())
1365
1366    def _GetGeometryFieldName(self):
1367        return self.GetLazyPropertyValue('GeometryFieldName')
1368
1369    GeometryFieldName = property(_GetGeometryFieldName, doc=DynamicDocString())
1370
1371    def _GetMaxStringLength(self):
1372        return self.GetLazyPropertyValue('MaxStringLength')
1373
1374    MaxStringLength = property(_GetMaxStringLength, doc=DynamicDocString())
1375
1376    def _GetFields(self):
1377        return self.GetLazyPropertyValue('Fields')
1378
1379    Fields = property(_GetFields, doc=DynamicDocString())
1380
1381    def GetFieldByName(self, name):
1382        # TODO: self.__class__.__doc__.Obj.ValidateMethodInvocation()
1383        if not hasattr(self, '_FieldsDict') or self._FieldsDict is None:
1384            self._FieldsDict = {}
1385            for field in self.Fields:
1386                self._FieldsDict[field.Name.upper()] = field
1387        if name.upper() not in self._FieldsDict:
1388            return None
1389        return self._FieldsDict[name.upper()]
1390
1391    def AddField(self, name, dataType, length=None, precision=None, isNullable=None, allowSafeCoercions=True, failIfExists=False):
1392        # TODO: self.__class__.__doc__.Obj.ValidateMethodInvocation()
1393
1394        # Verify that we have the required capabilities. If needed and
1395        # allowed by the caller, coerce unsupported data types to
1396        # supported ones.
1397
1398        self._RequireCapability('AddField')
1399
1400        if allowSafeCoercions and dataType in ['date', 'int16', 'int32', 'float32']:
1401            if self._TestCapability(dataType + ' datatype') is not None:
1402                if dataType == 'date':
1403                    newDataType = 'datetime'
1404                    if self._TestCapability(newDataType + ' datatype') is not None:
1405                        self._RequireCapability(dataType + ' DataType')
1406
1407                elif dataType == 'int16':
1408                    newDataType = 'int32'
1409                    if self._TestCapability(newDataType + ' datatype') is not None:
1410                        newDataType = 'float32'
1411                        if self._TestCapability(newDataType + ' datatype') is not None:
1412                            newDataType = 'float64'
1413                            if self._TestCapability(newDataType + ' datatype') is not None:
1414                                self._RequireCapability(dataType + ' DataType')
1415
1416                else:
1417                    newDataType = 'float64'
1418                    if self._TestCapability(newDataType + ' datatype') is not None:
1419                        self._RequireCapability(dataType + ' DataType')
1420
1421                dataType = newDataType
1422        else:
1423            self._RequireCapability(dataType + ' DataType')
1424       
1425        if isNullable:
1426            self._RequireCapability(dataType + ' IsNullable')
1427
1428        # If the data type is 'string', verify that the length
1429        # parameter was supplied and that it does not exceed the
1430        # maximum allowed length.
1431
1432        if dataType == 'string':
1433            if length is None:
1434                raise ValueError(_(u'Cannot add string field %(name)s to %(dn)s because the field length was not specified. When adding a string field, you must specify the field length.') % {u'name': name, u'dn': self.DisplayName})
1435            if self.MaxStringLength is not None and length > self.MaxStringLength:
1436                raise ValueError(_(u'Cannot add string field %(name)s to %(dn)s because the field length (%(length)i) exceeds the maximum allowed for this dataset (%(max)i).') % {u'name': name, u'length': length, u'dn': self.DisplayName, u'max': self.MaxStringLength})
1437
1438        # If the field already exists with the exact parameters
1439        # requested by the caller and the caller does not want us to
1440        # fail in that situation, return silently now.
1441
1442        existingField = self.GetFieldByName(name)
1443        if existingField is not None:
1444            if not failIfExists:
1445                if existingField.DataType == dataType and (length is None or existingField.Length >= length) and (precision is None or existingField.Precision >= precision) and (isNullable is None or existingField.IsNullable == isNullable):
1446                    self._LogDebug(_(u'%(class)s 0x%(id)08X: Not adding field with name %(Name)s because it already exists.'), {u'class': self.__class__.__name__, u'id': id(self), u'Name': name})
1447                    return
1448                raise ValueError(_(u'Cannot add field %(name)s to %(dn)s because a field with that name already exists and it has different characteristics than those you requested.') % {u'name': name, u'dn': self.DisplayName})
1449            raise ValueError(_(u'Cannot add field %(name)s to %(dn)s because a field with that name already exists.') % {u'name': name, u'dn': self.DisplayName})
1450
1451        # Call the derived class to add the field.
1452
1453        self._LogDebug(_(u'%(class)s 0x%(id)08X: Adding field %(i)i: Name=%(Name)s, DataType=%(DataType)s, Length=%(Length)s, Precision=%(Precision)s, IsNullable=%(IsNullable)s.'),
1454                       {u'class': self.__class__.__name__, u'id': id(self), u'i': len(self.Fields), u'Name': name, u'DataType': dataType, u'Length': repr(length), u'Precision': repr(precision), u'IsNullable': repr(isNullable)})
1455
1456        try:
1457            field = self._AddField(name, dataType, length, precision, isNullable)
1458        except Exception, e:
1459            raise RuntimeError(_(u'Failed to add a field named %(name)s to %(dn)s due to %(e)s: %(msg)s') % {u'name': name, u'dn': self.DisplayName, u'e': e.__class__.__name__, u'msg': self._Unicode(e)})
1460
1461        # If we succeeded, add the field to our cached list and
1462        # dictionary of fields.
1463
1464        self.Fields.append(field)
1465        self._FieldsDict[name.upper()] = field
1466
1467    def DeleteField(self, name, failIfDoesNotExist=False):
1468        # TODO: self.__class__.__doc__.Obj.ValidateMethodInvocation()
1469
1470        # Verify that we have the required capabilities.
1471
1472        self._RequireCapability('DeleteField')
1473
1474        # If the field does not already exist and the caller does not
1475        # want us to fail in that situation, return silently now.
1476
1477        existingField = self.GetFieldByName(name)
1478        if existingField is None:
1479            if not failIfDoesNotExist:
1480                self._LogDebug(_(u'%(class)s 0x%(id)08X: Not deleting field with name %(Name)s because it does not exist.'), {u'class': self.__class__.__name__, u'id': id(self), u'Name': name})
1481                return
1482            raise ValueError(_(u'Cannot delete field %(name)s from %(dn)s because a field with that name does not exist.') % {u'name': name, u'dn': self.DisplayName})
1483
1484        # Call the derived class to delete the field.
1485
1486        for i, f in enumerate(self.Fields):
1487            if f.Name == existingField.Name:
1488                break
1489
1490        self._LogDebug(_(u'%(class)s 0x%(id)08X: Deleting field %(i)i: Name=%(Name)s, DataType=%(DataType)s, Length=%(Length)s, Precision=%(Precision)s, IsNullable=%(IsNullable)s.'),
1491                       {u'class': self.__class__.__name__, u'id': id(self), u'i': i, u'Name': existingField.Name, u'DataType': existingField.DataType, u'Length': repr(existingField.Length), u'Precision': repr(existingField.Precision), u'IsNullable': repr(existingField.IsNullable)})
1492
1493        try:
1494            self._DeleteField(name)
1495        except Exception, e:
1496            raise RuntimeError(_(u'Failed to delete field %(name)s from %(dn)s due to %(e)s: %(msg)s') % {u'name': name, u'dn': self.DisplayName, u'e': e.__class__.__name__, u'msg': self._Unicode(e)})
1497
1498        # If we succeeded, remove the field from our cached list of
1499        # fields.
1500
1501        del self.Fields[i]
1502        del self._FieldsDict[existingField.Name.upper()]
1503
1504    def GetRowCount(self):
1505        try:
1506            rowCount = self._GetRowCount()
1507        except Exception, e:
1508            raise RuntimeError(_(u'Failed to obtain the count of rows in %(dn)s due to %(e)s: %(msg)s') % {u'dn': self.DisplayName, u'e': e.__class__.__name__, u'msg': self._Unicode(e)})
1509        self._LogDebug(_(u'%(class)s 0x%(id)08X: GetRowCount returned %(rc)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'rc': repr(rowCount)})
1510        return rowCount
1511
1512    def OpenSelectCursor(self, fields=None, where=None, orderBy=None, rowCount=None, reportProgress=True, rowDescriptionSingular=None, rowDescriptionPlural=None):        #TODO: Add spatial filter
1513        #TODO: self.__class__.__doc__.Obj.ValidateMethodInvocation()
1514        self._RequireCapability('SelectCursor')
1515        #TODO: self._RequireCapability('OrderBy')
1516        return self._OpenSelectCursor(fields, where, orderBy, rowCount, reportProgress, rowDescriptionSingular, rowDescriptionPlural)
1517
1518    def OpenUpdateCursor(self, fields=None, where=None, orderBy=None, rowCount=None, reportProgress=True, rowDescriptionSingular=None, rowDescriptionPlural=None):        #TODO: Add spatial filter
1519        #TODO: self.__class__.__doc__.Obj.ValidateMethodInvocation()
1520        self._RequireCapability('UpdateCursor')
1521        #TODO: self._RequireCapability('OrderBy')
1522        return self._OpenUpdateCursor(fields, where, orderBy, rowCount, reportProgress, rowDescriptionSingular, rowDescriptionPlural)
1523
1524    def OpenInsertCursor(self, rowCount=None, reportProgress=True, rowDescriptionSingular=None, rowDescriptionPlural=None):
1525        #TODO: self.__class__.__doc__.Obj.ValidateMethodInvocation()
1526        self._RequireCapability('InsertCursor')
1527        return self._OpenInsertCursor(rowCount, reportProgress, rowDescriptionSingular, rowDescriptionPlural)
1528
1529    # Private instance methods that the derived class should override
1530    # if the capability is supported
1531
1532    def _AddField(self, name, dataType, length, precision, isNullable):
1533        raise NotImplementedError(_(u'The _AddField method of class %s has not been implemented.') % self.__class__.__name__)
1534
1535    def _DeleteField(self, name):
1536        raise NotImplementedError(_(u'The _DeleteField method of class %s has not been implemented.') % self.__class__.__name__)
1537
1538    def _GetRowCount(self):
1539        raise NotImplementedError(_(u'The _GetRowCount method of class %s has not been implemented.') % self.__class__.__name__)
1540
1541    def _OpenSelectCursor(self, fields, where, orderBy, rowCount, reportProgress, rowDescriptionSingular, rowDescriptionPlural):
1542        raise NotImplementedError(_(u'The _OpenSelectCursor method of class %s has not been implemented.') % self.__class__.__name__)
1543
1544    def _OpenUpdateCursor(self, fields, where, orderBy, rowCount, reportProgress, rowDescriptionSingular, rowDescriptionPlural):
1545        raise NotImplementedError(_(u'The _OpenUpdateCursor method of class %s has not been implemented.') % self.__class__.__name__)
1546
1547    def _OpenInsertCursor(self, rowCount, reportProgress, rowDescriptionSingular, rowDescriptionPlural):
1548        raise NotImplementedError(_(u'The _OpenInsertCursor method of class %s has not been implemented.') % self.__class__.__name__)
1549
1550
1551class Field(object):
1552    __doc__ = DynamicDocString()
1553
1554    # Public properties
1555
1556    def _GetName(self):
1557        return self._Name
1558
1559    def _GetDataType(self):
1560        return self._DataType
1561
1562    def _GetLength(self):
1563        return self._Length
1564
1565    def _GetPrecision(self):
1566        return self._Precision
1567
1568    def _GetIsNullable(self):
1569        return self._IsNullable
1570
1571    def _GetIsSettable(self):
1572        return self._IsSettable
1573
1574    Name = property(_GetName, doc=DynamicDocString())
1575    DataType = property(_GetDataType, doc=DynamicDocString())
1576    Length = property(_GetLength, doc=DynamicDocString())
1577    Precision = property(_GetPrecision, doc=DynamicDocString())
1578    IsNullable = property(_GetIsNullable, doc=DynamicDocString())
1579    IsSettable = property(_GetIsSettable, doc=DynamicDocString())
1580
1581    # Private properties and instance methods that the derived class
1582    # may access but generally does not override
1583
1584    def __init__(self, name, dataType, length, precision, isNullable, isSettable):
1585        self._Name = name
1586        self._DataType = dataType
1587        self._Length = length
1588        self._Precision = precision
1589        self._IsNullable = isNullable
1590        self._IsSettable = isSettable
1591
1592
1593class _Cursor(object):
1594    __doc__ = DynamicDocString()
1595   
1596    # Public properties and instance methods
1597
1598    def _GetTable(self):
1599        return self._Table
1600
1601    Table = property(_GetTable, doc=DynamicDocString())
1602
1603    def _GetRowDescriptionSingular(self):
1604        return self._RowDescriptionSingular
1605
1606    RowDescriptionSingular = property(_GetRowDescriptionSingular, doc=DynamicDocString())
1607
1608    def _GetRowDescriptionPlural(self):
1609        return self._RowDescriptionPlural
1610
1611    RowDescriptionPlural = property(_GetRowDescriptionPlural, doc=DynamicDocString())
1612
1613    def _GetIsOpen(self):
1614        return self._IsOpen
1615
1616    IsOpen = property(_GetIsOpen, doc=DynamicDocString())
1617
1618    def Close(self):
1619        if hasattr(self, '_IsOpen') and self._IsOpen:
1620            if hasattr(self, '_ProgressReporter') and self._ProgressReporter is not None and self._ProgressReporter.HasStarted and not self._ProgressReporter.HasCompleted:
1621                self._ProgressReporter.Stop()
1622            self._Table._LogDebug(_(u'%(class)s 0x%(id)08X: Closing cursor on %(dn)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'dn': self._Table.DisplayName})
1623            try:
1624                self._Close()
1625            except:
1626                pass
1627
1628            self._IsOpen = False
1629
1630            for i, r in enumerate(_Cursor._CursorsToCloseAtExit):
1631                if r() == self:
1632                    del _Cursor._CursorsToCloseAtExit[i]
1633                    break
1634
1635        self._Table = None
1636
1637    def SetRowCount(self, rowCount):
1638        # TODO: self.__class__.__doc__.Obj.ValidateMethodInvocation()
1639
1640        if not self._IsOpen:
1641            raise RuntimeError(_(u'The cursor is closed.'))
1642
1643        if hasattr(self, '_ProgressReporter') and self._ProgressReporter is not None and not self._ProgressReporter.HasCompleted:
1644            self._ProgressReporter.TotalOperations = rowCount
1645
1646    # The rest of the methods of this class are private and are not
1647    # intended to be invoked by external callers.
1648
1649    def __init__(self, table, rowDescriptionSingular, rowDescriptionPlural):
1650        self._Table = table
1651        self._IsOpen = False
1652
1653        if rowDescriptionSingular is None and rowDescriptionPlural is None:
1654            if self._Table.GeometryType in ['Point', 'Point25D']:
1655                self._RowDescriptionSingular = _(u'point')
1656                self._RowDescriptionPlural = _(u'points')
1657            elif self._Table.GeometryType in ['LineString', 'LineString25D', 'MultiLineString', 'MultiLineString25D']:
1658                self._RowDescriptionSingular = _(u'line')
1659                self._RowDescriptionPlural = _(u'lines')
1660            elif self._Table.GeometryType in ['Polygon', 'Polygon25D', 'MultiPolygon', 'MultiPolygon25D']:
1661                self._RowDescriptionSingular = _(u'polygon')
1662                self._RowDescriptionPlural = _(u'polygons')
1663            elif self._Table.GeometryType in ['MultiPoint', 'MultiPoint25D']:
1664                self._RowDescriptionSingular = _(u'multipoint')
1665                self._RowDescriptionPlural = _(u'multipoints')
1666            elif self._Table.GeometryType in ['GeometryCollection', 'GeometryCollection25D']:
1667                self._RowDescriptionSingular = _(u'geometry collection')
1668                self._RowDescriptionPlural = _(u'geometry collections')
1669            else:
1670                self._RowDescriptionSingular = _(u'row')
1671                self._RowDescriptionPlural = _(u'rows')
1672        else:
1673            self._RowDescriptionSingular = rowDescriptionSingular
1674            self._RowDescriptionPlural = rowDescriptionPlural
1675
1676    def __del__(self):
1677        self.Close()
1678
1679    def _Close(self):                               # If the derived class overrides _Close(), it should call the base class at the end of its implementation.
1680        pass
1681
1682    def _SetValue_Base(self, field, value, requestedFields=None):
1683
1684        if not self._IsOpen:
1685            raise RuntimeError(_(u'The cursor is closed.'))
1686
1687        # Validate that field exists and that it is not the OID or
1688        # geometry field.
1689
1690        if not isinstance(field, basestring):
1691            raise TypeError(_(u'The field parameter must be a string.'))
1692
1693        f = self._Table.GetFieldByName(field)
1694        if f is None:
1695            raise RuntimeError(_(u'Cannot set the value of the "%(field)s" field of this %(singular)s of %(dn)s to %(value)s because the field does not exist.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': field, u'value': repr(value)})
1696
1697        if requestedFields is not None and f.Name not in requestedFields:
1698            raise RuntimeError(_(u'Cannot retrieve the value of the %(field)s field of this %(singular)s of %(dn)s. The field exists but was not included in the list of requested fields when the cursor was opened.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': f.Name})
1699
1700        if f.Name == self._Table.OIDFieldName:
1701            raise RuntimeError(_(u'Cannot set the value of the %(field)s field of this %(singular)s of %(dn)s to %(value)s because that field is the object ID (OID) or feature ID (FID) field, which is read-only.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': f.Name, u'value': repr(value)})
1702
1703        if f.Name == self._Table.GeometryFieldName:
1704            raise RuntimeError(_(u'Cannot set the value of the %(field)s field of this %(singular)s of %(dn)s to %(value)s because that field is the geometry field. To set the geometry field, call SetGeometry() rather than SetValue().') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': f.Name, u'value': repr(value)})
1705
1706        # Validate that the field is settable. Non-settable fields are
1707        # those managed by the underlying data format or programming
1708        # library. These include the OID field (which is handled
1709        # above) as well as the ArcGIS SHAPE_Length and SHAPE_Area
1710        # fields, which are set by ArcGIS.
1711
1712        if not f.IsSettable:
1713            raise RuntimeError(_(u'Cannot set the value of the %(field)s field of this %(singular)s of %(dn)s to %(value)s because that field is read-only; its values are managed by the underlying data format.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': f.Name, u'value': repr(value)})
1714
1715        # If the caller is trying to set the field to null, validate
1716        # that the field is nullable.
1717
1718        if value is None:
1719            if not f.IsNullable:
1720                raise RuntimeError(_(u'Cannot set the value of the %(field)s field of this %(singular)s of %(dn)s to None because that field is not nullable. You must provide a value for that field.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': f.Name})
1721
1722        # Otherwise, validate that the value's data type matches that
1723        # of the field, that it is within an acceptable range, etc.
1724
1725        else:
1726            if f.DataType == u'int16' or f.DataType == u'int32':
1727                if not isinstance(value, (types.IntType, types.LongType)):
1728                    raise TypeError(_(u'Cannot set the value of the %(field)s field of this %(singular)s of %(dn)s to %(value)s because the data type of that field is %(dt)s. To set %(dt)s fields, you must provide an instance of %(type1)s or %(type2)s.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': f.Name, u'value': repr(value), u'dt': f.DataType, u'type1': unicode(types.IntType), u'type2': unicode(types.LongType)})
1729                if f.DataType == u'int16' and (value < -32768 or value > 32767):
1730                    raise ValueError(_(u'Cannot set the value of the %(field)s field of this %(singular)s of %(dn)s to %(value)s because that value is outside of the allowed range of values for the field (-32768 to 32767).') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': f.Name, u'value': repr(value)})
1731                if f.DataType == u'int32' and (value < -2147483648 or value > 2147483647):
1732                    raise ValueError(_(u'Cannot set the value of the %(field)s field of this %(singular)s of %(dn)s to %(value)s because that value is outside of the allowed range of values for the field (-2147483648 to 2147483647).') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': f.Name, u'value': repr(value)})
1733                value = int(value)
1734
1735            elif f.DataType == u'float32' or f.DataType == u'float64':
1736                if not isinstance(value, types.FloatType):
1737                    raise TypeError(_(u'Cannot set the value of the %(field)s field of this %(singular)s of %(dn)s to %(value)s because the data type of that field is %(dt)s. To set %(dt)s fields, you must provide an instance of %(type)s.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': f.Name, u'value': repr(value), u'dt': f.DataType, u'type': unicode(types.FloatType)})
1738
1739                # I'm not doing a range check on float32 because it is
1740                # too complicated to implement correctly and it seems
1741                # unlikely that anyone would exceed the range of
1742                # float32 in normal circumstances. If they do, the
1743                # underlying programming library will hopefully catch
1744                # the problem.
1745
1746            elif f.DataType == u'string':
1747                if not isinstance(value, basestring):
1748                    raise TypeError(_(u'Cannot set the value of the %(field)s field of this %(singular)s of %(dn)s to %(value)s because the data type of that field is %(dt)s. To set %(dt)s fields, you must provide an instance of %(type1)s or %(type2)s.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': f.Name, u'value': repr(value), u'dt': f.DataType, u'type1': unicode(types.StringType), u'type2': unicode(types.UnicodeType)})
1749                if f.Length is not None and len(value) > f.Length:
1750                    raise TypeError(_(u'Cannot set the value of the %(field)s field of this %(singular)s of %(dn)s to %(value)s because the length of that string (%(len1)i) exceeds the maximum allowed by the field (%(len2)i).') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': f.Name, u'value': repr(value), u'len1': len(value), u'len2': f.Length})
1751
1752            elif f.DataType == u'date' or f.DataType == u'datetime':
1753                if not isinstance(value, (datetime.date, datetime.datetime)):
1754                    raise TypeError(_(u'Cannot set the value of the %(field)s field of this %(singular)s of %(dn)s to %(value)s because the data type of that field is %(dt)s. To set %(dt)s fields, you must provide an instance of %(type1)s or %(type2)s.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': f.Name, u'value': repr(value), u'dt': f.DataType, u'type1': unicode(datetime.date), u'type2': unicode(datetime.datetime)})
1755
1756            elif f.DataType == u'binary':
1757                if not isinstance(value, types.StringType):
1758                    raise TypeError(_(u'Cannot set the value of the %(field)s field of this %(singular)s of %(dn)s to %(value)s because the data type of that field is binary. To set binary fields, you must provide an instance of %(type)s.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': f.Name, u'value': repr(value), u'type': unicode(types.StringType)})
1759
1760        # Set the field.
1761
1762        if Dataset._DebugLoggingEnabled():
1763            self._Table._LogDebug(_(u'%(class)s 0x%(id)08X: Setting field %(field)s to %(value)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'field': f.Name, u'value': repr(value)})
1764
1765        try:
1766            self._SetValue(field, value)
1767        except Exception, e:
1768            raise RuntimeError(_(u'Failed to set the value of the %(field)s field of a %(singular)s from %(dn)s to %(value)s due to %(e)s: %(msg)s') % {u'field': f.Name, u'value': repr(value), u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'e': e.__class__.__name__, u'msg': unicode(e)})
1769
1770    def _SetGeometry_Base(self, geometry, requestedFields=None):
1771
1772        if not self._IsOpen:
1773            raise RuntimeError(_(u'The cursor is closed.'))
1774
1775        # Validate that the dataset has geometry and that the caller's
1776        # geometry type matches the dataset's geometry type.
1777
1778        if self._Table.GeometryType is None:
1779            raise RuntimeError(_(u'Cannot set the geometry of this %(singular)s of %(dn)s because the %(singular)s does not have geometry.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName})
1780
1781        if requestedFields is not None and self._Table.GeometryFieldName is not None and self._Table.GeometryFieldName not in requestedFields:
1782            raise RuntimeError(_(u'Cannot retrieve the geometry of this %(singular)s of %(dn)s. The %(singular)s has geometry but the geometry field (%(field)s) was not included in the list of requested fields when the cursor was opened.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': self._Table.GeometryFieldName})
1783
1784        if geometry is None:
1785            raise ValueError(_(u'Cannot set the geometry of this %(singular)s of %(dn)s to None because null geometries are not supported.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName})
1786
1787        if not hasattr(geometry, '__class__') or geometry.__class__.__name__ != 'Geometry':   # Test by name rather than isinstance to allow caller to pass in Geometry instances allocated by their own OGR module rather than MGET's assimilated copy
1788            raise TypeError(_(u'The geometry parameter must be an instance of the OGR Geometry class.'))
1789
1790        Dataset._ogr()
1791        if geometry.GetGeometryType() not in Dataset._GeometryTypeForOGRGeometry:
1792            raise ValueError(_(u'Cannot set the geometry of this %(singular)s of %(dn)s because the provided Geometry object has the geometry type %(gt)i ("%(gtname)s"), which is not currently supported.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'gt': geometry.GetGeometryType(), u'gtname': geometry.GetGeometryName()})
1793
1794        geometryType = Dataset._GeometryTypeForOGRGeometry[geometry.GetGeometryType()]
1795        if geometryType != self._Table.GeometryType:
1796            if self._Table.GeometryType in [u'Point', u'LineString', u'Polygon', u'Point25D', u'LineString25D', u'Polygon25D']:
1797                raise ValueError(_(u'Cannot set the geometry of this %(singular)s of %(dn)s because the provided Geometry object has the incompatible geometry type %(gt1)s. The Geometry object must have a geometry type of %(gt2)s.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'gt1': geometryType, u'gt2': self._Table.GeometryType})
1798            elif self._Table.GeometryType.startswith(u'Multi') and geometryType != self._Table.GeometryType[5:]:
1799                raise ValueError(_(u'Cannot set the geometry of this %(singular)s of %(dn)s because the provided Geometry object has the incompatible geometry type %(gt1)s. The Geometry object must have a geometry type of %(gt2)s or %(gt3)s.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'gt1': geometryType, u'gt2': self._Table.GeometryType[5:], u'gt3': self._Table.GeometryType})
1800            elif self._Table.GeometryType == u'GeometryCollection' and geometryType.endswith(u'25D'):
1801                raise ValueError(_(u'Cannot set the geometry of this %(singular)s of %(dn)s because the provided Geometry object has Z coordinates but the dataset does not.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName})
1802            elif self._Table.GeometryType == u'GeometryCollection25D' and not geometryType.endswith(u'25D'):
1803                raise ValueError(_(u'Cannot set the geometry of this %(singular)s of %(dn)s because the provided Geometry object does not have Z coordinates but the dataset requires them.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName})
1804       
1805        # Set the geometry.
1806
1807        if Dataset._DebugLoggingEnabled():
1808            wkt = geometry.ExportToWkt()
1809            if len(wkt) > 256:
1810                wkt = wkt[:256] + '...'
1811            self._Table._LogDebug(_(u'%(class)s 0x%(id)08X: Setting geometry to WKT "%(wkt)s".'), {u'class': self.__class__.__name__, u'id': id(self), u'wkt': wkt})
1812
1813        try:
1814            self._SetGeometry(geometry)
1815        except Exception, e:
1816            wkt = geometry.ExportToWkt()
1817            if len(wkt) > 256:
1818                wkt = wkt[:256] + '...'
1819            raise RuntimeError(_(u'Failed to set the geometry of a %(singular)s from %(dn)s to the equivalent of WKT "%(wkt)s" due to %(e)s: %(msg)s') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'wkt': wkt, u'e': e.__class__.__name__, u'msg': unicode(e)})
1820
1821    @staticmethod
1822    def _CloseCursorsAtExit():
1823        if hasattr(_Cursor, '_CursorsToCloseAtExit'):
1824            while len(_Cursor._CursorsToCloseAtExit) > 0:
1825                if _Cursor._CursorsToCloseAtExit[0]() is None:      # Should never happen because _Cursor.__del__() removes the object from the list
1826                    del _Cursor._CursorsToCloseAtExit[0]
1827                else:
1828                    _Cursor._CursorsToCloseAtExit[0]().Close()      # This will remove it from the list.
1829
1830atexit.register(_Cursor._CloseCursorsAtExit)
1831
1832
1833class SelectCursor(_Cursor):
1834    __doc__ = DynamicDocString()
1835   
1836    # Public properties and instance methods
1837
1838    def _GetAtEnd(self):
1839        return bool(self._AtEnd)
1840
1841    AtEnd = property(_GetAtEnd, doc=DynamicDocString())
1842
1843    def NextRow(self):
1844        if self._AtEnd:
1845            raise IndexError(_(u'The last %(singular)s has already been retrieved.') % {u'singular': self._RowDescriptionSingular})
1846
1847        if not self._IsOpen:
1848            raise RuntimeError(_(u'The cursor is closed.'))
1849
1850        if Dataset._DebugLoggingEnabled():
1851            self._Table._LogDebug(_(u'%(class)s 0x%(id)08X: Retrieving a %(singular)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'singular': self._RowDescriptionSingular})
1852
1853        try:
1854            rowAvailable = self._NextRow()
1855        except Exception, e:
1856            raise RuntimeError(_(u'Failed to retrieve a %(singular)s from %(dn)s due to %(e)s: %(msg)s') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'e': e.__class__.__name__, u'msg': unicode(e)})
1857
1858        if rowAvailable:
1859            self._AtEnd = False
1860            if self._ProgressReporter is not None:
1861                self._ProgressReporter.ReportProgress()
1862        else:
1863            self._AtEnd = True
1864            self._Table._LogDebug(_(u'%(class)s 0x%(id)08X: No more %(plural)s available.'), {u'class': self.__class__.__name__, u'id': id(self), u'plural': self._RowDescriptionPlural})
1865            self.Close()
1866
1867        return rowAvailable
1868
1869    def GetValue(self, field):
1870
1871        # Validate that a row has been retrieved and that we're not at
1872        # the end.
1873       
1874        if self._IsOpen and self._AtEnd is None:
1875            raise RuntimeError(_(u'The first %(singular)s has not been retrieved yet. Call NextRow() before calling GetValue().') % {u'singular': self._RowDescriptionSingular})
1876       
1877        if self._AtEnd:
1878            raise RuntimeError(_(u'All of the %(plural)s have already been retrieved. Do not call GetValue() after NextRow() has returned False.') % {u'plural': self._RowDescriptionPlural})
1879
1880        if not self._IsOpen:
1881            raise RuntimeError(_(u'The cursor is closed.'))
1882
1883        # Validate that the field exists and that the caller requested
1884        # it when opening the cursor.
1885
1886        if not isinstance(field, basestring):
1887            raise TypeError(_(u'The field parameter must be a string.'))
1888
1889        f = self._Table.GetFieldByName(field)
1890        if f is None:
1891            raise RuntimeError(_(u'Cannot retrieve the value of field "%(field)s" of this %(singular)s of %(dn)s because the field does not exist.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': field})
1892
1893        if self._RequestedFields is not None and f.Name not in self._RequestedFields:
1894            raise RuntimeError(_(u'Cannot retrieve the value of the %(field)s field of this %(singular)s of %(dn)s. The field exists but was not included in the list of requested fields when the cursor was opened.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': f.Name})
1895
1896        # Validate that it is not the geometry field.
1897
1898        if f.Name == self._Table.GeometryFieldName:
1899            raise RuntimeError(_(u'Cannot retrieve the value of the %(field)s field of this %(singular)s of %(dn)s because that field is the geometry field. To get the geometry field, call GetGeometry() rather than GetValue().') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': f.Name})
1900
1901        # Get and return the value of the field.
1902
1903        try:
1904            value = self._GetValue(field)
1905        except Exception, e:
1906            raise RuntimeError(_(u'Failed to retrieve the value of the %(field)s field of a %(singular)s from %(dn)s due to %(e)s: %(msg)s') % {u'field': f.Name, u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'e': e.__class__.__name__, u'msg': unicode(e)})
1907
1908        if Dataset._DebugLoggingEnabled():
1909            self._Table._LogDebug(_(u'%(class)s 0x%(id)08X: Get field %(field)s returned %(value)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'field': f.Name, u'value': repr(value)})
1910
1911        return value
1912
1913    def GetOID(self):
1914
1915        # Validate that a row has been retrieved and that we're not at
1916        # the end.
1917       
1918        if self._IsOpen and self._AtEnd is None:
1919            raise RuntimeError(_(u'The first %(singular)s has not been retrieved yet. Call NextRow() before calling GetOID().') % {u'singular': self._RowDescriptionSingular})
1920       
1921        if self._AtEnd:
1922            raise RuntimeError(_(u'All of the %(plural)s have already been retrieved. Do not call GetOID() after NextRow() has returned False.') % {u'plural': self._RowDescriptionPlural})
1923
1924        if not self._IsOpen:
1925            raise RuntimeError(_(u'The cursor is closed.'))
1926
1927        # Validate that the dataset has an OID, and if it has an OID
1928        # field, that the caller requested that field when opening the
1929        # cursor.
1930
1931        if self._Table.HasOID is None:
1932            raise RuntimeError(_(u'Cannot retrieve the object ID (OID) of this %(singular)s of %(dn)s because the %(singular)s does not have an OID.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName})
1933
1934        if self._RequestedFields is not None and self._Table.OIDFieldName is not None and self._Table.OIDFieldName not in self._RequestedFields:
1935            raise RuntimeError(_(u'Cannot retrieve the object ID (OID) of this %(singular)s of %(dn)s. The %(singular)s has an OID but the OID field (%(field)s) was not included in the list of requested fields when the cursor was opened.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': self._Table.OIDFieldName})
1936
1937        # Get and return the OID.
1938
1939        try:
1940            oid = self._GetOID()
1941        except Exception, e:
1942            raise RuntimeError(_(u'Failed to retrieve the object ID (OID) of a %(singular)s from %(dn)s due to %(e)s: %(msg)s') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'e': e.__class__.__name__, u'msg': unicode(e)})
1943
1944        if Dataset._DebugLoggingEnabled():
1945            self._Table._LogDebug(_(u'%(class)s 0x%(id)08X: Get OID returned "%(oid)s".'), {u'class': self.__class__.__name__, u'id': id(self), u'oid': repr(oid)})
1946
1947        return oid
1948
1949    def GetGeometry(self):
1950
1951        # Validate that a row has been retrieved and that we're not at
1952        # the end.
1953       
1954        if self._IsOpen and self._AtEnd is None:
1955            raise RuntimeError(_(u'The first %(singular)s has not been retrieved yet. Call NextRow() before calling GetGeometry().') % {u'singular': self._RowDescriptionSingular})
1956       
1957        if self._AtEnd:
1958            raise RuntimeError(_(u'All of the %(plural)s have already been retrieved. Do not call GetGeometry() after NextRow() has returned False.') % {u'plural': self._RowDescriptionPlural})
1959
1960        if not self._IsOpen:
1961            raise RuntimeError(_(u'The cursor is closed.'))
1962
1963        # Validate that the dataset has geometry, and if it has a
1964        # geometry field, that the caller requested that field when
1965        # opening the cursor.
1966
1967        if self._Table.GeometryType is None:
1968            raise RuntimeError(_(u'Cannot retrieve the geometry of this %(singular)s of %(dn)s because the %(singular)s does not have geometry.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName})
1969
1970        if self._RequestedFields is not None and self._Table.GeometryFieldName is not None and self._Table.GeometryFieldName not in self._RequestedFields:
1971            raise RuntimeError(_(u'Cannot retrieve the geometry of this %(singular)s of %(dn)s. The %(singular)s has geometry but the geometry field (%(field)s) was not included in the list of requested fields when the cursor was opened.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': self._Table.GeometryFieldName})
1972
1973        # Get and return the geometry.
1974
1975        try:
1976            geometry = self._GetGeometry()
1977        except Exception, e:
1978            raise RuntimeError(_(u'Failed to retrieve the geometry of a %(singular)s from %(dn)s due to %(e)s: %(msg)s') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'e': e.__class__.__name__, u'msg': unicode(e)})
1979
1980        if Dataset._DebugLoggingEnabled():
1981            wkt = geometry.ExportToWkt()
1982            if len(wkt) > 256:
1983                wkt = wkt[:256] + '...'
1984            self._Table._LogDebug(_(u'%(class)s 0x%(id)08X: Get geometry returned WKT "%(wkt)s".'), {u'class': self.__class__.__name__, u'id': id(self), u'wkt': wkt})
1985
1986        return geometry
1987
1988    # Private base class constructor. Do not invoke directly; use
1989    # Table.OpenSelectCursor instead. Do not override; put
1990    # your initialization code the derive class's _Open method.
1991
1992    def __init__(self, dataset, fields, where, orderBy, rowCount, reportProgress, rowDescriptionSingular, rowDescriptionPlural):
1993
1994        # Initialize the base class and our attributes.
1995
1996        super(SelectCursor, self).__init__(dataset, rowDescriptionSingular, rowDescriptionPlural)
1997       
1998        self._AtEnd = None
1999        self._RequestedFields = None
2000
2001        # Validate that the caller is requesting existing fields.
2002
2003        if fields is not None:
2004            self._RequestedFields = set()
2005            for field in fields:
2006                f = self._Table.GetFieldByName(field)
2007                if f is None:
2008                    raise ValueError(_(u'Cannot retrieve field "%(field)s" of %(dn)s because the field does not exist.') % {'dn': self._Table.DisplayName, u'field': field})
2009                self._RequestedFields.add(f.Name)
2010            fields = list(self._RequestedFields)
2011
2012        # Validate that the orderBy expression references valid fields
2013        # and includes valid sort orders.
2014
2015        if orderBy is not None:
2016            orderByList = map(lambda s: s.strip(), orderBy.split(','))
2017            for i in range(len(orderByList)):
2018                parts = orderByList[i].split()
2019                if len(parts) not in [1, 2] or len(parts) == 2 and parts[1].upper() not in ['A', 'ASC', 'ASCENDING', 'D', 'DESC', 'DESCENDING']:
2020                    raise ValueError(_(u'Cannot retrieve %(plural)s from %(dn)s using the ORDER BY expression "%(orderBy)s". The expression is invalid.') % {'plural': self._RowDescriptionPlural, u'dn': dataset.DisplayName, u'orderBy': orderBy})
2021                f = self._Table.GetFieldByName(parts[0])
2022                if f is None:
2023                    raise ValueError(_(u'Cannot retrieve %(plural)s from %(dn)s using the ORDER BY expression "%(orderBy)s". The expression refers to a field "%(field)s" that does not exist.') % {'plural': self._RowDescriptionPlural, u'dn': dataset.DisplayName, u'orderBy': orderBy, u'field': parts[0]})
2024                if len(parts) == 1 or parts[1][0].upper() == 'A':
2025                    orderByList[i] = f.Name + ' ASC'
2026                else:
2027                    orderByList[i] = f.Name + ' DESC'
2028            orderBy = ', '.join(orderByList)
2029
2030        # Call the derived class to open the cursor.
2031
2032        if isinstance(self, UpdateCursor):
2033            cursorType = _(u'update')
2034        else:
2035            cursorType = _(u'select')
2036       
2037        self._Table._LogDebug(_(u'%(class)s 0x%(id)08X: Opening %(curtype)s cursor on %(dn)s, fields=%(fields)s, where=%(where)s, orderBy=%(orderBy)s, rowCount=%(rc)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'dn': dataset.DisplayName, u'curtype': cursorType, u'fields': repr(fields), u'where': repr(where), u'orderBy': repr(orderBy), u'rc': repr(rowCount)})
2038        try:
2039            self._Open(fields, where, orderBy)
2040        except Exception, e:
2041            self._Table = None
2042            raise RuntimeError(_(u'Cannot retrieve %(plural)s from %(dn)s. Failed to open a %(curtype)s cursor due to %(e)s: %(msg)s') % {u'plural': self._RowDescriptionPlural, u'dn': dataset.DisplayName, u'curtype': cursorType, u'e': e.__class__.__name__, u'msg': unicode(e)})
2043
2044        self._IsOpen = True
2045
2046        if not hasattr(_Cursor, '_CursorsToCloseAtExit'):
2047            _Cursor._CursorsToCloseAtExit = []
2048        _Cursor._CursorsToCloseAtExit.insert(0, weakref.ref(self))
2049
2050        # If the caller wants progress to be reported, start a
2051        # progress reporter.
2052
2053        if reportProgress:
2054            self._ProgressReporter = ProgressReporter(progressMessage1=_(u'Still retrieving %(plural)s: %%(elapsed)s elapsed, %%(opsCompleted)i %(plural)s retrieved, %%(perOp)s per %(singular)s, %%(opsRemaining)i remaining, estimated completion time: %%(etc)s.') % {u'singular': self._RowDescriptionSingular, u'plural': self._RowDescriptionPlural},
2055                                                      progressMessage2=_(u'Still retrieving %(plural)s: %%(elapsed)s elapsed, %%(opsCompleted)i %(plural)s retrieved, %%(perOp)s per %(singular)s.') % {u'singular': self._RowDescriptionSingular, u'plural': self._RowDescriptionPlural},
2056                                                      completionMessage=_(u'Finished retrieving %(plural)s: %%(elapsed)s elapsed, %%(opsCompleted)i %(plural)s retrieved, %%(perOp)s per %(singular)s.') % {u'singular': self._RowDescriptionSingular, u'plural': self._RowDescriptionPlural},
2057                                                      abortedMessage=_(u'Query operation stopped before all %(plural)s were retrieved: %%(elapsed)s elapsed, %%(opsCompleted)i %(plural)s retrieved, %%(perOp)s per %(singular)s, %%(opsIncomplete)i %(plural)s not retrieved.') % {u'singular': self._RowDescriptionSingular, u'plural': self._RowDescriptionPlural})
2058            self._ProgressReporter.Start(rowCount)
2059        else:
2060            self._ProgressReporter = None
2061
2062    # Private methods that the derived class must override (except
2063    # _GetOID and _GetGeometry, if appropriate).
2064
2065    def _Open(self, fields, where, orderBy):
2066        raise NotImplementedError(_(u'The _Open method of class %s has not been implemented.') % self.__class__.__name__)
2067
2068    def _NextRow(self):
2069        raise NotImplementedError(_(u'The _NextRow method of class %s has not been implemented.') % self.__class__.__name__)
2070
2071    def _GetValue(self, field):
2072        raise NotImplementedError(_(u'The _GetValue method of class %s has not been implemented.') % self.__class__.__name__)
2073
2074    def _GetOID(self):
2075        raise NotImplementedError(_(u'The _GetOID method of class %s has not been implemented.') % self.__class__.__name__)
2076
2077    def _GetGeometry(self):
2078        raise NotImplementedError(_(u'The _GetGeometry method of class %s has not been implemented.') % self.__class__.__name__)
2079
2080
2081class UpdateCursor(SelectCursor):
2082    __doc__ = DynamicDocString()
2083   
2084    # Public properties and instance methods
2085
2086    def NextRow(self):
2087        if self._AtEnd:
2088            raise IndexError(_(u'The last %(singular)s has already been retrieved.') % {u'singular': self._RowDescriptionSingular})
2089
2090        if not self._IsOpen:
2091            raise RuntimeError(_(u'The cursor is closed.'))
2092
2093        if self._ProgressReporter is not None and self._NeedToReportProgress:
2094            self._ProgressReporter.ReportProgress()
2095            self._NeedToReportProgress = False
2096
2097        if Dataset._DebugLoggingEnabled():
2098            self._Table._LogDebug(_(u'%(class)s 0x%(id)08X: Retrieving a %(singular)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'singular': self._RowDescriptionSingular})
2099
2100        self._RowUpdatedOrDeleted = False
2101
2102        try:
2103            rowAvailable = self._NextRow()
2104        except Exception, e:
2105            raise RuntimeError(_(u'Failed to retrieve a %(singular)s from %(dn)s due to %(e)s: %(msg)s') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'e': e.__class__.__name__, u'msg': unicode(e)})
2106
2107        if rowAvailable:
2108            self._AtEnd = False
2109            self._NeedToReportProgress = self._ProgressReporter is not None
2110        else:
2111            self._AtEnd = True
2112            self._Table._LogDebug(_(u'%(class)s 0x%(id)08X: No more %(plural)s available.'), {u'class': self.__class__.__name__, u'id': id(self), u'plural': self._RowDescriptionPlural})
2113            self.Close()
2114
2115        return rowAvailable
2116
2117    def GetValue(self, field):
2118
2119        # Validate that we haven't updated or deleted this row yet and
2120        # call the base class method. The base class will take care of
2121        # the other validation.
2122
2123        if self._RowUpdatedOrDeleted:
2124            raise RuntimeError(_(u'This %(singular)s has already been updated or deleted. Do not call GetValue() after calling UpdateRow() or DeleteRow(). Call NextRow() after calling UpdateRow() or DeleteRow().') % {u'singular': self._RowDescriptionSingular})
2125       
2126        return super(UpdateCursor, self).GetValue(field)
2127
2128    def GetOID(self):
2129
2130        # Validate that we haven't updated or deleted this row yet and
2131        # call the base class method. The base class will take care of
2132        # the other validation.
2133
2134        if self._RowUpdatedOrDeleted:
2135            raise RuntimeError(_(u'This %(singular)s has already been updated or deleted. Do not call GetOID() after calling UpdateRow() or DeleteRow(). Call NextRow() after calling UpdateRow() or DeleteRow().') % {u'singular': self._RowDescriptionSingular})
2136       
2137        return super(UpdateCursor, self).GetOID()
2138
2139    def GetGeometry(self):
2140
2141        # Validate that we haven't updated or deleted this row yet and
2142        # call the base class method. The base class will take care of
2143        # the other validation.
2144
2145        if self._RowUpdatedOrDeleted:
2146            raise RuntimeError(_(u'This %(singular)s has already been updated or deleted. Do not call GetGeometry() after calling UpdateRow() or DeleteRow(). Call NextRow() after calling UpdateRow() or DeleteRow().') % {u'singular': self._RowDescriptionSingular})
2147       
2148        return super(UpdateCursor, self).GetGeometry()
2149
2150    def SetValue(self, field, value):
2151
2152        # Validate that a row has been retrieved, that we're not at
2153        # the end, and that we haven't updated or deleted this row
2154        # yet.
2155       
2156        if self._IsOpen and self._AtEnd is None:
2157            raise RuntimeError(_(u'The first %(singular)s has not been retrieved yet. Call NextRow() before calling SetValue().') % {u'singular': self._RowDescriptionSingular})
2158       
2159        if self._AtEnd:
2160            raise RuntimeError(_(u'All of the %(plural)s have already been retrieved. Do not call SetValue() after NextRow() has returned False.') % {u'plural': self._RowDescriptionPlural})
2161
2162        if not self._IsOpen:
2163            raise RuntimeError(_(u'The cursor is closed.'))
2164
2165        if self._RowUpdatedOrDeleted:
2166            raise RuntimeError(_(u'This %(singular)s has already been updated or deleted. Do not call SetValue() after calling UpdateRow() or DeleteRow(). Call NextRow() after calling UpdateRow() or DeleteRow().') % {u'singular': self._RowDescriptionSingular})
2167
2168        # Set the field.
2169       
2170        self._SetValue_Base(field, value, self._RequestedFields)
2171
2172    def SetGeometry(self, geometry):
2173
2174        # Validate that a row has been retrieved, that we're not at
2175        # the end, and that we haven't updated or deleted this row
2176        # yet.
2177       
2178        if self._IsOpen and self._AtEnd is None:
2179            raise RuntimeError(_(u'The first %(singular)s has not been retrieved yet. Call NextRow() before calling SetGeometry().') % {u'singular': self._RowDescriptionSingular})
2180       
2181        if self._AtEnd:
2182            raise RuntimeError(_(u'All of the %(plural)s have already been retrieved. Do not call SetGeometry() after NextRow() has returned False.') % {u'plural': self._RowDescriptionPlural})
2183
2184        if not self._IsOpen:
2185            raise RuntimeError(_(u'The cursor is closed.'))
2186
2187        if self._RowUpdatedOrDeleted:
2188            raise RuntimeError(_(u'This %(singular)s has already been updated or deleted. Do not call SetGeometry() after calling UpdateRow() or DeleteRow(). Call NextRow() after calling UpdateRow() or DeleteRow().') % {u'singular': self._RowDescriptionSingular})
2189
2190        # Set the geometry.
2191       
2192        self._SetGeometry_Base(geometry, self._RequestedFields)
2193
2194    def UpdateRow(self):
2195
2196        # Validate that this dataset supports row updating, that a row
2197        # has been retrieved, that we're not at the end, and that we
2198        # haven't updated or deleted this row yet.
2199
2200        if self._UpdateRowCapabilityError is not None:
2201            raise self._UpdateRowCapabilityError
2202       
2203        if self._IsOpen and self._AtEnd is None:
2204            raise RuntimeError(_(u'The first %(singular)s has not been retrieved yet. Call NextRow() before calling UpdateRow().') % {u'singular': self._RowDescriptionSingular})
2205       
2206        if self._AtEnd:
2207            raise RuntimeError(_(u'All of the %(plural)s have already been retrieved. Do not call UpdateRow() after NextRow() has returned False.') % {u'plural': self._RowDescriptionPlural})
2208
2209        if not self._IsOpen:
2210            raise RuntimeError(_(u'The cursor is closed.'))
2211
2212        if self._RowUpdatedOrDeleted:
2213            raise RuntimeError(_(u'This %(singular)s has already been updated or deleted. Do not call UpdateRow() after calling UpdateRow() or DeleteRow(). Call NextRow() after calling UpdateRow() or DeleteRow().') % {u'singular': self._RowDescriptionSingular})
2214
2215        # Update the row and report progress.
2216
2217        if Dataset._DebugLoggingEnabled():
2218            self._Table._LogDebug(_(u'%(class)s 0x%(id)08X: Updating this %(singular)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'singular': self._RowDescriptionSingular})
2219
2220        try:
2221            self._UpdateRow()
2222        except Exception, e:
2223            raise RuntimeError(_(u'Failed to update a %(singular)s in %(dn)s due to %(e)s: %(msg)s') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'e': e.__class__.__name__, u'msg': unicode(e)})
2224
2225        self._RowUpdatedOrDeleted = True
2226        self._ProgressReporter.RowsUpdated += 1
2227        if self._ProgressReporter is not None:
2228            self._ProgressReporter.ReportProgress()
2229            self._NeedToReportProgress = False
2230
2231    def DeleteRow(self):
2232
2233        # Validate that this dataset supports row deletion, that a row
2234        # has been retrieved, that we're not at the end, and that we
2235        # haven't updated or deleted this row yet.
2236
2237        if self._DeleteRowCapabilityError is not None:
2238            raise self._DeleteRowCapabilityError
2239       
2240        if self._IsOpen and self._AtEnd is None:
2241            raise RuntimeError(_(u'The first %(singular)s has not been retrieved yet. Call NextRow() before calling DeleteRow().') % {u'singular': self._RowDescriptionSingular})
2242       
2243        if self._AtEnd:
2244            raise RuntimeError(_(u'All of the %(plural)s have already been retrieved. Do not call DeleteRow() after NextRow() has returned False.') % {u'plural': self._RowDescriptionPlural})
2245
2246        if not self._IsOpen:
2247            raise RuntimeError(_(u'The cursor is closed.'))
2248
2249        if self._RowUpdatedOrDeleted:
2250            raise RuntimeError(_(u'This %(singular)s has already been updated or deleted. Do not call DeleteRow() after calling UpdateRow() or DeleteRow(). Call NextRow() after calling UpdateRow() or DeleteRow().') % {u'singular': self._RowDescriptionSingular})
2251
2252        # Update the row and report progress.
2253
2254        if Dataset._DebugLoggingEnabled():
2255            self._Table._LogDebug(_(u'%(class)s 0x%(id)08X: Deleting this %(singular)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'singular': self._RowDescriptionSingular})
2256
2257        try:
2258            self._DeleteRow()
2259        except Exception, e:
2260            raise RuntimeError(_(u'Failed to delete a %(singular)s from %(dn)s due to %(e)s: %(msg)s') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'e': e.__class__.__name__, u'msg': unicode(e)})
2261
2262        self._RowUpdatedOrDeleted = True
2263        self._ProgressReporter.RowsDeleted += 1
2264        if self._ProgressReporter is not None:
2265            self._ProgressReporter.ReportProgress()
2266            self._NeedToReportProgress = False
2267
2268    # Private base class constructor. Do not invoke directly; use
2269    # Table.OpenUpdateCursor instead. Do not override; put
2270    # your initialization code the derive class's _Open method.
2271
2272    def __init__(self, dataset, fields, where, orderBy, rowCount, reportProgress, rowDescriptionSingular, rowDescriptionPlural):
2273        super(UpdateCursor, self).__init__(dataset, fields, where, orderBy, rowCount, False, rowDescriptionSingular, rowDescriptionPlural)
2274
2275        self._UpdateRowCapabilityError = self._Table._TestCapability('updaterow')
2276        self._DeleteRowCapabilityError = self._Table._TestCapability('deleterow')
2277        self._RowUpdatedOrDeleted = False
2278        self._NeedToReportProgress = False
2279
2280        if reportProgress:
2281            self._ProgressReporter = _UpdateCursorProgressReporter(self._RowDescriptionSingular, self._RowDescriptionPlural)
2282            self._ProgressReporter.Start(rowCount)
2283
2284    # Private methods that the derived class must override (except
2285    # _SetGeometry when the derived class does not support geometry).
2286    # These are in addition to those defined by SelectCursor.
2287
2288    def _Close(self):                               # Be sure to call the base class from the derived class's implementation!
2289        try:
2290            if hasattr(self, '_ProgressReporter') and self._ProgressReporter is not None and hasattr(self, '_NeedToReportProgress') and self._NeedToReportProgress:
2291                self._ProgressReporter.ReportProgress()
2292                self._NeedToReportProgress = False
2293        finally:
2294            super(UpdateCursor, self)._Close()
2295
2296    def _SetValue(self, field, value):
2297        raise NotImplementedError(_(u'The _SetValue method of class %s has not been implemented.') % self.__class__.__name__)
2298
2299    def _SetGeometry(self, geometry):
2300        raise NotImplementedError(_(u'The _SetGeometry method of class %s has not been implemented.') % self.__class__.__name__)
2301
2302    def _UpdateRow(self):
2303        raise NotImplementedError(_(u'The _UpdateRow method of class %s has not been implemented.') % self.__class__.__name__)
2304
2305    def _DeleteRow(self):
2306        raise NotImplementedError(_(u'The _DeleteRow method of class %s has not been implemented.') % self.__class__.__name__)
2307
2308
2309class _UpdateCursorProgressReporter(ProgressReporter):
2310
2311    def __init__(self, rowDescriptionSingular, rowDescriptionPlural):
2312        self.RowsUpdated = 0
2313        self.RowsDeleted = 0
2314        super(_UpdateCursorProgressReporter, self).__init__(
2315            progressMessage1=_(u'Update in progress: %%(elapsed)s elapsed, %%(updated)i %(plural)s updated, %%(deleted)i deleted, %%(unchanged)i unchanged, %%(perOp)s per %(singular)s, %%(opsRemaining)i remaining, estimated completion time: %%(etc)s.') % {u'singular': rowDescriptionSingular, u'plural': rowDescriptionPlural},
2316            progressMessage2=_(u'Update in progress: %%(elapsed)s elapsed, %%(updated)i %(plural)s updated, %%(deleted)i deleted, %%(unchanged)i unchanged, %%(perOp)s per %(singular)s.') % {u'singular': rowDescriptionSingular, u'plural': rowDescriptionPlural},
2317            completionMessage=_(u'Update complete: %%(elapsed)s elapsed, %%(updated)i %(plural)s updated, %%(deleted)i deleted, %%(unchanged)i unchanged, %%(perOp)s per %(singular)s.') % {u'singular': rowDescriptionSingular, u'plural': rowDescriptionPlural},
2318            abortedMessage=_(u'Update stopped before all %(plural)s were processed: %%(elapsed)s elapsed, %%(updated)i %(plural)s updated, %%(deleted)i deleted, %%(unchanged)i unchanged, %%(perOp)s per %(singular)s, %%(opsIncomplete)i %(plural)s not processed.') % {u'singular': rowDescriptionSingular, u'plural': rowDescriptionPlural})
2319
2320    def _FormatProgressMessage1(self, timeElapsed, opsCompleted, timePerOp, opsRemaining, estimatedTimeOfCompletionString):
2321        return self._ProgressMessage1 % {u'elapsed' : unicode(datetime.timedelta(days=timeElapsed.days, seconds=timeElapsed.seconds)), u'updated': self.RowsUpdated, u'deleted': self.RowsDeleted, u'unchanged': opsCompleted - self.RowsUpdated - self.RowsDeleted, u'perOp': unicode(timePerOp), u'opsRemaining': opsRemaining, u'etc': estimatedTimeOfCompletionString}
2322
2323    def _FormatProgressMessage2(self, timeElapsed, opsCompleted, timePerOp):
2324        return self._ProgressMessage2 % {u'elapsed' : unicode(datetime.timedelta(days=timeElapsed.days, seconds=timeElapsed.seconds)), u'updated': self.RowsUpdated, u'deleted': self.RowsDeleted, u'unchanged': opsCompleted - self.RowsUpdated - self.RowsDeleted, u'perOp': unicode(timePerOp)}
2325
2326    def _FormatCompletionMessage(self, timeElapsed, opsCompleted, timePerOp):
2327        return self._CompletionMessage % {u'elapsed' : unicode(datetime.timedelta(days=timeElapsed.days, seconds=timeElapsed.seconds)), u'updated': self.RowsUpdated, u'deleted': self.RowsDeleted, u'unchanged': opsCompleted - self.RowsUpdated - self.RowsDeleted, u'perOp': unicode(timePerOp)}
2328
2329    def _FormatAbortedMessage(self, timeElapsed, opsCompleted, timePerOp, opsIncomplete):
2330        return self._AbortedMessage % {u'elapsed' : unicode(datetime.timedelta(days=timeElapsed.days, seconds=timeElapsed.seconds)), u'updated': self.RowsUpdated, u'deleted': self.RowsDeleted, u'unchanged': opsCompleted - self.RowsUpdated - self.RowsDeleted, u'perOp': unicode(timePerOp), u'opsIncomplete': opsIncomplete}
2331
2332
2333class InsertCursor(_Cursor):
2334    __doc__ = DynamicDocString()
2335   
2336    # Public properties and instance methods
2337
2338    def SetValue(self, field, value):
2339        self._SetValue_Base(field, value)
2340        self._FieldSet[field] = True
2341
2342    def SetGeometry(self, geometry):
2343        self._SetGeometry_Base(geometry)
2344        self._GeometrySet = True
2345
2346    def InsertRow(self):
2347
2348        if not self._IsOpen:
2349            raise RuntimeError(_(u'The cursor is closed.'))
2350
2351        # For datasets with geometry, validate that the caller has
2352        # set the geometry.
2353
2354        if self._Table.GeometryType is not None and not self._GeometrySet:
2355            raise RuntimeError(_(u'This %(singular)s cannot be inserted into %(dn)s because its geometry has not been set.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName})
2356
2357        # Set to null all settable nullable fields that were not set
2358        # by the caller.
2359
2360        debug = Dataset._DebugLoggingEnabled()
2361       
2362        for f in self._Table.Fields:
2363            if f.Name != self._Table.OIDFieldName and f.Name != self._Table.GeometryFieldName and f.IsSettable and f.Name not in self._FieldSet:
2364                if f.IsNullable:
2365                    if debug:
2366                        self._Table._LogDebug(_(u'%(class)s 0x%(id)08X: Setting field %(field)s to None.'), {u'class': self.__class__.__name__, u'id': id(self), u'field': f.Name})
2367                    self._SetValue(f.Name, None)
2368                    self._FieldSet[f.Name] = True
2369                else:
2370                    raise RuntimeError(_(u'This %(singular)s cannot be inserted into %(dn)s because field %(field)s has not been assigned a value and it is not nullable. You must provide a value for this field.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': f.Name})
2371
2372        # Insert the row and report progress.
2373
2374        if debug:
2375            self._Table._LogDebug(_(u'%(class)s 0x%(id)08X: Inserting this %(singular)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'singular': self._RowDescriptionSingular})
2376
2377        try:
2378            self._InsertRow()
2379        except Exception, e:
2380            raise RuntimeError(_(u'Failed to insert a %(singular)s into %(dn)s due to %(e)s: %(msg)s') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'e': e.__class__.__name__, u'msg': unicode(e)})
2381
2382        self._FieldSet = {}
2383        self._GeometrySet = False
2384
2385        if self._ProgressReporter is not None:
2386            self._ProgressReporter.ReportProgress()
2387
2388    # Private base class constructor. Do not invoke directly; use
2389    # Table.OpenInsertCursor instead. Do not override; put
2390    # your initialization code the derive class's _Open method.
2391
2392    def __init__(self, dataset, rowCount, reportProgress, rowDescriptionSingular, rowDescriptionPlural):
2393
2394        # Initialize the base class and our attributes.
2395
2396        super(InsertCursor, self).__init__(dataset, rowDescriptionSingular, rowDescriptionPlural)
2397
2398        self._FieldSet = {}
2399        self._GeometrySet = False
2400
2401        # Call the derived class to open the cursor.
2402       
2403        self._Table._LogDebug(_(u'%(class)s 0x%(id)08X: Opening insert cursor on %(dn)s, rowCount=%(rc)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'dn': dataset.DisplayName, u'rc': repr(rowCount)})
2404        try:
2405            self._Open()
2406        except Exception, e:
2407            self._Table = None    # Set this to None to make sure a reference is not leaked due when this constructor fails.
2408            raise RuntimeError(_(u'Cannot insert %(plural)s into %(dn)s. Failed to open an insert cursor due to %(e)s: %(msg)s') % {u'plural': self._RowDescriptionPlural, u'dn': dataset.DisplayName, u'e': e.__class__.__name__, u'msg': unicode(e)})
2409
2410        self._IsOpen = True
2411
2412        if not hasattr(_Cursor, '_CursorsToCloseAtExit'):
2413            _Cursor._CursorsToCloseAtExit = []
2414        _Cursor._CursorsToCloseAtExit.insert(0, weakref.ref(self))
2415
2416        # If the caller wants progress to be reported, start a
2417        # progress reporter.
2418
2419        if reportProgress:
2420            self._ProgressReporter = ProgressReporter(progressMessage1=_(u'Still inserting %(plural)s: %%(elapsed)s elapsed, %%(opsCompleted)i %(plural)s inserted, %%(perOp)s per %(singular)s, %%(opsRemaining)i remaining, estimated completion time: %%(etc)s.') % {u'singular': self._RowDescriptionSingular, u'plural': self._RowDescriptionPlural},
2421                                                      progressMessage2=_(u'Still inserting %(plural)s: %%(elapsed)s elapsed, %%(opsCompleted)i %(plural)s inserted, %%(perOp)s per %(singular)s.') % {u'singular': self._RowDescriptionSingular, u'plural': self._RowDescriptionPlural},
2422                                                      completionMessage=_(u'Finished inserting %(plural)s: %%(elapsed)s elapsed, %%(opsCompleted)i %(plural)s inserted, %%(perOp)s per %(singular)s.') % {u'singular': self._RowDescriptionSingular, u'plural': self._RowDescriptionPlural},
2423                                                      abortedMessage=_(u'Insert operation stopped before all %(plural)s were inserted: %%(elapsed)s elapsed, %%(opsCompleted)i %(plural)s inserted, %%(perOp)s per %(singular)s, %%(opsIncomplete)i %(plural)s not inserted.') % {u'singular': self._RowDescriptionSingular, u'plural': self._RowDescriptionPlural})
2424            self._ProgressReporter.Start(rowCount)
2425        else:
2426            self._ProgressReporter = None
2427
2428    # Private methods that the derived class must override (except
2429    # _SetGeometry when the derived class does not support geometry).
2430
2431    def _Open(self):
2432        raise NotImplementedError(_(u'The _Open method of class %s has not been implemented.') % self.__class__.__name__)
2433
2434    def _SetValue(self, field, value):
2435        raise NotImplementedError(_(u'The _SetValue method of class %s has not been implemented.') % self.__class__.__name__)
2436
2437    def _SetGeometry(self, geometry):
2438        raise NotImplementedError(_(u'The _SetGeometry method of class %s has not been implemented.') % self.__class__.__name__)
2439
2440    def _InsertRow(self):
2441        raise NotImplementedError(_(u'The _InsertRow method of class %s has not been implemented.') % self.__class__.__name__)
2442
2443
2444class Grid(Dataset):
2445    __doc__ = DynamicDocString()
2446
2447    # Public properties and instance methods
2448
2449    def _GetDimensions(self):
2450        return self.GetLazyPropertyValue('Dimensions')
2451
2452    Dimensions = property(_GetDimensions, doc=DynamicDocString())
2453
2454    def _GetShape(self):
2455        return self.GetLazyPropertyValue('Shape')
2456
2457    Shape = property(_GetShape, doc=DynamicDocString())
2458
2459    def _GetCoordDependencies(self):
2460        return self.GetLazyPropertyValue('CoordDependencies')
2461
2462    CoordDependencies = property(_GetCoordDependencies, doc=DynamicDocString())
2463
2464    def _GetCoordIncrements(self):
2465        return self.GetLazyPropertyValue('CoordIncrements')
2466
2467    CoordIncrements = property(_GetCoordIncrements, doc=DynamicDocString())
2468
2469    def _GetTIncrementUnit(self):
2470        return self.GetLazyPropertyValue('TIncrementUnit')
2471   
2472    TIncrementUnit = property(_GetTIncrementUnit, doc=DynamicDocString())
2473
2474    def _GetTSemiRegularity(self):
2475        return self.GetLazyPropertyValue('TSemiRegularity')
2476   
2477    TSemiRegularity = property(_GetTSemiRegularity, doc=DynamicDocString())
2478
2479    def _GetTCountPerSemiRegularPeriod(self):
2480        return self.GetLazyPropertyValue('TCountPerSemiRegularPeriod')
2481   
2482    TCountPerSemiRegularPeriod = property(_GetTCountPerSemiRegularPeriod, doc=DynamicDocString())
2483
2484    def _GetMinCoords(self):
2485        return self._MinCoords
2486
2487    MinCoords = property(_GetMinCoords, doc=DynamicDocString())
2488
2489    def _GetCenterCoords(self):
2490        return self._CenterCoords
2491
2492    CenterCoords = property(_GetCenterCoords, doc=DynamicDocString())
2493
2494    def _GetMaxCoords(self):
2495        return self._MaxCoords
2496
2497    MaxCoords = property(_GetMaxCoords, doc=DynamicDocString())
2498
2499    def _GetDataType(self):
2500        if self.DataIsScaled:
2501            return self.GetLazyPropertyValue('ScaledDataType')
2502        return self.UnscaledDataType
2503   
2504    DataType = property(_GetDataType, doc=DynamicDocString())
2505
2506    def _GetNoDataValue(self):
2507        if self.DataIsScaled:
2508            return self.GetLazyPropertyValue('ScaledNoDataValue')
2509        return self.UnscaledNoDataValue
2510   
2511    NoDataValue = property(_GetNoDataValue, doc=DynamicDocString())
2512
2513    def _GetData(self):
2514        if self.DataIsScaled:
2515            return self._ScaledData
2516        return self._UnscaledData
2517
2518    Data = property(_GetData, doc=DynamicDocString())
2519
2520    def _GetDataIsScaled(self):
2521        return self.GetLazyPropertyValue('ScalingFunction') is not None
2522
2523    DataIsScaled = property(_GetDataIsScaled, doc=DynamicDocString())
2524
2525    def _GetUnscaledDataType(self):
2526        return self.GetLazyPropertyValue('UnscaledDataType')
2527   
2528    UnscaledDataType = property(_GetUnscaledDataType, doc=DynamicDocString())
2529
2530    def _GetUnscaledNoDataValue(self):
2531        return self.GetLazyPropertyValue('UnscaledNoDataValue')
2532   
2533    UnscaledNoDataValue = property(_GetUnscaledNoDataValue, doc=DynamicDocString())
2534
2535    def _GetUnscaledData(self):
2536        return self._UnscaledData
2537
2538    UnscaledData = property(_GetUnscaledData, doc=DynamicDocString())
2539
2540    def GetIndicesForCoords(self, coords):
2541
2542        # Validate the coordinates.
2543
2544        if len(coords) != len(self.Dimensions):
2545            raise ValueError(_(u'%(dn)s has %(dim)i dimensions but %(coords)i coordinates were provided.') % {u'dn': self.DisplayName, u'dim': len(self.Dimensions), u'coords': len(coords)})
2546
2547        if self.Dimensions[0] == 't':
2548            if not isinstance(coords[0], (datetime.date, datetime.datetime)):
2549                raise TypeError(_(u'coords[0] is an instance of %(t1)s. It must be an instance of %(t2)s or %(t3)s') % {u't1': unicode(type(coords[0])), u't2': unicode(datetime.date), u't2': unicode(datetime.datetime)})
2550            numericIndicesStart = 1
2551        else:
2552            numericIndicesStart = 0
2553
2554        for i in range(numericIndicesStart, len(coords)):
2555            if not isinstance(coords[i], (int, long, float)):
2556                raise TypeError(_(u'coords[%(i)i] is an instance of %(t1)s. It must be an instance of %(t2)s, %(t3)s, or %(t4)s.') % {u'i': i, u't1': unicode(type(coords[i])), u't2': unicode(int), u't2': unicode(long), u't2': unicode(float)})
2557
2558        # First get the indices for dimensions that do not depend on
2559        # any others.
2560
2561        indices = [None] * len(coords)
2562        done = [False] * len(coords)
2563
2564        for i, d in enumerate(self.Dimensions):
2565            if self.CoordDependencies[i] is None:
2566                coord = coords[i]
2567
2568                # If this dimension is x and the grid uses a
2569                # geographic projection handle the "0 to 360 vs.
2570                # -180 to 180" problem.
2571
2572                if d == u'x':
2573                    sr = self.GetSpatialReference('obj')
2574                    if sr is not None and sr.IsGeographic():
2575                        coord = coord - (coord // 360) * 360        # Convert the requested x coordinate to 0 to 360, regardless of what it currently is
2576
2577                    # If coord is less than the min x extent, add 360
2578                    # until it is greater than or equal it, to handle
2579                    # coordinate systems such as NOAA OSCAR, which
2580                    # uses a 20 to 380 system.
2581
2582                    if coord < self.MinCoords[d, 0]:
2583                        while coord < self.MinCoords[d, 0]:
2584                            coord += 360.
2585
2586                    # Otherwise, if it is greater than or equal to the
2587                    # max x extent, subtract 360 until it is less than
2588                    # it, to handle coordinate systems such as MODIS
2589                    # L3 which uses a -180 to 180 system.
2590
2591                    elif coord >= self.MaxCoords[d, -1]:
2592                        while coord >= self.MaxCoords[d, -1]:
2593                            coord -= 360.
2594
2595                # If this dimension is x, y, or z and has a constant
2596                # increment, calculate the index directly.
2597               
2598                increment = self.CoordIncrements[i]
2599                if d in u'xyz' and increment is not None:
2600                    index = int(math.floor((coord - (self.GetLazyPropertyValue('CornerCoords')[i] - increment/2.)) / increment))
2601                    if index >= 0 and index <= self.Shape[i] - 1:
2602                        indices[i] = index
2603
2604                # Otherwise (this dimension is t or does not have a
2605                # constant increment), find the index from the full
2606                # list of indices using a binary search.
2607               
2608                else:
2609                    index = bisect.bisect_right(self.MaxCoords[d], coord)
2610                    if index > 0 and index < self.Shape[i] - 1 or index == 0 and coord >= self.MinCoords[d, 0]:
2611                        indices[i] = index
2612                       
2613                done[i] = True
2614
2615        # Now get the indices for dimensions that do depend on others.
2616
2617        if False in done:
2618            i = 0
2619            while i < len(self.Dimensions):
2620                if not done[i] and self.CoordDependencies[i] is not None and all([done[self.Dimensions.index(d)] for d in self.CoordDependencies[i]]):
2621                    maxCoordsKey = [self.Dimensions[i]]
2622                    minCoordKey = [self.Dimensions[i]]
2623                    for j in range(len(self.Dimensions)):
2624                        if self.Dimensions[j] == self.Dimensions[i]:
2625                            maxCoordsKey.append(slice(None))
2626                            minCoordKey.append(0)
2627                        elif self.Dimensions[j] in self.CoordDependencies[i]:
2628                            maxCoordsKey.append(indices[j])
2629                            minCoordKey.append(indices[j])
2630
2631                    if None not in maxCoordsKey and None not in minCoordKey:
2632                        index = bisect.bisect_right(self.MaxCoords.__getitem__(tuple(maxCoordsKey)), coords[i])
2633                        if index > 0 and index < self.Shape[i] or index == 0 and coords[i] >= self.MinCoords.__getitem__(tuple(minCoordKey)):
2634                            indices[i] = index
2635                   
2636                    done[i] = True
2637                    i = 0
2638
2639                else:
2640                    i += 1
2641
2642        # Return successfully.
2643
2644        return indices
2645
2646    # Private base class constructor. Do not invoke directly; use a
2647    # derived class instead. If you override, be sure to call it from
2648    # your derived class's __init__.
2649
2650    def __init__(self, parentCollection=None, queryableAttributes=None, queryableAttributeValues=None, lazyPropertyValues=None):
2651        self.__class__.__doc__.Obj.ValidateMethodInvocation()
2652
2653        # Initialize the base class.
2654
2655        super(Grid, self).__init__(parentCollection, queryableAttributes, queryableAttributeValues, lazyPropertyValues)
2656
2657        # Set various attributes that implement __getitem__.
2658
2659        self._MinCoords = _ContainerEmulator(self, '_GetMinCoords')
2660        self._CenterCoords = _ContainerEmulator(self, '_GetCenterCoords')
2661        self._MaxCoords = _ContainerEmulator(self, '_GetMaxCoords')
2662        self._ScaledData = _ContainerEmulator(self, '_GetScaledDataAsArray', '_SetScaledDataWithArray')
2663        self._UnscaledData = _ContainerEmulator(self, '_GetUnscaledDataAsArray', '_SetUnscaledDataWithArray')
2664
2665    # Private methods that the derived class generally does not
2666    # override.
2667
2668    def _GetMinCoords(self, key):
2669        return self._GetCoordsForOffset(key, -0.5)
2670
2671    def _GetCenterCoords(self, key):
2672        return self._GetCoordsForOffset(key, 0.0)
2673
2674    def _GetMaxCoords(self, key):
2675        return self._GetCoordsForOffset(key, 0.5)
2676
2677    def _GetCoordsForOffset(self, key, fixedIncrementOffset):
2678
2679        # Validate the key.
2680
2681        coord, coordNum, slices, sliceDims = self._GetSlicesForCoordsKey(key)
2682
2683        # If the caller is asking for a coordinate that does not have
2684        # a fixed increment, forward the call to the derived class.
2685
2686        if self.CoordIncrements[coordNum] is None:
2687            return self._GetCoords(coord, coordNum, slices, sliceDims, fixedIncrementOffset)
2688
2689        # The coordinate has a fixed increment. If the coordinate is
2690        # x, y, or z, calculate and return the requested values.
2691
2692        import numpy
2693
2694        if coord in 'xyz':
2695            offset = self.CoordIncrements[coordNum] * fixedIncrementOffset
2696            if slices is not None:
2697                if isinstance(slices[0], types.IntType):
2698                    return slices[0] * self.CoordIncrements[coordNum] + self.GetLazyPropertyValue('CornerCoords')[coordNum] + offset
2699                indices = slices[0].indices(self.Shape[coordNum])
2700                return numpy.arange(indices[0], indices[1], indices[2], dtype='float64') * self.CoordIncrements[coordNum] + self.GetLazyPropertyValue('CornerCoords')[coordNum] + offset
2701            return numpy.arange(self.Shape[coordNum], dtype='float64') * self.CoordIncrements[coordNum] + self.GetLazyPropertyValue('CornerCoords')[coordNum] + offset
2702
2703        # The coordinate is t. First check whether we previously
2704        # calculated and cached the full list of t coordinates. If
2705        # not, do it now.
2706
2707        if not hasattr(self, '_CachedTCoords'):
2708            self._CachedTCoords = {}
2709           
2710        if fixedIncrementOffset not in self._CachedTCoords:
2711            self._CachedTCoords[fixedIncrementOffset] = self._GetTCoordsList(fixedIncrementOffset, self.Shape[coordNum])
2712
2713        # Return a numpy array of the requested slice of t
2714        # coordinates (if any).
2715
2716        if slices is not None:
2717            if isinstance(slices[0], types.IntType):
2718                return self._CachedTCoords[fixedIncrementOffset][slices[0]]
2719            return numpy.array(self._CachedTCoords[fixedIncrementOffset][slices[0]], dtype='object')
2720        return numpy.array(self._CachedTCoords[fixedIncrementOffset], dtype='object')
2721
2722    def _GetSlicesForCoordsKey(self, key):
2723
2724        # Validate the key. The first element must be a dimension
2725        # name. The additional elements are optional, and depend on
2726        # whether that dimension depends on any others. If not, there
2727        # may optionally be exactly one additional element, which is
2728        # an integer index or slice into that dimension. If the
2729        # dimension does depend on others, there may optionaly be
2730        # exactly n + 1 additional elements, each integer indices or
2731        # slices into dimensions, where n is the number of
2732        # depended-upon dimensions. In this latter case, the order of
2733        # the indices/slices is the same as the order of the
2734        # dimensions, regardless of which dimension is requested and
2735        # what dimensions it depends on.
2736        #
2737        # Consider, for example, the 4D ROMS ocean model, in which the
2738        # values of z depend on x, y, and t. That is, the values of
2739        # the depth levels depend on latitude, longitude, and time
2740        # (search the internet for "vertical s-coordinate" for more
2741        # information). For this dataset, if the caller just requests
2742        # 'z' and provides no additional elements, a 4D array will be
2743        # returned with the shape of the entire dataset. That array
2744        # will be quite large, and the caller usually won't need it,
2745        # so he will usually supply indices or slices. In that case,
2746        # the caller must specify exactly four indices or slices,
2747        # ordered t, z, y, x, which is the order of the dimensions of
2748        # a 4D Grid dataset.
2749
2750        if isinstance(key, basestring):
2751            key = (key,)
2752
2753        if not isinstance(key, types.TupleType) or len(key) < 1 or not isinstance(key[0], basestring):
2754            raise TypeError(_(u'The key must be a tuple with at least one element. The first element must be the dimension name, one of: %(dimnames)s.') % {u'dimnames': ', '.join(map(lambda s: "'" + s + "'", self.Dimensions))})
2755        if key[0] not in self.Dimensions:
2756            raise KeyError(_(u'The first element of the key must be an existing dimension name, one of: %(dimnames)s.') % {u'dimnames': ', '.join(map(lambda s: "'" + s + "'", self.Dimensions))})
2757
2758        coord = key[0]
2759        coordNum = self.Dimensions.index(coord)
2760
2761        expectedSlices = 1
2762        if self.CoordDependencies[coordNum] is not None:
2763            expectedSlices += len(self.CoordDependencies[coordNum])
2764            sliceDims = u''
2765            for dim in self.Dimensions:
2766                if dim == key[0] or dim in self.CoordDependencies[coordNum]:
2767                    sliceDims += dim
2768        else:
2769            sliceDims = coord
2770
2771        if len(key) > 1:
2772            if len(key) - 1 != expectedSlices:
2773                raise KeyError(_(u'Invalid number of slices. When requesting %(dim)s coordinates of %(dn)s, you must either specify no slices (in which case the entire array of coordinates will be returned) or specify exactly %(slices)i slices.') % {u'dn': self.DisplayName, u'dim': coord, u'slices': expectedSlices})
2774
2775            slices = list(key[1:])
2776
2777            for i in range(0, len(slices)):
2778                if isinstance (slices[i], types.SliceType):
2779                    if not isinstance(slices[i].start, (types.NoneType, types.IntType)) or not isinstance(slices[i].stop, (types.NoneType, types.IntType)) or not isinstance(slices[i].step, (types.NoneType, types.IntType)):
2780                        raise TypeError(_(u'The slice start, stop, and step must be integers, or None.'))
2781                elif isinstance (slices[i], types.IntType):
2782                    dimNum = self.Dimensions.index(sliceDims[i])
2783                    if slices[i] >= self.Shape[dimNum] or slices[i] < 0-self.Shape[dimNum]:
2784                        raise IndexError(_(u'Index for %(dim)s dimension out of bounds.') % {u'dim': self.Dimensions[dimNum]})
2785                    if slices[i] < 0:
2786                        slices[i] += self.Shape[dimNum]
2787                else:
2788                    raise TypeError(_(u'Element %(i)i of the key must be an integer or a slice.') % {u'i': i+1})
2789        else:
2790            slices = None
2791
2792        # Return successfully.
2793
2794        return coord, coordNum, slices, sliceDims
2795
2796    def _GetTCoordsList(self, fixedIncrementOffset, listLength):
2797        tCoords = []
2798
2799        # self.GetLazyPropertyValue('CornerCoords')[coordNum] is the
2800        # coordinate of the first time slice. Depending on the
2801        # dataset, this coordinate may be given as the min, center, or
2802        # max for that slice. A lot of the following complexity deals
2803        # with the scenario where we are asked to produce a coordinate
2804        # type (min, center, max) that is different than that
2805        # specified for the first time slice.
2806
2807        coordNum = self.Dimensions.index('t')
2808        t0 = self.GetLazyPropertyValue('CornerCoords')[coordNum]
2809
2810        # If the t increment is day, hour, minute, or second, then it
2811        # is the same number of seconds every time step. We can use
2812        # simple timedeltas to do the math.
2813
2814        tCornerCoordType = self.GetLazyPropertyValue('TCornerCoordType')
2815        tIncrement = self.CoordIncrements[coordNum]
2816
2817        if self.TIncrementUnit in ['day', 'hour', 'minute', 'second']:
2818
2819            # Create a timedelta for the increment.
2820           
2821            if self.TIncrementUnit == 'day':
2822                increment = datetime.timedelta(days=tIncrement)
2823            elif self.TIncrementUnit == 'hour':
2824                increment = datetime.timedelta(hours=tIncrement)
2825            elif self.TIncrementUnit == 'minute':
2826                increment = datetime.timedelta(minutes=tIncrement)
2827            else:
2828                increment = datetime.timedelta(seconds=tIncrement)
2829
2830            # Calculate an offset that will yield the coordinate that
2831            # we're supposed to produce.
2832
2833            if tCornerCoordType == 'min' and fixedIncrementOffset == 0.5:
2834                offset = increment
2835            elif tCornerCoordType == 'min' and fixedIncrementOffset == 0.0 or tCornerCoordType == 'center' and fixedIncrementOffset == 0.5:
2836                offset = increment / 2
2837            elif tCornerCoordType == 'min' and fixedIncrementOffset == -0.5 or tCornerCoordType == 'center' and fixedIncrementOffset == 0.0 or tCornerCoordType == 'max' and fixedIncrementOffset == 0.5:
2838                offset = datetime.timedelta(0)
2839            elif tCornerCoordType == 'center' and fixedIncrementOffset == -0.5 or tCornerCoordType == 'max' and fixedIncrementOffset == 0.0:
2840                offset = datetime.timedelta(0) - increment / 2
2841            else:
2842                offset = datetime.timedelta(0) - increment
2843
2844            # Create the full list of t coordinates. If the
2845            # coordinates are not semi-regular, then it is easy.
2846
2847            if self.TSemiRegularity is None:
2848                tCoords.extend(map(lambda i: t0 + i * increment + offset, range(listLength)))
2849
2850            # If the coordinates are semi-regular, it is more
2851            # complicated. At the moment, only annual semi-regularity
2852            # is supported.
2853
2854            else:
2855                if self.TSemiRegularity != 'annual':
2856                    raise NotImplementedError(_(u'Programming error in this tool: \'%(sr)s\' semi-regularity has not been implemented. Please contact the author of this tool for assistance.') % {u'sr': self.TSemiRegularity})
2857
2858                # Count backwards from t0 to determine how many time
2859                # slices precede it in the starting year.
2860                #
2861                # If we have a lazy property called
2862                # TOffsetFromParsedTime, it means that t0 includes
2863                # that offset. So to count back to the beginning of
2864                # the year, we must remove TOffsetFromParsedTime from
2865                # t0.
2866
2867                tOffsetFromParsedTime = self.GetLazyPropertyValue('TOffsetFromParsedTime')
2868                if tOffsetFromParsedTime is not None:
2869                    deltaFromParsedTime = datetime.timedelta(tOffsetFromParsedTime)
2870                    t0 = t0 - deltaFromParsedTime
2871                else:
2872                    deltaFromParsedTime = datetime.timedelta(0)
2873               
2874                yearlyCount = 0
2875                startDate = t0
2876                while tCornerCoordType == 'min' and (startDate - increment).year == t0.year or tCornerCoordType == 'center' and (startDate - increment * 3 / 2).year == t0.year or tCornerCoordType == 'max' and (startDate - increment * 2).year == t0.year:
2877                    startDate -= increment
2878                    yearlyCount += 1
2879
2880                # Now construct the full list of t coordinates.
2881
2882                t = t0
2883                currentYear = t0.year
2884                for i in range(listLength):
2885                    yearlyCount += 1
2886                    if yearlyCount < self.TCountPerSemiRegularPeriod:
2887                        tCoords.append(t + offset + deltaFromParsedTime)
2888                        t += increment
2889                    else:
2890                        overrun = datetime.timedelta(0)
2891                        if tCornerCoordType == 'min':
2892                            if (t + increment).year != currentYear:
2893                                overrun = (t + increment) - datetime.datetime(currentYear + 1, 1, 1)
2894                        elif tCornerCoordType == 'center':
2895                            if (t + increment/2).year != currentYear:
2896                                overrun = (t + increment/2) - datetime.datetime(currentYear + 1, 1, 1)
2897                        else:
2898                            if t.year != currentYear:
2899                                overrun = t - datetime.datetime(currentYear + 1, 1, 1)
2900
2901                        if fixedIncrementOffset == -0.5:
2902                            tCoords.append(t + offset + deltaFromParsedTime)
2903                        elif fixedIncrementOffset == 0.0:
2904                            tCoords.append(t + offset - overrun/2 + deltaFromParsedTime)
2905                        else:
2906                            tCoords.append(t + offset - overrun + deltaFromParsedTime)
2907                       
2908                        yearlyCount = 0
2909                        currentYear += 1
2910                        t = datetime.datetime(currentYear, startDate.month, startDate.day, startDate.hour, startDate.minute, startDate.second, startDate.microsecond)
2911
2912        # If the t increment is month, season, or year, then it may be
2913        # a different number of seconds each time step. In these
2914        # cases, we increment according to the fraction of the month
2915        # or day of the year. We do not currently support
2916        # semi-regularity in these cases.
2917
2918        else:
2919            if self.TSemiRegularity is not None:
2920                raise NotImplementedError(_(u'Programming error in this tool: semi-regularity is not currently supported if the TIncrementUnit is \'%(unit)s\'. Please contact the author of this tool for assistance.') % {u'unit': self.TIncrementUnit})
2921
2922            if self.TIncrementUnit == 'month':
2923
2924                # Fail if the t increment is not an integer.
2925                # Currently, we only support incrementing by whole
2926                # months. If the user needs to increment by some
2927                # fractions of a month, they should use a
2928                # TIncrementUnit of 'day'.
2929
2930                if math.modf(tIncrement)[0] != 0:
2931                    raise NotImplementedError(_(u'Programming error in this tool: when the time increment unit is \'month\', the time coordinate increment must be a whole number of months. The requested non-integer values %(value)s is not supported. Please contact the author of this tool for assistance.') % {u'value': tIncrement})
2932                tIncrement = int(tIncrement)
2933
2934                # If we have a lazy property called
2935                # TOffsetFromParsedTime, it means that t0 includes
2936                # that offset. But datasets that require the
2937                # TOffsetFromParsedTime hack typically want the t
2938                # coordinates to start on the same offset from the
2939                # beginning of the month. For example, the MODIS
2940                # monthly SST always starts at 00:00:00 of the first
2941                # day of the month, minus 12 hours (i.e. 12:00:00 on
2942                # the last day of the previous month).
2943                #
2944                # To make this all work out, perform the computations
2945                # using the t0 without the offset, then add the offset
2946                # back in when we calculate the series of coordinates.
2947
2948                tOffsetFromParsedTime = self.GetLazyPropertyValue('TOffsetFromParsedTime')
2949                if tOffsetFromParsedTime is not None:
2950                    t0 = t0 - datetime.timedelta(tOffsetFromParsedTime)
2951                else:
2952                    tOffsetFromParsedTime = 0.
2953
2954                # Fail if t0 (after removing TOffsetFromParsedTime) is
2955                # not at midnight on the first day of the month.
2956                # Currently, we only support having t0 at the border
2957                # between months because the logic for handling any
2958                # other time is very complicated and we do not know of
2959                # any datasets that require it.
2960
2961                if t0.day != 1 or t0.hour != 0 or t0.minute != 0 or t0.second != 0 or t0.microsecond != 0:
2962                    raise NotImplementedError(_(u'Programming error in this tool: when the time increment unit is \'month\', the time corner coordinate must be the first day of the month at 00:00:00. Time corner coordinates that fall sometime within the month are not supported. Please contact the author of this tool for assistance.'))
2963
2964                # Construct the full list of t coordinates.
2965
2966                t = t0
2967               
2968                tPrev = t0
2969                for j in range(tIncrement):
2970                    if tPrev.month == 1:
2971                        tPrev = datetime.datetime(tPrev.year - 1, 12, 1)
2972                    else:
2973                        tPrev = datetime.datetime(tPrev.year, tPrev.month - 1, 1)
2974
2975                for i in range(listLength):
2976                    tNext = t
2977                    for j in range(tIncrement):
2978                        if tNext.month == 12:
2979                            tNext = datetime.datetime(tNext.year + 1, 1, 1)
2980                        else:
2981                            tNext = datetime.datetime(tNext.year, tNext.month + 1, 1)
2982
2983                    if tCornerCoordType == 'min':
2984                        if fixedIncrementOffset == -0.5:
2985                            tCoords.append(t + datetime.timedelta(tOffsetFromParsedTime))
2986                        elif fixedIncrementOffset == 0.0:
2987                            tCoords.append(t + (tNext - t) / 2 + datetime.timedelta(tOffsetFromParsedTime))
2988                        else:
2989                            tCoords.append(tNext + datetime.timedelta(tOffsetFromParsedTime))
2990
2991                    elif tCornerCoordType == 'center':
2992                        if fixedIncrementOffset == -0.5:
2993                            tCoords.append(tPrev + (t - tPrev) / 2 + datetime.timedelta(tOffsetFromParsedTime))
2994                        elif fixedIncrementOffset == 0.0:
2995                            tCoords.append(t + datetime.timedelta(tOffsetFromParsedTime))
2996                        else:
2997                            tCoords.append(t + (tNext - t) / 2 + datetime.timedelta(tOffsetFromParsedTime))
2998
2999                    else:
3000                        if fixedIncrementOffset == -0.5:
3001                            tCoords.append(tPrev + datetime.timedelta(tOffsetFromParsedTime))
3002                        elif fixedIncrementOffset == 0.0:
3003                            tCoords.append(tPrev + (t - tPrev) / 2 + datetime.timedelta(tOffsetFromParsedTime))
3004                        else:
3005                            tCoords.append(t + datetime.timedelta(tOffsetFromParsedTime))
3006
3007                    tPrev = t
3008                    t = tNext
3009
3010            # TODO: elif self.TIncrementUnit == 'season':
3011
3012            elif self.TIncrementUnit == 'year':
3013                tOffsetFromParsedTime = self.GetLazyPropertyValue('TOffsetFromParsedTime')
3014                if tOffsetFromParsedTime is not None:
3015                    deltaFromParsedTime = datetime.timedelta(tOffsetFromParsedTime)
3016                    t0 = t0 - deltaFromParsedTime
3017                else:
3018                    deltaFromParsedTime = datetime.timedelta(0)
3019
3020                if tCornerCoordType == 'min' and fixedIncrementOffset == 0.5:
3021                    t = datetime.datetime(t0.year + 1, t0.month, t0.day, t0.hour, t0.minute, t0.second, t0.microsecond)
3022                elif tCornerCoordType == 'min' and fixedIncrementOffset == 0.0 or tCornerCoordType == 'center' and fixedIncrementOffset == 0.5:
3023                    if t0.month > 6:
3024                        t = datetime.datetime(t0.year + 1, t0.month - 6, t0.day, t0.hour, t0.minute, t0.second, t0.microsecond)
3025                    else:
3026                        t = datetime.datetime(t0.year, t0.month + 6, t0.day, t0.hour, t0.minute, t0.second, t0.microsecond)
3027                elif tCornerCoordType == 'min' and fixedIncrementOffset == -0.5 or tCornerCoordType == 'center' and fixedIncrementOffset == 0.0 or tCornerCoordType == 'max' and fixedIncrementOffset == 0.5:
3028                    t = t0
3029                elif tCornerCoordType == 'center' and fixedIncrementOffset == -0.5 or tCornerCoordType == 'max' and fixedIncrementOffset == 0.0:
3030                    if t0.month <= 6:
3031                        t = datetime.datetime(t0.year - 1, t0.month + 6, t0.day, t0.hour, t0.minute, t0.second, t0.microsecond)
3032                    else:
3033                        t = datetime.datetime(t0.year, t0.month - 6, t0.day, t0.hour, t0.minute, t0.second, t0.microsecond)
3034                else:
3035                    t = datetime.datetime(t0.year - 1, t0.month, t0.day, t0.hour, t0.minute, t0.second, t0.microsecond)
3036
3037                for i in range(listLength):
3038                    tCoords.append(t + deltaFromParsedTime)
3039                    t = datetime.datetime(t.year + 1, t.month, t.day, t.hour, t.minute, t.second, t.microsecond)
3040
3041            else:
3042                raise NotImplementedError(_(u'Programming error in this tool: the t increment unit \'%(unit)s\' is not currently supported. Please contact the author of this tool for assistance.') % {u'unit': self.TIncrementUnit})
3043
3044        # Return the list of coordinates.
3045
3046        return tCoords
3047
3048    def _GetScaledDataAsArray(self, key):
3049        unscaledData = self._GetUnscaledDataAsArray(key)
3050        data = self.GetLazyPropertyValue('ScalingFunction')(unscaledData)
3051
3052        if data.dtype.name != self.DataType:
3053            import numpy
3054            data = numpy.cast[str(self.DataType)](data)
3055           
3056        if self.UnscaledNoDataValue is not None and self.NoDataValue is not None:
3057            if data.ndim > 0:
3058                data[unscaledData == self.UnscaledNoDataValue] = self.NoDataValue
3059            elif unscaledData == self.UnscaledNoDataValue:
3060                import numpy
3061                return numpy.array(self.NoDataValue, data.dtype)
3062           
3063        return data
3064
3065    def _SetScaledDataWithArray(self, key, value):
3066        unscaledData = self.GetLazyPropertyValue('UnscalingFunction')(value)
3067
3068        if self.UnscaledNoDataValue is not None and self.NoDataValue is not None:
3069            if unscaledData.ndim > 0:
3070                unscaledData[unscaledData == self.NoDataValue] = self.UnscaledNoDataValue
3071            elif unscaledData == self.UnscaledNoDataValue:
3072                import numpy
3073                return numpy.array(self.UnscaledNoDataValue, unscaledData.dtype)
3074       
3075        self._SetUnscaledDataWithArray(key, unscaledData)
3076
3077    def _GetUnscaledDataAsArray(self, key):
3078
3079        # Validate the key and if any of the phyical dimensions are
3080        # flipped (e.g. the y coordinate decreases as the y index
3081        # increases), flip the key indices for those dimensions.
3082       
3083        flippedKey = self._ValidateAndFlipKey(key)
3084
3085        # Convert the flipped key to a list of slices to specify to
3086        # the derived class the hyperslab of physical data that we
3087        # want. For the convenience of the derived class, there is a
3088        # slice for every dimension, with non-negative start and stop
3089        # attributes, start <= stop, and step == None.
3090       
3091        sliceList = self._GetSlicesForFlippedKey(flippedKey)
3092
3093        # If the physical dimension order is different than our
3094        # standard order (tzyx), reorder the slices into the physical
3095        # order.
3096
3097        physicalDimensions = self.GetLazyPropertyValue('PhysicalDimensions')
3098        if self.Dimensions != physicalDimensions:
3099            reorderedSliceList = []
3100            for dim in physicalDimensions:
3101                reorderedSliceList.append(sliceList[self.Dimensions.index(dim)])
3102        else:
3103            reorderedSliceList = sliceList
3104
3105        # Get a numpy array for the slice list. This may take some
3106        # time; the data could be on a remote server; it may need to
3107        # be downloaded and/or decompressed.
3108
3109        data, actualNoDataValue = self._ReadNumpyArray(reorderedSliceList)
3110
3111        # If the physical dimension order is different than our
3112        # standard order, transpose the returned numpy array to our
3113        # standard order.
3114       
3115        if self.Dimensions != physicalDimensions:
3116            transposeList = []
3117            for dim in self.Dimensions:
3118                transposeList.append(physicalDimensions.index(dim))
3119
3120            if transposeList not in [[0,1], [0,1,2], [0,1,2,3]]:
3121                data = data.transpose(transposeList)
3122
3123        # The array that we got back has the shape described by the
3124        # key but we cannot use the key to index into it because the
3125        # key refers to an array with the full shape, not this reduced
3126        # shape. Adjust the key indices to the reduced shape.
3127
3128        flippedKey = self._AdjustFlippedKeyIndicesToReducedShape(flippedKey)
3129
3130        # If the dataset should use a different NoData value and/or
3131        # data type than what was returned, change the array to use
3132        # the desired NoData value and/or recast it to use the desired
3133        # data type.
3134
3135        if self.UnscaledNoDataValue is not None and ((isinstance(self.UnscaledNoDataValue, types.IntType) and int(actualNoDataValue) != self.UnscaledNoDataValue or isinstance(self.UnscaledNoDataValue, types.FloatType) and actualNoDataValue != self.UnscaledNoDataValue)):
3136            if data.dtype.kind == 'i':
3137                self._LogDebug(_(u'%(class)s 0x%(id)08X: Changing the NoData value of the returned data from %(v1)i to %(v2)i.') % {u'class': self.__class__.__name__, u'id': id(self), u'v1': int(actualNoDataValue), u'v2': self.UnscaledNoDataValue})
3138                data[data == int(actualNoDataValue)] = self.UnscaledNoDataValue
3139            else:
3140                self._LogDebug(_(u'%(class)s 0x%(id)08X: Changing the NoData value of the returned data from %(v1)g to %(v2)g.') % {u'class': self.__class__.__name__, u'id': id(self), u'v1': actualNoDataValue, u'v2': self.UnscaledNoDataValue})
3141                data[data == actualNoDataValue] = self.UnscaledNoDataValue
3142
3143        if data.dtype.name != self.UnscaledDataType:
3144            self._LogDebug(_(u'%(class)s 0x%(id)08X: Changing the data type of the returned data from %(t1)s to %(t2)s.') % {u'class': self.__class__.__name__, u'id': id(self), u't1': data.dtype.name, u't2': self.UnscaledDataType})
3145            import numpy
3146            data = numpy.cast[str(self.UnscaledDataType)](data)
3147
3148        # Return the data.
3149
3150        if len(flippedKey) == 1:
3151            return data.__getitem__(flippedKey[0])
3152       
3153        return data.__getitem__(tuple(flippedKey))
3154
3155    def _ValidateAndFlipKey(self, key):
3156
3157        # Validate the key. In numpy terminology, we support single
3158        # element indexing and slice indexing. We do not support index
3159        # arrays (either of integers or booleans). Thus, the key must
3160        # follow these rules:
3161        #
3162        #     1. It must be an integer, a slice, or a tuple.
3163        #
3164        #     2. If a tuple, it may only contain integers and slices.
3165        #
3166        #     3. The tuple may contain no more elements than the
3167        #        dimensions of the grid.
3168
3169        if not isinstance(key, (int, slice, tuple)):
3170            raise TypeError(_(u'The key must be an integer, a slice, or a tuple of integers and slices.'))
3171
3172        if not isinstance(key, tuple):
3173            key = (key,)
3174
3175        if len(key) > len(self.Dimensions):
3176            raise IndexError(_(u'Too many indices.'))
3177
3178        for i in range(len(key)):
3179            if not isinstance(key[i], (int, slice)) or isinstance(key[i], slice) and (not isinstance(key[i].start, (int, types.NoneType)) or not isinstance(key[i].stop, (int, types.NoneType)) or not isinstance(key[i].step, (int, types.NoneType))):
3180                raise IndexError(_(u'Indices must be integers or integer slices.'))
3181            if isinstance(key[i], int):
3182                if key[i] >= self.Shape[i]:
3183                    raise IndexError(_(u'Index out of bounds for dimension \'%(dim)s\'; %(val)i > dimension length %(len)i.') % {u'dim': self.Dimensions[i], u'val': key[i], u'len': self.Shape[i]})
3184                elif key[i] < 0 - self.Shape[i]:
3185                    raise IndexError(_(u'Index out of bounds for dimension \'%(dim)s\'; %(val)i < 0 - dimension length %(len)i.') % {u'dim': self.Dimensions[i], u'val': key[i], u'len': self.Shape[i]})
3186       
3187        # If any of the phyical dimensions are flipped (e.g. the y
3188        # coordinate decreases as the y index increases), flip the key
3189        # indices for those dimensions. As shown in the following
3190        # example, we can flip an integer index by multiplying by -1
3191        # and subtracting 1. We can flip a slice by doing the same
3192        # operation with start and stop and multiplying step by -1.
3193        #
3194        #     >>> x1
3195        #     [0, 1, 2, 3, 4, 5]
3196        #     >>> x2
3197        #     [5, 4, 3, 2, 1, 0]
3198        #     >>>
3199        #     >>> x1[1]
3200        #     1
3201        #     >>> x2[-2]
3202        #     1
3203        #     >>>
3204        #     >>> x1.__getitem__(slice(3,5,1))
3205        #     [3, 4]
3206        #     >>> x2.__getitem__(slice(-4,-6,-1))
3207        #     [3, 4]
3208        #     >>>
3209        #     >>> x1.__getitem__(slice(-5,-3,1))
3210        #     [1, 2]
3211        #     >>> x2.__getitem__(slice(4,2,-1))
3212        #     [1, 2]
3213        #     >>>
3214        #     >>> x1.__getitem__(slice(5,3,-1))
3215        #     [5, 4]
3216        #     >>> x2.__getitem__(slice(-6,-4,1))
3217        #     [5, 4]
3218        #     >>>
3219        #     >>> x1.__getitem__(slice(-3,-5,-1))
3220        #     [3, 2]
3221        #     >>> x2.__getitem__(slice(2,4,1))
3222        #     [3, 2]
3223
3224        flippedKey = []
3225        physicalDimensionsFlipped = self.GetLazyPropertyValue('PhysicalDimensionsFlipped')
3226        for i in range(len(key)):
3227            if physicalDimensionsFlipped[i]:
3228                if isinstance(key[i], int):
3229                    flippedKey.append(-1*key[i] - 1)
3230                else:
3231                    start, stop, step = key[i].start, key[i].stop, key[i].step
3232                    if start is not None:
3233                        start = -1*start -1
3234                    if stop is not None:
3235                        stop = -1*stop -1
3236                    if step is not None:
3237                        step = -1*step
3238                    else:
3239                        step = -1   # If step is None, it defaults to 1, so flipping None results in -1
3240                    flippedKey.append(slice(start, stop, step))
3241            else:
3242                flippedKey.append(key[i])
3243
3244        return flippedKey
3245
3246    def _GetSlicesForFlippedKey(self, flippedKey):
3247        sliceList = []
3248       
3249        for i in range(len(self.Dimensions)):
3250            if i < len(flippedKey):
3251                if isinstance(flippedKey[i], slice):
3252                    indices = flippedKey[i].indices(self.Shape[i])
3253                    if indices[0] <= indices[1]:
3254                        sliceList.append(slice(indices[0], indices[1]))
3255                    else:
3256                        sliceList.append(slice(indices[1]+1, indices[0]+1))
3257                elif flippedKey[i] >= 0:
3258                    sliceList.append(slice(flippedKey[i], flippedKey[i] + 1))
3259                else:
3260                    sliceList.append(slice(self.Shape[i] + flippedKey[i], self.Shape[i] + flippedKey[i] + 1))
3261            else:
3262                sliceList.append(slice(0, self.Shape[i]))
3263
3264        return sliceList
3265
3266    def _AdjustFlippedKeyIndicesToReducedShape(self, flippedKey):
3267        newFlippedKey = []
3268       
3269        for i in range(len(flippedKey)):
3270            if isinstance(flippedKey[i], int):
3271                newFlippedKey.append(0)
3272            else:
3273                start, stop, step = flippedKey[i].indices(self.Shape[i])
3274                adjustment = min(start, stop)
3275                start -= adjustment
3276                stop -= adjustment
3277                if start == 0:
3278                    start = None
3279                if stop == 0:
3280                    stop = None
3281                newFlippedKey.append(slice(start, stop, step))
3282
3283        return newFlippedKey
3284   
3285    def _SetUnscaledDataWithArray(self, key, value):
3286
3287        # TODO: Validate that this grid is writable.
3288
3289        # Validate the key and if any of the phyical dimensions are
3290        # flipped (e.g. the y coordinate decreases as the y index
3291        # increases), flip the key indices for those dimensions
3292       
3293        flippedKey = self._ValidateAndFlipKey(key)
3294
3295        # Validate that the value is either a numpy array, an integer,
3296        # a float, or a complex.
3297
3298        import numpy
3299        if not isinstance(value, (numpy.ndarray, int, float, complex)):
3300            raise TypeError(_(u'The value must be a numpy array, an int, a float, or a complex.'))
3301
3302        # Validate that the value contains the expected number of
3303        # elements.
3304
3305        expectedSize = 1
3306        expectedShape = []
3307
3308        for i in range(len(self.Dimensions)):
3309            if i >= len(key):
3310                length = self.Shape[i]
3311            elif isinstance(key[i], slice):
3312                length = len(range(*key[i].indices(self.Shape[i])))
3313            else:
3314                length = 1
3315            expectedSize *= length
3316            expectedShape.append(length)
3317
3318        if expectedSize == 0:
3319            if not isinstance(value, numpy.ndarray) or value.size != 0:
3320                raise ValueError(_(u'The key and value do not match: the key describes an array of size %(expected)i, but the value is an array of size %(actual)i.') % {u'expected': 0, u'actual': value.size})
3321        elif expectedSize == 1:
3322            if isinstance(value, numpy.ndarray) and value.size != 1:
3323                raise ValueError(_(u'The key and value do not match: the key describes an array of size %(expected)i, but the value is an array of size %(actual)i.') % {u'expected': 1, u'actual': value.size})
3324        else:
3325            if not isinstance(value, numpy.ndarray):
3326                raise ValueError(_(u'The key and value do not match: the key describes an array of size %(expected)i, but the value is not an array.') % {u'expected': expectedSize})
3327            if value.size != expectedSize:
3328                raise ValueError(_(u'The key and value do not match: the key describes an array of size %(expected)i, but the value is an array of size %(actual)i.') % {u'expected': expectedSize, u'actual': value.size})
3329
3330        # If the value is not an array, make it one.
3331
3332        if not isinstance(value, numpy.ndarray):
3333            value = numpy.array(value)
3334
3335        # Reshape the array to the expected shape. This will expand
3336        # the dimensions (if needed).
3337
3338        value = value.reshape(expectedShape)
3339
3340        # Convert the flipped key to a list of slices to specify to
3341        # the derived class the hyperslab of physical data that we're
3342        # going to write. For the convenience of the derived class,
3343        # there is a slice for every dimension, with positive start
3344        # and stop attributes, start <= stop, and step == None.
3345        #
3346        # Also determine whether we need to flip any of the axes of
3347        # the caller's array prior to passing it to the derived class,
3348        # so the derived class does not need to worry about writing it
3349        # in reverse order.
3350        #
3351        # Finally, if the caller's key included slices that have
3352        # abs(step) != 1. If so, we save the caller the effort of
3353        # having to stride the writes across the physical store: we
3354        # read the hyperslab that encloses the entire range described
3355        # by the caller's slices, apply the array in memory (allowing
3356        # numpy to do the striding), and write the hyperslab back.
3357
3358        sliceList = []
3359        needToFlipData = []
3360        largeStep = False
3361        largeStepKey = []
3362
3363        for i in range(len(self.Dimensions)):
3364            if i < len(flippedKey):
3365                if isinstance(key[i], slice):
3366                    start, stop, step = flippedKey[i].indices(self.Shape[i])
3367                    largeStep = largeStep or step is not None and abs(step) > 1
3368                    if step is not None and step < 0:
3369                        needToFlipData.append(True)
3370                        rstart = start + step*((stop-start+1) // step)
3371                        rstop = start+1
3372                        rstep = -1*step
3373                        sliceList.append(slice(rstart, rstop))
3374                        largeStepKey.append(slice(0, rstop-rstart, rstep))
3375                    else:
3376                        needToFlipData.append(False)
3377                        sliceList.append(slice(start, stop))
3378                        largeStepKey.append(slice(0, stop-start, step))
3379                else:
3380                    needToFlipData.append(False)
3381                    if flippedKey[i] >= 0:
3382                        sliceList.append(slice(flippedKey[i], flippedKey[i] + 1))
3383                    else:
3384                        sliceList.append(slice(self.Shape[i] + flippedKey[i], self.Shape[i] + flippedKey[i] + 1))
3385                    largeStepKey.append(slice(None))
3386            else:
3387                needToFlipData.append(physicalDimensionsFlipped[i])
3388                sliceList.append(slice(0, self.Shape[i]))
3389                largeStepKey.append(slice(None))
3390
3391        # If we need to flip any of the axes of the caller's array, do
3392        # it now.
3393
3394        if True in needToFlipData:
3395            self._LogDebug(_(u'%(class)s 0x%(id)08X: Flipping the following axes prior to writing data: %(axes)s'), {u'class': self.__class__.__name__, u'id': id(self), u'axes': ', '.join(filter(lambda d: needToFlipData[self.Dimensions.index(d)], self.Dimensions))})
3396            value = value.__getitem__(map(lambda f: slice(None, None, {True: -1, False: None}[f]), needToFlipData))
3397
3398        # If the physical dimension order is different than our
3399        # standard order (tzyx), reorder the slices and data into the
3400        # physical order.
3401
3402        physicalDimensions = self.GetLazyPropertyValue('PhysicalDimensions')
3403        if self.Dimensions != physicalDimensions:
3404            reorderedSliceList = []
3405            reorderedLargeStepKey = []
3406            transposeList = []
3407
3408            for dim in physicalDimensions:
3409                reorderedSliceList.append(sliceList[self.Dimensions.index(dim)])
3410                reorderedLargeStepKey.append(largeStepKey[self.Dimensions.index(dim)])
3411                transposeList.append(physicalDimensions.index(dim))
3412
3413            value = value.transpose(transposeList)
3414        else:
3415            reorderedSliceList = sliceList
3416            reorderedLargeStepKey = largeStepKey
3417
3418        # If the data type of the value is not the same as this
3419        # dataset, cast the value to the correct data type.
3420
3421        if value.dtype.name != self.UnscaledDataType:
3422            if not numpy.can_cast(value.dtype, str(self.UnscaledDataType)):
3423                self._LogDebug(_(u'%(class)s 0x%(id)08X: Warning: casting from %(dt1)s to %(dt2)s. The loss of precision may produce unexpected results.'), {u'class': self.__class__.__name__, u'id': id(self), u'dt1': value.dtype.name, u'dt2': self.UnscaledDataType})
3424            value = numpy.cast[str(self.UnscaledDataType)](value)
3425
3426        # If the caller's key included slices that have abs(step) > 1,
3427        # read the hyperslab that encloses the entire range described
3428        # by the caller's slices, apply the array in memory (allowing
3429        # numpy to do the striding), and write it back.
3430
3431        if largeStep:
3432            existingData, actualNoDataValue = self._ReadNumpyArray(reorderedSliceList)      # TODO: What about actualNoDataValue here?
3433           
3434            if existingData.dtype.name != value.dtype.name:
3435                self._LogDebug(_(u'%(class)s 0x%(id)08X: Changing the data type of the returned data from %(t1)s to %(t2)s.') % {u'class': self.__class__.__name__, u'id': id(self), u't1': existingData.dtype.name, u't2': value.dtype.name})
3436                existingData = numpy.cast[str(value.dtype.name)](existingData)
3437
3438            existingData.__setitem__(largeStepKey, value)
3439
3440            self._WriteNumpyArray(reorderedSliceList, existingData)
3441
3442        # Otherwise just write the data (no need to read the existing
3443        # data first).
3444
3445        else:
3446            self._WriteNumpyArray(reorderedSliceList, value)
3447
3448    # Private methods that the derived class must override (except
3449    # _GetCoords when the derived class only uses fixed coordinate
3450    # increments).
3451
3452    def _GetCoords(self, coord, coordNum, slices, sliceDims, fixedIncrementOffset):
3453        raise NotImplementedError(_(u'The _GetCoords method of class %s has not been implemented.') % self.__class__.__name__)
3454
3455    def _ReadNumpyArray(self, sliceList):
3456        raise NotImplementedError(_(u'The _ReadNumpyArray method of class %s has not been implemented.') % self.__class__.__name__)
3457
3458    def _WriteNumpyArray(self, sliceList, data):
3459        raise NotImplementedError(_(u'The _WriteNumpyArray method of class %s has not been implemented.') % self.__class__.__name__)
3460
3461
3462class _ContainerEmulator(object):      # Private helper class for Grid
3463    def __init__(self, grid, getMethod, setMethod=None):
3464        self._Grid = weakref.ref(grid)                  # This must be a weak reference so that we do not create a reference cycle and prevent the Grid instance from being garbage collected.
3465        self._GetMethod = getMethod
3466        self._SetMethod = setMethod
3467
3468    def __getitem__(self, key):
3469        return getattr(self._Grid(), self._GetMethod)(key)
3470
3471    def __setitem__(self, key, value):
3472        if self._SetMethod is None:
3473            raise TypeError('This property does not support item assignment')
3474        return getattr(self._Grid(), self._SetMethod)(key, value)
3475
3476
3477class NumpyGrid(Grid):
3478    __doc__ = DynamicDocString()
3479
3480    def _GetArray(self):
3481        return self._NumpyArray
3482   
3483    Array = property(_GetArray, doc=DynamicDocString())
3484   
3485    def __init__(self, numpyArray, displayName, spatialReference, dimensions, coordIncrements, cornerCoords, unscaledNoDataValue=None,
3486                 tIncrementUnit=None, tSemiRegularity=None, tCountPerSemiRegularPeriod=None, tCornerCoordType=None, tOffsetFromParsedTime=None, coordDependencies=None,
3487                 physicalDimensions=None, physicalDimensionsFlipped=None,
3488                 scaledDataType=None, scaledNoDataValue=None, scalingFunction=None, unscalingFunction=None,
3489                 parentCollection=None, queryableAttributes=None, queryableAttributeValues=None, lazyPropertyValues=None):
3490        self.__class__.__doc__.Obj.ValidateMethodInvocation()
3491
3492        # Perform additional validation.
3493
3494        if len(numpyArray.shape) not in [2, 3, 4]:
3495            raise ValueError(_(u'numpyArray must have 2, 3, or 4 dimensions.'))
3496
3497        if len(dimensions) != len(numpyArray.shape):
3498            raise ValueError(_(u'The length of the dimensions string must be equal to the number of dimensions of numpyArray.'))
3499
3500        if unscaledNoDataValue is not None:
3501            if numpyArray.dtype.name in ['int8', 'uint8', 'int16', 'uint16', 'int32'] and not isinstance(unscaledNoDataValue, types.IntType):
3502                raise TypeError(_(u'When numpyArray has the %(name)s dtype, unscaledNoDataValue must be a Python int.') % {u'name': numpyArray.dtype.name})
3503            if numpyArray.dtype.name == 'uint32' and not isinstance(unscaledNoDataValue, (types.IntType, types.LongType)):
3504                raise TypeError(_(u'When numpyArray has the %(name)s dtype, unscaledNoDataValue must be a Python int or long.') % {u'name': numpyArray.dtype.name})
3505            if numpyArray.dtype.name in ['float32', 'float64'] and not isinstance(unscaledNoDataValue, types.FloatType):
3506                raise TypeError(_(u'When numpyArray has the %(name)s dtype, unscaledNoDataValue must be a Python float.') % {u'name': numpyArray.dtype.name})
3507
3508        if 't' in dimensions:
3509            if tIncrementUnit is None:
3510                raise ValueError(_(u'tIncrementUnit must be provided when dimensions contains \'t\'.'))
3511            if tCornerCoordType is None:
3512                raise ValueError(_(u'tCornerCoordType must be provided when dimensions contains \'t\'.'))
3513
3514        if physicalDimensions is not None:
3515            if len(physicalDimensions) != len(dimensions):
3516                raise ValueError(_(u'physicalDimensions must be the same length as dimensions.'))
3517            for d in physicalDimensions:
3518                if d not in dimensions:
3519                    raise ValueError(_(u'The physicalDimensions string must contain the same characters as the dimensions string (but they may be in a different order).'))
3520
3521        if not all((scaledDataType is not None, scaledNoDataValue is not None, scalingFunction is not None, unscalingFunction is not None)) and not all((scaledDataType is None, scaledNoDataValue is None, scalingFunction is None, unscalingFunction is None)):
3522            raise ValueError(_(u'All of (scaledDataType, scaledNoDataValue, scalingFunction, unscalingFunction) must be None, or none of them must be None.'))
3523
3524        # Initialize our properties.
3525
3526        self._NumpyArray = numpyArray
3527        self._DisplayName = displayName
3528
3529        # Build a dictionary of lazy property values from the caller's
3530        # parameters.
3531
3532        if coordDependencies is None:
3533            coordDependencies = tuple([None] * len(dimensions))
3534
3535        if 't' not in dimensions:
3536            tIncrementUnit = None
3537            tSemiRegularity = None
3538            tCountPerSemiRegularPeriod = None
3539            tCornerCoordType = None
3540            tOffsetFromParsedTime = None
3541
3542        if physicalDimensions is None:
3543            physicalDimensions = dimensions
3544
3545        if physicalDimensionsFlipped is None:
3546            physicalDimensionsFlipped = tuple([False] * len(dimensions))
3547
3548        lpv = {'SpatialReference': spatialReference,
3549               'Dimensions': dimensions,
3550               'Shape': numpyArray.shape,
3551               'CoordDependencies': coordDependencies,
3552               'CoordIncrements': coordIncrements,
3553               'TIncrementUnit': tIncrementUnit,
3554               'TSemiRegularity': tSemiRegularity,
3555               'TCountPerSemiRegularPeriod': tCountPerSemiRegularPeriod,
3556               'TCornerCoordType': tCornerCoordType,
3557               'TOffsetFromParsedTime': tOffsetFromParsedTime,
3558               'CornerCoords': cornerCoords,
3559               'PhysicalDimensions': physicalDimensions,
3560               'PhysicalDimensionsFlipped': physicalDimensionsFlipped,
3561               'UnscaledDataType': numpyArray.dtype.name,
3562               'UnscaledNoDataValue': unscaledNoDataValue,
3563               'ScaledDataType': scaledDataType,
3564               'ScaledNoDataValue': scaledNoDataValue,
3565               'ScalingFunction': scalingFunction,
3566               'UnscalingFunction': unscalingFunction}
3567
3568        if lazyPropertyValues is not None:
3569            for name, value in lazyPropertyValues.items():
3570                lpv[name] = value
3571
3572        # Initialize the base class.
3573
3574        super(NumpyGrid, self).__init__(parentCollection=parentCollection, queryableAttributes=queryableAttributes, queryableAttributeValues=queryableAttributeValues, lazyPropertyValues=lpv)
3575
3576    def _GetDisplayName(self):
3577        return self._DisplayName
3578
3579    def _GetLazyPropertyPhysicalValue(self, name):
3580        return None
3581
3582    @classmethod
3583    def _TestCapability(cls, capability):
3584        if capability in ['setspatialreference']:
3585            return None
3586        if isinstance(cls, NumpyGrid):
3587            return RuntimeError(_(u'The %(cls)s class does not support the "%(cap)s" capability.') % {u'cls': cls.__class__.__name__, u'cap': capability})
3588        return RuntimeError(_(u'The %(cls)s class does not support the "%(cap)s" capability.') % {u'cls': cls.__name__, u'cap': capability})
3589
3590    @classmethod
3591    def _GetSRTypeForSetting(cls):
3592        return 'Obj'
3593
3594    def _SetSpatialReference(self, sr):
3595        self.SetLazyPropertyValue('SpatialReference', sr)
3596
3597    def _ReadNumpyArray(self, sliceList):
3598        return self._NumpyArray.__getitem__(sliceList), self.GetLazyPropertyValue('UnscaledNoDataValue')
3599
3600    def _WriteNumpyArray(self, sliceList, data):
3601        self._NumpyArray.__setitem__(sliceList, data)
3602
3603    @classmethod
3604    def CreateFromGrid(cls, grid):
3605        cls.__doc__.Obj.ValidateMethodInvocation()
3606
3607        cornerCoords = [grid.CenterCoords[d, 0] for d in grid.Dimensions]
3608        if 't' in grid.Dimensions:
3609            if grid.GetLazyPropertyValue('TCornerCoordType') == 'min':
3610                cornerCoords[0] = grid.MinCoords['t', 0]
3611            elif grid.GetLazyPropertyValue('TCornerCoordType') == 'max':
3612                cornerCoords[0] = grid.MaxCoords['t', 0]
3613
3614        qav = {}
3615        for qa in grid.GetAllQueryableAttributes():
3616            qav[qa.Name] = grid.GetQueryableAttributeValue(qa.Name)
3617
3618        return NumpyGrid(grid.Data[:],
3619                         grid.DisplayName,
3620                         grid.GetSpatialReference('Obj'),
3621                         grid.Dimensions,
3622                         grid.CoordIncrements,
3623                         tuple(cornerCoords),
3624                         grid.UnscaledNoDataValue,
3625                         grid.TIncrementUnit,
3626                         grid.TSemiRegularity,
3627                         grid.TCountPerSemiRegularPeriod,
3628                         grid.GetLazyPropertyValue('TCornerCoordType'),
3629                         grid.GetLazyPropertyValue('TOffsetFromParsedTime'),
3630                         grid.CoordDependencies,
3631                         queryableAttributes=tuple(grid.GetAllQueryableAttributes()),
3632                         queryableAttributeValues=qav)
3633
3634
3635###############################################################################
3636# Metadata: module
3637###############################################################################
3638
3639from GeoEco.Dependencies import PythonAggregatedModuleDependency
3640from GeoEco.Metadata import *
3641from GeoEco.Types import *
3642
3643AddModuleMetadata(shortDescription=_(u'Base classes that define interfaces for accessing tabular and gridded datasets.'))
3644
3645###############################################################################
3646# Metadata: CollectibleObject class
3647###############################################################################
3648
3649AddClassMetadata(CollectibleObject,
3650    shortDescription=_(u'An object that can appear in a DatasetCollection.'),
3651    longDescription=_(
3652u"""The primary purpose of CollectibleObject is to provide
3653functionality that is common to both Dataset and DatasetCollection, to
3654facilitate easy reuse of that functionality by both classes.
3655CollectibleObject should not be instantiated directly."""))
3656
3657# Public properties
3658
3659AddPropertyMetadata(CollectibleObject.DisplayName,
3660    typeMetadata=UnicodeStringTypeMetadata(),
3661    shortDescription=_(u'Informal name of this object.'),
3662    isExposedToPythonCallers=True)
3663
3664AddPropertyMetadata(CollectibleObject.ParentCollection,
3665    typeMetadata=ClassInstanceTypeMetadata(cls=DatasetCollection, canBeNone=True),
3666    shortDescription=_(u'DatasetCollection that is the parent of this object (if any).'),
3667    isExposedToPythonCallers=True)
3668
3669# Public method: CollectibleObject.GetQueryableAttribute
3670
3671AddMethodMetadata(CollectibleObject.GetQueryableAttribute,
3672    shortDescription=_(u'Returns the queryable attribute with the specified name.'),
3673    isExposedToPythonCallers=True)
3674
3675AddArgumentMetadata(CollectibleObject.GetQueryableAttribute, u'self',
3676    typeMetadata=ClassInstanceTypeMetadata(cls=CollectibleObject),
3677    description=_(u'%s instance.') % CollectibleObject.__name__)
3678
3679AddArgumentMetadata(CollectibleObject.GetQueryableAttribute, u'name',
3680    typeMetadata=UnicodeStringTypeMetadata(),
3681    description=_(
3682u"""Name of the queryable attribute to return."""))
3683
3684AddResultMetadata(CollectibleObject.GetQueryableAttribute, u'attr',
3685    typeMetadata=ClassInstanceTypeMetadata(cls=QueryableAttribute, canBeNone=True),
3686    description=_(
3687u"""QueryableAttribute instance with the specified name. If one is not
3688defined for this object, the chain of parent dataset collections (if
3689any) will be searched, starting with the immediate parent. If one is
3690still not found, None will be returned."""))
3691
3692# Public method: CollectibleObject.GetQueryableAttributesWithDataType
3693
3694AddMethodMetadata(CollectibleObject.GetQueryableAttributesWithDataType,
3695    shortDescription=_(u'Returns a list queryable attributes having the specified data type.'),
3696    isExposedToPythonCallers=True)
3697
3698CopyArgumentMetadata(CollectibleObject.GetQueryableAttribute, u'self', CollectibleObject.GetQueryableAttributesWithDataType, u'self')
3699
3700AddArgumentMetadata(CollectibleObject.GetQueryableAttributesWithDataType, u'typeMetadata',
3701    typeMetadata=ClassTypeMetadata(cls=TypeMetadata),
3702    description=_(
3703u"""Subclass of TypeMetadata that indicates the desired data type."""))
3704
3705AddResultMetadata(CollectibleObject.GetQueryableAttributesWithDataType, u'attrList',
3706    typeMetadata=ListTypeMetadata(elementType=ClassInstanceTypeMetadata(cls=QueryableAttribute)),
3707    description=_(
3708u"""List of QueryableAttribute instances having the specified data
3709type. This object and all of its parent dataset collections will be
3710searched for matching instances. If no matching instances are found,
3711the list will be empty."""))
3712
3713# Public method: CollectibleObject.GetAllQueryableAttributes
3714
3715AddMethodMetadata(CollectibleObject.GetAllQueryableAttributes,
3716    shortDescription=_(u'Returns a list of all queryable attributes.'),
3717    isExposedToPythonCallers=True)
3718
3719CopyArgumentMetadata(CollectibleObject.GetQueryableAttribute, u'self', CollectibleObject.GetAllQueryableAttributes, u'self')
3720
3721AddResultMetadata(CollectibleObject.GetAllQueryableAttributes, u'attrList',
3722    typeMetadata=ListTypeMetadata(elementType=ClassInstanceTypeMetadata(cls=QueryableAttribute)),
3723    description=_(
3724u"""List of QueryableAttribute instances defined for this object and
3725all of its parent dataset collections. If no instances have been
3726defined for it or any of its parents, the list will be empty."""))
3727
3728# Public method: CollectibleObject.GetQueryableAttributeValue
3729
3730AddMethodMetadata(CollectibleObject.GetQueryableAttributeValue,
3731    shortDescription=_(u'Returns the value of the queryable attribute with the specified name.'),
3732    isExposedToPythonCallers=True)
3733
3734CopyArgumentMetadata(CollectibleObject.GetQueryableAttribute, u'self', CollectibleObject.GetQueryableAttributeValue, u'self')
3735
3736AddArgumentMetadata(CollectibleObject.GetQueryableAttributeValue, u'name',
3737    typeMetadata=UnicodeStringTypeMetadata(),
3738    description=_(
3739u"""Name of the queryable attribute to return the value of."""))
3740
3741AddResultMetadata(CollectibleObject.GetQueryableAttributeValue, u'value',
3742    typeMetadata=AnyObjectTypeMetadata(canBeNone=True),
3743    description=_(
3744u"""Value of the queryable attribute with the specified name. If one
3745is not defined for this object, the chain of parent dataset
3746collections (if any) will be searched, starting with the immediate
3747parent. If one is still not found, None will be returned.
3748
3749None will also be returned if a queryable attribute is found but the
3750value of it is None. To determine whether None was returned because
3751the attribute's value was None or because the attribute does not
3752exist, use GetQueryableAttribute() to determine if the queryable
3753attribute exists."""))
3754
3755# Public method: CollectibleObject.TestCapability
3756
3757AddMethodMetadata(CollectibleObject.TestCapability,
3758    shortDescription=_(u'Tests whether a capability is supported by this class or an instance of it.'),
3759    longDescription=_(
3760u"""If called on an instance, this method tests whether the underlying
3761object represented by the instance supports the capability.
3762
3763If called on the class, this method tests whether at least one
3764instance can possibly support the capability. If no instances can ever
3765support the capability, this method will indicate that the capability
3766is not supported."""),
3767    isExposedToPythonCallers=True)
3768
3769AddArgumentMetadata(CollectibleObject.TestCapability, u'cls',
3770    typeMetadata=ClassOrClassInstanceTypeMetadata(cls=CollectibleObject),
3771    description=_(u'%s class or an instance of it.') % CollectibleObject.__name__)
3772
3773AddArgumentMetadata(CollectibleObject.TestCapability, u'capability',
3774    typeMetadata=UnicodeStringTypeMetadata(makeLowercase=True),
3775    description=_(
3776u"""Capability to test.
3777
3778This function does not provide a list of capabilities that may be
3779tested. Please see the documentation for the other methods of this
3780class to determine what capabilties may be tested that are relevant to
3781those methods."""))
3782
3783AddResultMetadata(CollectibleObject.TestCapability, u'error',
3784    typeMetadata=ClassInstanceTypeMetadata(cls=Exception, canBeNone=True),
3785    description=_(
3786u"""None if the capability is supported, or an instance of Exception
3787if it is not supported. The string representation of the Exception
3788explains why the capability is not supported (if possible) in the
3789context of when it might be needed."""))
3790
3791# Private constructor: CollectibleObject.__init__
3792
3793AddMethodMetadata(CollectibleObject.__init__,
3794    shortDescription=_(u'CollectibleObject constructor. Not intended to be called directly. Only intended to be called from derived class constructors.'))
3795
3796CopyArgumentMetadata(CollectibleObject.GetQueryableAttribute, u'self', CollectibleObject.__init__, u'self')
3797
3798AddArgumentMetadata(CollectibleObject.__init__, u'parentCollection',
3799    typeMetadata=CollectibleObject.ParentCollection.__doc__.Obj.Type,
3800    description=CollectibleObject.ParentCollection.__doc__.Obj.ShortDescription)
3801
3802AddArgumentMetadata(CollectibleObject.__init__, u'queryableAttributes',
3803    typeMetadata=TupleTypeMetadata(elementType=ClassInstanceTypeMetadata(cls=QueryableAttribute), canBeNone=True),
3804    description=_(u'Tuple of QueryableAttributes defined for this object and its children.'))      # TODO: Add long description?
3805
3806AddArgumentMetadata(CollectibleObject.__init__, u'queryableAttributeValues',
3807    typeMetadata=DictionaryTypeMetadata(keyType=UnicodeStringTypeMetadata(), valueType=AnyObjectTypeMetadata(canBeNone=True), canBeNone=True),
3808    description=_(u'Mapping of case-insensitive QueryAttribute names to values defined for this object and its children.'))      # TODO: Add longer description?
3809
3810AddArgumentMetadata(CollectibleObject.__init__, u'lazyPropertyValues',
3811    typeMetadata=DictionaryTypeMetadata(keyType=UnicodeStringTypeMetadata(), valueType=AnyObjectTypeMetadata(canBeNone=True), canBeNone=True),
3812    description=_(u'Mapping of lazy property names to values defined for this object.'))      # TODO: Add longer description?
3813
3814AddResultMetadata(CollectibleObject.__init__, u'obj',
3815    typeMetadata=ClassInstanceTypeMetadata(cls=CollectibleObject),
3816    description=_(u'%s instance.') % CollectibleObject.__name__)
3817
3818###############################################################################
3819# Metadata: QueryableAttribute class
3820###############################################################################
3821
3822AddClassMetadata(QueryableAttribute,
3823    shortDescription=_(u'Defines the characteristics of an attribute of a CollectibleObject that may be used to retrieve it from a DatasetCollection.'))    # TODO: add longDescription
3824
3825# Public properties
3826
3827AddPropertyMetadata(QueryableAttribute.Name,
3828    typeMetadata=UnicodeStringTypeMetadata(),
3829    shortDescription=_(u'Case-insensitive, non-localized formal name of this this queryable attribute.'))
3830
3831AddPropertyMetadata(QueryableAttribute.DisplayName,
3832    typeMetadata=UnicodeStringTypeMetadata(),
3833    shortDescription=_(u'Localized informal name of this queryable attribute, to be displayed in user interfaces, log messages, and similar places.'))
3834
3835AddPropertyMetadata(QueryableAttribute.DataType,
3836    typeMetadata=ClassInstanceTypeMetadata(cls=TypeMetadata),
3837    shortDescription=_(u'TypeMetadata instance describing the data type this queryable attribute.'))  # TODO: add long description
3838
3839AddPropertyMetadata(QueryableAttribute.DerivedLazyDatasetProps,
3840    typeMetadata=DictionaryTypeMetadata(keyType=AnyObjectTypeMetadata(canBeNone=True), valueType=DictionaryTypeMetadata(keyType=UnicodeStringTypeMetadata(), valueType=AnyObjectTypeMetadata(canBeNone=True)), canBeNone=True),
3841    shortDescription=_(u'Dictionary mapping values of this queryable attribute to names and values of lazy Dataset properties to assign.'))  # TODO: add long description
3842
3843AddPropertyMetadata(QueryableAttribute.DerivedFromAttr,
3844    typeMetadata=UnicodeStringTypeMetadata(canBeNone=True),
3845    shortDescription=_(u'Name of another queryable attribute that this one is derived from.'))  # TODO: add long description
3846
3847AddPropertyMetadata(QueryableAttribute.DerivedValueMap,
3848    typeMetadata=DictionaryTypeMetadata(keyType=AnyObjectTypeMetadata(), valueType=AnyObjectTypeMetadata(), canBeNone=True),
3849    shortDescription=_(u'Dictionary mapping values of the attribute that this one is derived from to values to use for this attribute.'))  # TODO: add long description
3850
3851AddPropertyMetadata(QueryableAttribute.DerivedValueFunc,
3852    typeMetadata=AnyObjectTypeMetadata(canBeNone=True),
3853    shortDescription=_(u'Function (may be a lambda) or method that should be called to derive the value of this attribute from the one it is derived from.'))  # TODO: add long description
3854
3855# Public constructor: QueryableAttribute.__init__
3856
3857AddMethodMetadata(QueryableAttribute.__init__,
3858    shortDescription=_(u'QueryableAttribute constructor.'))
3859
3860AddArgumentMetadata(QueryableAttribute.__init__, u'self',
3861    typeMetadata=ClassInstanceTypeMetadata(cls=QueryableAttribute),
3862    description=_(u'%s instance.') % QueryableAttribute.__name__)
3863
3864AddArgumentMetadata(QueryableAttribute.__init__, u'name',
3865    typeMetadata=QueryableAttribute.Name.__doc__.Obj.Type,
3866    description=QueryableAttribute.Name.__doc__.Obj.ShortDescription)
3867
3868AddArgumentMetadata(QueryableAttribute.__init__, u'displayName',
3869    typeMetadata=QueryableAttribute.DisplayName.__doc__.Obj.Type,
3870    description=QueryableAttribute.DisplayName.__doc__.Obj.ShortDescription)
3871
3872AddArgumentMetadata(QueryableAttribute.__init__, u'dataType',
3873    typeMetadata=QueryableAttribute.DataType.__doc__.Obj.Type,
3874    description=QueryableAttribute.DataType.__doc__.Obj.ShortDescription)
3875
3876AddArgumentMetadata(QueryableAttribute.__init__, u'derivedLazyDatasetProps',
3877    typeMetadata=QueryableAttribute.DerivedLazyDatasetProps.__doc__.Obj.Type,
3878    description=QueryableAttribute.DerivedLazyDatasetProps.__doc__.Obj.ShortDescription)
3879
3880AddArgumentMetadata(QueryableAttribute.__init__, u'derivedFromAttr',
3881    typeMetadata=QueryableAttribute.DerivedFromAttr.__doc__.Obj.Type,
3882    description=QueryableAttribute.DerivedFromAttr.__doc__.Obj.ShortDescription)
3883
3884AddArgumentMetadata(QueryableAttribute.__init__, u'derivedValueMap',
3885    typeMetadata=QueryableAttribute.DerivedValueMap.__doc__.Obj.Type,
3886    description=QueryableAttribute.DerivedValueMap.__doc__.Obj.ShortDescription)
3887
3888AddArgumentMetadata(QueryableAttribute.__init__, u'derivedValueFunc',
3889    typeMetadata=QueryableAttribute.DerivedValueFunc.__doc__.Obj.Type,
3890    description=QueryableAttribute.DerivedValueFunc.__doc__.Obj.ShortDescription)
3891
3892AddResultMetadata(QueryableAttribute.__init__, u'obj',
3893    typeMetadata=ClassInstanceTypeMetadata(cls=QueryableAttribute),
3894    description=_(u'%s instance.') % QueryableAttribute.__name__)
3895
3896###############################################################################
3897# Metadata: Dataset class
3898###############################################################################
3899
3900AddClassMetadata(Dataset,
3901    shortDescription=_(u'Represents tabular or gridded dataset.'),
3902    longDescription=_(
3903u"""The primary purpose of this class is to provide functionality that
3904is common to both the Table and Grid classes, such as properties and
3905methods relating to georeferencing, to facilitate easy reuse of that
3906functionality by both classes. We do not anticipate many scenarios
3907where someone will want to generically manipulate both Table and Grid
3908instances behind the Dataset abstraction."""))
3909
3910# Public method: Dataset.ConvertSpatialReference
3911
3912AddMethodMetadata(Dataset.ConvertSpatialReference,
3913    shortDescription=_(u'Converts a spatial reference from one format to another, such as an OGC WKT string to a Proj4 string.'),
3914    isExposedToPythonCallers=True)
3915
3916AddArgumentMetadata(Dataset.ConvertSpatialReference, u'cls',
3917    typeMetadata=ClassOrClassInstanceTypeMetadata(cls=Dataset),
3918    description=_(u'%s class or an instance of it.') % Dataset.__name__)
3919
3920AddArgumentMetadata(Dataset.ConvertSpatialReference, u'srType',
3921    typeMetadata=UnicodeStringTypeMetadata(allowedValues=[u'WKT', u'ArcGIS', u'Proj4', u'Obj'], makeLowercase=True),
3922    description=_(
3923u"""The kind of the spatial reference to convert, i.e. the type of
3924object you are providing for the sr parameter:
3925
3926* WKT - a WKT string in standard OGC format.
3927
3928* ArcGIS - a WKT string in ESRI format, typically obtained from
3929  a dataset produced by ArcGIS. (The ESRI format differs from the OGC
3930  standard; various projections and parameters are named differently
3931  and certain nodes are not recognized. See the OSR documentation for
3932  more information.)
3933
3934* Proj4 - a string in the format recognized by the Proj4 library.
3935
3936* Obj - an instance of the OSR SpatialReference class.
3937"""))
3938
3939# TODO: Talk about allocation and deallocation of OSR SpatialReference objects
3940
3941AddArgumentMetadata(Dataset.ConvertSpatialReference, u'sr',
3942    typeMetadata=AnyObjectTypeMetadata(canBeNone=True),
3943    description=_(
3944u"""Spatial reference to convert. This may be None, representing an
3945"undefined" spatial reference, but see the documentation for the
3946outputSRType parameter."""))
3947
3948AddArgumentMetadata(Dataset.ConvertSpatialReference, u'outputSRType',
3949    typeMetadata=UnicodeStringTypeMetadata(allowedValues=[u'WKT', u'ArcGIS', u'Proj4', u'Obj'], makeLowercase=True),
3950    description=_(
3951u"""The kind of the spatial reference to return. The allowed values
3952are the same as for the srType parameter.
3953
3954If srType and outputSRType are the same, a copy of the input spatial
3955reference will be returned. If they are 'Obj', a deep copy of the
3956input OSR SpatialReference instance will be created by initializing a
3957new instance from the OGC WKT exported from the input instance.
3958
3959If sr is None, None will be returned, except if outputSRType is
3960'ArcGIS', in which case the string
3961'{B286C06B-0879-11D2-AACA-00C04FA33C20}' will be returned. ArcGIS uses
3962this string to represent the "Unknown" spatial reference."""))
3963
3964AddResultMetadata(Dataset.ConvertSpatialReference, u'outputSR',
3965    typeMetadata=AnyObjectTypeMetadata(),
3966    description=_(
3967u"""Spatial reference resulting from the conversion, either a Unicode
3968string, an OGR SpatialReference instance, or None."""))
3969
3970# Public method: Dataset.GetSpatialReference
3971
3972AddMethodMetadata(Dataset.GetSpatialReference,
3973    shortDescription=_(u'Returns the spatial reference of this dataset.'),
3974    isExposedToPythonCallers=True)
3975
3976AddArgumentMetadata(Dataset.GetSpatialReference, u'self',
3977    typeMetadata=ClassInstanceTypeMetadata(cls=Dataset),
3978    description=_(u'%s instance.') % Dataset.__name__)
3979
3980AddArgumentMetadata(Dataset.GetSpatialReference, u'srType',
3981    typeMetadata=UnicodeStringTypeMetadata(allowedValues=[u'WKT', u'ArcGIS', u'Proj4', u'Obj'], makeLowercase=True),
3982    description=_(
3983u"""Type of spatial reference that should be returned:
3984
3985* WKT - a WKT string in standard OGC format.
3986
3987* ArcGIS - a WKT string in ESRI format, typically obtained from
3988  a dataset produced by ArcGIS. (The ESRI format differs from the OGC
3989  standard; various projections and parameters are named differently
3990  and certain nodes are not recognized. See the OSR documentation for
3991  more information.)
3992
3993* Proj4 - a string in the format recognized by the Proj4 library.
3994
3995* Obj - an instance of the OSR SpatialReference class.
3996
3997An OSR SpatialReference instance is stored internally. If 'Obj' is
3998requested, a reference to this instance is returned, not a copy of it,
3999allowing you to make change to the internal instance. This behavior is
4000by design. Take care not to make changes unintentionally. Use the
4001ConvertSpatialReference function to obtain a deep copy of the
4002instance, if needed.
4003
4004If something other than 'Obj' is requested, a string of the specified
4005type is exported from the internal SpatialRefenence instance and
4006returned."""))
4007
4008AddResultMetadata(Dataset.GetSpatialReference, u'sr',
4009    typeMetadata=AnyObjectTypeMetadata(),
4010    description=_(
4011u"""Spatial reference of the requested type, either a Unicode string,
4012an OGR SpatialReference instance, or None.
4013
4014If the dataset does not support a spatial reference (e.g. it is a
4015plain table), or it does support a spatial reference but it has never
4016been set, None will be returned, except if srType is 'ArcGIS', in
4017which case the string '{B286C06B-0879-11D2-AACA-00C04FA33C20}' will be
4018returned. ArcGIS uses this string to represent the "Unknown" spatial
4019reference."""))
4020
4021# Public method: Dataset.SetSpatialReference
4022
4023AddMethodMetadata(Dataset.SetSpatialReference,
4024    shortDescription=_(u'Sets the spatial reference of this dataset.'),
4025    longDescription=_(
4026u"""This function is similar in operation to the ArcGIS Define
4027Projection geoprocessing tool; it changes the spatial reference of the
4028dataset without changing any of the data itself. It does not
4029geographically project the existing data to the new spatial reference.
4030It is used mainly to fix datasets that do not have a spatial reference
4031or that are using the wrong one.
4032
4033Not all datasets support setting the spatial reference. To determine
4034if the spatial reference can be set, test the 'SetSpatialReference'
4035capability."""),
4036    isExposedToPythonCallers=True)
4037
4038CopyArgumentMetadata(Dataset.GetSpatialReference, u'self', Dataset.SetSpatialReference, u'self')
4039
4040AddArgumentMetadata(Dataset.SetSpatialReference, u'srType',
4041    typeMetadata=UnicodeStringTypeMetadata(allowedValues=[u'WKT', u'ArcGIS', u'Proj4', u'Obj'], makeLowercase=True),
4042    description=_(
4043u"""Type of spatial reference you are providing for the sr parameter.
4044
4045The allowed values are:
4046
4047* WKT - sr is a WKT string in standard OGC format.
4048
4049* ArcGIS - sr is a WKT string in ESRI format, typically obtained from
4050  a dataset produced by ArcGIS. (The ESRI format differs from the OGC
4051  standard; various projections and parameters are named differently
4052  and certain nodes are not recognized. See the OSR documentation for
4053  more information.)
4054
4055* Proj4 - sr is a string suitable for passing to the Proj4 utility.
4056
4057* Obj - sr is an instance of the OSR SpatialReference class.
4058"""))
4059
4060AddArgumentMetadata(Dataset.SetSpatialReference, u'sr',
4061    typeMetadata=AnyObjectTypeMetadata(),
4062    description=_(
4063u"""Spatial reference for the dataset."""))
4064
4065###############################################################################
4066# Metadata: DatasetCollection class
4067###############################################################################
4068
4069AddClassMetadata(DatasetCollection,
4070    shortDescription=_(u'TODO: Add description.'))
4071
4072# Public properties
4073
4074AddPropertyMetadata(DatasetCollection.CacheDirectory,
4075    typeMetadata=DirectoryTypeMetadata(canBeNone=True),
4076    shortDescription=_(u'Directory for caching local copies of remote datasets.'))      # TODO: add longDescription
4077
4078# Public method: DatasetCollection.QueryDatasets
4079
4080AddMethodMetadata(DatasetCollection.QueryDatasets,
4081    shortDescription=_(u'Queries the collection for Datasets that match a search expression.'),
4082    isExposedToPythonCallers=True,
4083    dependencies=[PythonAggregatedModuleDependency('pyparsing')])
4084
4085AddArgumentMetadata(DatasetCollection.QueryDatasets, u'self',
4086    typeMetadata=ClassInstanceTypeMetadata(cls=DatasetCollection),
4087    description=_(u'%s instance.') % DatasetCollection.__name__)
4088
4089AddArgumentMetadata(DatasetCollection.QueryDatasets, u'expression',
4090    typeMetadata=UnicodeStringTypeMetadata(canBeNone=True),
4091    description=_(
4092u"""TODO: Write description"""))
4093
4094AddArgumentMetadata(DatasetCollection.QueryDatasets, u'reportProgress',
4095    typeMetadata=BooleanTypeMetadata(),
4096    description=_(
4097u"""TODO: Write description"""))
4098
4099AddArgumentMetadata(DatasetCollection.QueryDatasets, u'options',
4100    typeMetadata=DictionaryTypeMetadata(keyType=ClassInstanceTypeMetadata(cls=basestring), valueType=AnyObjectTypeMetadata(canBeNone=True)),
4101    description=_(
4102u"""TODO: Write description"""))
4103
4104AddResultMetadata(DatasetCollection.QueryDatasets, u'datasets',
4105    typeMetadata=ListTypeMetadata(elementType=ClassInstanceTypeMetadata(cls=Dataset)),
4106    description=_(
4107u"""TODO: Write description"""))
4108
4109# Public method: DatasetCollection.GetOldestDataset
4110
4111AddMethodMetadata(DatasetCollection.GetOldestDataset,
4112    shortDescription=_(u'Queries the collection and returns the oldest Dataset that matches the search expression.'),
4113    isExposedToPythonCallers=True,
4114    dependencies=[PythonAggregatedModuleDependency('pyparsing')])
4115
4116CopyArgumentMetadata(DatasetCollection.QueryDatasets, u'self', DatasetCollection.GetOldestDataset, u'self')
4117CopyArgumentMetadata(DatasetCollection.QueryDatasets, u'expression', DatasetCollection.GetOldestDataset, u'expression')
4118CopyArgumentMetadata(DatasetCollection.QueryDatasets, u'options', DatasetCollection.GetOldestDataset, u'options')
4119
4120AddResultMetadata(DatasetCollection.GetOldestDataset, u'dataset',
4121    typeMetadata=ClassInstanceTypeMetadata(cls=Dataset),
4122    description=_(
4123u"""TODO: Write description"""))
4124
4125# Public method: DatasetCollection.GetNewestDataset
4126
4127AddMethodMetadata(DatasetCollection.GetNewestDataset,
4128    shortDescription=_(u'Queries the collection and returns the newest Dataset that matches the search expression.'),
4129    isExposedToPythonCallers=True,
4130    dependencies=[PythonAggregatedModuleDependency('pyparsing')])
4131
4132CopyArgumentMetadata(DatasetCollection.QueryDatasets, u'self', DatasetCollection.GetNewestDataset, u'self')
4133CopyArgumentMetadata(DatasetCollection.QueryDatasets, u'expression', DatasetCollection.GetNewestDataset, u'expression')
4134CopyArgumentMetadata(DatasetCollection.QueryDatasets, u'options', DatasetCollection.GetNewestDataset, u'options')
4135
4136AddResultMetadata(DatasetCollection.GetNewestDataset, u'dataset',
4137    typeMetadata=ClassInstanceTypeMetadata(cls=Dataset),
4138    description=_(
4139u"""TODO: Write description"""))
4140
4141# Private constructor: DatasetCollection.__init__
4142
4143AddMethodMetadata(DatasetCollection.__init__,
4144    shortDescription=_(u'DatasetCollection constructor. Not intended to be called directly. Only intended to be called from derived class constructors.'))
4145
4146CopyArgumentMetadata(DatasetCollection.QueryDatasets, u'self', DatasetCollection.__init__, u'self')
4147
4148AddArgumentMetadata(DatasetCollection.__init__, u'parentCollection',
4149    typeMetadata=DatasetCollection.ParentCollection.__doc__.Obj.Type,
4150    description=DatasetCollection.ParentCollection.__doc__.Obj.ShortDescription)
4151
4152AddArgumentMetadata(DatasetCollection.__init__, u'queryableAttributes',
4153    typeMetadata=CollectibleObject.__init__.__doc__.Obj.GetArgumentByName(u'queryableAttributes').Type,
4154    description=CollectibleObject.__init__.__doc__.Obj.GetArgumentByName(u'queryableAttributes').Description)
4155
4156AddArgumentMetadata(DatasetCollection.__init__, u'queryableAttributeValues',
4157    typeMetadata=CollectibleObject.__init__.__doc__.Obj.GetArgumentByName(u'queryableAttributeValues').Type,
4158    description=CollectibleObject.__init__.__doc__.Obj.GetArgumentByName(u'queryableAttributeValues').Description)
4159
4160AddArgumentMetadata(DatasetCollection.__init__, u'lazyPropertyValues',
4161    typeMetadata=CollectibleObject.__init__.__doc__.Obj.GetArgumentByName(u'lazyPropertyValues').Type,
4162    description=CollectibleObject.__init__.__doc__.Obj.GetArgumentByName(u'lazyPropertyValues').Description)
4163
4164AddArgumentMetadata(DatasetCollection.__init__, u'cacheDirectory',
4165    typeMetadata=DatasetCollection.CacheDirectory.__doc__.Obj.Type,
4166    description=DatasetCollection.CacheDirectory.__doc__.Obj.ShortDescription)
4167
4168AddResultMetadata(DatasetCollection.__init__, u'obj',
4169    typeMetadata=ClassInstanceTypeMetadata(cls=DatasetCollection),
4170    description=_(u'%s instance.') % DatasetCollection.__name__)
4171
4172###############################################################################
4173# Metadata: Table class
4174###############################################################################
4175
4176AddClassMetadata(Table,
4177    shortDescription=_(u'Represents a tabular dataset, such as a database table, shapefile, or geodatabase feature class.'))
4178
4179# Public properties
4180
4181AddPropertyMetadata(Table.HasOID,
4182    typeMetadata=BooleanTypeMetadata(),
4183    shortDescription=_(u'True if rows of this dataset have object IDs (OIDs).'),
4184    longDescription=_(
4185u"""An object ID is a 32-bit integer that uniquely identifies the row.
4186Many data formats have object IDs, including shapefiles (which have
4187feature IDs, or FIDs) and ArcGIS geodatabase tables and feature
4188classes (which have a field called OBJECTID). Object IDs are assigned
4189and managed by a GIS system or the data format itself.
4190
4191If this dataset does have an object ID, the HasOID property will be
4192True and you can retrieve the object ID through the GetOID method of
4193the SelectCursor and UpdateCursor classes. Because object IDs are
4194assigned and managed by the underlying data format or GIS system, it
4195is not possible to set object IDs."""))
4196
4197AddPropertyMetadata(Table.OIDFieldName,
4198    typeMetadata=UnicodeStringTypeMetadata(canBeNone=True),
4199    shortDescription=_(u'Name of the field of the dataset that contains the object ID, or None of no such field exists.'),
4200    longDescription=_(
4201u"""Some data formats store the object ID in a field, and you can
4202retrieve the object ID by getting the value of that field. That method
4203of retrieving the object ID is generally not recommended, because some
4204data formats that have object IDs do not store them in a field.
4205Instead, you should use the GetOID method of the SelectCursor or
4206UpdateCursor classes, which works whether or not the object ID is
4207accessible through a field."""))
4208
4209AddPropertyMetadata(Table.GeometryType,
4210    typeMetadata=UnicodeStringTypeMetadata(canBeNone=True),
4211    shortDescription=_(u'Geometry type of the dataset, or None if the dataset does not have geometry (i.e. it is a plain table).'),
4212    longDescription=_(
4213u"""The supported geometry types are a subset of those provided by the
4214OGR library, namely:
4215
4216* Point
4217* LineString
4218* Polygon
4219* MultiPoint
4220* MultiLineString
4221* MultiPolygon
4222* GeometryCollection
4223* Point25D
4224* LineString25D
4225* Polygon25D
4226* MultiPoint25D
4227* MultiLineString25D
4228* MultiPolygon25D
4229* GeometryCollection25D
4230
4231The 25D types have "two and a half dimensional" geometry, as defined
4232by OGR. In ArcGIS terminology, these geometry types those that have Z
4233coordinates.
4234
4235Not all derived classes support all of these geometry types. For
4236example, ArcGISTabularLayers do not support the GeometryCollection or
4237GeometryCollection25D geometry type. To test whether a derived class
4238supports a geometry type, pass the string 'GeometryType t' to the
4239TestCapability method, replacing t with the geometry type you want to
4240test.
4241
4242It is not possible to test the geometry type on a per-dataset basis;
4243calling TestCapability on a class instance will yield the same result
4244as calling it on the class."""))
4245
4246AddPropertyMetadata(Table.GeometryFieldName,
4247    typeMetadata=UnicodeStringTypeMetadata(canBeNone=True),
4248    shortDescription=_(u'Name of the field of the dataset that contains the geometry, or None of no such field exists.'),
4249    longDescription=_(
4250u"""Some data formats store the geometry in a field, and you can
4251retrieve it by getting the value of that field. That method of
4252retrieving the geometry is generally not recommended, because some
4253data formats that have geometry do not store it in a field. Instead,
4254you should use the GetGeometry method of the SelectCursor or
4255UpdateCursor classes, which works whether or not the geometry is
4256accessible through a field."""))
4257
4258AddPropertyMetadata(Table.MaxStringLength,
4259    typeMetadata=IntegerTypeMetadata(minValue=1, canBeNone=True),
4260    shortDescription=_(u'Maximum length of string fields that can be created with the AddField method, or None if there is no known maximum length.'),
4261    longDescription=_(
4262u"""Some data formats limit the length of string fields. For example,
4263shapefiles limit string fields to 254 characters. If the limit is
4264known, it can be obtained here.
4265
4266Note that if None is returned, it may indicate either that there is no
4267limit, or that one exists but it is not known. If None is returned,
4268the underlying API used to add the field may still fail if you request
4269a length that is too large."""))
4270
4271AddPropertyMetadata(Table.Fields,
4272    typeMetadata=TupleTypeMetadata(elementType=ClassInstanceTypeMetadata(cls=Field)),
4273    shortDescription=_(u'Tuple of Field instances representing the fields of the dataset.'),
4274    longDescription=_(
4275u"""For efficiency, this property directly references the internal
4276tuple of Field instances maintained by the Table instance. Take care
4277to not modify the instances contained within it."""))
4278
4279### Public method: Table.GetFieldByName
4280##
4281##AddMethodMetadata(Table.GetFieldByName,
4282##    shortDescription=_(u'Given the name of a field of the dataset, returns the Field instance representing the field.'),
4283##    longDescription=_(
4284##u"""This method is mainly intended to be a convenient alternative
4285##toperforming a linear probe on the Fields property."""),
4286##    isExposedToPythonCallers=True)
4287##
4288##AddArgumentMetadata(Table.GetFieldByName, u'self',
4289##    typeMetadata=ClassInstanceTypeMetadata(cls=Table),
4290##    description=_(u'%s instance.') % Table.__name__)
4291##
4292##AddArgumentMetadata(Table.GetFieldByName, u'name',
4293##    typeMetadata=UnicodeStringTypeMetadata(),
4294##    description=_(
4295##u"""Name of the field to be returned. This name is case-insensitive,
4296##even if the underlying data store is case sensitive."""))
4297##
4298##AddResultMetadata(Table.GetFieldByName, u'field',
4299##    typeMetadata=ClassInstanceTypeMetadata(cls=Field, canBeNone=True),
4300##    description=_(
4301##u"""Field instance representing the field, or None if the dataset does
4302##not contain a field with the specified name."""))
4303##
4304### Public method: Table.AddField
4305##
4306##AddMethodMetadata(Table.AddField,
4307##    shortDescription=_(u'Adds a field to the dataset.'),
4308##    longDescription=_(
4309##u"""If the field cannot be added, an appropriate error will be raised.
4310##This can happen for a variety of reasons including but not limited to:
4311##
4312##* The underlying data format or programming library used to access
4313##  it does not support the addition of fields.
4314##
4315##* The table is not empty, and the format or library only allow fields
4316##  to be added when it is empty.
4317##
4318##* The format or library does not support the requested parameters. For
4319##  example, it may not support the requested data type.
4320##
4321##* The dataset is read-only, or the caller does not have sufficient
4322##  privileges to add fields.
4323##"""),
4324##    isExposedToPythonCallers=True)
4325##
4326##CopyArgumentMetadata(Table.GetFieldByName, u'self', Table.AddField, u'self')
4327##
4328##AddArgumentMetadata(Table.AddField, u'name',
4329##    typeMetadata=UnicodeStringTypeMetadata(),
4330##    description=_(
4331##u"""Name of the field to add.
4332##
4333##The name must conform to all rules imposed by the underlying data
4334##format and programming library used to access it. The caller is
4335##expected to be aware of these rules and this function attempts to fail
4336##if any rule is violated. Certain libraries are designed to
4337##automatically modify the caller's illegal name to a legal name. Where
4338##possible, this function overrides that behavior and tries to fail
4339##anyway.
4340##
4341##This function treats names as case-insensitive, even if the underlying
4342##format and library support case-sensitive names. This behavior should
4343##be of little consequence to most callers; this function will pass the
4344##name to the library without changing the case. The only callers that
4345##will be affected are those who require the ability to create multiple
4346##fields with the same name but different case. That scenario is not
4347##supported, regardless of whether the format and library may support
4348##it."""))
4349##
4350##AddArgumentMetadata(Table.AddField, u'dataType',
4351##    typeMetadata=UnicodeStringTypeMetadata(allowedValues=[u'binary', u'date', u'datetime', u'float32', u'float64', u'int16', u'int32', u'string'], makeLowercase=True),
4352##    description=_(
4353##u"""Data type of the field to add.
4354##
4355##The underlying data format and programming library may only support a
4356##subset of the possible data types. To test whether a data type is
4357##supported, pass the string 't DataType' to the TestCapability
4358##method, replacing t with the data type you want to test.
4359##
4360##If the allowSafeCoercions parameter is True (the default) and the
4361##format or library do not support the requested data type but they do
4362##support an alternative data type that can fully represent the values
4363##of the requested data type, the field will be created with that
4364##alternative data type. See the allowSafeCoercions parameter for more
4365##information."""))
4366##
4367##AddArgumentMetadata(Table.AddField, u'length',
4368##    typeMetadata=IntegerTypeMetadata(canBeNone=True),
4369##    description=_(
4370##u"""Length of the field to add.
4371##
4372##The caller is expected to be aware of the appropriate values for this
4373##parameter, based on the underlying data format and programming library
4374##used to access it. If this parameter is provided, it will be passed
4375##without any validation to the library. Typically, this parameter
4376##should only be provided when creating fields that have the 'string' or
4377##'binary' data type.
4378##
4379##Most formats and libraries impose upper limits on the length of string
4380##fields. For some of these, the MaxStringLength property will return
4381##the upper limit."""))
4382##
4383##AddArgumentMetadata(Table.AddField, u'precision',
4384##    typeMetadata=IntegerTypeMetadata(canBeNone=True),
4385##    description=_(
4386##u"""Precision of the field to add.
4387##
4388##The caller is expected to be aware of the appropriate values for this
4389##parameter, based on the underlying data format and programming library
4390##used to access it. If this parameter is provided, it will be passed
4391##without any validation to the library. Typically, this parameter
4392##should only be provided when creating fields that have the 'float32'
4393##or 'float64' data type."""))
4394##
4395##AddArgumentMetadata(Table.AddField, u'isNullable',
4396##    typeMetadata=BooleanTypeMetadata(canBeNone=True),
4397##    description=_(
4398##u"""Indicates whether or not the added field should be nullable.
4399##
4400##The default value of this parameter, None, indicates that the
4401##nullability should be decided by the default behavior of the
4402##underlying data format and programming library used to access it. The
4403##value True indicates that the library should be instructed to create a
4404##nullable field; False indicates that it should be instructed to create
4405##a non-nullable field.
4406##
4407##If True or False is provided and the underlying format or library does
4408##not support nullable fields, this function attempts recognize the
4409##condition and raise an error. Some libraries will allow nullable
4410##fields to be created even though the underlying format does not truly
4411##support them. For example, at the time of this writing, OGR did not
4412##recognize the concept of nullability and essentially treated all
4413##fields as nullable. In situations like this, we try not to rely on the
4414##underlying library to decide whether nullability is supported but
4415##detect it independently."""))
4416##
4417##AddArgumentMetadata(Table.AddField, u'allowSafeCoercions',
4418##    typeMetadata=BooleanTypeMetadata(),
4419##    description=_(
4420##u"""If True and the underlying data format or programming library do
4421##not support the requested data type but they do support an alternative
4422##data type that can fully represent the values of the requested data
4423##type, the field will be added with the alternative data type. If False
4424##and the requested data type is not supported, an error will be raised.
4425##
4426##If True is provided, this function will try the following alternative
4427##data types in the order listed and use the first one that is
4428##supported:
4429##
4430##===================  =======================
4431##Requested Data Type  Alternate Data Types
4432##===================  =======================
4433##date                 datetime
4434##int16                int32, float32, float64
4435##int32                float64
4436##float32              float64
4437##===================  =======================
4438##"""))
4439##
4440##AddArgumentMetadata(Table.AddField, u'failIfExists',
4441##    typeMetadata=BooleanTypeMetadata(),
4442##    description=_(
4443##u"""If True and a field already exists with the name requested by the
4444##caller, an error will raised. If False and a field already exists with
4445##the name requested by the caller, an error will not be raised as long
4446##as that the field has the exact characteristics requested by the
4447##caller (data type, length, and so on).
4448##
4449##As noted above, this function treats field names as case-insensitive,
4450##even if the underlying data format or programming library treat them
4451##as case-sensitive."""))
4452##
4453### Public method: Table.DeleteField
4454##
4455##AddMethodMetadata(Table.DeleteField,
4456##    shortDescription=_(u'Deletes a field from the dataset.'),
4457##    longDescription=_(
4458##u"""If the field cannot be deleted, an appropriate error will be
4459##raised. This can happen for a variety of reasons including but not
4460##limited to:
4461##
4462##* The underlying data format or programming library used to access
4463##  it does not support the deletion of fields.
4464##
4465##* The table is not empty, and the format or library only allow fields
4466##  to be deleted when it is empty.
4467##
4468##* The dataset is read-only, or the caller does not have sufficient
4469##  privileges to delete fields.
4470##"""),
4471##    isExposedToPythonCallers=True)
4472##
4473##CopyArgumentMetadata(Table.GetFieldByName, u'self', Table.DeleteField, u'self')
4474##
4475##AddArgumentMetadata(Table.DeleteField, u'name',
4476##    typeMetadata=UnicodeStringTypeMetadata(),
4477##    description=_(
4478##u"""Name of the field to delete.
4479##
4480##The name must conform to all rules imposed by the underlying data
4481##format and programming library used to access it. The caller is
4482##expected to be aware of these rules and this function attempts to fail
4483##if any rule is violated. Certain libraries are designed to
4484##automatically modify the caller's illegal name to a legal name. Where
4485##possible, this function overrides that behavior and tries to fail
4486##anyway.
4487##
4488##This function treats names as case-insensitive, even if the underlying
4489##format and library support case-sensitive names. This behavior should
4490##be of little consequence to most callers. The only callers that will
4491##be affected are those who require the ability to have multiple fields
4492##with the same name but different case. That scenario is not supported,
4493##regardless of whether the format and library may support it, and the
4494##behavior of this function in that scenario is undefined."""))
4495##
4496##AddArgumentMetadata(Table.DeleteField, u'failIfDoesNotExist',
4497##    typeMetadata=BooleanTypeMetadata(),
4498##    description=_(
4499##u"""If True and a field does not exist with the name requested by the
4500##caller, an error will raised. If False and a field does not exist with
4501##the name requested by the caller, an error will not be raised.
4502##
4503##As noted above, this function treats field names as case-insensitive,
4504##even if the underlying data format or programming library treat them
4505##as case-sensitive."""))
4506##
4507### Public method: Table.GetRowCount
4508##
4509##AddMethodMetadata(Table.GetRowCount,
4510##    shortDescription=_(u'Returns the number of rows in the dataset.'),
4511##    longDescription=_(
4512##u"""For certain datasets, calling this function might require the
4513##underlying programming library used to access the data to read the
4514##entire table, a potentially lengthy operation. If this behavior is
4515##undesirable, the caller is expected to know that it will occur and not
4516##call this function."""),
4517##    isExposedToPythonCallers=True)
4518##
4519##CopyArgumentMetadata(Table.GetFieldByName, u'self', Table.GetRowCount, u'self')
4520##
4521##AddResultMetadata(Table.GetRowCount, u'count',
4522##    typeMetadata=IntegerTypeMetadata(minValue=0),
4523##    description=_(
4524##u"""Number of rows in the dataset."""))
4525##
4526### Public method: Table.OpenSelectCursor
4527##
4528##AddMethodMetadata(Table.OpenSelectCursor,
4529##    shortDescription=_(u'Opens a forward-only cursor for reading rows from the dataset.'),
4530##    isExposedToPythonCallers=True)
4531##
4532##CopyArgumentMetadata(Table.GetFieldByName, u'self', Table.OpenSelectCursor, u'self')
4533##
4534##AddArgumentMetadata(Table.OpenSelectCursor, u'fields',
4535##    typeMetadata=ListTypeMetadata(elementType=UnicodeStringTypeMetadata(), canBeNone=True),
4536##    description=_(
4537##u"""Fields to retrieve for each row (i.e., the columns specified in
4538##the SELECT clause of a SQL SELECT statement). If no fields are
4539##provided, all fields will be returned.
4540##
4541##Do not provide "\*", as would be done in a SQL SELECT statement that
4542##wanted to retrieve all fields. To retrieve all fields, simply omit
4543##this parameter when you call this method.
4544##
4545##This usual reason to suppy a list of fields is to minimize database
4546##and network load. If you are not concerned about database peformance,
4547##there is no reason to provide a value for this parameter.
4548##
4549##If you do provide a value for this parameter and the dataset has a
4550##geometry field and you want to access the geometry through the
4551##cursor's GetGeometry method, be sure you request the geometry field
4552##with this parameter."""))
4553##
4554##AddArgumentMetadata(Table.OpenSelectCursor, u'where',
4555##    typeMetadata=UnicodeStringTypeMetadata(canBeNone=True),
4556##    description=_(
4557##u"""SQL WHERE clause that specifies the subset of rows to retrieve. If
4558##this parameter is not provided, all of the rows will be retrieved.
4559##
4560##The exact syntax and behavior of this parameter depends on the
4561##underlying data format and programming library used to access it. For
4562##more information, please see the documentation for the derived class
4563##you intend to use to access the dataset.
4564##
4565##The underlying format and library perform the actual evaluation of
4566##this parameter and determine all comparison rules, such as whether
4567##string comparisons are case-sensitive or case-insensitive. At present,
4568##there is no mechanism to interrogate or manipulate these rules; you
4569##must live with the default behavior of the underlying format and
4570##library."""))
4571##
4572##AddArgumentMetadata(Table.OpenSelectCursor, u'orderBy',
4573##    typeMetadata=UnicodeStringTypeMetadata(canBeNone=True, mustMatchRegEx=u'\s*\S+(\s+([aA][sS][cC]|[dD][eE][sS][cC]))?\s*(,\s*\S+(\s+([aA][sS][cC]|[dD][eE][sS][cC]))?\s*)*'),
4574##    description=_(
4575##u"""SQL ORDER BY clause that specifies the order in which the rows
4576##should be returned. If this parameter is not provided, the rows will
4577##ordered according to the default behavior of the underlying data
4578##format and the programming library used to access it.
4579##
4580##The ORDER BY clause must be a a comma-separated list of fields. Each
4581##field can optionally be followed by a space and the word ASC to
4582##indicate ascending order or DESC to indicate descending order. If
4583##neither is specified, ASC is assumed.
4584##
4585##The underlying format and library perform the actual evaluation of
4586##this parameter and determine the rules of sorting, such as whether
4587##string comparisons are case-sensitive or case-insensitive. At present,
4588##there is no mechanism to interrogate or manipulate these rules; you
4589##must live with the default behavior of the underlying format and
4590##library."""))
4591##
4592##AddArgumentMetadata(Table.OpenSelectCursor, u'rowCount',
4593##    typeMetadata=IntegerTypeMetadata(canBeNone=True, minValue=1),
4594##    description=_(
4595##u"""Number of rows that this cursor will access, if known ahead of
4596##time. If the number of rows is not known, omit this parameter.
4597##
4598##This parameter is only used in progress reporting and is ignored if
4599##reportProgress is False. If this parameter is provided, the progress
4600##reports will include the number of rows remaining and an estimated
4601##time of completion. If a value is not provided, the progress reports
4602##will only include the number of rows accessed so far.
4603##
4604##If you omit both this parameter and the WHERE clause parameter, and
4605##the underlying data format and programming library support retrieval
4606##of row counts, then the row count will be obtained automatically when
4607##the cursor is opened."""))
4608##
4609##AddArgumentMetadata(Table.OpenSelectCursor, u'reportProgress',
4610##    typeMetadata=BooleanTypeMetadata(),
4611##    description=_(
4612##u"""If True, progress messages will be logged periodically. If False,
4613##no messages will be logged.
4614##
4615##Progress messages are logged with the Python logging module, using the
4616##"informational" logging level and the "GeoEco.Datasets" logger. The
4617##first message will be logged after one minute has passed, and
4618##subsequent messages will be logged every five minutes thereafter."""))
4619##
4620##AddArgumentMetadata(Table.OpenSelectCursor, u'rowDescriptionSingular',
4621##    typeMetadata=UnicodeStringTypeMetadata(canBeNone=True),
4622##    description=_(
4623##u"""Localized word to use in progress and error messages for a single
4624##row. If this parameter is omitted, an appropriate generic word will be
4625##automatically selected based on the geometry type of the dataset, such
4626##as "point", "line", "polygon", and so on. If the dataset does not have
4627##geometry, "row" will be used."""))
4628##
4629##AddArgumentMetadata(Table.OpenSelectCursor, u'rowDescriptionPlural',
4630##    typeMetadata=UnicodeStringTypeMetadata(canBeNone=True),
4631##    description=_(
4632##u"""Localized word to use in progress and error messages for plural
4633##rows. If this parameter is omitted, an appropriate generic word will
4634##be automatically selected based on the geometry type of the dataset,
4635##such as "points", "lines", "polygons", and so on. If the dataset does not
4636##have geometry, "rows" will be used."""))
4637##
4638##AddResultMetadata(Table.OpenSelectCursor, u'cursor',
4639##    typeMetadata=ClassInstanceTypeMetadata(cls=SelectCursor),
4640##    description=_(
4641##u"""SelectCursor instance representing the open select cursor. The
4642##cursor will be closed automatically when all references to the
4643##instance are released. See the documentation for the SelectCursor
4644##class for more information about how to use the SelectCursor
4645##instance."""))
4646##
4647### Public method: Table.OpenUpdateCursor
4648##
4649##AddMethodMetadata(Table.OpenUpdateCursor,
4650##    shortDescription=_(u'Opens a forward-only cursor for updating and deleting rows of the dataset.'),
4651##    isExposedToPythonCallers=True)
4652##
4653##CopyArgumentMetadata(Table.OpenSelectCursor, u'self', Table.OpenUpdateCursor, u'self')
4654##CopyArgumentMetadata(Table.OpenSelectCursor, u'fields', Table.OpenUpdateCursor, u'fields')
4655##CopyArgumentMetadata(Table.OpenSelectCursor, u'where', Table.OpenUpdateCursor, u'where')
4656##CopyArgumentMetadata(Table.OpenSelectCursor, u'orderBy', Table.OpenUpdateCursor, u'orderBy')
4657##CopyArgumentMetadata(Table.OpenSelectCursor, u'rowCount', Table.OpenUpdateCursor, u'rowCount')
4658##CopyArgumentMetadata(Table.OpenSelectCursor, u'reportProgress', Table.OpenUpdateCursor, u'reportProgress')
4659##CopyArgumentMetadata(Table.OpenSelectCursor, u'rowDescriptionSingular', Table.OpenUpdateCursor, u'rowDescriptionSingular')
4660##CopyArgumentMetadata(Table.OpenSelectCursor, u'rowDescriptionPlural', Table.OpenUpdateCursor, u'rowDescriptionPlural')
4661##
4662##AddResultMetadata(Table.OpenUpdateCursor, u'cursor',
4663##    typeMetadata=ClassInstanceTypeMetadata(cls=UpdateCursor),
4664##    description=_(
4665##u"""UpdateCursor instance representing the open update cursor. The
4666##cursor will be closed automatically when all references to the
4667##instance are released. See the documentation for the UpdateCursor
4668##class for more information about how to use the UpdateCursor
4669##instance."""))
4670##
4671### Public method: Table.OpenInsertCursor
4672##
4673##AddMethodMetadata(Table.OpenInsertCursor,
4674##    shortDescription=_(u'Opens a forward-only cursor for updating and deleting rows of the dataset.'),
4675##    isExposedToPythonCallers=True)
4676##
4677##CopyArgumentMetadata(Table.OpenSelectCursor, u'self', Table.OpenInsertCursor, u'self')
4678##
4679##AddArgumentMetadata(Table.OpenInsertCursor, u'rowCount',
4680##    typeMetadata=IntegerTypeMetadata(canBeNone=True, minValue=1),
4681##    description=_(
4682##u"""Number of rows that this cursor will insert, if known ahead of
4683##time. If the number of rows is not known, omit this parameter.
4684##
4685##This parameter is only used in progress reporting and is ignored if
4686##reportProgress is False. If this parameter is provided, the progress
4687##reports will include the number of rows remaining and an estimated
4688##time of completion. If a value is not provided, the progress reports
4689##will only include the number of rows inserted so far."""))
4690##
4691##CopyArgumentMetadata(Table.OpenSelectCursor, u'reportProgress', Table.OpenInsertCursor, u'reportProgress')
4692##CopyArgumentMetadata(Table.OpenSelectCursor, u'rowDescriptionSingular', Table.OpenInsertCursor, u'rowDescriptionSingular')
4693##CopyArgumentMetadata(Table.OpenSelectCursor, u'rowDescriptionPlural', Table.OpenInsertCursor, u'rowDescriptionPlural')
4694##
4695##AddResultMetadata(Table.OpenInsertCursor, u'cursor',
4696##    typeMetadata=ClassInstanceTypeMetadata(cls=InsertCursor),
4697##    description=_(
4698##u"""OpenInsertCursor instance representing the open insert cursor. The
4699##cursor will be closed automatically when all references to the
4700##instance are released. See the documentation for the InsertCursor
4701##class for more information about how to use the InsertCursor
4702##instance."""))
4703
4704###############################################################################
4705# Metadata: Field class
4706###############################################################################
4707
4708AddClassMetadata(Field,
4709    shortDescription=_(u'Represents a field of a tabular dataset.'),
4710    longDescription=_(
4711u"""The Field class is not intended to be instantiated explicitly by
4712external callers. To obtain an instance, call GetFieldByName or
4713examine the Fields list of a Table instance."""))
4714
4715# Public properties
4716
4717AddPropertyMetadata(Field.Name,
4718    typeMetadata=UnicodeStringTypeMetadata(),
4719    shortDescription=_(u'Name of the field.'))
4720
4721AddPropertyMetadata(Field.DataType,
4722    typeMetadata=UnicodeStringTypeMetadata(allowedValues=[u'binary', u'date', u'datetime', u'float32', u'float64', u'geometry', u'int16', u'int32', u'oid', u'string']),
4723    shortDescription=_(u'Data type of the field.'))
4724
4725AddPropertyMetadata(Field.Length,
4726    typeMetadata=IntegerTypeMetadata(canBeNone=True),
4727    shortDescription=_(u'Length of the field.'),
4728    longDescription=_(
4729u"""The underlying data format and programming library used to access
4730it determine the exact meaning of this property. It is generally only
4731relevant if the field's data type is 'string' or 'binary', in which
4732case it is the maximum number of characters or bytes that may be
4733stored in the field. The value 0 or a Python None typically indicates
4734that the maximum limit is not known or depends on available disk
4735space."""))
4736
4737AddPropertyMetadata(Field.Precision,
4738    typeMetadata=IntegerTypeMetadata(canBeNone=True),
4739    shortDescription=_(u'Precision of the field.'),
4740    longDescription=_(
4741u"""The underlying data format and programming library used to access
4742it determine the exact meaning of this property. It is generally only
4743relevant if the field's data type is 'float32' or 'float64', in which
4744case it is typically either the maximum number of digits that can be
4745stored in the field or the maximum number of decimal places. """))
4746
4747AddPropertyMetadata(Field.IsNullable,
4748    typeMetadata=BooleanTypeMetadata(),
4749    shortDescription=_(u'True if the field may be set to a database null value, or False if it may only be set to a non-null value.'),
4750    longDescription=_(
4751u"""Some underlying programming libraries, such as OGR, do not provide
4752a mechanism for determining whether a field is nullable. In these
4753cases, True is always returned, and it will be up to the underlying
4754library to succeed or fail when a field is actually set to null."""))
4755
4756AddPropertyMetadata(Field.IsSettable,
4757    typeMetadata=BooleanTypeMetadata(),
4758    shortDescription=_(u'True if the field may be set, or False if it is read-only.'),
4759    longDescription=_(
4760u"""Certain fields, such as the Shape_Length and Shape_Area fields of
4761feature classes in ArcGIS geodatabases, are read-only and managed by
4762the underlying data format or programming library used to access
4763it."""))
4764
4765###############################################################################
4766# Metadata: _Cursor class
4767###############################################################################
4768
4769AddClassMetadata(_Cursor,
4770    shortDescription=_(u'Base class for other cursor classes.'),
4771    longDescription=_(
4772u"""This base class provides functionality common to the other cursor
4773classes. It is mainly intended to be a container for common methods
4774and is not intended to be instantiated explicitly by external callers.
4775To obtain an instance, call Table.OpenSelectCursor,
4776Table.OpenUpdateCursor, or Table.OpenInsertCursor."""))
4777
4778# Public properties
4779
4780AddPropertyMetadata(_Cursor.Table,
4781    typeMetadata=ClassInstanceTypeMetadata(cls=Table),
4782    shortDescription=_(u'Table to which this cursor is opened.'))
4783
4784# Public method: _Cursor.SetRowCount
4785
4786AddMethodMetadata(_Cursor.SetRowCount,
4787    shortDescription=_(u'Sets the number of rows that this cursor is expected to process.'),
4788    longDescription=_(
4789u"""The row count is only used in progress reporting and is ignored if
4790the reportProgress parameter was False when the cursor was opened. If
4791a row count has been provided, the progress reports will include the
4792number of rows remaining and an estimated time of completion. If a row
4793count has not been provided, the progress reports will only include
4794the number of rows processed so far.
4795
4796Typically, if the row count is known ahead of time, you should provide
4797it to the method used to open the cursor. Use SetRowCount when you
4798want to revise the row count after opening the cursor. Do not decrease
4799the row count to a value smaller than the number of rows processed so
4800far."""),
4801    isExposedToPythonCallers=True)
4802
4803AddArgumentMetadata(_Cursor.SetRowCount, u'self',
4804    typeMetadata=ClassInstanceTypeMetadata(cls=_Cursor),
4805    description=_(u'%s instance.') % _Cursor.__name__)
4806
4807AddArgumentMetadata(_Cursor.SetRowCount, u'rowCount',
4808    typeMetadata=IntegerTypeMetadata(minValue=1),
4809    description=_(u"""New row count for this cursor."""))
4810
4811###############################################################################
4812# Metadata: SelectCursor class
4813###############################################################################
4814
4815AddClassMetadata(SelectCursor,
4816    shortDescription=_(u'Represents a forward-only cursor used to read rows from a tabular dataset.'),
4817    longDescription=_(
4818u"""The SelectCursor class is not intended to be instantiated
4819explicitly by external callers. To obtain an instance, call
4820Table.OpenSelectCursor.
4821
4822After obtaining a SelectCursor instance, call NextRow to advance the
4823cursor to the first row. If NextRow returns True, use GetValue and
4824GetGeometry to access fields of the row and its geometry. Call NextRow
4825again to advance to the next row. When NextRow returns False, no rows
4826remain. To close the cursor, release all references to the
4827SelectCursor instance.
4828
4829The typical Python code looks like this::
4830
4831    cursor = dataset.OpenSelectCursor(...)
4832    try:
4833        while cursor.NextRow():
4834            value = cursor.GetValue(...)
4835            ...
4836    finally:
4837        del cursor
4838
4839To ensure that the cursor is closed in the event of an error, be sure
4840to use a ``try...finally`` statement, as shown above."""))
4841
4842# Public properties
4843
4844AddPropertyMetadata(SelectCursor.AtEnd,
4845    typeMetadata=BooleanTypeMetadata(),
4846    shortDescription=_(u'True if the last row of this cursor has been processed and none remain.'))
4847
4848# Public method: SelectCursor.NextRow
4849
4850AddMethodMetadata(SelectCursor.NextRow,
4851    shortDescription=_(u'Advances the cursor to the next row.'),
4852    isExposedToPythonCallers=True)
4853
4854AddArgumentMetadata(SelectCursor.NextRow, u'self',
4855    typeMetadata=ClassInstanceTypeMetadata(cls=SelectCursor),
4856    description=_(u'%s instance.') % SelectCursor.__name__)
4857
4858AddResultMetadata(SelectCursor.NextRow, u'rowAvailable',
4859    typeMetadata=BooleanTypeMetadata(),
4860    description=_(
4861u"""True if a row is available. False if no more rows are available.
4862
4863After opening the cursor, you must call NextRow prior to accessing the
4864first row, and call it again prior to accessing each subsequent row.
4865Once NextRow returns False, no more rows are avaiable and row-access
4866functions such as GetValue will fail.
4867
4868If NextRow has not been called yet, or the last time it was called it
4869returned True, the AtEnd property will be False. Once NextRow returns
4870False, AtEnd will be True."""))
4871
4872# Public method: SelectCursor.GetValue
4873
4874AddMethodMetadata(SelectCursor.GetValue,
4875    shortDescription=_(u'Retrieves the value of a field of the current row, given the name of the field.'),
4876    isExposedToPythonCallers=True)
4877
4878CopyArgumentMetadata(SelectCursor.NextRow, u'self', SelectCursor.GetValue, u'self')
4879
4880AddArgumentMetadata(SelectCursor.GetValue, u'field',
4881    typeMetadata=UnicodeStringTypeMetadata(),
4882    description=_(
4883u"""Name of the field to get the value of.
4884
4885If you specified a list of fields to retrieve when you opened the
4886cursor, you will only be able to retrieve the values of those fields.
4887If you did not specify such a list, then you will be able to retrieve
4888all of the fields of the dataset.
4889
4890This method cannot be used to get the geometry of the row, even if the
4891underlying data format stores the geometry in a named field. To get
4892the geometry, use the GetGeometry method."""))
4893
4894AddResultMetadata(SelectCursor.GetValue, u'value',
4895    typeMetadata=AnyObjectTypeMetadata(canBeNone=True),
4896    description=_(
4897u"""Value of the field.
4898
4899If the value of the field is a database null, a Python None will be
4900returned. Otherwise the Python data type of the returned value will
4901depend on the data type of the field:
4902
4903=================  ====================
4904Field Data Type    Returned Python Type
4905=================  ====================
4906binary             str
4907date, datetime     datetime.datetime
4908float32, float64   float
4909int16, int32, oid  int
4910string             unicode
4911=================  ====================
4912
4913For fields with the date data type, the time of the returned
4914``datetime.datetime`` instance will be 00:00:00."""))
4915
4916# Public method: SelectCursor.GetGeometry
4917
4918AddMethodMetadata(SelectCursor.GetGeometry,
4919    shortDescription=_(u'Retrieves the geometry of the current row.'),
4920    longDescription=_(
4921u"""This method will fail if the dataset does not have geometry. To
4922determine if it has geometry, check the GeometryType property of the
4923cursor's Dataset."""),
4924    isExposedToPythonCallers=True)
4925
4926CopyArgumentMetadata(SelectCursor.NextRow, u'self', SelectCursor.GetGeometry, u'self')
4927
4928AddResultMetadata(SelectCursor.GetGeometry, u'geometry',
4929    typeMetadata=AnyObjectTypeMetadata(canBeNone=True),
4930    description=_(
4931u"""Instance of the OGR Geometry class representing the geometry of
4932the row. If the row has "null geometry", a Python None will be
4933returned.
4934
4935OGR utilizes its own memory management scheme that requires explicit
4936management of certain kinds of objects, including Geometry instances.
4937The Geometry instance returned here is owned by you (the caller of
4938GetGeometry). Unless you transfer ownership of the instance to someone
4939else, you are responsible for calling the Destroy method of the
4940instance to explicitly deallocate its internal memory.
4941
4942The Geometry instance is allocated from MGET's internal copy of OGR
4943(the GeoEco.AssimilatedModules.osgeo.ogr module). Although MGET does
4944not alter OGR in any way, the returned instance is not really intended
4945to be used with other copies of OGR. Thus, if you install your own
4946copy of OGR, the Geometry instances returned by this method may not be
4947compatible with your copy, due to how OGR is implemented. To work
4948around this, you could export the Geometry instance returned here to
4949WKT and instantiate a Geometry instance from it using your own copy of
4950OGR. Feel free to contact the MGET development team for help with this
4951problem."""))
4952
4953###############################################################################
4954# Metadata: UpdateCursor class
4955###############################################################################
4956
4957AddClassMetadata(UpdateCursor,
4958    shortDescription=_(u'Represents a forward-only cursor used to update or delete rows in a tabular dataset.'),
4959    longDescription=_(
4960u"""Not all datasets support update cursors. To determine if a dataset
4961supports update cursors, call TestCapability('UpdateCursor') on the
4962dataset. Some datasets that support update cursors only support
4963updating rows, not deleting them, or visa versa. To check this, call
4964TestCapability('UpdateRow') and TestCapability('DeleteRow') on the
4965dataset.
4966
4967The UpdateCursor class is not intended to be instantiated explicitly
4968by external callers. To obtain an instance, call
4969Table.OpenUpdateCursor.
4970
4971After obtaining an UpdateCursor instance, call NextRow to advance the
4972cursor to the first row. If NextRow returns True, use GetValue and
4973GetGeometry to access fields of the row and its geometry. To update
4974the row, use SetValue and SetGeometry followed by UpdateRow. Use
4975DeleteRow to delete the row. Call NextRow again to advance to the next
4976row. When NextRow returns False, no rows remain. To close the cursor,
4977release all references to the UpdateCursor instance.
4978
4979The typical Python code looks like this::
4980
4981    cursor = dataset.OpenUpdateCursor(...)
4982    try:
4983        while cursor.NextRow():
4984            value = cursor.GetValue(...)
4985            ...
4986            if wantToUpdateRow:
4987                cursor.SetValue(...)
4988                ...
4989                cursor.UpdateRow()
4990            elif wantToDeleteRow:
4991                cursor.DeleteRow()
4992    finally:
4993        del cursor
4994
4995To ensure that the cursor is closed in the event of an error, be sure
4996to use a ``try...finally`` statement, as shown above."""))
4997
4998# Public method: UpdateCursor.SetValue
4999
5000AddMethodMetadata(UpdateCursor.SetValue,
5001    shortDescription=_(u'Sets the value of a field of the current row, given the name of the field and its new value.'),
5002    longDescription=_(
5003u"""Changes to the row are not actually submitted through the
5004underlying programming library to the underlying data store until
5005UpdateRow is called. If you call SetValue but then neglect to call
5006UpdateRow before calling NextRow, your changes will be lost."""),
5007    isExposedToPythonCallers=True)
5008
5009AddArgumentMetadata(UpdateCursor.SetValue, u'self',
5010    typeMetadata=ClassInstanceTypeMetadata(cls=UpdateCursor),
5011    description=_(u'%s instance.') % UpdateCursor.__name__)
5012
5013AddArgumentMetadata(UpdateCursor.SetValue, u'field',
5014    typeMetadata=UnicodeStringTypeMetadata(),
5015    description=_(
5016u"""Name of the field to set the value of.
5017
5018If you specified a list of fields to retrieve when you opened the
5019cursor, you will only be able to set the values of those fields. If
5020you did not specify such a list, then you will be able to set all of
5021the fields of the dataset.
5022
5023This method cannot be used to set the geometry of the row, even if the
5024underlying data format stores the geometry in a named field. To set
5025the geometry, use the SetGeometry method.
5026
5027This method cannot be used to set the object ID (OID) field (sometimes
5028called feature ID, or FID). That field is read-only and managed by the
5029underlying data format or programming library used to access it.
5030
5031The underlying data format or programming library may expose other
5032read-only fields. For example, some versions of ArcGIS maintain fields
5033called Shape_Length and Shape_Area in feature classes in ArcGIS
5034geodatabases. These may not be set either. To determine if a field may
5035be set, examine the IsSettable property of the Field instances
5036contained in the Field list of the cursor's Dataset."""))
5037
5038AddArgumentMetadata(UpdateCursor.SetValue, u'value',
5039    typeMetadata=AnyObjectTypeMetadata(canBeNone=True),
5040    description=_(
5041u"""Value of the field.
5042
5043To set the field to a database null, use Python's None. Otherwise, you
5044must provide an instance of the Python type that is appropriate for
5045the data type of the field:
5046
5047=================  =======================
5048Field Data Type    Appropriate Python Type
5049=================  =======================
5050binary             str
5051date, datetime     datetime.datetime
5052float32, float64   float
5053int16, int32       int
5054string             unicode
5055=================  =======================
5056
5057To determine the data type of a field, call GetFieldByName on the
5058Dataset and examine the DataType property. """))
5059
5060# Public method: UpdateCursor.SetGeometry
5061
5062AddMethodMetadata(UpdateCursor.SetGeometry,
5063    shortDescription=_(u'Sets the geometry of the current row.'),
5064    longDescription=_(
5065u"""This method will fail if the dataset does not have geometry. To
5066determine if it has geometry, check the GeometryType property of the
5067cursor's Dataset.
5068
5069Changes to the row are not actually submitted through the underlying
5070programming library to the underlying data store until UpdateRow is
5071called. If you call SetGeometry but then neglect to call UpdateRow
5072before calling NextRow, your changes will be lost."""),
5073    isExposedToPythonCallers=True)
5074
5075CopyArgumentMetadata(UpdateCursor.SetValue, u'self', UpdateCursor.SetGeometry, u'self')
5076
5077AddArgumentMetadata(UpdateCursor.SetGeometry, u'geometry',
5078    typeMetadata=AnyObjectTypeMetadata(),
5079    description=_(
5080u"""Instance of the OGR Geometry class representing the geometry of
5081the row.
5082
5083MGET includes an internal copy of OGR (the
5084GeoEco.AssimilatedModules.osgeo.ogr module) and it is expected that
5085you will allocate the Geometry instance from there. The typical Python
5086code looks like this::
5087
5088    from GeoEco.Dependencies import ImportGDALModule
5089    ogr = ImportGDALModule('osgeo.ogr')
5090    geometry = ogr.Geometry(...)            # Can also use other functions to create Geometry instances
5091    try:
5092        ...
5093        cursor.SetGeometry(geometry)
5094    finally:
5095        geometry.Destroy()
5096
5097OGR utilizes its own memory management scheme that requires explicit
5098management of certain kinds of objects, including Geometry instances.
5099Ownership of the Geometry instance that you provide remains with you
5100(the caller of SetGeometry). Unless you subsequently transfer
5101ownership of the instance to someone else, you are responsible for
5102calling the Destroy method of the instance to explicitly deallocate
5103its internal memory, as shown above. To ensure that the deallocation
5104occurs in the event of an error, be sure to use a ``try...finally``
5105statement, as shown."""))
5106
5107# Public method: UpdateCursor.UpdateRow
5108
5109AddMethodMetadata(UpdateCursor.UpdateRow,
5110    shortDescription=_(u'Submits any changes made to the current row to the underlying data store.'),
5111    longDescription=_(
5112u"""You cannot access a row after calling UpdateRow; GetValue,
5113SetValue, and so on will not work. You cannot delete it. You must call
5114NextRow to advance the cursor to the next row.
5115
5116Certain datasets may implement a transactional updating scheme in
5117which the change will not be committed to the underlying data store
5118until the cursor has been closed. For more information, please see the
5119documentation for the derived class you are using to access the
5120dataset."""),
5121    isExposedToPythonCallers=True)
5122
5123CopyArgumentMetadata(UpdateCursor.SetValue, u'self', UpdateCursor.UpdateRow, u'self')
5124
5125# Public method: UpdateCursor.DeleteRow
5126
5127AddMethodMetadata(UpdateCursor.DeleteRow,
5128    shortDescription=_(u'Deletes the current row.'),
5129    longDescription=_(
5130u"""You cannot access a row after calling DeleteRow; GetValue,
5131SetValue, and so on will not work. You cannot update it. You must call
5132NextRow to advance the cursor to the next row.
5133
5134Certain datasets may implement a transactional updating scheme in
5135which the delete will not be committed to the underlying data store
5136until the cursor has been closed. For more information, please see the
5137documentation for the derived class you are using to access the
5138dataset."""),
5139    isExposedToPythonCallers=True)
5140
5141CopyArgumentMetadata(UpdateCursor.SetValue, u'self', UpdateCursor.DeleteRow, u'self')
5142
5143###############################################################################
5144# Metadata: InsertCursor class
5145###############################################################################
5146
5147AddClassMetadata(InsertCursor,
5148    shortDescription=_(u'Represents a cursor used to insert rows into a tabular dataset.'),
5149    longDescription=_(
5150u"""Not all datasets support insert cursors. To determine if a dataset
5151supports insert cursors, call TestCapability('InsertCursor') on the
5152dataset.
5153
5154The InsertCursor class is not intended to be instantiated explicitly
5155by external callers. To obtain an instance, call
5156Table.OpenInsertCursor.
5157
5158After obtaining an InsertCursor instance, use SetValue and SetGeometry
5159to set the values of the fields and the geometry of a new row. Call
5160InsertRow to submit the new row to the underlying data store. Repeat
5161this pattern until you are finished inserting rows. Then close the
5162cursor by releasing all references to the InsertCursor instance.
5163
5164The typical Python code looks like this::
5165
5166    cursor = dataset.OpenInsertCursor(...)
5167    try:
5168        for ...:                    # Loop over rows, inserting one at a time
5169            cursor.SetValue(...)
5170            ...
5171            cursor.InsertRow()
5172    finally:
5173        del cursor
5174
5175To ensure that the cursor is closed in the event of an error, be sure
5176to use a ``try...finally`` statement, as shown above."""))
5177
5178# Public method: InsertCursor.SetValue
5179
5180AddMethodMetadata(InsertCursor.SetValue,
5181    shortDescription=_(u'Sets the value of a field of the current row, given the name of the field and its new value.'),
5182    longDescription=_(
5183u"""The row is not actually submitted through the underlying
5184programming library to the underlying data store until InsertRow is
5185called. If you call SetValue but then neglect to call InsertRow before
5186closing the cursor, your new row will be lost."""),
5187    isExposedToPythonCallers=True)
5188
5189AddArgumentMetadata(InsertCursor.SetValue, u'self',
5190    typeMetadata=ClassInstanceTypeMetadata(cls=InsertCursor),
5191    description=_(u'%s instance.') % InsertCursor.__name__)
5192
5193AddArgumentMetadata(InsertCursor.SetValue, u'field',
5194    typeMetadata=UnicodeStringTypeMetadata(),
5195    description=_(
5196u"""Name of the field to set the value of.
5197
5198This method cannot be used to set the geometry of the row, even if the
5199underlying data format stores the geometry in a named field. To set
5200the geometry, use the SetGeometry method.
5201
5202This method cannot be used to set the object ID (OID) field (sometimes
5203called feature ID, or FID). That field is read-only and managed by the
5204underlying data format or programming library used to access it.
5205
5206The underlying data format or programming library may expose other
5207read-only fields. For example, some versions of ArcGIS maintain fields
5208called Shape_Length and Shape_Area in feature classes in ArcGIS
5209geodatabases. These may not be set either. To determine if a field may
5210be set, examine the IsSettable property of the Field instances
5211contained in the Field list of the cursor's Dataset."""))
5212
5213CopyArgumentMetadata(UpdateCursor.SetValue, u'value', InsertCursor.SetValue, u'value')
5214
5215# Public method: InsertCursor.SetGeometry
5216
5217AddMethodMetadata(InsertCursor.SetGeometry,
5218    shortDescription=_(u'Sets the geometry of the current row.'),
5219    longDescription=_(
5220u"""This method will fail if the dataset does not have geometry. To
5221determine if it has geometry, check the GeometryType property of the
5222cursor's Dataset.
5223
5224The row is not actually submitted through the underlying programming
5225library to the underlying data store until InsertRow is called. If you
5226call SetGeometry but then neglect to call InsertRow before closing the
5227cursor, your new row will be lost."""),
5228    isExposedToPythonCallers=True)
5229
5230CopyArgumentMetadata(InsertCursor.SetValue, u'self', InsertCursor.SetGeometry, u'self')
5231CopyArgumentMetadata(UpdateCursor.SetGeometry, u'geometry', InsertCursor.SetGeometry, u'geometry')
5232
5233# Public method: InsertCursor.InsertRow
5234
5235AddMethodMetadata(InsertCursor.InsertRow,
5236    shortDescription=_(u'Submits the new row to the underlying data store.'),
5237    longDescription=_(
5238u"""If you do not explicitly set values of all of the row's fields by
5239calling SetValue prior to calling InsertRow, InsertRow will set those
5240fields to database null (if they are not read-only). If a field that
5241has not been set is not nullable, InsertRow will report an error. To
5242determine if a field is nullable, call GetFieldByName on the Datset
5243and examine the IsNullable property.
5244
5245Certain datasets may implement a transactional updating scheme in
5246which the new row will not be committed to the underlying data store
5247until the cursor has been closed. For more information, please see the
5248documentation for the derived class you are using to access the
5249dataset."""),
5250    isExposedToPythonCallers=True)
5251
5252CopyArgumentMetadata(InsertCursor.SetValue, u'self', InsertCursor.InsertRow, u'self')
5253
5254###############################################################################
5255# Metadata: Grid class
5256###############################################################################
5257
5258AddClassMetadata(Grid,
5259    shortDescription=_(u'Represents a 2D, 3D, or 4D gridded dataset, such as a Scientific Data Set from an HDF file or Grid variable from an OPeNDAP URL.')),
5260
5261# Private constructor: Grid.__init__
5262
5263AddMethodMetadata(Grid.__init__,
5264    shortDescription=_(u'Grid constructor. Not intended to be called directly. Only intended to be called from derived class constructors.'),
5265    dependencies=[PythonAggregatedModuleDependency('numpy')])
5266
5267AddArgumentMetadata(Grid.__init__, u'self',
5268    typeMetadata=ClassInstanceTypeMetadata(cls=Grid),
5269    description=_(u'%s instance.') % Grid.__name__)
5270
5271AddArgumentMetadata(Grid.__init__, u'parentCollection',
5272    typeMetadata=CollectibleObject.__init__.__doc__.Obj.GetArgumentByName(u'parentCollection').Type,
5273    description=CollectibleObject.__init__.__doc__.Obj.GetArgumentByName(u'parentCollection').Description)
5274
5275AddArgumentMetadata(Grid.__init__, u'queryableAttributes',
5276    typeMetadata=CollectibleObject.__init__.__doc__.Obj.GetArgumentByName(u'queryableAttributes').Type,
5277    description=CollectibleObject.__init__.__doc__.Obj.GetArgumentByName(u'queryableAttributes').Description)
5278
5279AddArgumentMetadata(Grid.__init__, u'queryableAttributeValues',
5280    typeMetadata=CollectibleObject.__init__.__doc__.Obj.GetArgumentByName(u'queryableAttributeValues').Type,
5281    description=CollectibleObject.__init__.__doc__.Obj.GetArgumentByName(u'queryableAttributeValues').Description)
5282
5283AddArgumentMetadata(Grid.__init__, u'lazyPropertyValues',
5284    typeMetadata=CollectibleObject.__init__.__doc__.Obj.GetArgumentByName(u'lazyPropertyValues').Type,
5285    description=CollectibleObject.__init__.__doc__.Obj.GetArgumentByName(u'lazyPropertyValues').Description)
5286
5287AddResultMetadata(Grid.__init__, u'grid',
5288    typeMetadata=ClassInstanceTypeMetadata(cls=Grid),
5289    description=_(u'%s instance.') % Grid.__name__)
5290
5291# TODO: Add metadata
5292
5293###############################################################################
5294# Metadata: NumpyGrid class
5295###############################################################################
5296
5297AddClassMetadata(NumpyGrid,
5298    shortDescription=_(u'Wraps a numpy array in the Grid interface.'),
5299    longDescription=_(
5300u"""NumpyGrid provides a convenient mechanism for modifying Grids. The
5301typical usage pattern is:
5302
53031. Obtain a Grid instance from somewhere (e.g. an ArcGISRasterBand
5304   instance representing a band of an ArcGIS raster).
5305
53062. Call NumpyGrid.CreateFromGrid to wrap the Grid in a NumpyGrid.
5307
53083. Get and set slices of the Data property of the NumpyGrid.
5309
53104. Import the NumpyGrid into the desired output DatasetCollection.
5311"""))
5312
5313# Constructor: NumpyGrid.__init__
5314
5315AddMethodMetadata(NumpyGrid.__init__,
5316    shortDescription=_(u'NumpyGrid constructor.'),
5317    dependencies=[PythonAggregatedModuleDependency('numpy')])
5318
5319AddArgumentMetadata(NumpyGrid.__init__, u'self',
5320    typeMetadata=ClassInstanceTypeMetadata(cls=NumpyGrid),
5321    description=_(u'%s instance.') % NumpyGrid.__name__)
5322
5323AddArgumentMetadata(NumpyGrid.__init__, u'numpyArray',
5324    typeMetadata=NumPyArrayTypeMetadata(allowedDTypes=[u'int8', u'uint8', u'int16', u'uint16', u'int32', u'uint32', u'float32', u'float64']),
5325    description=_(u'TODO: Add description'))
5326
5327AddArgumentMetadata(NumpyGrid.__init__, u'displayName',
5328    typeMetadata=UnicodeStringTypeMetadata(),
5329    description=_(u'TODO: Add description'))
5330
5331AddArgumentMetadata(NumpyGrid.__init__, u'spatialReference',
5332    typeMetadata=AnyObjectTypeMetadata(),
5333    description=_(u'TODO: Add description'))
5334
5335AddArgumentMetadata(NumpyGrid.__init__, u'dimensions',
5336    typeMetadata=UnicodeStringTypeMetadata(allowedValues=[u'yx', u'zyx', u'tyx', u'tzyx']),
5337    description=_(u'TODO: Add description'))
5338
5339AddArgumentMetadata(NumpyGrid.__init__, u'coordIncrements',
5340    typeMetadata=TupleTypeMetadata(elementType=FloatTypeMetadata(mustBeGreaterThan=0., canBeNone=True), mustBeSameLengthAsArgument=u'dimensions'),
5341    description=_(u'TODO: Add description'))
5342
5343AddArgumentMetadata(NumpyGrid.__init__, u'cornerCoords',
5344    typeMetadata=TupleTypeMetadata(elementType=FloatTypeMetadata(), mustBeSameLengthAsArgument=u'dimensions'),
5345    description=_(u'TODO: Add description'))
5346
5347AddArgumentMetadata(NumpyGrid.__init__, u'unscaledNoDataValue',
5348    typeMetadata=AnyObjectTypeMetadata(canBeNone=True),
5349    description=_(u'TODO: Add description'))
5350
5351AddArgumentMetadata(NumpyGrid.__init__, u'tIncrementUnit',
5352    typeMetadata=UnicodeStringTypeMetadata(allowedValues=[u'second', u'minute', u'hour', u'day', u'month', u'year'], canBeNone=True),
5353    description=_(u'TODO: Add description'))
5354
5355AddArgumentMetadata(NumpyGrid.__init__, u'tSemiRegularity',
5356    typeMetadata=UnicodeStringTypeMetadata(allowedValues=[u'annual'], canBeNone=True),
5357    description=_(u'TODO: Add description'))
5358
5359AddArgumentMetadata(NumpyGrid.__init__, u'tCountPerSemiRegularPeriod',
5360    typeMetadata=IntegerTypeMetadata(mustBeGreaterThan=0, canBeNone=True),
5361    description=_(u'TODO: Add description'))
5362
5363AddArgumentMetadata(NumpyGrid.__init__, u'tCornerCoordType',
5364    typeMetadata=UnicodeStringTypeMetadata(allowedValues=[u'min', u'center', u'max'], canBeNone=True),
5365    description=_(u'TODO: Add description'))
5366
5367AddArgumentMetadata(NumpyGrid.__init__, u'tOffsetFromParsedTime',
5368    typeMetadata=FloatTypeMetadata(canBeNone=True),
5369    description=_(u'TODO: Add description'))
5370
5371AddArgumentMetadata(NumpyGrid.__init__, u'coordDependencies',
5372    typeMetadata=TupleTypeMetadata(elementType=UnicodeStringTypeMetadata(allowedValues=[u'x', u'y', u'z', u't', u'yx', u'zx', u'tx', u'ty', u'zy', u'tz', u'zyx', u'tyx', u'tzy'], canBeNone=True), mustBeSameLengthAsArgument=u'dimensions', canBeNone=True),
5373    description=_(u'TODO: Add description'))
5374
5375AddArgumentMetadata(NumpyGrid.__init__, u'physicalDimensions',
5376    typeMetadata=UnicodeStringTypeMetadata(allowedValues=[u'yx', u'xy', u'zyx', u'zxy', u'yzx', u'yxz', u'xzy', u'xyz', u'tyx', u'txy', u'ytx', u'yxt', u'xty', u'xyt', u'tzyx', u'tzxy', u'tyzx', u'tyxz', u'txzy', u'txyz', u'ztyx', u'ztxy', u'zytx', u'zyxt', u'zxty', u'zxyt', u'ytzx', u'ytxz', u'yztx', u'yzxt', u'yxtz', u'yxzt', u'xtzy', u'xtyz', u'xzty', u'xzyt', u'xytz', u'xyzt'], canBeNone=True),
5377    description=_(u'TODO: Add description'))
5378
5379AddArgumentMetadata(NumpyGrid.__init__, u'physicalDimensionsFlipped',
5380    typeMetadata=TupleTypeMetadata(elementType=BooleanTypeMetadata(), mustBeSameLengthAsArgument=u'dimensions', canBeNone=True),
5381    description=_(u'TODO: Add description'))
5382
5383AddArgumentMetadata(NumpyGrid.__init__, u'scaledDataType',
5384    typeMetadata=UnicodeStringTypeMetadata(allowedValues=[u'int8', u'uint8', u'int16', u'uint16', u'int32', u'uint32', u'float32', u'float64'], canBeNone=True),
5385    description=_(u'TODO: Add description'))
5386
5387AddArgumentMetadata(NumpyGrid.__init__, u'scaledNoDataValue',
5388    typeMetadata=AnyObjectTypeMetadata(canBeNone=True),
5389    description=_(u'TODO: Add description'))
5390
5391AddArgumentMetadata(NumpyGrid.__init__, u'scalingFunction',
5392    typeMetadata=AnyObjectTypeMetadata(canBeNone=True),
5393    description=_(u'TODO: Add description'))
5394
5395AddArgumentMetadata(NumpyGrid.__init__, u'unscalingFunction',
5396    typeMetadata=AnyObjectTypeMetadata(canBeNone=True),
5397    description=_(u'TODO: Add description'))
5398
5399CopyArgumentMetadata(Grid.__init__, u'parentCollection', NumpyGrid.__init__, u'parentCollection')
5400CopyArgumentMetadata(Grid.__init__, u'queryableAttributes', NumpyGrid.__init__, u'queryableAttributes')
5401CopyArgumentMetadata(Grid.__init__, u'queryableAttributeValues', NumpyGrid.__init__, u'queryableAttributeValues')
5402CopyArgumentMetadata(Grid.__init__, u'lazyPropertyValues', NumpyGrid.__init__, u'lazyPropertyValues')
5403
5404AddResultMetadata(NumpyGrid.__init__, u'numpyGrid',
5405    typeMetadata=ClassInstanceTypeMetadata(cls=NumpyGrid),
5406    description=_(u'%s instance.') % NumpyGrid.__name__)
5407
5408# Public method: NumpyGrid.CreateFromGrid
5409
5410AddMethodMetadata(NumpyGrid.CreateFromGrid,
5411    shortDescription=_(u'Reads an entire Grid into a numpy array and wraps it in a NumpyGrid instance.'),
5412    dependencies=[PythonAggregatedModuleDependency('numpy')])
5413
5414AddArgumentMetadata(NumpyGrid.CreateFromGrid, u'cls',
5415    typeMetadata=ClassOrClassInstanceTypeMetadata(cls=NumpyGrid),
5416    description=_(u'%s class or an instance of it.') % NumpyGrid.__name__)
5417
5418AddArgumentMetadata(NumpyGrid.CreateFromGrid, u'grid',
5419    typeMetadata=ClassInstanceTypeMetadata(cls=Grid),
5420    description=_(
5421u"""Grid instance to read."""))
5422
5423AddResultMetadata(NumpyGrid.CreateFromGrid, u'numpyGrid',
5424    typeMetadata=ClassInstanceTypeMetadata(cls=NumpyGrid),
5425    description=_(u'%s instance.') % NumpyGrid.__name__)
5426
5427
5428###############################################################################
5429# Names exported by this module
5430###############################################################################
5431
5432__all__ = ['Dataset', 'Table', 'Field', 'SelectCursor', 'UpdateCursor', 'InsertCursor', 'Grid', 'NumpyGrid']
Note: See TracBrowser for help on using the browser.