Liste des classes d'entités avec des domaines actifs?

19

J'ai une géodatabase fichier Esri avec des domaines d'attribut définis. Je dois supprimer certains des domaines d'attribut, mais je ne peux pas car «le domaine est utilisé par une règle d'attribut». . Comment puis-je découvrir quelle (s) classe (s) d'entités utilisent les domaines?

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)

Il existe plus d'une centaine de classes d'entités dans la géodatabase, la recherche interactive des propriétés de champ FC pour chacune n'est pas un démarreur. La gdb est trop grande pour être convertie en gdb personnelle et passer par la porte arrière avec ms-access (une méthode douteuse de toute façon).


(2011-May-26): Une autre façon d'exprimer ceci est "quelle classe d'entités utilise le domaine X?"

Matt Wilkie
la source
Utilisez-vous des domaines sous-typés?
Kirk Kuykendall
@kirk, oui il y a un sous-type, mais les domaines que j'essaie de supprimer n'utilisent pas le sous
matt wilkie
1
Dans ce cas, je pense que le code de Brian fonctionnerait.
Kirk Kuykendall
1
@kirk, correction: Je ne pensais pas que j'utilisais des sous-types + domaines, mais après avoir beaucoup déblayé et ouvert un dossier de support technique, il s'avère que j'en utilisais un après tout. C'était un véritable click-fest pour identifier le cuplrit particulier restant. J'aurais dû investir plus de temps dans le suivi de votre méthode c #!
matt wilkie

Réponses:

3

Pour répondre à la question de la gestion des classes d'entités avec des sous-types, c'est possible avec 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))

Le code de sous-type, stcode, sera nul s'il n'y a pas de sous-types, donc le code imprime «Aucun».

Le dictionnaire des sous-types en a plus, alors inspectez-le dans le code.

Richard Morgan
la source
Changer ma réponse acceptée à celle-ci. C'est court et direct. Ma version de votre code sur github.com/envygeo/arcplus/blob/master/ArcToolbox/Scripts/… . Merci!
matt wilkie
21

Python propose des méthodes pour répertorier les classes d'entités dans une géodatabase, parcourir chaque classe d'entités de la liste, répertorier les champs de chaque classe d'entités et afficher le domaine de chaque champ.

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

Le code ci-dessus devrait fonctionner dans ArcGIS 10 et il imprimera une liste directement dans la fenêtre de l'interpréteur python. Vous pouvez ensuite copier et coller la liste dans un éditeur de texte ou Excel pour consulter les résultats plus facilement.

Brian
la source
Cela gérera-t-il également les domaines sous-typés?
Kirk Kuykendall
Je ne sais pas si cela gérera les sous-types ou les domaines sous-typés. Je n'ai jamais utilisé de sous-types auparavant. Si un domaine est affecté à un champ particulier, le nom de domaine sera imprimé.
Brian
magnifique, merci Brian. Au départ, cela ne fonctionnait pas pour moi, mais finalement je me suis souvenu que listFC ne récurrente pas dans FeatureDatasets sans aide supplémentaire ( gis.stackexchange.com/questions/5893/… ). Tout va bien maintenant! :)
matt wilkie
@Kirk, non, il ne voit pas les sous-types utilisant des domaines.
matt wilkie
Suivez l'exemple resources.arcgis.com/en/help/main/10.1/index.html#//… pour parcourir tous les sous-types et leurs domaines associés.
Michael Stimson
8

Comme je ne pense pas que python gère les sous-types, je publie ce code c # qui devrait. Je l'ai testé avec un échantillon de géodonnées eau / eaux usées d'Esri et j'ai trouvé les domaines inutilisés suivants:

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

Souvent, les administrateurs de bases de données se fâchent que les domaines - qui sont essentiellement des tables de recherche - ne soient pas accessibles via SQL.

