列出具有活动域的要素类?


19

我有一个定义了属性域的Esri文件地理数据库。我需要删除某些属性域,但不能删除,因为“属性规则使用该域”。。如何发现正在使用域的要素类?

Executing: DeleteDomain R:\v5\YT_Canvec.gdb Permanency
Start Time: Thu May 19 11:01:02 2011
ERROR 999999: Error executing function.
The domain is used by an attribute rule.
Failed to execute (DeleteDomain).
Failed at Thu May 19 11:01:02 2011 (Elapsed Time: 0.00 seconds)

地理数据库中有100多个要素类,以交互方式查看每个要素的FC字段属性都是非入门级的。gdb太大,无法转换为个人gdb并通过ms-access(无论如何都是狡猾的方法)进入后门。


(2011年5月26日):另一种表达方式是“哪个要素类正在使用域X?”


您是否正在使用子类型的域?
柯克·库肯达尔

@kirk,是的,有一个子类型,但是我要删除的域未使用该子类型
matt wilkie 2011年

1
在那种情况下,我认为Brian的代码会起作用。
柯克·库肯达尔

1
@kirk,更正:我不认为我在使用子类型+域,但是经过大量的讨论并提出了技术支持案例之后,事实证明我实际上毕竟是在使用一个。这是名副其实的Click-fest,用于识别特定的剩余杯形蛋糕。我应该花更多的时间跟进您的c#方法!
马特·威尔基

Answers:


3

要回答使用子类型处理要素类的问题,可以使用arcpy(10.1+)。

arcpy.env.workspace = your_gdb

for FC in arcpy.ListFeatureClasses():
    for stcode, stdict in list(arcpy.da.ListSubtypes(FC).items()):
        for stkey in list(stdict.keys()):
            if stkey == 'FieldValues':
                for field, fieldvals in list(stdict[stkey].items()):
                    if fieldvals[1] is not None:
                        print(
                            "{},{},{},{}".format(FC,
                                                 'None' if stcode == 0 else stdict['Name'],
                                                 field,
                                                 fieldvals[1].name))

如果没有子类型,则子类型代码stcode将为零,因此该代码将打印出“无”。

亚型字典有更多的它,所以检查它的代码。


将我接受的答案更改为此。简短而直接。我在github.com/envygeo/arcplus/blob/master/ArcToolbox/Scripts/…上的代码版本。谢谢!
马特·威尔基

21

Python提供了一些方法来列出地理数据库中的要素类,遍历列表中的每个要素类,列出每个要素类中的字段以及显示每个字段的域。

import arcpy

#Set workspace environment to geodatabase
arcpy.env.workspace = your_gdb

#Get list of feature classes in geodatabase
FCs = arcpy.ListFeatureClasses()

#Loop through feature classes in list
for FC in FCs:

    #List fields in feature class
    fields = arcpy.ListFields(FC)

    #Loop through fields
    for field in fields:

        #Check if field has domain
        if field.domain != "":

            #Print feature class, field, domain name
            print FC, field.name, field.domain

上面的代码应在ArcGIS 10中运行,并且将在python解释器窗口中直接打印一个列表。然后,您可以将列表复制并粘贴到文本编辑器或Excel中,以更轻松地查看结果。


这样也可以处理子类型的域吗?
柯克·库肯达尔

我不确定这是否可以处理子类型或子类型的域。我以前从未使用过子类型。如果为特定字段分配了域,则将打印域名。
布莱恩

美丽,谢谢Brian。最初,它对我不起作用,但最终我记得,没有一些额外的帮助(gis.stackexchange.com/questions/5893/…),listFC不会递归到FeatureDatasets中。现在一切都好!:)
马特·威尔基(Matt wilkie)2011年

@Kirk,不,它看不到使用域的子类型。
马特·威尔基

按照示例resources.arcgis.com/en/help/main/10.1/index.html#//…遍历所有子类型及其关联的域。
Michael Stimson

8

由于我认为python不处理子类型,因此我将发布此C#代码。我使用Esri的示例水/废水geodb进行了测试,发现以下未使用的域:

HistoryType is not used
PLSSFirstDivisionType is not used
PLSSDirection is not used
PLSSPrincipalMeridian is not used
ParcelType is not used
PLSSSpecialSurveyType is not used
CartoLineType is not used
PLSSSecondDivisionType is not used

通常,DBA会烦恼无法通过SQL访问域(本质上是查找表)。

这段代码是通过arcmap测试的(根据Matt的评论进行了更新):

protected override void OnClick()
{
    string fgdbPath = @"C:\projects\NetTools\InfrastructureEditingTemplate\MapsandGeodatabase\LocalGovernment.gdb";
    var dict = SummarizeDomains(fgdbPath);
    ListDomains(dict);
    // list what featureclasses use a particular domain ...
    string domName = "State_Bnd_Rules";
    if (dict.ContainsKey(domName))
    {
        if (dict[domName].Count > 0)
        {
            Debug.Print("{0} is used by these featureclasses: ", domName);
            foreach (string fcfldName in dict[domName])
            {
                Debug.Print("\t{0}", fcfldName);
            }
        }
        else
            Debug.Print("{0} is not used by any featureclasses", domName);
    }
    else
    {
        Debug.Print("Domain name not found in geodb: {0}", domName);
    }
}

