1

I'm looking for a way to get the full path of a feature class within feature datasets (either in personal or file geodatabases) without using arcpy. My first attempt was to use ogr which works fine if the feature classes are stored in the geodatabase directly. But I didn't manage to get the full path if a feature dataset is involved. I tried the "OpenFileGDB" driver as well as the "FileGDB" driver. My test geodatabase stores five feature classes, four of them are within the feature dataset and one directly in the geodatabase.

My code so far:

gdb_path = r"C:\my_path\test_gdb_fd.gdb"
gdb_driver = ogr.GetDriverByName("FileGDB")
gdb = gdb_driver.Open(gdb_path)
for featsClass_idx in range(gdb.GetLayerCount()):
    featsClass = gdb.GetLayerByIndex(featsClass_idx)
    print featsClass.GetName()

this prints the basenames of all feature classes without any information whether the feature class is within the feature dataset or not. I'm using GDAL 2.2.4, FileGDB API 1.3, Python 2.7 32 bit

1 Answer 1

2

I'm not super familiar with working with GDBs in the context of GDAL. If it's possible to do via the actual APIs that would obviously be ideal, but I can't comment on that.

That said, if it winds up not being possible and you're okay with getting hacky, you can dig around in the a00000004.gdbtable file of the GDB itself. See here for a partial reverse engineering of the spec, but the takeaway is that the aforementioned file contains fragments of XML that describe the GDB contents.

Here's a quick and dirty parser to dump full paths to all feature classes:

import mmap
import os
from xml.etree import cElementTree as cET

gdb = r'C:\Users\gberard\Documents\ArcGIS\New File Geodatabase.gdb'
table = os.path.join(gdb, 'a00000004.gdbtable')

tag_a = '<DEFeatureClassInfo'
tag_b = '</DEFeatureClassInfo>'

with open(table, 'r') as f:
    mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)

    off_b = 0
    while True:
        off_a = mm.find(tag_a, off_b)
        off_b = mm.find(tag_b, off_a) + len(tag_b)
        if off_a < 0 or off_b < 0:
            break

        tree = cET.fromstring(mm[off_a:off_b])
        elem = tree.find('CatalogPath')
        print os.path.join(gdb, elem.text[1:])

Which would output:

C:\Users\gberard\Documents\ArcGIS\New File Geodatabase.gdb\SomeFeatureDataset\SomeFC
C:\Users\gberard\Documents\ArcGIS\New File Geodatabase.gdb\OtherFeatureDataset\OtherFC 
C:\Users\gberard\Documents\ArcGIS\New File Geodatabase.gdb\JustAFeatureClass

Again, use the APIs if you can. The above is gross, but it is an option.

1
  • 1
    This method is a lot faster than using arcpy.walk or even ogr, but be aware that tables and feature datasets, presumably other objects too, that have been deleted from the gdb will STILL be recorded in the a00000004.gdbtable file. I haven't figured out how to identify those yet.
    – Evan Thoms
    Nov 9, 2021 at 23:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.