Wie können 500 CSV-Dateien mit QGIS effizient und einfach neu projiziert werden?

11

Ich weiß, meine Frage ähnelt einigen alten auf dieser Seite.

Ich habe viele CSV-Dateien (Geokoordinaten) zum Importieren in qgis (und zum anschließenden Konvertieren), und der übliche Weg ist nicht der beste (zu lange).

Ich habe fast 500 CSV-Dateien (wgs84-Koordinaten) und dies ist, was ich tun möchte:

  1. Importieren Sie alle CSV-Dateien gleichzeitig in QGIS
  2. Projizieren Sie sie
  3. Exportieren Sie sie (erneut) in CSV-Dateien, jedoch mit unterschiedlichen Koordinaten (Konvertierung in UTM33N).

Ich versuche zu verstehen, wie man die Python-Konsole benutzt, aber ich gehe nicht weiter :(

Kann mir jemand erklären, wie man es Schritt für Schritt erreicht?

Raquel Ribeiro
quelle
siehe meine Antwort unten. Das Problem wurde bereits gelöst und erklärt
Generic Wevers
2
Und warum ist das Duplikat mit dem markierten? Vielleicht versucht das OP, Pyqgis zu lernen und wie man Python benutzt, wenn man seine / ihre Kühnheit berücksichtigt.
Nickves
Bitte geben Sie Ihre Frage an. Möchten Sie sie nicht manuell in QGIS laden? Möchten Sie sie in ein anderes Format konvertieren? Was genau ist deine Frage?
Bugmenot123
1. Importieren Sie alle Dateien in einem Prozess in qgis 2. Projizieren Sie sie 3. Exportieren Sie sie erneut als CSV, aber in utm-Koordinaten
Raquel Ribeiro
cat * .csv> one_file.csv (oder was auch immer das Windows-Äquivalent ist) kombiniert alle Ihre CSV-Dateien zu einer. 500 ist wirklich keine so große Zahl :-)
John Powell

Antworten:

15

Wenn Sie CSV-Dateien von der Python-Konsole in QGIS neu projizieren möchten, können Sie das folgende Skript verwenden. Alles, was Sie ändern müssten, sind die drei Pfade, die in den Kommentaren erwähnt werden.

Im Wesentlichen importiert das Skript Ihre CSV-Dateien als Shapefiles in QGIS (vorausgesetzt, Ihre geometrischen Felder heißen Xund Y). Anschließend werden die Algorithmen qgis:reprojectlayerund qgis:fieldcalculatoraus der Processing Toolbox verwendet , um die Felder Xund Ymit den neuen Koordinaten neu zu projizieren und zu aktualisieren . Anschließend werden diese in einem Ordner gespeichert und in einem von Ihnen angegebenen Pfad in CSV-Dateien konvertiert. Am Ende haben Sie also Shapefiles und CSV-Dateien in separaten Ordnern aktualisiert.

import glob, os, processing

path_to_csv = "C:/Users/You/Desktop/Testing//"  # Change path to the directory of your csv files
shape_result = "C:/Users/You/Desktop/Testing/Shapefile results//"  # Change path to where you want the shapefiles saved

os.chdir(path_to_csv)  # Sets current directory to path of csv files
for fname in glob.glob("*.csv"):  # Finds each .csv file and applies following actions
        uri = "file:///" + path_to_csv + fname + "?delimiter=%s&crs=epsg:4326&xField=%s&yField=%s" % (",", "x", "y")
        name = fname.replace('.csv', '')
        lyr = QgsVectorLayer(uri, name, 'delimitedtext')
        QgsMapLayerRegistry.instance().addMapLayer(lyr)  # Imports csv files to QGIS canvas (assuming 'X' and 'Y' fields exist)

crs = 'EPSG:32633'  # Set crs
shapefiles = QgsMapLayerRegistry.instance().mapLayers().values()  # Identifies loaded layers before transforming and updating 'X' and 'Y' fields
for shapes in shapefiles:
        outputs_0 = processing.runalg("qgis:reprojectlayer", shapes, crs, None)
        outputs_1 = processing.runalg("qgis:fieldcalculator", outputs_0['OUTPUT'], 'X', 0, 10, 10, False, '$x', None)
        outputs_2 = processing.runalg("qgis:fieldcalculator", outputs_1['OUTPUT_LAYER'], 'Y', 0, 10, 10, False, '$y', shape_result + shapes.name())

