如何使用QGIS轻松高效地重新投影500个CSV文件?


11

我知道,我的问题类似于该网站上的一些旧问题。

我有很多CSV文件(地理坐标)要导入到qgis(然后进行转换),通常的方法不是最好的方法(太长)。

我有将近500个CSV文件(wgs84坐标),这就是我想要做的:

  1. 一次将所有CSV文件导入QGIS
  2. 投射他们
  3. 再次将它们导出到CSV文件中,但坐标不同(转换为UTM33N)

我试图了解如何使用python控制台,但我没有继续:(

谁能向我解释如何逐步实现它?


请参阅下面的答案。该问题已得到解决和解释
通用Wevers 2015年

2
为什么要复制带有标记的副本?如果您考虑他/她的粗体,也许OP试图学习pyqgis以及如何使用python。
尼克,2015年

请指定您的问题。您是否不想将它们手动加载到QGIS中?是否要将它们转换为另一种格式?你到底是什么问题?
bugmenot123

1.将所有文件通过一个进程导入到qgis 2.投影它们3.将所有文件再次以csv格式导出,但以utm坐标导出
Raquel Ribeiro

cat * .csv> one_file.csv(或Windows等效的任何文件)会将您的所有csv文件合并为一个。500确实不是一个很大的数字:-)
John Powell

Answers:


15

如果要从QGIS中的Python控制台重新投影csv文件,则可以使用以下脚本。您只需要更改注释中提到的三个路径即可。

本质上,该脚本将您的csv文件作为shapefile导入QGIS(假设您的几何字段命名为XY)。然后,它使用“ 处理工具箱”中qgis:reprojectlayerqgis:fieldcalculator算法重新投影并使用新坐标更新和字段。然后,将它们保存在文件夹中,并在您指定的路径中将它们转换为csv文件。因此,最后,您在单独的文件夹中更新了shapefile和csv文件。XY

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

希望这可以帮助!


2
好的答案-一切都在这里!如果您不介意的话,一个问题是:即使从python控制台执行操作,仍然必须在QgsMapLayerRegistry处添加/删除图层?
尼克

1
@nickves-哈哈,非常感谢哥们!嗯,我可能不必添加/删除图层(我敢肯定脚本可以大大减少)。我不是专家,但我稍后会对其进行测试并与您联系。除非您可以提供更为整洁的脚本,否则您应该将其发布为答案,否则我会投票赞成它:)
Joseph

@nickves-再次感谢您的建议伙伴!已对代码进行了编辑,以避免再次添加/删除图层:)
Joseph

@RaquelRibeiro-非常欢迎!很高兴这很有帮助:)
约瑟夫(Joseph)

@约瑟夫,我可以再问一遍吗?在第14和15行,数字:0、10、10定义了什么?(输出坐标的右边有太多零,我想将其最小化)
Raquel Ribeiro

8

一种将WGS84中包含“ lon lat”的空格分隔的文件转换为UTM33N的快速解决方案,但您没有得到任何其他数据:

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

那行得通,并且保留了数据的顺序,所以可能是另一个循环,例如使用awk将描述性数据与坐标相结合?

编辑。由于我在下面做出的混乱评论,因此我将在此处编辑答案。

以下脚本应能够读取多个csv文件,并向每个文件添加新的坐标列。

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

在OSX上,您将需要安装sed的最新版本(2009),并在循环中使用第一行(未注释)。对于Linux,请注释掉第一个,然后使用第二个。-F " "根据csv文件中分隔符的格式调整,例如,-F ","以逗号分隔。还要注意,高程变换是针对椭圆体而不是大地水准面,因此请确保相应地变换高度。


我刚刚记得做过类似的事情,并在我的博客中发布了解决方案。它是为Mac编写的,但基于bash。最大的区别是使用sed在OS X,这是我在帖子的结尾处理这个问题:mercergeoinfo.blogspot.se/2014/01/...
mercergeoinfo

最后的评论有点混乱。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}如果您不在OSX上,请在上面的bash脚本中使用此行来循环浏览所有文件,将/ usr / local / sed替换为sed。如果您的csv文件以空格分隔(如上一行所假设的那样),则不是理想选择,但它可以工作。如果您用逗号分隔-F " "-F ","
则更

我想知道为什么注释中更新了代码,而您没有更新上面的答案。注释中的代码确实很难阅读。您在答案下方看到编辑链接了吗?
米罗2015年

是的,但这并不是真正的更新,更像是附加更新。非常混乱,我同意。我想我应该更新原始答案。谢谢
mercergeoinfo

7

为此,使用qgis甚至OGR是过大的。
pyprojhttps://pypi.python.org/pypi/pyproj)与python csv writer和一些标准库技巧结合使用。您无需为此安装任何其他东西pyproj

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

我意识到海报是Python所没有的。我不经常使用QGIS,所以对这个平台有更多经验的人可以解释python的安装位置吗?发布者应将其设为独立脚本,并可能从IDLE运行它。我没有当前安装,所以我不知道是否pyproj需要为海报单独安装,或者已经安装了。
blord-castillo

1
以前从未使用过部分函数。从现在开始。+1
尼克

4

您不需要python。只需使用命令行和ogr2ogr。对于您而言,最重要的是-t_srs srs_def参数。

这已在“ 如何将带有x,y列的excel文件转换为shapefile”的答案中进行了解释

更新 我没有时间为您编写完整的代码。但是问题是,它在python中需要的代码比您想象的要多。

您的主要问题是使用csv文件不如使用shapefile舒适。因此,您首先需要将csv转换为需要VRT文件的形状。在第一个链接中对此进行了解释。在这里,您将需要编写一个python脚本循环遍历您的文件,从而自动生成vrt文件。

这是我自己使用的脚本。您必须测试它是否适合您。我已经包括了从WGS 84到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)

您需要根据csv文件调整Field namesrcxy的参数。

更新2

经过一番思考,我问自己为什么要使用QGIS?您可以使用像这样的python脚本直接将坐标从WGS转换为UTM。在这种情况下,它是一个简单的打开的csv,可以读取坐标,转换坐标并将其保存到新文件中。


我认为这不是我要寻找的...我有将近500个csv文件(wgs84坐标),这就是我想要做的:1.将所有csv文件一次导入到q gis 2.投影它们3。再次将它们导出到csv文件中,但坐标不同(转换为utm33N)
拉奎尔·里贝罗

我认为我需要类似的批处理流程或类似的东西才能做到这一点
Raquel Ribeiro

4
但是为什么要这么做呢?1.您可以在没有qgis的情况下从命令行执行相同的操作(您所描述的操作)。2.您可以在批处理模式下执行此操作。3.在python中几乎是一样的。你也将使用ogr2ogr
通用Wevers

2
使用命令行“简单”确实不是答案。如果您不知道如何操作,则命令行永远都不容易使用。而且我真的在链接的答案中找不到解决方案。为什么不只给穷人一个例子,用ogr2​​ogr做批处理,一切都会好起来的?
Bernd V.

1
好的,1.您可以阅读gis.stackexchange.com/help/how-to-ask。在那之后的5分钟内,您会承认,该问题的研究非常匮乏,可以通过已经给出的答案来解决。2.如果仍然无法解决,我想每个人都会很乐意提供帮助。但由于我是一个好人,因此我会提供更多提示。
通用Wevers,2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.