Mendaftar kelas fitur dengan domain aktif?

19

Saya memiliki geodatabase file Esri dengan domain atribut yang ditentukan. Saya perlu menghapus beberapa domain atribut tetapi tidak bisa karena "Domain digunakan oleh aturan atribut." . Bagaimana saya menemukan kelas fitur mana yang menggunakan domain?

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)

Ada lebih dari seratus kelas fitur di geodatabase, yang secara interaktif melihat properti bidang FC untuk masing-masingnya adalah non-starter. Gdb terlalu besar untuk dikonversi ke gdb pribadi dan masuk di pintu belakang dengan ms-access (bagaimanapun juga metode yang cerdik).


(2011-May-26): Cara lain untuk frasa ini adalah "Kelas Fitur mana yang menggunakan domain X?"

matt wilkie
sumber
Apakah Anda menggunakan domain subtipe?
Kirk Kuykendall
@kirk, ya ada subtipe, tapi domain yang saya coba hapus tidak menggunakan subtipe
matt wilkie
1
Dalam hal ini saya pikir kode Brian akan berfungsi.
Kirk Kuykendall
1
@kirk, koreksi: Saya tidak berpikir saya menggunakan subtipe + domain, tetapi setelah banyak mucking tentang dan membuka kasus dukungan teknis, ternyata saya benar-benar menggunakan satu setelah semua. Benar-benar klik-fest untuk mengidentifikasi cuplrit yang tersisa. Saya seharusnya menginvestasikan lebih banyak waktu untuk menindaklanjuti metode c # Anda!
matt wilkie

Jawaban:

3

Untuk menjawab pertanyaan tentang penanganan kelas fitur dengan subtipe, dimungkinkan dengan 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))

Kode subtipe, kode, akan menjadi nol jika tidak ada subtipe, sehingga kode tersebut mencetak 'Tidak Ada'.

The subtipe kamus telah lebih dari itu, sehingga memeriksanya dalam kode.

Richard Morgan
sumber
Mengubah jawaban yang saya terima untuk yang ini. Ini singkat dan langsung. Versi kode saya di github.com/envygeo/arcplus/blob/master/ArcToolbox/Scripts/… . Terima kasih!
matt wilkie
21

Python memiliki metode untuk mendaftarkan kelas fitur dalam geodatabase, mengulang setiap kelas fitur dalam daftar, mendaftarkan bidang di setiap kelas fitur, dan memperlihatkan domain dari setiap bidang.

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

Kode di atas harus bekerja di ArcGIS 10 dan itu akan mencetak daftar tepat di jendela juru bahasa python. Anda kemudian dapat menyalin dan menempel daftar ke editor teks atau Excel untuk meninjau hasilnya dengan lebih mudah.

Brian
sumber
Apakah ini akan menangani domain subtipe juga?
Kirk Kuykendall
Saya tidak yakin apakah ini akan menangani subtipe atau domain subtipe. Saya belum pernah menggunakan subtipe sebelumnya. Jika ada domain yang ditetapkan untuk bidang tertentu, nama domain akan dicetak.
Brian
cantik, terima kasih Brian. Awalnya itu tidak berhasil untuk saya, tetapi akhirnya saya ingat bahwa listFC tidak muncul kembali ke FeatureDatasets tanpa bantuan tambahan ( gis.stackexchange.com/questions/5893/… ). Semuanya baik sekarang! :)
matt wilkie
@Kirk, tidak, itu tidak melihat subtipe menggunakan domain.
matt wilkie
Ikuti contoh resources.arcgis.com/en/help/main/10.1/index.html#//… untuk menelusuri semua subtipe dan domain terkaitnya.
Michael Stimson
8

Karena saya tidak berpikir python menangani subtipe, saya memposting kode c # ini yang seharusnya. Saya mengujinya dengan sampel air / air limbah geodb Esri dan menemukan domain yang tidak digunakan berikut:

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

Seringkali DBA merasa terganggu bahwa domain - yang pada dasarnya adalah tabel pencarian - tidak dapat diakses melalui SQL.

