Merge / FeatureClasses in verschieden GDBs und Ordnern

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Galunder13
User
Beiträge: 8
Registriert: Dienstag 21. Oktober 2014, 10:50

Guten Tag,

ich habe folgendes Problem, welches sich zunächst simpel anhört aber worauf ich keine Lösung finde, kurze Vorgeschichte, bisher habe ich nur JAVA programmiert und in den letzten Wochen habe ich mir Python selbst beigebracht:

ich soll mehrere FeatureClasses, welche sich in unterschiedlichen Ordnern und GDBs befinden automatisch in eine GDB zusammenführen per Knopfdruck.

Bisher habe ich versucht die Dateipfade automatisiert zusammenzustellen, da sich bei denen jeweils einzelne Pfade nur "Keywords" unterscheiden

Der allgemeine Dateipfad lautet ungefähr so:
C:\Hauptordner\Unterordner_KeyWort\Unterordner2_KeyWort\GDB\FeatureDataSet\FeatureClass

1. Lösungsansatz

Ich habe angefangen mit:

liste_ordner = arcpy.os.listdir(r"C:\Hauptordner")

Und mir dann mit Schritt für Schritt den Dateipfad für jede einzelne FeatureClass zusammengestellt.
Das hat dann auch geklappt ich habe die Dateipfade in eine String-Liste zusammengestellt. Welche ich dann im Merge-Befehl einsetzen wollte, wobei dann jeweils die Fehlermeldung kam Datei nicht bekannt oder nicht vorhanden.

Dazu denke ich ob man evtl. den Datentyp der Liste so ändern kann das er in den Mergebefehl als Input passt

2. Lösungsansatz

Ich habe versucht mit dem Befehl arcpy.ListFeatureClasses() versucht die FeatureClasses direkt zu finden. Wobei ich bisher nicht geschafft habe die einzelne Feature-Datei auszufiltern oder bzw. das andere Problem ist das sich die Liste mit None-Werten zugemüllt wird


Den Code werde ich gleich einsetzen sobald ich ihn mal bisschen aufgeräumt habe durch das Try-Error-Testen ist er etwas unübersichtlich geworden. Ich hoffe jemand kann jetzt schon hilfreiche Lösungsansätze für mein Problem bieten.


Gruß Galunder13
Galunder13
User
Beiträge: 8
Registriert: Dienstag 21. Oktober 2014, 10:50

So sieht der komplette Code mit beiden Ansätzen aus.

Code: Alles auswählen

import arcpy

#Grundlage
liste_ordner = arcpy.os.listdir(r"PFAD")

str(liste_ordner)

liste_zwsch = []
x = 0
while x < len(liste_ordner):
    liste_zwsch.insert(1,(liste_ordner[x][(len(liste_ordner[x])-4):(len(liste_ordner[x]))]))
    x += 1


#Loesungsansatz Nummer 1
x = 0
while x < len(liste_ordner): 
    liste_ordner[x] = liste_ordner[x] +"\\" + "AB_" + liste_ordner[x] + ".gdb\\"
    x += 1



x = 0
fcList = []
while x < len(liste_ordner):
    arcpy.env.workspace = "PFAD" + liste_ordner[x] + "\\"
    fcList.insert(1,[fc for fc in arcpy.ListDatasets() if not fc.startswith('Refe')])
    x += 1



liste_kuerzel = []
x = 0
var =""
while x < len(fcList):
    var = var + str(fcList[x])
    var = var[7:len(var)-2]
    liste_kuerzel.insert(1,var)
    x += 1
    var = ""


x = 0
var = ""
liste_pfad = []
while x < len(fcList):
    var = ""
    var = "r\"PFAD" + liste_gdb[x] + "\\"
    var = var + "F_" + liste_kuerzel[x] + "\\"
    var = var + "F0_" + liste_kuerzel[x] + "_w\""
    liste_pfad.insert(1,var)
    x+=1



x = 0
listeString = ""
while x < len(liste_pfad):
    listeString = listeString + " , " + listeString[x]
    x += 1
