Come riproiettare 500 file CSV in modo efficiente e semplice usando QGIS?


11

Lo so, la mia domanda è simile ad alcuni vecchi su questo sito.

Ho molti file CSV (coordinate geografiche) da importare in qgis (e poi per convertirli), e il solito modo non è il modo migliore per farlo (troppo a lungo).

Ho quasi 500 file CSV (coordinate wgs84) e questo è quello che voglio fare:

  1. Importa tutti i file CSV contemporaneamente in QGIS
  2. Proiettali
  3. Esportali in file CSV (di nuovo) ma con coordinate diverse (conversione in UTM33N)

Sto cercando di capire come usare la console Python ma non sto andando avanti :(

Qualcuno può spiegarmi come raggiungerlo passo dopo passo?


vedi la mia risposta qui sotto. il problema era già stato risolto e spiegato
Generic Wevers,

2
E perché quel duplicato è quello contrassegnato? Forse l'OP cerca di imparare il pyqgis e come usare Python se consideri i suoi grassetti.
Nick,

Si prega di specificare la domanda. Vuoi non caricarli manualmente in QGIS? Vuoi convertirli in un altro formato? Qual è esattamente la tua domanda?
bugmenot123,

1. Importa tutti i file in un unico processo su qgis 2. proiettali 3. esportali di nuovo come CSV ma in coordinate utm
Raquel Ribeiro,

cat * .csv> one_file.csv (o qualunque sia l'equivalente di Windows) combinerà tutti i tuoi file CSV in uno. 500 non è un numero così grande :-)
John Powell,

Risposte:


15

Se stai cercando di riproiettare i file CSV dalla console Python in QGIS, puoi utilizzare il seguente script. Tutto ciò che dovresti cambiare sono i tre percorsi che sono menzionati nei commenti.

In sostanza, lo script importa i tuoi file CSV in QGIS come shapefile (supponendo che i tuoi campi geometrici siano chiamati Xe Y). Quindi utilizza gli algoritmi qgis:reprojectlayere qgis:fieldcalculatordalla Casella degli strumenti di elaborazione per riproiettare e aggiornare i campi Xe Ycon le nuove coordinate. Quindi li salva in una cartella e li converte in file CSV nel percorso specificato. Quindi, alla fine, hai aggiornato shapefile e file CSV in cartelle separate.

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")   

Spero che questo ti aiuti!


2
Ottima risposta - hai tutto lì !. Una domanda se non ti dispiace: devi ancora aggiungere / rimuovere layer su QgsMapLayerRegistry anche se fai cose dalla console di Python?
Nick

1
@nickves - Haha grazie mille amico! Hmm, potrei non dover aggiungere / rimuovere layer (sono certo che la sceneggiatura può essere ridotta drasticamente). Non sono un esperto, ma lo proverò più tardi e ti risponderò. A meno che tu non possa fornire uno script molto più accurato, nel qual caso dovresti pubblicarlo come risposta, lo voterei :)
Joseph

@nickves - Grazie ancora per il tuo suggerimento amico! Il codice è stato modificato per evitare di aggiungere / rimuovere i livelli una seconda volta :)
Joseph,

@RaquelRibeiro - Benvenuto! Sono contento che sia stato utile :)
Joseph

@Joseph posso chiederti di nuovo qualcosa? Alle righe 14 e 15, i numeri: 0, 10, 10 stanno definendo esattamente cosa? (le coordinate di output hanno troppi zeri a destra e voglio minimizzarli)
Raquel Ribeiro

8

Una soluzione rapida per trasformare un file separato da spazio contenente "lon lat" in WGS84 in UTM33N ma non ottieni altri dati:

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

Funziona e conserva l'ordine dei dati, quindi forse un altro ciclo usando ad esempio awk per combinare i dati descrittivi con le coordinate?

Modificare. A causa dei commenti disordinati che ho fatto di seguito, invece, modificherò la risposta qui.

Il seguente script dovrebbe svolgere il compito di leggere più file CSV, aggiungendo nuove colonne di coordinate a ciascun file.

#!/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

Su OSX dovrai installare l'ultima versione (2009) di sed e usare la prima linea non commentata nel loop. Per Linux commenta il primo e usa il secondo. Regola in -F " "base al formato del separatore nei tuoi file CSV, ad esempio -F ","per la virgola separata. Si noti inoltre che la trasformazione di elevazione è nell'ellissoide, non nel geoide, quindi assicurarsi di trasformare le altezze di conseguenza.


