GIS Shape Datein Ordner und Unterordner auslesen

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.
Karamell
User
Beiträge: 14
Registriert: Montag 19. November 2018, 09:53

Hallo zusammen,
sry, ich bin absoluter Neuling was Programmieren angeht...
ich versuche, die Koordinatensysteme (spatial reference) von Shapedatein in einem Ordner, bzw auch Unterordnern auszulesen.
Wenn ich eine Datei direkt anspreche funktioniert das.
Diese spreche ich wie folgt an:

Code: Alles auswählen

 indata = r"C:\Test\050307_bohransatz_GK.shp" 

Ohne ".shp" kommt die gleiche Fehlermeldung, wie wenn ich einen Ordner anspreche:

"Traceback (most recent call last):
File "Y:\Koordinatenumstellung_Test\Skripte_Toolboxes\KoordinatensystemPruefen.py", line 12, in <module>
sr = dsc.spatialReference
AttributeError: DescribeData: Method spatialReference does not exist"

Mein Code für das Ansprechen eines Ordners:

Code: Alles auswählen

# Import modules
import arcpy
import sys
import traceback
import os

# Set local variables
prj = "" 
indata = r"C:\Test" 
dsc = arcpy.Describe(indata) 
sr = dsc.spatialReference 
prj = sr.name.lower()
Ich habe "indata" auch schon durch "a" ersetzt. was das gleiche Ergebnis zur Folge hatte.
Hat jmd. einen Tip?
Grüße Karamell
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich weiss nicht, was du mit "durch a ersetzt" meinst. Aber allgemein kannst du nicht einfach irgendwo ein Verzeichnis einkippen, wo eine Datei erwartet wird. Was soll denn dabei dann rumkommen? Du hast da X Dateien drin, die alle unterschiedliche spatial references haben koennen. Da kann doch ein API call, der davon *EINE* zurueck gibt, schon rein logisch betrachtet dein Problem nicht loesen.

Was du tun musst ist dir das os und os.path Modul anschauen, und dann mit der os.walk-Funktion ueber dein Verzeichnis laufen, shp-Files identifizieren, deren spatial reference holen & die natuerlich in einer Liste oder einem Woerterbuch mit dem Schluessel des Dateinamens speichern.
Karamell
User
Beiträge: 14
Registriert: Montag 19. November 2018, 09:53

Das Ersetzen mit "a" habe ich von hier: viewtopic.php?t=16844

Ich habe in mein Skript eine if else Funktion eingebaut und würde das ganze in den Modelbuilder von Arcmap einbinden. Der Modelbuilder verarbeitet die Shapes ohne spatial reference direkt weiter (so meine Hoffnung).
Benutzeravatar
sls
User
Beiträge: 480
Registriert: Mittwoch 13. Mai 2015, 23:52
Wohnort: Country country = new Zealand();

@Karamell: in `a` wird dort aber mittels listdir() alle Inhalte des Ordners hinterlegt, du übergibst der Methode aber nur den Pfad zum Ordner, ohne über die Inhalte zu iterieren. Das funktioniert so nicht. __deets__' schrieb ja schon, wie man das so macht. Hoffen würde ich nicht, sondern einfach eine for-schleife verwenden. Man kann so jedes Shape der Datei hinzufügen, in Arc(gis) werden die einzelnen Layer dann in einer Datei überlappt.
When we say computer, we mean the electronic computer.
Karamell
User
Beiträge: 14
Registriert: Montag 19. November 2018, 09:53

Danke schonmal für die Tips
Nach langer Suche und ausprobieren komme ich leider immer noch nicht weiter.
Ich habe neben os auch os.path importiert. Und nach einem os.walk Beispiel (u.a.) folgenden Codeanfang:

Code: Alles auswählen

for root, dirs, files in os.walk('C:/Test', topdown=True):
   #Proceed all files:
      for file_name in files:
         proceed_file(os.path.join(root, file_name))
hier bekomme ich die Fehlermeldung:

NameError: name 'proceed_file' is not defined

Ich komme mit dem walk einfach nicht weiter.
Benutzeravatar
sls
User
Beiträge: 480
Registriert: Mittwoch 13. Mai 2015, 23:52
Wohnort: Country country = new Zealand();