listeString = listeString[3:]



#Loesungansatz 2
featList = []
x = 0
y = 0
while x < len(liste_ordner):
    while y < len(liste_ordner):
        arcpy.env.workspace = "K:\PFAD" + liste_ordner[x] + "\\" + str(fcList[y])[3:14]
        featList.insert(1 ,str(arcpy.ListFeatureClasses()))
        y +=1
        arcpy.AddMessage("Y: " + str(y))
    x += 1
    y = 0
    arcpy.AddMessage("X: " + str(x))
#ENDE 2
BlackJack

@Galunder13: Ich würde erst einmal unabhängig vom konkreten Problem empfehlen ein Grundlagentutorial durchzuarbeiten. Es gibt ``for``-Schleifen um Himmels willen.

Bei der Namensgebung sollte man weder Datentypen in die Namen aufnehmen noch irgendwelche kryptischen Abkürzungen verwenden. Also so etwas wie `liste_zwsch` ist gar nicht gut.

Was soll Zeile 6 bewirken?

Und die Schleife danach? Das `insert()` macht keinen Sinn und ist ineffizient nachdem mehr als ein Element in der Liste ist. Was da mit den Dateinamen gemacht wird erscheint mir sehr wirr. Man muss schon zweimal hinschauen um zu sehen das da auf sehr umständliche Art eine Liste erstellt wird welche die Dateinamen aus `liste_ordner` ohne die jeweils letzten vier Zeichen erstellt wird. Mal abgesehen davon das man die Länge von der Zeichenkette überhaupt nicht benötigt, wird die auch maximal umständlich zweimal ermittelt. An der Stelle wäre es auch sicherer `os.splitext()` zu verwenden, falls dort Dateinamenserweiterungen entfernt werden sollen. Und eventuell möchte man die Erweiterung auch prüfen oder filtern.

Pfadteile setzt man mit `os.path.join()` zusammen und nicht mit ``+``.

Bei der Schleife zu `liste_kuerzel` ist das Initialisieren von `var` mit einer leeren Zeichenkette vor und am Ende der Schleife ungünstig gelöst. Mach das *einmal* in der Schleife bevor es benutzt wird. Und dann wirst Du feststellen das es gar nicht benutzt werden muss und die erste Zeile in der Schleife total unsinnig ist. Wo kommt beim Slicen die magische 7 her? Und auch hier braucht man für den Endpunkt die Länge nicht extra ermitteln. Wobei die magische 2 wohl auch einer Erklärung bedarf. Dort Listen in mit `str()` in Zeichenketten umwandeln sieht auch komisch aus. Warum will man so etwas machen? Eine Liste mit Zeichenkettendarstellungen von Listen? Und da wird dann auch noch etwas heraus gesliced? Das sieht kaputt aus. Auf der Zeichenkettendarstellung von Containerobjekten zu operieren statt mit der Datestruktur selbst ist ein „code smell”. Genauso das Erzeugen von Zeichenketten die Python-Zeichenkettenliterale enthalten. Warum erzeugst Du Quelltext?

Beim Zusammensetzen von Zeichenketten und Werten sollte man auch Zeichenkettenformatierung mittels `format()`-Methode einen zusammenstückeln mit ``+`` vorziehen.

Die letzte Schleife vor dem ``# Lösungsansatz 2``-Kommentar ist umständlich und ineffizient die `.join()`-Methode nachprogrammiert.
Galunder13
User
Beiträge: 8
Registriert: Dienstag 21. Oktober 2014, 10:50

