Jak skutecznie i łatwo ponownie zaprojektować 500 plików CSV za pomocą QGIS?

11

Wiem, moje pytanie jest podobne do niektórych starych na tej stronie.

Mam dużo plików CSV (współrzędne geograficzne) do zaimportowania do qgis (a następnie do ich konwersji), a zwykły sposób nie jest najlepszym sposobem (zbyt długim).

Mam prawie 500 plików CSV (współrzędne wgs84) i chcę to zrobić:

  1. Zaimportuj wszystkie pliki CSV jednocześnie do QGIS
  2. Wyświetl je
  3. Wyeksportuj je do plików CSV (ponownie), ale z różnymi współrzędnymi (konwersja do UTM33N)

Próbuję zrozumieć, jak korzystać z konsoli python, ale nie przechodzę dalej :(

Czy ktoś może mi wyjaśnić, jak to zrobić krok po kroku?

Raquel Ribeiro
źródło
patrz moja odpowiedź poniżej. problem został już rozwiązany i wyjaśniony
Generic Wevers
2
I dlaczego jest to duplikat z zaznaczonym? Być może OP próbuje nauczyć się pyqgis i jak używać Pythona, jeśli weźmie się pod uwagę jego / jej pogrubienia.
nickves
Proszę podać swoje pytanie. Czy nie chcesz ładować ich ręcznie do QGIS? Czy chcesz przekonwertować je na inny format? Jakie dokładnie jest twoje pytanie?
bugmenot123
1. Zaimportuj wszystkie pliki w jednym procesie do qgis 2. wyświetl je 3. wyeksportuj je ponownie jako csv, ale we współrzędnych utm
Raquel Ribeiro
cat * .csv> one_file.csv (lub cokolwiek to jest odpowiednik Windows) połączy wszystkie twoje pliki csv w jeden. 500 naprawdę nie jest tak dużą liczbą :-)
John Powell

Odpowiedzi:

15

Jeśli chcesz ponownie wyrzucić pliki csv z konsoli Python w QGIS, możesz użyć następującego skryptu. Wszystko, co musisz zmienić, to trzy ścieżki wymienione w komentarzach.

Zasadniczo skrypt importuje pliki csv do QGIS jako pliki kształtów (zakładając, że twoje pola geometryczne są nazwane Xi Y). Następnie korzysta z algorytmów qgis:reprojectlayeri qgis:fieldcalculatorz przybornika przetwarzania, aby zmienić projekt Xi zaktualizować Ypola i o nowe współrzędne. Następnie zapisuje je w folderze i konwertuje je na pliki csv w określonej ścieżce. W końcu zaktualizowałeś pliki shapefile i pliki csv w osobnych folderach.

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

Mam nadzieję że to pomoże!

Joseph
źródło
2
Świetna odpowiedź - masz to wszystko! Jedno pytanie, jeśli nie masz nic przeciwko: nadal musisz dodawać / usuwać warstwy w QgsMapLayerRegistry, nawet jeśli robisz rzeczy z konsoli python?
nickves
1
@nickves - Haha, wielkie dzięki, kolego! Hmm, może nie będę musiał dodawać / usuwać warstw (jestem pewien, że skrypt można znacznie zmniejszyć). Nie jestem ekspertem, ale przetestuję to później i skontaktuję się z Tobą. Chyba że możesz podać znacznie bardziej schludny skrypt, w którym to przypadku powinieneś opublikować go jako odpowiedź, chciałbym go zagłosować :)
Joseph
@nickves - Jeszcze raz dziękuję za sugestię, kolego! Kod został edytowany, aby uniknąć dodawania / usuwania warstw po raz drugi :)
Joseph
@RaquelRibeiro - Serdecznie witamy! Cieszę się, że było pomocne :)
Joseph
@Joseph, mogę o coś jeszcze zapytać? W wierszach 14 i 15 liczby: 0, 10, 10 dokładnie określają, co? (współrzędne wyjściowe mają zbyt wiele zer po prawej stronie i chcę je zminimalizować)
Raquel Ribeiro,
8

Szybkie rozwiązanie do transformacji pliku rozdzielanego spacjami zawierającego „lon lat” w WGS84 na UTM33N, ale nie otrzymujesz żadnych innych danych:

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

To działa i zachowuje kolejność danych, więc może kolejna pętla używa np. Awk do łączenia danych opisowych ze współrzędnymi?

Edytować. Ze względu na niechlujne komentarze, które napisałem poniżej, zmienię tutaj odpowiedź.

Poniższy skrypt powinien wykonać zadanie odczytu wielu plików csv, dodając nowe kolumny współrzędnych do każdego pliku.

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

W OSX musisz zainstalować najnowszą (2009) wersję sed i użyć pierwszej, niekomentowanej linii w pętli. W przypadku Linuksa skomentuj pierwszy i użyj drugiego. Dostosuj -F " "zgodnie z formatem separatora w plikach csv, np. -F ","Dla separatora przecinkowego. Zwróć też uwagę, że transformacja elewacji dotyczy elipsoidy, a nie geoidy, więc pamiętaj o odpowiedniej transformacji wysokości.

