Feature-Classes mit aktiven Domains auflisten?

18

Ich habe eine Esri-File-Geodatabase mit definierten Attributdomänen . Ich muss einige der Attributdomänen löschen, kann dies jedoch nicht, da "Die Domäne wird von einer Attributregel verwendet". . Wie kann ich feststellen, welche Feature-Class (s) die Domains verwenden?

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)

In der Geodatabase gibt es über hundert Feature-Classes, bei denen die Eigenschaften der FC-Felder interaktiv betrachtet werden und bei denen es sich jeweils um Nicht-Starter handelt. Die GDB ist zu groß, um sie in eine persönliche GDB umzuwandeln und mit MS-Access in die Hintertür zu gelangen (eine zweifelhafte Methode).


(26.05.2011): Eine andere Möglichkeit, dies zu formulieren, ist "Welche Feature-Class verwendet Domain X?".

Matt Wilkie
quelle
Verwenden Sie subtypisierte Domains?
Kirk Kuykendall
@kirk, ja, es gibt einen Subtyp, aber die Domains, die ich entfernen möchte, verwenden nicht den Subtyp
matt wilkie
1
In diesem Fall würde Brians Code funktionieren.
Kirk Kuykendall
1
@kirk, Korrektur: Ich habe nicht gedacht, dass ich Subtypen + Domains verwende, aber nachdem ich viel darüber nachgedacht und einen technischen Support-Fall eröffnet habe, stellte sich heraus, dass ich tatsächlich einen verwendet habe. Es war ein wahres Klick-Fest, das verbleibende Cuplit zu identifizieren. Ich hätte mehr Zeit in die Nachverfolgung Ihrer c # -Methode investieren sollen!
Matt Wilkie

Antworten:

3

Um die Frage der Behandlung von Feature-Classes mit Subtypen zu beantworten, ist es mit arcpy (10.1+) möglich.

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))

Der Subtypcode stcode ist Null, wenn keine Subtypen vorhanden sind. Der Code gibt daher "Keine" aus.

Das Untertypen-Wörterbuch hat mehr zu bieten, prüfen Sie es also im Code.

Richard Morgan
quelle
Ändern meiner akzeptierten Antwort auf diese. Es ist kurz und direkt. Meine Version Ihres Codes unter github.com/envygeo/arcplus/blob/master/ArcToolbox/Scripts/… . Vielen Dank!
Matt Wilkie
21

Python bietet Methoden zum Auflisten von Feature-Classes in einer Geodatabase, zum Durchlaufen der einzelnen Feature-Classes in der Liste, zum Auflisten der Felder in den einzelnen Feature-Classes und zum Anzeigen der Domänen der einzelnen Felder.

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

Der obige Code sollte in ArcGIS 10 funktionieren und eine Liste direkt im Python-Interpreter-Fenster ausgeben. Sie können die Liste dann kopieren und in einen Texteditor oder Excel einfügen, um die Ergebnisse einfacher zu überprüfen.

Brian
quelle
Werden auch untergeordnete Domains behandelt?
Kirk Kuykendall
Ich bin nicht sicher, ob dies Subtypen oder Subtyp-Domains behandeln wird. Ich habe noch nie Subtypen verwendet. Wenn einem bestimmten Feld eine Domain zugewiesen ist, wird der Domainname gedruckt.
Brian
schön, danke Brian. Anfänglich hat es bei mir nicht geklappt, aber irgendwann fiel mir ein, dass listFC nicht ohne zusätzliche Hilfe in FeatureDatasets wiederhergestellt werden kann ( gis.stackexchange.com/questions/5893/… ). Alles gut jetzt! :)
matt wilkie
@Kirk, nein, es werden keine Untertypen mit Domains angezeigt.
Matt Wilkie
Folgen Sie dem Beispiel resources.arcgis.com/en/help/main/10.1/index.html#//… , um alle Untertypen und die zugehörigen Domänen durchzugehen.
Michael Stimson
8

Da ich nicht glaube, dass Python Subtypen handhabt, poste ich diesen C # -Code, der es sollte. Ich habe es mit Esris Probenwasser / Abwasser-Geodb getestet und die folgenden nicht verwendeten Domänen gefunden:

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

DBAs ärgern sich häufig darüber, dass auf Domänen - die im Wesentlichen Nachschlagetabellen sind - nicht über SQL zugegriffen werden kann.