ich glaube es wäre hilfreich wen ich noch sage, dass das Skript noch in ArcMap von ESRI eingebunden werden soll
Es gibt ``for``-Schleifen um Himmels willen.
ich weis eigentlich habe gerade nicht so drüber nachgedacht...zuerst sollte die Funktion für mich stimmen :) , haette es evtl im nachhinein geändert
Also so etwas wie `liste_zwsch` ist gar nicht gut.
ist ganz drausen war nur am anfang für einen Zwischenschritt
Was soll Zeile 6 bewirken?
Das war einfach nur ein Versuch das alles laufen zu bringen :), ist mittlerweile auch gelöscht
Was da mit den Dateinamen gemacht wird erscheint mir sehr wirr
Nunja die Datenstruktur liegt momentan so vor, daran kann ich nichts ändern , zudem soll das Skript ja dann vollautomatisch laufen. Und das was ich bisher gemacht hab hat dazu geführt (wen auch auf umständliche und ineffiziente weise) das ich eine Liste aller Dateipfade habe zu den ganzen FeatureClasses welche ich in die MergeFunktion einbauen möchte

Code: Alles auswählen

arcpy.Merge_management([liste_pfade], mergeZiell)
Nun kommt dabei aber das die Eingabe fehlerhaft ist, wobei wen ich

Code: Alles auswählen

arcpy.Merge_management([liste_pfade[0], liste_pfade[1]] , mergeZiell)
eingebe funktionierts.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Galunder13 hat geschrieben:ich weis eigentlich habe gerade nicht so drüber nachgedacht...zuerst sollte die Funktion für mich stimmen , haette es evtl im nachhinein geändert
wenn man die Wahl hat, etwas umständlich, unverständlich und wirr mit einer while-Schleife zu schreiben, oder eine for-Schleife zu nehmen, warum sollte man sich dann für das erstere entscheiden?

Lass Dir mal `[liste_pfade]` und `[liste_pfade[0], liste_pfade[1]]` ausgeben und suche den Unterschied.
Galunder13
User
Beiträge: 8
Registriert: Dienstag 21. Oktober 2014, 10:50

Weil erstes für den Ersteller gar nicht so umständlich, unverständlich und wirr ist :D ( war schon immer ein Problem von mir, das hat den Mathe-Lehrer immer gefreut )

Nun ja der Unterschied ist das bei [liste_pfade] die [] am Anfang/Ende doppelt vorkommen

Das Problem ist dann wen ich in die Funktion nur listen_pfade als Eingabe eintippe, nimmt die Funktion nur einen Parameter
Galunder13
User
Beiträge: 8
Registriert: Dienstag 21. Oktober 2014, 10:50

Code: Alles auswählen

import arcpy
import os



arcpy.env.overwriteOutput = True
#Grundlage
#Liste aller Unterordner
liste_ordner = arcpy.os.listdir(r"PFAD")


#Liste der Dateipfade mit der GDB erweitert
x = 0
for i in liste_ordner:
    liste_ordner[x] = os.path.join(r"PFAD" ,i, 'AROK_' + i + '.gdb')
    x += 1




#Liste der Dateipfade mit der Feature_Dataset erweitert
x = 0
fcList = []
while x < len(liste_ordner):
    arcpy.env.workspace = liste_ordner[x]
    fcList.insert(1,[fc for fc in arcpy.ListDatasets() if not fc.startswith('Refe')])
    x += 1

#Kürzel aus der Dataset herausgeschnitten um nächsten Pfad_Part zu generieren
liste_kuerzel = []
for i in fcList:
    var =""
    var = var + str(i)
    var = var[7:len(var)-2]
    liste_kuerzel.insert(1,var)

#Dateipfad vollendet
x = 0
for i in liste_ordner:
    liste_ordner[x] = os.path.join( i , 'FNP_' + liste_kuerzel[x] , 'fnp0_' + liste_kuerzel[x] + '_w')
    x += 1

#Merge Starten
arcpy.AddMessage("MERGE STARTET")
mergeZiel = r"PFAD"
arcpy.Merge_management(liste_ordner , mergeZiel)
*EDIT
Habe den Code umgeschrieben. Danke für die Tipps bisher. Nun besteht das Problem NICHT mehr danke :D .
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@Galunder13: statt die Listenelemente zu verändern, wird in Python üblicherweise einfach eine neue Liste erzeugt. Also:

Code: Alles auswählen

