Skopiować geobazę ArcSDE do pliku geobazie za pomocą ArcPy?

9

Chciałbym wykonać dokładną kopię (domen, zestawów danych elementów, klas obiektów itp.) Bazy danych SDE do geobazy danych pliku.

Wypróbowałem kilka możliwości, w tym:

  1. za pomocą procesu kopiowania (zarządzanie danymi)
  2. tworzenie nowego GDB i ręczne kopiowanie każdego zestawu danych funkcji z SDE
  3. eksportowanie dokumentu obszaru roboczego xml z SDE i importowanie go do GDB

Wydaje się Copy_management, że proces nie działałby w przypadku kopiowania SDE do GDB, ponieważ typy danych wejściowych i wyjściowych muszą być zgodne.

Proces importowania każdego zestawu danych funkcji do nowego GDB można prawdopodobnie zautomatyzować również za pomocą funkcji Copy_management poprzez iterację po każdym zestawie danych funkcji, choć wydaje się, że może to powodować problemy z niekompletnym kopiowaniem, jeśli wystąpi błąd w jednym z procesów.

Wydaje się, że eksportowanie i importowanie obszarów roboczych xml działa, chociaż proces ten tworzy niewiarygodnie duże pliki, gdy proces jest używany na dużych geobazach.

Czy istnieje prostszy sposób na skopiowanie zawartości i schematu SDE do GDB niż wymienione wyżej sposoby w sposób, który można zautomatyzować?

Jeśli nie, czy istnieją powody, dla których powyższe możliwości nie powinny być wykorzystane w tym procesie?

Krausers
źródło
1
Powiązane: gis.stackexchange.com/q/63368/753
blah238

Odpowiedzi:

5

Jedynym sposobem na uzyskanie prawdziwej kopii danych (domen, zestawów danych, relacji itp.) Jest użycie metody ręcznego kopiowania i wklejania w katalogu. ESRI nie dał nam jeszcze możliwości przesyłania tych danych w jakikolwiek inny sposób za pomocą pojedynczej operacji, którą można łatwo wykonać skryptem.

Mam proces nocny, który kopiuje moje dwie podstawowe bazy danych SDE do plików geobaz danych w celu zapewnienia ciągłości operacji. Dzieje się tak, aby w razie nagłego wypadku mój personel miał jakieś dane do pracy, dopóki mój sklep IT nie będzie mógł odbudować mojego SDE z kopii zapasowej. Po wielu próbach i błędach zdecydowałem, że możemy żyć z ograniczeniami korzystania z FeatureClassToFeatureClass_conversion i TableToTable_conversion do przesyłania naszych danych każdego wieczora.

Tak, tracimy część funkcjonalności geobazy, ale teraz będzie ona działać bez nadzoru w nocy i jest gotowa do pracy, jak tylko ją zdobędę. W moim przypadku jedyną funkcją, której tak naprawdę brakuje nam (zakładając działanie w trybie awaryjnym), jest to, że moje klasy relacji są zepsute, ponieważ konwersja resetuje ObjectID, które łączą dwie tabele.

Dopóki ESRI nie da nam więcej opcji, będziesz musiał spojrzeć na to, co w tej chwili chcesz poświęcić; czas i wysiłek czy funkcjonalność?

Lemur
źródło
Dokument XML Worskspace nie działa?
Jyler,
8

Wiem, że ten post jest trochę stary, ale pomyślałem, że podzielę się moją odpowiedzią, ponieważ napotkałem ten sam problem. Poniższy skrypt POWINIEN skopiować wszystkie tabele, klasy elementów i relacje spoza zbioru danych, a także skopiuje wszystkie zestawy danych, w tym klasy elementów, topologię itp. W zestawie danych. Pominie wszelkie błędy podczas kopiowania i będzie kontynuowane. Spowoduje to utworzenie pliku dziennika zawierającego dane, takie jak liczba elementów źródłowej bazy danych i liczba elementów docelowych, dzięki czemu można porównać kopię, a także zarejestrować błędy, które napotka.

import arcpy, os, shutil, time
import logging as log
from datetime import datetime

def formatTime(x):
    minutes, seconds_rem = divmod(x, 60)
    if minutes >= 60:
        hours, minutes_rem = divmod(minutes, 60)
        return "%02d:%02d:%02d" % (hours, minutes_rem, seconds_rem)
    else:
        minutes, seconds_rem = divmod(x, 60)
        return "00:%02d:%02d" % (minutes, seconds_rem)

