Czy wyświetlasz klasy obiektów z aktywnymi domenami?

19

Mam geobazę pliku Esri ze zdefiniowanymi domenami atrybutów . Muszę usunąć niektóre domeny atrybutów, ale nie mogę, ponieważ „Domena jest używana przez regułę atrybutów”. . Jak mogę dowiedzieć się, które klasy obiektów korzystają z domen?

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)

W geobazie znajduje się ponad sto klas obiektów, interaktywnie przyglądających się właściwościom pola FC dla każdego z nich, który nie jest starterem. Gdb jest zbyt duży, aby przekonwertować go na osobisty gdb i wejść do tylnych drzwi z dostępem MS (i tak podejrzana metoda).


(2011-26 maja): Innym sposobem sformułowania tego jest „która klasa obiektów korzysta z domeny X?”

matowe wilkie
źródło
Czy używasz domen podtytułowanych?
Kirk Kuykendall
@kirk, tak, jest podtyp, ale domeny, które próbuję usunąć, nie używają tego podtypu
matt wilkie
1
W takim przypadku myślę, że kod Briana zadziałałby.
Kirk Kuykendall
1
@kirk, poprawka: Nie sądzę, że używam podtypów + domen, ale po długim zastanawianiu się i otwarciu skrzynki pomocy technicznej okazuje się, że w końcu z niej korzystałem. To był prawdziwy fest-click, aby zidentyfikować konkretny pozostały cuplrit. Powinienem poświęcić więcej czasu na śledzenie twojej metody c #!
matt wilkie

Odpowiedzi:

3

Aby odpowiedzieć na pytanie o obsługę klas obiektów z podtypami, jest to możliwe dzięki 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))

Kod podtypu, kod stcode, będzie wynosił zero, jeśli nie będzie żadnych podtypów, więc kod wyświetla „Brak”.

Słowniku podtypy ma więcej do niego, więc sprawdzić je w kodzie.

Richard Morgan
źródło
Zmiana mojej zaakceptowanej odpowiedzi na to. Jest krótki i bezpośredni. Moja wersja twojego kodu na github.com/envygeo/arcplus/blob/master/ArcToolbox/Scripts/… . Dzięki!
matt wilkie
21

Python ma metody wyliczania klas obiektów w geobazie, przeszukiwania każdej klasy obiektów na liście, wyszczególniania pól w każdej klasie obiektów i pokazywania domeny każdego pola.

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

Powyższy kod powinien działać w ArcGIS 10 i wyświetli listę bezpośrednio w oknie interpretera Pythona. Następnie możesz skopiować i wkleić listę do edytora tekstu lub programu Excel, aby łatwiej przeglądać wyniki.

Brian
źródło
Czy obejmie to także domeny podsieci?
Kirk Kuykendall
Nie jestem pewien, czy będzie to obsługiwać podtypy lub domeny podtytułowe. Nigdy wcześniej nie korzystałem z podtypów. Jeśli do określonego pola przypisana jest domena, nazwa domeny zostanie wydrukowana.
Brian
piękne, dzięki Brian. Początkowo nie działało to dla mnie, ale w końcu przypomniałem sobie, że listFC nie powraca do FeatureDatasets bez dodatkowej pomocy ( gis.stackexchange.com/questions/5893/... ). Wszystko dobrze teraz! :)
matt wilkie
@Kirk, nie, nie widzi podtypów korzystających z domen.
matt wilkie
Postępuj zgodnie z przykładem resources.arcgis.com/en/help/main/10.1/index.html#//…, aby przejść przez wszystkie podtypy i powiązane z nimi domeny.
Michael Stimson,
8

Ponieważ nie sądzę, że Python obsługuje podtypy, zamieszczam ten kod c #, który powinien. Przetestowałem to za pomocą próbnej geodb Esri wody / ścieków i znalazłem następujące nieużywane domeny:

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

Często DBA denerwuje się, że do domen - które są zasadniczo tabelami odnośników - nie można uzyskać dostępu przez SQL.

Ten kod przetestowany z arcmap ( zaktualizowany zgodnie z komentarzem Matta):

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
źródło
chociaż lista nieużywanych domen jest przydatna, jest to odwrotność problemu do rozwiązania. Tak naprawdę szukałem „który FC używa domeny X?” (więc mogę usunąć link i uczynić domenę nieużywaną domeną). ((Nadal nie wypróbowałem kodu, po prostu używam nazwy funkcji))
Matt Wilkie
@matt o tak, to ma sens. Zmieniłem kod, aby pokazać, jak to zrobić.
Kirk Kuykendall
uhh, może to powinno być pełne pytanie, ale gdzie mam umieścić ten kod? Nie mogę znaleźć odpowiednika v10 edytora VBA ( Narzędzia-> Makra-> Edytor Visual Basic ).
matt wilkie
Musisz zainstalować program Visual Studio Express (bezpłatny) lub nowszy oraz ArcGIS SDK . Gdy to zrobisz, powinieneś być w stanie postępować zgodnie z instrukcją tworzenia przycisku polecenia , a następnie skopiować i wkleić mój kod do zdarzenia Click. Musisz także dodać odpowiednie odniesienia do projektu.
Kirk Kuykendall
5

Ten kod powinien zwrócić to, o co jest proszony. W zwięzły sposób przejdzie przez wszystkie klasy elementów i tabele w obszarze roboczym GDB / FS i zwróci wszystkie pola powiązane z domeną, nazwą pola i klasą elementu / tabelą, do której należy.

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 
KOKOSOWIEC
źródło
4

Niestety odpowiedź Briana, która jest bezpośrednią i użyteczną odpowiedzią na zadane pytanie, nie rozwiązuje mojego rzeczywistego problemu. Podejrzewam, że z powodu błędu w dostępnym gdb (chociaż żadna z klas funkcji nie ma dołączonych domen, wciąż jest jedna, której nie mogę usunąć). W każdym razie znalazłem inną metodę określania, które domeny mają powiązane domeny. Jest interaktywny, ale o wiele szybszy niż przeglądanie każdej właściwości pola na każdym fc:

Przeciągnij i upuść paczki fc z problemu gdb na inny gdb i sprawdź okno dialogowe Transfer danych . Połączone domeny atrybutów, jeśli istnieją, będą znajdować się na dole listy. Powtarzaj w coraz mniejszych pęczkach, aż zawęzisz się, co @ $% ## fc sprawia ci trudność.

ostatecznie zawęził się do 2 FC powiązanych z domeną CV

matowe wilkie
źródło
Co ciekawe, nawet jeśli przeciągnij i upuść mówi, że HD_148009_2jest połączony z domeną CV Permanency, skrypt Arcana Briana nie zgłasza żadnej połączonej domeny, podobnie jak inspektor pól właściwości klasy elementów w ArcCatalog. Jednak teraz w końcu zawęziłem go na tyle, aby zapisać raport o błędzie dzięki pomocy technicznej Esri.
matt wilkie
4

Wyobrażam sobie, że Matt Wilkie musiał podnieść wzrok i napisać, by ulepszyć kod Briana. Musiałem uzyskać wszystkie domeny dla tabel, klasy obiektów w katalogu głównym bazy danych i funkcje we wszystkich zestawach danych funkcji. Wyeksportowałem te informacje jako plik CSV, aby niektórzy inni pracownicy mogli oczyścić nasze środowiska geobazy ze starych domen.

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
źródło
0

Esri: FAQ: Jak znaleźć wszystkie miejsca, do których odwołują się domeny w mojej geobazie? . „Funkcje Pythona, które mogą wyświetlać właściwości tych struktur w geobazie. Wśród właściwości są przywoływane domeny. Dostarczono przykładowy skrypt i geobazę plików, które pokazują, w jaki sposób można używać funkcji Pythona do wyświetlania domen i innych właściwości klas elementów i tabele. Domeny można powiązać z polami w klasie obiektów lub tabeli; można je dodatkowo ustawić dla pól sklasyfikowanych według podtypu. ”

Wyniki są głośne w przypadku tego pytania, wykraczając poza to, które domeny są używane, ale stanowi szerszą platformę do rozpoczęcia.

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]

Fragment kodu, zredagowany dla zwięzłości:

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)
...
matowe wilkie
źródło