Ce code testé depuis arcmap ( mis à jour par le commentaire 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
la source
si lister les domaines inutilisés est utile, c'est l'inverse du problème à résoudre. Je cherchais en fait "quel FC utilise le domaine X?" (Je peux donc supprimer le lien et faire du domaine un domaine inutilisé). ((Je n'ai toujours pas essayé le code, je vais juste citer le nom de la fonction))
matt wilkie
@matt oh, oui, cela a du sens. J'ai changé le code pour montrer comment faire cela.
Kirk Kuykendall
euhh, peut-être que cela devrait être une question à part entière, mais, où dois-je mettre ce code? Je ne trouve pas l'équivalent v10 de l'éditeur VBA ( Tools-> Macros-> Visual Basic Editor ).
Matt Wilkie
Vous devez installer Visual Studio Express (gratuit) ou une version ultérieure et le SDK ArcGIS . Une fois que vous avez fait cela, vous devriez pouvoir suivre cette procédure pas à pas pour créer un bouton de commande , puis copier et coller mon code dans l'événement Click. Vous devrez également ajouter des références appropriées au projet.
Kirk Kuykendall
5

Ce code devrait retourner ce qui est demandé. Il traversera succinctement toutes les classes d'entités et les tables d'un espace de travail GDB / FS et renverra tous les champs associés à un domaine, le nom du champ et la classe / table d'entités à laquelle il appartient.

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
la source
4

Malheureusement, la réponse de Brian, qui est une réponse directe et utilisable à la question posée, ne résout pas mon problème réel. Je présume en raison d'un bogue dans la gdb à portée de main (même si aucune des classes d'entités n'a de domaine attaché, il y en a toujours un que je ne suis pas autorisé à supprimer). Dans tous les cas, j'ai trouvé une autre méthode pour déterminer quels fc ont des domaines associés. C'est interactif, mais beaucoup plus rapide que de parcourir chaque propriété de champ sur chaque fc:

Faites glisser et déposez des groupes de fc de gdb problématique vers un autre gdb et inspectez la boîte de dialogue Transfert de données . Les domaines d'attributs liés, le cas échéant, seront en bas de la liste. Répétez l'opération en grappes de plus en plus petites jusqu'à ce que vous vous rétrécissiez, ce qui vous donne du mal à @ $% ## fc.

enfin réduit à 2 FC liés à un domaine CV

Matt Wilkie
la source
Curieusement, même si le glisser-déposer indique qu'il HD_148009_2est lié au domaine CV Permanency, le script arcpy de Brian ne signale aucun domaine lié, et l'inspecteur des champs Propriétés de la classe d'entités dans ArcCatalog non plus. Cependant, maintenant je l'ai finalement suffisamment réduit pour enregistrer un rapport de bogue avec le support technique d'Esri.
matt wilkie
4

C'est ce que j'imagine que Matt Wilkie a dû rechercher et écrire pour augmenter le code de Brian. J'ai dû obtenir tous les domaines pour les tables, les classes d'entités dans le répertoire racine d'une base de données et les fonctionnalités dans tous les jeux de données d'entités. J'ai exporté les informations en tant que csv pour permettre à d'autres travailleurs de nettoyer nos environnements de géodatabase d'anciens domaines.

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
la source
0

Esri: FAQ: Comment puis-je trouver tous les endroits où les domaines sont référencés dans ma géodatabase? ."Fonctions Python pouvant répertorier les propriétés de ces structures dans une géodatabase. Parmi les propriétés figurent les domaines référencés. Un exemple de script et de géodatabase fichier est fourni qui montre comment les fonctions Python peuvent être utilisées pour répertorier les domaines et autres propriétés des classes d'entités et tables. Les domaines peuvent être associés à des champs dans une classe d'entités ou une table; ils peuvent en outre être définis pour des champs classés par sous-type. "

Les résultats sont bruyants pour cette question, allant au-delà des domaines utilisés, mais constituent une plate-forme plus large pour commencer.

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]

Extrait de code, édité par souci de concision:

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
la source