python结合ogr2ogr之地理数据格式转换-1
前面寫過一篇公眾號文章,當時為了能將矢量CAD數據轉換成kml,找到了一個開源的.net庫netdxf,該庫支持dxf文件直接讀寫,方便快捷:
DXF轉KML-1http://mp.weixin.qq.com/s?__biz=MzIwMDc1NjA0NQ==&mid=2247483785&idx=1&sn=a2d64db37b1a0b1024ba386e84334198&chksm=96f91faba18e96bd63444cb155fee0bb2bcbb06614e1f31abd7b5eeb96673c7fd5e6bdc0d7fb&scene=21#wechat_redirect
DXF轉KML-2http://mp.weixin.qq.com/s?__biz=MzIwMDc1NjA0NQ==&mid=2247483789&idx=1&sn=3e0fc1d491508a5f7685d8763720fb0a&chksm=96f91fafa18e96b9c84e1aed05ad3e7aa23c72f758039353b1693e98544f0f4c5e34eafd8a7d&scene=21#wechat_redirect
當時的思路是:使用該庫讀取dxf文件,處理讀取的部分數據:line,polyline,circle等,然后將讀取的坐標進行相應的轉換后再序列化輸出kml,雖然省了讀的功夫,但是寫也比較麻煩,最主要的問題是:
-
要素不全,只轉換了部分線型,但有時候給的圖偏偏不是這幾種線型,還得編輯一下再轉
-
轉換的線型也只輸出了坐標信息,其它信息統統舍去,雖然如圖層、顏色之類的信息可能也沒有用
-
需要有一步就是先將dwg打開另存為dxf
可是地理數據種類很多,有時候又是shapefile文件呢?遇到的時候我先把它轉成dxf再轉換kml,這一波下來,和我直接使用arcgis轉換也沒啥區別了,當時的初衷不是為了省事,不借助那么多平臺嗎?于是我又網上搜了一下,發現了一個很強大的工具ogr2ogr
ogr2ogr的安裝和使用http://mp.weixin.qq.com/s?__biz=MzIwMDc1NjA0NQ==&mid=2247483802&idx=1&sn=4cec448d32b45affa9b59970a6cefed5&chksm=96f91fb8a18e96ae3261d021a3ae019cf17395ef56cb8e2a621b02d6ae49f25f07234ac8863d&scene=21#wechat_redirect
該工具支持幾十種矢量和柵格數據之間的轉換,更可怕的是,它還能進行坐標轉換。可是它需要用python調用,于是前面也學習了一下python的安裝和開發環境布置,然后就放下了。因為python從來沒學過,只知道它很吊很強大。因為最終我是要制作一個桌面版的exe的,所以我需要解決幾個問題:
1、窗體設計
2、文件讀取與轉換
3、打包成exe
python的窗體程序不像C#那么簡單,它不是自帶的,需要安裝別的模塊,有好多模塊提供了這個功能,最后我選擇了PyQt5,安裝方法就不贅述了,界面如下:
創建一個新窗體
這就和別的Form很像了,也是控件拖拽的方式,根據需要我設計了如下界面:
選擇一個要轉換的文件類型,瀏覽選擇對應的文件,然后選擇一個輸出的文件類型,瀏覽選擇輸出文件,選擇對應的坐標系,坐標系是根據EPSG來控制的,主要是因為ogr2ogr支持的就是這種模式,這意味著,它是可以在不同橢球之間進行轉換的,詳細的我們用得到的EPSG對照表如下:
EPGS對照表-CGCS2000http://mp.weixin.qq.com/s?__biz=MzIwMDc1NjA0NQ==&mid=2247483807&idx=1&sn=546bf94b91adc62ff42287de622c6c12&chksm=96f91fbda18e96ab77f202fb0be74f13c82ddada64a227c1e4c15577279876c51947522c3c0e&scene=21#wechat_redirect
這個表當時寫的時候是從EXCEL里拉出來的,有些錯誤,不過應該能看得出來。
下面就要貽笑大方之家了,因為python真是小白
窗體和調用的分離
使用PyQt5生成了一個*.ui文件,因為我在vscode里開發,在vscode里安裝的PyQt5,所以可以直接將*.ui轉成.py文件,但是設計的窗體難免不完美需要改,如果調用窗體的部分和窗體轉換的py是一個文件,下次改變的窗體重新生成py就會覆蓋調用部分的代碼,所以要調用和窗體代碼分離。
# -*- coding: utf-8 -*-# Form implementation generated from reading ui file 'g:\python\learting\shapefile\ogr.ui' # # Created by: PyQt5 UI code generator 5.15.6 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing.from PyQt5 import QtCore, QtGui, QtWidgetsclass Ui_MainWindow(object):def setupUi(self, MainWindow):MainWindow.setObjectName("MainWindow")MainWindow.resize(509, 201)MainWindow.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly)self.centralwidget = QtWidgets.QWidget(MainWindow)self.centralwidget.setObjectName("centralwidget")self.label = QtWidgets.QLabel(self.centralwidget)self.label.setGeometry(QtCore.QRect(30, 10, 54, 12))self.label.setObjectName("label")self.label_2 = QtWidgets.QLabel(self.centralwidget)self.label_2.setGeometry(QtCore.QRect(20, 40, 54, 12))self.label_2.setObjectName("label_2")self.textEdit = QtWidgets.QTextEdit(self.centralwidget)self.textEdit.setGeometry(QtCore.QRect(90, 10, 221, 21))self.textEdit.setObjectName("textEdit")self.textEdit_2 = QtWidgets.QTextEdit(self.centralwidget)self.textEdit_2.setGeometry(QtCore.QRect(90, 40, 221, 21))self.textEdit_2.setObjectName("textEdit_2")self.pushButton = QtWidgets.QPushButton(self.centralwidget)self.pushButton.setGeometry(QtCore.QRect(430, 10, 75, 23))self.pushButton.setObjectName("pushButton")self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)self.pushButton_2.setGeometry(QtCore.QRect(430, 40, 75, 23))self.pushButton_2.setObjectName("pushButton_2")self.comboBox = QtWidgets.QComboBox(self.centralwidget)self.comboBox.setGeometry(QtCore.QRect(310, 10, 111, 22))self.comboBox.setEditable(True)self.comboBox.setObjectName("comboBox")self.comboBox.addItem("")self.comboBox.addItem("")self.comboBox.addItem("")self.comboBox.addItem("")self.comboBox_2 = QtWidgets.QComboBox(self.centralwidget)self.comboBox_2.setGeometry(QtCore.QRect(310, 40, 111, 22))self.comboBox_2.setTabletTracking(False)self.comboBox_2.setEditable(True)self.comboBox_2.setObjectName("comboBox_2")self.comboBox_2.addItem("")self.comboBox_2.addItem("")self.comboBox_2.addItem("")self.comboBox_2.addItem("")self.label_3 = QtWidgets.QLabel(self.centralwidget)self.label_3.setGeometry(QtCore.QRect(20, 70, 91, 16))self.label_3.setObjectName("label_3")self.comboBox_3 = QtWidgets.QComboBox(self.centralwidget)self.comboBox_3.setGeometry(QtCore.QRect(140, 70, 361, 22))sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)sizePolicy.setHorizontalStretch(0)sizePolicy.setVerticalStretch(0)sizePolicy.setHeightForWidth(self.comboBox_3.sizePolicy().hasHeightForWidth())self.comboBox_3.setSizePolicy(sizePolicy)font = QtGui.QFont()font.setFamily("楷體")font.setPointSize(8)self.comboBox_3.setFont(font)self.comboBox_3.setObjectName("comboBox_3")self.comboBox_3.addItem("")self.comboBox_3.addItem("")self.comboBox_3.addItem("")self.label_4 = QtWidgets.QLabel(self.centralwidget)self.label_4.setGeometry(QtCore.QRect(20, 100, 91, 16))self.label_4.setObjectName("label_4")self.comboBox_4 = QtWidgets.QComboBox(self.centralwidget)self.comboBox_4.setGeometry(QtCore.QRect(140, 100, 361, 21))sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)sizePolicy.setHorizontalStretch(0)sizePolicy.setVerticalStretch(0)sizePolicy.setHeightForWidth(self.comboBox_4.sizePolicy().hasHeightForWidth())self.comboBox_4.setSizePolicy(sizePolicy)font = QtGui.QFont()font.setFamily("楷體")font.setPointSize(8)self.comboBox_4.setFont(font)self.comboBox_4.setCursor(QtGui.QCursor(QtCore.Qt.SizeVerCursor))self.comboBox_4.setFocusPolicy(QtCore.Qt.TabFocus)self.comboBox_4.setAcceptDrops(False)self.comboBox_4.setLayoutDirection(QtCore.Qt.LeftToRight)self.comboBox_4.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToMinimumContentsLength)self.comboBox_4.setObjectName("comboBox_4")self.comboBox_4.addItem("")self.comboBox_4.addItem("")self.comboBox_4.addItem("")self.pushButton_3 = QtWidgets.QPushButton(self.centralwidget)self.pushButton_3.setGeometry(QtCore.QRect(230, 130, 75, 23))self.pushButton_3.setObjectName("pushButton_3")self.pushButton_4 = QtWidgets.QPushButton(self.centralwidget)self.pushButton_4.setGeometry(QtCore.QRect(310, 130, 75, 23))self.pushButton_4.setObjectName("pushButton_4")self.pushButton_5 = QtWidgets.QPushButton(self.centralwidget)self.pushButton_5.setGeometry(QtCore.QRect(390, 130, 75, 23))self.pushButton_5.setObjectName("pushButton_5")MainWindow.setCentralWidget(self.centralwidget)self.menubar = QtWidgets.QMenuBar(MainWindow)self.menubar.setGeometry(QtCore.QRect(0, 0, 509, 23))self.menubar.setObjectName("menubar")MainWindow.setMenuBar(self.menubar)self.statusbar = QtWidgets.QStatusBar(MainWindow)self.statusbar.setObjectName("statusbar")MainWindow.setStatusBar(self.statusbar)self.retranslateUi(MainWindow)QtCore.QMetaObject.connectSlotsByName(MainWindow)def retranslateUi(self, MainWindow):_translate = QtCore.QCoreApplication.translateMainWindow.setWindowTitle(_translate("MainWindow", "基礎地理數據格式轉換"))self.label.setText(_translate("MainWindow", "源文件:"))self.label_2.setText(_translate("MainWindow", "目標文件:"))self.pushButton.setText(_translate("MainWindow", "瀏覽"))self.pushButton_2.setText(_translate("MainWindow", "瀏覽"))self.comboBox.setItemText(0, _translate("MainWindow", "所有文件(*.*)"))self.comboBox.setItemText(1, _translate("MainWindow", "CAD文件(*.dwg)"))self.comboBox.setItemText(2, _translate("MainWindow", "ESRI Shapefile文件(*.shp)"))self.comboBox.setItemText(3, _translate("MainWindow", "kml文件(*.kml)"))self.comboBox_2.setItemText(0, _translate("MainWindow", "所有文件(*.*)"))self.comboBox_2.setItemText(1, _translate("MainWindow", "CAD文件(*.dwg)"))self.comboBox_2.setItemText(2, _translate("MainWindow", "ESRI Shapefile文件(*.shp)"))self.comboBox_2.setItemText(3, _translate("MainWindow", "kml文件(*.kml)"))self.label_3.setText(_translate("MainWindow", "源文件坐標系"))self.comboBox_3.setItemText(0, _translate("MainWindow", "EPSG:4490 China Geodetic Coordinate System 2000"))self.comboBox_3.setItemText(1, _translate("MainWindow", "EPSG:4527 CGCS2000 / 3-degree Gauss-Kruger zone 39"))self.comboBox_3.setItemText(2, _translate("MainWindow", "EPSG:4548 CGCS2000 / 3-degree Gauss-Kruger CM 117E"))self.label_4.setText(_translate("MainWindow", "<html><head/><body><p>目標文件坐標系</p></body></html>"))self.comboBox_4.setItemText(0, _translate("MainWindow", "EPSG:4490 China Geodetic Coordinate System 2000"))self.comboBox_4.setItemText(1, _translate("MainWindow", "EPSG:4527 CGCS2000 / 3-degree Gauss-Kruger zone 39"))self.comboBox_4.setItemText(2, _translate("MainWindow", "EPSG:4548 CGCS2000 / 3-degree Gauss-Kruger CM 117E"))self.pushButton_3.setText(_translate("MainWindow", "輸出"))self.pushButton_4.setText(_translate("MainWindow", "打開"))self.pushButton_5.setText(_translate("MainWindow", "退出"))窗體調用
python是腳本語言,從頭開始一行一行的執行,不像別的語言都有一個入口,那么怎么調用這個form呢?
class MyWin(QMainWindow, Ui_MainWindow):def __init__(self, parent=None):super(MyWin, self).__init__(parent)self.setupUi(self)self.IniUI()def IniUI(self): if __name__ == '__main__':app = QApplication(sys.argv)mw = MyWin()mw.show()sys.exit(app.exec_())這段是網上抄的:if __name__ == '__main__':相當于確定了程序執行的入口,__name__
在當前文件它的值就等于__main__,作為模塊導入到別的文件,它的值就等于模塊名,也意味著當前文件是主文件了。def __init__(self, parent=None): 是構造函數,self.setupUi(self)應該是窗體文件里對應的初始化模塊,self.IniUI()就是我自己要做的處理了。
信號與槽
控件和代碼如何連接呢?比如我如何確定點擊一下按鈕,能響應打開選擇文件的操作:牽扯到的概念叫:信號與槽
按鈕點擊相當于發射信號,打開文件相當于槽,然后使用connect連接起來,就能執行了,當然每個控件都可能不止發射一種信號,clicked只是pushbutton的一種信號,槽也可以對應好幾個信號,信號可以連接好幾個槽。可以自定義槽,當然也有默認的槽。
調用ogr2ogr
需要調用subprocess.Popen模塊,所以要導入subprocess,當成命令行的方式調用。
from fileinput import filename from pickle import GLOBAL from re import I from matplotlib import type1fontfrom pytest import cmdline from Ui_ogr import Ui_MainWindow from PyQt5.QtWidgets import QMainWindow, QApplication,QFileDialog,QMessageBox import sys from PyQt5.QtCore import QCoreApplication import subprocess infile=""#輸入文件路徑 outfile=""#輸出文件路徑 infileType=""#輸入文件類型 outfileType=""#輸出文件類型 inEPSG=""#輸入EPSG outEPSG=""#輸出EPSG class MyWin(QMainWindow, Ui_MainWindow):def __init__(self, parent=None):super(MyWin, self).__init__(parent)self.setupUi(self)self.IniUI()def IniUI(self):# 添加“打開文件”按鈕的信號和槽。注意getFile函數不加小括號()self.pushButton.clicked.connect(self.GetinType)self.pushButton.clicked.connect(self.getFile)self.pushButton_2.clicked.connect(self.GetoutType)self.pushButton_2.clicked.connect(self.saveNewFile)# self.comboBox.activated.connect(self.GetinType)# self.comboBox_2.activated.connect(self.GetoutType)# self.comboBox_3.activated.connect(self.GetinEPSG)# self.comboBox_4.activated.connect(self.GetoutEPSG)self.pushButton_3.clicked.connect(self.GetinEPSG)self.pushButton_3.clicked.connect(self.GetoutEPSG)self.pushButton_3.clicked.connect(self.TransBegin)self.pushButton_5.clicked.connect(QCoreApplication.instance().quit)def getFile(self):global infileinfile,ft = QFileDialog.getOpenFileName(self,"選擇待轉換的原始文件", "", infileType) # 獲取文件self.textEdit.setText(infile)def saveNewFile(self):global outfileoutfile,tn = QFileDialog.getSaveFileName(self,"選擇存儲的目標文件", "", outfileType) # 獲取文件self.textEdit_2.setText(outfile)def GetinType(self):global infileTypeinfileType=self.comboBox.currentText()def GetoutType(self):global outfileTypeoutfileType=self.comboBox_2.currentText()def GetinEPSG(self):global inEPSGinEPSG=self.comboBox_3.currentText()# self.textEdit.setText(inEPSG)def GetoutEPSG(self):global outEPSGoutEPSG=self.comboBox_4.currentText()def TransBegin(self):if infile=="":QMessageBox.information(self, "提醒", "沒有選擇要轉換的文件")if outfile=="":QMessageBox.information(self, "提醒", "沒有選擇存儲文件的路徑") s=outfileType.find('文件')type=outfileType[0:s]# print(type)scoor=inEPSG[0:9]tcoor=outEPSG[0:9]cmdLine = "ogr2ogr "+"-s_srs "+ scoor +" -t_srs "+ tcoor +" -f \"" + type + "\" \"" + outfile.replace('/', '\\') + "\"" + " \"" + infile.replace('/', '\\') + "\""# print(cmdLine)# print("\r")# 避免出現 ERROR 1: PROJ: proj_create_from_database: Cannot find proj.db# os.environ['PROJ_LIB'] = r'D:\\Python39\\Lib\\site-packages\\osgeo\\data\\proj/'subprocess.Popen(cmdLine, shell=True)QMessageBox.information(self, "結果", "轉換完成!")if __name__ == '__main__':app = QApplication(sys.argv)mw = MyWin()mw.show()sys.exit(app.exec_())簡單測試了下,shapefile轉kml可以,但是CAD文件提示驅動器問題,這個后面還有繼續思考。
如何打包:
安裝pyinstaller
使用命令 pyintstaller --onefile --windowed -*py
--onefile是打包成一個exe,否則的話會將需要依賴的庫獨立出來打包成一個文件夾,這個要看需要了。
--windowed是打包成窗體程序,不要閃小黑框了。
基本過程就是這樣了。
問題:
測試的數據種類有限,有的數據提示轉換成功但是沒有數據,有的轉換不成功但是沒有明顯的提示告知錯誤原因,都是后面還要繼續摸索的地方。
總結
以上是生活随笔為你收集整理的python结合ogr2ogr之地理数据格式转换-1的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: N-S图(盒图)
- 下一篇: 如何避免动态字体Font Texture