def getDatabaseItemCount(workspace):
    arcpy.env.workspace = workspace
    feature_classes = []
    for dirpath, dirnames, filenames in arcpy.da.Walk(workspace,datatype="Any",type="Any"):
        for filename in filenames:
            feature_classes.append(os.path.join(dirpath, filename))
    return feature_classes, len(feature_classes)

def replicateDatabase(dbConnection, targetGDB):
    startTime = time.time()

    featSDE,cntSDE = getDatabaseItemCount(dbConnection)
    featGDB,cntGDB = getDatabaseItemCount(targetGDB)

    now = datetime.now()
    logName = now.strftime("SDE_REPLICATE_SCRIPT_%Y-%m-%d_%H-%M-%S.log")
    log.basicConfig(datefmt='%m/%d/%Y %I:%M:%S %p', format='%(asctime)s %(message)s',\
    filename=logName,level=log.INFO)

    print "Old Target Geodatabase: %s -- Feature Count: %s" %(targetGDB, cntGDB)
    log.info("Old Target Geodatabase: %s -- Feature Count: %s" %(targetGDB, cntGDB))
    print "Geodatabase being copied: %s -- Feature Count: %s" %(dbConnection, cntSDE)
    log.info("Geodatabase being copied: %s -- Feature Count: %s" %(dbConnection, cntSDE))

    arcpy.env.workspace = dbConnection

    #deletes old targetGDB
    try:
        shutil.rmtree(targetGDB)
        print "Deleted Old %s" %(os.path.split(targetGDB)[-1])
        log.info("Deleted Old %s" %(os.path.split(targetGDB)[-1]))
    except Exception as e:
        print e
        log.info(e)

    #creates a new targetGDB
    GDB_Path, GDB_Name = os.path.split(targetGDB)
    print "Now Creating New %s" %(GDB_Name)
    log.info("Now Creating New %s" %(GDB_Name))
    arcpy.CreateFileGDB_management(GDB_Path, GDB_Name)

    datasetList = [arcpy.Describe(a).name for a in arcpy.ListDatasets()]
    featureClasses = [arcpy.Describe(a).name for a in arcpy.ListFeatureClasses()]
    tables = [arcpy.Describe(a).name for a in arcpy.ListTables()]

    #Compiles a list of the previous three lists to iterate over
    allDbData = datasetList + featureClasses + tables

    for sourcePath in allDbData:
        targetName = sourcePath.split('.')[-1]
        targetPath = os.path.join(targetGDB, targetName)
        if arcpy.Exists(targetPath)==False:
            try:
                print "Atempting to Copy %s to %s" %(targetName, targetPath)
                log.info("Atempting to Copy %s to %s" %(targetName, targetPath))
                arcpy.Copy_management(sourcePath, targetPath)
                print "Finished copying %s to %s" %(targetName, targetPath)
                log.info("Finished copying %s to %s" %(targetName, targetPath))
            except Exception as e:
                print "Unable to copy %s to %s" %(targetName, targetPath)
                print e
                log.info("Unable to copy %s to %s" %(targetName, targetPath))
                log.info(e)
        else:
            print "%s already exists....skipping....." %(targetName)
            log.info("%s already exists....skipping....." %(targetName))
    featGDB,cntGDB = getDatabaseItemCount(targetGDB)
    print "Completed replication of %s -- Feature Count: %s" %(dbConnection, cntGDB)
    log.info("Completed replication of %s -- Feature Count: %s" %(dbConnection, cntGDB))
    totalTime = (time.time() - startTime)
    totalTime = formatTime(totalTime)
    log.info("Script Run Time: %s" %(totalTime))

if __name__== "__main__":
    databaseConnection = r"YOUR_SDE_CONNECTION"
    targetGDB = "DESTINATION_PATH\\SDE_Replicated.gdb"
    replicateDatabase(databaseConnection, targetGDB)   

Miałem z tym naprawdę szczęście. Replikowałem bazę danych SDE na geobazę plików. Nie przeprowadziłem jednak zbyt wielu testów tego skryptu, ponieważ spełnił on wszystkie moje potrzeby. Przetestowałem to za pomocą ArcGIS 10.3. Należy również zauważyć, że rozmawiałem z kimś, kto użył tego skryptu, i napotkał problem z błędem podczas kopiowania niektórych zestawów danych z powodu niewłaściwych uprawnień i pustych tabel.

Lemur - dlaczego nie utworzyć relacji opartych na globalnym identyfikatorze zamiast identyfikatora obiektu? Że twoje relacje zostaną zachowane. Jeśli nie masz globalnego identyfikatora, bardzo go polecam.

