| 1 | # Datasets/ArcGIS.py - Classes that define the interfaces used to
|
|---|
| 2 | # access tabular and raster datasets using ArcGIS APIs.
|
|---|
| 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 |
|
|---|
| 21 | import os
|
|---|
| 22 | import types
|
|---|
| 23 |
|
|---|
| 24 | from GeoEco.ArcGIS import GeoprocessorManager
|
|---|
| 25 | from GeoEco.Datasets import CollectibleObject, Dataset, DatasetCollection, QueryableAttribute, Grid, Database, Table, Field, SelectCursor, UpdateCursor, InsertCursor
|
|---|
| 26 | from GeoEco.Datasets.Collections import DatasetCollectionTree
|
|---|
| 27 | from GeoEco.Datasets.GDAL import GDALDataset, GDALRasterBand
|
|---|
| 28 | from GeoEco.DynamicDocString import DynamicDocString
|
|---|
| 29 | from GeoEco.Internationalization import _
|
|---|
| 30 |
|
|---|
| 31 |
|
|---|
| 32 | class ArcGISTabularLayer(Table):
|
|---|
| 33 | __doc__ = DynamicDocString()
|
|---|
| 34 |
|
|---|
| 35 | # Public classmethods for opening, creating, and deleting datasets
|
|---|
| 36 |
|
|---|
| 37 | @classmethod
|
|---|
| 38 | def Open(cls, name):
|
|---|
| 39 | cls.__doc__.Obj.ValidateMethodInvocation()
|
|---|
| 40 | return ArcGISTabularLayer(name)
|
|---|
| 41 |
|
|---|
| 42 | @classmethod
|
|---|
| 43 | def Create(cls, workspace, name, geometryType=None, srType=None, sr=None, configKeyword=None, spatialGrid1=None, spatialGrid2=None, spatialGrid3=None, autoDeleteFieldAddedByArcGIS=True):
|
|---|
| 44 | cls.__doc__.Obj.ValidateMethodInvocation()
|
|---|
| 45 |
|
|---|
| 46 | # If geometryType is None, create a table.
|
|---|
| 47 |
|
|---|
| 48 | if isinstance(cls, ArcGISTabularLayer):
|
|---|
| 49 | className = cls.__class__.__name__
|
|---|
| 50 | else:
|
|---|
| 51 | className = cls.__name__
|
|---|
| 52 |
|
|---|
| 53 | gp = GeoprocessorManager.GetWrappedGeoprocessor()
|
|---|
| 54 | if geometryType is None:
|
|---|
| 55 | cls._LogDebug(_(u'%(class)s: Creating table "%(name)s" in workspace "%(ws)s" with configKeyword=%(kw)s.'), {u'class': className, u'name': name, u'ws': workspace, u'kw': unicode(configKeyword)})
|
|---|
| 56 | try:
|
|---|
| 57 | createdName = gp.CreateTable_management(workspace, name, None, configKeyword)
|
|---|
| 58 | except Exception, e:
|
|---|
| 59 | raise RuntimeError(_(u'Failed to create table "%(name)s" in ArcGIS workspace "%(ws)s" due to %(e)s: %(msg)s') % {u'name': name, u'ws': workspace, u'e': e.__class__.__name__, u'msg': unicode(e)})
|
|---|
| 60 |
|
|---|
| 61 | # Otherwise create a feature class.
|
|---|
| 62 |
|
|---|
| 63 | else:
|
|---|
| 64 | if geometryType == 'point':
|
|---|
| 65 | shapeType = 'POINT'
|
|---|
| 66 | hasZ = 'DISABLED'
|
|---|
| 67 | elif geometryType == 'point25d':
|
|---|
| 68 | shapeType = 'POINT'
|
|---|
| 69 | hasZ = 'ENABLED'
|
|---|
| 70 | elif geometryType == 'multipoint':
|
|---|
| 71 | shapeType = 'MULTIPOINT'
|
|---|
| 72 | hasZ = 'DISABLED'
|
|---|
| 73 | elif geometryType == 'multipoint25d':
|
|---|
| 74 | shapeType = 'MULTIPOINT'
|
|---|
| 75 | hasZ = 'ENABLED'
|
|---|
| 76 | elif geometryType in ['linestring', 'multilinestring']:
|
|---|
| 77 | shapeType = 'POLYLINE'
|
|---|
| 78 | hasZ = 'DISABLED'
|
|---|
| 79 | elif geometryType in ['linestring25d', 'multilinestring25d']:
|
|---|
| 80 | shapeType = 'POLYLINE'
|
|---|
| 81 | hasZ = 'ENABLED'
|
|---|
| 82 | elif geometryType in ['polygon', 'multipolygon']:
|
|---|
| 83 | shapeType = 'POLYGON'
|
|---|
| 84 | hasZ = 'DISABLED'
|
|---|
| 85 | elif geometryType in ['polygon25d', 'multipolygon25d']:
|
|---|
| 86 | shapeType = 'POLYGON'
|
|---|
| 87 | hasZ = 'ENABLED'
|
|---|
| 88 | else:
|
|---|
| 89 | raise ValueError(_(u'Cannot create feature class "%(name)s" in ArcGIS workspace "%(ws)s" with the geometry type %(gt)s. ArcGIS does not support that geometry type.') % {u'name': name, u'workspace': workspace, u'gt': geometryType})
|
|---|
| 90 |
|
|---|
| 91 | sr = cls.ConvertSpatialReference(srType, sr, 'arcgis')
|
|---|
| 92 |
|
|---|
| 93 | cls._LogDebug(_(u'%(class)s: Creating feature class "%(name)s" in workspace "%(ws)s" with shapeType=%(st)s, hasZ=%(hasZ)s, spatialReference=%(sr)s, configKeyword=%(kw)s, spatialGrid1=%(sg1)s, spatialGrid2=%(sg1)s, spatialGrid3=%(sg1)s.'),
|
|---|
| 94 | {u'class': className, u'name': name, u'ws': workspace, u'st': shapeType, u'hasZ': hasZ, u'sr': unicode(sr), u'kw': unicode(configKeyword), u'sg1': unicode(spatialGrid1), u'sg2': unicode(spatialGrid2), u'sg3': unicode(spatialGrid3)})
|
|---|
| 95 |
|
|---|
| 96 | try:
|
|---|
| 97 | createdName = gp.CreateFeatureClass_management(workspace, name, shapeType, None, 'DISABLED', hasZ, sr, configKeyword, spatialGrid1, spatialGrid2, spatialGrid3)
|
|---|
| 98 | except Exception, e:
|
|---|
| 99 | raise RuntimeError(_(u'Failed to create feature class "%(name)s" in ArcGIS workspace "%(ws)s" due to %(e)s: %(msg)s') % {u'name': name, u'ws': workspace, u'e': e.__class__.__name__, u'msg': unicode(e)})
|
|---|
| 100 |
|
|---|
| 101 | # Instantiate an ArcGISTabularLayer for the newly-created
|
|---|
| 102 | # table or feature class.
|
|---|
| 103 |
|
|---|
| 104 | return ArcGISTabularLayer(createdName, autoDeleteFieldAddedByArcGIS)
|
|---|
| 105 |
|
|---|
| 106 | @classmethod
|
|---|
| 107 | def Delete(cls, name, dataType=None, failIfDoesNotExist=False):
|
|---|
| 108 | cls.__doc__.Obj.ValidateMethodInvocation()
|
|---|
| 109 |
|
|---|
| 110 | # Validate that the object exists and that it is a table-like
|
|---|
| 111 | # object (i.e. it has fields).
|
|---|
| 112 |
|
|---|
| 113 | if isinstance(cls, ArcGISTabularLayer):
|
|---|
| 114 | className = cls.__class__.__name__
|
|---|
| 115 | else:
|
|---|
| 116 | className = cls.__name__
|
|---|
| 117 |
|
|---|
| 118 | gp = GeoprocessorManager.GetWrappedGeoprocessor()
|
|---|
| 119 | if not gp.Exists(name):
|
|---|
| 120 | if not failIfDoesNotExist:
|
|---|
| 121 | cls._LogDebug(_(u'%(class)s: Not deleting "%(name)s" because ArcGIS reports that it does not exist.'), {u'class': className, u'name': name})
|
|---|
| 122 | return
|
|---|
| 123 | raise ValueError(_(u'Cannot delete "%(name)s" because ArcGIS reports that it does not exist.') % {u'class': className, u'name': name})
|
|---|
| 124 |
|
|---|
| 125 | d = gp.Describe(name)
|
|---|
| 126 | if not hasattr(d, 'Fields') or d.Fields is None or d.Fields.Next() is None:
|
|---|
| 127 | raise ValueError(_(u'Cannot delete "%(name)s" because it is not an ArcGIS tabular dataset. ArcGIS reports that this object does not have any fields.') % {u'name': name})
|
|---|
| 128 |
|
|---|
| 129 | # If it is table view, ArcGIS versions 9.2 through 9.3.1
|
|---|
| 130 | # cannot delete it (I am not sure about later versions). In
|
|---|
| 131 | # that case, just report a warning and return. For more info,
|
|---|
| 132 | # see http://forums.esri.com/Thread.asp?c=93&f=1729&t=224956.
|
|---|
| 133 |
|
|---|
| 134 | if d.DataType.lower() == 'tableview' and GeoprocessorManager.GetArcGISMajorVersion() == 9 and GeoprocessorManager.GetArcGISMinorVersion() in [2, 3]:
|
|---|
| 135 | self._LogWarning(_(u'Cannot delete ArcGIS TableView "%(name)s" because this version of ArcGIS does not allow table views to be deleted. For more information, please see http://forums.esri.com/Thread.asp?c=93&f=1729&t=224956.'), {u'name': name})
|
|---|
| 136 | return
|
|---|
| 137 |
|
|---|
| 138 | # Delete the object.
|
|---|
| 139 |
|
|---|
| 140 | if dataType is not None:
|
|---|
| 141 | cls._LogDebug(_(u'%(class)s: Deleting %(dt)s "%(name)s".'), {u'class': className, u'dt': dataType, u'name': name})
|
|---|
| 142 | else:
|
|---|
| 143 | cls._LogDebug(_(u'%(class)s: Deleting "%(name)s".'), {u'class': className, u'name': name})
|
|---|
| 144 |
|
|---|
| 145 | try:
|
|---|
| 146 | gp.Delete_management(name, dataType)
|
|---|
| 147 | except Exception, e:
|
|---|
| 148 | raise RuntimeError(_(u'Failed to delete "%(name)s" due to %(e)s: %(msg)s') % {u'name': name, u'e': e.__class__.__name__, u'msg': unicode(e)})
|
|---|
| 149 |
|
|---|
| 150 | # Public properties and instance methods
|
|---|
| 151 |
|
|---|
| 152 | def _GetName(self):
|
|---|
| 153 | return self._Name
|
|---|
| 154 |
|
|---|
| 155 | Name = property(_GetName, doc=DynamicDocString())
|
|---|
| 156 |
|
|---|
| 157 | def _GetDataType(self):
|
|---|
| 158 | return self._DataType
|
|---|
| 159 |
|
|---|
| 160 | DataType = property(_GetDataType, doc=DynamicDocString())
|
|---|
| 161 |
|
|---|
| 162 | def _GetPhysicalDataType(self):
|
|---|
| 163 | return self._PhysicalDataType
|
|---|
| 164 |
|
|---|
| 165 | PhysicalDataType = property(_GetPhysicalDataType, doc=DynamicDocString())
|
|---|
| 166 |
|
|---|
| 167 | def _GetAutoDeleteFieldAddedByArcGIS(self):
|
|---|
| 168 | return self._AutoDeleteFieldAddedByArcGIS
|
|---|
| 169 |
|
|---|
| 170 | AutoDeleteFieldAddedByArcGIS = property(_GetAutoDeleteFieldAddedByArcGIS, doc=DynamicDocString())
|
|---|
| 171 |
|
|---|
| 172 | # Overridden private base class instance methods
|
|---|
| 173 |
|
|---|
| 174 | @classmethod
|
|---|
| 175 | def _TestCapability(cls, capability):
|
|---|
| 176 | if capability in ['setspatialreference', 'addfield', 'deletefield', 'selectcursor', 'updatecursor', 'insertcursor', 'updaterow', 'deleterow']:
|
|---|
| 177 | return None
|
|---|
| 178 |
|
|---|
| 179 | if capability in ['int16 datatype', 'int32 datatype', 'float32 datatype', 'float64 datatype', 'string datatype']:
|
|---|
| 180 | return None
|
|---|
| 181 |
|
|---|
| 182 | if capability == 'date datatype':
|
|---|
| 183 | if isinstance(cls, ArcGISTabularLayer) and cls._PhysicalDataType.lower() not in ['shapefile', 'dbftable']:
|
|---|
| 184 | return TypeError(_('Cannot create a field of data type "date" in %(dn)s because that data type is not supported by ArcGIS for the underlying database or file format. Try the data type "datetime" instead.') % {u'dn': cls.DisplayName})
|
|---|
| 185 | return None
|
|---|
| 186 |
|
|---|
| 187 | if capability == 'datetime datatype':
|
|---|
| 188 | if isinstance(cls, ArcGISTabularLayer) and cls._PhysicalDataType.lower() in ['shapefile', 'dbftable']:
|
|---|
| 189 | return TypeError(_('Cannot create a field of data type "datetime" in %(dn)s because that data type is not supported by DBF files. DBF files can only store dates, not dates with times. Try the data type "date" instead.') % {u'dn': cls.DisplayName})
|
|---|
| 190 | return None
|
|---|
| 191 |
|
|---|
| 192 | if capability == 'binary datatype':
|
|---|
| 193 | if isinstance(cls, ArcGISTabularLayer) and cls._PhysicalDataType.lower() in ['shapefile', 'dbftable']:
|
|---|
| 194 | return TypeError(_('Cannot create a field of data type "binary" in %(dn)s because that data type is not supported by DBF files.') % {u'dn': cls.DisplayName})
|
|---|
| 195 | return None
|
|---|
| 196 |
|
|---|
| 197 | if capability.endswith(' datatype'):
|
|---|
| 198 | if isinstance(cls, ArcGISTabularLayer):
|
|---|
| 199 | return TypeError(_('Cannot create a field of data type "%(dt)s" in %(dn)s because that data type is not supported.') % {u'dt': capability.split()[0], u'dn': cls.DisplayName})
|
|---|
| 200 | else:
|
|---|
| 201 | return TypeError(_('Cannot create a field of data type "%(dt)s" because that data type is not supported by ArcGIS.') % {u'dt': capability.split()[0]})
|
|---|
| 202 |
|
|---|
| 203 | if capability.endswith(' isnullable'):
|
|---|
| 204 | if isinstance(cls, ArcGISTabularLayer) and cls._PhysicalDataType.lower() in ['shapefile', 'dbftable'] and not capability.startswith('date '):
|
|---|
| 205 | return TypeError(_('Cannot create a nullable field in %(dn)s because shapefiles and DBF files do not support null values, except in date fields.') % {u'dn': cls.DisplayName})
|
|---|
| 206 | return None
|
|---|
| 207 |
|
|---|
| 208 | if isinstance(cls, ArcGISTabularLayer):
|
|---|
| 209 | return RuntimeError(_(u'The %(cls)s class does not support the "%(cap)s" capability.') % {u'cls': cls.__class__.__name__, u'cap': capability})
|
|---|
| 210 | return RuntimeError(_(u'The %(cls)s class does not support the "%(cap)s" capability.') % {u'cls': cls.__name__, u'cap': capability})
|
|---|
| 211 |
|
|---|
| 212 | @classmethod
|
|---|
| 213 | def _GetSRTypeForSetting(cls):
|
|---|
| 214 | return 'ArcGIS'
|
|---|
| 215 |
|
|---|
| 216 | def _SetSpatialReference(self, sr):
|
|---|
| 217 | GeoprocessorManager.GetWrappedGeoprocessor().DefineProjection_management(self._Name, sr)
|
|---|
| 218 |
|
|---|
| 219 | def _AddField(self, name, dataType, length, precision, isNullable):
|
|---|
| 220 | if dataType == 'int16':
|
|---|
| 221 | dataType = 'SHORT'
|
|---|
| 222 | elif dataType == 'int32':
|
|---|
| 223 | dataType = 'LONG'
|
|---|
| 224 | elif dataType == 'float32':
|
|---|
| 225 | dataType = 'FLOAT'
|
|---|
| 226 | elif dataType == 'float64':
|
|---|
| 227 | dataType = 'DOUBLE'
|
|---|
| 228 | elif dataType == 'string':
|
|---|
| 229 | dataType = 'TEXT'
|
|---|
| 230 | elif dataType == 'date' or dataType == 'datetime':
|
|---|
| 231 | dataType = 'DATE'
|
|---|
| 232 | elif dataType == 'binary':
|
|---|
| 233 | dataType = 'BLOB'
|
|---|
| 234 | else:
|
|---|
| 235 | raise NotImplementedError(_(u'The _AddField method of class %(cls)s does not support the %(dt)s data type.') % {u'cls': self.__class__.__name__, u'dt': dataType})
|
|---|
| 236 |
|
|---|
| 237 | gp = GeoprocessorManager.GetWrappedGeoprocessor()
|
|---|
| 238 | newName = gp.ValidateFieldName(name, self._Name)
|
|---|
| 239 | if newName != name:
|
|---|
| 240 | raise ValueError(_(u'The name "%(name)s" is prohibited by either ArcGIS or the underlying database or file format. ArcGIS suggested the name "%(newName)s" instead.') % {u'name': name, u'newName': newName})
|
|---|
| 241 |
|
|---|
| 242 | gp.AddField_management(self._Name, name, dataType)
|
|---|
| 243 |
|
|---|
| 244 | if self.AutoDeleteFieldAddedByArcGIS and not self._DeletedFieldAddedByArcGIS:
|
|---|
| 245 | if self.DataType.lower() == 'shapefile' and self.GetFieldByName('Id') is not None:
|
|---|
| 246 | self.DeleteField('Id')
|
|---|
| 247 | if self.DataType.lower() == 'dbasetable' and self.GetFieldByName('Field1') is not None:
|
|---|
| 248 | self.DeleteField('Field1')
|
|---|
| 249 | self._DeletedFieldAddedByArcGIS = True
|
|---|
| 250 |
|
|---|
| 251 | def _DeleteField(self, name):
|
|---|
| 252 | GeoprocessorManager.GetWrappedGeoprocessor().DeleteField_management(self._Name, name)
|
|---|
| 253 |
|
|---|
| 254 | def _GetRowCount(self):
|
|---|
| 255 | return GeoprocessorManager.GetWrappedGeoprocessor().GetCount_management(self._Name)
|
|---|
| 256 |
|
|---|
| 257 | # Other private properties and instance methods
|
|---|
| 258 |
|
|---|
| 259 | def __init__(self, name, autoDeleteFieldAddedByArcGIS=False):
|
|---|
| 260 |
|
|---|
| 261 | # Validate that the dataset exists. Obtain a geoprocessor
|
|---|
| 262 | # Describe object and validate that the dataset has fields.
|
|---|
| 263 |
|
|---|
| 264 | gp = GeoprocessorManager.GetWrappedGeoprocessor()
|
|---|
| 265 | if not gp.Exists(name):
|
|---|
| 266 | raise ValueError(_(u'Failed to open ArcGIS dataset "%(name)s". ArcGIS reports that the dataset does not exist.') % {u'name': name})
|
|---|
| 267 |
|
|---|
| 268 | d = gp.Describe(name)
|
|---|
| 269 | if not hasattr(d, 'Fields') or d.Fields is None or d.Fields.Next() is None:
|
|---|
| 270 | raise ValueError(_(u'Failed to open "%(name)s" as an ArcGIS tabular dataset. ArcGIS reports that this object does not have any fields.') % {u'name': name})
|
|---|
| 271 |
|
|---|
| 272 | # Initialize properties.
|
|---|
| 273 |
|
|---|
| 274 | self._Name = name
|
|---|
| 275 | self._DataType = d.DataType
|
|---|
| 276 | self._PhysicalDataType = gp.Describe(d.CatalogPath).DataType
|
|---|
| 277 | self._AutoDeleteFieldAddedByArcGIS = autoDeleteFieldAddedByArcGIS and d.DataType.lower() in ['shapefile', 'dbasetable']
|
|---|
| 278 | self._DeletedFieldAddedByArcGIS = False
|
|---|
| 279 |
|
|---|
| 280 | # Initialize the base class from parameters of the Describe
|
|---|
| 281 | # object.
|
|---|
| 282 |
|
|---|
| 283 | if self._DataType == self._PhysicalDataType:
|
|---|
| 284 | displayName = _(u'ArcGIS %(dt)s "%(name)s"') % {u'dt': self._DataType, u'name': name}
|
|---|
| 285 | else:
|
|---|
| 286 | displayName = _(u'ArcGIS %(dt)s "%(name)s" of %(pdt)s "%(cp)s"') % {u'dt': self._DataType, u'name': name, u'pdt': self._PhysicalDataType, u'cp': d.CatalogPath}
|
|---|
| 287 | self._LogDebug(_(u'%(class)s 0x%(id)08X: Opening %(dn)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'dn': displayName})
|
|---|
| 288 |
|
|---|
| 289 | hasOID = hasattr(d, 'HasOID') and bool(d.HasOID)
|
|---|
| 290 |
|
|---|
| 291 | if hasattr(d, 'OIDFieldName') and isinstance(d.OIDFieldName, basestring):
|
|---|
| 292 | oidFieldName = unicode(d.OIDFieldName)
|
|---|
| 293 | else:
|
|---|
| 294 | oidFieldName = None
|
|---|
| 295 |
|
|---|
| 296 | geometryType = None
|
|---|
| 297 | if hasattr(d, 'ShapeType') and isinstance(d.ShapeType, basestring):
|
|---|
| 298 | shapeType = d.ShapeType.lower()
|
|---|
| 299 | if shapeType == 'point':
|
|---|
| 300 | if hasattr(d, 'HasZ') and bool(d.HasZ):
|
|---|
| 301 | geometryType = u'Point25D'
|
|---|
| 302 | else:
|
|---|
| 303 | geometryType = u'Point'
|
|---|
| 304 | elif shapeType == 'multipoint':
|
|---|
| 305 | if hasattr(d, 'HasZ') and bool(d.HasZ):
|
|---|
| 306 | geometryType = u'MultiPoint25D'
|
|---|
| 307 | else:
|
|---|
| 308 | geometryType = u'MultiPoint'
|
|---|
| 309 | elif shapeType == 'polyline':
|
|---|
| 310 | if hasattr(d, 'HasZ') and bool(d.HasZ):
|
|---|
| 311 | geometryType = u'MultiLineString25D'
|
|---|
| 312 | else:
|
|---|
| 313 | geometryType = u'MultiLineString'
|
|---|
| 314 | elif shapeType == 'polygon':
|
|---|
| 315 | if hasattr(d, 'HasZ') and bool(d.HasZ):
|
|---|
| 316 | geometryType = u'MultiPolygon25D'
|
|---|
| 317 | else:
|
|---|
| 318 | geometryType = u'MultiPolygon'
|
|---|
| 319 | else:
|
|---|
| 320 | self._LogWarning(_(u'The %(dn)s has an unsupported shape type "%(st)s". Geometry will not be available for this dataset.'), {u'dn': displayName, u'st': d.ShapeType})
|
|---|
| 321 |
|
|---|
| 322 | geometryFieldName = None
|
|---|
| 323 | if geometryType is not None:
|
|---|
| 324 | if hasattr(d, 'ShapeFieldName') and isinstance(d.ShapeFieldName, basestring) and len(d.ShapeFieldName) > 0:
|
|---|
| 325 | geometryFieldName = d.ShapeFieldName
|
|---|
| 326 | else:
|
|---|
| 327 | self._LogWarning(_(u'ArcGIS reported that %(dn)s has %(geom)s geometry but could not provide the name of the shape field. Geometry will not be available for this dataset.'), {u'dn': displayName, u'geom': geometryType})
|
|---|
| 328 | geometryType = None
|
|---|
| 329 |
|
|---|
| 330 | if geometryType is not None:
|
|---|
| 331 | srType = 'arcgis'
|
|---|
| 332 | sr = gp.CreateSpatialReference_management(d.SpatialReference).split(';')[0]
|
|---|
| 333 | else:
|
|---|
| 334 | srType = None
|
|---|
| 335 | sr = None
|
|---|
| 336 |
|
|---|
| 337 | if d.DataType.lower() in ['dbasetable', 'textfile', 'shapefile']:
|
|---|
| 338 | maxStringLength = 254
|
|---|
| 339 | else:
|
|---|
| 340 | maxStringLength = None
|
|---|
| 341 |
|
|---|
| 342 | fields = []
|
|---|
| 343 | fieldsObj = d.Fields
|
|---|
| 344 | f = fieldsObj.Next()
|
|---|
| 345 | while f is not None:
|
|---|
| 346 | dataType = f.Type.lower()
|
|---|
| 347 | if dataType == 'oid':
|
|---|
| 348 | dataType = 'oid'
|
|---|
| 349 | elif dataType == 'geometry':
|
|---|
| 350 | dataType = 'geometry'
|
|---|
| 351 | elif dataType == 'smallinteger':
|
|---|
| 352 | dataType = 'int16'
|
|---|
| 353 | elif dataType == 'integer':
|
|---|
| 354 | dataType = 'int32'
|
|---|
| 355 | elif dataType == 'single':
|
|---|
| 356 | dataType = 'float32'
|
|---|
| 357 | elif dataType == 'double':
|
|---|
| 358 | dataType = 'float64'
|
|---|
| 359 | elif dataType == 'string':
|
|---|
| 360 | dataType = 'string'
|
|---|
| 361 | elif dataType == 'date':
|
|---|
| 362 | if d.DataType.lower() in ['dbasetable', 'shapefile']:
|
|---|
| 363 | dataType = 'date'
|
|---|
| 364 | else:
|
|---|
| 365 | dataType = 'datetime'
|
|---|
| 366 | elif dataType == 'blob':
|
|---|
| 367 | dataType = 'binary'
|
|---|
| 368 | else:
|
|---|
| 369 | dataType = 'unknown'
|
|---|
| 370 | isNullable = f.IsNullable
|
|---|
| 371 | isNullable = isinstance(isNullable, (types.BooleanType, types.IntType)) and bool(isNullable) or isinstance(isNullable, basestring) and isNullable.lower() == 'true'
|
|---|
| 372 | isSettable = dataType != 'oid' and not (f.Name.lower() == 'shape_length' and geometryType in [u'MultiLineString', u'MultiLineString25D', u'MultiPolygon', u'MultiPolygon25D']) and not (f.Name.lower() == 'shape_area' and geometryType in [u'MultiPolygon', u'MultiPolygon25D'])
|
|---|
| 373 | fields.append(Field(f.Name, dataType, f.Length, f.Precision, isNullable, isSettable))
|
|---|
| 374 | f = fieldsObj.Next()
|
|---|
| 375 |
|
|---|
| 376 | super(ArcGISTabularLayer, self).__init__(displayName, hasOID, oidFieldName, geometryType, geometryFieldName, srType, sr, maxStringLength, fields, ArcGISSelectCursor, ArcGISUpdateCursor, ArcGISInsertCursor)
|
|---|
| 377 |
|
|---|
| 378 |
|
|---|
| 379 | class ArcGISWorkspace(DatasetCollectionTree, Database):
|
|---|
| 380 | __doc__ = DynamicDocString()
|
|---|
| 381 |
|
|---|
| 382 | def _GetPath(self):
|
|---|
| 383 | return self._Path
|
|---|
| 384 |
|
|---|
| 385 | Path = property(_GetPath, doc=DynamicDocString())
|
|---|
| 386 |
|
|---|
| 387 | def _GetDatasetType(self):
|
|---|
| 388 | return self._DatasetType
|
|---|
| 389 |
|
|---|
| 390 | DatasetType = property(_GetDatasetType, doc=DynamicDocString())
|
|---|
| 391 |
|
|---|
| 392 | def _GetCacheTree(self):
|
|---|
| 393 | return self._CacheTree
|
|---|
| 394 |
|
|---|
| 395 | CacheTree = property(_GetCacheTree, doc=DynamicDocString())
|
|---|
| 396 |
|
|---|
| 397 | def __init__(self, path, datasetType, pathParsingExpressions=None, pathCreationExpressions=None, cacheTree=True, queryableAttributes=None, queryableAttributeValues=None, lazyPropertyValues=None, cacheDirectory=None):
|
|---|
| 398 | # TODO: self.__doc__.Obj.ValidateMethodInvocation()
|
|---|
| 399 |
|
|---|
| 400 | # Validate datasetType.
|
|---|
| 401 |
|
|---|
| 402 | if not issubclass(datasetType, (ArcGISRaster, ArcGISTable)):
|
|---|
| 403 | raise TypeError(_(u'datasetType must be an ArcGISRaster or ArcGISTable, or a subclass of one of them.'))
|
|---|
| 404 |
|
|---|
| 405 | # If the path exists, validate that it is an
|
|---|
| 406 | # appropriately-typed ArcGIS object.
|
|---|
| 407 |
|
|---|
| 408 | gp = GeoprocessorManager.GetWrappedGeoprocessor()
|
|---|
| 409 |
|
|---|
| 410 | if gp.Exists(path):
|
|---|
| 411 | d = gp.Describe(path)
|
|---|
| 412 | if not(d.DataType.lower() in [u'workspace', u'folder'] or
|
|---|
| 413 | issubclass(datasetType, ArcGISRaster) and d.DataType.lower() == u'rastercatalog' or
|
|---|
| 414 | issubclass(datasetType, ArcGISTable) and d.DataType.lower() == u'featuredataset'):
|
|---|
| 415 | raise ValueError(_(u'Failed to open "%(path)s" as an ArcGIS workspace. ArcGIS reports that it is a %(dt)s, which cannot be opened as a workspace.') % {u'path': path, u'dt': d.DataType})
|
|---|
| 416 | self._DisplayName = _(u'ArcGIS %(dt)s %(path)s') % {u'dt': d.DataType, u'path': path}
|
|---|
| 417 | else:
|
|---|
| 418 | self._DisplayName = _(u'ArcGIS Folder or Workspace %(path)s') % {u'path': path}
|
|---|
| 419 |
|
|---|
| 420 | # Initialize our properties.
|
|---|
| 421 |
|
|---|
| 422 | self._Path = path
|
|---|
| 423 | self._DatasetType = datasetType
|
|---|
| 424 | self._CacheTree = cacheTree
|
|---|
| 425 | if self._CacheTree:
|
|---|
| 426 | self._TreeContentsCache = {}
|
|---|
| 427 | else:
|
|---|
| 428 | self._TreeContentsCache = None
|
|---|
| 429 | self._TreeDataTypeCache = {}
|
|---|
| 430 |
|
|---|
| 431 | # Initialize the base class.
|
|---|
| 432 |
|
|---|
| 433 | super(ArcGISWorkspace, self).__init__(pathParsingExpressions, pathCreationExpressions, queryableAttributes=queryableAttributes, queryableAttributeValues=queryableAttributeValues, lazyPropertyValues=lazyPropertyValues, cacheDirectory=cacheDirectory)
|
|---|
| 434 |
|
|---|
| 435 | def _GetDisplayName(self):
|
|---|
| 436 | return self._DisplayName
|
|---|
| 437 |
|
|---|
| 438 | @classmethod
|
|---|
| 439 | def _TestCapability(cls, capability):
|
|---|
| 440 | if capability in ['createtable', 'deletetable']:
|
|---|
| 441 | return None
|
|---|
| 442 |
|
|---|
| 443 | capList = capability.split(' ', 2)
|
|---|
| 444 | if len(capList) == 3 and capList[0] == 'geometrytype':
|
|---|
| 445 | if capList[1] in ['point', 'point25d', 'linestring', 'linestring25d', 'polygon', 'polygon25d', 'multipoint', 'multipoint25d', 'multilinestring', 'multilinestring25d', 'multipolygon', 'multipolygon25d']:
|
|---|
| 446 | return None
|
|---|
| 447 | return RuntimeError(_(u'Cannot create table %(table)s with "%(geom)s" geometry in %(dn)s. ArcGIS does not support that geometry type.') % {u'tableName': capList[2], u'geom': capList[1], u'dn': cls._DisplayName})
|
|---|
| 448 |
|
|---|
| 449 | if isinstance(cls, ArcGISWorkspace):
|
|---|
| 450 | return RuntimeError(_(u'The %(cls)s class does not support the "%(cap)s" capability.') % {u'cls': cls.__class__.__name__, u'cap': capability})
|
|---|
| 451 | return RuntimeError(_(u'The %(cls)s class does not support the "%(cap)s" capability.') % {u'cls': cls.__name__, u'cap': capability})
|
|---|
| 452 |
|
|---|
| 453 | def _ListContents(self, pathComponents):
|
|---|
| 454 |
|
|---|
| 455 | # If we are supposed to cache the tree, probe our cache for
|
|---|
| 456 | # the contents of this path.
|
|---|
| 457 |
|
|---|
| 458 | path = os.path.join(self.Path, *pathComponents)
|
|---|
| 459 |
|
|---|
| 460 | if self._CacheTree and path in self._TreeContentsCache:
|
|---|
| 461 | self._LogDebug(_(u'%(class)s 0x%(id)08X: Retrieved cached contents of %(dt)s %(path)s'), {u'class': self.__class__.__name__, u'id': id(self), u'dt': self._TreeDataTypeCache[path], u'path': path})
|
|---|
| 462 | return self._TreeContentsCache[path]
|
|---|
| 463 |
|
|---|
| 464 | # We did not retrieve the contents of this path from the
|
|---|
| 465 | # cache. Get the contents from ArcGIS.
|
|---|
| 466 |
|
|---|
| 467 | gp = GeoprocessorManager.GetWrappedGeoprocessor()
|
|---|
| 468 |
|
|---|
| 469 | d = gp.Describe(path)
|
|---|
| 470 | self._TreeDataTypeCache[path] = d.DataType
|
|---|
| 471 |
|
|---|
| 472 | self._LogDebug(_(u'%(class)s 0x%(id)08X: Listing contents of %(dt)s %(path)s'), {u'class': self.__class__.__name__, u'id': id(self), u'dt': d.DataType, u'path': path})
|
|---|
| 473 |
|
|---|
| 474 | contents = []
|
|---|
| 475 |
|
|---|
| 476 | # Change the geoprocessor's workspace to the path, so we can
|
|---|
| 477 | # use the geoprocessor's List functions. If the workspace
|
|---|
| 478 | # hasn't been set, ArcGIS 9.2 will return None for the
|
|---|
| 479 | # workspace property, but will raise an exception if we set it
|
|---|
| 480 | # to None. To work around this screwy design, we convert None
|
|---|
| 481 | # to '', which is what ArcGIS 9.1 returns when the workspace
|
|---|
| 482 | # hasn't been set.
|
|---|
| 483 |
|
|---|
| 484 | oldWorkspace = gp.Workspace
|
|---|
| 485 | if oldWorkspace is None:
|
|---|
| 486 | oldWorkspace = ''
|
|---|
| 487 | gp.Workspace = path
|
|---|
| 488 |
|
|---|
| 489 | try:
|
|---|
| 490 | # If we have not reached the lowest-level path component,
|
|---|
| 491 | # enumerate ArcGIS objects that are containers.
|
|---|
| 492 |
|
|---|
| 493 | if len(pathComponents) < len(self.PathParsingExpressions) - 1:
|
|---|
| 494 | if d.DataType.lower() == u'folder':
|
|---|
| 495 | enum = gp.ListWorkspaces('*')
|
|---|
| 496 | workspace = enum.Next()
|
|---|
| 497 | while isinstance(workspace, basestring) and len(workspace) > 0:
|
|---|
| 498 | d = gp.Describe(workspace)
|
|---|
| 499 | if d.DataType.lower() == u'workspace' or d.DataType.lower() == u'folder' and (os.path.basename(workspace) != 'info' or not os.path.exists(os.path.join(path, workspace, 'arc.dir'))):
|
|---|
| 500 | workspace = os.path.basename(workspace)
|
|---|
| 501 | contents.append(workspace)
|
|---|
| 502 | self._TreeDataTypeCache[os.path.join(path, workspace)] = d.DataType
|
|---|
| 503 | workspace = enum.Next()
|
|---|
| 504 |
|
|---|
| 505 | elif d.DataType.lower() == u'workspace': # If the current path is a workspace, then enumerate raster catalogs or feature datasets in the workspace
|
|---|
| 506 | enum = gp.ListDatasets('*')
|
|---|
| 507 | dataset = enum.Next()
|
|---|
| 508 | while isinstance(dataset, basestring) and len(dataset) > 0:
|
|---|
| 509 | d = gp.Describe(dataset)
|
|---|
| 510 | if issubclass(self._DatasetType, ArcGISRaster) and d.DataType.lower() == u'rastercatalog' or issubclass(self._DatasetType, ArcGISTable) and d.DataType.lower() == u'featuredataset':
|
|---|
| 511 | dataset = os.path.basename(dataset)
|
|---|
| 512 | contents.append(dataset)
|
|---|
| 513 | self._TreeDataTypeCache[os.path.join(path, dataset)] = d.DataType
|
|---|
| 514 | dataset = enum.Next()
|
|---|
| 515 |
|
|---|
| 516 | # Otherwise (we have reached the lowest-level path
|
|---|
| 517 | # component), enumerate the type of object we're
|
|---|
| 518 | # ultimately looking for.
|
|---|
| 519 |
|
|---|
| 520 | elif issubclass(self._DatasetType, ArcGISRaster):
|
|---|
| 521 |
|
|---|
| 522 | # Raster catalogs require special processing.
|
|---|
| 523 | #
|
|---|
| 524 | # TODO: Read raster catalogs using the tabular API and
|
|---|
| 525 | # create queryable attributes for each field of the
|
|---|
| 526 | # raster catalog.
|
|---|
| 527 |
|
|---|
| 528 | if d.DataType.lower() == u'rastercatalog':
|
|---|
| 529 | enum = gp.ListDatasets('*')
|
|---|
| 530 | raster = enum.Next()
|
|---|
| 531 | while isinstance(raster, basestring) and len(raster) > 0:
|
|---|
| 532 | if raster.startswith(os.path.basename(path) + '\\') or raster.startswith(os.path.basename(path) + '/'): # Delete redundant raster catalog name from beginning of raster name, if present. Looks like an ArcGIS bug. Found in 9.3.1; other versions not tested. Causes problems later with multiband rasters.
|
|---|
| 533 | raster = raster[len(os.path.basename(path))+1:]
|
|---|
| 534 | contents.append(raster)
|
|---|
| 535 | raster = enum.Next()
|
|---|
| 536 |
|
|---|
| 537 | # Other containers of rasters (directories,
|
|---|
| 538 | # geodatabases) do not require special processing.
|
|---|
| 539 |
|
|---|
| 540 | else:
|
|---|
| 541 | enum = gp.ListRasters('*')
|
|---|
| 542 | raster = enum.Next()
|
|---|
| 543 | while isinstance(raster, basestring) and len(raster) > 0:
|
|---|
| 544 | contents.append(os.path.basename(raster))
|
|---|
| 545 | raster = enum.Next()
|
|---|
| 546 |
|
|---|
| 547 | elif issubclass(self._DatasetType, ArcGISTable):
|
|---|
| 548 |
|
|---|
| 549 | # If the caller is looking for ArcGISTables, enumerate
|
|---|
| 550 | # the feature classes. Additionally, if the path does
|
|---|
| 551 | # not resolve to a FeatureDataset, enumerate the
|
|---|
| 552 | # tables.
|
|---|
| 553 |
|
|---|
| 554 | enum = gp.ListFeatureClasses('*')
|
|---|
| 555 | featureClass = enum.Next()
|
|---|
| 556 | while isinstance(featureClass, basestring) and len(featureClass) > 0:
|
|---|
| 557 | contents.append(os.path.basename(featureClass))
|
|---|
| 558 | featureClass = enum.Next()
|
|---|
| 559 |
|
|---|
| 560 | if d.DataType.lower() != u'featuredataset':
|
|---|
| 561 | enum = gp.ListTables('*')
|
|---|
| 562 | table = enum.Next()
|
|---|
| 563 | while isinstance(table, basestring) and len(table) > 0:
|
|---|
| 564 | contents.append(os.path.basename(table))
|
|---|
| 565 | table = enum.Next()
|
|---|
| 566 |
|
|---|
| 567 | # Change the geoprocessor's workspace back to what it was.
|
|---|
| 568 |
|
|---|
| 569 | finally:
|
|---|
| 570 | gp.Workspace = oldWorkspace
|
|---|
| 571 |
|
|---|
| 572 | # Sort the contents and add them to the cache, if required.
|
|---|
| 573 |
|
|---|
| 574 | contents.sort()
|
|---|
| 575 |
|
|---|
| 576 | if self._CacheTree:
|
|---|
| 577 | self._TreeContentsCache[path] = contents
|
|---|
| 578 |
|
|---|
| 579 | return contents
|
|---|
| 580 |
|
|---|
| 581 | def _ConstructFoundObject(self, pathComponents, attrValues, options):
|
|---|
| 582 |
|
|---|
| 583 | # If we're looking for rasters and the path components point
|
|---|
| 584 | # to a file system object that is not inside a file
|
|---|
| 585 | # geodatabase, construct and return a GDALDataset. Accessing
|
|---|
| 586 | # rasters with GDAL is much faster than the ArcGIS
|
|---|
| 587 | # geoprocessor, and is the only way we can read and write data
|
|---|
| 588 | # (at least prior to ArcGIS 10, which theoretically allows it
|
|---|
| 589 | # via the geoprocessor).
|
|---|
| 590 | #
|
|---|
| 591 | # Note that we retrieve the raster's SpatialReference using
|
|---|
| 592 | # the ArcGIS geoprocessor to work around the unfortunate fact
|
|---|
| 593 | # that GDAL does not know how to recognize some of the
|
|---|
| 594 | # ESRI-specific WKT strings that ArcGIS stores in rasters.
|
|---|
| 595 |
|
|---|
| 596 | if issubclass(self._DatasetType, ArcGISRaster) and os.path.exists(os.path.join(self.Path, *pathComponents)) and self._TreeDataTypeCache[os.path.join(self.Path, *pathComponents[:-1])].lower() == u'folder':
|
|---|
| 597 | gp = GeoprocessorManager.GetWrappedGeoprocessor()
|
|---|
| 598 | try:
|
|---|
| 599 | sr = gp.CreateSpatialReference_management(gp.Describe(os.path.join(self.Path, *pathComponents)).SpatialReference).split(';')[0]
|
|---|
| 600 | except:
|
|---|
| 601 | sr = gp.CreateSpatialReference_management(gp.Describe(os.path.join(self.Path, *pathComponents)).SpatialReference).split(';')[0] # Sometimes Arc 10 fails randomly with RuntimeError: DescribeData: Method SpatialReference does not exist. Try again.
|
|---|
| 602 | spatialReference = Dataset.ConvertSpatialReference('arcgis', sr, 'obj')
|
|---|
| 603 | return GDALDataset(os.path.join(*pathComponents), parentCollection=self, queryableAttributeValues=attrValues, lazyPropertyValues={'SpatialReference': spatialReference}, cacheDirectory=self.CacheDirectory, **options)
|
|---|
| 604 |
|
|---|
| 605 | # Otherwise construct and return an object of the type
|
|---|
| 606 | # specified to our own constructor.
|
|---|
| 607 |
|
|---|
| 608 | return self.DatasetType(os.path.join(*pathComponents), parentCollection=self, queryableAttributeValues=attrValues, cacheDirectory=self.CacheDirectory, **options)
|
|---|
| 609 |
|
|---|
| 610 | def _GetLocalFile(self, pathComponents):
|
|---|
| 611 | return os.path.join(self.Path, *pathComponents), False # False indicates that it is NOT ok for the caller to delete the file after decompressing it, to save space
|
|---|
| 612 |
|
|---|
| 613 | def _RemoveExistingDatasetsFromList(self, pathComponents, datasets, progressReporter):
|
|---|
| 614 | self.DatasetType._RemoveExistingDatasetsFromList(os.path.join(self.Path, *pathComponents), datasets, progressReporter)
|
|---|
| 615 |
|
|---|
| 616 | def _ImportDatasetsToPath(self, pathComponents, sourceDatasets, mode, progressReporter, options):
|
|---|
| 617 | self.DatasetType._ImportDatasetsToPath(os.path.join(self.Path, *pathComponents), sourceDatasets, mode, progressReporter, options)
|
|---|
| 618 |
|
|---|
| 619 | # Overridden methods of Database
|
|---|
| 620 |
|
|---|
| 621 | def ImportTable(self, destTableName, sourceTable, fields=None, where=None, orderBy=None, rowCount=None, reportProgress=True, rowDescriptionSingular=None, rowDescriptionPlural=None, copiedOIDFieldName=None, allowSafeCoercions=True):
|
|---|
| 622 |
|
|---|
| 623 | # First call the base class method to actually do the import.
|
|---|
| 624 |
|
|---|
| 625 | table = super(ArcGISWorkspace, self).ImportTable(destTableName, sourceTable, fields, where, orderBy, rowCount, reportProgress, rowDescriptionSingular, rowDescriptionPlural, copiedOIDFieldName, allowSafeCoercions)
|
|---|
| 626 |
|
|---|
| 627 | # Now, if the resulting table has a geometry column, check
|
|---|
| 628 | # whether it has a spatial index. If not, create one. We do
|
|---|
| 629 | # this specifically because I have noticed that sometimes for
|
|---|
| 630 | # shapefiles, ArcGIS appears to discard the spatial index that
|
|---|
| 631 | # was requested in the call to CreateFeatureClass_management.
|
|---|
| 632 | # I have not determined whether it is caused by subsequently
|
|---|
| 633 | # adding fields, by adding records, or what.
|
|---|
| 634 |
|
|---|
| 635 | if table.GeometryType is not None:
|
|---|
| 636 | gp = GeoprocessorManager.GetWrappedGeoprocessor()
|
|---|
| 637 | d = gp.Describe(table._GetFullPath())
|
|---|
| 638 |
|
|---|
| 639 | if hasattr(d, 'HasSpatialIndex') and not d.HasSpatialIndex:
|
|---|
| 640 | if reportProgress:
|
|---|
| 641 | self._LogInfo(_(u'Adding a spatial index to %(dn)s.') % {u'dn': table.DisplayName})
|
|---|
| 642 | else:
|
|---|
| 643 | self._LogDebug(_(u'Adding a spatial index to %(dn)s.') % {u'dn': table.DisplayName})
|
|---|
| 644 |
|
|---|
| 645 | gp.AddSpatialIndex(table._GetFullPath(), 0, 0, 0)
|
|---|
| 646 |
|
|---|
| 647 | # Return successfully.
|
|---|
| 648 |
|
|---|
| 649 | return table
|
|---|
| 650 |
|
|---|
| 651 | def _TableExists(self, tableName):
|
|---|
| 652 | gp = GeoprocessorManager.GetWrappedGeoprocessor()
|
|---|
| 653 | return gp.Exists(os.path.join(self._Path, tableName))
|
|---|
| 654 |
|
|---|
| 655 | def _CreateTable(self, tableName, geometryType, spatialReference, geometryFieldName):
|
|---|
| 656 |
|
|---|
| 657 | # If the caller did not specify a geometryType, create a
|
|---|
| 658 | # regular table.
|
|---|
| 659 |
|
|---|
| 660 | gp = GeoprocessorManager.GetWrappedGeoprocessor()
|
|---|
| 661 | if geometryType is None:
|
|---|
| 662 | gp.CreateTable_management(self._Path, tableName)
|
|---|
| 663 |
|
|---|
| 664 | # Otherwise create a feature class.
|
|---|
| 665 |
|
|---|
| 666 | else:
|
|---|
| 667 | geometryType = geometryType.upper()
|
|---|
| 668 |
|
|---|
| 669 | hasZ = {False: 'DISABLED', True: 'ENABLED'}[geometryType[-3:] == u'25D']
|
|---|
| 670 |
|
|---|
| 671 | if geometryType in [u'POINT', u'POINT25D']:
|
|---|
| 672 | geometryType = u'POINT'
|
|---|
| 673 | elif geometryType in [u'MULTIPOINT', u'MULTIPOINT25D']:
|
|---|
| 674 | geometryType = u'MULTIPOINT'
|
|---|
| 675 | elif geometryType in [u'LINESTRING', u'MULTILINESTRING', u'LINESTRING25D', u'MULTILINESTRING25D']:
|
|---|
| 676 | geometryType = u'POLYLINE'
|
|---|
| 677 | elif geometryType in [u'POLYGON', u'MULTIPOLYGON', u'POLYGON25D', u'MULTIPOLYGON25D']:
|
|---|
| 678 | geometryType = u'POLYGON'
|
|---|
| 679 |
|
|---|
| 680 | srString = Dataset.ConvertSpatialReference('Obj', spatialReference, 'ArcGIS')
|
|---|
| 681 |
|
|---|
| 682 | gp.CreateFeatureClass_management(self._Path, tableName, geometryType, None, 'DISABLED', hasZ, srString, None, 0, 0, 0)
|
|---|
| 683 |
|
|---|
| 684 | # Return an ArcGISTable instance for the new table.
|
|---|
| 685 |
|
|---|
| 686 | return ArcGISTable(os.path.join(self._Path, tableName))
|
|---|
| 687 |
|
|---|
| 688 | def _DeleteTable(self, tableName):
|
|---|
| 689 | gp = GeoprocessorManager.GetWrappedGeoprocessor()
|
|---|
| 690 | return gp.Delete_management(os.path.join(self._Path, tableName))
|
|---|
| 691 |
|
|---|
| 692 |
|
|---|
| 693 | class ArcGISRaster(DatasetCollection):
|
|---|
| 694 | __doc__ = DynamicDocString()
|
|---|
| 695 |
|
|---|
| 696 | def _GetPath(self):
|
|---|
| 697 | return self._Path
|
|---|
| 698 |
|
|---|
| 699 | Path = property(_GetPath, doc=DynamicDocString())
|
|---|
| 700 |
|
|---|
| 701 | def _GetDecompressedFileToReturn(self):
|
|---|
| 702 | return self._DecompressedFileToReturn
|
|---|
| 703 |
|
|---|
| 704 | DecompressedFileToReturn = property(_GetDecompressedFileToReturn, doc=DynamicDocString())
|
|---|
| 705 |
|
|---|
| 706 | def _GetArcGISDataType(self):
|
|---|
| 707 | return self.GetLazyPropertyValue('ArcGISDataType')
|
|---|
| 708 |
|
|---|
| 709 | ArcGISDataType = property(_GetArcGISDataType, doc=DynamicDocString())
|
|---|
| 710 |
|
|---|
| 711 | def __init__(self, path, decompressedFileToReturn=None, parentCollection=None, queryableAttributeValues=None, lazyPropertyValues=None, cacheDirectory=None):
|
|---|
| 712 | # TODO: self.__doc__.Obj.ValidateMethodInvocation()
|
|---|
| 713 |
|
|---|
| 714 | # Initialize our properties.
|
|---|
| 715 |
|
|---|
| 716 | self._Path = path
|
|---|
| 717 | self._DecompressedFileToReturn = decompressedFileToReturn
|
|---|
| 718 | self._GDALDataset = None
|
|---|
| 719 | self._TempCopy = None
|
|---|
| 720 |
|
|---|
| 721 | if parentCollection is None:
|
|---|
| 722 | self._DisplayName = _(u'ArcGIS raster "%(path)s"') % {u'path': path}
|
|---|
| 723 | else:
|
|---|
| 724 | self._DisplayName = _(u'ArcGIS raster "%(path)s"') % {u'path': os.path.join(parentCollection.Path, path)}
|
|---|
| 725 |
|
|---|
| 726 | # We allow querying for Grid datasets by band number. If the
|
|---|
| 727 | # parent collection(s) did not define the Band queryable
|
|---|
| 728 | # attribute, we must define it.
|
|---|
| 729 | #
|
|---|
| 730 | # TODO: Add name attribute, derived from describe's BaseName property?
|
|---|
| 731 |
|
|---|
| 732 | queryableAttributes = None
|
|---|
| 733 | if parentCollection is not None:
|
|---|
| 734 | bandAttribute = parentCollection.GetQueryableAttribute(u'Band')
|
|---|
| 735 | if parentCollection is None or bandAttribute is None:
|
|---|
| 736 | bandAttribute = QueryableAttribute(u'Band', _(u'Band number'), IntegerTypeMetadata(minValue=1))
|
|---|
| 737 | queryableAttributes = (bandAttribute,)
|
|---|
| 738 |
|
|---|
| 739 | # Initialize the base class.
|
|---|
| 740 |
|
|---|
| 741 | super(ArcGISRaster, self).__init__(parentCollection, queryableAttributes, queryableAttributeValues, lazyPropertyValues, cacheDirectory)
|
|---|
| 742 |
|
|---|
| 743 | # Validate that the caller has not assigned a value to the
|
|---|
| 744 | # Band queryable attribute, either directly to us or to our
|
|---|
| 745 | # parent collection(s).
|
|---|
| 746 |
|
|---|
| 747 | if self.GetQueryableAttributeValue(u'Band') is not None:
|
|---|
| 748 | raise ValueError(_(u'This ArcGISRaster or its parent collection(s) specify a value for the Band queryable attribute. This is not allowed, as the value of that queryable attribute is assigned by the ArcGISRasterBand class.'))
|
|---|
| 749 |
|
|---|
| 750 | def _GetDisplayName(self):
|
|---|
| 751 | if self._GDALDataset is not None:
|
|---|
| 752 | return self._GDALDataset.DisplayName
|
|---|
| 753 | return self._DisplayName
|
|---|
| 754 |
|
|---|
| 755 | def _GetLazyPropertyPhysicalValue(self, name):
|
|---|
| 756 |
|
|---|
| 757 | # If this is a lazy property that we know how to retrieve, get
|
|---|
| 758 | # it from the geoprocessor's Describe object.
|
|---|
| 759 |
|
|---|
| 760 | if name not in ['ArcGISDataType', 'Bands', 'SpatialReference']:
|
|---|
| 761 | return None
|
|---|
| 762 |
|
|---|
| 763 | if self.ParentCollection is None:
|
|---|
| 764 | path = self._Path
|
|---|
| 765 | else:
|
|---|
| 766 | path = os.path.join(self.ParentCollection.Path, self._Path)
|
|---|
| 767 |
|
|---|
| 768 | gp = GeoprocessorManager.GetWrappedGeoprocessor()
|
|---|
| 769 |
|
|---|
| 770 | if not gp.Exists(path):
|
|---|
| 771 | raise ValueError(_(u'Failed to open ArcGIS raster "%(path)s". ArcGIS reports that it does not exist.') % {u'path': path})
|
|---|
| 772 |
|
|---|
| 773 | d = gp.Describe(path)
|
|---|
| 774 | if d.DataType.lower() not in [u'rasterdataset', u'rasterlayer']:
|
|---|
| 775 | raise ValueError(_(u'Failed to open "%(path)s" as an ArcGIS raster. ArcGIS reports that it is a %(dt)s, which cannot be opened as a raster.') % {u'path': path, u'dt': d.DataType})
|
|---|
| 776 |
|
|---|
| 777 | self._DisplayName = _(u'ArcGIS %(dt)s "%(path)s"') % {u'dt': d.DataType, u'path': path}
|
|---|
| 778 |
|
|---|
| 779 | self.SetLazyPropertyValue('ArcGISDataType', d.DataType)
|
|---|
| 780 | self.SetLazyPropertyValue('Bands', d.BandCount)
|
|---|
| 781 | self.SetLazyPropertyValue('SpatialReference', Dataset.ConvertSpatialReference('arcgis', gp.CreateSpatialReference_management(d.SpatialReference).split(';')[0], 'obj'))
|
|---|
| 782 |
|
|---|
| 783 | # Log a debug message with the properties of the raster.
|
|---|
| 784 |
|
|---|
| 785 | self._LogDebug(_(u'%(class)s 0x%(id)08X: Retrieved lazy properties of %(dn)s: Bands=%(Bands)s, SpatialReference=%(SpatialReference)s.'),
|
|---|
| 786 | {u'class': self.__class__.__name__,
|
|---|
| 787 | u'id': id(self),
|
|---|
| 788 | u'dn': self.DisplayName,
|
|---|
| 789 | u'Bands': repr(self.GetLazyPropertyValue('Bands')),
|
|---|
| 790 | u'SpatialReference': repr(Dataset.ConvertSpatialReference('obj', self.GetLazyPropertyValue('SpatialReference'), 'arcgis'))})
|
|---|
| 791 |
|
|---|
| 792 | # Return the value of the requested property.
|
|---|
| 793 |
|
|---|
| 794 | return self.GetLazyPropertyValue(name)
|
|---|
| 795 |
|
|---|
| 796 | def _QueryDatasets(self, parsedExpression, progressReporter, options, parentAttrValues):
|
|---|
| 797 |
|
|---|
| 798 | # Go through the bands of this dataset, testing whether each
|
|---|
| 799 | # one matches the query expression. For each match, construct
|
|---|
| 800 | # either a GDALRasterBand or an ArcGISRasterBand and add it to
|
|---|
| 801 | # our list of datasets to return.
|
|---|
| 802 |
|
|---|
| 803 | datasetsFound = []
|
|---|
| 804 | triedGDAL = False
|
|---|
| 805 | gdalDataset = None
|
|---|
| 806 |
|
|---|
| 807 | for band in range(1, self.GetLazyPropertyValue('Bands') + 1):
|
|---|
| 808 | if parsedExpression is not None:
|
|---|
| 809 | attrValues = {u'Band': band}
|
|---|
| 810 | attrValues.update(parentAttrValues)
|
|---|
| 811 | try:
|
|---|
| 812 | result = parsedExpression.eval(attrValues)
|
|---|
| 813 | except Exception, e:
|
|---|
| 814 | continue # TODO: report better message
|
|---|
| 815 | else:
|
|---|
| 816 | result = True
|
|---|
| 817 |
|
|---|
| 818 | if result is None or result:
|
|---|
| 819 | self._LogDebug(_(u'%(class)s 0x%(id)08X: Query result for band %(band)i: %(result)s'), {u'class': self.__class__.__name__, u'id': id(self), u'band': band, u'result': repr(result)})
|
|---|
| 820 |
|
|---|
| 821 | if result:
|
|---|
| 822 | if not triedGDAL:
|
|---|
| 823 | gdalDataset = self._TryToInstantiateGDALDataset()
|
|---|
| 824 | triedGDAL = True
|
|---|
| 825 |
|
|---|
| 826 | if gdalDataset is not None:
|
|---|
| 827 | datasetsFound.append(GDALRasterBand(gdalDataset, band))
|
|---|
| 828 | else:
|
|---|
| 829 | datasetsFound.append(ArcGISRasterBand(self, band))
|
|---|
| 830 |
|
|---|
| 831 | if progressReporter is not None:
|
|---|
| 832 | progressReporter.ReportProgress()
|
|---|
| 833 |
|
|---|
| 834 | return datasetsFound
|
|---|
| 835 |
|
|---|
| 836 | def _TryToInstantiateGDALDataset(self):
|
|---|
| 837 |
|
|---|
| 838 | # If we already instantiated a GDALDataset for this raster,
|
|---|
| 839 | # return it.
|
|---|
| 840 |
|
|---|
| 841 | if self._GDALDataset is not None:
|
|---|
| 842 | return self._GDALDataset
|
|---|
| 843 |
|
|---|
| 844 | # Before we try to instantiate a GDALDataset for this raster,
|
|---|
| 845 | # retrieve the SpatialReference lazy property. Unfortunately,
|
|---|
| 846 | # GDAL does not know how to recognize some of the
|
|---|
| 847 | # ESRI-specific WKT strings that ArcGIS stores in rasters. To
|
|---|
| 848 | # work around this, we retrieve it using the geoprocessor.
|
|---|
| 849 | # Next, when we instantiate the GDALDataset, we will provide
|
|---|
| 850 | # it as a lazy property so that GDALDataset does not try to
|
|---|
| 851 | # retrieve it.
|
|---|
| 852 |
|
|---|
| 853 | self.GetLazyPropertyValue('SpatialReference') # Do not do anything with it. We just need it to be stored in the self._LazyPropertyValues dictionary.
|
|---|
| 854 |
|
|---|
| 855 | # If we created an IMG file for it in a temporary cache
|
|---|
| 856 | # directory, instantate and return a GDALDataset for it.
|
|---|
| 857 |
|
|---|
| 858 | if self._TempCopy is not None:
|
|---|
| 859 | self._GDALDataset = GDALDataset(self._TempCopy, parentCollection=self.ParentCollection, queryableAttributeValues=self._QueryableAttributeValues, lazyPropertyValues=self._LazyPropertyValues)
|
|---|
| 860 | self._RegisterForCloseAtExit()
|
|---|
| 861 | return self._GDALDataset
|
|---|
| 862 |
|
|---|
| 863 | # We have not instantiated a GDALDataset for this raster and
|
|---|
| 864 | # did not make a temporary IMG file. Determine if the path
|
|---|
| 865 | # resolves to a file system object that is not in a file
|
|---|
| 866 | # geodatabase. If so, instantiate and return a GDALDataset for
|
|---|
| 867 | # it.
|
|---|
| 868 |
|
|---|
| 869 | if self.ParentCollection is None:
|
|---|
| 870 | path = self._Path
|
|---|
| 871 | else:
|
|---|
| 872 | path = os.path.join(self.ParentCollection.Path, self._Path)
|
|---|
| 873 |
|
|---|
| 874 | if os.path.exists(path) and len(os.path.dirname(path)) > 0 and GeoprocessorManager.GetWrappedGeoprocessor().Describe(os.path.dirname(path)).DataType.lower() == u'folder':
|
|---|
| 875 | self._GDALDataset = GDALDataset(path, decompressedFileToReturn=self.DecompressedFileToReturn, parentCollection=self.ParentCollection, queryableAttributeValues=self._QueryableAttributeValues, lazyPropertyValues=self._LazyPropertyValues, cacheDirectory=self.CacheDirectory)
|
|---|
| 876 | self._RegisterForCloseAtExit()
|
|---|
| 877 | return self._GDALDataset
|
|---|
| 878 |
|
|---|
| 879 | # The path does not resolve to a file system object that is
|
|---|
| 880 | # not in a file geodatabase. Therefore, it must be an
|
|---|
| 881 | # in-memory raster layer or a raster in a geodatabase, and we
|
|---|
| 882 | # cannot access it directly with a GDALDataset.
|
|---|
| 883 |
|
|---|
| 884 | return None
|
|---|
| 885 |
|
|---|
| 886 | def _InstantiateGDALDataset(self):
|
|---|
| 887 |
|
|---|
| 888 | # First try to open the raster directly with GDAL.
|
|---|
| 889 |
|
|---|
| 890 | if self._TryToInstantiateGDALDataset() is not None:
|
|---|
| 891 | return self._GDALDataset
|
|---|
| 892 |
|
|---|
| 893 | # We failed to open raster directly with GDAL, probably
|
|---|
| 894 | # because it is in a geodatabase or is an in-memory raster
|
|---|
| 895 | # layer. Create a temporary cache directory and copy the
|
|---|
| 896 | # raster into it as an IMG file.
|
|---|
| 897 | #
|
|---|
| 898 | # TODO: This design is less than optimal if the raster has
|
|---|
| 899 | # multiple bands and the caller is not interested in all of
|
|---|
| 900 | # them. Investigate whether it is possible to copy just a
|
|---|
| 901 | # single band with the ArcGIS CopyRaster_management tool. If
|
|---|
| 902 | # so, consider changing the design so that ArcGISRasterBand
|
|---|
| 903 | # creates temporary copies of individual bands.
|
|---|
| 904 |
|
|---|
| 905 | if self.ParentCollection is None:
|
|---|
| 906 | path = self._Path
|
|---|
| 907 | else:
|
|---|
| 908 | path = os.path.join(self.ParentCollection.Path, self._Path)
|
|---|
| 909 |
|
|---|
| 910 | tempDir = self._CreateTempDirectory()
|
|---|
| 911 | tempPath = os.path.join(tempDir, 'raster.img')
|
|---|
| 912 |
|
|---|
| 913 | self._LogDebug(_(u'%(class)s 0x%(id)08X: Copying %(dn)s to "%(temp)s".'), {u'class': self.__class__.__name__, u'id': id(self), u'dn': self.DisplayName, u'temp': tempPath})
|
|---|
| 914 | GeoprocessorManager.GetWrappedGeoprocessor().CopyRaster_management(path, tempPath)
|
|---|
| 915 |
|
|---|
| 916 | self._TempCopy = tempPath
|
|---|
| 917 |
|
|---|
| 918 | # Now try to open it again. This should succeed.
|
|---|
| 919 |
|
|---|
| 920 | if self._TryToInstantiateGDALDataset() is not None:
|
|---|
| 921 | return self._GDALDataset
|
|---|
| 922 |
|
|---|
| 923 | def _Close(self):
|
|---|
| 924 | if hasattr(self, '_GDALDataset') and self._GDALDataset is not None:
|
|---|
| 925 | self._GDALDataset.Close()
|
|---|
| 926 | self._GDALDataset = None
|
|---|
| 927 | super(ArcGISRaster, self)._Close()
|
|---|
| 928 |
|
|---|
| 929 | @classmethod
|
|---|
| 930 | def _RemoveExistingDatasetsFromList(cls, path, datasets, progressReporter):
|
|---|
| 931 |
|
|---|
| 932 | # Because ArcGIS does not support adding bands to existing
|
|---|
| 933 | # rasters, we cannot implement 'add' mode in the way that most
|
|---|
| 934 | # people would expect (that is, if len(datasets) is greater
|
|---|
| 935 | # than the number of existing bands, add the remaining
|
|---|
| 936 | # datasets as new bands). Instead we assume that if the raster
|
|---|
| 937 | # exists, it already has all of the necessary bands. Thus, if
|
|---|
| 938 | # it exists, remove all of the datasets from the caller's
|
|---|
| 939 | # list.
|
|---|
| 940 |
|
|---|
| 941 | numDatasets = len(datasets)
|
|---|
| 942 |
|
|---|
| 943 | if cls._ArcGISRasterExists(path):
|
|---|
| 944 | cls._LogDebug(_(u'%(class)s: ArcGIS raster "%(path)s" exists.'), {u'class': cls.__name__, u'path': path})
|
|---|
| 945 | while len(datasets) > 0:
|
|---|
| 946 | del datasets[0]
|
|---|
| 947 | else:
|
|---|
| 948 | cls._LogDebug(_(u'%(class)s: ArcGIS raster "%(path)s" does not exist.'), {u'class': cls.__name__, u'path': path})
|
|---|
| 949 |
|
|---|
| 950 | # Report that we checked all of these datasets.
|
|---|
| 951 |
|
|---|
| 952 | if progressReporter is not None:
|
|---|
| 953 | progressReporter.ReportProgress(numDatasets)
|
|---|
| 954 |
|
|---|
| 955 | @classmethod
|
|---|
| 956 | def _ArcGISRasterExists(cls, name):
|
|---|
| 957 |
|
|---|
| 958 | # Check whether name is a file system path. If it is,
|
|---|
| 959 | # determine its existence using the file system. This is
|
|---|
| 960 | # faster than using the geoprocessor.
|
|---|
| 961 |
|
|---|
| 962 | if (name[0] in ['/', '\\'] or hasattr(os.path, 'splitdrive') and os.path.splitdrive(name)[0] != '' or hasattr(os.path, 'splitunc') and os.path.splitunc(name)[0] != '') and os.path.splitext(os.path.dirname(name))[1].lower() != '.gdb':
|
|---|
| 963 | return os.path.exists(name)
|
|---|
| 964 |
|
|---|
| 965 | # name is not a file system path. Determine its existence
|
|---|
| 966 | # using the geoprocessor.
|
|---|
| 967 |
|
|---|
| 968 | return GeoprocessorManager.GetWrappedGeoprocessor().Exists(name)
|
|---|
| 969 |
|
|---|
| 970 | @classmethod
|
|---|
| 971 | def _ImportDatasetsToPath(cls, path, sourceDatasets, mode, progressReporter, options):
|
|---|
| 972 |
|
|---|
| 973 | # Unpack the options dictionary.
|
|---|
| 974 |
|
|---|
| 975 | useUnscaledData = 'useUnscaledData' in options and options['useUnscaledData']
|
|---|
| 976 | calculateStatistics = 'calculateStatistics' not in options or options['calculateStatistics'] # Note that default for calculateStatistics is True.
|
|---|
| 977 | buildRAT = 'buildRAT' in options and options['buildRAT']
|
|---|
| 978 | buildPyramids = 'buildPyramids' in options and options['buildPyramids'] # TODO: Should we just read the environment?
|
|---|
| 979 |
|
|---|
| 980 | if 'blockSize' in options:
|
|---|
| 981 | blockSize = options['blockSize']
|
|---|
| 982 | else:
|
|---|
| 983 | blockSize = None
|
|---|
| 984 |
|
|---|
| 985 | # Validate that the source datasets are all Grids and have
|
|---|
| 986 | # dimensions 'yx', evenly-spaced coordinates, valid data
|
|---|
| 987 | # types, and the same spatial reference, shape, coordinate
|
|---|
| 988 | # increments, corner coordinates, and data type.
|
|---|
| 989 |
|
|---|
| 990 | gdal = cls._gdal()
|
|---|
| 991 | gdal.ErrorReset()
|
|---|
| 992 |
|
|---|
| 993 | if not hasattr(ArcGISRaster, '_GDALDataTypeForNumpyDataType'):
|
|---|
| 994 | ArcGISRaster._GDALDataTypeForNumpyDataType = {u'uint8': gdal.GDT_Byte,
|
|---|
| 995 | u'int8': gdal.GDT_Byte,
|
|---|
| 996 | u'uint16': gdal.GDT_UInt16,
|
|---|
| 997 | u'int16': gdal.GDT_Int16,
|
|---|
| 998 | u'uint32': gdal.GDT_UInt32,
|
|---|
| 999 | u'int32': gdal.GDT_Int32,
|
|---|
| 1000 | u'float32': gdal.GDT_Float32,
|
|---|
| 1001 | u'float64': gdal.GDT_Float64,
|
|---|
| 1002 | u'complex32': gdal.GDT_CFloat32,
|
|---|
| 1003 | u'complex64': gdal.GDT_CFloat64}
|
|---|
| 1004 |
|
|---|
| 1005 | for dataset in sourceDatasets:
|
|---|
| 1006 | if not isinstance(dataset, Grid):
|
|---|
| 1007 | raise TypeError(_(u'Cannot import %(dn)s into ArcGIS raster "%(path)s" because it is a %(type)s, which is not a Grid. It must be a Grid to be imported into an ArcGIS raster.') % {u'dn': dataset.DisplayName, u'path': path, u'type': dataset.__class__.__name__})
|
|---|
| 1008 | if dataset.Dimensions != u'yx':
|
|---|
| 1009 | raise ValueError(_(u'Cannot import %(dn)s into ArcGIS raster "%(path)s" because it has dimensions "%(dim)s". It must have dimensions "yx" to be imported into an ArcGIS raster.') % {u'dn': dataset.DisplayName, u'path': path, u'dim': dataset.Dimensions})
|
|---|
| 1010 | if dataset.CoordDependencies != (None, None):
|
|---|
| 1011 | raise ValueError(_(u'Cannot import %(dn)s into ArcGIS raster "%(path)s" because it does not have evenly-spaced coordinates (the coordinate dependencies are %(deps)s). It must have evenly-spaced coordinates (coordinate dependencies of (None, None)) to be imported into an ArcGIS raster.') % {u'dn': dataset.DisplayName, u'path': path, u'deps': repr(dataset.CoordDependencies)})
|
|---|
| 1012 | if useUnscaledData:
|
|---|
| 1013 | if dataset.UnscaledDataType not in ArcGISRaster._GDALDataTypeForNumpyDataType.keys():
|
|---|
| 1014 | raise ValueError(_(u'Cannot import %(dn)s into ArcGIS raster "%(path)s" because it has the unscaled data type %(dt)s. To be imported into an ArcGIS raster, it must have one of the following unscaled data types: %(dts)s.') % {u'dn': dataset.DisplayName, u'path': path, u'dt': dataset.UnscaledDataType, u'dts': u', '.join(ArcGISRaster._GDALDataTypeForNumpyDataType.keys())})
|
|---|
| 1015 | elif dataset.DataType not in ArcGISRaster._GDALDataTypeForNumpyDataType.keys():
|
|---|
| 1016 | raise ValueError(_(u'Cannot import %(dn)s into ArcGIS raster "%(path)s" because it has the data type %(dt)s. To be imported into an ArcGIS raster, it must have one of the following data types: %(dts)s.') % {u'dn': dataset.DisplayName, u'path': path, u'dt': dataset.DataType, u'dts': u', '.join(ArcGISRaster._GDALDataTypeForNumpyDataType.keys())})
|
|---|
| 1017 |
|
|---|
| 1018 | for i in range(1, len(sourceDatasets)):
|
|---|
| 1019 | if not sourceDatasets[0].GetSpatialReference('obj').IsSame(sourceDatasets[i].GetSpatialReference('obj')):
|
|---|
| 1020 | raise ValueError(_(u'Cannot import both %(dn1)s and %(dn2)s into ArcGIS raster "%(path)s" because the two source datasets have different spatial references (%(sr1)s and %(sr2)s). This function requires that all of the bands of an ArcGIS raster have the same spatial reference.') % {u'dn1': sourceDatasets[0].DisplayName, u'dn2': sourceDatasets[i].DisplayName, u'path': path, u'sr1': repr(sourceDatasets[0].GetSpatialReference('wkt')), u'sr2': repr(sourceDatasets[i].GetSpatialReference('wkt'))})
|
|---|
| 1021 | if sourceDatasets[0].Shape != sourceDatasets[i].Shape:
|
|---|
| 1022 | raise ValueError(_(u'Cannot import both %(dn1)s and %(dn2)s into ArcGIS raster "%(path)s" because the two source datasets have different shapes (%(shape1)s and %(shape2)s). This function requires that all of the bands of an ArcGIS raster have the same shape.') % {u'dn1': sourceDatasets[0].DisplayName, u'dn2': sourceDatasets[i].DisplayName, u'path': path, u'shape1': repr(sourceDatasets[0].Shape), u'shape2': repr(sourceDatasets[i].Shape)})
|
|---|
| 1023 | if sourceDatasets[0].CoordIncrements != sourceDatasets[i].CoordIncrements:
|
|---|
| 1024 | raise ValueError(_(u'Cannot import both %(dn1)s and %(dn2)s into ArcGIS raster "%(path)s" because the two source datasets have different coordinate increments (%(incr1)s and %(incr2)s). This function requires that all of the bands of an ArcGIS raster have the same coordinate increments.') % {u'dn1': sourceDatasets[0].DisplayName, u'dn2': sourceDatasets[i].DisplayName, u'path': path, u'incr1': repr(sourceDatasets[0].CoordIncrements), u'incr2': repr(sourceDatasets[i].CoordIncrements)})
|
|---|
| 1025 | if sourceDatasets[0].MinCoords['x',0] != sourceDatasets[i].MinCoords['x',0]:
|
|---|
| 1026 | raise ValueError(_(u'Cannot import both %(dn1)s and %(dn2)s into ArcGIS raster "%(path)s" because the two source datasets have different minimum x coordinates (%(c1)s and %(c2)s). This function requires that all of the bands of an ArcGIS raster have the same corner coordinates.') % {u'dn1': sourceDatasets[0].DisplayName, u'dn2': sourceDatasets[i].DisplayName, u'path': path, u'c1': repr(sourceDatasets[0].MinCoords['x',0]), u'c2': repr(sourceDatasets[i].MinCoords['x',0])})
|
|---|
| 1027 | if sourceDatasets[0].MinCoords['y',0] != sourceDatasets[i].MinCoords['y',0]:
|
|---|
| 1028 | raise ValueError(_(u'Cannot import both %(dn1)s and %(dn2)s into ArcGIS raster "%(path)s" because the two source datasets have different minimum y coordinates (%(c1)s and %(c2)s). This function requires that all of the bands of an ArcGIS raster have the same corner coordinates.') % {u'dn1': sourceDatasets[0].DisplayName, u'dn2': sourceDatasets[i].DisplayName, u'path': path, u'c1': repr(sourceDatasets[0].MinCoords['y',0]), u'c2': repr(sourceDatasets[i].MinCoords['y',0])})
|
|---|
| 1029 | if useUnscaledData:
|
|---|
| 1030 | if sourceDatasets[0].UnscaledDataType != sourceDatasets[i].UnscaledDataType:
|
|---|
| 1031 | raise ValueError(_(u'Cannot import both %(dn1)s and %(dn2)s into ArcGIS raster "%(path)s" because the two source datasets have different unscaled data types (%(dt1)s and %(dt2)s). This function requires that all of the bands of an ArcGIS raster have the same data type.') % {u'dn1': sourceDatasets[0].DisplayName, u'dn2': sourceDatasets[i].DisplayName, u'path': path, u'dt1': sourceDatasets[0].UnscaledDataType, u'dt2': sourceDatasets[i].UnscaledDataType})
|
|---|
| 1032 | elif sourceDatasets[0].DataType != sourceDatasets[i].DataType:
|
|---|
| 1033 | raise ValueError(_(u'Cannot import both %(dn1)s and %(dn2)s into ArcGIS raster "%(path)s" because the two source datasets have different data types (%(dt1)s and %(dt2)s). This function requires that all of the bands of an ArcGIS raster the same data type.') % {u'dn1': sourceDatasets[0].DisplayName, u'dn2': sourceDatasets[i].DisplayName, u'path': path, u'dt1': sourceDatasets[0].DataType, u'dt2': sourceDatasets[i].DataType})
|
|---|
| 1034 |
|
|---|
| 1035 | # If the mode is 'replace' and the raster exists, delete it.
|
|---|
| 1036 |
|
|---|
| 1037 | gp = GeoprocessorManager.GetWrappedGeoprocessor()
|
|---|
| 1038 |
|
|---|
| 1039 | if mode == u'replace' and cls._ArcGISRasterExists(path):
|
|---|
| 1040 | cls._LogDebug(_(u'%(class)s: Deleting existing ArcGIS raster "%(path)s".'), {u'class': cls.__name__, u'path': path})
|
|---|
| 1041 | try:
|
|---|
| 1042 | gp.Delete_management(path)
|
|---|
| 1043 | except Exception, e:
|
|---|
| 1044 | raise RuntimeError(_(u'Failed to delete the existing ArcGIS raster "%(path)s" due to %(e)s: %(msg)s') % {u'path': path, u'e': e.__class__.__name__, u'msg': cls._Unicode(e)})
|
|---|
| 1045 |
|
|---|
| 1046 | # Otherwise, if the path is a file system path, create the
|
|---|
| 1047 | # parent directories, if they do not exist already. Use
|
|---|
| 1048 | # ArcGIS's CreateFolder_management tool, so the ArcGIS catalog
|
|---|
| 1049 | # is aware of the directories.
|
|---|
| 1050 |
|
|---|
| 1051 | elif (path[0] in ['/', '\\'] or hasattr(os.path, 'splitdrive') and os.path.splitdrive(path)[0] != '' or hasattr(os.path, 'splitunc') and os.path.splitunc(path)[0] != '') and not os.path.exists(os.path.dirname(path)):
|
|---|
| 1052 | cls._LogDebug(_(u'%(class)s: Creating directory "%(path)s".'), {u'class': cls.__name__, u'path': os.path.dirname(path)})
|
|---|
| 1053 |
|
|---|
| 1054 | if hasattr(os.path, 'splitdrive') and os.path.splitdrive(path)[0] != '':
|
|---|
| 1055 | root, subdirs = os.path.splitdrive(os.path.dirname(path))
|
|---|
| 1056 | root = root + '\\'
|
|---|
| 1057 | elif hasattr(os.path, 'splitunc') and os.path.splitunc(path)[0] != '':
|
|---|
| 1058 | root, subdirs = os.path.splitunc(os.path.dirname(path))
|
|---|
| 1059 | else:
|
|---|
| 1060 | root, subdirs = path[0], os.path.dirname(path)[1:]
|
|---|
| 1061 |
|
|---|
| 1062 | subdirsList = []
|
|---|
| 1063 | while len(subdirs) > 1:
|
|---|
| 1064 | subdirsList.insert(0, os.path.basename(subdirs))
|
|---|
| 1065 | subdirs = os.path.dirname(subdirs)
|
|---|
| 1066 |
|
|---|
| 1067 | dirToCheck = root
|
|---|
| 1068 | for subdir in subdirsList:
|
|---|
| 1069 | if not os.path.isdir(os.path.join(dirToCheck, subdir)):
|
|---|
| 1070 | try:
|
|---|
| 1071 | gp.CreateFolder_management(dirToCheck, subdir)
|
|---|
| 1072 | except Exception, e:
|
|---|
| 1073 | raise RuntimeError(_(u'Failed to create the directory "%(path)s" due to %(e)s: %(msg)s') % {u'path': os.path.join(dirToCheck, subdir), u'e': e.__class__.__name__, u'msg': cls._Unicode(e)})
|
|---|
| 1074 | dirToCheck = os.path.join(dirToCheck, subdir)
|
|---|
| 1075 |
|
|---|
| 1076 | # If the output format is ArcInfo binary grid, verify that the
|
|---|
| 1077 | # raster name conforms to ArcGIS's rules and if not, raise a
|
|---|
| 1078 | # helpful error message. (Many users make mistakes with binary
|
|---|
| 1079 | # grid names but cannot tell what went wrong from ArcGIS's
|
|---|
| 1080 | # unhelpful error messages.)
|
|---|
| 1081 |
|
|---|
| 1082 | outputIsFile = os.path.isdir(os.path.dirname(path)) and os.path.splitext(os.path.dirname(path))[1].lower() != '.gdb'
|
|---|
| 1083 | outputIsAIG = outputIsFile and u'.' not in os.path.basename(path)
|
|---|
| 1084 | if outputIsAIG:
|
|---|
| 1085 | rasterName = os.path.basename(path)
|
|---|
| 1086 | if u' ' in rasterName:
|
|---|
| 1087 | raise ValueError(_(u'Cannot create an ArcGIS raster named "%(path)s" because the raster\'s name contains a space. The names of rasters in ArcInfo Binary Grid format cannot contain spaces.') % {u'path': path})
|
|---|
| 1088 | if rasterName[0] >= u'0' and rasterName[0] <= u'9':
|
|---|
| 1089 | raise ValueError(_(u'Cannot create an ArcGIS raster named "%(path)s" because the raster\'s name starts with a number. The names of rasters in ArcInfo Binary Grid format cannot start with numbers.') % {u'path': path})
|
|---|
| 1090 | if len(sourceDatasets) > 1:
|
|---|
| 1091 | if len(rasterName) > 9:
|
|---|
| 1092 | raise ValueError(_(u'Cannot create an ArcGIS grid stack named "%(path)s" because the grid stack\'s name is longer than nine characters. The names of ArcGIS grid stacks must be no longer than nine characters.') % {u'path': path})
|
|---|
| 1093 | elif len(rasterName) > 13:
|
|---|
| 1094 | raise ValueError(_(u'Cannot create an ArcGIS raster named "%(path)s" because the raster\'s name is longer than thirteen characters. The names of rasters in ArcInfo Binary Grid format must be no longer than thirteen characters.') % {u'path': path})
|
|---|
| 1095 |
|
|---|
| 1096 | # At this point, the raster should not exist. If the
|
|---|
| 1097 | # destination is the file system (rather than a geodatabase)
|
|---|
| 1098 | # and the output format one that GDAL can create, use GDAL to
|
|---|
| 1099 | # create the raster directly at the destination path. This
|
|---|
| 1100 | # will be much faster than calling any geoprocessor functions.
|
|---|
| 1101 | #
|
|---|
| 1102 | # Do not use GDAL to calculate statistics or build pyramids.
|
|---|
| 1103 | # Even though GDAL can do it faster than the geoprocessor, we
|
|---|
| 1104 | # prefer to use the geoprocessor to maximize the compatibility
|
|---|
| 1105 | # of the output raster with ArcGIS. For example, I do not know
|
|---|
| 1106 | # how to get GDAL to build an ArcGIS-compatible histogram.
|
|---|
| 1107 | # Without the histogram ArcGIS defaults to displaying integer
|
|---|
| 1108 | # rasters using a black-to-white stretch rather than a unique
|
|---|
| 1109 | # value classifier with random colors. ArcGIS users will
|
|---|
| 1110 | # expect colors; to get the necessary histogram, we have to
|
|---|
| 1111 | # calculate statistics with the geoprocessor.
|
|---|
| 1112 |
|
|---|
| 1113 | if outputIsFile and not outputIsAIG and os.path.splitext(path)[1].lower() not in [u'.asc', u'.gif', u'.j2c', u'.j2k', u'.jp2', u'.jpc', u'.jpe', u'.jpg', u'.jpeg', u'.jpx', u'.png', u'.txt']:
|
|---|
| 1114 | cls._LogDebug(_(u'%(class)s: Creating ArcGIS raster "%(path)s" with GDAL.'), {u'class': cls.__name__, u'path': path})
|
|---|
| 1115 | GDALDataset._ImportDatasetsToPath(path, sourceDatasets, mode, None, {'useArcGISSpatialReference': True, 'useUnscaledData': useUnscaledData, 'calculateStatistics': False, 'blockSize': blockSize})
|
|---|
| 1116 | gp.RefreshCatalog(os.path.dirname(path))
|
|---|
| 1117 |
|
|---|
| 1118 | # Otherwise, create a temporary directory, write an IMG file
|
|---|
| 1119 | # to it with GDAL, and copy the IMG file to the destination
|
|---|
| 1120 | # path.
|
|---|
| 1121 |
|
|---|
| 1122 | else:
|
|---|
| 1123 | cls._LogDebug(_(u'%(class)s: Creating ArcGIS raster "%(path)s" by creating a temporary IMG file and then copying it with the geoprocessor.'), {u'class': cls.__name__, u'path': path})
|
|---|
| 1124 |
|
|---|
| 1125 | # Create a temporary directory.
|
|---|
| 1126 |
|
|---|
| 1127 | import tempfile, shutil
|
|---|
| 1128 | tempDir = tempfile.mkdtemp(prefix='GeoEco_' + cls.__name__ + '_Temp_')
|
|---|
| 1129 | cls._LogDebug(_(u'%(class)s: Created temporary directory %(dir)s.'), {u'class': cls.__name__, u'dir': tempDir})
|
|---|
| 1130 |
|
|---|
| 1131 | try:
|
|---|
| 1132 | # Write the IMG file to the temporary directory.
|
|---|
| 1133 |
|
|---|
| 1134 | tempRaster = os.path.join(tempDir, 'raster.img')
|
|---|
| 1135 | GDALDataset._ImportDatasetsToPath(tempRaster, sourceDatasets, mode, None, {'useArcGISSpatialReference': True, 'useUnscaledData': useUnscaledData, 'calculateStatistics': False, 'blockSize': blockSize})
|
|---|
| 1136 | gp.RefreshCatalog(tempDir)
|
|---|
| 1137 |
|
|---|
| 1138 | # If the destination raster is an ArcInfo ASCII grid,
|
|---|
| 1139 | # use the RasterToASCII_conversion tool to create it.
|
|---|
| 1140 |
|
|---|
| 1141 | if os.path.splitext(path)[1].lower() in ['.asc', '.txt']:
|
|---|
| 1142 | cls._LogDebug(_(u'%(class)s: Converting "%(src)s" to "%(dest)s".'), {u'class': cls.__name__, u'src': tempRaster, u'dest': path})
|
|---|
| 1143 | gp.RasterToASCII_conversion(tempRaster, path)
|
|---|
| 1144 |
|
|---|
| 1145 | # Otherwise we'll use CopyRaster_management.
|
|---|
| 1146 |
|
|---|
| 1147 | else:
|
|---|
| 1148 |
|
|---|
| 1149 | # If the destination raster is an ArcInfo binary grid
|
|---|
| 1150 | # and copying the raster to that directory will cause
|
|---|
| 1151 | # ArcGIS to crash due to a bug, create temporary
|
|---|
| 1152 | # ArcInfo binary grids that will prevent the crash.
|
|---|
| 1153 |
|
|---|
| 1154 | if outputIsAIG:
|
|---|
| 1155 | tempRasters = cls._CreateTemporaryRastersToPreventArcGISCrash(os.path.dirname(path), 1)
|
|---|
| 1156 |
|
|---|
| 1157 | # Copy the raster to the destination path. Due to the
|
|---|
| 1158 | # problems described in tickets #310 and #311, call
|
|---|
| 1159 | # Copy_mangement on ArcGIS 9.1, and
|
|---|
| 1160 | # CopyRaster_management on later versions.
|
|---|
| 1161 |
|
|---|
| 1162 | try:
|
|---|
| 1163 | cls._LogDebug(_(u'%(class)s: Copying "%(src)s" to "%(dest)s".'), {u'class': cls.__name__, u'src': tempRaster, u'dest': path})
|
|---|
| 1164 | if GeoprocessorManager.GetArcGISMajorVersion() <= 9 and GeoprocessorManager.GetArcGISMinorVersion() <= 1:
|
|---|
| 1165 | gp.Copy_Management(tempRaster, path)
|
|---|
| 1166 | else:
|
|---|
| 1167 | gp.CopyRaster_Management(tempRaster, path)
|
|---|
| 1168 |
|
|---|
| 1169 | # If we created temporary rasters to prevent an ArcGIS
|
|---|
| 1170 | # crash, delete them now.
|
|---|
| 1171 |
|
|---|
| 1172 | finally:
|
|---|
| 1173 | if outputIsAIG:
|
|---|
| 1174 | cls._DeleteTemporaryRastersThatPreventedArcGISCrash(tempRasters)
|
|---|
| 1175 |
|
|---|
| 1176 | # Delete the temporary directory.
|
|---|
| 1177 |
|
|---|
| 1178 | finally:
|
|---|
| 1179 | cls._LogDebug(_(u'%(class)s: Deleting temporary directory %(dir)s.'), {u'class': cls.__name__, u'dir': tempDir})
|
|---|
| 1180 | shutil.rmtree(tempDir, onerror=DatasetCollection._LogFailedRemoval)
|
|---|
| 1181 |
|
|---|
| 1182 | # The raster now exists at the final destination. If any of
|
|---|
| 1183 | # the additional processing steps fail, delete the raster.
|
|---|
| 1184 |
|
|---|
| 1185 | try:
|
|---|
| 1186 | # Calculate statistics.
|
|---|
| 1187 |
|
|---|
| 1188 | if calculateStatistics:
|
|---|
| 1189 | cls._LogDebug(_(u'%(class)s: Calculating statistics for ArcGIS raster "%(path)s".'), {u'class': cls.__name__, u'path': path})
|
|---|
| 1190 | gp.CalculateStatistics_management(path)
|
|---|
| 1191 |
|
|---|
| 1192 | # Build the raster attribute table. This can only be done for
|
|---|
| 1193 | # single-band integer rasters.
|
|---|
| 1194 |
|
|---|
| 1195 | if buildRAT and len(sourceDatasets) == 1 and (useUnscaledData and sourceDatasets[0].UnscaledDataType in [u'int8', u'uint8', u'int16', u'uint16', u'int32', u'uint32'] or not useUnscaledData and sourceDatasets[0].DataType in [u'int8', u'uint8', u'int16', u'uint16', u'int32', u'uint32']):
|
|---|
| 1196 | cls._LogDebug(_(u'%(class)s: Building a raster attribute table for ArcGIS raster "%(path)s".'), {u'class': cls.__name__, u'path': path})
|
|---|
| 1197 | gp.BuildRasterAttributeTable_management(path)
|
|---|
| 1198 |
|
|---|
| 1199 | # Build pyramids.
|
|---|
| 1200 |
|
|---|
| 1201 | if buildPyramids:
|
|---|
| 1202 | cls._LogDebug(_(u'%(class)s: Building pyramids for ArcGIS raster "%(path)s".'), {u'class': cls.__name__, u'path': path})
|
|---|
| 1203 | gp.BuildPyramids_management(path)
|
|---|
| 1204 |
|
|---|
| 1205 | except:
|
|---|
| 1206 | cls._LogDebug(_(u'%(class)s: Deleting partially-created ArcGIS raster "%(path)s" because an error was raised during creation.'), {u'class': cls.__name__, u'path': path})
|
|---|
| 1207 | try:
|
|---|
| 1208 | gp.Delete_management(path)
|
|---|
| 1209 | except Exception, e:
|
|---|
| 1210 | cls._LogWarning(_(u'Failed to delete the partially-created ArcGIS raster "%(path)s" due to %(e)s: %(msg)s') % {u'path': path, u'e': e.__class__.__name__, u'msg': cls._Unicode(e)})
|
|---|
| 1211 | raise
|
|---|
| 1212 |
|
|---|
| 1213 | # Report progress.
|
|---|
| 1214 |
|
|---|
| 1215 | if progressReporter is not None:
|
|---|
| 1216 | progressReporter.ReportProgress()
|
|---|
| 1217 |
|
|---|
| 1218 | @classmethod
|
|---|
| 1219 | def _CreateTemporaryRastersToPreventArcGISCrash(cls, directory, rastersToAdd):
|
|---|
| 1220 |
|
|---|
| 1221 | # See http://forums.esri.com/Thread.asp?c=93&f=1729&t=196716&mc=0 for a
|
|---|
| 1222 | # complete description of the problem that this function is designed to
|
|---|
| 1223 | # work around.
|
|---|
| 1224 |
|
|---|
| 1225 | # First determine the maximum entry in the arc.dir file.
|
|---|
| 1226 |
|
|---|
| 1227 | import glob
|
|---|
| 1228 |
|
|---|
| 1229 | infoEntries = glob.glob(os.path.join(directory, "info", "arc[0-9][0-9][0-9][0-9].*"))
|
|---|
| 1230 | if len(infoEntries) <= 0:
|
|---|
| 1231 | return []
|
|---|
| 1232 |
|
|---|
| 1233 | infoEntries.sort()
|
|---|
| 1234 | maxInfoEntry = int(os.path.basename(infoEntries[len(infoEntries)-1])[3:7])
|
|---|
| 1235 |
|
|---|
| 1236 | # If adding rastersToAdd integer rasters to the directory
|
|---|
| 1237 | # would not cause the maximum entry to cross a multiple of
|
|---|
| 1238 | # 1024, then it would not cause Arc to crash, and we can just
|
|---|
| 1239 | # return.
|
|---|
| 1240 |
|
|---|
| 1241 | maxInfoEntry = divmod(maxInfoEntry, 1024)[1]
|
|---|
| 1242 | if maxInfoEntry + rastersToAdd * 3 < 1023: # Each integer raster requires 3 entries in arc.dir (floating point rasters only require 2)
|
|---|
| 1243 | return []
|
|---|
| 1244 |
|
|---|
| 1245 | # If the current entry is only 1 less than the 1024 boundary
|
|---|
| 1246 | # (actually 1 less than 1023 since the entries are 0-based),
|
|---|
| 1247 | # we can just return because all rasters require at least two
|
|---|
| 1248 | # entries, so it is guaranteed that we would not hit the magic
|
|---|
| 1249 | # crashing number (which is a multiple of 1024 - 1, again
|
|---|
| 1250 | # since the entries are 0-based).
|
|---|
| 1251 |
|
|---|
| 1252 | if maxInfoEntry == 1023 - 1:
|
|---|
| 1253 | return []
|
|---|
| 1254 |
|
|---|
| 1255 | # We're going to cross a multiple of 1024 and there could be a
|
|---|
| 1256 | # crash. Create enough temporary rasters to safely take us
|
|---|
| 1257 | # just over the boundary.
|
|---|
| 1258 |
|
|---|
| 1259 | import GeoEco
|
|---|
| 1260 |
|
|---|
| 1261 | cls._LogDebug(_(u'%(class)s: Creating temporary rasters in %(dir)s to prevent ArcGIS from crashing when %(rasters)i ArcInfo binary grids are added to that directory. The temporary rasters begin with \"tmp\" and should be deleted automatically after the real rasters are added. If they are not, you can delete them manually.') % {u'class': cls.__name__, u'dir' : directory, u'rasters' : rastersToAdd})
|
|---|
| 1262 |
|
|---|
| 1263 | entriesNeeded = 1024 - maxInfoEntry
|
|---|
| 1264 | rastersCreated = []
|
|---|
| 1265 | gp = GeoprocessorManager.GetWrappedGeoprocessor()
|
|---|
| 1266 |
|
|---|
| 1267 | while entriesNeeded > 0:
|
|---|
| 1268 | raster = None
|
|---|
| 1269 | while raster is None or os.path.exists(raster):
|
|---|
| 1270 | raster = os.path.join(directory, "tmp%08X" % random.randint(0, sys.maxint))
|
|---|
| 1271 |
|
|---|
| 1272 | # If creating an integer raster would not crash Arc, do
|
|---|
| 1273 | # it. Otherwise create a floating-point raster. Due to the
|
|---|
| 1274 | # problems described in tickets #310 and #311, call
|
|---|
| 1275 | # Copy_mangement on ArcGIS 9.1 and 9.2, and
|
|---|
| 1276 | # CopyRaster_management on ArcGIS 9.3.
|
|---|
| 1277 |
|
|---|
| 1278 | if entriesNeeded != 4:
|
|---|
| 1279 | dummyRaster = os.path.join(os.path.dirname(sys.modules[u'GeoEco'].__file__), u'ArcGISToolbox', u'Rasters', u'dummyint')
|
|---|
| 1280 | entriesNeeded -= 3
|
|---|
| 1281 | else:
|
|---|
| 1282 | dummyRaster = os.path.join(os.path.dirname(sys.modules[u'GeoEco'].__file__), u'ArcGISToolbox', u'Rasters', u'dummyfloat')
|
|---|
| 1283 | entriesNeeded -= 2
|
|---|
| 1284 |
|
|---|
| 1285 | if GeoprocessorManager.GetArcGISMajorVersion() <= 9 and GeoprocessorManager.GetArcGISMinorVersion() <= 2:
|
|---|
| 1286 | gp.Copy_Management(dummyRaster, raster)
|
|---|
| 1287 | else:
|
|---|
| 1288 | gp.CopyRaster_Management(dummyRaster, raster)
|
|---|
| 1289 |
|
|---|
| 1290 | rastersCreated.append(raster)
|
|---|
| 1291 |
|
|---|
| 1292 | cls._LogDebug(_(u'%(class)s: %(count)i temporary rasters created.') % {u'class': cls.__name__, u'count': len(rastersCreated)})
|
|---|
| 1293 |
|
|---|
| 1294 | # Return the list of rasters we created.
|
|---|
| 1295 |
|
|---|
| 1296 | return rastersCreated
|
|---|
| 1297 |
|
|---|
| 1298 | @classmethod
|
|---|
| 1299 | def _DeleteTemporaryRastersThatPreventedArcGISCrash(cls, rasters):
|
|---|
| 1300 | if len(rasters) > 0:
|
|---|
| 1301 | cls._LogDebug(_(u'%(class)s: Deleting temporary rasters from %(dir)s.') % {u'class': cls.__name__, u'dir' : directory})
|
|---|
| 1302 | gp = GeoprocessorManager.GetWrappedGeoprocessor()
|
|---|
| 1303 | for raster in rasters:
|
|---|
| 1304 | gp.Delete(raster)
|
|---|
| 1305 |
|
|---|
| 1306 |
|
|---|
| 1307 | class ArcGISRasterBand(Grid):
|
|---|
| 1308 | __doc__ = DynamicDocString()
|
|---|
| 1309 |
|
|---|
| 1310 | def _GetBand(self):
|
|---|
| 1311 | return self._Band
|
|---|
| 1312 |
|
|---|
| 1313 | Band = property(_GetBand, doc=DynamicDocString())
|
|---|
| 1314 |
|
|---|
| 1315 | def __init__(self, arcGISRaster, band, queryableAttributeValues=None, lazyPropertyValues=None):
|
|---|
| 1316 | # TODO: self.__doc__.Obj.ValidateMethodInvocation()
|
|---|
| 1317 |
|
|---|
| 1318 | # Initialize our properties.
|
|---|
| 1319 |
|
|---|
| 1320 | self._Band = band
|
|---|
| 1321 | self._NoDataValueRetrievalFailed = False
|
|---|
| 1322 |
|
|---|
| 1323 | # Assign values to known queryable attributes and lazy
|
|---|
| 1324 | # properties.
|
|---|
| 1325 |
|
|---|
| 1326 | qav = {}
|
|---|
| 1327 | if queryableAttributeValues is not None:
|
|---|
| 1328 | qav.update(queryableAttributeValues)
|
|---|
| 1329 |
|
|---|
| 1330 | qav[u'Band'] = band
|
|---|
| 1331 |
|
|---|
| 1332 | lpv = {}
|
|---|
| 1333 | if lazyPropertyValues is not None:
|
|---|
| 1334 | lpv.update(lazyPropertyValues)
|
|---|
| 1335 |
|
|---|
| 1336 | lpv['Dimensions'] = u'yx'
|
|---|
| 1337 | lpv['PhysicalDimensions'] = u'yx'
|
|---|
| 1338 | lpv['PhysicalDimensionsFlipped'] = (True, False) # TODO: This is correct when we read the raster with GDAL, which we do with ArcGIS 9.x, but with ArcGIS 10.x we may ultimately read it with Arc's own numpy API. What does it do?
|
|---|
| 1339 | lpv['CoordDependencies'] = (None, None)
|
|---|
| 1340 |
|
|---|
| 1341 | if arcGISRaster.GetLazyPropertyValue('TIncrementUnit', False) is None:
|
|---|
| 1342 | lpv['TIncrementUnit'] = None
|
|---|
| 1343 |
|
|---|
| 1344 | if arcGISRaster.GetLazyPropertyValue('TSemiRegularity', False) is None:
|
|---|
| 1345 | lpv['TSemiRegularity'] = None
|
|---|
| 1346 |
|
|---|
| 1347 | if arcGISRaster.GetLazyPropertyValue('TCountPerSemiRegularPeriod', False) is None:
|
|---|
| 1348 | lpv['TCountPerSemiRegularPeriod'] = None
|
|---|
| 1349 |
|
|---|
| 1350 | if arcGISRaster.GetLazyPropertyValue('TCornerCoordType', False) is None:
|
|---|
| 1351 | lpv['TCornerCoordType'] = None
|
|---|
| 1352 |
|
|---|
| 1353 | # Initialize the base class.
|
|---|
| 1354 |
|
|---|
| 1355 | super(ArcGISRasterBand, self).__init__(arcGISRaster, queryableAttributeValues=qav, lazyPropertyValues=lpv)
|
|---|
| 1356 |
|
|---|
| 1357 | def _Close(self):
|
|---|
| 1358 | super(ArcGISRasterBand, self)._Close()
|
|---|
| 1359 | if hasattr(self, 'ParentCollection') and self.ParentCollection is not None:
|
|---|
| 1360 | self.ParentCollection.Close()
|
|---|
| 1361 |
|
|---|
| 1362 | def _GetDisplayName(self):
|
|---|
| 1363 | return _(u'band %(band)i of %(dn)s') % {u'band': self._Band, u'dn': self.ParentCollection.DisplayName}
|
|---|
| 1364 |
|
|---|
| 1365 | def _GetLazyPropertyPhysicalValue(self, name):
|
|---|
| 1366 |
|
|---|
| 1367 | # If it is not a known lazy property, return None.
|
|---|
| 1368 |
|
|---|
| 1369 | if name not in ['SpatialReference', 'Shape', 'CoordIncrements', 'CornerCoords', 'UnscaledDataType', 'UnscaledNoDataValue']:
|
|---|
| 1370 | return None
|
|---|
| 1371 |
|
|---|
| 1372 | # The spatial reference is maintained by ArcGIS on a
|
|---|
| 1373 | # per-raster basis, not a per-band basis. If the caller wants
|
|---|
| 1374 | # that, retrieve its value from our parent ArcGISRaster.
|
|---|
| 1375 |
|
|---|
| 1376 | if name == 'SpatialReference':
|
|---|
| 1377 | return self.ParentCollection.GetLazyPropertyValue(name)
|
|---|
| 1378 |
|
|---|
| 1379 | # If the caller wants any of the other properties, get them
|
|---|
| 1380 | # from the geoprocessor's Describe object, unless the caller
|
|---|
| 1381 | # wants the UnscaledNoDataValue and we previously tried to get
|
|---|
| 1382 | # it from the Describe object and failed.
|
|---|
| 1383 |
|
|---|
| 1384 | gp = GeoprocessorManager.GetWrappedGeoprocessor()
|
|---|
| 1385 |
|
|---|
| 1386 | if self.ParentCollection.ParentCollection is None:
|
|---|
| 1387 | path = self.ParentCollection.Path
|
|---|
| 1388 | else:
|
|---|
| 1389 | path = os.path.join(self.ParentCollection.ParentCollection.Path, self.ParentCollection.Path)
|
|---|
| 1390 |
|
|---|
| 1391 | if self.ParentCollection.GetLazyPropertyValue('Bands') > 1:
|
|---|
| 1392 | path = os.path.join(path, u'Band_' + unicode(self._Band))
|
|---|
| 1393 |
|
|---|
| 1394 | if name in ['Shape', 'CoordIncrements', 'CornerCoords', 'UnscaledDataType'] or name == 'UnscaledNoDataValue' and not self._NoDataValueRetrievalFailed:
|
|---|
| 1395 | d = gp.Describe(path)
|
|---|
| 1396 |
|
|---|
| 1397 | self.SetLazyPropertyValue('Shape', (d.Height, d.Width))
|
|---|
| 1398 | self.SetLazyPropertyValue('CoordIncrements', (d.MeanCellHeight, d.MeanCellWidth))
|
|---|
| 1399 | self.SetLazyPropertyValue('CornerCoords', (float(d.Extent.split()[1]) + d.MeanCellHeight / 2.0, float(d.Extent.split()[0]) + d.MeanCellWidth / 2.0))
|
|---|
| 1400 |
|
|---|
| 1401 | pixelType = d.PixelType.upper()
|
|---|
| 1402 | if pixelType in ['U1', 'U2', 'U4', 'U8', 'UCHAR']:
|
|---|
| 1403 | self.SetLazyPropertyValue('UnscaledDataType', u'uint8')
|
|---|
| 1404 | elif pixelType in ['S8', 'CHAR']:
|
|---|
| 1405 | self.SetLazyPropertyValue('UnscaledDataType', u'int8')
|
|---|
| 1406 | elif pixelType in ['U16', 'USHORT']:
|
|---|
| 1407 | self.SetLazyPropertyValue('UnscaledDataType', u'uint16')
|
|---|
| 1408 | elif pixelType in ['S16', 'SHORT']:
|
|---|
| 1409 | self.SetLazyPropertyValue('UnscaledDataType', u'int16')
|
|---|
| 1410 | elif pixelType in ['U32', 'ULONG']:
|
|---|
| 1411 | self.SetLazyPropertyValue('UnscaledDataType', u'uint32')
|
|---|
| 1412 | elif pixelType in ['S32', 'LONG']:
|
|---|
| 1413 | self.SetLazyPropertyValue('UnscaledDataType', u'int32')
|
|---|
| 1414 | elif pixelType in ['F32', 'FLOAT']:
|
|---|
| 1415 | self.SetLazyPropertyValue('UnscaledDataType', u'float32')
|
|---|
| 1416 | elif pixelType in ['F64', 'DOUBLE']:
|
|---|
| 1417 | self.SetLazyPropertyValue('UnscaledDataType', u'float64')
|
|---|
| 1418 | else:
|
|---|
| 1419 | raise ValueError(_(u'%(dn)s cannot be opened because it has an unknown ArcGIS PixelType "%(pt)s".') % {u'pt': d.PixelType})
|
|---|
| 1420 |
|
|---|
| 1421 | # ArcGIS 9.3.1 and possibly other versions sometimes raise
|
|---|
| 1422 | # the following exception when you try to access the
|
|---|
| 1423 | # NoDataValue attribute of a Describe object for a raster:
|
|---|
| 1424 | #
|
|---|
| 1425 | # RuntimeError: DescribeData: Get attribute NoDataValue does not exist
|
|---|
| 1426 | #
|
|---|
| 1427 | # It can happen whether or not the raster has a NoData
|
|---|
| 1428 | # value. I have discerned no reliable pattern to the
|
|---|
| 1429 | # failure and do not know how to work around it using
|
|---|
| 1430 | # ArcGIS. Instead I extract a small portion of the raster
|
|---|
| 1431 | # to an IMG file and read the NoData value with GDAL. See
|
|---|
| 1432 | # below.
|
|---|
| 1433 |
|
|---|
| 1434 | try:
|
|---|
| 1435 | noDataValue = d.NoDataValue
|
|---|
| 1436 | except:
|
|---|
| 1437 | self._LogDebug(_(u'%(class)s 0x%(id)08X: Warning: Failed to retrieve the NoDataValue from the geoprocessor Describe object for %(dn)s. This is a known bug in ArcGIS. If the NoDataValue is needed, it will be obtained exporting part of the raster to a temporary IMG file and reading it with GDAL.'), {u'class': self.__class__.__name__, u'id': id(self), u'dn': self.DisplayName})
|
|---|
| 1438 | self._NoDataValueRetrievalFailed = True
|
|---|
| 1439 | else:
|
|---|
| 1440 | self.SetLazyPropertyValue('UnscaledNoDataValue', noDataValue)
|
|---|
| 1441 |
|
|---|
| 1442 | # Log a debug message with the properties of the band.
|
|---|
| 1443 |
|
|---|
| 1444 | if not self._NoDataValueRetrievalFailed:
|
|---|
| 1445 | self._LogDebug(_(u'%(class)s 0x%(id)08X: Retrieved lazy properties of %(dn)s: Dimensions=%(Dimensions)s, PhysicalDimensions=%(PhysicalDimensions)s, PhysicalDimensionsFlipped=%(PhysicalDimensionsFlipped)s, Shape=%(Shape)s, CoordIncrements=%(CoordIncrements)s, CornerCoords=%(CornerCoords)s, UnscaledDataType=%(UnscaledDataType)s, UnscaledNoDataValue=%(UnscaledNoDataValue)s.'),
|
|---|
| 1446 | {u'class': self.__class__.__name__,
|
|---|
| 1447 | u'id': id(self),
|
|---|
| 1448 | u'dn': self.DisplayName,
|
|---|
| 1449 | u'Dimensions': self.GetLazyPropertyValue('Dimensions'),
|
|---|
| 1450 | u'PhysicalDimensions': self.GetLazyPropertyValue('PhysicalDimensions'),
|
|---|
| 1451 | u'PhysicalDimensionsFlipped': repr(self.GetLazyPropertyValue('PhysicalDimensionsFlipped')),
|
|---|
| 1452 | u'Shape': repr(self.GetLazyPropertyValue('Shape')),
|
|---|
| 1453 | u'CoordIncrements': repr(self.GetLazyPropertyValue('CoordIncrements')),
|
|---|
| 1454 | u'CornerCoords': self.GetLazyPropertyValue('CornerCoords'),
|
|---|
| 1455 | u'UnscaledDataType': repr(self.GetLazyPropertyValue('UnscaledDataType')),
|
|---|
| 1456 | u'UnscaledNoDataValue': repr(self.GetLazyPropertyValue('UnscaledNoDataValue'))})
|
|---|
| 1457 | else:
|
|---|
| 1458 | self._LogDebug(_(u'%(class)s 0x%(id)08X: Retrieved lazy properties of %(dn)s: Dimensions=%(Dimensions)s, PhysicalDimensions=%(PhysicalDimensions)s, PhysicalDimensionsFlipped=%(PhysicalDimensionsFlipped)s, Shape=%(Shape)s, CoordIncrements=%(CoordIncrements)s, CornerCoords=%(CornerCoords)s, UnscaledDataType=%(UnscaledDataType)s.'),
|
|---|
| 1459 | {u'class': self.__class__.__name__,
|
|---|
| 1460 | u'id': id(self),
|
|---|
| 1461 | u'dn': self.DisplayName,
|
|---|
| 1462 | u'Dimensions': self.GetLazyPropertyValue('Dimensions'),
|
|---|
| 1463 | u'PhysicalDimensions': self.GetLazyPropertyValue('PhysicalDimensions'),
|
|---|
| 1464 | u'PhysicalDimensionsFlipped': repr(self.GetLazyPropertyValue('PhysicalDimensionsFlipped')),
|
|---|
| 1465 | u'Shape': repr(self.GetLazyPropertyValue('Shape')),
|
|---|
| 1466 | u'CoordIncrements': repr(self.GetLazyPropertyValue('CoordIncrements')),
|
|---|
| 1467 | u'CornerCoords': self.GetLazyPropertyValue('CornerCoords'),
|
|---|
| 1468 | u'UnscaledDataType': repr(self.GetLazyPropertyValue('UnscaledDataType'))})
|
|---|
| 1469 |
|
|---|
| 1470 | # If the caller is requesting the NoDataValue property and we
|
|---|
| 1471 | # failed to retrieve it from the Describe object, we must
|
|---|
| 1472 | # retrieve it with GDAL instead. If we already opened a
|
|---|
| 1473 | # GDALDataset for this raster, just read the NoDataValue
|
|---|
| 1474 | # directly from it. Otherwise create a temporary directory,
|
|---|
| 1475 | # export a small portion of the raster to an IMG file, read
|
|---|
| 1476 | # the NoData value with GDAL, and delete the temporary
|
|---|
| 1477 | # directory.
|
|---|
| 1478 |
|
|---|
| 1479 | if name == 'UnscaledNoDataValue' and self._NoDataValueRetrievalFailed:
|
|---|
| 1480 | if self.ParentCollection._GDALDataset is None:
|
|---|
| 1481 | import shutil, tempfile
|
|---|
| 1482 | tempDir = tempfile.mkdtemp(prefix='GeoEco_Temp_')
|
|---|
| 1483 | self._LogDebug(_(u'%(class)s 0x%(id)08X: Created temporary directory %(dir)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'dir': tempDir})
|
|---|
| 1484 |
|
|---|
| 1485 | try:
|
|---|
| 1486 | tempRaster = os.path.join(tempDir, u'raster')
|
|---|
| 1487 | extent = u'%g %g %g %g' % (self.MinCoords['x',0], self.MinCoords['y',0], self.MaxCoords['x', min(2, self.Shape[1]-1)], self.MaxCoords['y', min(2, self.Shape[0]-1)])
|
|---|
| 1488 |
|
|---|
| 1489 | self._LogDebug(_(u'%(class)s 0x%(id)08X: Clipping %(dn)s to "%(extent)s", output "%(dest)s".'), {u'class': self.__class__.__name__, u'id': id(self), u'dn': self.DisplayName, u'extent': extent, u'dest': tempRaster})
|
|---|
| 1490 |
|
|---|
| 1491 | gp.Clip_management(path, extent, tempRaster)
|
|---|
| 1492 |
|
|---|
| 1493 | self._LogDebug(_(u'%(class)s 0x%(id)08X: Getting the NoDataValue of temporary raster "%(tempRaster)s" with GDAL.'), {u'class': self.__class__.__name__, u'id': id(self), u'tempRaster': tempRaster})
|
|---|
| 1494 |
|
|---|
| 1495 | gdal = self._gdal()
|
|---|
| 1496 | gdal.ErrorReset()
|
|---|
| 1497 | gdalconst = self._gdalconst()
|
|---|
| 1498 |
|
|---|
| 1499 | try:
|
|---|
| 1500 | gdalDataset = gdal.Open(self._Str(tempRaster), gdalconst.GA_ReadOnly)
|
|---|
| 1501 | except Exception, e:
|
|---|
| 1502 | gdal.ErrorReset()
|
|---|
| 1503 | raise RuntimeError(_(u'Temporary raster "%(tempRaster)s" could not be opened with the Geospatial Data Abstraction Library (GDAL). This was being done to work around a bug in ArcGIS that prevents us from obtaining the No Data value of %(dn)s. Because the workaround failed, the No Data value cannot be determined. Detailed error information: gdal.Open() reported %(e)s: %(msg)s.') % {u'tempRaster': tempRaster, u'dn': self.DisplayName, u'e': e.__class__.__name__, u'msg': self._Str(e)})
|
|---|
| 1504 | try:
|
|---|
| 1505 | try:
|
|---|
| 1506 | band = gdalDataset.GetRasterBand(1)
|
|---|
| 1507 | except Exception, e:
|
|---|
| 1508 | self._gdal().ErrorReset()
|
|---|
| 1509 | raise RuntimeError(_(u'Could not open band %(band)i of temporary raster "%(tempRaster)s" with the Geospatial Data Abstraction Library (GDAL). This was being done to work around a bug in ArcGIS that prevents us from obtaining the No Data value of %(dn)s. Because the workaround failed, the No Data value cannot be determined. Detailed error information: dataset.GetRasterBand(%(band)i) reported %(e)s: %(msg)s.') % {u'tempRaster': tempRaster, u'dn': self.DisplayName, u'band': self._Band, u'e': e.__class__.__name__, u'msg': self._Unicode(e)})
|
|---|
| 1510 | try:
|
|---|
| 1511 | noDataValue = band.GetNoDataValue()
|
|---|
| 1512 | finally:
|
|---|
| 1513 | del band
|
|---|
| 1514 | finally:
|
|---|
| 1515 | del gdalDataset
|
|---|
| 1516 | finally:
|
|---|
| 1517 | self._LogDebug(_(u'%(class)s 0x%(id)08X: Deleting temporary directory %(dir)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'dir': tempDir})
|
|---|
| 1518 | shutil.rmtree(tempDir, onerror=DatasetCollection._LogFailedRemoval)
|
|---|
| 1519 |
|
|---|
| 1520 | else:
|
|---|
| 1521 | band = self.ParentCollection._GDALDataset._OpenBand(self.Band)
|
|---|
| 1522 | try:
|
|---|
| 1523 | noDataValue = band.GetNoDataValue()
|
|---|
| 1524 | finally:
|
|---|
| 1525 | del band
|
|---|
| 1526 |
|
|---|
| 1527 | if noDataValue is not None and self.GetLazyPropertyValue('UnscaledDataType') in [u'int8', u'uint8', u'int16', u'uint16', u'int32', u'uint32']:
|
|---|
| 1528 | noDataValue = int(noDataValue)
|
|---|
| 1529 |
|
|---|
| 1530 | self._LogDebug(_(u'%(class)s 0x%(id)08X: Retrieved lazy properties of %(dn)s: UnscaledNoDataValue=%(UnscaledNoDataValue)s.'), {u'class': self.__class__.__name__, u'id': id(self), u'dn': self.DisplayName, u'UnscaledNoDataValue': repr(noDataValue)})
|
|---|
| 1531 |
|
|---|
| 1532 | return noDataValue
|
|---|
| 1533 |
|
|---|
| 1534 | # Return the value of the requested property.
|
|---|
| 1535 |
|
|---|
| 1536 | return self.GetLazyPropertyValue(name)
|
|---|
| 1537 |
|
|---|
| 1538 | def _ReadNumpyArray(self, sliceList):
|
|---|
| 1539 |
|
|---|
| 1540 | # Instantiate a GDALDataset for the raster.
|
|---|
| 1541 |
|
|---|
| 1542 | dataset = self.ParentCollection._InstantiateGDALDataset()
|
|---|
| 1543 |
|
|---|
| 1544 | # The following code is copied from
|
|---|
| 1545 | # GDALRasterBand._ReadNumpyArray(). Rather than instantiate a
|
|---|
| 1546 | # GDALRasterBand simply to be able to call that function, I
|
|---|
| 1547 | # just copied the code. This is a bit dodgy from an
|
|---|
| 1548 | # encapsulation and maintenance point of view but the code is
|
|---|
| 1549 | # so simple that I consider it a reasonable tradeoff.
|
|---|
| 1550 | #
|
|---|
| 1551 | # Open the dataset and retrieve the band object.
|
|---|
| 1552 |
|
|---|
| 1553 | band = dataset._OpenBand(self.Band)
|
|---|
| 1554 | try:
|
|---|
| 1555 |
|
|---|
| 1556 | # Get the data. Note that sliceList[0] contains the y
|
|---|
| 1557 | # indices and sliceList[1] contains the x indices.
|
|---|
| 1558 |
|
|---|
| 1559 | xoff = sliceList[1].start
|
|---|
| 1560 | yoff = sliceList[0].start
|
|---|
| 1561 | win_xsize = sliceList[1].stop - sliceList[1].start
|
|---|
| 1562 | win_ysize = sliceList[0].stop - sliceList[0].start
|
|---|
| 1563 |
|
|---|
| 1564 | self._LogDebug(_(u'%(class)s 0x%(id)08X: Band %(Band)i: Reading with GDAL a block of %(win_xsize)i columns by %(win_ysize)i rows at offsets x=%(xoff)i, y=%(yoff)i.') % {u'class': self.__class__.__name__, u'id': id(self), u'Band': self.Band, u'xoff': xoff, u'yoff': yoff, u'win_xsize': win_xsize, u'win_ysize': win_ysize})
|
|---|
| 1565 |
|
|---|
| 1566 | try:
|
|---|
| 1567 | data = band.ReadAsArray(xoff, yoff, win_xsize, win_ysize)
|
|---|
| 1568 | except Exception, e:
|
|---|
| 1569 | self._gdal().ErrorReset()
|
|---|
| 1570 | raise RuntimeError(_(u'Failed to retrieve a block of data of %(win_xsize)i columns by %(win_ysize)i rows at offsets x=%(xoff)i, y=%(yoff)i from band %(band)i of %(dn)s (a local copy of %(dn2)s) with the Geospatial Data Abstraction Library (GDAL). Verify that the dataset exists, is accessible, and has the expected dimensions. Detailed error information: band.ReadAsArray(%(xoff)i, %(yoff)i, %(win_xsize)i, %(win_ysize)i) reported %(e)s: %(msg)s.') % {u'Band': self.Band, u'dn': dataset.DisplayName, u'dn2': self.ParentCollection.DisplayName, u'xoff': xoff, u'yoff': yoff, u'win_xsize': win_xsize, u'win_ysize': win_ysize, u'e': e.__class__.__name__, u'msg': self._Unicode(e)})
|
|---|
| 1571 |
|
|---|
| 1572 | # Return the data along with the NoData value that GDAL is
|
|---|
| 1573 | # using. Note that the data type and NoData value may not
|
|---|
| 1574 | # be what our UnscaledDataType and UnscaledNoDataValue
|
|---|
| 1575 | # properties call for. If they are different, our caller
|
|---|
| 1576 | # (Grid._ReadData) is responsible for handling it.
|
|---|
| 1577 |
|
|---|
| 1578 | return data, band.GetNoDataValue()
|
|---|
| 1579 |
|
|---|
| 1580 | finally:
|
|---|
| 1581 | del band
|
|---|
| 1582 |
|
|---|
| 1583 | # Return the array.
|
|---|
| 1584 |
|
|---|
| 1585 | return data
|
|---|
| 1586 |
|
|---|
| 1587 | @classmethod
|
|---|
| 1588 | def ConstructFromArcGISPath(cls, path, decompressedFileToReturn=None, queryableAttributeValues=None, lazyPropertyValues=None, cacheDirectory=None):
|
|---|
| 1589 | # TODO: cls.__doc__.Obj.ValidateMethodInvocation()
|
|---|
| 1590 |
|
|---|
| 1591 | # Get the geoprocessor Describe object's DataType for the
|
|---|
| 1592 | # path. If it is a RasterBand, instantiate an ArcGISRaster for
|
|---|
| 1593 | # its parent and then query it for the ArcGISRasterBand that
|
|---|
| 1594 | # has the specified band number.
|
|---|
| 1595 |
|
|---|
| 1596 | d = GeoprocessorManager.GetWrappedGeoprocessor().Describe(path)
|
|---|
| 1597 | dataType = d.DataType.lower()
|
|---|
| 1598 | if dataType == 'rasterband':
|
|---|
| 1599 | arcGISRaster = ArcGISRaster(d.Path, decompressedFileToReturn=decompressedFileToReturn, queryableAttributeValues=queryableAttributeValues, lazyPropertyValues=lazyPropertyValues, cacheDirectory=cacheDirectory)
|
|---|
| 1600 | return arcGISRaster.QueryDatasets(u'Band = ' + str(int(d.Name.split('_')[1])), reportProgress=False)[0]
|
|---|
| 1601 |
|
|---|
| 1602 | # If it is a RasterDataset, RasterLayer, or File (which could
|
|---|
| 1603 | # be a compressed raster), instantiate an ArcGISRaster for it
|
|---|
| 1604 | # and query it for the first ArcGISRasterBand. If it has
|
|---|
| 1605 | # multiple bands, report a warning.
|
|---|
| 1606 |
|
|---|
| 1607 | if dataType in ['rasterdataset', 'rasterlayer', 'file']:
|
|---|
| 1608 | arcGISRaster = ArcGISRaster(path, decompressedFileToReturn=decompressedFileToReturn, queryableAttributeValues=queryableAttributeValues, lazyPropertyValues=lazyPropertyValues, cacheDirectory=cacheDirectory)
|
|---|
| 1609 | if d.BandCount > 1:
|
|---|
| 1610 | cls._LogWarning(_(u'%(dn)s has %(bands)i bands. The first band will be used.') % {u'dn': arcGISRaster.DisplayName, u'bands': d.BandCount})
|
|---|
| 1611 | return arcGISRaster.QueryDatasets(u'Band = 1', reportProgress=False)[0]
|
|---|
| 1612 |
|
|---|
| 1613 | # If we got to here, we do not know how to open it.
|
|---|
| 1614 |
|
|---|
| 1615 | raise ValueError(_(u'Cannot open "%(path)s" as an ArcGIS raster band, raster dataset, or raster layer. ArcGIS reports that it is a "%(dataType)".') % {u'path': path, u'dataType': d.DataType})
|
|---|
| 1616 |
|
|---|
| 1617 |
|
|---|
| 1618 | class ArcGISCopyableTable(object):
|
|---|
| 1619 | __doc__ = DynamicDocString()
|
|---|
| 1620 |
|
|---|
| 1621 | def GetArcGISCopyablePath(self):
|
|---|
| 1622 | raise NotImplementedError(_(u'The GetArcGISCopyablePath method of class %s has not been implemented.') % self.__class__.__name__)
|
|---|
| 1623 |
|
|---|
| 1624 |
|
|---|
| 1625 | class ArcGISTable(Table, ArcGISCopyableTable):
|
|---|
| 1626 | __doc__ = DynamicDocString()
|
|---|
| 1627 |
|
|---|
| 1628 | def _GetPath(self):
|
|---|
| 1629 | return self._Path
|
|---|
| 1630 |
|
|---|
| 1631 | Path = property(_GetPath, doc=DynamicDocString())
|
|---|
| 1632 |
|
|---|
| 1633 | def _GetArcGISDataType(self):
|
|---|
| 1634 | return self.GetLazyPropertyValue('ArcGISDataType')
|
|---|
| 1635 |
|
|---|
| 1636 | ArcGISDataType = property(_GetArcGISDataType, doc=DynamicDocString())
|
|---|
| 1637 |
|
|---|
| 1638 | def _GetArcGISPhysicalDataType(self):
|
|---|
| 1639 | return self.GetLazyPropertyValue('ArcGISPhysicalDataType')
|
|---|
| 1640 |
|
|---|
| 1641 | ArcGISPhysicalDataType = property(_GetArcGISPhysicalDataType, doc=DynamicDocString())
|
|---|
| 1642 |
|
|---|
| 1643 | def _GetAutoDeleteFieldAddedByArcGIS(self):
|
|---|
| 1644 | return self._AutoDeleteFieldAddedByArcGIS
|
|---|
| 1645 |
|
|---|
| 1646 | AutoDeleteFieldAddedByArcGIS = property(_GetAutoDeleteFieldAddedByArcGIS, doc=DynamicDocString())
|
|---|
| 1647 |
|
|---|
| 1648 | def __init__(self, path, autoDeleteFieldAddedByArcGIS=True, parentCollection=None, queryableAttributeValues=None, lazyPropertyValues=None, cacheDirectory=None): # TODO: can't remove cacheDirectory without changing ArcGISWorkspace._ConstructFoundObject
|
|---|
| 1649 | self.__doc__.Obj.ValidateMethodInvocation()
|
|---|
| 1650 |
|
|---|
| 1651 | # Initialize our properties.
|
|---|
| 1652 |
|
|---|
| 1653 | self._Path = path
|
|---|
| 1654 |
|
|---|
| 1655 | if parentCollection is None:
|
|---|
| 1656 | self._DisplayName = _(u'ArcGIS table "%(path)s"') % {u'path': path}
|
|---|
| 1657 | else:
|
|---|
| 1658 | self._DisplayName = _(u'ArcGIS table "%(path)s"') % {u'path': os.path.join(parentCollection.Path, path)}
|
|---|
| 1659 |
|
|---|
| 1660 | self._AutoDeleteFieldAddedByArcGIS = autoDeleteFieldAddedByArcGIS
|
|---|
| 1661 | self._DeletedFieldAddedByArcGIS = False
|
|---|
| 1662 |
|
|---|
| 1663 | # Initialize the base class.
|
|---|
| 1664 |
|
|---|
| 1665 | super(ArcGISTable, self).__init__(parentCollection=parentCollection, queryableAttributeValues=queryableAttributeValues, lazyPropertyValues=lazyPropertyValues)
|
|---|
| 1666 |
|
|---|
| 1667 | def _GetDisplayName(self):
|
|---|
| 1668 | return self._DisplayName
|
|---|
| 1669 |
|
|---|
| 1670 | def _GetFullPath(self):
|
|---|
| 1671 | if self.ParentCollection is None:
|
|---|
| 1672 | return self._Path
|
|---|
| 1673 | return os.path.join(self.ParentCollection.Path, self._Path)
|
|---|
| 1674 |
|
|---|
| 1675 | def _GetLazyPropertyPhysicalValue(self, name):
|
|---|
| 1676 |
|
|---|
| 1677 | # If it is not a known lazy property, return None.
|
|---|
| 1678 |
|
|---|
| 1679 | if name not in ['SpatialReference', 'HasOID', 'OIDFieldName', 'GeometryType', 'GeometryFieldName', 'MaxStringLength', 'Fields', 'ArcGISDataType', 'ArcGISPhysicalDataType']:
|
|---|
| 1680 | return None
|
|---|
| 1681 |
|
|---|
| 1682 | # Get the geoprocessor Describe object for this dataset and
|
|---|
| 1683 | # verify that it is a table.
|
|---|
| 1684 |
|
|---|
| 1685 | gp = GeoprocessorManager.GetWrappedGeoprocessor()
|
|---|
| 1686 | path = self._GetFullPath()
|
|---|
| 1687 |
|
|---|
| 1688 | if not gp.Exists(path):
|
|---|
| 1689 | raise ValueError(_(u'Failed to open ArcGIS table "%(path)s". ArcGIS reports that it does not exist.') % {u'path': path})
|
|---|
| 1690 |
|
|---|
| 1691 | d = gp.Describe(path)
|
|---|
| 1692 | if not hasattr(d, 'Fields') or d.Fields is None or d.Fields.Next() is None:
|
|---|
| 1693 | raise ValueError(_(u'Failed to open ArcGIS dataset "%(path)s" as a table. ArcGIS reports that this object does not have any fields, therefore it cannot be accessed as a table.') % {u'path': path})
|
|---|
| 1694 |
|
|---|
| 1695 | # Get the ArcGIS data type of this table and the data type of
|
|---|
| 1696 | # its catalog path and set the display name to something more
|
|---|
| 1697 | # descriptive.
|
|---|
| 1698 |
|
|---|
| 1699 | arcGISDataType = self._Unicode(d.DataType)
|
|---|
| 1700 | arcGISPhysicalDataType = self._Unicode(gp.Describe(d.CatalogPath).DataType)
|
|---|
| 1701 |
|
|---|
| 1702 | self.SetLazyPropertyValue('ArcGISDataType', arcGISDataType)
|
|---|
| 1703 | self.SetLazyPropertyValue('ArcGISPhysicalDataType', arcGISPhysicalDataType)
|
|---|
| 1704 |
|
|---|
| 1705 | if arcGISDataType == arcGISPhysicalDataType:
|
|---|
| 1706 | self._DisplayName = _(u'ArcGIS %(dt)s "%(path)s"') % {u'dt': arcGISDataType, u'path': path}
|
|---|
| 1707 | else:
|
|---|
| 1708 | self._DisplayName = _(u'ArcGIS %(dt)s "%(path)s" of %(pdt)s "%(cp)s"') % {u'dt': arcGISDataType, u'path': path, u'pdt': arcGISPhysicalDataType, u'cp': d.CatalogPath}
|
|---|
| 1709 |
|
|---|
| 1710 | # Get the rest of the lazy properties.
|
|---|
| 1711 |
|
|---|
| 1712 | if hasattr(d, 'OIDFieldName') and isinstance(d.OIDFieldName, basestring) and len(d.OIDFieldName) > 0:
|
|---|
| 1713 | self.SetLazyPropertyValue('HasOID', True)
|
|---|
| 1714 | self.SetLazyPropertyValue('OIDFieldName', self._Unicode(d.OIDFieldName))
|
|---|
| 1715 | else:
|
|---|
| 1716 | self.SetLazyPropertyValue('HasOID', False)
|
|---|
| 1717 | self.SetLazyPropertyValue('OIDFieldName', None)
|
|---|
| 1718 |
|
|---|
| 1719 | geometryType = None
|
|---|
| 1720 | geometryFieldName = None
|
|---|
| 1721 |
|
|---|
| 1722 | if hasattr(d, 'ShapeFieldName') and isinstance(d.ShapeFieldName, basestring) and len(d.ShapeFieldName) > 0:
|
|---|
| 1723 | geometryFieldName = self._Unicode(d.ShapeFieldName)
|
|---|
| 1724 | shapeType = d.ShapeType.lower()
|
|---|
| 1725 | if shapeType == 'point':
|
|---|
| 1726 | if hasattr(d, 'HasZ') and bool(d.HasZ):
|
|---|
| 1727 | geometryType = u'Point25D'
|
|---|
| 1728 | else:
|
|---|
| 1729 | geometryType = u'Point'
|
|---|
| 1730 | elif shapeType == 'multipoint':
|
|---|
| 1731 | if hasattr(d, 'HasZ') and bool(d.HasZ):
|
|---|
| 1732 | geometryType = u'MultiPoint25D'
|
|---|
| 1733 | else:
|
|---|
| 1734 | geometryType = u'MultiPoint'
|
|---|
| 1735 | elif shapeType == 'polyline':
|
|---|
| 1736 | if hasattr(d, 'HasZ') and bool(d.HasZ):
|
|---|
| 1737 | geometryType = u'MultiLineString25D'
|
|---|
| 1738 | else:
|
|---|
| 1739 | geometryType = u'MultiLineString'
|
|---|
| 1740 | elif shapeType == 'polygon':
|
|---|
| 1741 | if hasattr(d, 'HasZ') and bool(d.HasZ):
|
|---|
| 1742 | geometryType = u'MultiPolygon25D'
|
|---|
| 1743 | else:
|
|---|
| 1744 | geometryType = u'MultiPolygon'
|
|---|
| 1745 | else:
|
|---|
| 1746 | self._LogWarning(_(u'The %(dn)s has an unsupported shape type "%(st)s". Geometry will not be available for this dataset.'), {u'dn': self._DisplayName, u'st': d.ShapeType})
|
|---|
| 1747 | geometryFieldName = None
|
|---|
| 1748 |
|
|---|
| 1749 | self.SetLazyPropertyValue('GeometryType', geometryType)
|
|---|
| 1750 | self.SetLazyPropertyValue('GeometryFieldName', geometryFieldName)
|
|---|
| 1751 |
|
|---|
| 1752 | if geometryType is not None:
|
|---|
| 1753 | self.SetLazyPropertyValue('SpatialReference', Dataset.ConvertSpatialReference('arcgis', gp.CreateSpatialReference_management(d.SpatialReference).split(';')[0], 'obj'))
|
|---|
| 1754 | else:
|
|---|
| 1755 | self.SetLazyPropertyValue('SpatialReference', None)
|
|---|
| 1756 |
|
|---|
| 1757 | if arcGISDataType.lower() in [u'dbasetable', u'textfile', u'shapefile']:
|
|---|
| 1758 | self.SetLazyPropertyValue('MaxStringLength', 254)
|
|---|
| 1759 | elif arcGISDataType.lower() == u'arcinfotable':
|
|---|
| 1760 | self.SetLazyPropertyValue('MaxStringLength', 320)
|
|---|
| 1761 | else:
|
|---|
| 1762 | self.SetLazyPropertyValue('MaxStringLength', None)
|
|---|
| 1763 |
|
|---|
| 1764 | # Get the fields.
|
|---|
| 1765 |
|
|---|
| 1766 | fields = []
|
|---|
| 1767 | fieldsObj = d.Fields
|
|---|
| 1768 | f = fieldsObj.Next()
|
|---|
| 1769 | while f is not None:
|
|---|
| 1770 | fields.append(self._ConstructFieldObject(f))
|
|---|
| 1771 | f = fieldsObj.Next()
|
|---|
| 1772 |
|
|---|
| 1773 | self.SetLazyPropertyValue('Fields', tuple(fields))
|
|---|
| 1774 |
|
|---|
| 1775 | # Log a debug message.
|
|---|
| 1776 |
|
|---|
| 1777 | if self._DebugLoggingEnabled():
|
|---|
| 1778 | self._LogDebug(_(u'%(class)s 0x%(id)08X: Retrieved lazy properties of %(dn)s: ArcGISDataType=%(ArcGISDataType)s, ArcGISPhysicalDataType=%(ArcGISPhysicalDataType)s, MaxStringLength=%(MaxStringLength)s, HasOID=%(HasOID)s, OIDFieldName=%(OIDFieldName)s, GeometryType=%(GeometryType)s, GeometryFieldName=%(GeometryFieldName)s, SpatialReference=%(SpatialReference)s.'),
|
|---|
| 1779 | {u'class': self.__class__.__name__,
|
|---|
| 1780 | u'id': id(self),
|
|---|
| 1781 | u'dn': self.DisplayName,
|
|---|
| 1782 | u'ArcGISDataType': self.GetLazyPropertyValue('ArcGISDataType'),
|
|---|
| 1783 | u'ArcGISPhysicalDataType': self.GetLazyPropertyValue('ArcGISPhysicalDataType'),
|
|---|
| 1784 | u'MaxStringLength': repr(self.GetLazyPropertyValue('MaxStringLength')),
|
|---|
| 1785 | u'HasOID': repr(self.GetLazyPropertyValue('HasOID')),
|
|---|
| 1786 | u'OIDFieldName': repr(self.GetLazyPropertyValue('OIDFieldName')),
|
|---|
| 1787 | u'GeometryType': repr(self.GetLazyPropertyValue('GeometryType')),
|
|---|
| 1788 | u'GeometryFieldName': repr(self.GetLazyPropertyValue('GeometryFieldName')),
|
|---|
| 1789 | u'SpatialReference': repr(Dataset.ConvertSpatialReference('obj', self.GetLazyPropertyValue('SpatialReference'), 'arcgis'))})
|
|---|
| 1790 |
|
|---|
| 1791 | for i, f in enumerate(fields):
|
|---|
| 1792 | self._LogDebug(_(u'%(class)s 0x%(id)08X: Field %(i)i: Name=%(Name)s, DataType=%(DataType)s, Length=%(Length)s, Precision=%(Precision)s, IsNullable=%(IsNullable)s, IsSettable=%(IsSettable)s.'),
|
|---|
| 1793 | {u'class': self.__class__.__name__,
|
|---|
| 1794 | u'id': id(self),
|
|---|
| 1795 | u'i': i,
|
|---|
| 1796 | u'Name': f.Name,
|
|---|
| 1797 | u'DataType': f.DataType,
|
|---|
| 1798 | u'Length': repr(f.Length),
|
|---|
| 1799 | u'Precision': repr(f.Precision),
|
|---|
| 1800 | u'IsNullable': repr(f.IsNullable),
|
|---|
| 1801 | u'IsSettable': repr(f.IsSettable)})
|
|---|
| 1802 |
|
|---|
| 1803 | # Return the value of the requested property.
|
|---|
| 1804 |
|
|---|
| 1805 | return self.GetLazyPropertyValue(name)
|
|---|
| 1806 |
|
|---|
| 1807 | def _ConstructFieldObject(self, f):
|
|---|
| 1808 | dataType = f.Type.lower()
|
|---|
| 1809 | if dataType == 'oid':
|
|---|
| 1810 | dataType = 'oid'
|
|---|
| 1811 | elif dataType == 'geometry':
|
|---|
| 1812 | dataType = 'geometry'
|
|---|
| 1813 | elif dataType == 'smallinteger':
|
|---|
| 1814 | dataType = 'int16'
|
|---|
| 1815 | elif dataType == 'integer':
|
|---|
| 1816 | dataType = 'int32'
|
|---|
| 1817 | elif dataType == 'single':
|
|---|
| 1818 | dataType = 'float32'
|
|---|
| 1819 | elif dataType == 'double':
|
|---|
| 1820 | dataType = 'float64'
|
|---|
| 1821 | elif dataType == 'string':
|
|---|
| 1822 | dataType = 'string'
|
|---|
| 1823 | elif dataType == 'date':
|
|---|
| 1824 | if self.ArcGISDataType.lower() in ['dbasetable', 'shapefile', 'arcinfotable']:
|
|---|
| 1825 | dataType = 'date'
|
|---|
| 1826 | else:
|
|---|
| 1827 | dataType = 'datetime'
|
|---|
| 1828 | elif dataType == 'blob':
|
|---|
| 1829 | dataType = 'binary'
|
|---|
| 1830 | else:
|
|---|
| 1831 | dataType = 'unknown'
|
|---|
| 1832 | isNullable = f.IsNullable
|
|---|
| 1833 | isNullable = isinstance(isNullable, (types.BooleanType, types.IntType)) and bool(isNullable) or isinstance(isNullable, basestring) and isNullable.lower() == 'true'
|
|---|
| 1834 | isSettable = dataType != 'oid' and not (f.Name.lower() == 'shape_length' and self.GetLazyPropertyValue('GeometryType') in [u'MultiLineString', u'MultiLineString25D', u'MultiPolygon', u'MultiPolygon25D']) and not (f.Name.lower() == 'shape_area' and self.GetLazyPropertyValue('GeometryType') in [u'MultiPolygon', u'MultiPolygon25D'])
|
|---|
| 1835 | return Field(f.Name, dataType, f.Length, f.Precision, isNullable, isSettable)
|
|---|
| 1836 |
|
|---|
| 1837 | @classmethod
|
|---|
| 1838 | def _TestCapability(cls, capability):
|
|---|
| 1839 | if capability in ['setspatialreference', 'addfield', 'deletefield', 'selectcursor', 'updatecursor', 'insertcursor', 'updaterow', 'deleterow']: # TODO: Test all of this for INFO tables.
|
|---|
| 1840 | return None
|
|---|
| 1841 |
|
|---|
| 1842 | if capability in ['int16 datatype', 'int32 datatype', 'float32 datatype', 'float64 datatype', 'string datatype']:
|
|---|
| 1843 | return None
|
|---|
| 1844 |
|
|---|
| 1845 | if capability == 'date datatype':
|
|---|
| 1846 | if isinstance(cls, ArcGISTable) and cls.ArcGISPhysicalDataType.lower() not in ['shapefile', 'dbftable', 'arcinfotable']:
|
|---|
| 1847 | return TypeError(_('Cannot create a field of data type "date" in %(dn)s because that data type is not supported by the ArcGIS %(dt)s data format. The ArcGIS %(dt)s data format can store dates with times, but not dates without times. Try the data type "datetime" instead.') % {u'dn': cls.DisplayName, u'dt': cls.ArcGISPhysicalDataType})
|
|---|
| 1848 | return None
|
|---|
| 1849 |
|
|---|
| 1850 | if capability == 'datetime datatype':
|
|---|
| 1851 | if isinstance(cls, ArcGISTable) and cls.ArcGISPhysicalDataType.lower() in ['shapefile', 'dbftable', 'arcinfotable']:
|
|---|
| 1852 | return TypeError(_('Cannot create a field of data type "datetime" in %(dn)s because that data type is not supported by the ArcGIS %(dt)s data format. The ArcGIS %(dt)s can store dates, but not dates with times. Try the data type "date" instead.') % {u'dn': cls.DisplayName, u'dt': cls.ArcGISPhysicalDataType})
|
|---|
| 1853 | return None
|
|---|
| 1854 |
|
|---|
| 1855 | if capability == 'binary datatype':
|
|---|
| 1856 | if isinstance(cls, ArcGISTable) and cls.ArcGISPhysicalDataType.lower() in ['shapefile', 'dbftable', 'arcinfotable']:
|
|---|
| 1857 | return TypeError(_('Cannot create a field of data type "binary" in %(dn)s because that data type is not supported by the ArcGIS %(dt)s data format.') % {u'dn': cls.DisplayName, u'dt': cls.ArcGISPhysicalDataType})
|
|---|
| 1858 | return None
|
|---|
| 1859 |
|
|---|
| 1860 | if capability.endswith(' datatype'):
|
|---|
| 1861 | if isinstance(cls, ArcGISTable):
|
|---|
| 1862 | return TypeError(_('Cannot create a field of data type "%(dt)s" in %(dn)s because that data type is not supported by the ArcGIS %(dt2)s data format.') % {u'dt': capability.split()[0], u'dn': cls.DisplayName, u'dt2': cls.ArcGISPhysicalDataType})
|
|---|
| 1863 | else:
|
|---|
| 1864 | return TypeError(_('Cannot create a field of data type "%(dt)s" because that data type is not supported by ArcGIS.') % {u'dt': capability.split()[0]})
|
|---|
| 1865 |
|
|---|
| 1866 | if capability.endswith(' isnullable'):
|
|---|
| 1867 | if isinstance(cls, ArcGISTable) and cls.ArcGISPhysicalDataType.lower() in ['shapefile', 'dbftable'] and not capability.startswith('date '):
|
|---|
| 1868 | return TypeError(_('Cannot create a nullable field in %(dn)s because the ArcGIS %(dt)s data format does not support null values, except in date fields.') % {u'dn': cls.DisplayName, u'dt': cls.ArcGISPhysicalDataType})
|
|---|
| 1869 | return None
|
|---|
| 1870 |
|
|---|
| 1871 | if isinstance(cls, ArcGISTable):
|
|---|
| 1872 | return RuntimeError(_(u'The %(cls)s class does not support the "%(cap)s" capability.') % {u'cls': cls.__class__.__name__, u'cap': capability})
|
|---|
| 1873 | return RuntimeError(_(u'The %(cls)s class does not support the "%(cap)s" capability.') % {u'cls': cls.__name__, u'cap': capability})
|
|---|
| 1874 |
|
|---|
| 1875 | @classmethod
|
|---|
| 1876 | def _GetSRTypeForSetting(cls):
|
|---|
| 1877 | return 'ArcGIS'
|
|---|
| 1878 |
|
|---|
| 1879 | def _SetSpatialReference(self, sr):
|
|---|
| 1880 | GeoprocessorManager.GetWrappedGeoprocessor().DefineProjection_management(self._GetFullPath(), sr)
|
|---|
| 1881 |
|
|---|
| 1882 | def GetArcGISCopyablePath(self):
|
|---|
| 1883 | return self._GetFullPath()
|
|---|
| 1884 |
|
|---|
| 1885 | def _AddField(self, name, dataType, length, precision, isNullable):
|
|---|
| 1886 | if dataType == 'int16':
|
|---|
| 1887 | dataType = 'SHORT'
|
|---|
| 1888 | elif dataType == 'int32':
|
|---|
| 1889 | dataType = 'LONG'
|
|---|
| 1890 | elif dataType == 'float32':
|
|---|
| 1891 | dataType = 'FLOAT'
|
|---|
| 1892 | elif dataType == 'float64':
|
|---|
| 1893 | dataType = 'DOUBLE'
|
|---|
| 1894 | elif dataType == 'string':
|
|---|
| 1895 | dataType = 'TEXT'
|
|---|
| 1896 | elif dataType == 'date' or dataType == 'datetime':
|
|---|
| 1897 | dataType = 'DATE'
|
|---|
| 1898 | elif dataType == 'binary':
|
|---|
| 1899 | dataType = 'BLOB'
|
|---|
| 1900 | else:
|
|---|
| 1901 | raise NotImplementedError(_(u'The _AddField method of class %(cls)s does not support the %(dt)s data type.') % {u'cls': self.__class__.__name__, u'dt': dataType})
|
|---|
| 1902 |
|
|---|
| 1903 | gp = GeoprocessorManager.GetWrappedGeoprocessor()
|
|---|
| 1904 | newName = gp.ValidateFieldName(name, self._GetFullPath())
|
|---|
| 1905 | if newName != name:
|
|---|
| 1906 | raise ValueError(_(u'The name "%(name)s" is prohibited by either ArcGIS or the underlying database or file format. ArcGIS suggested the name "%(newName)s" instead.') % {u'name': name, u'newName': newName})
|
|---|
| 1907 |
|
|---|
| 1908 | gp.AddField_management(self._GetFullPath(), name, dataType)
|
|---|
| 1909 |
|
|---|
| 1910 | if self.AutoDeleteFieldAddedByArcGIS and not self._DeletedFieldAddedByArcGIS:
|
|---|
| 1911 | if self.ArcGISPhysicalDataType.lower() == 'shapefile' and self.GetFieldByName('Id') is not None:
|
|---|
| 1912 | self.DeleteField('Id')
|
|---|
| 1913 | elif self.ArcGISPhysicalDataType.lower() == 'dbasetable' and self.GetFieldByName('Field1') is not None:
|
|---|
| 1914 | self.DeleteField('Field1')
|
|---|
| 1915 | self._DeletedFieldAddedByArcGIS = True
|
|---|
| 1916 |
|
|---|
| 1917 | return self._ConstructFieldObject(gp.ListFields(self._GetFullPath(), name).Next())
|
|---|
| 1918 |
|
|---|
| 1919 | def _DeleteField(self, name):
|
|---|
| 1920 | GeoprocessorManager.GetWrappedGeoprocessor().DeleteField_management(self._GetFullPath(), name)
|
|---|
| 1921 |
|
|---|
| 1922 | def _GetRowCount(self):
|
|---|
| 1923 | return GeoprocessorManager.GetWrappedGeoprocessor().GetCount_management(self._GetFullPath())
|
|---|
| 1924 |
|
|---|
| 1925 | def _OpenSelectCursor(self, fields, where, orderBy, rowCount, reportProgress, rowDescriptionSingular, rowDescriptionPlural):
|
|---|
| 1926 | return ArcGISSelectCursor(self, fields, where, orderBy, rowCount, reportProgress, rowDescriptionSingular, rowDescriptionPlural)
|
|---|
| 1927 |
|
|---|
| 1928 | def _OpenUpdateCursor(self, fields, where, orderBy, rowCount, reportProgress, rowDescriptionSingular, rowDescriptionPlural):
|
|---|
| 1929 | return ArcGISUpdateCursor(self, fields, where, orderBy, rowCount, reportProgress, rowDescriptionSingular, rowDescriptionPlural)
|
|---|
| 1930 |
|
|---|
| 1931 | def _OpenInsertCursor(self, rowCount, reportProgress, rowDescriptionSingular, rowDescriptionPlural):
|
|---|
| 1932 | return ArcGISInsertCursor(self, rowCount, reportProgress, rowDescriptionSingular, rowDescriptionPlural)
|
|---|
| 1933 |
|
|---|
| 1934 | @classmethod
|
|---|
| 1935 | def _RemoveExistingDatasetsFromList(cls, path, datasets, progressReporter):
|
|---|
| 1936 | numDatasets = len(datasets)
|
|---|
| 1937 | if GeoprocessorManager.GetWrappedGeoprocessor().Exists(path):
|
|---|
| 1938 | cls._LogDebug(_(u'%(class)s: ArcGIS table or feature class "%(path)s" exists.'), {u'class': cls.__name__, u'path': path})
|
|---|
| 1939 | while len(datasets) > 0:
|
|---|
| 1940 | del datasets[0]
|
|---|
| 1941 | else:
|
|---|
| 1942 | cls._LogDebug(_(u'%(class)s: ArcGIS table or feature class "%(path)s" does not exist.'), {u'class': cls.__name__, u'path': path})
|
|---|
| 1943 |
|
|---|
| 1944 | if progressReporter is not None:
|
|---|
| 1945 | progressReporter.ReportProgress(numDatasets)
|
|---|
| 1946 |
|
|---|
| 1947 | @classmethod
|
|---|
| 1948 | def _ImportDatasetsToPath(cls, path, sourceDatasets, mode, progressReporter, options):
|
|---|
| 1949 |
|
|---|
| 1950 | # Validate that there is only one source dataset. We do not
|
|---|
| 1951 | # currently support importing multiple source datasets into a
|
|---|
| 1952 | # single destination dataset. (This could theoretically be
|
|---|
| 1953 | # supported in the future by appending multiple datasets into
|
|---|
| 1954 | # the same destination.)
|
|---|
| 1955 |
|
|---|
| 1956 | if len(sourceDatasets) > 1:
|
|---|
| 1957 | raise ValueError(_(u'Cannot import %(count)i datasets into ArcGIS table or feature class "%(path)s". Importing of multiple datasets into a single table or feature class is not currently supported. If you are receiving this error from a tool that has a parameter that is a list of expressions that define the output dataset namen, you may have made a mistake in your expressions that caused the same output dataset name to be generated for multiple input datasets. If that is the problem, you can fix it by modifying the expressions to generate a unique output dataset name for each input dataset.') % {u'count': len(sourceDatasets), u'path': path})
|
|---|
| 1958 |
|
|---|
| 1959 | # If the mode is 'replace' and the destination dataset exists,
|
|---|
| 1960 | # delete it.
|
|---|
| 1961 |
|
|---|
| 1962 | gp = GeoprocessorManager.GetWrappedGeoprocessor()
|
|---|
| 1963 |
|
|---|
| 1964 | if mode == u'replace' and gp.Exists(path):
|
|---|
| 1965 | cls._LogDebug(_(u'%(class)s: Deleting existing ArcGIS dataset "%(path)s".'), {u'class': cls.__name__, u'path': path})
|
|---|
| 1966 | try:
|
|---|
| 1967 | gp.Delete_management(path)
|
|---|
| 1968 | except Exception, e:
|
|---|
| 1969 | raise RuntimeError(_(u'Failed to delete the existing ArcGIS dataset "%(path)s" due to %(e)s: %(msg)s') % {u'path': path, u'e': e.__class__.__name__, u'msg': cls._Unicode(e)})
|
|---|
| 1970 |
|
|---|
| 1971 | # Otherwise, make sure all the parent levels in the
|
|---|
| 1972 | # destination path exist (directories, geodatabase, feature
|
|---|
| 1973 | # dataset), creating them if necessary. Use ArcGIS tools such
|
|---|
| 1974 | # as CreateFolder_management to create them, so the ArcGIS
|
|---|
| 1975 | # catalog is aware of them.
|
|---|
| 1976 |
|
|---|
| 1977 | elif not gp.Exists(os.path.dirname(path)):
|
|---|
| 1978 |
|
|---|
| 1979 | # If the destination path is in the file system, figure
|
|---|
| 1980 | # out whether it contains levels for a geodatabase and a
|
|---|
| 1981 | # feature dataset. If neither, it must be a shapefile, DBF
|
|---|
| 1982 | # table, or something similar.
|
|---|
| 1983 |
|
|---|
| 1984 | if path[0] in ['/', '\\'] or hasattr(os.path, 'splitdrive') and os.path.splitdrive(path)[0] != '' or hasattr(os.path, 'splitunc') and os.path.splitunc(path)[0] != '':
|
|---|
| 1985 | if os.path.splitext(os.path.dirname(path))[1].lower() in ['.mdb', '.gdb']:
|
|---|
| 1986 | featureDataset = None
|
|---|
| 1987 | geodatabase = os.path.dirname(path)
|
|---|
| 1988 | directory = os.path.dirname(geodatabase)
|
|---|
| 1989 | elif os.path.splitext(os.path.dirname(os.path.dirname(path)))[1].lower() in ['.mdb', '.gdb']:
|
|---|
| 1990 | featureDataset = os.path.dirname(path)
|
|---|
| 1991 | geodatabase = os.path.dirname(featureDataset)
|
|---|
| 1992 | directory = os.path.dirname(geodatabase)
|
|---|
| 1993 | else:
|
|---|
| 1994 | featureDataset = None
|
|---|
| 1995 | geodatabase = None
|
|---|
| 1996 | directory = os.path.dirname(path)
|
|---|
| 1997 |
|
|---|
| 1998 | # Create the directory if it does not exist.
|
|---|
| 1999 |
|
|---|
| 2000 | if not os.path.exists(directory):
|
|---|
| 2001 | cls._LogDebug(_(u'%(class)s: Creating directory "%(path)s".'), {u'class': cls.__name__, u'path': directory})
|
|---|
| 2002 |
|
|---|
| 2003 | if hasattr(os.path, 'splitdrive') and os.path.splitdrive(directory)[0] != '':
|
|---|
| 2004 | root, subdirs = os.path.splitdrive(directory)
|
|---|
| 2005 | root = root + '\\'
|
|---|
| 2006 | elif hasattr(os.path, 'splitunc') and os.path.splitunc(directory)[0] != '':
|
|---|
| 2007 | root, subdirs = os.path.splitunc(directory)
|
|---|
| 2008 | else:
|
|---|
| 2009 | root, subdirs = directory[0], directory[1:]
|
|---|
| 2010 |
|
|---|
| 2011 | subdirsList = []
|
|---|
| 2012 | while len(subdirs) > 1:
|
|---|
| 2013 | subdirsList.insert(0, os.path.basename(subdirs))
|
|---|
| 2014 | subdirs = os.path.dirname(subdirs)
|
|---|
| 2015 |
|
|---|
| 2016 | dirToCheck = root
|
|---|
| 2017 | for subdir in subdirsList:
|
|---|
| 2018 | if not os.path.isdir(os.path.join(dirToCheck, subdir)):
|
|---|
| 2019 | try:
|
|---|
| 2020 | gp.CreateFolder_management(dirToCheck, subdir)
|
|---|
| 2021 | except Exception, e:
|
|---|
| 2022 | raise RuntimeError(_(u'Failed to create the directory "%(path)s" due to %(e)s: %(msg)s') % {u'path': os.path.join(dirToCheck, subdir), u'e': e.__class__.__name__, u'msg': cls._Unicode(e)})
|
|---|
| 2023 | dirToCheck = os.path.join(dirToCheck, subdir)
|
|---|
| 2024 |
|
|---|
| 2025 | # If the path includes a geodatabase and it does not
|
|---|
| 2026 | # exist, create it.
|
|---|
| 2027 |
|
|---|
| 2028 | if geodatabase is not None and not gp.Exists(geodatabase):
|
|---|
| 2029 | if os.path.splitext(geodatabase).lower() == '.mdb':
|
|---|
| 2030 | gp.CreatePersonalGDB_management(directory, os.path.basename(geodatabase))
|
|---|
| 2031 | else:
|
|---|
| 2032 | gp.CreateFileGDB_management(directory, os.path.basename(geodatabase))
|
|---|
| 2033 |
|
|---|
| 2034 | # If the path includes a feature dataset and it does not
|
|---|
| 2035 | # exist, create it.
|
|---|
| 2036 |
|
|---|
| 2037 | if featureDataset is not None and not gp.Exists(featureDataset):
|
|---|
| 2038 | gp.CreateFeatureDataset_management(geodatabase, os.path.basename(featureDataset))
|
|---|
| 2039 |
|
|---|
| 2040 | # TODO: Otherwise (the desination path is not in the file
|
|---|
| 2041 | # system), make sure the destination exists and create the
|
|---|
| 2042 | # feature dataset. I'm not willing to code this without
|
|---|
| 2043 | # having an ArcSDE geodatabase to test it with.
|
|---|
| 2044 |
|
|---|
| 2045 | else:
|
|---|
| 2046 | raise NotImplementedError(_(u'Cannot import %(dn)s into destination ArcGIS table or feature class "%(path)s" because that destination is not a folder, personal geodatabase, or file geodatabase. Other destinations (e.g. ArcSDE geodatabases) are not fully supported. Please contact the author of this tool for assistance.') % {u'dn': sourceDatasets[0].DisplayName, u'path': path})
|
|---|
| 2047 |
|
|---|
| 2048 | # At this point, the parent workspace of the destination
|
|---|
| 2049 | # dataset exists, and the destination dataset does not exist.
|
|---|
| 2050 | # If the source dataset is copyable with ArcGIS's
|
|---|
| 2051 | # Copy_management tool, use it because it will be much faster
|
|---|
| 2052 | # than manually copying each row.
|
|---|
| 2053 |
|
|---|
| 2054 | if isinstance(sourceDatasets[0], ArcGISCopyableTable):
|
|---|
| 2055 | arcGISCopyablePath = sourceDatasets[0].GetArcGISCopyablePath()
|
|---|
| 2056 | try:
|
|---|
| 2057 | cls._LogDebug(_(u'%(class)s: Copying "%(src)s" to ArcGIS dataset "%(path)s".'), {u'class': cls.__name__, u'src': arcGISCopyablePath, u'path': path})
|
|---|
| 2058 | gp.Copy_management(arcGISCopyablePath, path)
|
|---|
| 2059 | finally:
|
|---|
| 2060 | sourceDatasets[0].Close() # This will make sure it deletes temporary files, if it had to create any in order to make itself ArcGIS-copyable.
|
|---|
| 2061 |
|
|---|
| 2062 | # Otherwise copy the rows manually. This will work with any
|
|---|
| 2063 | # Table instance.
|
|---|
| 2064 |
|
|---|
| 2065 | else:
|
|---|
| 2066 | workspace = ArcGISWorkspace(os.path.dirname(path), ArcGISTable, pathParsingExpressions=[r'(?P<TableName>.+)'], queryableAttributes=(QueryableAttribute(u'TableName', _(u'Table name'), UnicodeStringTypeMetadata()),))
|
|---|
| 2067 | workspace.ImportTable(os.path.basename(path), sourceDatasets[0], reportProgress=False)
|
|---|
| 2068 |
|
|---|
| 2069 | # Report progress.
|
|---|
| 2070 |
|
|---|
| 2071 | if progressReporter is not None:
|
|---|
| 2072 | progressReporter.ReportProgress()
|
|---|
| 2073 |
|
|---|
| 2074 |
|
|---|
| 2075 | class _ArcGISReadableCursor(object):
|
|---|
| 2076 | __doc__ = DynamicDocString()
|
|---|
| 2077 |
|
|---|
| 2078 | def _NextRow(self):
|
|---|
| 2079 | self._Row = self._Cursor.Next() # TODO: See if we can improve performance by not going through the _ArcGISWrapper when accessing rows.
|
|---|
| 2080 | return self._Row is not None
|
|---|
| 2081 |
|
|---|
| 2082 | def _GetValue(self, field):
|
|---|
| 2083 | return self._Row.GetValue(field)
|
|---|
| 2084 |
|
|---|
| 2085 | def _GetOID(self):
|
|---|
| 2086 | return self._Row.GetValue(self._Table.OIDFieldName)
|
|---|
| 2087 |
|
|---|
| 2088 | def _GetGeometry(self):
|
|---|
| 2089 | ogr = self._Table._ogr()
|
|---|
| 2090 | g = self._Row.GetValue(self._Table.GeometryFieldName)
|
|---|
| 2091 |
|
|---|
| 2092 | if g is not None:
|
|---|
| 2093 | if self._Table.GeometryType == 'Point':
|
|---|
| 2094 | geometry = ogr.Geometry(ogr.wkbPoint)
|
|---|
| 2095 | part = g.GetPart()
|
|---|
| 2096 | geometry.AddPoint_2D(part.X, part.Y)
|
|---|
| 2097 |
|
|---|
| 2098 | elif self._Table.GeometryType == 'Point25D':
|
|---|
| 2099 | geometry = ogr.Geometry(ogr.wkbPoint25D)
|
|---|
| 2100 | part = g.GetPart()
|
|---|
| 2101 | geometry.AddPoint(part.X, part.Y, part.Z)
|
|---|
| 2102 |
|
|---|
| 2103 | elif self._Table.GeometryType == 'MultiPoint':
|
|---|
| 2104 | if g.PartCount == 1:
|
|---|
| 2105 | geometry = ogr.Geometry(ogr.wkbPoint)
|
|---|
| 2106 | part = g.GetPart(0)
|
|---|
| 2107 | geometry.AddPoint_2D(part.X, part.Y)
|
|---|
| 2108 | else:
|
|---|
| 2109 | geometry = ogr.Geometry(ogr.wkbMultiPoint)
|
|---|
| 2110 | for i in range(g.PartCount):
|
|---|
| 2111 | part = g.GetPart(i)
|
|---|
| 2112 | point = ogr.Geometry(ogr.wkbPoint)
|
|---|
| 2113 | point.AddPoint_2D(part.X, part.Y)
|
|---|
| 2114 | geometry.AddGeometryDirectly(point)
|
|---|
| 2115 |
|
|---|
| 2116 | elif self._Table.GeometryType == 'MultiPoint25D':
|
|---|
| 2117 | if g.PartCount == 1:
|
|---|
| 2118 | geometry = ogr.Geometry(ogr.wkbPoint25D)
|
|---|
| 2119 | part = g.GetPart(0)
|
|---|
| 2120 | geometry.AddPoint(part.X, part.Y, part.Z)
|
|---|
| 2121 | else:
|
|---|
| 2122 | geometry = ogr.Geometry(ogr.wkbMultiPoint25D)
|
|---|
| 2123 | for i in range(g.PartCount):
|
|---|
| 2124 | part = g.GetPart(i)
|
|---|
| 2125 | point = ogr.Geometry(ogr.wkbPoint)
|
|---|
| 2126 | point.AddPoint(part.X, part.Y, part.Z)
|
|---|
| 2127 | geometry.AddGeometryDirectly(point)
|
|---|
| 2128 |
|
|---|
| 2129 | elif self._Table.GeometryType == 'MultiLineString':
|
|---|
| 2130 | if g.PartCount == 1:
|
|---|
| 2131 | geometry = ogr.Geometry(ogr.wkbLineString)
|
|---|
| 2132 | part = g.GetPart(0)
|
|---|
| 2133 | point = part.Next()
|
|---|
| 2134 | while point is not None:
|
|---|
| 2135 | geometry.AddPoint_2D(point.X, point.Y)
|
|---|
| 2136 | point = part.Next()
|
|---|
| 2137 | else:
|
|---|
| 2138 | geometry = ogr.Geometry(ogr.wkbMultiLineString)
|
|---|
| 2139 | for i in range(g.PartCount):
|
|---|
| 2140 | line = ogr.Geometry(ogr.wkbLineString)
|
|---|
| 2141 | part = g.GetPart(i)
|
|---|
| 2142 | point = part.Next()
|
|---|
| 2143 | while point is not None:
|
|---|
| 2144 | line.AddPoint_2D(point.X, point.Y)
|
|---|
| 2145 | point = part.Next()
|
|---|
| 2146 | geometry.AddGeometryDirectly(line)
|
|---|
| 2147 |
|
|---|
| 2148 | elif self._Table.GeometryType == 'MultiLineString25D':
|
|---|
| 2149 | if g.PartCount == 1:
|
|---|
| 2150 | geometry = ogr.Geometry(ogr.wkbLineString25D)
|
|---|
| 2151 | part = g.GetPart(0)
|
|---|
| 2152 | point = part.Next()
|
|---|
| 2153 | while point is not None:
|
|---|
| 2154 | geometry.AddPoint(point.X, point.Y, point.Z)
|
|---|
| 2155 | point = part.Next()
|
|---|
| 2156 | else:
|
|---|
| 2157 | geometry = ogr.Geometry(ogr.wkbMultiLineString25D)
|
|---|
| 2158 | for i in range(g.PartCount):
|
|---|
| 2159 | line = ogr.Geometry(ogr.wkbLineString25D)
|
|---|
| 2160 | part = g.GetPart(i)
|
|---|
| 2161 | point = part.Next()
|
|---|
| 2162 | while point is not None:
|
|---|
| 2163 | line.AddPoint(point.X, point.Y, point.Z)
|
|---|
| 2164 | point = part.Next()
|
|---|
| 2165 | geometry.AddGeometryDirectly(line)
|
|---|
| 2166 |
|
|---|
| 2167 | elif self._Table.GeometryType == 'MultiPolygon':
|
|---|
| 2168 | if g.PartCount == 1:
|
|---|
| 2169 | geometry = ogr.Geometry(ogr.wkbPolygon)
|
|---|
| 2170 | ring = ogr.Geometry(ogr.wkbLinearRing)
|
|---|
| 2171 | part = g.GetPart(0)
|
|---|
| 2172 | point = part.Next()
|
|---|
| 2173 | while point is not None:
|
|---|
| 2174 | ring.AddPoint_2D(point.X, point.Y)
|
|---|
| 2175 | point = part.Next()
|
|---|
| 2176 | if point is None:
|
|---|
| 2177 | geometry.AddGeometryDirectly(ring)
|
|---|
| 2178 | point = part.Next()
|
|---|
| 2179 | if point is not None:
|
|---|
| 2180 | ring = ogr.Geometry(ogr.wkbLinearRing)
|
|---|
| 2181 | else:
|
|---|
| 2182 | geometry = ogr.Geometry(ogr.wkbMultiPolygon)
|
|---|
| 2183 | for i in range(g.PartCount):
|
|---|
| 2184 | polygon = ogr.Geometry(ogr.wkbPolygon)
|
|---|
| 2185 | ring = ogr.Geometry(ogr.wkbLinearRing)
|
|---|
| 2186 | part = g.GetPart(i)
|
|---|
| 2187 | point = part.Next()
|
|---|
| 2188 | while point is not None:
|
|---|
| 2189 | ring.AddPoint_2D(point.X, point.Y)
|
|---|
| 2190 | point = part.Next()
|
|---|
| 2191 | if point is None:
|
|---|
| 2192 | polygon.AddGeometryDirectly(ring)
|
|---|
| 2193 | point = part.Next()
|
|---|
| 2194 | if point is not None:
|
|---|
| 2195 | ring = ogr.Geometry(ogr.wkbLinearRing)
|
|---|
| 2196 | geometry.AddGeometryDirectly(polygon)
|
|---|
| 2197 |
|
|---|
| 2198 | elif self._Table.GeometryType == 'MultiPolygon25D':
|
|---|
| 2199 | if g.PartCount == 1:
|
|---|
| 2200 | geometry = ogr.Geometry(ogr.wkbPolygon25D)
|
|---|
| 2201 | ring = ogr.Geometry(ogr.wkbLinearRing)
|
|---|
| 2202 | part = g.GetPart(0)
|
|---|
| 2203 | point = part.Next()
|
|---|
| 2204 | while point is not None:
|
|---|
| 2205 | ring.AddPoint(point.X, point.Y, point.Z)
|
|---|
| 2206 | point = part.Next()
|
|---|
| 2207 | if point is None:
|
|---|
| 2208 | geometry.AddGeometryDirectly(ring)
|
|---|
| 2209 | point = part.Next()
|
|---|
| 2210 | if point is not None:
|
|---|
| 2211 | ring = ogr.Geometry(ogr.wkbLinearRing)
|
|---|
| 2212 | else:
|
|---|
| 2213 | geometry = ogr.Geometry(ogr.wkbMultiPolygon25D)
|
|---|
| 2214 | for i in range(g.PartCount):
|
|---|
| 2215 | polygon = ogr.Geometry(ogr.wkbPolygon25D)
|
|---|
| 2216 | ring = ogr.Geometry(ogr.wkbLinearRing)
|
|---|
| 2217 | part = g.GetPart(i)
|
|---|
| 2218 | point = part.Next()
|
|---|
| 2219 | while point is not None:
|
|---|
| 2220 | ring.AddPoint(point.X, point.Y, point.Z)
|
|---|
| 2221 | point = part.Next()
|
|---|
| 2222 | if point is None:
|
|---|
| 2223 | polygon.AddGeometryDirectly(ring)
|
|---|
| 2224 | point = part.Next()
|
|---|
| 2225 | if point is not None:
|
|---|
| 2226 | ring = ogr.Geometry(ogr.wkbLinearRing)
|
|---|
| 2227 | geometry.AddGeometryDirectly(polygon)
|
|---|
| 2228 |
|
|---|
| 2229 | else:
|
|---|
| 2230 | raise NotImplementedError(_(u'The %(dn)s has an unsupported geometry type "%(gt)s".') % {u'dn': self._Table.DisplayName, u'gt': self._Table.GeometryType})
|
|---|
| 2231 |
|
|---|
| 2232 | else:
|
|---|
| 2233 | self._Table._LogWarning(_(u'The %(singular)s of %(dn)s with %(field)s = %(value)s has a null geometry.'), {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': self._Table.OIDFieldName, u'value': repr(self._Cursor.GetValue(self._Table.OIDFieldName))})
|
|---|
| 2234 | geometry = None
|
|---|
| 2235 |
|
|---|
| 2236 | return geometry
|
|---|
| 2237 |
|
|---|
| 2238 |
|
|---|
| 2239 | class _ArcGISWritableCursor(object):
|
|---|
| 2240 | __doc__ = DynamicDocString()
|
|---|
| 2241 |
|
|---|
| 2242 | def _SetValue(self, field, value):
|
|---|
| 2243 | # TODO: Verify the following check
|
|---|
| 2244 | if value is None and not GeoprocessorManager.GetGeoprocessorIsCOMObject() and (GeoprocessorManager.GetArcGISMajorVersion() < 9 or GeoprocessorManager.GetArcGISMajorVersion() == 9 and (GeoprocessorManager.GetArcGISMinorVersion() == 2 or GeoprocessorManager.GetArcGISMinorVersion() == 3 and GeoprocessorManager.GetArcGISServicePack() < 3)):
|
|---|
| 2245 | if GeoprocessorManager.GetArcGISMajorVersion() == 9 and GeoprocessorManager.GetArcGISMinorVersion() == 3 and GeoprocessorManager.GetArcGISServicePack() == 2: # This is ArcGIS 9.3.1
|
|---|
| 2246 | raise RuntimeError(_(u'Cannot set the value of field %(field)s of this %(singular) of %(dn)s to NULL due to ArcGIS bug NIM010734. ESRI fixed this problem in ArcGIS 9.3.1 Service Pack 1. Please install Service Pack 1 and try again. For more information on the bug, search http://support.esri.com for the bug number (NIM010734).') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': field})
|
|---|
| 2247 | raise RuntimeError(_(u'Cannot set the value of field %(field)s of this %(singular) of %(dn)s to NULL due to ArcGIS bug NIM010734. ESRI fixed this problem in ArcGIS 9.3.1 Service Pack 1. Please upgrade to ArcGIS 9.3.1, install Service Pack 1, and try again. If that is not possible, please contact the author of this tool for assistance. For more information on the bug, search http://support.esri.com for the bug number (NIM010734).') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': field})
|
|---|
| 2248 |
|
|---|
| 2249 | if isinstance(value, types.IntType) and self._Table.ArcGISPhysicalDataType in ['shapefile', 'dbftable']: # TODO: Check limits of ArcInfoTable
|
|---|
| 2250 | length = self._Table.GetFieldByName(field).Length
|
|---|
| 2251 | if value >= 10**length or value <= -1 * 10**(length-1):
|
|---|
| 2252 | raise ValueError(_(u'Cannot set the value of field %(field)s of this %(singular) of %(dn)s to %(value)s because that value is outside of the allowed range of values for the field (%(r1)i to %(r2)i).') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName, u'field': field, u'value': repr(value), u'r1': -1 * 10**(length-1) + 1, u'r2': 10**length - 1})
|
|---|
| 2253 |
|
|---|
| 2254 | self._Row.SetValue(field, value)
|
|---|
| 2255 |
|
|---|
| 2256 | def _SetGeometry(self, geometry):
|
|---|
| 2257 | if geometry.IsEmpty():
|
|---|
| 2258 | raise ValueError(_(u'Cannot set the geometry of this %(singular)s of %(dn)s because the provided Geometry object is empty. ArcGIS does not support empty geometries.') % {u'singular': self._RowDescriptionSingular, u'dn': self._Table.DisplayName})
|
|---|
| 2259 |
|
|---|
| 2260 | ogr = self._Table._ogr()
|
|---|
| 2261 | gp = GeoprocessorManager.GetWrappedGeoprocessor()
|
|---|
| 2262 | geometryType = geometry.GetGeometryType()
|
|---|
| 2263 |
|
|---|
| 2264 | if geometryType in [ogr.wkbPoint, ogr.wkbPoint25D]:
|
|---|
| 2265 | g = gp.CreateObject('Point')
|
|---|
| 2266 | g.X = geometry.GetX()
|
|---|
| 2267 | g.Y = geometry.GetY()
|
|---|
| 2268 | if geometryType == ogr.wkbPoint25D:
|
|---|
| 2269 | g.Z = geometry.GetZ()
|
|---|
| 2270 |
|
|---|
| 2271 | elif geometryType in [ogr.wkbLineString, ogr.wkbLineString25D]:
|
|---|
| 2272 | g = gp.CreateObject('Array')
|
|---|
| 2273 | p = gp.CreateObject('Point')
|
|---|
| 2274 | for i in range(geometry.GetPointCount()):
|
|---|
| 2275 | p.X = geometry.GetX(i)
|
|---|
| 2276 | p.Y = geometry.GetY(i)
|
|---|
| 2277 | if geometryType == ogr.wkbPoint25D:
|
|---|
| 2278 | p.Z = geometry.GetZ(i)
|
|---|
| 2279 | g.Add(p)
|
|---|
| 2280 |
|
|---|
| 2281 | elif geometryType in [ogr.wkbPolygon, ogr.wkbPolygon25D]:
|
|---|
| 2282 | g = gp.CreateObject('Array')
|
|---|
| 2283 | a = gp.CreateObject('Array')
|
|---|
| 2284 | p = gp.CreateObject('Point')
|
|---|
| 2285 | for i in range(geometry.GetGeometryCount()):
|
|---|
| 2286 | r = geometry.GetGeometryRef(i)
|
|---|
| 2287 | firstX = r.GetX(0)
|
|---|
| 2288 | firstY = r.GetY(0)
|
|---|
| 2289 | if geometryType == ogr.wkbPolygon25D:
|
|---|
| 2290 | firstZ = r.GetZ(0)
|
|---|
| 2291 | for j in range(r.GetPointCount()):
|
|---|
| 2292 | p.X = r.GetX(j)
|
|---|
| 2293 | p.Y = r.GetY(j)
|
|---|
| 2294 | if geometryType == ogr.wkbPolygon25D:
|
|---|
| 2295 | p.Z = r.GetZ(j)
|
|---|
| 2296 | a.Add(p)
|
|---|
| 2297 | if r.GetX(j) != firstX or r.GetY(j) != firstY or geometryType == ogr.wkbPolygon25D and r.GetZ(j) != firstZ:
|
|---|
| 2298 | p.X = firstX
|
|---|
| 2299 | p.Y = firstY
|
|---|
| 2300 | if geometryType == ogr.wkbPolygon25D:
|
|---|
| 2301 | p.Z = firstZ
|
|---|
| 2302 | a.Add(p)
|
|---|
| 2303 | g.Add(a)
|
|---|
| 2304 |
|
|---|
| 2305 | elif geometryType in [ogr.wkbMultiPoint, ogr.wkbMultiPoint25D]:
|
|---|
| 2306 | g = gp.CreateObject('Array')
|
|---|
| 2307 | p = gp.CreateObject('Point')
|
|---|
| 2308 | for i in range(geometry.GetGeometryCount()):
|
|---|
| 2309 | r = geometry.GetGeometryRef(i)
|
|---|
| 2310 | p.X = r.GetX()
|
|---|
| 2311 | p.Y = r.GetY()
|
|---|
| 2312 | if geometryType == ogr.wkbMultiPoint25D:
|
|---|
| 2313 | p.Z = r.GetY()
|
|---|
| 2314 | g.Add(p)
|
|---|
| 2315 |
|
|---|
| 2316 | elif geometryType in [ogr.wkbMultiLineString, ogr.wkbMultiLineString25D]:
|
|---|
| 2317 | g = gp.CreateObject('Array')
|
|---|
| 2318 | p = gp.CreateObject('Point')
|
|---|
| 2319 | for i in range(geometry.GetGeometryCount()):
|
|---|
| 2320 | r = geometry.GetGeometryRef(i)
|
|---|
| 2321 | a = gp.CreateObject('Array')
|
|---|
| 2322 | for j in range(r.GetPointCount()):
|
|---|
| 2323 | p.X = r.GetX(j)
|
|---|
| 2324 | p.Y = r.GetY(j)
|
|---|
| 2325 | if geometryType == ogr.wkbPoint25D:
|
|---|
| 2326 | p.Z = r.GetZ(j)
|
|---|
| 2327 | a.Add(p)
|
|---|
| 2328 | g.Add(a)
|
|---|
| 2329 |
|
|---|
| 2330 | elif geometryType in [ogr.wkbMultiPolygon, ogr.wkbMultiPolygon25D]:
|
|---|
| 2331 | g = gp.CreateObject('Array')
|
|---|
| 2332 | p = gp.CreateObject('Point')
|
|---|
| 2333 | for i in range(geometry.GetGeometryCount()):
|
|---|
| 2334 | r1 = geometry.GetGeometryRef(i)
|
|---|
| 2335 | a = gp.CreateObject('Array')
|
|---|
| 2336 | for j in range(r1.GetGeometryCount()):
|
|---|
| 2337 | r2 = r1.GetGeometryRef(j)
|
|---|
| 2338 | firstX = r2.GetX(0)
|
|---|
| 2339 | firstY = r2.GetY(0)
|
|---|
| 2340 | if geometryType == ogr.wkbPolygon25D:
|
|---|
| 2341 | firstZ = r2.GetZ(0)
|
|---|
| 2342 | for k in range(r2.GetPointCount()):
|
|---|
| 2343 | p.X = r2.GetX(k)
|
|---|
| 2344 | p.Y = r2.GetY(k)
|
|---|
| 2345 | if geometryType == ogr.wkbMultiPolygon25D:
|
|---|
| 2346 | p.Z = r2.GetZ(k)
|
|---|
| 2347 | a.Add(p)
|
|---|
| 2348 | if r2.GetX(k) != firstX or r2.GetY(k) != firstY or geometryType == ogr.wkbMultiPolygon25D and r2.GetZ(k) != firstZ:
|
|---|
| 2349 | p.X = firstX
|
|---|
| 2350 | p.Y = firstY
|
|---|
| 2351 | if geometryType == ogr.wkbMultiPolygon25D:
|
|---|
| 2352 | p.Z = firstZ
|
|---|
| 2353 | a.Add(p)
|
|---|
| 2354 | g.Add(a)
|
|---|
| 2355 |
|
|---|
| 2356 | else:
|
|---|
| 2357 | raise NotImplementedError(_(u'The _SetGeometry method of class %(cls)s does not support for OGR geometry type %(gt)i.') % {u'cls': self.__class__.__name__, u'gt': geometryType})
|
|---|
| 2358 |
|
|---|
| 2359 | self._Row.SetValue(self._Table.GeometryFieldName, g)
|
|---|
| 2360 |
|
|---|
| 2361 |
|
|---|
| 2362 | class ArcGISSelectCursor(_ArcGISReadableCursor, SelectCursor):
|
|---|
| 2363 | __doc__ = DynamicDocString()
|
|---|
| 2364 |
|
|---|
| 2365 | def _Open(self, fields, where, orderBy):
|
|---|
| 2366 | if orderBy is not None and GeoprocessorManager.GetArcGISMajorVersion() == 9 and GeoprocessorManager.GetArcGISMinorVersion() < 2:
|
|---|
| 2367 | raise RuntimeError(_(u'The installed version of ArcGIS does not allow you to specify the sort order of cursors. Please upgrade to ArcGSI 9.2 or later, or omit the orderBy parameter and try again.'))
|
|---|
| 2368 |
|
|---|
| 2369 | if fields is not None:
|
|---|
| 2370 | fields = u'; '.join(fields)
|
|---|
| 2371 |
|
|---|
| 2372 | if orderBy is not None:
|
|---|
| 2373 | orderBy = '; '.join(map(lambda s: s.strip().split()[0] + ' ' + s.strip().split()[1][0].upper(), orderBy.split(',')))
|
|---|
| 2374 |
|
|---|
| 2375 | self._Row = None
|
|---|
| 2376 | self._Cursor = GeoprocessorManager.GetWrappedGeoprocessor().SearchCursor(self._Table._GetFullPath(), where, None, fields, orderBy)
|
|---|
| 2377 |
|
|---|
| 2378 | def _Close(self):
|
|---|
| 2379 | self._Row = None
|
|---|
| 2380 | self._Cursor = None
|
|---|
| 2381 | super(ArcGISSelectCursor, self)._Close()
|
|---|
| 2382 |
|
|---|
| 2383 |
|
|---|
| 2384 | class ArcGISUpdateCursor(_ArcGISReadableCursor, _ArcGISWritableCursor, UpdateCursor):
|
|---|
| 2385 | __doc__ = DynamicDocString()
|
|---|
| 2386 |
|
|---|
| 2387 | def _Open(self, fields, where, orderBy):
|
|---|
| 2388 | if orderBy is not None and GeoprocessorManager.GetArcGISMajorVersion() == 9 and GeoprocessorManager.GetArcGISMinorVersion() < 2:
|
|---|
| 2389 | raise RuntimeError(_(u'The installed version of ArcGIS does not allow you to specify the sort order of cursors. Please upgrade to ArcGSI 9.2 or later, or omit the orderBy parameter and try again.'))
|
|---|
| 2390 |
|
|---|
| 2391 | if fields is not None:
|
|---|
| 2392 | fields = u'; '.join(fields)
|
|---|
| 2393 |
|
|---|
| 2394 | if orderBy is not None:
|
|---|
| 2395 | orderBy = '; '.join(map(lambda s: s.strip().split()[0] + ' ' + s.strip().split()[1][0].upper(), orderBy.split(',')))
|
|---|
| 2396 |
|
|---|
| 2397 | self._Row = None
|
|---|
| 2398 | self._Cursor = GeoprocessorManager.GetWrappedGeoprocessor().UpdateCursor(self._Table._GetFullPath(), where, None, fields, orderBy)
|
|---|
| 2399 |
|
|---|
| 2400 | def _Close(self):
|
|---|
| 2401 | self._Row = None
|
|---|
| 2402 | self._Cursor = None
|
|---|
| 2403 | super(ArcGISUpdateCursor, self)._Close()
|
|---|
| 2404 |
|
|---|
| 2405 | def _UpdateRow(self):
|
|---|
| 2406 | self._Cursor.UpdateRow(self._Row)
|
|---|
| 2407 | self._Row = None
|
|---|
| 2408 |
|
|---|
| 2409 | def _DeleteRow(self):
|
|---|
| 2410 | self._Cursor.DeleteRow(self._Row)
|
|---|
| 2411 | self._Row = None
|
|---|
| 2412 |
|
|---|
| 2413 |
|
|---|
| 2414 | class ArcGISInsertCursor(_ArcGISWritableCursor, InsertCursor):
|
|---|
| 2415 | __doc__ = DynamicDocString()
|
|---|
| 2416 |
|
|---|
| 2417 | def _Open(self):
|
|---|
| 2418 | self._Row = None
|
|---|
| 2419 | self._Cursor = GeoprocessorManager.GetWrappedGeoprocessor().InsertCursor(self._Table._GetFullPath())
|
|---|
| 2420 |
|
|---|
| 2421 | def _Close(self):
|
|---|
| 2422 | self._Row = None
|
|---|
| 2423 | self._Cursor = None
|
|---|
| 2424 | super(ArcGISInsertCursor, self)._Close()
|
|---|
| 2425 |
|
|---|
| 2426 | def _SetValue(self, field, value):
|
|---|
| 2427 | if self._Row is None:
|
|---|
| 2428 | self._Row = self._Cursor.NewRow()
|
|---|
| 2429 | super(ArcGISInsertCursor, self)._SetValue(field, value)
|
|---|
| 2430 |
|
|---|
| 2431 | def _SetGeometry(self, geometry):
|
|---|
| 2432 | if self._Row is None:
|
|---|
| 2433 | self._Row = self._Cursor.NewRow()
|
|---|
| 2434 | super(ArcGISInsertCursor, self)._SetGeometry(geometry)
|
|---|
| 2435 |
|
|---|
| 2436 | def _InsertRow(self):
|
|---|
| 2437 | self._Cursor.InsertRow(self._Row)
|
|---|
| 2438 | self._Row = None
|
|---|
| 2439 |
|
|---|
| 2440 |
|
|---|
| 2441 | ###############################################################################
|
|---|
| 2442 | # Metadata: module
|
|---|
| 2443 | ###############################################################################
|
|---|
| 2444 |
|
|---|
| 2445 | from GeoEco.ArcGIS import ArcGISDependency
|
|---|
| 2446 | from GeoEco.Metadata import *
|
|---|
| 2447 | from GeoEco.Types import *
|
|---|
| 2448 |
|
|---|
| 2449 | AddModuleMetadata(shortDescription=_(u'Classes used to access tabular and raster datasets through the ArcGIS geoprocessor.'))
|
|---|
| 2450 |
|
|---|
| 2451 | ###############################################################################
|
|---|
| 2452 | # Metadata: ArcGISTabularLayer class
|
|---|
| 2453 | ###############################################################################
|
|---|
| 2454 |
|
|---|
| 2455 | AddClassMetadata(ArcGISTabularLayer,
|
|---|
| 2456 | shortDescription=_(u'Represents a table, feature class, feature layer, or other table-like object accessible through the ArcGIS geoprocessor.'))
|
|---|
| 2457 |
|
|---|
| 2458 | # Public method: ArcGISTabularLayer.Open
|
|---|
| 2459 |
|
|---|
| 2460 | AddMethodMetadata(ArcGISTabularLayer.Open,
|
|---|
| 2461 | shortDescription=_(u'Returns an ArcGISTabularLayer instance representing a table, feature class, or other table-like object accessible through the ArcGIS geoprocessor.'),
|
|---|
| 2462 | isExposedToPythonCallers=True,
|
|---|
| 2463 | dependencies=[ArcGISDependency(9, 1)])
|
|---|
| 2464 |
|
|---|
| 2465 | AddArgumentMetadata(ArcGISTabularLayer.Open, u'cls',
|
|---|
| 2466 | typeMetadata=ClassOrClassInstanceTypeMetadata(cls=ArcGISTabularLayer),
|
|---|
| 2467 | description=_(u'%s class or an instance of it.') % ArcGISTabularLayer.__name__)
|
|---|
| 2468 |
|
|---|
| 2469 | AddArgumentMetadata(ArcGISTabularLayer.Open, u'name',
|
|---|
| 2470 | typeMetadata=UnicodeStringTypeMetadata(),
|
|---|
| 2471 | description=_(
|
|---|
| 2472 | u"""Name of the table-like object to open.
|
|---|
| 2473 |
|
|---|
| 2474 | The object can be a table, table view, feature class, feature layer,
|
|---|
| 2475 | raster dataset, raster layer, or anything else that has fields and
|
|---|
| 2476 | that may be accessed through the ArcGIS geoprocessor."""))
|
|---|
| 2477 |
|
|---|
| 2478 | AddResultMetadata(ArcGISTabularLayer.Open, u'dataset',
|
|---|
| 2479 | typeMetadata=ClassInstanceTypeMetadata(cls=ArcGISTabularLayer),
|
|---|
| 2480 | description=_(
|
|---|
| 2481 | u"""An ArcGISTabularLayer instance representing the opened object.
|
|---|
| 2482 |
|
|---|
| 2483 | Due to the design of the ArcGIS geoprocessor, the returned instance
|
|---|
| 2484 | does not actually maintain an open connection or handle to the object.
|
|---|
| 2485 | The object will not be "locked" in any way. The instance does cache a
|
|---|
| 2486 | snapshot of the field list and other properties of the object and
|
|---|
| 2487 | assumes that these will not be changed for the lifetime of the
|
|---|
| 2488 | instance, except by calls placed to that instance."""))
|
|---|
| 2489 |
|
|---|
| 2490 | # Public method: ArcGISTabularLayer.Create
|
|---|
| 2491 |
|
|---|
| 2492 | AddMethodMetadata(ArcGISTabularLayer.Create,
|
|---|
| 2493 | shortDescription=_(u'Creates a table or feature class with the ArcGIS geoprocessor and returns an ArcGISTabularLayer instance representing it.'),
|
|---|
| 2494 | isExposedToPythonCallers=True,
|
|---|
| 2495 | dependencies=[ArcGISDependency(9, 1)])
|
|---|
| 2496 |
|
|---|
| 2497 | CopyArgumentMetadata(ArcGISTabularLayer.Open, u'cls', ArcGISTabularLayer.Create, u'cls')
|
|---|
| 2498 |
|
|---|
| 2499 | AddArgumentMetadata(ArcGISTabularLayer.Create, u'workspace',
|
|---|
| 2500 | typeMetadata=UnicodeStringTypeMetadata(),
|
|---|
| 2501 | description=_(
|
|---|
| 2502 | u"""Existing ArcGIS workspace that the table or feature class should
|
|---|
| 2503 | be created within."""))
|
|---|
| 2504 |
|
|---|
| 2505 | AddArgumentMetadata(ArcGISTabularLayer.Create, u'name',
|
|---|
| 2506 | typeMetadata=UnicodeStringTypeMetadata(),
|
|---|
| 2507 | description=_(
|
|---|
| 2508 | u"""Name of the table or feature class to create.
|
|---|
| 2509 |
|
|---|
| 2510 | The ArcGIS Create Table geoprocessing tool will be used to create
|
|---|
| 2511 | tables. The Create Feature Class tool will be used to create
|
|---|
| 2512 | features."""))
|
|---|
| 2513 |
|
|---|
| 2514 | AddArgumentMetadata(ArcGISTabularLayer.Create, u'geometryType',
|
|---|
| 2515 | typeMetadata=UnicodeStringTypeMetadata(canBeNone=True, allowedValues=[u'Point', u'Point25D', u'MultiPoint', u'MultiPoint25D', u'LineString', u'MultiLineString', u'LineString25D', u'MultiLineString25D', u'Polygon', u'MultiPolygon', u'Polygon25D', u'MultiPolygon25D'], makeLowercase=True),
|
|---|
| 2516 | description=_(
|
|---|
| 2517 | u"""Type of geometry to use to create the feature class, or None if a
|
|---|
| 2518 | table should be created.
|
|---|
| 2519 |
|
|---|
| 2520 | Note that ArcGIS does not support certain geometry types, such as
|
|---|
| 2521 | GeometryCollection, that may be available through other libraries such
|
|---|
| 2522 | as OGR."""))
|
|---|
| 2523 |
|
|---|
| 2524 | AddArgumentMetadata(ArcGISTabularLayer.Create, u'srType',
|
|---|
| 2525 | typeMetadata=UnicodeStringTypeMetadata(canBeNone=True, allowedValues=[u'WKT', u'ArcGIS', u'Proj4', u'Obj'], makeLowercase=True),
|
|---|
| 2526 | description=_(
|
|---|
| 2527 | u"""Type of spatial reference that you are providing for the sr
|
|---|
| 2528 | parameter, or None if the spatial reference should remain undefined.
|
|---|
| 2529 | This parameter is ignored when creating a table (i.e. if geometryType
|
|---|
| 2530 | is None).
|
|---|
| 2531 |
|
|---|
| 2532 | The allowed values are:
|
|---|
| 2533 |
|
|---|
| 2534 | * WKT - sr is a WKT string in standard OGC format.
|
|---|
| 2535 |
|
|---|
| 2536 | * ArcGIS - sr is a WKT string in ESRI format, typically obtained from
|
|---|
| 2537 | a dataset produced by ArcGIS. (The ESRI format differs from the OGC
|
|---|
| 2538 | standard; various projections and parameters are named differently
|
|---|
| 2539 | and certain nodes are not recognized. See the OSR documentation for
|
|---|
| 2540 | more information.)
|
|---|
| 2541 |
|
|---|
| 2542 | * Proj4 - sr is a string suitable for passing to the Proj4 utility.
|
|---|
| 2543 |
|
|---|
| 2544 | * Obj - sr is an instance of the OSR SpatialReference class.
|
|---|
| 2545 |
|
|---|
| 2546 | You may provide a spatial reference of any of these types. If the type
|
|---|
| 2547 | is not 'ArcGIS', this function will use the OSR MorphToESRI function
|
|---|
| 2548 | to convert the spatial reference to ESRI format prior to creating the
|
|---|
| 2549 | feature class. Not all spatial references can be converted properly to
|
|---|
| 2550 | ESRI format. In some cases, OSR may report an error, but in others it
|
|---|
| 2551 | may create an ESRI string that is syntactically correct but is missing
|
|---|
| 2552 | expected WKT parameters. When using a spatial reference for the first
|
|---|
| 2553 | time, be sure to check the resulting feature class with ArcGIS to
|
|---|
| 2554 | ensure the spatial reference is correct."""))
|
|---|
| 2555 |
|
|---|
| 2556 | AddArgumentMetadata(ArcGISTabularLayer.Create, u'sr',
|
|---|
| 2557 | typeMetadata=AnyObjectTypeMetadata(canBeNone=True),
|
|---|
| 2558 | description=_(
|
|---|
| 2559 | u"""Spatial reference to use when creating the feature class, or None
|
|---|
| 2560 | if the spatial reference should remain undefined. This parameter is
|
|---|
| 2561 | ignored when creating a table.
|
|---|
| 2562 |
|
|---|
| 2563 | If you provide this parameter you must also provide the srType
|
|---|
| 2564 | parameter, which determines what type of spatial reference you are
|
|---|
| 2565 | providing."""))
|
|---|
| 2566 |
|
|---|
| 2567 | AddArgumentMetadata(ArcGISTabularLayer.Create, u'configKeyword',
|
|---|
| 2568 | typeMetadata=AnyObjectTypeMetadata(canBeNone=True),
|
|---|
| 2569 | description=_(
|
|---|
| 2570 | u"""ArcGIS configuration keyword that determines storage parameters
|
|---|
| 2571 | for the table or feature class. This parameter is usually needed only
|
|---|
| 2572 | in specific uncommon scenarios. For more information, please see the
|
|---|
| 2573 | ArcGIS documentation for the Create Table or Create Feature Class
|
|---|
| 2574 | tool."""))
|
|---|
| 2575 |
|
|---|
| 2576 | AddArgumentMetadata(ArcGISTabularLayer.Create, u'spatialGrid1',
|
|---|
| 2577 | typeMetadata=FloatTypeMetadata(canBeNone=True, minValue=0.0),
|
|---|
| 2578 | description=_(
|
|---|
| 2579 | u"""The size of the feature class's first spatial grid index. This
|
|---|
| 2580 | parameter is ignored when creating a table.
|
|---|
| 2581 |
|
|---|
| 2582 | The ArcGIS 9.3 documentation for the Create Feature Class tool
|
|---|
| 2583 | provides the following description for this parameter:
|
|---|
| 2584 |
|
|---|
| 2585 | "The following formats support spatial index grids: personal
|
|---|
| 2586 | geodatabase, file geodatabase or ArcSDE geodatabase. If this value is
|
|---|
| 2587 | left blank (or 0) a valid grid size will be calculated automatically."
|
|---|
| 2588 | """))
|
|---|
| 2589 |
|
|---|
| 2590 | AddArgumentMetadata(ArcGISTabularLayer.Create, u'spatialGrid2',
|
|---|
| 2591 | typeMetadata=FloatTypeMetadata(canBeNone=True, minValue=0.0),
|
|---|
| 2592 | description=_(
|
|---|
| 2593 | u"""The size of the feature class's second spatial grid index. This
|
|---|
| 2594 | parameter is ignored when creating a table.
|
|---|
| 2595 |
|
|---|
| 2596 | The ArcGIS 9.3 documentation for the Create Feature Class tool
|
|---|
| 2597 | provides the following description for this parameter:
|
|---|
| 2598 |
|
|---|
| 2599 | "This value must be at least 3 times larger than the first index grid.
|
|---|
| 2600 | The following formats support more than one spatial index grids: file
|
|---|
| 2601 | geodatabase or ArcSDE geodatabase. For more information, see the Add
|
|---|
| 2602 | Spatial Index tool. Note that personal geodatabase support only one
|
|---|
| 2603 | spatial index grid." """))
|
|---|
| 2604 |
|
|---|
| 2605 | AddArgumentMetadata(ArcGISTabularLayer.Create, u'spatialGrid3',
|
|---|
| 2606 | typeMetadata=FloatTypeMetadata(canBeNone=True, minValue=0.0),
|
|---|
| 2607 | description=_(
|
|---|
| 2608 | u"""The size of the feature class's third spatial grid index. This
|
|---|
| 2609 | parameter is ignored when creating a table.
|
|---|
| 2610 |
|
|---|
| 2611 | The ArcGIS 9.3 documentation for the Create Feature Class tool
|
|---|
| 2612 | provides the following description for this parameter:
|
|---|
| 2613 |
|
|---|
| 2614 | "This value must be at least 3 times larger than the second index
|
|---|
| 2615 | grid. The following formats support more than one spatial index grids:
|
|---|
| 2616 | file geodatabase or ArcSDE geodatabase. Note that personal geodatabase
|
|---|
| 2617 | support only one spatial index grid." """))
|
|---|
| 2618 |
|
|---|
| 2619 | AddArgumentMetadata(ArcGISTabularLayer.Create, u'autoDeleteFieldAddedByArcGIS',
|
|---|
| 2620 | typeMetadata=BooleanTypeMetadata(),
|
|---|
| 2621 | description=_(
|
|---|
| 2622 | u"""If True (the default) and you are creating a shapefile, the "Id"
|
|---|
| 2623 | field added by ArcGIS will be automatically deleted when you add the
|
|---|
| 2624 | first field to the shapefile. This parameter is ignored when creating
|
|---|
| 2625 | a table or a feature class that is not a shapefile.
|
|---|
| 2626 |
|
|---|
| 2627 | ArcGIS requires that shapefiles have at least one field, in addition
|
|---|
| 2628 | to the OID and Shape fields. To meet this requirement, ArcGIS
|
|---|
| 2629 | automatically adds a field called "Id" to new shapefiles. The default
|
|---|
| 2630 | behavior of ArcGISTabularLayer is to automatically delete this field
|
|---|
| 2631 | the first time you call AddField on the returned ArcGISTabularLayer
|
|---|
| 2632 | instance, under the assumption that most people do not want the Id
|
|---|
| 2633 | field. You can override this behavior by passing False for this
|
|---|
| 2634 | parameter."""))
|
|---|
| 2635 |
|
|---|
| 2636 | AddResultMetadata(ArcGISTabularLayer.Create, u'dataset',
|
|---|
| 2637 | typeMetadata=ClassInstanceTypeMetadata(cls=ArcGISTabularLayer),
|
|---|
| 2638 | description=_(
|
|---|
| 2639 | u"""An ArcGISTabularLayer instance representing the created table or
|
|---|
| 2640 | feature class.
|
|---|
| 2641 |
|
|---|
| 2642 | Due to the design of the ArcGIS geoprocessor, the returned instance
|
|---|
| 2643 | does not actually maintain an open connection or handle to the object.
|
|---|
| 2644 | The object will not be "locked" in any way. The instance does cache a
|
|---|
| 2645 | snapshot of the field list and other properties of the object and
|
|---|
| 2646 | assumes that these will not be changed for the lifetime of the
|
|---|
| 2647 | instance, except by calls placed to that instance."""))
|
|---|
| 2648 |
|
|---|
| 2649 | # Public method: ArcGISTabularLayer.Delete
|
|---|
| 2650 |
|
|---|
| 2651 | AddMethodMetadata(ArcGISTabularLayer.Delete,
|
|---|
| 2652 | shortDescription=_(u'Deletes a table, feature class, or other table-like object accessible through the ArcGIS geoprocessor.'),
|
|---|
| 2653 | isExposedToPythonCallers=True,
|
|---|
| 2654 | dependencies=[ArcGISDependency(9, 1)])
|
|---|
| 2655 |
|
|---|
| 2656 | CopyArgumentMetadata(ArcGISTabularLayer.Open, u'cls', ArcGISTabularLayer.Delete, u'cls')
|
|---|
| 2657 |
|
|---|
| 2658 | AddArgumentMetadata(ArcGISTabularLayer.Delete, u'name',
|
|---|
| 2659 | typeMetadata=UnicodeStringTypeMetadata(),
|
|---|
| 2660 | description=_(
|
|---|
| 2661 | u"""Name of the table-like object to delete with the ArcGIS
|
|---|
| 2662 | geoprocessor.
|
|---|
| 2663 |
|
|---|
| 2664 | The object can be a table, feature class, feature layer, raster
|
|---|
| 2665 | dataset, raster layer, or anything else that has fields and that may
|
|---|
| 2666 | be accessed through the ArcGIS geoprocessor. The ArcGIS Delete
|
|---|
| 2667 | geoprocessing tool will be used to delete it.
|
|---|
| 2668 |
|
|---|
| 2669 | In ArcGIS 9.2, 9.3, 9.3.1, and possibly later versions, that tool was
|
|---|
| 2670 | not capable of deleting tool views. If you use
|
|---|
| 2671 | ArcGISTabularLayer.Delete to delete a table view with ArcGIS
|
|---|
| 2672 | 9.2-9.3.1, it will detect the problem, report a warning saying the
|
|---|
| 2673 | table view was not deleted, and return successfully. Because we did
|
|---|
| 2674 | not know how later versions of ArcGIS would behave, we did not
|
|---|
| 2675 | implement this detection for later versions. Therefore, if the problem
|
|---|
| 2676 | exists in later versions, it is likely that an error will be reported
|
|---|
| 2677 | and the function will fail."""))
|
|---|
| 2678 |
|
|---|
| 2679 | AddArgumentMetadata(ArcGISTabularLayer.Delete, u'dataType',
|
|---|
| 2680 | typeMetadata=UnicodeStringTypeMetadata(canBeNone=True),
|
|---|
| 2681 | description=_(
|
|---|
| 2682 | u"""The type of object to delete, in the case where there are multiple
|
|---|
| 2683 | objects with the same name but different data types.
|
|---|
| 2684 |
|
|---|
| 2685 | This situation is rare, so this parameter should usually be omitted.
|
|---|
| 2686 | The ArcGIS documentation mentions only one case, where a geodatbase
|
|---|
| 2687 | contains a feature dataset and feature class with the same name. In
|
|---|
| 2688 | that case, you would specify 'FeatureClass' for this parameter to
|
|---|
| 2689 | delete the feature class."""))
|
|---|
| 2690 |
|
|---|
| 2691 | AddArgumentMetadata(ArcGISTabularLayer.Delete, u'failIfDoesNotExist',
|
|---|
| 2692 | typeMetadata=BooleanTypeMetadata(),
|
|---|
| 2693 | description=_(
|
|---|
| 2694 | u"""If True, this function will fail if the specified field does not
|
|---|
| 2695 | exist. If False (the default), this function will succeed but report a
|
|---|
| 2696 | warning if the field does not exist."""))
|
|---|
| 2697 |
|
|---|
| 2698 | # Public properties
|
|---|
| 2699 |
|
|---|
| 2700 | AddPropertyMetadata(ArcGISTabularLayer.Name,
|
|---|
| 2701 | typeMetadata=UnicodeStringTypeMetadata(),
|
|---|
| 2702 | shortDescription=_(u'ArcGIS\'s name of this object, suitable for passing to ArcGIS geoprocessing tools.'))
|
|---|
| 2703 |
|
|---|
| 2704 | AddPropertyMetadata(ArcGISTabularLayer.DataType,
|
|---|
| 2705 | typeMetadata=UnicodeStringTypeMetadata(),
|
|---|
| 2706 | shortDescription=_(u'The ArcGIS data type of this object, obtained from the DataType property of the geoprocessor Describe object.'))
|
|---|
| 2707 |
|
|---|
| 2708 | AddPropertyMetadata(ArcGISTabularLayer.PhysicalDataType,
|
|---|
| 2709 | typeMetadata=UnicodeStringTypeMetadata(),
|
|---|
| 2710 | shortDescription=_(u'If this object is an ArcGIS feature layer, this is the ArcGIS data type of the feature class that backs the layer, obtained from the DataType property of the geoprocessor Describe object.'))
|
|---|
| 2711 |
|
|---|
| 2712 | AddPropertyMetadata(ArcGISTabularLayer.AutoDeleteFieldAddedByArcGIS,
|
|---|
| 2713 | typeMetadata=BooleanTypeMetadata(),
|
|---|
| 2714 | shortDescription=_(u'If this object was returned by ArcGISTabularLayer.Create, this is the value of the autoDeleteFieldAddedByArcGIS parameter passed to Create.'))
|
|---|
| 2715 |
|
|---|
| 2716 | ###############################################################################
|
|---|
| 2717 | # Metadata: ArcGISSelectCursor class
|
|---|
| 2718 | ###############################################################################
|
|---|
| 2719 |
|
|---|
| 2720 | AddClassMetadata(ArcGISSelectCursor,
|
|---|
| 2721 | shortDescription=_(u'Represents a forward-only cursor used to read rows from a table, feature class, or other table-like object accessible through the ArcGIS geoprocessor.'),
|
|---|
| 2722 | longDescription=_(
|
|---|
| 2723 | u"""To use the orderBy parameter when opening a cursor, ArcGIS 9.2 or
|
|---|
| 2724 | later is required."""))
|
|---|
| 2725 |
|
|---|
| 2726 | ###############################################################################
|
|---|
| 2727 | # Metadata: ArcGISUpdateCursor class
|
|---|
| 2728 | ###############################################################################
|
|---|
| 2729 |
|
|---|
| 2730 | AddClassMetadata(ArcGISUpdateCursor,
|
|---|
| 2731 | shortDescription=_(u'Represents a forward-only cursor used to update or delete rows in a table, feature class, or other table-like object accessible through the ArcGIS geoprocessor.'),
|
|---|
| 2732 | longDescription=_(
|
|---|
| 2733 | u"""Update cursors are fully supported, including updating and
|
|---|
| 2734 | deleting rows, for most table-like objects that ArcGIS can access.
|
|---|
| 2735 |
|
|---|
| 2736 | To use the orderBy parameter when opening a cursor, ArcGIS 9.2 or
|
|---|
| 2737 | later is required."""))
|
|---|
| 2738 |
|
|---|
| 2739 | ###############################################################################
|
|---|
| 2740 | # Metadata: ArcGISInsertCursor class
|
|---|
| 2741 | ###############################################################################
|
|---|
| 2742 |
|
|---|
| 2743 | AddClassMetadata(ArcGISInsertCursor,
|
|---|
| 2744 | shortDescription=_(u'Represents a forward-only cursor used to insert rows into a table, feature class, or other table-like object accessible through the ArcGIS geoprocessor.'),
|
|---|
| 2745 | longDescription=_(
|
|---|
| 2746 | u"""Insert cursors are supported for most table-like objects that
|
|---|
| 2747 | ArcGIS can access."""))
|
|---|
| 2748 |
|
|---|
| 2749 | ###############################################################################
|
|---|
| 2750 | # Metadata: ArcGISWorkspace class
|
|---|
| 2751 | ###############################################################################
|
|---|
| 2752 |
|
|---|
| 2753 | AddClassMetadata(ArcGISWorkspace,
|
|---|
| 2754 | shortDescription=_(u'TODO: Add description'))
|
|---|
| 2755 |
|
|---|
| 2756 | # TODO: Add metadata
|
|---|
| 2757 |
|
|---|
| 2758 | ###############################################################################
|
|---|
| 2759 | # Metadata: ArcGISRaster class
|
|---|
| 2760 | ###############################################################################
|
|---|
| 2761 |
|
|---|
| 2762 | _UseUnscaledDataDescription = _(
|
|---|
| 2763 | u"""If True and the original data is stored as integers that are
|
|---|
| 2764 | processed through a "scaling equation" to produce the actual floating
|
|---|
| 2765 | point values, the output rasters will be created with the integers
|
|---|
| 2766 | rather the floating point values. If False, or the original data is
|
|---|
| 2767 | not processed through a scaling equation, the output rasters will be
|
|---|
| 2768 | created using the data's original data type.""")
|
|---|
| 2769 |
|
|---|
| 2770 | _CalculateStatisticsDescription = _(
|
|---|
| 2771 | u"""If True, statistics will be calculated for the output rasters
|
|---|
| 2772 | using the ArcGIS Calculate Statistics geoprocessing tool. This is
|
|---|
| 2773 | usually a good idea for most raster formats because ArcGIS will only
|
|---|
| 2774 | display them with helpful colors and gradients if statistics have been
|
|---|
| 2775 | calculated. For certain formats, the explicit calculation of
|
|---|
| 2776 | statistics is not necessary because it happens automatically when the
|
|---|
| 2777 | rasters are created. If you're using one of those formats, you can set
|
|---|
| 2778 | this option to False to speed up the creation of the output
|
|---|
| 2779 | rasters.""")
|
|---|
| 2780 |
|
|---|
| 2781 | _BuildRATDescription = _(
|
|---|
| 2782 | u"""If True and the output rasters use an integer data type, raster
|
|---|
| 2783 | attribute tables (RATs) will be built for the output rasters using the
|
|---|
| 2784 | ArcGIS Build Raster Attribute Table tool. Raster attribute tables are
|
|---|
| 2785 | essentially histograms: they store the counts of cells having each
|
|---|
| 2786 | value. If you do not need this information, you can skip the building
|
|---|
| 2787 | of raster attribute tables to speed up the creation of the output
|
|---|
| 2788 | rasters. Note that for certain raster formats, such as ArcInfo Binary
|
|---|
| 2789 | Grid, the explicit buliding of raster attribute tables is not
|
|---|
| 2790 | necessary because it happens automatically when the rasters are
|
|---|
| 2791 | created.
|
|---|
| 2792 |
|
|---|
| 2793 | This option is ignored if the output rasters use a floating point data
|
|---|
| 2794 | type.""")
|
|---|
| 2795 |
|
|---|
| 2796 | _BuildPyramidsDescription = _(
|
|---|
| 2797 | u"""If True, pyramids will be built for the output rasters using the
|
|---|
| 2798 | ArcGIS Build Pyramids tool. Pyramids, also known as overviews, are
|
|---|
| 2799 | reduced resolution versions of the rasters that can improve the speed
|
|---|
| 2800 | at which they are displayed in the ArcGIS user interface.""")
|
|---|
| 2801 |
|
|---|
| 2802 | AddClassMetadata(ArcGISRaster,
|
|---|
| 2803 | shortDescription=_(u'TODO: Add description'))
|
|---|
| 2804 |
|
|---|
| 2805 | # TODO: Add metadata
|
|---|
| 2806 |
|
|---|
| 2807 | ###############################################################################
|
|---|
| 2808 | # Metadata: ArcGISRasterBand class
|
|---|
| 2809 | ###############################################################################
|
|---|
| 2810 |
|
|---|
| 2811 | AddClassMetadata(ArcGISRasterBand,
|
|---|
| 2812 | shortDescription=_(u'TODO: Add description'))
|
|---|
| 2813 |
|
|---|
| 2814 | # TODO: Add metadata
|
|---|
| 2815 |
|
|---|
| 2816 | ###############################################################################
|
|---|
| 2817 | # Metadata: ArcGISTable class
|
|---|
| 2818 | ###############################################################################
|
|---|
| 2819 |
|
|---|
| 2820 | AddClassMetadata(ArcGISTable,
|
|---|
| 2821 | shortDescription=_(u'TODO: Add description'))
|
|---|
| 2822 |
|
|---|
| 2823 | # Public method: ArcGISTable.__init__
|
|---|
| 2824 |
|
|---|
| 2825 | AddMethodMetadata(ArcGISTable.__init__,
|
|---|
| 2826 | shortDescription=_(u'Constructs an instance representing table accessible through the ArcGIS geoprocessor.'),
|
|---|
| 2827 | isExposedToPythonCallers=True,
|
|---|
| 2828 | dependencies=[ArcGISDependency(9, 1)])
|
|---|
| 2829 |
|
|---|
| 2830 | AddArgumentMetadata(ArcGISTable.__init__, u'self',
|
|---|
| 2831 | typeMetadata=ClassInstanceTypeMetadata(cls=ArcGISTable),
|
|---|
| 2832 | description=_(u'ArcGISTable instance.'))
|
|---|
| 2833 |
|
|---|
| 2834 | AddArgumentMetadata(ArcGISTable.__init__, u'path',
|
|---|
| 2835 | typeMetadata=UnicodeStringTypeMetadata(),
|
|---|
| 2836 | description=_(
|
|---|
| 2837 | u"""Full path or name of the table-like object to open. This can be a
|
|---|
| 2838 | table, table view, shapefile, ArcInfo coverage, geodatabase feature
|
|---|
| 2839 | class, feature layer, raster dataset, raster layer, or anything else
|
|---|
| 2840 | that has fields and that may be accessed through the ArcGIS
|
|---|
| 2841 | geoprocessor."""))
|
|---|
| 2842 |
|
|---|
| 2843 | AddArgumentMetadata(ArcGISTable.__init__, u'autoDeleteFieldAddedByArcGIS',
|
|---|
| 2844 | typeMetadata=BooleanTypeMetadata(),
|
|---|
| 2845 | description=_(
|
|---|
| 2846 | u"""If True (the default) and you are opening a shapefile or dBASE
|
|---|
| 2847 | table (.DBF file), the "Id" field (if a shapefile) or "Field1" (if a
|
|---|
| 2848 | dBASE table) that added by ArcGIS when the shapefile or table was
|
|---|
| 2849 | created will be automatically deleted the first time you call the
|
|---|
| 2850 | AddField function. This parameter is ignored you open a table or a
|
|---|
| 2851 | feature class that is not a shapefile or dBASE table.
|
|---|
| 2852 |
|
|---|
| 2853 | ArcGIS requires that shapefiles and dBASE tables have at least one
|
|---|
| 2854 | field, in addition to the OID and Shape fields. To meet this
|
|---|
| 2855 | requirement, ArcGIS automatically adds a field called "Id" to new
|
|---|
| 2856 | shapefiles and "Field1" to dBASE tables . The default behavior of
|
|---|
| 2857 | ArcGISTable is to automatically delete this field the first time you
|
|---|
| 2858 | call AddField on the returned ArcGISTable instance, under the
|
|---|
| 2859 | assumption that most people do not want these fields. You can override
|
|---|
| 2860 | this behavior by passing False for this parameter."""))
|
|---|
| 2861 |
|
|---|
| 2862 | CopyArgumentMetadata(CollectibleObject.__init__, u'parentCollection', ArcGISTable.__init__, u'parentCollection')
|
|---|
| 2863 | CopyArgumentMetadata(CollectibleObject.__init__, u'queryableAttributeValues', ArcGISTable.__init__, u'queryableAttributeValues')
|
|---|
| 2864 | CopyArgumentMetadata(CollectibleObject.__init__, u'lazyPropertyValues', ArcGISTable.__init__, u'lazyPropertyValues')
|
|---|
| 2865 | CopyArgumentMetadata(DatasetCollection.__init__, u'cacheDirectory', ArcGISTable.__init__, u'cacheDirectory') # TODO: Remove?
|
|---|
| 2866 |
|
|---|
| 2867 | AddResultMetadata(ArcGISTable.__init__, u'table',
|
|---|
| 2868 | typeMetadata=ClassInstanceTypeMetadata(cls=ArcGISTable),
|
|---|
| 2869 | description=_(
|
|---|
| 2870 | u"""An ArcGISTable instance representing the object."""))
|
|---|
| 2871 |
|
|---|
| 2872 | # TODO: Add metadata
|
|---|
| 2873 |
|
|---|
| 2874 | ###############################################################################
|
|---|
| 2875 | # Names exported by this module
|
|---|
| 2876 | ###############################################################################
|
|---|
| 2877 |
|
|---|
| 2878 | __all__ = ['ArcGISWorkspace', 'ArcGISRaster', 'ArcGISRasterBand', 'ArcGISTable', 'ArcGISSelectCursor', 'ArcGISUpdateCursor', 'ArcGISInsertCursor']
|
|---|