Mi sono appena ricordato di aver fatto qualcosa di simile qualche tempo fa e di aver pubblicato una soluzione sul mio blog. È scritto per Mac ma è basato su bash. La differenza più grande è il problema con sed su OS X, che tratterò alla fine del post: mercergeoinfo.blogspot.se/2014/01/…
mercergeoinfo

L'ultimo commento è stato un po 'disordinato. Usa questa riga nel precedente script bash per scorrere tutti i file. 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}Sostituisci / usr / local / sed con solo sed se non sei su OSX. Questo non è l'ideale se i tuoi file CSV sono separati da spazio, come presuppone la riga precedente, ma funziona. Se hai la virgola separata, cambia -F " "in-F ","
mercergeoinfo il

Mi chiedo perché sia ​​il codice aggiornato nei commenti e non hai aggiornato la risposta sopra. Il codice nel commento è davvero difficile da leggere. Vedi il link di modifica sotto la tua risposta?
Miro,

Sì, ma non è stato davvero un aggiornamento, più simile a un ulteriore. Abbastanza disordinato, sono d'accordo. Immagino che dovrei aggiornare la risposta originale. Grazie
mercergeoinfo il

7

L'uso di qgis o OGR è eccessivo per questo.
Usa pyproj( https://pypi.python.org/pypi/pyproj ) in combinazione con lo scrittore python csv e alcuni trucchi della libreria standard. Non è necessario installare altro che pyprojper questo!

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

Mi rendo conto che il poster è piuttosto inesperto con il pitone. Non utilizzo regolarmente QGIS, quindi qualcuno con più esperienza con quella piattaforma può spiegare dove è installato Python? Il poster dovrebbe renderlo uno script autonomo e probabilmente eseguirlo da IDLE. Non ho un'installazione corrente, quindi non so se pyprojdebba essere installato separatamente per il poster o se è già lì.
blord-castillo,

1
mai usato prima la funzione parziale. Lo farò d'ora in poi. +1
nichel

4

Non hai bisogno di Python. Usa semplicemente la riga di comando e ogr2ogr. Nel tuo caso, il più importante è il parametro -t_srs srs_def.

Questo è già spiegato in questa risposta a Come posso convertire un file Excel con le colonne x, y in un file di forma?

AGGIORNAMENTO Non ho il tempo di scriverti il ​​tuo codice completo. Ma il problema sarà che ha bisogno di un po 'più di codice in Python di quanto tu possa pensare.

Il tuo problema principale sarà che lavorare con i file CSV non è comodo come usare gli shapefile. Quindi dovrai prima convertire il CSV in una forma che necessita del file VRT. Questo è spiegato nel primo link. Qui dovrai scrivere uno script Python in loop tra i tuoi file che genera automaticamente i file vrt.

Questa è una sceneggiatura che ho usato da solo. Devi testare se funziona per te. Ho già incluso la conversione da WGS 84 a UTM 33N

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)

È necessario regolare i parametri per Nome campo , src , x e y in base al tuo file CSV.

UPDATE2

Dopo aver riflettuto un po ', mi chiedo perché vuoi usare QGIS? È possibile utilizzare uno script Python come questo per convertire direttamente le coordinate da WGS a UTM. In questo caso è un semplice csv aperto, leggi coordinate, trasforma coordinate e salvalo in un nuovo file.


penso che questo non sia quello che sto cercando ... Ho quasi 500 file CSV (coordinate WGS84) e questo è quello che voglio fare: 1. Importare tutti i file CSV in una sola volta in q gis 2. proiettarli 3. esportali in file CSV (di nuovo) ma con coordinate diverse (conversione in utm33N)
Raquel Ribeiro,

penso di aver bisogno di un processo batch o qualcosa del genere per farlo ...
Raquel Ribeiro,

4
ma perché vuoi farlo? 1. puoi fare lo stesso (quello che hai descritto) dalla riga di comando senza qgis. 2. puoi farlo in modalità batch. 3. in Python è quasi lo stesso. useresti anche ogr2ogr
Generic Wevers,

2
"Semplicemente" utilizzando la riga di comando non è in realtà una risposta. La riga di comando non è mai facile da usare se non hai idea di come farlo. E davvero non riesco a trovare la soluzione nella risposta collegata. Perché non dare semplicemente al poveretto un batch di esempio con ogr2ogr, e tutto andrebbe bene?
Bernd V.

1
ok, 1. puoi leggere gis.stackexchange.com/help/how-to-ask . dopo questo e 5 minuti di google ammetterai che la domanda è molto scarsamente ricercata e può essere risolta con risposte già fornite. 2. Se non è ancora possibile risolverlo, suppongo che tutti saranno lieti di aiutarti. ma dato che sono una brava persona, darò qualche suggerimento in più.
Wevers generico il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.