os.chdir(shape_result)  # Sets current directory to path of new shapefiles
for layer in glob.glob("*.shp"):  # Finds each .shp file and applies following actions
        new_layer = QgsVectorLayer(layer, os.path.basename(layer), "ogr")
        new_name = layer.replace('.shp', '')
        csvpath = "C:/Users/You/Desktop/Testing/CSV results/" + new_name + ".csv"  # Change path to where you want the csv(s) saved
        QgsVectorFileWriter.writeAsVectorFormat(new_layer, csvpath, 'utf-8', None, "CSV")   

Hoffe das hilft!

Joseph
quelle
2
Tolle Antwort - Sie haben alles da!. Eine Frage, wenn Sie nichts dagegen haben: Sie müssen bei QgsMapLayerRegistry noch Ebenen hinzufügen / entfernen, auch wenn Sie Dinge über die Python-Konsole tun?
Nickves
1
@nickves - Haha vielen Dank Kumpel! Hmm, ich muss möglicherweise keine Ebenen hinzufügen / entfernen (ich bin sicher, dass das Skript drastisch reduziert werden kann). Ich bin kein Experte, aber ich werde es später testen und mich bei Ihnen melden. Wenn Sie kein viel übersichtlicheres Skript bereitstellen können, in welchem ​​Fall Sie es als Antwort veröffentlichen sollten, würde ich es positiv bewerten :)
Joseph
@nickves - Nochmals vielen Dank für Ihren Vorschlag Kumpel! Code wurde bearbeitet, um zu vermeiden, dass die Ebenen ein zweites Mal hinzugefügt / entfernt werden :)
Joseph
@ RaquelRibeiro - Herzlich willkommen! Ich
Joseph
@ Joseph darf ich dich nochmal etwas fragen? In den Zeilen 14 und 15 definieren die Zahlen: 0, 10, 10 genau was? (Die Ausgabekoordinaten haben zu viele Nullen auf der rechten Seite und ich möchte sie minimieren)
Raquel Ribeiro
8

Eine schnelle Lösung zum Transformieren einer durch Leerzeichen getrennten Datei mit "lon lat" in WGS84 in UTM33N, aber Sie erhalten keine anderen Daten:

#!/bin/bash
#
for i in $( ls *.csv ); do
    gdaltransform -s_srs EPSG:4326 -t_srs EPSG:32633 < ${i} > utm${i}
done

Das funktioniert und es behält die Reihenfolge der Daten bei, also vielleicht eine andere Schleife, die zB awk verwendet, um die beschreibenden Daten mit den Koordinaten zu kombinieren?

Bearbeiten. Aufgrund der unordentlichen Kommentare, die ich unten gemacht habe, werde ich die Antwort stattdessen hier bearbeiten.

Das folgende Skript sollte mehrere CSV-Dateien lesen und jeder Datei neue Koordinatenspalten hinzufügen.

#!/bin/bash
#
for i in $( ls *.csv ); do
 paste -d',' ${i} <(awk -v OFS="," -F " " 'NR>1 {print $1 " " $2}' ${i} | gdaltransform -s_srs EPSG:4326 -t_srs EPSG:32633 | awk '{gsub(" ",",",$0); print $0}' | /usr/local/bin/sed "1i\X,Y,Z") > utm${i}
#
 #paste -d',' ${i} <(awk -v OFS="," -F " " 'NR>1 {print $1 " " $2}' ${i} | gdaltransform -s_srs EPSG:4326 -t_srs EPSG:32633 | awk '{gsub(" ",",",$0); print $0}' |sed "1i\X,Y,Z") > utm${i}
#
done

Unter OSX müssen Sie die neueste (2009) Version von sed installieren und die erste, nicht kommentierte Zeile in der Schleife verwenden. Für Linux das erste auskommentieren und das zweite verwenden. Passen Sie das -F " "Trennzeichen entsprechend dem Format des Trennzeichens in Ihren CSV-Dateien an, z -F ",". B. für durch Kommas getrennte. Beachten Sie auch, dass sich die Höhentransformation auf das Ellipsoid und nicht auf das Geoid bezieht. Stellen Sie daher sicher, dass Sie die Höhen entsprechend transformieren.

