Bagaimana cara memproyeksi ulang 500 file CSV secara efisien dan mudah menggunakan QGIS?


11

Saya tahu, pertanyaan saya mirip dengan yang lama di situs ini.

Saya punya banyak file CSV (koordinat geografis) untuk diimpor ke qgis (dan kemudian dikonversi), dan cara yang biasa bukan cara terbaik untuk melakukannya (terlalu lama).

Saya memiliki hampir 500 file CSV (koordinat wgs84) dan inilah yang ingin saya lakukan:

  1. Impor semua file CSV sekaligus ke QGIS
  2. Proyeksikan mereka
  3. Ekspor mereka ke file CSV (lagi) tetapi dengan koordinat yang berbeda (konversi ke UTM33N)

Saya mencoba memahami cara menggunakan konsol python tapi saya tidak bergerak :(

Adakah yang bisa menjelaskan kepada saya bagaimana cara mencapainya langkah demi langkah?


lihat jawaban saya di bawah ini. masalahnya sudah dipecahkan dan dijelaskan
Generic Wevers

2
Dan mengapa itu duplikat dengan yang ditandai? Mungkin OP mencoba mempelajari pyqgis dan bagaimana menggunakan python jika Anda mempertimbangkan huruf tebal.
nickves

Silakan tentukan pertanyaan Anda. Apakah Anda ingin tidak memuatnya secara manual ke QGIS? Apakah Anda ingin mengubahnya menjadi format lain? Apa sebenarnya pertanyaan Anda?
bugmenot123

1. Impor semua file dalam satu proses ke qgis 2. proyeksikan mereka 3. ekspor semuanya lagi sebagai csv tetapi dalam koordinat utm
Raquel Ribeiro

cat * .csv> one_file.csv (atau apa pun yang setara dengan windows) akan menggabungkan semua file csv Anda menjadi satu. 500 sebenarnya bukan angka yang besar :-)
John Powell

Jawaban:


15

Jika Anda ingin memproyeksikan ulang file csv dari Python Console di QGIS maka Anda dapat menggunakan skrip berikut. Yang perlu Anda ubah adalah tiga jalur yang disebutkan dalam komentar.

Pada dasarnya, skrip mengimpor file csv Anda ke QGIS sebagai shapefile (dengan asumsi bidang geometris Anda dinamai Xdan Y). Kemudian menggunakan qgis:reprojectlayerdan qgis:fieldcalculatoralgoritma dari Toolbox Pemrosesan untuk memproyeksi ulang dan memperbarui bidang Xdan Ydengan koordinat baru. Itu kemudian menyimpan ini dalam folder dan mengubahnya menjadi file csv di jalur yang Anda tentukan. Jadi pada akhirnya, Anda telah memperbarui file shapefile dan csv di folder terpisah.

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

Semoga ini membantu!


2
Jawaban yang bagus - Anda memiliki semuanya di sana !. Satu pertanyaan jika Anda tidak keberatan: Anda masih harus menambahkan / menghapus layer di QgsMapLayerRegistry bahkan jika Anda melakukan sesuatu dari konsol python?
nickves

1
@nickves - Haha, terima kasih banyak sobat! Hmm saya mungkin tidak perlu menambah / menghapus layer (saya yakin skrip dapat dikurangi secara dramatis). Saya bukan ahli tetapi saya akan mengujinya nanti dan akan menghubungi Anda. Kecuali jika Anda dapat memberikan skrip yang jauh lebih rapi dalam hal ini Anda harus mempostingnya sebagai jawaban, saya akan membesarkannya :)
Joseph

@nickves - Sekali lagi terima kasih atas saran Anda! Kode telah diedit untuk menghindari menambah / menghapus lapisan kedua kalinya :)
Joseph

@RaquelRibeiro - Selamat datang! Senang itu membantu :)
Joseph

@ Joseph boleh saya tanya sesuatu lagi? Pada baris 14 & 15, angka-angka: 0, 10, 10 menentukan dengan tepat apa? (koordinat output memiliki terlalu banyak nol di sebelah kanan dan saya ingin meminimalkannya)
Raquel Ribeiro

8

Solusi cepat untuk mengubah file yang dipisahkan oleh ruang yang berisi "lon lat" di WGS84 menjadi UTM33N tetapi Anda tidak mendapatkan data lain:

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

Itu bekerja dan mempertahankan urutan data jadi mungkin loop lain menggunakan mis. Awk untuk menggabungkan data deskriptif dengan koordinat?

Edit. Karena komentar berantakan yang saya buat di bawah ini, saya akan mengedit jawabannya di sini.

Script berikut harus melakukan pekerjaan membaca beberapa file csv, menambahkan kolom koordinat baru untuk setiap 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

Pada OSX Anda harus menginstal versi sed (2009) terbaru dan menggunakan baris pertama tanpa komentar dalam loop. Untuk Linux beri komentar pertama dan gunakan yang kedua. Sesuaikan -F " "sesuai dengan format pemisah dalam file csv Anda misalnya -F ","untuk dipisahkan dengan koma. Perhatikan juga bahwa transformasi ketinggian adalah ke ellipsoid, bukan geoid, jadi pastikan untuk mengubah ketinggian yang sesuai.


Saya baru ingat melakukan sesuatu yang serupa beberapa waktu lalu dan memposting solusi ke blog saya. Ini ditulis untuk Mac tetapi berbasis bash. Perbedaan terbesar adalah masalah dengan OS X, yang saya tangani di akhir posting: mercergeoinfo.blogspot.se/2014/01/…
mercergeoinfo