Die Fehlermeldung sagt, dass Python die Funktion `proceed_file` in deinem Modul nicht finden kann. Wo soll die Funktion herkommen? Die muss schon definiert sein.
When we say computer, we mean the electronic computer.
Karamell
User
Beiträge: 14
Registriert: Montag 19. November 2018, 09:53

Wenn ich den Pfad, Dateinamen und Endung in Python liste, wie kann ich dann eine if else Funktion über die Liste laufen lassen?
Benutzeravatar
sls
User
Beiträge: 480
Registriert: Mittwoch 13. Mai 2015, 23:52
Wohnort: Country country = new Zealand();

Du kannst die if-else-Abfrage einfach in deine For-Loop einbauen.

Pseudo-Code:

Code: Alles auswählen

for file in path:
    if file.endswith('.shp'):
    	# do this
    else:
    	# do that
Falls du jetzt meinst, dass du eine Funktion aufrufen möchtest, in der eine Bedingung geprüft wird, ja, dann musst du halt die Funktion in der Schleife aufrufen.
When we say computer, we mean the electronic computer.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hier könnte man auch die pathlib nehmen:

Code: Alles auswählen

from pathlib import Path
filenames = Path(r'C:\Test').rglob('*.shp')
Damit sucht er nach allen *.shp-Dateien in C:\Test und allen Unterordnern. Und dieses Ergebnis frühstückt man dann - wie schon angeregt wurde - in einer Schleife ab.
Karamell
User
Beiträge: 14
Registriert: Montag 19. November 2018, 09:53

Diesen Code habe ich, um bei einem einzelnen Shape auszugeben, ob das Koordinatensystem bekannt ist oder nicht:

Code: Alles auswählen

# Set local variables
prj = "" 
indata = r'C:/Test/050307_bohransatz_GK.shp'
dsc = arcpy.Describe(indata) 
sr = dsc.spatialReference 
prj = sr.name.lower()

try:
 
   # check if indata has spatial reference or not
   if prj.find("unknown") > -1:
       # Set the Is Unknown parameter to TRUE
       arcpy.SetParameterAsText(1,"true") #The first parameter refers to the "Is Unknown" variable
      
       print("Coordinate system unknown") 


   else:
       # Set the Is Unknown parameter to FALSE
       arcpy.SetParameterAsText(1,"false") 
     
       print("Coordinate system is known") 

except Exception as e:
    AddPrintMessage(e[0], 2)
Wenn ich nun mit os.walk über Ordner (und Subordner) iterieren möchte, komme ich nicht weiter. Ich bekomme eine Fehlermeldung bei der if- Zeile: "unindent does not match any outer indentation level." Das ist mein Code dazu:

Code: Alles auswählen

# Set local variables
prj = "" 
indata = r'C:/Test'
dsc = arcpy.Describe(indata) 
sr = dsc.spatialReference 
prj = sr.name.lower()

# Iteration mit Ausgabe Liste
for root, dirs, files in os.walk('C:/Test', topdown=True):
   #Proceed all files:
      for file_name in files:
         print(os.path.join(root, file_name))         
   

      for file in 'path':
 
   # check if indata has spatial reference or not
   if prj.find("unknown") > -1:
       # Set the Is Unknown parameter to TRUE
       arcpy.SetParameterAsText(1,"true") #The first parameter refers to the "Is Unknown" variable
      
       print("Coordinate system unknown") 


   else:
       # Set the Is Unknown parameter to FALSE
       arcpy.SetParameterAsText(1,"false") 
     
       print("Coordinate system is known") 

except Exception as e:
    AddPrintMessage(e[0], 2)
Ich hab einfach keine Ahnung, wie ich das zusammen bekommen soll. :cry:
Karamell
User
Beiträge: 14
Registriert: Montag 19. November 2018, 09:53

Mit Pathlib

Code: Alles auswählen

from pathlib import Path
filenames = Path(r'C:\Test').rglob('*.shp')      
   
        for file in 'path':
bekomme ich vor dem "for" eine Syntafehler: "unexpected indent"
Benutzeravatar
sls
User
Beiträge: 480
Registriert: Mittwoch 13. Mai 2015, 23:52
Wohnort: Country country = new Zealand();

Ich würde dir empfehlen, mal ein Grundlagen-Tutorial durch zu arbeiten. Das sind alles recht triviale Fehlermeldungen. Einrückungen sind in Python wichtig. Im ersten Beispiel ist die if-Bedingung nicht *innerhalb* des For-Schleifenkörpers, ergo *denkt* Python "das gehört nicht zusammen", somit wäre deine For-Schleife allerdings unvollständig. Nicht günstig.