mercergeoinfo
quelle
Ich habe mich gerade daran erinnert, vor einiger Zeit etwas Ähnliches getan und eine Lösung in meinem Blog veröffentlicht zu haben. Es ist für Mac geschrieben, basiert aber auf Bash. Der größte Unterschied ist das Problem mit sed unter OS X, mit dem ich mich am Ende des Beitrags befasse
mercergeoinfo
Der letzte Kommentar war etwas chaotisch. Verwenden Sie diese Zeile im obigen Bash-Skript, um alle Dateien zu durchlaufen. paste -d',' ${i} <(awk -v OFS="," -F " " 'NR>1 {print $1 " " $2}' ${i} | gdaltransform -s_srs EPSG:4326 -t_srs EPSG:32633 | awk '{gsub(" ",",",$0); print $0}' | /usr/local/bin/sed "1i\X,Y,Z") > utm${i}Ersetzen Sie / usr / local / sed durch nur sed, wenn Sie nicht unter OSX arbeiten. Dies ist nicht ideal, wenn Ihre CSV-Dateien durch Leerzeichen getrennt sind, wie in der obigen Zeile angenommen, aber es funktioniert. Wenn Sie Komma getrennt haben, wechseln Sie -F " "zu-F ","
mercergeoinfo
Ich frage mich, warum der aktualisierte Code in Kommentaren enthalten ist und Sie Ihre Antwort oben nicht aktualisiert haben. Der Code im Kommentar ist wirklich schwer zu lesen. Sehen Sie den Bearbeitungslink unter Ihrer Antwort?
Miro
Ja, aber dann war es nicht wirklich ein Update, eher ein zusätzliches. Ziemlich chaotisch, da stimme ich zu. Ich denke, ich sollte die ursprüngliche Antwort aktualisieren. Danke
mercergeoinfo
7