private void ListDomains(Dictionary<string,List<string>> dict)
{
    foreach (KeyValuePair<string, List<string>> kvp in dict)
    {
        Debug.Print("Domain {0}",kvp.Key);
        if (kvp.Value.Count > 0)
        {
            foreach (string fcfldName in kvp.Value)
            {
                Debug.Print("\t{0}", fcfldName);
            }
        }
        else
            Debug.Print("\tUNUSED DOMAIN!");
    }
}

private Dictionary<string, List<string>> SummarizeDomains(string fgdPath)
{
    var ws = Open(fgdPath);
    var dict = InitDict(ws);

    var enumDs1 = ws.get_Datasets(esriDatasetType.esriDTAny);
    IDataset ds;
    while ((ds = enumDs1.Next()) != null)
    {
        Debug.Print("processing {0}", ds.Name);
        if (ds is IObjectClass)
            LoadDomains((IObjectClass)ds, dict);
        else if (ds is IFeatureDataset)
        {
            var enumDs2 = ds.Subsets;
            enumDs2.Reset();
            IDataset ds2;
            while ((ds2 = enumDs2.Next()) != null)
            {
                if (ds2 is IObjectClass)
                    LoadDomains((IObjectClass)ds2, dict);
            }
        }
    }
    return dict;
}
private void LoadDomains(IObjectClass oc, Dictionary<string, List<string>> dict)
{
    if (oc is ISubtypes && ((ISubtypes)oc).HasSubtype)
        LoadSubtypeDomains(oc, dict);
    else
    {
        for (int i = 0; i < oc.Fields.FieldCount; i++)
        {
            var fld = oc.Fields.get_Field(i);
            if (fld.Domain == null)
                continue;
            if (dict.ContainsKey(fld.Domain.Name))
                dict[fld.Domain.Name].Add(String.Format("{0}.{1}",((IDataset)oc).Name,fld.Name));
            else
                throw new Exception("domain not found: " + fld.Domain.Name);
        }
    }
}
private void LoadSubtypeDomains(IObjectClass oc, Dictionary<string, List<string>> dict)
{
    ISubtypes subTypes = oc as ISubtypes;
    var enumSubtypes = subTypes.Subtypes;
    enumSubtypes.Reset();
    int code;
    string stName;
    while ((stName = enumSubtypes.Next(out code)) != null)
    {
        for (int i = 0; i < oc.Fields.FieldCount; i++)
        {
            string fldName = oc.Fields.get_Field(i).Name;
            var domain = subTypes.get_Domain(code, fldName);
            if (domain != null)
            {
                if (dict.ContainsKey(domain.Name))
                    dict[domain.Name].Add(String.Format("{0}.{1}.{2}",stName,((IDataset)oc).Name,fldName));
                else
                    throw new Exception("domain not found: " + domain.Name);
            }
        }
    }
}
private Dictionary<string, List<string>> InitDict(IWorkspace ws)
{
    var dict = new Dictionary<string, List<string>>(StringComparer.InvariantCultureIgnoreCase);
    var enumDomain = ((IWorkspaceDomains)ws).Domains;
    enumDomain.Reset();
    IDomain d = null;
    while ((d = enumDomain.Next()) != null)
        dict.Add(d.Name, new List<string>());
    return dict;
}

private IWorkspace Open(string fgdbPath)
{
    Type t = Type.GetTypeFromProgID("esriDataSourcesGDB.FileGDBWorkspaceFactory");
    var wsf = Activator.CreateInstance(t) as IWorkspaceFactory;
    return wsf.OpenFromFile(fgdbPath, 0);
}

