2011년 12월 25일 일요일

[ArcObjects] Layer와 Feature Class/Table의 필드 별칭 설정

▣ 미션
 - ArcMap에서 아래 그림과 같이 테이블과 속성정보 조회시 영문필드에 한글필드로 별칭을 보여주고 싶다.
- 면적 등과 같이 Numeric 필드일 경우 소숫점 2째자리까지만 보여주고 천단위 구분자를 두고싶다.
- GeoDatabase(Personal, File, ArcSDE)에서 레이어를 불러올 경우 한글별칭을 기본값으로 사용하고 싶다.

▣ 설명
 - Layer의 필드정보는 Feature Layer 수준, FeatureClass, Table의 필드정보는 GeoDatabase 수준에서 변경.
 - 따라서 FeatureClass, Table의 필드정보 변경은 물리적으로 저장되고 Layer의 필드정보는 ArcMap과 같이 Application 내에서만 임시 적용됨.
 - FeatureClass에 이미 한글별칭이 적용되어 있더라도 Application에서 실시간 조인이 이루어지는 경우는 별칭이 적용되지 않을 수 있음.

▣ ArcObjects Interface
 - INumberFormat
 - ITableFields
 - IFieldInfo
 - IClassSchemaEdit
 - ISchemaLock

▣ Code Snippet
using System.Runtime.InteropServices;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Carto;
■ 필드이름과 매칭되는 Alias 정보 생성
System.Collections.Hashtable FieldAliasTable = new System.Collections.Hashtable();

public void AddFieldAlias() {
    FieldAliasTable.Add("OBJECTID", "일련번호");
    FieldAliasTable.Add("SHAPE", "공간정보");
    FieldAliasTable.Add("SHAPE_AREA", "면적");
    FieldAliasTable.Add("SHAPE_LENGTH", "둘레");
    
    // 기타 필드 추가
    FieldAliasTable.Add("NAM", "명칭");
}
■ 레이어의 필드 정보 변경
- 다음 코드는 위 Alias 매칭 테이블에 담긴 정보를 이용하여 면적 등과 같이 Numeric 필드일 경우 소숫점 2째자리까지만 보여주고 천단위 구분자 적용
- 테이블과 조인된 필드일 경우에도 한글 별칭을 일괄적으로 적용하는 예임
- 또한 필드 이름 중 NAM 이라는 String 필드가 포함되어 있을 경우 Identify 등에서 사용되는 Primary Display Field로 설정함
public void AlterLayerFieldAlias(IFeatureLayer featureLayer) {
    // Number format for Double, Single Fields
    INumericFormat numberFormat = new NumericFormatClass();
    numberFormat.RoundingOption = esriRoundingOptionEnum.esriRoundNumberOfDecimals;
    numberFormat.RoundingValue = 2;    // Number of decimal places
    numberFormat.ZeroPad = true;      // Pad with zeros
    numberFormat.UseSeparator = true; // Show thousands separators

    IGeoFeatureLayer geoFeatureLayer = (IGeoFeatureLayer)featureLayer;
    ITableFields tableFields = featureLayer as ITableFields;
    for (int fieldId = 0; fieldId < tableFields.FieldCount; fieldId++) {
        IFieldInfo fieldInfo = tableFields.get_FieldInfo(fieldId);
        IField field = tableFields.get_Field(fieldId);

        string fieldName = field.Name.ToUpper();
        string fieldAlias = fieldInfo.Alias;

        if (fieldName.Contains(".")) {
            int pos = fieldName.LastIndexOf(".");
            fieldName = fieldName.Substring(pos + 1).Trim();
        }

        switch (field.Type) {
            case esriFieldType.esriFieldTypeDouble:
            case esriFieldType.esriFieldTypeSingle:
                fieldInfo.NumberFormat = (INumberFormat)numberFormat;
                break;
            case esriFieldType.esriFieldTypeString:
                if (fieldName.Contains("NAM")) {
                    // Primary display field
                    geoFeatureLayer.DisplayField = field.Name;
                }
                break;
        }

        if (FieldAliasTable.Contains(fieldName)) {
            fieldAlias = FieldAliasTable[fieldName].ToString();
        }

        fieldInfo.Alias = fieldAlias;
    }
}
■ ObjectClass(FeatureClass, Table)의 필드 Alias 변경
 - 다음 코드는 ArcCatalog에서 각각의 필드에 대한 별칭을 UI상에서 적용하는 과정을 코드로 구현한 예임
 - 클래스나 필드의 별칭을 변경하기 위해서는 반드시 GeoDatabase에 등록되어 있어야 한다.
public void AlterFieldAlias(IObjectClass objectClass, string fieldName, string aliasName) {
    if (objectClass.ObjectClassID == -1) {
        // string suggestedOIDFieldName = "OBJECTID";
        // RegisterWithGeodatabase(objectClass, suggestedOIDFieldName);
        return;
    }
    
    // Attempt to acquire an exclusive schema lock for the object class.
    ISchemaLock schemaLock = (ISchemaLock)objectClass;
    try {
        schemaLock.ChangeSchemaLock(esriSchemaLock.esriExclusiveSchemaLock);

        IClassSchemaEdit schemaEdit = (IClassSchemaEdit)objectClass;
        if (objectClass.FindField(fieldName) != -1) {
            schemaEdit.AlterFieldAliasName(fieldName, aliasName);
        }
    } catch (COMException comExc) {
        throw comExc;
    } finally {
        // Reset the lock on the object class to a shared lock.
        schemaLock.ChangeSchemaLock(esriSchemaLock.esriSharedSchemaLock);
    }
}
■ ObjectClass의 GeoDatabase 등록
 - 다음 코드는 위 그림과 같이 GeoDatabase에 등록되지 않은 테이블이나 FeatureClass를 등록하는 예임
public void RegisterWithGeodatabase(IObjectClass objectClass, string suggestedOIDFieldName) {
    // Attempt to acquire an exclusive schema lock for the object class.
    ISchemaLock schemaLock = (ISchemaLock)objectClass;
    try {
        schemaLock.ChangeSchemaLock(esriSchemaLock.esriExclusiveSchemaLock);

        IClassSchemaEdit classSchemaEdit = (IClassSchemaEdit)objectClass;
        classSchemaEdit.RegisterAsObjectClass(suggestedOIDFieldName, string.Empty);
    } catch (COMException comExc) {
        throw comExc;
    } finally {
        // Reset the lock on the object class to a shared lock.
        schemaLock.ChangeSchemaLock(esriSchemaLock.esriSharedSchemaLock);
    }
}