Kode ini diuji dari arcmap ( diperbarui per komentar 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
sumber
sementara daftar domain yang tidak digunakan berguna, ini adalah kebalikan dari masalah yang harus dipecahkan. Saya sebenarnya mencari "FC mana yang menggunakan domain X?" (jadi saya dapat menghapus tautan, dan menjadikan domain tersebut domain yang tidak digunakan). ((Saya masih belum mencoba kode, saya hanya akan nama fungsi))
matt wilkie
@ tikar oh, ya, itu masuk akal. Saya telah mengubah kode untuk menunjukkan bagaimana melakukan itu.
Kirk Kuykendall
uhh, mungkin ini seharusnya menjadi pertanyaan yang lengkap, tapi, di mana saya meletakkan kode ini? Saya tidak dapat menemukan setara v10 dari editor VBA ( Tools-> Macros-> Visual Basic Editor ).
matt wilkie
Anda harus menginstal Visual Studio Express (gratis) atau lebih besar, dan ArcGIS SDK . Setelah Anda selesai melakukannya, Anda harus dapat mengikuti langkah-langkah ini untuk membuat tombol perintah , lalu salin dan tempel kode saya ke acara Klik. Anda juga perlu menambahkan referensi yang sesuai ke proyek.
Kirk Kuykendall
5

Kode ini harus mengembalikan apa yang diminta. Ini akan dengan singkat melintasi semua kelas fitur dan tabel di ruang kerja GDB / FS dan mengembalikan semua bidang yang terkait dengan domain, nama bidang, dan kelas fitur / tabel di dalamnya.

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 
KELAPA
sumber
4

Sayangnya jawaban Brian, yang merupakan jawaban langsung dan dapat digunakan pertanyaan yang diajukan, tidak menyelesaikan masalah saya yang sebenarnya. Saya kira karena ada bug di gdb (meskipun tidak ada kelas fitur yang memiliki domain terlampir, masih ada satu yang tidak boleh saya hapus). Bagaimanapun saya menemukan metode lain untuk menentukan fc mana yang memiliki domain terkait. Ini interaktif, tetapi jauh lebih cepat daripada menelusuri setiap properti bidang pada setiap fc:

Seret dan lepas tandan fc dari masalah gdb ke gdb lain dan periksa dialog Transfer Data . Domain atribut tertaut, jika ada, akan berada di bagian bawah daftar. Ulangi dalam kelompok yang lebih kecil dan lebih kecil sampai Anda mempersempit yang @ $% ## fc memberi Anda kesulitan.

akhirnya dipersempit menjadi 2 FC yang terhubung ke domain CV

matt wilkie
sumber
Anehnya, meskipun drag-n-drop mengatakan HD_148009_2terhubung ke CV Domain Permanency, skrip arc Brian melaporkan tidak ada domain tertaut, dan begitu pula inspektur bidang Feature Class Properties di ArcCatalog. Namun sekarang saya akhirnya mempersempitnya cukup untuk mencatat laporan bug dengan dukungan teknis Esri.
matt wilkie
4

Inilah yang saya bayangkan Matt Wilkie harus mencari dan menulis untuk menambah kode Brian. Saya harus mendapatkan semua domain untuk tabel, kelas fitur di direktori root database, dan fitur di semua dataset fitur. Saya mengekspor informasi sebagai csv untuk memungkinkan beberapa pekerja lain membersihkan lingkungan geodatabase kami di domain lama.

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

Esri: FAQ: Bagaimana saya bisa menemukan semua tempat di mana domain direferensikan dalam geodatabase saya? ."Fungsi-fungsi Python yang dapat membuat daftar properti dari struktur-struktur ini dalam sebuah geodatabase. Di antara sifat-sifat tersebut adalah domain yang dirujuk. Sebuah skrip sampel dan file geodatabase disediakan yang menunjukkan bagaimana fungsi-fungsi Python dapat digunakan untuk membuat daftar domain dan properti lain dari kelas fitur dan tabel. Domain dapat dikaitkan dengan bidang dalam kelas fitur atau tabel; mereka juga dapat diatur untuk bidang yang dikategorikan oleh subtipe. "

Hasilnya berisik untuk pertanyaan ini, melampaui domain apa yang digunakan, tetapi platform yang lebih luas untuk memulai.

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]

Kutipan kode, diedit untuk singkatnya:

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
sumber