Elenco delle classi di funzionalità con domini attivi?


19

Ho un geodatabase di file Esri con i domini degli attributi definiti. Devo eliminare alcuni domini di attributi ma non posso perché "Il dominio è utilizzato da una regola di attributo". . Come posso scoprire quali classi di caratteristiche stanno usando i domini?

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)

Ci sono oltre un centinaio di classi di funzioni nel geodatabase, che guardano in modo interattivo le proprietà del campo FC per ognuna di esse non è un avviatore. Il gdb è troppo grande per essere convertito in un gdb personale e accedere alla backdoor con ms-access (un metodo non sicuro).


(2011-May-26): Un altro modo per formulare questa frase è "quale classe caratteristica sta usando il dominio X?"


Stai usando domini sottotipati?
Kirk Kuykendall,

@kirk, sì, esiste un sottotipo, ma i domini che sto cercando di rimuovere non utilizzano il sottotipo
matt wilkie,

1
In tal caso, penso che il codice di Brian funzionerebbe.
Kirk Kuykendall,

1
@kirk, correzione: non pensavo di usare sottotipi + domini, ma dopo essere stato molto scervellato e aver aperto un caso di supporto tecnico, ho scoperto che in realtà ne stavo usando uno dopo tutto. È stato un vero e proprio click-fest per identificare la particolare cuplrit rimanente. Avrei dovuto investire più tempo nel dare seguito al tuo metodo c #!
matt wilkie,

Risposte:


3

Per rispondere alla domanda sulla gestione delle classi di funzionalità con sottotipi, è possibile con 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))

Il codice del sottotipo, stcode, sarà zero se non ci sono sottotipi, quindi il codice stampa "Nessuno".

Il dizionario dei sottotipi ha di più, quindi ispezionalo nel codice.


Cambiando la mia risposta accettata a questa. È breve e diretto. La mia versione del tuo codice su github.com/envygeo/arcplus/blob/master/ArcToolbox/Scripts/… . Grazie!
Matt Wilson

21

Python ha metodi per elencare le classi di caratteristiche in un geodatabase, scorrere in sequenza ogni classe di caratteristiche nell'elenco, elencare i campi in ciascuna classe di caratteristiche e mostrare il dominio di ciascun 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

Il codice sopra dovrebbe funzionare in ArcGIS 10 e stamperà un elenco proprio nella finestra dell'interprete di Python. È quindi possibile copiare e incollare l'elenco in un editor di testo o Excel per rivedere più facilmente i risultati.


Gestirà anche domini sottotipati?
Kirk Kuykendall,

Non sono sicuro se questo gestirà sottotipi o domini sottotipati. Non ho mai usato sottotipi prima. Se è stato assegnato un dominio a un determinato campo, verrà stampato il nome del dominio.
Brian,

bellissima, grazie Brian. Inizialmente non ha funzionato per me, ma alla fine mi sono ricordato che listFC non rientra in FeatureDatasets senza un aiuto aggiuntivo ( gis.stackexchange.com/questions/5893/… ). Tutto bene adesso! :)
matt wilkie,

@Kirk, no non vede i sottotipi che usano i domini.
matt wilkie,

Seguire l'esempio resources.arcgis.com/en/help/main/10.1/index.html#//… per esaminare tutti i sottotipi e i rispettivi domini associati.
Michael Stimson,

8

Dal momento che non penso che Python gestisca i sottotipi, sto pubblicando questo codice c # che dovrebbe. L'ho testato con il campione di acqua / acque reflue geodb di Esri e ho trovato i seguenti domini non utilizzati:

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

Spesso i DBA sono infastiditi dal fatto che i domini, che sono essenzialmente tabelle di ricerca, non sono accessibili tramite SQL.

Questo codice è stato testato da arcmap ( aggiornato per il commento di 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);
}

mentre è utile elencare domini inutilizzati, questo è l'inverso del problema da risolvere. In realtà stavo cercando "quale FC sta usando il dominio X?" (quindi posso rimuovere il collegamento e rendere il dominio un dominio inutilizzato). ((Non ho ancora provato il codice, sto solo andando sul nome della funzione))
matt wilkie

@matt oh, sì, ha senso. Ho modificato il codice per mostrare come farlo.
Kirk Kuykendall,

Forse questa dovrebbe essere una domanda a tutti gli effetti, ma dove devo inserire questo codice? Non riesco a trovare l'equivalente v10 dell'editor VBA ( Strumenti-> Macro-> Visual Basic Editor ).
matt wilkie

È necessario installare Visual Studio Express (gratuito) o versione successiva e ArcGIS SDK . Una volta fatto, dovresti essere in grado di seguire questa procedura dettagliata per la creazione di un pulsante di comando , quindi copiare e incollare il mio codice nell'evento Click. Dovrai anche aggiungere riferimenti appropriati al progetto.
Kirk Kuykendall,

5

Questo codice dovrebbe restituire ciò che viene richiesto. Attraverserà in modo succinto tutte le classi e le tabelle di feature in uno spazio di lavoro GDB / FS e restituirà tutti i campi associati a un dominio, il nome del campo e la classe / tabella di feature a cui appartiene.

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

Purtroppo la risposta di Brian, che è una risposta diretta e utilizzabile alla domanda posta, non risolve il mio vero problema. Presumo a causa di un bug nel gdb a portata di mano (anche se nessuna delle classi di funzionalità ha domini collegati, ce n'è ancora uno che non mi è permesso cancellare). In ogni caso ho trovato un altro metodo per determinare quali fc hanno domini associati. È interattivo, ma molto più veloce che passare attraverso ogni proprietà del campo su ogni singolo fc:

Trascina e rilascia grappoli di fc dal problema gdb a un altro gdb e controlla la finestra di dialogo Trasferimento dati . I domini degli attributi collegati, se presenti, saranno in fondo all'elenco. Ripeti in gruppi sempre più piccoli fino a quando non restringi il campo @ $% ## fc.

infine ridotto a 2 FC collegati a un dominio CV


Curiosamente, anche se drag-n-drop dice che HD_148009_2è collegato al dominio CV Permanency, lo script arcpy di Brian non riporta alcun dominio collegato, e nemmeno la finestra di ispezione dei campi delle proprietà della classe di caratteristiche in ArcCatalog. Comunque ora l'ho ridotto abbastanza da registrare una segnalazione di bug con il supporto tecnico di Esri.
matt wilkie,

4

Questo è quello che immagino che Matt Wilkie abbia dovuto cercare e scrivere per aumentare il codice di Brian. Ho dovuto ottenere tutti i domini per le tabelle, le classi di funzionalità nella directory principale di un database e le funzionalità in tutti i set di dati delle funzionalità. Ho esportato le informazioni come un CSV per consentire ad alcuni altri lavoratori di ripulire i nostri ambienti di geodatabase di vecchi domini.

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: FAQ: Come posso trovare tutti i luoghi in cui si fa riferimento ai domini nel mio geodatabase? . "Funzioni Python che possono elencare le proprietà di queste strutture in un geodatabase. Tra le proprietà ci sono i domini referenziati. Sono forniti uno script di esempio e un geodatabase di file che dimostrano come le funzioni Python potrebbero essere usate per elencare i domini e le altre proprietà delle classi di caratteristiche e tabelle. I domini possono essere associati ai campi in una classe di caratteristiche o tabella; possono inoltre essere impostati per i campi classificati da un sottotipo. "

I risultati sono rumorosi per questa domanda, andando oltre i domini in uso, ma è una piattaforma più ampia per iniziare.

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]

Estratto di codice, modificato per brevità:

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)
...
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.