'''
Name: avi_schema.py

Overview: Provides methods for build AVI geodatabase schema.

Description:
    This class is used to generate AVI feature classes as well as define
    other schema objects such as domains.
    
Notes:

Author: Doug Crane
        Sept, 2009

Modifications:

'''

import pdb
import os
import sys

import arcpy

# General SRD utilities that may be needed.
from srd.srd_exception import *
import srd.srd_logging as srd_logging
import srd.srd_misc as srd_misc
import srd.srd_temp_ws as srd_temp_ws
import srd.srd_recordset as srd_recordset

from avi_record import *

__author__ = 'Doug Crane'
__version__ = '1.0'

__all__ = ['AVI_Schema']

# ----------------------------------------------------------
class AVI_Schema(object):
    '''
    Provides methods for creating AVI geodatabase feature classes
    as well as populating them from the standard AVI coverage structure.
    '''

    # List of standard AVI fields
    # Structure is: NAME,TYPE,PRECISION,SCALE,LENGTH,ALIAS,NULLABLE,REQUIRED    
    fieldDefList = []
    fieldDefList.append(['POLY_NUM','LONG','','','','Polygon ID','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['MOIST_REG','TEXT','','','1','Moisture Regime','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['DENSITY','TEXT','','','1','Density','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['HEIGHT','SHORT','','','','Height','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['SP1','TEXT','','','2','Species 1','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['SP1_PER','SHORT','','','','Species 1 Percent','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['SP2','TEXT','','','2','Species 2','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['SP2_PER','SHORT','','','','Species 2 Percent','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['SP3','TEXT','','','2','Species 3','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['SP3_PER','SHORT','','','','Species 3 Percent','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['SP4','TEXT','','','2','Species 4','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['SP4_PER','SHORT','','','','Species 4 Percent','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['SP5','TEXT','','','2','Species 5','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['SP5_PER','SHORT','','','','Species 5 Percent','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['STRUC','TEXT','','','1','Stand Structure','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['STRUC_VAL','SHORT','','','','Structure Modifier','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['ORIGIN','SHORT','','','','Origin','NULLABLE','NON_REQUIRED']) 
    fieldDefList.append(['TPR','TEXT','','','1','Timber Productivity Rating','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['INITIALS','TEXT','','','2','Interpreters Initials','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['NFL','TEXT','','','2','Non-Forest Vegetated','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['NFL_PER','SHORT','','','','Shrub Cover Percent','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['NAT_NON','TEXT','','','3','Naturally Non-Vegetated','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['ANTH_VEG','TEXT','','','3','Anthropogenic Vegetated','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['ANTH_NON','TEXT','','','3','Anthropogenic Non-Vegetated','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['MOD1','TEXT','','','2','Modifier 1','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['MOD1_EXT','SHORT','','','','Modifier 1 Extent','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['MOD1_YR','SHORT','','','','Modifier 1 Year','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['MOD2','TEXT','','','2','Modifier 2','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['MOD2_EXT','SHORT','','','','Modifier 2 Extent','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['MOD2_YR','SHORT','','','','Modifier 2 Year','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['DATA','TEXT','','','1','Existing Data','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['DATA_YR','SHORT','','','','Existing Data Year','NULLABLE','NON_REQUIRED'])
    
    fieldDefList.append(['UMOIST_REG','TEXT','','','1','U Moisture Regime','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['UDENSITY','TEXT','','','1','U Density','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['UHEIGHT','SHORT','','','','U Height','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['USP1','TEXT','','','2','U Species 1','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['USP1_PER','SHORT','','','','U Species 1 Percent','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['USP2','TEXT','','','2','U Species 2','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['USP2_PER','SHORT','','','','U Species 2 Percent','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['USP3','TEXT','','','2','U Species 3','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['USP3_PER','SHORT','','','','U Species 3 Percent','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['USP4','TEXT','','','2','U Species 4','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['USP4_PER','SHORT','','','','U Species 4 Percent','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['USP5','TEXT','','','2','U Species 5','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['USP5_PER','SHORT','','','','U Species 5 Percent','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['USTRUC','TEXT','','','1','U Stand Structure','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['USTRUC_VAL','SHORT','','','','U Structure Modifier','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['UORIGIN','SHORT','','','','U Origin','NULLABLE','NON_REQUIRED']) 
    fieldDefList.append(['UTPR','TEXT','','','1','U Timber Productivity Rating','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['UINITIALS','TEXT','','','2','U Interpreters Initials','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['UNFL','TEXT','','','2','U Non-Forest Vegetated','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['UNFL_PER','SHORT','','','','U Shrub Cover Percent','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['UNAT_NON','TEXT','','','3','U Naturally Non-Vegetated','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['UANTH_VEG','TEXT','','','3','U Anthropogenic Vegetated','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['UANTH_NON','TEXT','','','3','U Anthropogenic Non-Vegetated','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['UMOD1','TEXT','','','2','U Modifier 1','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['UMOD1_EXT','SHORT','','','','U Modifier 1 Extent','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['UMOD1_YR','SHORT','','','','U Modifier 1 Year','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['UMOD2','TEXT','','','2','U Modifier 2','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['UMOD2_EXT','SHORT','','','','U Modifier 2 Extent','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['UMOD2_YR','SHORT','','','','U Modifier 2 Year','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['UDATA','TEXT','','','1','U Existing Data','NULLABLE','NON_REQUIRED'])
    fieldDefList.append(['UDATA_YR','SHORT','','','','U Existing Data Year','NULLABLE','NON_REQUIRED'])

    # List of all AVI fields that will be indexed.
    indexFieldNames = []
    indexFieldNames.append("moist_reg")
    indexFieldNames.append("density")
    indexFieldNames.append("height")
    indexFieldNames.append("sp1")
    indexFieldNames.append("sp1_per")
    indexFieldNames.append("sp2")
    indexFieldNames.append("sp2_per")
    indexFieldNames.append("sp3")
    indexFieldNames.append("sp3_per")
    indexFieldNames.append("sp4")
    indexFieldNames.append("sp4_per")
    indexFieldNames.append("sp5")
    indexFieldNames.append("sp5_per")
    indexFieldNames.append("struc")
    indexFieldNames.append("struc_val")
    indexFieldNames.append("origin")
    indexFieldNames.append("tpr")
    indexFieldNames.append("nfl")
    indexFieldNames.append("nfl_per")
    indexFieldNames.append("nat_non")
    indexFieldNames.append("anth_veg")
    indexFieldNames.append("anth_non")
    indexFieldNames.append("mod1")
    indexFieldNames.append("mod1_ext")
    indexFieldNames.append("mod1_yr")
    indexFieldNames.append("mod2")
    indexFieldNames.append("mod2_ext")
    indexFieldNames.append("mod2_yr")
    indexFieldNames.append("data")
    indexFieldNames.append("data_yr")
    indexFieldNames.append("umoist_reg")
    indexFieldNames.append("udensity")
    indexFieldNames.append("uheight")
    indexFieldNames.append("usp1")
    indexFieldNames.append("usp1_per")
    indexFieldNames.append("usp2")
    indexFieldNames.append("usp2_per")
    indexFieldNames.append("usp3")
    indexFieldNames.append("usp3_per")
    indexFieldNames.append("usp4")
    indexFieldNames.append("usp4_per")
    indexFieldNames.append("usp5")
    indexFieldNames.append("usp5_per")
    indexFieldNames.append("ustruc")
    indexFieldNames.append("ustruc_val")
    indexFieldNames.append("uorigin")
    indexFieldNames.append("utpr")
    indexFieldNames.append("unfl")
    indexFieldNames.append("unfl_per")
    indexFieldNames.append("unat_non")
    indexFieldNames.append("uanth_veg")
    indexFieldNames.append("uanth_non")
    indexFieldNames.append("umod1")
    indexFieldNames.append("umod1_ext")
    indexFieldNames.append("umod1_yr")
    indexFieldNames.append("umod2")
    indexFieldNames.append("umod2_ext")
    indexFieldNames.append("umod2_yr")
    indexFieldNames.append("udata")
    indexFieldNames.append("udata_yr")


    fieldTypeDict = {}
    fieldTypeDict['poly_num'] = [('integer', 0)]
    fieldTypeDict['moist_reg'] = [('string', 1)]
    fieldTypeDict['density'] = [('string', 1)]
    fieldTypeDict['height'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['sp1'] = [('string', 2)]
    fieldTypeDict['sp1_per'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['sp2'] = [('string', 2)]
    fieldTypeDict['sp2_per'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['sp3'] = [('string', 2)]
    fieldTypeDict['sp3_per'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['sp4'] = [('string', 2)]
    fieldTypeDict['sp4_per'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['sp5'] = [('string', 2)]
    fieldTypeDict['sp5_per'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['struc'] = [('string', 1)]
    fieldTypeDict['struc_val'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['origin'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['tpr'] = [('string', 1)]
    fieldTypeDict['nfl'] = [('string', 2)]
    fieldTypeDict['nfl_per'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['nat_non'] = [('string', 3)]
    fieldTypeDict['anth_veg'] = [('string', 3)]
    fieldTypeDict['anth_non'] = [('string', 3)]
    fieldTypeDict['mod1'] = [('string', 2)]
    fieldTypeDict['mod1_ext'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['mod1_yr'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['mod2'] = [('string', 2)]
    fieldTypeDict['mod2_ext'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['mod2_yr'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['initials'] = [('string', 2)]
    fieldTypeDict['data'] = [('string', 1)]
    fieldTypeDict['data_yr'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['umoist_reg'] = [('string', 1)]
    fieldTypeDict['udensity'] = [('string', 1)]
    fieldTypeDict['uheight'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['usp1'] = [('string', 2)]
    fieldTypeDict['usp1_per'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['usp2'] = [('string', 2)]
    fieldTypeDict['usp2_per'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['usp3'] = [('string', 2)]
    fieldTypeDict['usp3_per'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['usp4'] = [('string', 2)]
    fieldTypeDict['usp4_per'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['usp5'] = [('string', 2)]
    fieldTypeDict['usp5_per'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['ustruc'] = [('string', 1)]
    fieldTypeDict['ustruc_val'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['uorigin'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['utpr'] = [('string', 1)]
    fieldTypeDict['unfl'] = [('string', 2)]
    fieldTypeDict['unfl_per'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['unat_non'] = [('string', 3)]
    fieldTypeDict['uanth_veg'] = [('string', 3)]
    fieldTypeDict['uanth_non'] = [('string', 3)]
    fieldTypeDict['umod1'] = [('string', 2)]
    fieldTypeDict['umod1_ext'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['umod1_yr'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['umod2'] = [('string', 2)]
    fieldTypeDict['umod2_ext'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['umod2_yr'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['uinitials'] = [('string', 2)]
    fieldTypeDict['udata'] = [('string', 1)]
    fieldTypeDict['udata_yr'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['photo_yr'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['dist_ptrn'] = [('integer', 0), ('smallinteger', 0)]
    fieldTypeDict['aris'] = [('string', 11)]
    
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def __init__(self):
        '''
        Parameters:
        '''

        self._logChn = srd_logging.SRD_Log()

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def assignDomains(self, gdbPath, tableName):
        '''
        Assigns domains to associated fields.

        Assumes that all domains have been defined in the
        geodatabase containing the AVI table.

        Parameters:
            gdbPath - path to GDB containing the AVI table.

            tableName - name of table or feature class containing
                        AVI fields.

        '''

        # Build a list of existing domains so we determine if an assignment can be made.
        existingDomainList = [domainName.upper() for domainName in arcpy.Describe(gdbPath).Domains]
        
        # Build list of domain/field assignments.
        # Note that only overstory fields are defined
        # A mapping to understory fields will be performed
        # when assignments are made.
        assignmentList = []

        assignmentList.append([[tableName],
                           'MOIST_REG',
                           'MOIST_REG',
                           '#'])

        assignmentList.append([[tableName],
                           'DENSITY',
                           'DENSITY',
                           '#'])

        assignmentList.append([[tableName],
                           'HEIGHT',
                           'HEIGHT',
                           '#'])

        assignmentList.append([[tableName],
                           'SP1',
                           'SP',
                           '#'])

        assignmentList.append([[tableName],
                           'SP1_PER',
                           'SP_PER',
                           '#'])

        assignmentList.append([[tableName],
                           'SP2',
                           'SP',
                           '#'])

        assignmentList.append([[tableName],
                           'SP2_PER',
                           'SP_PER',
                           '#'])

        assignmentList.append([[tableName],
                           'SP3',
                           'SP',
                           '#'])

        assignmentList.append([[tableName],
                           'SP3_PER',
                           'SP_PER',
                           '#'])

        assignmentList.append([[tableName],
                           'SP4',
                           'SP',
                           '#'])

        assignmentList.append([[tableName],
                           'SP4_PER',
                           'SP_PER',
                           '#'])

        assignmentList.append([[tableName],
                           'SP5',
                           'SP',
                           '#'])

        assignmentList.append([[tableName],
                           'SP5_PER',
                           'SP_PER',
                           '#'])

        assignmentList.append([[tableName],
                           'STRUC',
                           'STRUC',
                           '#'])

        assignmentList.append([[tableName],
                           'STRUC_VAL',
                           'STRUC_VAL',
                           '#'])

        assignmentList.append([[tableName],
                           'TPR',
                           'TPR',
                           '#'])

        assignmentList.append([[tableName],
                           'NFL',
                           'NFL',
                           '#'])

        assignmentList.append([[tableName],
                           'NFL_PER',
                           'NFL_PER',
                           '#'])

        assignmentList.append([[tableName],
                           'NAT_NON',
                           'NAT_NON',
                           '#'])

        assignmentList.append([[tableName],
                           'ANTH_VEG',
                           'ANTH_VEG',
                           '#'])

        assignmentList.append([[tableName],
                           'ANTH_NON',
                           'ANTH_NON',
                           '#'])

        assignmentList.append([[tableName],
                           'MOD1',
                           'MOD',
                           '#'])

        assignmentList.append([[tableName],
                           'MOD1_EXT',
                           'MOD_EXT',
                           '#'])

        assignmentList.append([[tableName],
                           'MOD2',
                           'MOD',
                           '#'])

        assignmentList.append([[tableName],
                           'MOD2_EXT',
                           'MOD_EXT',
                           '#'])

        assignmentList.append([[tableName],
                           'DATA',
                           'DATA',
                           '#'])

        # Assign both over/under story fields.
        for curAssignment in assignmentList:
            tblList = curAssignment[0]
            fieldName = curAssignment[1]
            domainName = curAssignment[2]
            subtypeList = curAssignment[3]

            if domainName.upper() in existingDomainList:
                for tblName in tblList:
                    self._logChn.logMsg('\n***\n*** Setting Domain: %s for %s in %s' % (domainName,fieldName,tblName))
                    tblPath = os.path.join(gdbPath,tblName)
                    arcpy.AssignDomainToField_management(tblPath,
                                                            fieldName,
                                                            domainName,
                                                            subtypeList)
                    # Update understory at same time.
                    fieldName = 'U%s' % fieldName
                    arcpy.AssignDomainToField_management(tblPath,
                                                         fieldName,
                                                         domainName,
                                                         subtypeList)
            else:
                self._logChn.logMsg('\n***\n *** ERROR ***\n   Domain: %s does not exist in: %s, cannot assign' %(domainName, gdbPath))
                
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def createAllDomains(self, gdbPath):
        '''
        Generates all of the AVI domains.

        Parameters:
            gdbPath - path to geodatabase where domains are to
                      be defined.
        
        '''

        self.createSpeciesDomain(gdbPath)
        self.createSpeciesPercentDomain(gdbPath)
        self.createMoistureRegimeDomain(gdbPath)
        self.createDensityDomain(gdbPath)
        self.createHeightDomain(gdbPath)
        self.createStandStructureDomain(gdbPath)
        self.createStandStructureModifierDomain(gdbPath)
        self.createNonForestVegetatedDomain(gdbPath)
        self.createNonForestVegetatedPercentDomain(gdbPath)
        self.createTPRDomain(gdbPath)
        self.createNaturallNonVegetatedDomain(gdbPath)
        self.createAnthVegetatedDomain(gdbPath)
        self.createAnthNonVegetatedDomain(gdbPath)
        self.createModifierDomain(gdbPath)
        self.createModifierExtentDomain(gdbPath)
        self.createExistingDataDomain(gdbPath)
        
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def _buildDomainsFromList(self, gdbPath, domainList):
        '''
        Using a list of domain definitions, create the empty
        domain. In most cases there will only be one entry
        in list since we will be creating the domains one at
        a time. List is used for flexability in case we wish
        to extend use.

        Parameters:
            gdbPath - path to geodatabase to create domains in.

            domainList - list of domains to create. This is a
                         list that contains additional lists each
                         of which is a domain definition. Each domain
                         list contains the following items:
                             domain name
                             domain description
                             field type
                             domain type
                             split policy
                             merge policy.
        '''

        for curDomain in domainList:
            domainName = curDomain[0]
            domainDes = curDomain[1]
            fieldType = curDomain[2]
            domainType = curDomain[3]
            splitPolicy = curDomain[4]
            mergePolicy = curDomain[5]
            
            self._logChn.logMsg('\n***\n*** Building domain: %s' % domainName)

            # Try to delete just in case it already exists.
            try:
                arcpy.DeleteDomain_management(gdbPath, domainName)
            except:
                pass
            
            arcpy.CreateDomain_management(gdbPath,
                                       domainName,
                                       domainDes,
                                       fieldType,
                                       domainType,
                                       splitPolicy,
                                       mergePolicy)

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def createExistingDataDomain(self, gdbPath):
        '''
        Parameters:
            gdbPath - path to geodatabase where domain is o be created.
            
        '''

        # Domain parameters:
        # domain_name,domain_description,fieldType,domainType,splitPolicy,mergePolicy
        # Use list just for convienience.
        domainList = []
        domainList.append(['DATA',
                           'Existing Data',
                           'TEXT',
                           'CODED',
                           'DUPLICATE',
                           'DEFAULT'])
        
        self._buildDomainsFromList(gdbPath, domainList)

        # Define all domain values and descriptions.
        domainVals = []
        domainVals.append(['F','F Field Checked'])
        domainVals.append(['P','P PSP'])
        domainVals.append(['V','V Volume Plot'])
        domainVals.append(['C','C Cruise Data'])
        domainVals.append(['S','S Supplementary Photography'])
        domainVals.append(['A','A Air Call'])
        domainVals.append(['L','L Large-scale Photography'])
        domainVals.append(['I','I Interpreted TPR'])
        
        domainName = domainList[0][0]
        self._logChn.logMsg('\n*** Populating domain: %s' % domainName)
        for domainCode,domainDes in domainVals:
            arcpy.AddCodedValueToDomain_management(gdbPath,
                                                   domainName,
                                                   domainCode,
                                                   domainDes)
            

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def createModifierExtentDomain(self, gdbPath):
        '''
        Parameters:
            gdbPath - path to geodatabase where domain is o be created.
            
        '''

        domainList = []
        domainList.append(['MOD_EXT',
                           'Modifier Extent',
                           'SHORT',
                           'CODED',
                           'DUPLICATE',
                           'DEFAULT'])
        
        self._buildDomainsFromList(gdbPath, domainList)

        # Define all domain values and descriptions.
        domainVals = []
        domainVals.append([0,'0'])
        domainVals.append([1,'1 1% - 25%'])
        domainVals.append([2,'2 26% - 50%'])
        domainVals.append([3,'3 51% - 75%'])
        domainVals.append([4,'4 76% - 94%'])
        domainVals.append([5,'5 95% - 100%'])
        
        domainName = domainList[0][0]
        self._logChn.logMsg('\n*** Populating domain: %s' % domainName)
        for domainCode,domainDes in domainVals:
            arcpy.AddCodedValueToDomain_management(gdbPath,
                                                   domainName,
                                                   domainCode,
                                                   domainDes)

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def createModifierDomain(self, gdbPath):
        '''
        Parameters:
            gdbPath - path to geodatabase where domain is o be created.
            
        '''

        # Domain parameters:
        # domain_name,domain_description,fieldType,domainType,splitPolicy,mergePolicy
        # Use list just for convienience.
        domainList = []
        domainList.append(['MOD',
                           'Modifier',
                           'TEXT',
                           'CODED',
                           'DUPLICATE',
                           'DEFAULT'])
        
        self._buildDomainsFromList(gdbPath, domainList)

        # Define all domain values and descriptions.
        domainVals = []
        domainVals.append(['CC','CC Clearcut'])
        domainVals.append(['BU','BU Burn'])
        domainVals.append(['WF','WF Windfall'])
        domainVals.append(['CL','CL Clearing'])
        domainVals.append(['DI','DI Disease'])
        domainVals.append(['IK','IK Insect Kill'])
        domainVals.append(['UK','UK Unknown Kill'])
        domainVals.append(['WE','WE Weather (red belt)'])
        domainVals.append(['DT','DT Dead Tops'])
        domainVals.append(['BT','BT Broken Tops'])
        domainVals.append(['SN','SN Snags'])
        domainVals.append(['ST','ST Scattered Timber'])
        domainVals.append(['SI','SI Site Improved'])
        domainVals.append(['SC','SC Seedbed Prepared'])
        domainVals.append(['PL','PL Planted'])
        domainVals.append(['TH','TH Thinned'])
        domainVals.append(['GR','GR Grazing'])
        domainVals.append(['IR','IR Irrigated'])
        domainVals.append(['BV','BV Beaver Activity'])
        domainVals.append(['SF','SF Seasonal Flooding'])
        domainVals.append(['SL','SL Slump or Landslide'])
        domainVals.append(['BK','BK Mountain Beetle Kill'])
        domainVals.append(['FS','FS FireSmart Treatment'])
        domainVals.append(['UP','UP Understorey Protection'])
        domainVals.append(['MO','MO MOF Block'])
        domainVals.append(['RT','RT Retention'])
        
        domainName = domainList[0][0]
        self._logChn.logMsg('\n*** Populating domain: %s' % domainName)
        for domainCode,domainDes in domainVals:
            arcpy.AddCodedValueToDomain_management(gdbPath,
                                                   domainName,
                                                   domainCode,
                                                   domainDes)

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def createAnthNonVegetatedDomain(self, gdbPath):
        '''
        Parameters:
            gdbPath - path to geodatabase where domain is o be created.
            
        '''

        # Domain parameters:
        # domain_name,domain_description,fieldType,domainType,splitPolicy,mergePolicy
        # Use list just for convienience.
        domainList = []
        domainList.append(['ANTH_NON',
                           'Anthropogenic Non-vegetated',
                           'TEXT',
                           'CODED',
                           'DUPLICATE',
                           'DEFAULT'])
        
        self._buildDomainsFromList(gdbPath, domainList)

        # Define all domain values and descriptions.
        domainVals = []
        domainVals.append(['ASC','ASC City-Town'])
        domainVals.append(['ASR','ASR Ribbon Development'])
        domainVals.append(['AIH','AIH Right-of-way (Roads etc..)'])
        domainVals.append(['AIE','AIE Peat Extraction'])
        domainVals.append(['AIG','AIG Gravel Pit'])
        domainVals.append(['AIF','AIF Farmstead'])
        domainVals.append(['AIM','AIM Suface Mine'])
        domainVals.append(['AII','AII Industrial'])        
        
        domainName = domainList[0][0]
        self._logChn.logMsg('\n*** Populating domain: %s' % domainName)
        for domainCode,domainDes in domainVals:
            arcpy.AddCodedValueToDomain_management(gdbPath,
                                                   domainName,
                                                   domainCode,
                                                   domainDes)

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def createAnthVegetatedDomain(self, gdbPath):
        '''
        Parameters:
            gdbPath - path to geodatabase where domain is o be created.
            
        '''

        # Domain parameters:
        # domain_name,domain_description,fieldType,domainType,splitPolicy,mergePolicy
        # Use list just for convienience.
        domainList = []
        domainList.append(['ANTH_VEG',
                           'Anthropogenic Vegetated',
                           'TEXT',
                           'CODED',
                           'DUPLICATE',
                           'DEFAULT'])
        
        self._buildDomainsFromList(gdbPath, domainList)

        # Define all domain values and descriptions.
        domainVals = []
        domainVals.append(['CA','CA Annual Crop'])
        domainVals.append(['CP','CP Perennial Crop'])
        domainVals.append(['CPR','CPR Rough Pasture'])
        domainVals.append(['CIP','CIP Pipeline-Transmission Line'])
        domainVals.append(['CIW','CIW Geophysical Activity'])
        domainVals.append(['CIH','CIH Winter Road'])
        
        domainName = domainList[0][0]
        self._logChn.logMsg('\n*** Populating domain: %s' % domainName)
        for domainCode,domainDes in domainVals:
            arcpy.AddCodedValueToDomain_management(gdbPath,
                                                   domainName,
                                                   domainCode,
                                                   domainDes)
            
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def createNaturallNonVegetatedDomain(self, gdbPath):
        '''
        Parameters:
            gdbPath - path to geodatabase where domain is o be created.
            
        '''

        # Domain parameters:
        # domain_name,domain_description,fieldType,domainType,splitPolicy,mergePolicy
        # Use list just for convienience.
        domainList = []
        domainList.append(['NAT_NON',
                           'Naturally Non-vegetated',
                           'TEXT',
                           'CODED',
                           'DUPLICATE',
                           'DEFAULT'])
        
        self._buildDomainsFromList(gdbPath, domainList)

        # Define all domain values and descriptions.
        domainVals = []
        domainVals.append(['NWI','NWI Snow or Ice'])
        domainVals.append(['NWL','NWL Lake'])
        domainVals.append(['NWR','NWR River'])
        domainVals.append(['NWF','NWF Flooded'])
        domainVals.append(['NMB','NMB Mineral'])
        domainVals.append(['NMC','NMC Cutbank'])
        domainVals.append(['NMR','NMR Barren Rock'])
        domainVals.append(['NMS','NMS Sand'])
        
        domainName = domainList[0][0]
        self._logChn.logMsg('\n*** Populating domain: %s' % domainName)
        for domainCode,domainDes in domainVals:
            arcpy.AddCodedValueToDomain_management(gdbPath,
                                                   domainName,
                                                   domainCode,
                                                   domainDes)
            
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def createTPRDomain(self, gdbPath):
        '''
        Parameters:
            gdbPath - path to geodatabase where domain is o be created.
            
        '''

        # Domain parameters:
        # domain_name,domain_description,fieldType,domainType,splitPolicy,mergePolicy
        # Use list just for convienience.
        domainList = []
        domainList.append(['TPR',
                           'Timber Productivity Rating',
                           'TEXT',
                           'CODED',
                           'DUPLICATE',
                           'DEFAULT'])
        
        self._buildDomainsFromList(gdbPath, domainList)

        # Define all domain values and descriptions.
        domainVals = []
        domainVals.append(['G','G Good'])
        domainVals.append(['M','M Medium'])
        domainVals.append(['F','F Fair'])
        domainVals.append(['U','U Unproductive'])
        
        domainName = domainList[0][0]
        self._logChn.logMsg('\n*** Populating domain: %s' % domainName)
        for domainCode,domainDes in domainVals:
            arcpy.AddCodedValueToDomain_management(gdbPath,
                                                   domainName,
                                                   domainCode,
                                                   domainDes)
            
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def createNonForestVegetatedPercentDomain(self, gdbPath):
        '''
        Parameters:
            gdbPath - path to geodatabase where domain is o be created.
            
        '''

        # Domain parameters:
        # domain_name,domain_description,fieldType,domainType,splitPolicy,mergePolicy
        # Use list just for convienience.
        domainList = []
        domainList.append(['NFL_PER',
                           'Non-forest Vegetated Percent',
                           'SHORT',
                           'CODED',
                           'DUPLICATE',
                           'DEFAULT'])
        
        self._buildDomainsFromList(gdbPath, domainList)

        # Define all domain values and descriptions.
        domainVals = []
        domainVals.append([0,'0'])
        domainVals.append([1,'10%'])
        domainVals.append([2,'20%'])
        domainVals.append([3,'30%'])
        domainVals.append([4,'40%'])
        domainVals.append([5,'50%'])
        domainVals.append([6,'60%'])
        domainVals.append([7,'70%'])
        domainVals.append([8,'80%'])
        domainVals.append([9,'90%'])
        domainVals.append([10,'100%'])
        
        domainName = domainList[0][0]
        self._logChn.logMsg('\n*** Populating domain: %s' % domainName)
        for domainCode,domainDes in domainVals:
            arcpy.AddCodedValueToDomain_management(gdbPath,
                                                   domainName,
                                                   domainCode,
                                                   domainDes)

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def createNonForestVegetatedDomain(self, gdbPath):
        '''
        Parameters:
            gdbPath - path to geodatabase where domain is o be created.
            
        '''

        # Domain parameters:
        # domain_name,domain_description,fieldType,domainType,splitPolicy,mergePolicy
        # Use list just for convienience.
        domainList = []
        domainList.append(['NFL',
                           'Non-forest Vegetated',
                           'TEXT',
                           'CODED',
                           'DUPLICATE',
                           'DEFAULT'])
        
        self._buildDomainsFromList(gdbPath, domainList)

        # Define all domain values and descriptions.
        domainVals = []
        domainVals.append(['SC','SC Closed Shrub'])
        domainVals.append(['SO','SO Open Shrub'])
        domainVals.append(['HG','HG Herbaceous Grassland'])
        domainVals.append(['HF','HF Herbaceous Forbs'])
        domainVals.append(['BR','BR Bryophyte'])
                
        domainName = domainList[0][0]
        self._logChn.logMsg('\n*** Populating domain: %s' % domainName)
        for domainCode,domainDes in domainVals:
            arcpy.AddCodedValueToDomain_management(gdbPath,
                                                   domainName,
                                                   domainCode,
                                                   domainDes)
            
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def createStandStructureModifierDomain(self, gdbPath):
        '''
        Parameters:
            gdbPath - path to geodatabase where domain is o be created.
            
        '''

        # Domain parameters:
        # domain_name,domain_description,fieldType,domainType,splitPolicy,mergePolicy
        # Use list just for convienience.
        domainList = []
        domainList.append(['STRUC_VAL',
                           'Stand Structure Modifier',
                           'SHORT',
                           'RANGE',
                           'DUPLICATE',
                           'DEFAULT'])
        
        self._buildDomainsFromList(gdbPath, domainList)

        domainName = domainList[0][0]
        arcpy.SetValueForRangeDomain_management(gdbPath,
                                                domainName,
                                                0,
                                                9)
            
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def createStandStructureDomain(self, gdbPath):
        '''
        Parameters:
            gdbPath - path to geodatabase where domain is o be created.
            
        '''

        # Domain parameters:
        # domain_name,domain_description,fieldType,domainType,splitPolicy,mergePolicy
        # Use list just for convienience.
        domainList = []
        domainList.append(['STRUC',
                           'Stand Structure',
                           'TEXT',
                           'CODED',
                           'DUPLICATE',
                           'DEFAULT'])
        
        self._buildDomainsFromList(gdbPath, domainList)

        # Define all domain values and descriptions.
        domainVals = []
        domainVals.append(['M','M Multi-layer'])
        domainVals.append(['H','H Horizontal'])
        domainVals.append(['C','C Complex'])
        
        domainName = domainList[0][0]
        self._logChn.logMsg('\n*** Populating domain: %s' % domainName)
        for domainCode,domainDes in domainVals:
            arcpy.AddCodedValueToDomain_management(gdbPath,
                                                   domainName,
                                                   domainCode,
                                                   domainDes)
                        
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def createHeightDomain(self, gdbPath):
        '''
        Parameters:
            gdbPath - path to geodatabase where domain is o be created.
            
        '''

        # Domain parameters:
        # domain_name,domain_description,fieldType,domainType,splitPolicy,mergePolicy
        # Use list just for convienience.
        domainList = []
        domainList.append(['HEIGHT',
                           'Stand Height',
                           'SHORT',
                           'RANGE',
                           'DUPLICATE',
                           'DEFAULT'])
        
        self._buildDomainsFromList(gdbPath, domainList)

        domainName = domainList[0][0]
        arcpy.SetValueForRangeDomain_management(gdbPath,
                                                domainName,
                                                0,
                                                99)
            
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def createDensityDomain(self, gdbPath):
        '''
        Parameters:
            gdbPath - path to geodatabase where domain is o be created.
            
        '''

        # Domain parameters:
        # domain_name,domain_description,fieldType,domainType,splitPolicy,mergePolicy
        # Use list just for convienience.
        domainList = []
        domainList.append(['DENSITY',
                           'Crown Closure',
                           'TEXT',
                           'CODED',
                           'DUPLICATE',
                           'DEFAULT'])
        
        self._buildDomainsFromList(gdbPath, domainList)

        # Define all domain values and descriptions.
        domainVals = []
        domainVals.append(['A','A 6% - 30%'])
        domainVals.append(['B','B 31% - 50%'])
        domainVals.append(['C','C 51% - 70%'])
        domainVals.append(['D','D 71% - 100%'])
        
        domainName = domainList[0][0]
        self._logChn.logMsg('\n*** Populating domain: %s' % domainName)
        for domainCode,domainDes in domainVals:
            arcpy.AddCodedValueToDomain_management(gdbPath,
                                                   domainName,
                                                   domainCode,
                                                   domainDes)
            
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def createMoistureRegimeDomain(self, gdbPath):
        '''
        Parameters:
            gdbPath - path to geodatabase where domain is o be created.
            
        '''

        # Domain parameters:
        # domain_name,domain_description,fieldType,domainType,splitPolicy,mergePolicy
        # Use list just for convienience.
        domainList = []
        domainList.append(['MOIST_REG',
                           'Moisture Regime',
                           'TEXT',
                           'CODED',
                           'DUPLICATE',
                           'DEFAULT'])
        
        self._buildDomainsFromList(gdbPath, domainList)

        # Define all domain values and descriptions.
        domainVals = []
        domainVals.append(['d','d Dry'])
        domainVals.append(['m','m Mesic'])
        domainVals.append(['w','w Wet'])
        domainVals.append(['a','a Aquatic'])
        
        domainName = domainList[0][0]
        self._logChn.logMsg('\n*** Populating domain: %s' % domainName)
        for domainCode,domainDes in domainVals:
            arcpy.AddCodedValueToDomain_management(gdbPath,
                                                   domainName,
                                                   domainCode,
                                                   domainDes)
            
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def createSpeciesPercentDomain(self, gdbPath):
        '''
        Parameters:
            gdbPath - path to geodatabase where domain is o be created.
            
        '''

        # Domain parameters:
        # domain_name,domain_description,fieldType,domainType,splitPolicy,mergePolicy
        # Use list just for convienience.
        domainList = []
        domainList.append(['SP_PER',
                           'Species Percent',
                           'SHORT',
                           'CODED',
                           'DUPLICATE',
                           'DEFAULT'])
        
        self._buildDomainsFromList(gdbPath, domainList)

        # Define all domain values and descriptions.
        domainVals = []
        domainVals.append([0,'0'])
        domainVals.append([1,'10%'])
        domainVals.append([2,'20%'])
        domainVals.append([3,'30%'])
        domainVals.append([4,'40%'])
        domainVals.append([5,'50%'])
        domainVals.append([6,'60%'])
        domainVals.append([7,'70%'])
        domainVals.append([8,'80%'])
        domainVals.append([9,'90%'])
        domainVals.append([10,'100%'])
        
        domainName = domainList[0][0]
        self._logChn.logMsg('\n*** Populating domain: %s' % domainName)
        for domainCode,domainDes in domainVals:
            arcpy.AddCodedValueToDomain_management(gdbPath,
                                                   domainName,
                                                   domainCode,
                                                   domainDes)
            
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def createSpeciesDomain(self, gdbPath):
        '''
        Parameters:
            gdbPath - path to geodatabase where domain is o be created.
            
        '''

        # Domain parameters:
        # domain_name,domain_description,fieldType,domainType,splitPolicy,mergePolicy
        # Use list just for convienience.
        domainList = []
        domainList.append(['SP',
                           'Species',
                           'TEXT',
                           'CODED',
                           'DUPLICATE',
                           'DEFAULT'])
        
        self._buildDomainsFromList(gdbPath, domainList)

        # Define all domain values and descriptions.
        domainVals = []
        domainVals.append(['A','A (Undifferentiated Populus)'])
        domainVals.append(['Aw','Aw (Populus tremuloides)'])
        domainVals.append(['Pb','Pb (Populus balsamifera)'])
        domainVals.append(['Bw','Bw (Betula papyrifera)'])
        domainVals.append(['P','P (Undifferentiated Pinus)'])
        domainVals.append(['Pl','Pl (Pinus contorta)'])
        domainVals.append(['Pj','Pj (Pinus banksiana)'])
        domainVals.append(['Pa','Pa (Pinus albicaulis)'])
        domainVals.append(['Pf','Pf (Pinus flexilis)'])
        domainVals.append(['Sw','Sw (Picea glauca)'])
        domainVals.append(['Sb','Sb (Picea mariana)'])
        domainVals.append(['Se','Se (Picea engelmannii)'])
        domainVals.append(['Lt','Lt (Larix laricina)'])
        domainVals.append(['Lw','Lw (Larix occidentalis)'])
        domainVals.append(['La','La (Larix lyallii)'])
        domainVals.append(['Fb','Fb (Abies balsamea)'])
        domainVals.append(['Fa','Fa (Abies lasiocarpa)'])
        domainVals.append(['Fd','Fd (Pseudotsuga menziesii)'])

        domainName = domainList[0][0]
        self._logChn.logMsg('\n*** Populating domain: %s' % domainName)
        for domainCode,domainDes in domainVals:
            arcpy.AddCodedValueToDomain_management(gdbPath,
                                                   domainName,
                                                   domainCode,
                                                   domainDes)

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def createFileGeodatabase(self, gdbDir, gdbName):
        '''
        Creates a new FGDB (will not overwrite and existing one.

        Parameters:
            gdbDir - directory where FGDB is to be created.

            gdbName - name of FGDB to create.

        Example:
        
        '''

        # Make sure we have a valid directory to create it in.
        if os.path.exists(gdbDir):
            if not os.path.isdir(gdbDir):
                raise SRD_Exception('Indicated directory for FGDB: %s not a directory' % gdbDir)
        else:
            raise SRD_Exception('Directory for FGDB: %s not found' % gdbDir)

        name,ext = os.path.splitext(gdbName)
        gdbName = '%s.gdb' % name
        gdbPath = os.path.join(gdbDir, gdbName)

        # Do not let an existing FGDB to be overwritten.
        if arcpy.Exists(gdbPath):
            msg = 'FGDB: %s already exists, cannot overwrite' % gdbPath
            raise SRD_Exception(msg)

        self._logChn.logMsg('\n***\n*** Creating FGDB: %s' % gdbPath)
        arcpy.CreateFileGDB_management(gdbDir, gdbName, '10.0')
                    
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def createFeatureClass(self, gdbPath, fcName, spatialRefType, useAlias=True):
        '''
        Generates the AVI feature class

        Parameters:
            gdbPath - path to geodatabase that will contain the
                      AVI feature class.

            fcName - name of the AVI feature class to create.

            spatialRefType - spatial reference code to define
                             spatial system for feature class.
                             Must be one of:
                                 '11'
                                 '12'
                                 'TTM'

            useAlias - optional parameter indicating if you wish to
                       assign the field alias. In some cases you may
                       wish just to see the actual field name in which
                       case pass False for this parameter. The default
                       is true.

        '''

        self.fgdbPath = gdbPath

        # Must have FGDB already in order to create feature class.
        if not arcpy.Exists(gdbPath):
            raise SRD_Exception('FGDB: %s not found, cannot create feature class' % gdbPath)
            
        # Create the polygon feature class.
        fcPath = os.path.join(gdbPath, fcName)
        if arcpy.Exists(fcPath):
            arcpy.Delete_management(fcPath)

        # Define desired spatial reference for feature class
        spatialRefType = str(spatialRefType).strip().upper()
        if spatialRefType == '11':
            sr = arcpy.SpatialReference('NAD 1983 UTM Zone 11N')
        elif spatialRefType == '12':
            sr = arcpy.SpatialReference('NAD 1983 UTM Zone 12N')
        elif spatialRefType == 'TTM':
            sr = arcpy.SpatialReference('NAD 1983 10TM AEP Forest')
        else:
            raise SRD_Exception('Invalid UTM Zone: %s passed, must be either 11, 12, or TTM')

        self._logChn.logMsg('\n***\n*** Creating Feature Class: %s' % fcPath)
        arcpy.CreateFeatureclass_management(gdbPath,
                                            fcName,
                                            'POLYGON',
                                            '',
                                            'DISABLED',
                                            'DISABLED',
                                            sr,
                                            '',
                                            5000.0)

        self.fcPath = fcPath
        
        # Add AVI attribute fields to the feature class.        
        # Structure is: NAME,TYPE,PRECISION,SCALE,LENGTH,ALIAS,NULLABLE,REQUIRED
        # Base it on standard AVI fields with some additional fields added.
        fieldList = self.fieldDefList[:]
        fieldList = self._getSupplementalFields(fieldList)
        
        self._addFields(self.fcPath, fieldList, useAlias)

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def validateFieldStructure(self, fcPath):
        '''
        Validates the field structure of the feature class against
        the standard schema. Will return list of errors encountered.
        Parameters:

            gdbPath - path to FGDB containing AVI feature class.

            fcName - name of the AVI feature class.

        '''

        errMsgList = []
        
        # Build list of field definitions for validation.
        fieldDefValidationList = self.fieldDefList[:]
        fieldDefValidationList = self._getSupplementalFields(fieldDefValidationList)

        # Convert field definitions to a dictionary for easy look-up.
        fieldDefValidationDict = {}
        for fldDef in fieldDefValidationList:
            fldName = fldDef[0]
            fieldDefValidationDict[fldName] = fldDef

        fieldList = arcpy.ListFields(fcPath)
        for field in fieldList:

            # Is current field an AVI field that needs validation.
            aviFieldName = field.name.upper()
            if fieldDefValidationDict.has_key(aviFieldName):
                fldDef = fieldDefValidationDict[aviFieldName]
                fldName = fldDef[0]
                fldType = fldDef[1]
                fldPrecision = fldDef[2]
                fldScale = fldDef[3]
                fldLength = fldDef[4]

                # Translate listFields type to definition type.
                aviFieldType = field.type.upper()
                if aviFieldType == 'SMALLINTEGER':
                    aviFieldType = 'SHORT'
                elif aviFieldType == 'INTEGER':
                    aviFieldType = 'LONG'
                elif aviFieldType == 'STRING':
                    aviFieldType = 'TEXT'
                elif aviFieldType == 'SINGLE':
                    aviFieldType = 'FLOAT'

                # Validate structure.
                if fldType.upper() <> aviFieldType:
                    errMsgList.append('Invalid Field Type for: %s, defined as: %s but should be: %s' %(aviFieldName, aviFieldType, fldType))

                if fldPrecision:
                    if int(fldPrecision) <> field.precision:
                        errMsgList.append('Invalid Field Precision for: %s, defined as: %s but should be: %s' %(aviFieldName, field.precision, fldPrecision))

                if fldScale:
                    if int(fldScale) <> field.scale:
                        errMsgList.append('Invalid Field Scale for: %s, defined as: %s but should be: %s' %(aviFieldName, field.scale, fldScale))

                if fldLength:
                    if int(fldLength) <> field.length:
                        errMsgList.append('Invalid Field Length for: %s, defined as: %s but should be: %s' %(aviFieldName, field.length, fldLength))

        return errMsgList
    
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def _getSupplementalFields(self, srcFieldList):
        '''
        In addition to the standard AVI fields some supplemental fields
        will be added to collect required data. This method will add
        these fields and return the srcFieldList.

        Parameters:
            srcFieldList - list of standard AVI fields to append
                           supplemental fields to.
        '''

        srcFieldList.append(['PHOTO_YR','SHORT','','','','Photography Year','NULLABLE','NON_REQUIRED'])
        srcFieldList.append(['ARIS','TEXT','','','11','ARIS','NULLABLE','NON_REQUIRED'])
        srcFieldList.append(['DIST_PTRN','SHORT','','','','Distribution Pattern','NULLABLE','NON_REQUIRED'])
        srcFieldList.append(['LID_UND','TEXT','','','1','LIDAR Detected Understory','NULLABLE','NON_REQUIRED'])

        return srcFieldList
    
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def buildAllIndexes(self, gdbPath, fcName ):
        '''
        Wrapper for building all AVI indexes.

        Parameters:

            gdbPath - path to FGDB containing AVI feature class.

            fcName - name of the AVI feature class.
        
        '''


        self.buildIndexes(gdbPath, fcName, self.indexFieldNames)

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def deleteAllIndexes(self, gdbPath, fcName ):
        '''
        Wrapper for deleting all AVI indexes.

        Parameters:

            gdbPath - path to FGDB containing AVI feature class.

            fcName - name of the AVI feature class.
        
        '''


        self.deleteIndexes(gdbPath, fcName, self.indexFieldNames)
        
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def buildIndexes(self, gdbPath, fcName, idxFieldList ):
        '''
        Builds all AVI indexes indicated by idxFieldList.

        Parameters:

            gdbPath - path to FGDB containing AVI feature class.

            fcName - name of the AVI feature class.

            idxFieldList - list of field names that will be used to
                           build indexes for.

        '''

        self._logChn.logMsg('***\n*** Building AVI indexes...')
        
        fcPath = os.path.join(gdbPath, fcName)

        indexNameList = [idx.name.lower() for idx in arcpy.ListIndexes(fcPath)]
            
        for fieldName in idxFieldList:
            indexName = '%s_idx' % fieldName

            # Remove any existing domain.
            if indexName.lower() in indexNameList:
                arcpy.RemoveIndex_management(fcPath, indexName)

            self._logChn.logMsg('Building: %s' % indexName)
            arcpy.AddIndex_management(fcPath, fieldName, indexName)

        self._logChn.logMsg('***\n*** AVI Indexes built')
        
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def deleteIndexes(self, gdbPath, fcName, idxFieldList ):
        '''
        Deletes all indexes indicated by idxFieldList.

        Parameters:

            gdbPath - path to FGDB containing AVI feature class.

            fcName - name of the AVI feature class.

            idxFieldList - list of field names that will be used to
                           delete indexes.
        '''

        self._logChn.logMsg('***\n*** Deleting AVI indexes...')
        
        fcPath = os.path.join(gdbPath, fcName)

        # Get list of existing index names in order to remove if we have to.
        indexNameList = [idx.name.lower() for idx in arcpy.ListIndexes(fcPath)]
                    
        for fieldName in idxFieldList:
            indexName = '%s_idx' % fieldName
            if indexName.lower() in indexNameList:
                self._logChn.logMsg('Deleting: %s' % indexName)
                arcpy.RemoveIndex_management(fcPath, indexName)

        self._logChn.logMsg('***\n*** AVI Indexes deleted')
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def addAVIFields(self, outFC, useAlias = True):
        '''
        Adds standard AVI fields to a feature class.
        Will only add field if field does not exist.

        Parameters:
            outFC - feature class to add fields to.

        Example:

        '''
        
        self._logChn.logMsg('\n***\n*** Adding AVI fields to %s' % outFC)

        self._addFields(outFC, self.fieldDefList, useAlias)
        
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def addSupplementalFields(self, outFC, useAlias = True):
        '''
        Adds supplemental AVI fields to a feature class.
        Will only add field if field does not exist.

        Parameters:
            outFC - feature class to add fields to.

        Example:
            import arcgisscripting
            from util_geoprocessing import *
            from avi_schema import *
            
            outFC = r'D:\prjs\AVI_Geodatabase\avi_11.gdb\avi'
            
            GP = arcgisscripting.create()
            logChn = UtilLogging(GP)
            aviSchemaObj = aviSchema(GP, logChn)
            aviSchemaObj.addSupplementalFields(outFC)

        '''

        fieldList = []
        fieldList = self._getSupplementalFields(fieldList)
        
        self._logChn.logMsg('\n***\n*** Adding Supplemental fields to %s' % outFC)

        self._addFields(outFC, fieldList, useAlias)
        
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def _addFields(self, outFC, fieldList, useAlias = True):
        '''
        Adds fields to a feature class using information contained in
        field list. Will only add field if field does not exist.

        Parameters:
            outFC - feature class to add fields to.

            fieldList - list of fields to add. This is a list of field definitions
                        each of which contains:
                            NAME,
                            TYPE,
                            PRECISION,
                            SCALE,
                            LENGTH,
                            ALIAS,
                            NULLABLE,
                            REQUIRED

            useAlias - optional parameter indicating if you wish to
                       assign the field alias. In some cases you may
                       wish just to see the actual field name in which
                       case pass False for this parameter. The default
                       is true.
                            
        '''

        # Structure is: NAME,TYPE,PRECISION,SCALE,LENGTH,ALIAS,NULLABLE,REQUIRED

        self._logChn.logMsg('\n***\n*** Adding fields to %s' % outFC)

        descObj = arcpy.Describe(outFC)
        if hasattr(descObj, 'dataType'):
            dataType = descObj.dataType.lower()
            
        try:
            dataObj = 'data_obj'
            arcpy.MakeTableView_management(outFC, dataObj)
            delDataObj = True
        except:
            dataObj = outFC
            delDataObj = False
        
        # Add the required fields.
        for fldDef in fieldList:
            fldName = fldDef[0]
            fldType = fldDef[1]
            fldPrecision = fldDef[2]
            fldScale = fldDef[3]
            fldLength = fldDef[4]
            # User may not want alias.
            if useAlias:
                fldAlias = fldDef[5]
            else:
                fldAlias = fldName
            fldNullable = fldDef[6]
            fldRequired = fldDef[7]

            # Only add field if it does not exist.
            if not arcpy.ListFields(dataObj, fldName):
                self._logChn.logMsg('Adding %s' % fldName)
                arcpy.AddField_management(dataObj, fldName, fldType, fldPrecision, fldScale, fldLength, fldAlias, fldNullable, fldRequired)

        if delDataObj:
            arcpy.Delete_management(dataObj)
            
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def loadAVICoverageToGeodatabase(self, srcCov, aviFCPath):
        '''
        Loads an AVI coverage and its associated ATT into the
        AVI feature class. All features will be appended from
        the coverage into the feature class. This means that if
        the AVI feature class already has records then these will
        be supplemented with additional records from the coverage.

        Parameters:
            srcCov - path to source AVI coverage that will be loaded.
                     Note that it is expected to be a typical coverage
                     with the attributes contained in a seperate INFO
                     table with the same name as the coverage but with
                     an ATT extension.

            aviFCPath - path to the AVI geodatabase feature class that
                        will be populated with the features from the
                        coverage.
        '''

        # Perform some data validation.
        if not arcpy.Exists(srcCov):
            msg = 'Source AVI coverage: %s not found' % srcCov
            raise SRD_Exception(msg)

        # Must have spatial reference information in case the data
        # must be reprojected when appended to feature class.
        spatialRef = arcpy.Describe(srcCov).SpatialReference
        if spatialRef.Name.upper() == 'UNKNOWN':
            msg = 'Coverage: %s does not have any projection information' % srcCov
            raise SRD_Exception(msg)

        outFeatureCnt = int(arcpy.GetCount_management(aviFCPath).getOutput(0))

        if not arcpy.Exists(aviFCPath):
            msg = 'Destination AVI Feature Class: %s not found' % aviFCPath
            raise SRD_Exception(msg)
            
        # Obtain the INFO table with our required attributes.
        covPath = os.path.dirname(srcCov)
        covName = os.path.basename(covPath)
        covWS = os.path.dirname(covPath)
        attPath = os.path.join(covWS,'%s.att' % covName)
        if not arcpy.Exists(attPath):
            msg = 'AVI Feature Class ATT: %s not found' % attPath
            raise SRD_Exception(msg)

        self._logChn.logMsg('\n*** Importing coverage: \n  %s \n into AVI geodatabase: %s\n***' % (srcCov, aviFCPath))

        aviFL = 'avi_fl'
        arcpy.MakeFeatureLayer_management(srcCov, aviFL)
        featureCnt = int(arcpy.GetCount_management(aviFL).getOutput(0))
        self._logChn.logMsg('\n\n *** %s features to import' % featureCnt)
        
        # Create table view of attribute table as well.
        attView = 'att_view'
        arcpy.MakeTableView_management(attPath, attView)

        # Join coverage polygons to attributes.
        arcpy.AddJoin_management(aviFL, "POLY_NUM", attView, "POLY_NUM", "KEEP_ALL")

        # Build list of field names so we can remap the fields
        # in the layer to remove the table names prefixed to the
        # field names.
        fldMapList = self.buildFieldRemapList(aviFL)

        # Create another feature layer so that remapping of fields
        # will strip the table name when we copy to new feature class.
        tmpFL = 'tmp_fl'
        arcpy.MakeFeatureLayer_management(aviFL, tmpFL, "", "", fldMapList)
        featureCnt = int(arcpy.GetCount_management(tmpFL).getOutput(0))

        tmpFGDB = srd_temp_ws.SRD_TempWS('tmp_ws')

        # Copy the features to temporary feature class so we can
        # obtain daaset with proper field names.
        tmpOutPolysName = 'tmp_avi'
        tmpOutPolys = os.path.join(tmpFGDB.FGDBPath, tmpOutPolysName)

        arcpy.CopyFeatures_management(tmpFL, tmpOutPolys)

        # Make sure we have the required fields otherwise our append
        # will not bring over all required data.
        aviRec = AVI_Record(aviFCPath)
        missingFieldList = []

        for fieldName in aviRec.allFieldNames:
            # We have traditional AVI fields as well as the extended
            # set that includes ARIS etc. If missing an extended field
            # then just ignore.
            if not fieldName in aviRec.extendedFieldNames:
                if not arcpy.ListFields(tmpOutPolys, fieldName):
                    missingFieldList.append(fieldName)

        if len(missingFieldList) > 0:
            msg = 'Could not transfer following required fields: %s' % missingFieldList
            raise SRD_Exception(msg)

        # Perform an append to destination AVI feature class so
        # structure will be acceptable. Note that since we use
        # NO_TEST only those fields from the coverage that match
        # our output AVI field structure will be copied.
        arcpy.Append_management(tmpOutPolys, aviFCPath, 'NO_TEST')
        
        # Warn user in case there are already features in the destination
        # AVI feature class.
        if outFeatureCnt > 0:
            self._logChn.logMsg('\n\n*** WARNING *** \n Output feature class:\n %s \n already contains: %s features, \n will append to existing features\n***' % (aviFCPath, outFeatureCnt), 'DEBUG', 1)

        # If the coverage does not have same projection as output warn user.
        outSpatialRef = arcpy.Describe(aviFCPath).SpatialReference
        if spatialRef.Name.upper() <> outSpatialRef.Name.upper():
            msg = '\n***\n*** WARNING ***\nCoverage projection is not same as output projection'
            msg += '\nCoverage projection: %s ' % spatialRef.Name
            msg += '\nOutput projection: %s \n***' % outSpatialRef.Name
            self._logChn.logMsg(msg)

        self._logChn.logMsg('\n*** AVI import complete')
        featureCnt = int(arcpy.GetCount_management(aviFCPath).getOutput(0))
        self._logChn.logMsg('Feature count of AVI feature class: %s' % featureCnt)

        tmpFGDB.deleteAllTempWS('tmp_ws')

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def buildFieldRemapList(self, fcPath, selectFields=None):
        '''
        Builds a field map for a joined feature class.
        The shapefield and OID field will be excluded.

        Parameters:

            fcPath - path to feature class containing fields to remap.

            selectFields - list of field names to restrict remapping to.
            
        '''
        
        fldMapList = ''
        fldNamesList = []
        fieldList = arcpy.ListFields(fcPath)

        for fld in fieldList:
            if not fld.type.upper() in ('GEOMETRY', 'OID', 'BLOB'):
                fldName = fld.name.upper()

                # With a coverage or INFO table the seperator between
                # the table and field name will be a ':' rather than a
                # '.' Must be able to handle both situations.
                if ':' in fldName:
                    baseFldName = fldName.split(':')[1]
                else:
                    if '.' in fldName:
                        baseFldName = fldName.split('.')[1]
                    else:
                        baseFldName = fldName

                # Must also be field in our list of select fields.
                if not selectFields is None:
                    if baseFldName in selectFields:
                        fieldVisibility = ' VISIBLE'
                    else:
                        fieldVisibility = ' HIDDEN'
                else:
                    fieldVisibility = ' VISIBLE'
                    
                # In case of duplicated fields we will generate a
                # unique name for the field.
                i = 0
                while baseFldName in fldNamesList:
                    i += 1
                    baseFldName = '%s%s' % (baseFldName,i)
                    
                fldNamesList.append(baseFldName)
                fldMap = fldName + ' ' + baseFldName + fieldVisibility
                if fldMapList == '':
                    fldMapList = fldMap
                else:
                    fldMapList = fldMapList + ';' + fldMap
        
        return fldMapList
        
# ======================================================================
if __name__ == '__main__':
    
   
    arcpy.gp.logHistory = False

    myObj = AVI_Schema()

    gdbDirArg = r'D:\test Data\AVI'
    gdbName = 'AVI_SCHEMA.gdb'
    gdbPathArg = os.path.join(gdbDirArg, gdbName)

    if arcpy.Exists(gdbPathArg):
        arcpy.Delete_management(gdbPathArg)
    myObj.createFileGeodatabase(gdbDirArg, gdbName)

    ##myObj.createAllDomains(gdbPathArg)

    fcNameArg = 'AVI'
##    myObj.createFeatureClass(gdbPathArg, fcNameArg, 'TTM')

    ##myObj.assignDomains(gdbPathArg, fcNameArg)
    
    ##myObj.buildIndexes(gdbPathArg, fcNameArg, myObj.indexFieldNames)

    ##myObj.deleteIndexes(gdbPathArg, fcNameArg, myObj.indexFieldNames)

    fcPathArg = os.path.join(gdbPathArg, fcNameArg)
    covPathArg = r'D:\testData\AVI\c5_cuts_2004\polygon'
##    myObj.loadAVICoverageToGeodatabase(covPathArg, fcPathArg)

##    fcPathArg = r'D:\testData\AVI\Schema_tests.gdb\bad_struct'
##    errMsgList = myObj.validateFieldStructure(fcPathArg)
##    if errMsgList:
##        print errMsgList
    