Im zweiten Beispiel ist eine Einrückung hingegen nicht notwendig, die for-loop gehört an den Anfang der Zeile da sie ja nicht *innerhalb* einer Bedingung, Funktion, Schleife usw. steht.
When we say computer, we mean the electronic computer.
Karamell
User
Beiträge: 14
Registriert: Montag 19. November 2018, 09:53

sls, ich dachte, ich bekomme das mit den Beispielen, die mir esri gibt hin, wenn ich die etwas modifiziere. Mittlerweile wäre es wahrscheinlich schneller gegangen von vorne anzufangen. Wozu iwie nicht die Zeit ist.

Ich werde weiter probieren. Danke schonmal an alle für die Ratschläge.
Benutzeravatar
sls
User
Beiträge: 480
Registriert: Mittwoch 13. Mai 2015, 23:52
Wohnort: Country country = new Zealand();

@Karamell: ich habe mal eine Zeit lang mit QuantumGIS und Arcgis gearbeitet, dafür aber nie irgendwelche Skripte erstellt. Noch einige Anmerkungen zu deinen Beispielen:

"Set local variables" ist nicht richtig, da diese auf Modulebene deklariert wurden. Daher haben sie einen globalen Zustand und sind "von Überall" aus erreichbar. Auf Modulebene sollten nur Konstanten, Klassen oder Funktionen definiert sein. `prj` ist kein guter Name für einen Bezeichner, besser ist es anderen Programmierern die Ratespiele zu ersparen und einfach "project_name" zu schreiben, außerdem ist es sinnlos der Variablen zunächst einen leeren String zu zuweisen, um anschließend einen völlig anderen Wert zu übergeben, lass `prj = ""` einfach weg, das wird doch sowieso später überschrieben.

Man sollte niemals nackte Exceptions fangen, sondern nur solche Fehler die tatsächlich auftreten, zumal du hier bei jedem erdenklichen Fehlerfall eine bestimmte Anweisung durchführst. Um dich noch mit deinem Einrückungsproblem zu erlösen:

Code: Alles auswählen

for file in 'path':
   # check if indata has spatial reference or not
   if prj.find("unknown") > -1:
       # Set the Is Unknown parameter to TRUE
       arcpy.SetParameterAsText(1,"true") #The first parameter refers to the "Is Unknown" variable
       print("Coordinate system unknown") 
Der `if`-Block befindet sich jetzt im Schleifenkörper, der Einrückungsfehler verschwindet.

EDIT: dafür taucht dann ein anderer Fehler auf, weil du mit `file` in der Schleife nichts mehr anstellst. Was soll hier `prj.find()` bewirken?, `file` wird übrigens über einen String `path` iterieren, ergo wird in jedem Schleifendurchlauf erst p, dann a, dann t, dann h in `file` hinterlegt sein. Ich rate noch dringender, ein Tutorial durch zu ackern, andernfalls könnten ziemlich hässliche Dinge passieren, die du dir niemals herleiten kannst wenn du mit Koordinaten in einem GIS herumhantierst.
When we say computer, we mean the electronic computer.
Karamell
User
Beiträge: 14
Registriert: Montag 19. November 2018, 09:53

@sls auf der ArcGIS Seite ist fast genau das aufgeführt, was ich brauche: http://desktop.arcgis.com/de/arcmap/10. ... nching.htm
Außer dass ich eben über Ordner iterieren muss und mir nur die Ausgabe hat spatial referenz oder eben nicht wichtig ist.
Daher stammt das Skript. Wie gesagt, ich dachte, es wäre einfacher, das auf meine Bedürfnisse umzuwandeln.
Ich danke Dir für Deine Geduld! Und schaue mir das morgen nochmal näher an.
Benutzeravatar
sls
User
Beiträge: 480
Registriert: Mittwoch 13. Mai 2015, 23:52
Wohnort: Country country = new Zealand();

@Karamell:

anbei eine Möglichkeit, wie man das implementieren könnte:

(ungetestet)

Code: Alles auswählen

import arcpy
from pathlib import Path

FILENAMES = Path(r'C:\Test').rglob('*.shp')