Die Verwendung von QGIS oder sogar OGR ist dafür übertrieben.
Verwenden Sie pyproj( https://pypi.python.org/pypi/pyproj ) in Kombination mit dem Python-CSV-Writer und einigen Standard-Bibliothekstricks. Sie müssen nichts anderes als pyprojdafür installieren !

import csv
import pyproj
from functools import partial
from os import listdir, path

#Define some constants at the top
#Obviously this could be rewritten as a class with these as parameters

lon = 'lon' #name of longitude field in original files
lat = 'lat' #name of latitude field in original files
f_x = 'x' #name of new x value field in new projected files
f_y = 'y' #name of new y value field in new projected files
in_path = u'D:\\Scripts\\csvtest\\input' #input directory
out_path = u'D:\\Scripts\\csvtest\\output' #output directory
input_projection = 'epsg:4326' #WGS84
output_projecton = 'epsg:32633' #UTM33N

#Get CSVs to reproject from input path
files= [f for f in listdir(in_path) if f.endswith('.csv')]

#Define partial function for use later when reprojecting
project = partial(
    pyproj.transform,
    pyproj.Proj(init=input_projection),
    pyproj.Proj(init=output_projecton))

for csvfile in files:
    #open a writer, appending '_project' onto the base name
    with open(path.join(out_path, csvfile.replace('.csv','_project.csv')), 'wb') as w:
        #open the reader
        with open(path.join( in_path, csvfile), 'rb') as r:
            reader = csv.DictReader(r)
            #Create new fieldnames list from reader
            # replacing lon and lat fields with x and y fields
            fn = [x for x in reader.fieldnames]
            fn[fn.index(lon)] = f_x
            fn[fn.index(lat)] = f_y
            writer = csv.DictWriter(w, fieldnames=fn)
            #Write the output
            writer.writeheader()
            for row in reader:
                x,y = (float(row[lon]), float(row[lat]))
                try:
                    #Add x,y keys and remove lon, lat keys
                    row[f_x], row[f_y] = project(x, y)
                    row.pop(lon, None)
                    row.pop(lat, None)
                    writer.writerow(row)
                except Exception as e:
                    #If coordinates are out of bounds, skip row and print the error
                    print e
Blord-Castillo
quelle
Mir ist klar, dass das Poster mit Python ziemlich unerfahren ist. Ich verwende QGIS nicht regelmäßig. Kann jemand mit mehr Erfahrung mit dieser Plattform erklären, wo Python installiert ist? Das Poster sollte dies zu einem eigenständigen Skript machen und es wahrscheinlich von IDLE ausführen. Ich habe keine aktuelle Installation, daher weiß ich nicht, ob pyprojdas Poster separat installiert werden muss oder bereits vorhanden ist.
Blord-Castillo
1
noch nie Teilfunktion verwendet. Wird von nun an tun. +1
Nickves
4

Du brauchst kein Python. Verwenden Sie einfach die Befehlszeile und ogr2ogr. In Ihrem Fall ist der Parameter -t_srs srs_def am wichtigsten.

Dies wird bereits in dieser Antwort auf Wie kann ich eine Excel-Datei mit x, y-Spalten in ein Shapefile konvertieren?

UPDATE Ich habe nicht die Zeit, Ihnen Ihren vollständigen Code zu schreiben. Das Problem wird jedoch sein, dass in Python etwas mehr Code benötigt wird, als Sie vielleicht denken.

Ihr Hauptproblem wird sein, dass das Arbeiten mit CSV-Dateien nicht so komfortabel ist wie das Verwenden von Shapefiles. Daher müssen Sie zuerst die CSV in eine Form konvertieren, die eine VRT-Datei benötigt. Dies wird im ersten Link erklärt. Hier müssen Sie ein Python-Skript schreiben, das Ihre Dateien durchläuft und automatisch die vrt-Dateien generiert.

Dies ist ein Skript, das ich selbst verwendet habe. Sie müssen testen, ob es für Sie funktioniert. Ich habe bereits die Konvertierung von WGS 84 auf UTM 33N aufgenommen

from os import listdir, stat, mkdir, system
path = "your path here"
out_path = "your output path here"
files = filter(listdir(path), '*.csv') #for Python 3.x
# files= [f for f in listdir(path) if f.endswith('.csv')] #for Python 2.7

for x in range(len(files)):
    name = files[x].replace('.csv', '')
    # 2. create vrt file for reading csv
    outfile_path1 = out_path + name + '.vrt'
    text_file = open(outfile_path1, "w")
    text_file.write('<OGRVRTDataSource> \n')
    text_file.write('    <OGRVRTLayer name="' + str(name) + '"> \n')
    text_file.write('        <SrcDataSource relativeToVRT="1">' + name + '.csv</SrcDataSource> \n')
    text_file.write('        <GeometryType>wkbPoint</GeometryType> \n')
    text_file.write('        <LayerSRS>WGS84</LayerSRS> \n')
    text_file.write('        <GeometryField encoding="PointFromColumns" x="Lon" y="Lat"/> \n')
    text_file.write('        <Field name="Name" src="Name" type="String" /> \n')
    text_file.write('    </OGRVRTLayer> \n')
    text_file.write('</OGRVRTDataSource> \n')
    # 3. convert csv/vrt to point shapefile
    outfile_path2 = out_path + name + '.shp'
    command = ('ogr2ogr -f "ESRI Shapefile" -t_srs EPSG:32633' + outfile_path2 + ' ' +  outfile_path1)
    system(command)

Sie müssen die Parameter für Feldname , src , x und y entsprechend Ihrer CSV-Datei anpassen .

UPDATE2

Nach einigem Überlegen frage ich mich, warum Sie QGIS überhaupt verwenden möchten. Sie können ein Python-Skript wie dieses verwenden , um Ihre Koordinaten direkt von WGS in UTM zu konvertieren. In diesem Fall handelt es sich um eine einfache offene CSV, die Koordinate liest, die Koordinate transformiert und in einer neuen Datei speichert.

Generische Wevers
quelle
Ich denke, das ist nicht das, wonach ich suche ... Ich habe fast 500 CSV-Dateien (wgs84-Koordinaten) und dies ist, was ich tun möchte: 1. Importieren Sie alle CSV-Dateien auf einmal in q gis 2. Projizieren Sie sie 3. Exportieren Sie sie (erneut) in CSV-Dateien, jedoch mit unterschiedlichen Koordinaten (Konvertierung in utm33N)
Raquel Ribeiro
Ich glaube, ich brauche einen Batch-Prozess oder ähnliches, um das zu tun ...
Raquel Ribeiro
4
aber warum willst du das machen 1. Sie können dasselbe (was Sie beschrieben haben) über die Befehlszeile ohne qgis tun. 2. Sie können dies im Batch-Modus tun. 3. In Python ist es fast das gleiche. Sie würden auch ogr2ogr
Generic Wevers
2
"Einfach" über die Kommandozeile ist wirklich keine Antwort. Die Befehlszeile ist nie einfach zu bedienen, wenn Sie keine Ahnung haben, wie es geht. Und ich kann die Lösung in der verknüpften Antwort wirklich nicht finden. Warum nicht einfach dem armen Kerl eine Beispielcharge mit ogr2ogr geben, und alles wäre in Ordnung?
Bernd V.
1
ok, 1. Sie können gis.stackexchange.com/help/how-to-ask lesen . Danach und 5 Minuten googeln werden Sie zugeben, dass die Frage sehr schlecht recherchiert ist und mit bereits gegebenen Antworten gelöst werden kann. 2. Wenn es immer noch nicht gelöst werden kann, werden wohl alle gerne helfen. aber da ich ein guter Mensch bin, werde ich noch einige Hinweise geben.
Generic Wevers