Dieser Code wurde von arcmap getestet ( gemäß Matts Kommentar aktualisiert ):

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
quelle
Das Auflisten nicht verwendeter Domains ist zwar nützlich, aber das ist die Umkehrung des zu lösenden Problems. Ich suchte tatsächlich nach "Welcher FC verwendet Domain X?" (so kann ich den Link entfernen und machen die Domäne einen ungenutzten Domain). ((Ich habe den Code noch nicht ausprobiert, ich gehe nur auf den Namen der Funktion))
Matt Wilkie
@matt oh ja, das macht Sinn. Ich habe den Code geändert, um zu zeigen, wie das geht.
Kirk Kuykendall
uhh, vielleicht sollte dies eine vollwertige Frage sein, aber wo soll ich diesen Code hinstellen? Ich kann das V10-Äquivalent des VBA-Editors nicht finden ( Extras-> Makros-> Visual Basic-Editor ).
Matt Wilkie
Sie müssen Visual Studio Express (kostenlos) oder höher und das ArcGIS SDK installieren . Sobald Sie dies getan haben, sollten Sie in der Lage sein, diese exemplarische Vorgehensweise zum Erstellen einer Befehlsschaltfläche zu befolgen und dann meinen Code zu kopieren und in das Click-Ereignis einzufügen. Sie müssen dem Projekt auch entsprechende Referenzen hinzufügen.
Kirk Kuykendall
5

Dieser Code sollte zurückgeben, was gefragt wird. Alle Objektklassen und Tabellen in einer Arbeitsbereich-GDB / FS werden genau durchlaufen, und alle mit einer Domäne verknüpften Felder, der Feldname und die Objektklasse / Tabelle, zu der sie gehört, werden zurückgegeben.

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
quelle
4

Leider löst Brians Antwort, die eine direkte und brauchbare Antwort auf die gestellte Frage ist, mein eigentliches Problem nicht. Ich nehme an, dass es einen Fehler in der vorliegenden GDB gibt (auch wenn keine der Feature-Classes über angehängte Domains verfügt, darf ich dennoch keine löschen). Auf jeden Fall habe ich eine andere Methode gefunden, um zu bestimmen, welche FCs zugeordnete Domänen haben. Es ist interaktiv, aber viel schneller als das Durchlaufen jeder Feldeigenschaft auf jedem einzelnen fc:

Ziehen Sie mehrere Fcs per Drag & Drop von der Problem-GDB auf eine andere GDB und überprüfen Sie das Dialogfeld " Datenübertragung ". Gegebenenfalls verknüpfte Attributdomänen befinden sich am Ende der Liste. Wiederholen Sie den Vorgang in immer kleineren Gruppen, bis Sie sich verengen, was Ihnen mit @ $% ## fc schwer fällt.

Endlich auf 2 FCs eingegrenzt, die mit einer CV-Domain verknüpft sind

Matt Wilkie
quelle
Interessanterweise meldet das arcpy-Skript von Brian keine verknüpfte Domäne, obwohl Drag & Drop HD_148009_2mit der CV-Domäne verknüpft Permanencyist. Dies gilt auch für den Feldinspektor "Feature-Class-Eigenschaften" in ArcCatalog. Jetzt habe ich es endlich soweit eingegrenzt, dass ein Fehlerbericht mit dem technischen Support von Esri erstellt werden kann.
Matt Wilkie
3

Ich stelle mir vor, dass Matt Wilkie nachschlagen und schreiben musste, um Brians Code zu erweitern. Ich musste alle Domänen für Tabellen, Feature-Classes im Stammverzeichnis einer Datenbank und Features in allen Feature-Datasets abrufen. Ich habe die Informationen als CSV exportiert, damit einige andere Worker unsere Geodatabase-Umgebungen von alten Domains bereinigen können.

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
quelle
0

Esri: FAQ: Wie kann ich alle Stellen in meiner Geodatabase finden, an denen auf Domains verwiesen wird? . "Python-Funktionen, mit denen die Eigenschaften dieser Strukturen in einer Geodatabase aufgelistet werden können. Unter den Eigenschaften befinden sich die referenzierten Domänen. Es werden ein Beispielskript und eine File-Geodatabase bereitgestellt, die veranschaulichen, wie Python-Funktionen zum Auflisten der Domänen und anderer Eigenschaften von Feature-Classes und verwendet werden können Tabellen. Domänen können mit Feldern in einer Feature-Class oder Tabelle verknüpft werden. Außerdem können sie für Felder festgelegt werden, die nach einem Subtyp kategorisiert sind. "

Die Ergebnisse sind bei dieser Frage verrauscht und gehen über die verwendeten Domänen hinaus, bieten jedoch eine breitere Plattform für den Einstieg.

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]

Code-Auszug, der Kürze halber bearbeitet:

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 Wilkie
quelle