def set_coordinate_status(message, *args):
    try:
        for status in args:
            arcpy.SetParameterAsText(status[0], status[1])
        arcpy.AddMessage(message)
    except SinnvolleArcpyException:
        arcpy.AddPrintMessage("Irgendwas sinnvolles")


def resolve_shape(path):
    try:
    	dsc = arcpy.Describe(path)
    	sr = dsc.spatialReference
    	return sr.name.lower()
    except SinnvolleArcpyException:
        arcpy.AddPrintMessage("Irgendwas sinnvolles")

def main():
    for shape in FILENAMES:
        prj = resolve_shape(shape)

        if prj.find("_stateplane_") > -1:
            message = "Coordinate system is StatePlane"
            set_coordinate_status(
                message, ((1, 'false'), (2, 'true'))
            )
        elif prj == "unknown":
            message = "To continue, first define a coordinate system!"
            set_coordinate_status(
                message, ((1, 'true'), (2, 'false'))
            )
        else:
            message = "Coordinate system is not StatePlane or Unknown"
            set_coordinate_status(
                message, ((1, 'false', 2, 'false'))
            )


if __name__ == '__main__':
    main()
Ich würde dazu auch die Doku von Arcpy konsultieren und die Exceptions durch etwas sinnvolles ersetzen (also Fehler, welche man erwarten könnte, nicht einfach pauschal alles abfangen. Ich habe das mal hier offengelassen, weil ich keine Lust habe Cookies von Arcpy's Dokuseite zu akzeptieren)

So kannst du beliebig viele Shapes ohne großes Zutun verarbeiten und reichst basierend auf den Ergebnissen was auch immer in `prj` so *drin* ist die notwendigen Parameter an eine Funktion die dann die Status setzt.

EDIT: noch als Ergänzung zu dem Beispiel in der Doku. Das Beispiel ist nicht gut, da es massiv gegen DRY verstößt (Don't repeat yourself), vorallem weil hier halt nichts generisch aufgebaut ist. Die Wartung von solchem Code wird zur Hölle.
When we say computer, we mean the electronic computer.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Statt `prj.find("_stateplane_") > -1` nimmt man `in`. Da `set_coordinate_status` in allen if-Blöcken vorkommt, kann man diesen Aufruf mit zusätzlichen Variablen nach hinten schieben, die Bedeutung von true und false ist mir aber nicht bekannt, so dass mir keine guten Namen dafür einfallen.
Benutzeravatar
sls
User
Beiträge: 480
Registriert: Mittwoch 13. Mai 2015, 23:52
Wohnort: Country country = new Zealand();

@Sirius3: danke für die Hinweise, ich hab's angepasst. Was `true` oder `false` hier macht und wie das für arcpy relevant ist; kein blassen.

Code: Alles auswählen

import arcpy
from pathlib import Path

FILENAMES = Path(r'C:\Test').rglob('*.shp')


def set_coordinate_status(message, *args):
    try:
        for status in args:
            arcpy.SetParameterAsText(status[0], status[1])
        arcpy.AddMessage(message)
    except SinnvolleArcpyException:
        arcpy.AddPrintMessage("Irgendwas sinnvolles")


def resolve_shape(path):
    try:
    	describe = arcpy.Describe(path)
    	spatial_reference = dsc.spatialReference
    	return spatial_reference.name.lower()
    except SinnvolleArcpyException:
        arcpy.AddPrintMessage("Irgendwas sinnvolles")


def main():
    for shape in FILENAMES:
        prj = resolve_shape(shape)

        if "_stateplane_" in prj:
            message = "Coordinate system is StatePlane"
            statuses =  ((1, 'false'), (2, 'true'))
            )
        elif prj == "unknown":
            message = "To continue, first define a coordinate system!"
            statuses = ((1, 'true'), (2, 'false'))
            )
        else:
            message = "Coordinate system is not StatePlane or Unknown"
            statuses = ((1, 'false', 2, 'false'))
            )
        set_coordinate_status(message, statuses)


if __name__ == '__main__':
    main()
When we say computer, we mean the electronic computer.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Ich hätte das noch in erster_wert, zweiter_wert aufgespalten und statuses im Aufruf zusammengebaut.
Karamell
User
Beiträge: 14
Registriert: Montag 19. November 2018, 09:53

pathlib ersetzt mir in diesem Fall das os.walk?
Antworten