Komentar terakhir agak berantakan. Gunakan baris ini di skrip bash di atas untuk mengulang semua 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}Ganti / usr / local / sed hanya dengan sed jika Anda tidak menggunakan OSX. Ini tidak ideal jika file csv Anda dipisahkan oleh ruang, seperti yang diasumsikan di atas, tetapi berfungsi. Jika Anda memiliki koma yang terpisah, maka ubah -F " "ke-F ","
mercergeoinfo

Saya heran mengapa kode yang diperbarui dalam komentar dan Anda tidak memperbarui jawaban Anda di atas. Kode dalam komentar sangat sulit dibaca. Apakah Anda melihat tautan edit di bawah jawaban Anda?
Miro

Yup, tapi itu bukan pembaruan, lebih seperti tambahan. Sangat berantakan, saya setuju. Saya kira saya harus memperbarui jawaban aslinya. Terima kasih
mercergeoinfo

7

Menggunakan qgis atau bahkan OGR adalah berlebihan untuk ini.
Gunakan pyproj( https://pypi.python.org/pypi/pyproj ) dikombinasikan dengan penulis python csv dan beberapa trik pustaka standar. Anda tidak perlu menginstal apa pun selain pyprojuntuk ini!

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

Saya menyadari poster itu cukup berpengalaman dengan python. Saya tidak secara teratur menggunakan QGIS, jadi bisakah seseorang yang lebih berpengalaman dengan platform itu menjelaskan di mana python diinstal? Poster harus membuat ini skrip mandiri dan mungkin menjalankannya dari IDLE. Saya tidak memiliki instalasi saat ini, jadi saya tidak tahu apakah pyprojperlu diinstal secara terpisah untuk poster, atau sudah ada di sana.
blord-castillo

1
tidak pernah menggunakan fungsi parsial sebelumnya. Akan lakukan mulai sekarang. +1
nickes

4

Anda tidak perlu python. Cukup gunakan baris perintah dan ogr2ogr. Dalam kasus Anda yang paling penting adalah parameter -t_srs srs_def.

Ini sudah dijelaskan dalam jawaban ini untuk Bagaimana saya bisa mengonversi file excel dengan kolom x, y ke shapefile?

PEMBARUAN Saya tidak punya waktu untuk menulis kode lengkap kepada Anda. Tetapi masalahnya adalah bahwa itu membutuhkan kode lebih sedikit di python daripada yang Anda pikirkan.

Masalah utama Anda adalah bekerja dengan file csv tidak senyaman menggunakan shapefile. Jadi, pertama-tama Anda perlu mengkonversi csv ke bentuk yang membutuhkan file VRT. Ini dijelaskan di tautan pertama. Di sini Anda perlu menulis perulangan skrip python melalui file Anda yang secara otomatis menghasilkan file vrt.

Ini adalah skrip yang saya gunakan sendiri. Anda harus menguji apakah itu cocok untuk Anda. Saya sudah memasukkan konversi dari WGS 84 ke 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)

Anda perlu menyesuaikan parameter untuk Nama bidang , src , x dan y sesuai dengan file csv Anda.

UPDATE2

Setelah berpikir, saya bertanya pada diri sendiri mengapa Anda ingin menggunakan QGIS sama sekali? Anda bisa menggunakan skrip python seperti ini untuk secara langsung mengkonversi koordinat Anda dari WGS ke UTM. Dalam hal ini adalah csv terbuka sederhana, baca koordinat, ubah koordinat dan simpan ke file baru.


saya pikir ini bukan yang saya cari ... Saya memiliki hampir 500 file csv (koordinat wgs84) dan ini yang ingin saya lakukan: 1. Impor semua file csv sekaligus ke q gis 2. proyeksikan mereka 3. ekspor mereka ke file csv (lagi) tetapi dengan koordinat yang berbeda (konversi ke utm33N)
Raquel Ribeiro

saya pikir saya perlu proses batch atau semacamnya untuk melakukannya ...
Raquel Ribeiro

4
tetapi mengapa Anda ingin melakukan itu? 1. Anda dapat melakukan hal yang sama (apa yang Anda gambarkan) dari baris perintah tanpa qgis. 2. Anda dapat melakukan ini dalam mode batch. 3. dalam python hampir sama. Anda juga akan menggunakan ogr2ogr
Generic Wevers

2
"Cukup" menggunakan baris perintah benar-benar bukan jawaban. Baris perintah tidak pernah mudah digunakan jika Anda tidak tahu bagaimana melakukannya. Dan saya benar-benar tidak dapat menemukan solusi dalam jawaban yang ditautkan. Mengapa tidak memberikan contoh kepada orang miskin saja dengan ogr2ogr, dan semuanya akan baik-baik saja?
Bernd V.

1
ok, 1. Anda dapat membaca gis.stackexchange.com/help/how-to-ask . setelah itu dan 5 menit dari googleing Anda akan mengakui, bahwa pertanyaannya sangat kurang diteliti dan dapat diselesaikan dengan jawaban yang sudah diberikan. 2. Jika masih tidak bisa diselesaikan, saya kira semua orang akan senang membantu. tetapi karena saya orang yang baik, saya akan memberikan beberapa petunjuk lagi.
Generic Wevers
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.