列出未使用的域很有用,但这与要解决的问题相反。我实际上是在寻找“哪个FC正在使用域X?” (因此我可以删除链接,并使该域成为未使用的域)。(((我仍然没有尝试过代码,我只是使用函数名))
matt wilkie 2011年

@matt哦,是的,这很有意义。我更改了代码以显示如何执行此操作。
Kirk Kuykendall

嗯,也许这应该是一个完整的问题,但是,我应该把这段代码放在哪里?我找不到VBA编辑器的等效v10(工具->宏-> Visual Basic编辑器)。
马特·威尔基

您需要安装Visual Studio Express(免费)或更高版本,以及ArcGIS SDK。完成此操作后,您应该能够按照此演练创建命令按钮,然后将我的代码复制并粘贴到Click事件中。您还需要向项目添加适当的引用。
柯克·库肯达尔

5

此代码应返回所要求的内容。它将简洁地遍历工作空间GDB / FS中的所有要素类和表,并返回与域,字段名称及其所属的要素类/表关联的所有字段。

import os
import arcpy
lst=[]
for dirpath,dirnames,files in arcpy.da.Walk( # the path to your workspace
, datatype=["FeatureClass","Table"]):
     for file in files:
         lst.append(os.path.join(dirpath,file))
for i in lst:
     for fld in arcpy.ListFields(i):
         if fld.domain != "":
             print os.path.basename(i),fld.name, fld.domain 

4

不幸的是,布莱恩(Brian)的答案是所提出问题的直接而有用的答案,并不能解决我的实际问题。我认为是由于手头gdb中存在一个错误(即使要素类都未附加域,但仍有一个我不允许删除)。无论如何,我发现了另一种确定哪个fc具有关联域的方法。它是交互式的,但是比遍历每个fc的每个field属性快得多:

将一堆fc从问题gdb拖放到另一个gdb,然后检查“ 数据传输”对话框。链接的属性域(如果有)将在列表的底部。一遍又一遍地重复,直到缩小@ $%## fc给您带来的困难。

最终缩小到2个与CV域链接的FC


奇怪的是,即使拖放表示HD_148009_2链接到CV域Permanency,Brian的arcpy脚本也没有报告链接域,而且ArcCatalog中的“要素类属性”字段检查器也没有报告。但是,现在我终于将其范围缩小到足以在Esri技术支持下记录错误报告了。
马特·威尔基

4

我想这就是Matt Wilkie必须查找并编写代码以增强Brian的代码的原因。我必须获取表的所有域,数据库的根目录中的要素类以及所有要素数据集中的要素。我将信息导出为csv,以允许其他工作人员清理旧域的地理数据库环境。

def domainInfo(csvExportFolder):
    import arcpy,csv,os

    fcTabList = []
    list = []

    #Set workspace environment to geodatabase
    arcpy.env.workspace = r"H:\GIS\SDEConnections\Admin\Infrastructure.sde"

    #Prepping the csv
    csvFile = csv.writer(open(csvExportFolder+"\\"+ "Infrastructure Domains" + ".csv","wb"),delimiter = "|")
    csvFile.writerow(["FeatureDataSet","FeatureClass","FieldName","Domain"])

    #Get list of all features in geodatabase
    fdsList = arcpy.ListDatasets()
    fcs = arcpy.ListFeatureClasses()
    tbs = arcpy.ListTables()

    for fds in fdsList:
        fcs = arcpy.ListFeatureClasses("","",fds)
        if len(fcs) != 0:
            for fc in fcs:
                fcTabList.append([fds,fc])

    for fc in fcs:
        fcTabList.append([None,fc])

    for tb in tbs:
        fcTabList.append([None,tb])

    # Loop through all features in the database list
    for item in fcTabList:
        fds = item[0]
        fc = item[1]
        # List fields in feature class
        fields = arcpy.ListFields(fc)

        # Loop through fields
        for field in fields:

            # Check if field has domain
            if field.domain != "":

                # Print feature class, field, domain name
                csvFile.writerow([fds,fc,field.name,field.domain])

def main():
    csvExportFolder = r"H:\GIS"
    domainInfo(csvExportFolder)

if __name__ == "__main__":
    main()

0

Esri:常见问题解答:如何在我的地理数据库中找到所有引用域的地方?“可以在地理数据库中列出这些结构的属性的Python函数。所引用的域是这些属性。提供了示例脚本和文件地理数据库,这些示例脚本和文件地理数据库演示了如何使用Python函数列出域和要素类的其他属性以及域可以与要素类或表中的字段关联;还可以为按子类型分类的字段设置域。”

结果超出了所使用的域名范围,但对于该问题而言却是嘈杂的,但却是更广泛的入门平台。

Executing: ParseDomainReferences [...]

fc at root level: Pt1
  fld OBJECTID
  fld SHAPE
  fld Field_Text, domain [Pets]
  fld Field_Long
  fld Field_Short, domain [Counts]
  fld Field_Double, domain [Ratios]
[...]
Subtype Code: 1
subCode: ('Default', False)
subCode: ('Name', u'One')
subCode: ('SubtypeField', u'Field_Long')
FieldValues
fldName: Field_Double, default: [no default], domain: Ratios
fldName: OBJECTID, default: [no default], domain: [no domain]
fldName: Field_Long, default: [no default], domain: [no domain]
fldName: Field_Short, default: 1, domain: Counts
fldName: SHAPE, default: [no default], domain: [no domain]
fldName: Field_Text, default: N, domain: [no domain]
[...etc]

代码摘录,为简洁起见进行了编辑:

def ParseFieldList (fc, fcPath):
...
      for fld in fldList:
        if fld.domain != None:
          if fld.domain != "":
...
        arcpy.AddMessage ("  fld " + fld.name + s)

      # get subtype list
      subDict = arcpy.da.ListSubtypes (fcPath)
      if len (subDict) > 0:
        for stCode in subDict.iteritems():
...
          valkey, vallist = stCode
          arcpy.AddMessage ("Subtype Code: {0}".format(valkey))
          i = 0
          for subCode in vallist.iteritems():
            i += 1
            if i < 4:
              arcpy.AddMessage ("subCode: {0}".format(subCode))
            else:
              fldkey, fldlist = subCode
              arcpy.AddMessage (fldkey)
              for fld in fldlist.iteritems():
...
                if dom != None:
                  s2 = dom.name
                arcpy.AddMessage ("fldName: " + fldName + ", default: " + s1 + ", domain: " + s2)
...
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.