-aktualizacja

Dodałem trochę więcej logiki do kodu, aby obsłużyć złe ścieżki połączenia z bazą danych oraz lepszą rejestrację i obsługę błędów:

import time, os, datetime, sys, logging, logging.handlers, shutil
import arcpy

########################## user defined functions ##############################

def getDatabaseItemCount(workspace):
    log = logging.getLogger("script_log")
    """returns the item count in provided database"""
    arcpy.env.workspace = workspace
    feature_classes = []
    log.info("Compiling a list of items in {0} and getting count.".format(workspace))
    for dirpath, dirnames, filenames in arcpy.da.Walk(workspace,datatype="Any",type="Any"):
        for filename in filenames:
            feature_classes.append(os.path.join(dirpath, filename))
    log.info("There are a total of {0} items in the database".format(len(feature_classes)))
    return feature_classes, len(feature_classes)

def replicateDatabase(dbConnection, targetGDB):
    log = logging.getLogger("script_log")
    startTime = time.time()

    if arcpy.Exists(dbConnection):
        featSDE,cntSDE = getDatabaseItemCount(dbConnection)
        log.info("Geodatabase being copied: %s -- Feature Count: %s" %(dbConnection, cntSDE))
        if arcpy.Exists(targetGDB):
            featGDB,cntGDB = getDatabaseItemCount(targetGDB)
            log.info("Old Target Geodatabase: %s -- Feature Count: %s" %(targetGDB, cntGDB))
            try:
                shutil.rmtree(targetGDB)
                log.info("Deleted Old %s" %(os.path.split(targetGDB)[-1]))
            except Exception as e:
                log.info(e)

        GDB_Path, GDB_Name = os.path.split(targetGDB)
        log.info("Now Creating New %s" %(GDB_Name))
        arcpy.CreateFileGDB_management(GDB_Path, GDB_Name)

        arcpy.env.workspace = dbConnection

        try:
            datasetList = [arcpy.Describe(a).name for a in arcpy.ListDatasets()]
        except Exception, e:
            datasetList = []
            log.info(e)
        try:
            featureClasses = [arcpy.Describe(a).name for a in arcpy.ListFeatureClasses()]
        except Exception, e:
            featureClasses = []
            log.info(e)
        try:
            tables = [arcpy.Describe(a).name for a in arcpy.ListTables()]
        except Exception, e:
            tables = []
            log.info(e)

        #Compiles a list of the previous three lists to iterate over
        allDbData = datasetList + featureClasses + tables

        for sourcePath in allDbData:
            targetName = sourcePath.split('.')[-1]
            targetPath = os.path.join(targetGDB, targetName)
            if not arcpy.Exists(targetPath):
                try:
                    log.info("Atempting to Copy %s to %s" %(targetName, targetPath))
                    arcpy.Copy_management(sourcePath, targetPath)
                    log.info("Finished copying %s to %s" %(targetName, targetPath))
                except Exception as e:
                    log.info("Unable to copy %s to %s" %(targetName, targetPath))
                    log.info(e)
            else:
                log.info("%s already exists....skipping....." %(targetName))

        featGDB,cntGDB = getDatabaseItemCount(targetGDB)
        log.info("Completed replication of %s -- Feature Count: %s" %(dbConnection, cntGDB))

    else:
        log.info("{0} does not exist or is not supported! \
        Please check the database path and try again.".format(dbConnection))

#####################################################################################

def formatTime(x):
    minutes, seconds_rem = divmod(x, 60)
    if minutes >= 60:
        hours, minutes_rem = divmod(minutes, 60)
        return "%02d:%02d:%02d" % (hours, minutes_rem, seconds_rem)
    else:
        minutes, seconds_rem = divmod(x, 60)
        return "00:%02d:%02d" % (minutes, seconds_rem)

