¿Listado de clases de entidad con dominios activos?

19

Tengo una geodatabase de archivos Esri con dominios de atributo definidos. Necesito eliminar algunos de los dominios de atributo, pero no puedo porque "el dominio es utilizado por una regla de atributo". . ¿Cómo podría descubrir qué clase de entidad está utilizando los dominios?

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)

Hay más de un centenar de clases de entidad en la geodatabase, mirando interactivamente las propiedades del campo FC para cada una no es un iniciador. El gdb es demasiado grande para convertirlo en un gdb personal y entrar por la puerta trasera con ms-access (un método poco fiable de todos modos).


(26 de mayo de 2011): Otra forma de expresar esto es "¿Qué clase de entidad está usando el dominio X?"

wilkie mate
fuente
¿Estás utilizando dominios subtipados?
Kirk Kuykendall
@kirk, sí, hay un subtipo, pero los dominios que estoy tratando de eliminar no están usando el subtipo
matt wilkie
1
En ese caso, creo que el código de Brian funcionaría.
Kirk Kuykendall
1
@kirk, corrección: no pensé que estaba usando subtipos + dominios, pero después de mucho hablar y abrir un caso de soporte técnico, resulta que en realidad estaba usando uno después de todo. Fue un verdadero click-fest para identificar el cuplrit restante particular. ¡Debería haber invertido más tiempo en el seguimiento de su método C #!
Matt Wilkie

Respuestas:

3

Para responder a la pregunta de manejar clases de entidad con subtipos, es posible 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))

El código de subtipo, stcode, será cero si no hay subtipos, por lo que el código imprime 'Ninguno'.

El diccionario de subtipos tiene más, así que inspecciónalo en código.

Richard Morgan
fuente
Cambiar mi respuesta aceptada a esta. Es corto y directo. Mi versión de su código en github.com/envygeo/arcplus/blob/master/ArcToolbox/Scripts/… . ¡Gracias!
Matt Wilkie
21

Python tiene métodos para enumerar clases de entidad en una geodatabase, recorriendo cada clase de entidad en la lista, enumerando campos en cada clase de entidad y mostrando el dominio 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

El código anterior debería funcionar en ArcGIS 10 e imprimirá una lista directamente en la ventana del intérprete de Python. Luego puede copiar y pegar la lista en un editor de texto o Excel para revisar los resultados más fácilmente.

Brian
fuente
¿Esto también manejará dominios subtipados?
Kirk Kuykendall
No estoy seguro de si esto manejará subtipos o dominios subtipados. Nunca he usado subtipos antes. Si hay un dominio asignado a un campo en particular, se imprimirá el nombre de dominio.
Brian
hermoso, gracias Brian. Inicialmente no funcionó para mí, pero eventualmente recordé que listFC no se repite en FeatureDatasets sin ayuda adicional ( gis.stackexchange.com/questions/5893/… ). Todo bien ahora! :)
matt wilkie
@ Kirk, no, no ve subtipos usando dominios.
Matt Wilkie
Siga el ejemplo resources.arcgis.com/en/help/main/10.1/index.html#//… para ver todos los subtipos y sus dominios asociados.
Michael Stimson
8

Como no creo que Python maneje subtipos, estoy publicando este código C # que debería. Lo probé con el agua de muestra / agua residual de Esri geodb y encontré los siguientes dominios no 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

A menudo, los DBA se molestan porque no se puede acceder a los dominios, que son esencialmente tablas de búsqueda, a través de SQL.

Este código probado desde arcmap ( actualizado por el comentario 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
fuente
Si bien es útil enumerar dominios no utilizados, esta es la inversa del problema que se debe resolver. En realidad estaba buscando "¿qué FC está usando el dominio X?" (para que pueda eliminar el enlace y convertir el dominio en un dominio no utilizado). ((Todavía no he probado el código, solo voy por el nombre de la función))
matt wilkie
@matt oh, sí, eso tiene sentido. He cambiado el código para mostrar cómo hacerlo.
Kirk Kuykendall
uhh, tal vez esta debería ser una pregunta completa, pero, ¿dónde pongo este código? No puedo localizar el equivalente v10 del editor VBA ( Herramientas-> Macros-> Editor de Visual Basic ).
Matt Wilkie
Deberá instalar Visual Studio Express (gratis) o superior, y el SDK de ArcGIS . Una vez que haya hecho eso, debería poder seguir este tutorial para crear un botón de comando , luego copiar y pegar mi código en el evento Click. También necesitará agregar referencias apropiadas al proyecto.
Kirk Kuykendall
5

Este código debe devolver lo que se solicita. Recorrerá sucintamente todas las clases y tablas de entidades en un espacio de trabajo GDB / FS y devolverá todos los campos asociados con un dominio, el nombre del campo y la clase / tabla de entidades a la que pertenece.

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

Lamentablemente, la respuesta de Brian, que es una respuesta directa y útil a la pregunta formulada, no resuelve mi problema real. Supongo que debido a un error en el gdb en cuestión (aunque ninguna de las clases de entidad tiene dominios adjuntos, todavía hay uno que no puedo eliminar). En cualquier caso, encontré otro método para determinar qué fc tienen dominios asociados. Es interactivo, pero mucho más rápido que pasar por cada propiedad de campo en cada fc:

Arrastre y suelte grupos de archivos fc del problema gdb a otro gdb e inspeccione el cuadro de diálogo Transferencia de datos . Los dominios de atributos vinculados, si los hay, estarán al final de la lista. Repita en grupos cada vez más pequeños hasta que reduzca lo que @ $% ## fc le está causando dificultades.

finalmente se redujo a 2 FC vinculados a un dominio CV

wilkie mate
fuente
Curiosamente, aunque drag-n-drop dice que HD_148009_2está vinculado al CV Domain Permanency, el script arcpy de Brian no informa de ningún dominio vinculado, y tampoco lo hace el inspector de campos de Propiedades de clase de entidad en ArcCatalog. Sin embargo, ahora finalmente lo he reducido lo suficiente como para registrar un informe de error con el soporte técnico de Esri.
Matt Wilkie
4

Esto es lo que imagino que Matt Wilkie tuvo que buscar y escribir para aumentar el código de Brian. Tenía que obtener todos los dominios para tablas, clases de entidades en el directorio raíz de una base de datos y funciones en todos los conjuntos de datos de funciones. Exporté la información como un csv para permitir que otros trabajadores limpien nuestros entornos de geodatabase de dominios antiguos.

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

Esri: Preguntas frecuentes: ¿Cómo puedo encontrar todos los lugares donde se hace referencia a dominios en mi geodatabase? . "Funciones de Python que pueden enumerar las propiedades de estas estructuras en una geodatabase. Entre las propiedades se encuentran los dominios a los que se hace referencia. Se proporciona un script de muestra y una geodatabase de archivos que demuestran cómo las funciones de Python podrían usarse para enumerar los dominios y otras propiedades de clases de entidad y tablas. Los dominios se pueden asociar con campos en una tabla o clase de entidad; también se pueden establecer para campos categorizados por un subtipo ".

Los resultados son ruidosos para esta pregunta, van más allá de los dominios que están en uso, pero es una plataforma más amplia para comenzar.

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]

Extracto del código, editado por brevedad:

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)
...
wilkie mate
fuente