mercergeoinfo
źródło
Właśnie przypomniałem sobie, że jakiś czas temu zrobiłem coś podobnego i opublikowałem rozwiązanie na moim blogu. Jest napisany dla komputerów Mac, ale oparty jest na bash. Największą różnicą jest problem z sedem na OS X, z którym mam do czynienia na końcu postu: mercergeoinfo.blogspot.se/2014/01/…
mercergeoinfo
Ostatni komentarz był nieco niechlujny. Użyj tej linii w powyższym skrypcie bash, aby przeglądać wszystkie pliki. 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}Zamień / usr / local / sed na tylko sed, jeśli nie korzystasz z OSX. Nie jest to idealne, jeśli twoje pliki csv są rozdzielone spacjami, jak zakłada powyższy wiersz, ale działa. Jeśli -F " "-F ","
dzielisz
Zastanawiam się, dlaczego zaktualizowano kod w komentarzach, a odpowiedź nie została zaktualizowana powyżej. Kod w komentarzu jest naprawdę trudny do odczytania. Czy widzisz link edycji pod swoją odpowiedzią?
Miro,
Tak, ale to nie była tak naprawdę aktualizacja, a raczej dodatkowa. Zgadzam się, że to dość bałagan Chyba powinienem zaktualizować oryginalną odpowiedź. Dzięki
mercergeoinfo,
7

Używanie qgis lub nawet OGR to przesada.
Użyj pyproj( https://pypi.python.org/pypi/pyproj ) w połączeniu z pisarzem csv Python i kilkoma standardowymi sztuczkami bibliotecznymi. Nie musisz instalować niczego poza pyprojtym!

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
źródło
Zdaję sobie sprawę, że plakat jest dość niedoświadczony w Pythonie. Nie używam regularnie QGIS, więc czy ktoś z większym doświadczeniem z tą platformą może wyjaśnić, gdzie jest zainstalowany Python? Plakat powinien uczynić z tego samodzielny skrypt i prawdopodobnie uruchomić go z IDLE. Nie mam bieżącej instalacji, więc nie wiem, czy pyprojnależy zainstalować osobno na plakat, czy już tam jest.
blord-castillo
1
nigdy wcześniej nie używał funkcji częściowej. Zrobi to teraz. +1
nickves
4

Nie potrzebujesz pytona. Wystarczy użyć wiersza polecenia i ogr2ogr. W twoim przypadku najważniejszy jest parametr -t_srs srs_def.

Wyjaśniono to już w odpowiedzi na pytanie Jak przekonwertować plik programu Excel z kolumnami x, y na plik kształtu?

AKTUALIZACJA Nie mam czasu na napisanie pełnego kodu. Problemem będzie jednak to, że potrzebuje trochę więcej kodu w pythonie, niż może się wydawać.

Twoim głównym problemem będzie to, że praca z plikami csv nie jest tak wygodna, jak używanie plików kształtów. Dlatego najpierw musisz przekonwertować plik csv do kształtu, który wymaga pliku VRT. Wyjaśnia to pierwszy link. W tym miejscu musisz napisać pętlę skryptów Pythona, która automatycznie generuje pliki vrt.

To jest skrypt, którego sam użyłem. Musisz sprawdzić, czy to działa dla Ciebie. Dołączyłem już konwersję z WGS 84 do 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)

Trzeba dostosować parametry do nazwy pola , src , X i Y zgodnie z pliku csv.

AKTUALIZACJA 2

Po namyśle zastanawiam się, dlaczego w ogóle chcesz korzystać z QGIS? Można użyć skryptu Pythona, jak to się bezpośrednio konwertować współrzędne z WGS do UTM. W tym przypadku jest to prosty otwarty plik csv, odczyt współrzędnych, transformacja współrzędnych i zapisanie jej w nowym pliku.

Ogólne Wevers
źródło
myślę, że nie tego szukam ... Mam prawie 500 plików csv (współrzędne wgs84) i właśnie to chcę zrobić: 1. Zaimportuj wszystkie pliki csv do q gis 2. wyświetl je 3. eksportuj je do plików csv (ponownie), ale z różnymi współrzędnymi (konwersja do utm33N)
Raquel Ribeiro
myślę, że potrzebuję procesu wsadowego lub czegoś takiego, aby to zrobić ...
Raquel Ribeiro
4
ale dlaczego chcesz to zrobić? 1. możesz zrobić to samo (co opisałeś) z wiersza poleceń bez qgis. 2. Możesz to zrobić w trybie wsadowym. 3. w Pythonie jest prawie tak samo. używałbyś również ogr2ogr
Generic Wevers
2
„Po prostu” użycie wiersza poleceń nie jest tak naprawdę odpowiedzią. Linia poleceń nigdy nie jest łatwa w użyciu, jeśli nie masz pojęcia, jak to zrobić. Naprawdę nie mogę znaleźć rozwiązania w powiązanej odpowiedzi. Dlaczego nie dać biednemu przykładowi partii z ogr2ogr, a wszystko będzie dobrze?
Bernd V.
1
ok, 1. możesz przeczytać gis.stackexchange.com/help/how-to-ask . po tym i 5 minutach googleowania przyznasz, że pytanie jest bardzo słabo zbadane i można je rozwiązać na podstawie już udzielonych odpowiedzi. 2. Jeśli nadal nie można go rozwiązać, myślę, że wszyscy będą zadowoleni z pomocy. ale ponieważ jestem dobrą osobą, dam więcej wskazówek.
Generic Wevers,