if __name__ == "__main__":
    startTime = time.time()
    now = datetime.datetime.now()

    ############################### user variables #################################
    '''change these variables to the location of the database being copied, the target 
    database location and where you want the log to be stored'''

    logPath = ""
    databaseConnection = "path_to_sde_or_gdb_database"
    targetGDB = "apth_to_replicated_gdb\\Replicated.gdb"

    ############################### logging items ###################################
    # Make a global logging object.
    logName = os.path.join(logPath,(now.strftime("%Y-%m-%d_%H-%M.log")))

    log = logging.getLogger("script_log")
    log.setLevel(logging.INFO)

    h1 = logging.FileHandler(logName)
    h2 = logging.StreamHandler()

    f = logging.Formatter("[%(levelname)s] [%(asctime)s] [%(lineno)d] - %(message)s",'%m/%d/%Y %I:%M:%S %p')

    h1.setFormatter(f)
    h2.setFormatter(f)

    h1.setLevel(logging.INFO)
    h2.setLevel(logging.INFO)

    log.addHandler(h1)
    log.addHandler(h2)

    log.info('Script: {0}'.format(os.path.basename(sys.argv[0])))

    try:
        ########################## function calls ######################################

        replicateDatabase(databaseConnection, targetGDB)

        ################################################################################
    except Exception, e:
        log.exception(e)

    totalTime = formatTime((time.time() - startTime))
    log.info('--------------------------------------------------')
    log.info("Script Completed After: {0}".format(totalTime))
    log.info('--------------------------------------------------')
PMK
źródło
Peter, użyłem skryptu, który podałeś, i zaktualizowałem 2 zmienne na dole. Otrzymuję błąd Traceback (ostatnie ostatnie połączenie): Plik „ServerPath \\ CopySDEtoGDB.py”, wiersz 90, w <module> replicateDatabase (databaseConnection, targetGDB) Plik „ServerPath \\ CopySDEtoGDB.py”, wiersz 55, w replicateDatabase datasetList = [arcpy.Describe (a) .name for in arcpy.ListDatasets ()] TypeError: Obiekt „NoneType” nie jest iterowalny. Jakiś pomysł, co to oznacza?
Courtney
Courtney - Wygląda na to, że w ścieżce do zmiennej połączenia z bazą danych występuje literówka lub niewielki błąd. Zgłasza błąd, ponieważ próbuje iterować listę pustą w wierszu 55. Udało mi się odtworzyć błąd, który wystąpił, używając niepoprawnej ścieżki w zmiennej „databaseConnection”. jaka jest rzeczywista ścieżka użyta w zmiennej?
PMK,
Gdybym chciał to robić co noc, czy nie zastąpi to istniejących funkcji? Nie chcę tworzyć nowego FGD za każdym razem, gdy chcę tylko zastąpić istniejący cel.
NULL.Dude
Peter, jeśli istnieje docelowy GDB, skrypt się nie powiedzie
NULL. Koleś
2

Użyłem skryptu podobnego do powyższego Petera i miałem szczęście, chociaż jego jest lepszy. Jedną rzeczą, na którą należy zwrócić uwagę, że ktoś może się potknąć, jest to, że jeśli używasz 64-bitowego geoprzetwarzania Pythona i masz ArcFM załadowany na ESRI, to zawiedzie we wszystkich funkcjach, które zostały skonfigurowane do używania ArcFM lub Projektanta z ERROR 000260. ponieważ musisz użyć 32-bitowego Pythona, inaczej ArcFM nie będzie poprawnie licencjonowany.

Bardziej szczegółowy opis korzystania z 32-bitowego ArcPy znajduje się w dwóch pierwszych komentarzach do tego wątku w programie Exchange

https://infrastructurecommunity.schneider-electric.com/docs/DOC-2563

rrankman
źródło
Jeśli te dwa komentarze dostarczają odpowiedzi na to pytanie, ich treść powinna być cytowana lub streszczona tutaj, a nie tylko linkowana - patrz meta.stackexchange.com/questions/225370/... Jeśli już dostarczyłeś streszczenie, być może po prostu zmień „Sprawdź pierwsze dwa komentarze do tego wątku w Exchange "do" Aby uzyskać bardziej szczegółowy opis, zobacz dwa pierwsze komentarze do tego wątku w Exchange ".
PolyGeo
0

Jeśli masz uprawnienia administratora, możesz użyć prostych poleceń kopiowania i wklejania, aby eksportować lub importować sde do pliku geo-bazy danych na odwrót i zajrzyj tutaj, aby uzyskać więcej informacji.

Ganeshnarim
źródło
Dzięki Ganeshnarim - chciałbym zautomatyzować ten proces za pomocą Pythona, aby jakikolwiek ręczny proces kopiowania / wklejania w ArcCatalog nie spełniał moich potrzeb. Miałem również ograniczony sukces z tą metodą, jak się wydaje (w ArcMap 10.1) kopiowanie SDE po prostu tworzy inny link do tej samej bazy danych (podczas gdy gdyby ta sama technika została zastosowana w pliku lub osobistej geobazie, kopia byłaby wykonana)
Krausers,