ordner_komplett = []
for ordner in liste_ordner:
    ordner_komplett.apend(os.path.join(r"PFAD" , ordner, 'AROK_%s.gdb' % ordner)
Dabei ist `i` ein ungünstiger Schleifenvariablenname, weil er nichts sagt und üblicherweise für ganze Zahlen statt für Dateinamen steht.
Die while-Schleife in Zeile 24 schreit auch nach einer for-Schleife.
`var`in der Schleife ab Zeile 31 ist völlig überflüssig, da nur `i` (wieder so ein Name) umständlich hineinkopiert wird

Code: Alles auswählen

kuerzel = []
for fc in fcList:
    kuerzel.append(fc[7:-2])
oder mit List-Comprehension:

Code: Alles auswählen

kuerzel = [fc[7:-2] for fc in fcList]
wobei BlackJack schon die Magischen Zahlen moniert hatte. Statt `fc`wäre auch etwas sprechenderes besser.

Deine Fehlerbeschreibung von Merge_management hört sich sehr seltsam an, denn warum sollte ein Merge von einer Liste nur 1 Element nehmen. Dazu noch, wenn man die falsche Semantik benutzt genau das letzte? Hast Du schon probiert, die Liste liste_ordner auszugeben und von Hand den Merge durchzuführen?
Galunder13
User
Beiträge: 8
Registriert: Dienstag 21. Oktober 2014, 10:50

Also habs bereits umgebaut und es läuft jetzt auch, war nochn Tippfehler...(bin momentan dabei es so umzubauen das Alles mitgenommen wird, das war nur ein Teilbereich)

die 7:-2 entsteht einfach daraus das ich ein Kürzel welches ca. so aussieht ABC1990 benötige... und da ich es automatisch auslesen muss scheint mir der einzige weg: den Dateinamen auszulesen dessen Name das Kürzel enthält und zurecht zuschneiden(geht auch nur da die Dateien immer den gleichen Namensaufbau haben. und auch haben werden)

und das i kommt noch von Java, das werde ich bestimmt auch bald abstellen :)
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@Galunder13: auch in Java dürfen Variablennamen mehr als einen Buchstaben haben. Und statt magischer Indizes könnte man Patternmatching benutzen.
BlackJack

@Galunder13: Wozu die magischen Indexe gut sind muss im Quelltext stehen. Das versteht doch sonst niemand, inklusive Dir selbst wenn Du da nach ein paar Wochen noch mal drauf schaust. Und was mich daran immer noch fertig macht ist dass diese Operation auf der Zeichenkettendarstellung einer Liste gemacht wird. Das ist absolut gruselig. An der Stelle frage ich mich auch ob da überhaupt das heraus kommt was Du da erwartest. Wenn da wirklich 'ABC1990' heraus kommt, dann muss die Liste ja irgendwie so aussehen ``['xxxxxABC1990']``, da kann also eigentlich höchstens *ein* Name drin sein. Für *einen* Namen eine Liste zu verwenden macht aber irgendwie keinen Sinn. Das ist sehr verwirrend und sehr wahrscheinlich auch sehr Fehleranfällig wenn da irgendwo mal etwas vorkommt womit Du nicht rechnest.

Auch in Java ist `i` als Name für irgend etwas anderes als ganze Zahlen etwas was in den meisten Programmierern Gewaltfantasien hervorruft. Ich würde mal sagen das ist in fast jeder prozeduralen oder an C-Syntax angelehnten Programmiersprache so. Letztendlich ist das eine Konvention die älter als Programmiersprachen ist, weil `i`, `j`, und `k` auch vor elektronischen Rechnern schon die klassischen Laufvariablen aus den natürlichen Zahlen waren.

`insert()` ist immer noch die falsche Methode um Elemente *anzuhängen*.

Und die Organisation der Daten in ”paralellen” Listen ist unübersichtlich und fehleranfällig. Zusammengehörende Daten sollte man auch in einem Wert zusammen fassen. Zum Beispiel in Tupeln, oder einem Typ der mit `collections.namedtuple()` erstellt wurde.
Antworten