27.2.13

Converting ArcGIS Feature Class to PyTables

Here is an example of converting a point geometry type ArcGIS Feature Class to a PyTables table. I decided to avoid using the arcpy.da.FeatureClassToNumpyArray, because I want more flexibility on how data is converted (e.g. geometry types, blobs, rasters, dates). Right now this only handles a point feature class, but I hope to add in other geometry types and make is into a module:
import arcpy
import tables
from tables import *

def feature_class_to_hdf5(featureClass, tableName, groupName='', indexFields=[]):
    '''
    TODO: add polygon and line geometry type support
    TODO: add blob field
    TODO: add raster support
    TODO: add Date logic
    '''
    try:
        #Validate feature class
        desc = arcpy.Describe(featureClass)
        if desc.dataType != 'FeatureClass':
            raise ValueError('featureClass must be a featureClass')
        if desc.featureType != 'Simple':
            raise ValueError('feature_class_to_table only support simple feature types')
        if desc.shapeType != 'Point':
            raise ValueError('sorry')

        #Construct output location for hdf5
        output_file_name = os.path.splitext(os.path.split(desc.catalogPath)[1])[0]
        if '.gdb' in desc.catalogPath:
            output_directory = os.path.split(os.path.split(desc.catalogPath)[0])[0]
        elif '.shp' in desc.catalogPath:
            output_directory = os.path.split(desc.catalogPath)[0]
        output_hd5_path = os.path.join(output_directory, output_file_name + '.h5')
        
        #Map ArcGIS Field Types to PyTable Field Types
        fields_dictionary = {}
        fields_dictionary['OID'] = Int32Col(dflt=1, pos = 0)
        fields_dictionary['X'] = Float64Col(dflt=1, pos = 1)
        fields_dictionary['Y'] = Float64Col(dflt=1, pos = 2)

        h5f_field_names = ['OID', 'X', 'Y']
        arc_field_names = ['OID@', 'SHAPE@X', 'SHAPE@Y']
        for f in desc.fields:
            if f.type in ['String','GUID']:
                fields_dictionary[str(f.name)] = StringCol(itemsize=f.length, dflt="n/a", pos=len(arc_field_names))
            elif f.type == 'Single':
                fields_dictionary[str(f.name)] = Float32Col(dflt=1, pos=len(arc_field_names))
            elif f.type == 'Double':
                fields_dictionary[str(f.name)] = Float64Col(dflt=1, pos=len(arc_field_names))
            elif f.type == 'SmallInteger':
                fields_dictionary[str(f.name)] = Int16Col(dflt=1, pos=len(arc_field_names))
            elif f.type == 'Integer':
                fields_dictionary[str(f.name)] = Int32Col(dflt=1, pos=len(arc_field_names))
            elif f.type == 'Date':
                fields_dictionary[str(f.name)] = Time64Col(dflt=1, pos=len(arc_field_names)) #Needs test...
            else:
                continue
            arc_field_names.append(str(f.name))
            h5f_field_names.append(str(f.name))

        #Create PyTables DescriptionClass
        DescriptionClass = type(tableName, (IsDescription,), fields_dictionary)

        with tables.openFile(output_hd5_path, 'w', title=featureClass) as h5_file:
            filters = Filters(complib='blosc', complevel=1)
            h5_feature_class = h5_file.createTable('/', tableName, DescriptionClass, filters=filters)
            with arcpy.da.SearchCursor(featureClass, field_names=arc_field_names) as cursor:
                for r in cursor:
                    new_row = h5_feature_class.row
                    for i, fieldName in enumerate(h5f_field_names):
                        try:
                            new_row[fieldName] = r[i]
                        except TypeError as e:
                            if 'unicode' in str(e).lower():
                                new_row[fieldName] = unicodedata.normalize('NFKD', r[i]).encode('ascii','ignore')
                            else:
                                raise
                    new_row.append()
            h5_file.flush()

    except Exception as e:
        print e
        raise

5 comments: