Список классов объектов с активными доменами?

19

У меня есть файловая база геоданных Esri с определенными доменами атрибутов . Мне нужно удалить некоторые из доменов атрибутов, но не могу, потому что «Домен используется правилом атрибутов». , Как я могу узнать, какие классы объектов используют домены?

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)

В базе геоданных более ста классов пространственных объектов, в интерактивном режиме поиск свойств поля FC для каждого из них не является началом. GDB слишком велик, чтобы преобразовать его в персональный GDB и войти в черный ход с помощью ms-access (в любом случае, хитрый метод).


(2011-May-26): Еще один способ сформулировать это так: «какой класс объектов использует домен X?»

Мэтт Уилки
источник
Вы используете подтип доменов?
Кирк Куйкендалл
@kirk, да, есть подтип, но домены, которые я пытаюсь удалить, не используют подтип
matt wilkie
1
В этом случае я думаю, что код Брайана сработает.
Кирк Куйкендалл
1
@kirk, исправление: я не думал, что использую подтипы + домены, но после долгих размышлений и открытия случая технической поддержки выясняется, что я действительно использовал один из них. Это был настоящий клик-фест, чтобы идентифицировать конкретный оставшийся кубрит. Я должен был потратить больше времени на изучение вашего метода c #!
Мэтт Уилки

Ответы:

3

Чтобы ответить на вопрос обработки классов объектов с подтипами, это можно сделать с помощью 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))

Код подтипа stcode будет равен нулю, если нет подтипов, поэтому код выводит «None».

Словарь подтипа имеет больше к нему, так что проверить его в коде.

Ричард Морган
источник
Меняя мой принятый ответ на этот. Это коротко и прямо. Моя версия вашего кода на github.com/envygeo/arcplus/blob/master/ArcToolbox/Scripts/… . Благодарность!
Мэтт Вилки
21

В Python есть методы для перечисления классов пространственных объектов в базе геоданных, циклического просмотра каждого класса пространственных объектов в списке, перечисления полей в каждом классе пространственных объектов и отображения домена каждого поля.

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

Приведенный выше код должен работать в ArcGIS 10, и он напечатает список прямо в окне интерпретатора Python. Затем вы можете скопировать и вставить список в текстовый редактор или Excel для более удобного просмотра результатов.

Брайан
источник
Будет ли это обрабатывать подтип доменов тоже?
Кирк Куйкендалл
Я не уверен, будет ли это обрабатывать подтипы или подтиповые домены. Я никогда не использовал подтипы раньше. Если для определенного поля назначен домен, будет напечатано имя домена.
Брайан
Прекрасно, спасибо Брайан. Первоначально это не работало для меня, но в итоге я вспомнил, что listFC не возвращается в FeatureDatasets без некоторой дополнительной помощи ( gis.stackexchange.com/questions/5893/… ). Все хорошо сейчас! :)
Мэтт Уилки
@ Кирк, нет, он не видит подтипы, использующие домены.
Мэтт Уилки
Следуйте примеру resources.arcgis.com/en/help/main/10.1/index.html#//…, чтобы просмотреть все подтипы и связанные с ними домены.
Майкл Стимсон
8

Поскольку я не думаю, что python обрабатывает подтипы, я публикую этот код на C #, который должен. Я проверил его с помощью образца Esri Geodb для воды и сточных вод и обнаружил следующие неиспользуемые домены:

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

Часто администраторы БД раздражаются, что к доменам, которые по сути являются справочными таблицами, нельзя получить доступ через SQL.

Этот код протестирован из arcmap ( обновлено согласно комментарию Мэтта):

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);
}
Кирк Куйкендалл
источник
Хотя перечисление неиспользуемых доменов полезно, это обратная задача, которую необходимо решить. Я на самом деле искал "какой FC использует домен X?" (поэтому я могу удалить ссылку и сделать домен неиспользуемым доменом). ((Я до сих пор не пробовал код, я просто использую название функции))
matt wilkie
@ О, да, это имеет смысл. Я изменил код, чтобы показать, как это сделать.
Кирк Куйкендалл
ээээ, может быть, это должен быть полноценный вопрос, но куда мне поместить этот код? Я не могу найти v10 эквивалент редактора VBA ( Инструменты-> Макросы-> Редактор Visual Basic ).
Мэтт Уилки
Вам нужно установить Visual Studio Express (бесплатно) или более поздней версии и ArcGIS SDK . Как только вы это сделаете, вы сможете выполнить это пошаговое руководство по созданию командной кнопки , а затем скопировать и вставить мой код в событие Click. Вам также необходимо добавить соответствующие ссылки на проект.
Кирк Куйкендалл
5

Этот код должен возвращать то, что запрашивается. Он будет кратко перебирать все классы пространственных объектов и таблицы в рабочей области GDB / FS и возвращать все поля, связанные с доменом, именем поля и классом пространственных объектов / таблицей, к которой он принадлежит.

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
источник
4

К сожалению, ответ Брайана, который является прямым и полезным ответом на заданный вопрос, не решает мою актуальную проблему. Я предполагаю, что из-за ошибки в gdb (хотя ни к одному из классов пространственных объектов не присоединены домены, есть еще один, который мне не разрешено удалять). В любом случае я нашел другой метод для определения того, какие фк имеют связанные домены. Это интерактивно, но намного быстрее, чем прохождение каждого свойства поля на каждом отдельном объекте:

Перетащите наборы файлов с проблемного GDB на другой GDB и проверьте диалоговое окно Data Transfer . Связанные домены атрибутов, если таковые имеются, будут внизу списка. Повторяйте все меньшие и меньшие сгустки, пока не сузите круг, который @ $% ## fc доставляет вам неприятности.

наконец, сузился до 2 FC, связанных с доменом CV

Мэтт Уилки
источник
Любопытно, что несмотря на то, что drag-n-drop говорит, что HD_148009_2он связан с доменом CV Permanency, arcpy скрипт Брайана не сообщает о связанном домене, а также не делает инспектор полей Свойства класса объектов в ArcCatalog. Однако теперь я, наконец, сузил его настолько, чтобы регистрировать отчет об ошибках в технической поддержке Esri.
Мэтт Уилки
4

Это то, что, по-моему, Мэтту Уилки пришлось искать и писать, чтобы дополнить код Брайана. Мне нужно было получить все домены для таблиц, классов объектов в корневом каталоге базы данных и объектов во всех наборах классов объектов. Я экспортировал информацию в виде csv, чтобы позволить другим работникам очистить нашу базу геоданных от старых доменов.

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: Как я могу найти все места, на которые ссылаются домены в моей базе геоданных? , "Python-функции, которые могут перечислять свойства этих структур в базе геоданных. Среди свойств - ссылочные домены. Предоставляются пример сценария и файловая база геоданных, которые демонстрируют, как функции Python могут использоваться для перечисления доменов и других свойств классов объектов и Таблицы. Домены могут быть связаны с полями в классе пространственных объектов или в таблице; они могут быть дополнительно установлены для полей, классифицированных по подтипу. "

Результаты зашумлены по этому вопросу, выходят за рамки того, какие домены используются, но являются более широкой платформой для начала работы.

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]

Отрывок кода, отредактированный для краткости:

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)
...
Мэтт Уилки
источник