Listando classes de recursos com domínios ativos?

19

Eu tenho um geodatabase de arquivo Esri com domínios de atributo definidos. Preciso excluir alguns dos domínios de atributo, mas não posso porque "O domínio é usado por uma regra de atributo". . Como posso descobrir quais classes de recurso estão usando os domínios?

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)

Existem mais de cem classes de recursos no geodatabase, analisando interativamente as propriedades do campo FC, pois cada uma delas não é iniciante. O gdb é muito grande para converter em um gdb pessoal e entrar pela porta dos fundos com o ms-access (um método desonesto de qualquer maneira).


(26 de maio de 2011): Outra maneira de expressar isso é "qual classe de recurso está usando o domínio X?"

Matt Wilson
fonte
Você está usando domínios subtipados?
Kirk Kuykendall 19/05
@kirk, sim, há um subtipo, mas os domínios Estou tentando remover não estiver usando o subtipo
wilkie mate
1
Nesse caso, acho que o código de Brian funcionaria.
Kirk Kuykendall 19/05
1
@kirk, correção: eu não achava que estava usando subtipos + domínios, mas depois de muita discussão e abertura de um caso de suporte técnico, na verdade eu estava usando um afinal. Foi um verdadeiro click-fest para identificar o cuplrit restante em particular. Eu deveria ter investido mais tempo no acompanhamento do seu método c #!
26511 Mattt wilkie

Respostas:

3

Para responder à questão de lidar com classes de recursos com subtipos, é possível com o 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))

O código do subtipo, stcode, será zero se não houver subtipos; portanto, o código imprime 'Nenhum'.

O dicionário de subtipos tem mais, então inspecione-o no código.

Richard Morgan
fonte
Alterando minha resposta aceita para esta. É curto e direto. Minha versão do seu código em github.com/envygeo/arcplus/blob/master/ArcToolbox/Scripts/… . Obrigado!
matt wilkie 11/06
21

O Python possui métodos para listar classes de recursos em um geodatabase, percorrendo cada classe de recurso na lista, listando campos em cada classe de recurso e mostrando o domínio de cada campo.

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

O código acima deve funcionar no ArcGIS 10 e imprimirá uma lista diretamente na janela do interpretador python. Você pode copiar e colar a lista em um editor de texto ou Excel para revisar os resultados mais facilmente.

Brian
fonte
Isso também manipula domínios subtipados?
Kirk Kuykendall
Não tenho certeza se isso manipulará subtipos ou domínios subtipados. Eu nunca usei subtipos antes. Se houver um domínio atribuído a um campo específico, o nome do domínio será impresso.
19711 Brian
linda obrigada Brian. Inicialmente, não funcionou para mim, mas, eventualmente, lembrei que o listFC não recorre ao FeatureDatasets sem ajuda adicional ( gis.stackexchange.com/questions/5893/… ). Tudo bem agora! :)
matt wilkie
@ Kirk, não, não vê subtipos usando domínios.
27511 Matthew Brockie em:
Siga o exemplo resources.arcgis.com/en/help/main/10.1/index.html#//… para passar por todos os subtipos e seus domínios associados.
Michael Stimson
8

Como não acho que python lida com subtipos, estou postando esse código c # que deveria. Testei-o com o geodb de amostra de água / efluente da Esri e encontrei os seguintes domínios não utilizados:

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

Freqüentemente, os DBA ficam irritados porque os domínios - que são essencialmente tabelas de pesquisa - não podem ser acessados ​​via SQL.

Este código foi testado no arcmap ( atualizado pelo comentário de 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);
}
Kirk Kuykendall
fonte
Embora a lista de domínios não utilizados seja útil, esse é o inverso do problema a ser resolvido. Na verdade, eu estava procurando "qual FC está usando o domínio X?" (para remover o link e tornar o domínio um domínio não utilizado). ((Eu ainda não tentei o código, eu estou indo só no nome da função))
Matt Wilkie
@matt oh, sim, isso faz sentido. Alterei o código para mostrar como fazer isso.
21411 Kirk Kuykendall
Talvez essa seja uma pergunta completa, mas onde coloco esse código? Não consigo localizar o equivalente v10 do editor VBA ( Ferramentas-> Macros-> Editor do Visual Basic ).
31511 Mattel, São
Você precisará instalar o Visual Studio Express (gratuito) ou superior e o ArcGIS SDK . Depois de fazer isso, você poderá seguir este passo a passo para criar um botão de comando e copiar e colar meu código no evento Click. Você também precisará adicionar referências apropriadas ao projeto.
Kirk Kuykendall
5

Este código deve retornar o que está sendo solicitado. Ele percorrerá sucintamente todas as classes e tabelas de recursos em um espaço de trabalho GDB / FS e retornará todos os campos associados a um domínio, o nome do campo e a classe / tabela de recursos a que ele pertence.

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 
COCO
fonte
4

Infelizmente, a resposta de Brian, que é uma resposta direta e utilizável à pergunta, não resolve meu problema real. Presumo que, devido a um erro no gdb em questão (mesmo que nenhuma das classes de recursos tenha domínios anexados, ainda há um que não tenho permissão para excluir). De qualquer forma, encontrei outro método para determinar quais fc têm domínios associados. É interativo, mas muito mais rápido do que passar por cada propriedade de campo em cada fc:

Arraste e solte grupos de fc do problema gdb para outro gdb e inspecione a caixa de diálogo Transferência de dados. Os domínios de atributo vinculado, se houver, estarão no final da lista. Repita em grupos cada vez menores até diminuir o valor de @ $% ## fc que está causando dificuldades.

finalmente diminuiu para 2 FCs vinculados a um domínio CV

Matt Wilson
fonte
Curiosamente, mesmo que o drag-n-drop diga que HD_148009_2está vinculado ao domínio CV Permanency, o script arcpy de Brian não relata domínio vinculado e o inspetor de campos Feature Class Properties no ArcCatalog também não. No entanto, agora eu finalmente o reduzi o suficiente para registrar um relatório de erro com o suporte técnico da Esri.
22611 Matthew
4

É isso que imagino que Matt Wilkie teve que procurar e escrever para aumentar o código de Brian. Eu tive que obter todos os domínios para tabelas, classes de recursos no diretório raiz de um banco de dados e recursos em todos os conjuntos de dados de recursos. Exportei as informações como um CSV para permitir que outros trabalhadores limpassem nossos ambientes de banco de dados geográficos de domínios antigos.

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()
René Casiano
fonte
0

Esri: FAQ: Como posso encontrar todos os locais onde os domínios são referenciados no meu banco de dados geográficos? . "Funções Python que podem listar as propriedades dessas estruturas em um geodatabase. Entre as propriedades estão os domínios referenciados. São fornecidos um exemplo de script e geodatabase de arquivo que demonstram como as funções Python podem ser usadas para listar os domínios e outras propriedades das classes de recursos e Os domínios podem ser associados a campos em uma classe ou tabela de recursos; além disso, podem ser definidos para campos categorizados por um subtipo ".

Os resultados são barulhentos para esta questão, indo além dos domínios em uso, mas são uma plataforma mais ampla para começar.

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]

Trecho de código, editado por questões de brevidade:

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)
...
Matt Wilson
fonte