Pyqt5實戰—獲取省/市/區矢量數據


    python有兩個用於制作GUI界面的包,twinker和Pyqt5。其中Pyqt5可以結合Qt designer使用,制作界面簡單方便,交互性強,因此筆者打算入門Pyqt5。“紙上得來終覺淺,絕知此事要躬行”。筆者始終奉行這句話,因此筆者擬實踐幾個小例子,從而更快速的入門。

            項目(一)獲取矢量化的省/市/區的shapefile文件

1、本次獲取省/市/區邊界數據來源

    這是一個別人基於百度api寫的一個省市坐標查詢的html代碼。

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
    <style type="text/css">
        body, html,#allmap {width: 100%;height: 100%;overflow: hidden;margin:0;font-family:"微軟雅黑";}
        #panel{
            position:absolute;
            left:5px;
            top:5px;
        }
        #result{
            background: #fff;
            padding:5px;
        }
    </style>
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
    <script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=1XjLLEhZhQNUzd93EjU5nOGQ"></script>
    <title>添加行政區划</title>
</head>
<body>
    <div id="allmap"></div>
    <div id="panel">
        <div>
        <input type="text" id="keyword" value="昆明市"/>
        <input type="button" value="查看范圍" id="commitBtn"/>
        邊界經緯度坐標
        <textarea id="pathStr"></textarea>
        邊界墨卡托坐標
        <textarea id="pathMc"></textarea>
        </div>
        <div id="result">
        </div>
    </div>
</body>
</html>
<script type="text/javascript">
    // 百度地圖API功能
    var map = new BMap.Map("allmap");
    map.centerAndZoom(new BMap.Point(116.403765, 39.914850), 5);
    map.enableScrollWheelZoom();
    var mercatorProjection = map.getMapType().getProjection();
    $("#commitBtn").bind('click', function(){
        getBoundary($("#keyword").val());
    });
    function getBoundary(city){
        var bdary = new BMap.Boundary();
        bdary.get(city, function(rs){       //獲取行政區域
            map.clearOverlays();        //清除地圖覆蓋物
            var count = rs.boundaries.length; //行政區域的點有多少個
            if (count === 0) {
                alert('未能獲取當前輸入行政區域');
                return ;
            }
            var pointArray = [];
            for (var i = 0; i < count; i++) {
                var ply = new BMap.Polygon(rs.boundaries[i], {strokeWeight: 2, strokeColor: "#ff0000"}); //建立多邊形覆蓋物
                map.addOverlay(ply);  //添加覆蓋物
                pointArray = pointArray.concat(ply.getPath());
            }
            var pathStr = "";
            var pathMc = "";
            for (var i = 0; i < pointArray.length; i++) {

                var mc = mercatorProjection.lngLatToPoint(pointArray[i]);
                pathStr += pointArray[i].lng + "," + pointArray[i].lat + ";";
                pathMc += mc.x + "," + mc.y + ";";
            }
            $('#pathStr').html(pathStr);
            $('#pathMc').html(pathMc);
            var ply = new BMap.Polygon(pointArray , {strokeWeight: 2, strokeColor: "#ff0000"}); //建立多邊形覆蓋物
            var bounds = ply.getBounds();
            var ne = bounds.getNorthEast();
            var sw = bounds.getSouthWest();
            var neMc = mercatorProjection.lngLatToPoint(ne);
            var swMc = mercatorProjection.lngLatToPoint(sw);
            var str = "經緯度:左下角,右上角:" + sw.lng + "," + sw.lat + ";" + ne.lng + "," + ne.lat
                                                 + "<br/>墨卡托坐標:左下角,右上角:" + swMc.x + "," + swMc.y + ";" + neMc.x + "," + neMc.y;
            $('#result').html(str);
            console.log(bounds);
            map.setViewport(pointArray);    //調整視野
        });
    }
    //getBoundary('北京');
</script>

                                                              

     我們可以通過這個html頁面,查詢省市的邊界經緯度坐標。眾所周知,邊界都是由眾多點坐標組成的。這里選擇經緯度坐標,拷貝到txt文件中。

2、Point To Polygon

    第二步是本次實驗的核心步驟,即讀取全部點坐標,創建包含這些點坐標lon/lat字段值和geometry的shapefile格式的矢量數據文件,核心部分代碼如下,放在run.py中,負責調用UI界面。

def convert_to_shp(self):
        poi_file = self.lineEdit.text()   #lineEdit/lineEdit_2是兩個文本框的名稱,lineEdit.text讀取輸入的文本信息
        outputfile = self.lineEdit_2.text()
        p_lon =[]
        p_lat =[]
        with open(poi_file,'r') as file_obj:   #讀取點坐標
            lines = file_obj.read()
            lines = str(lines)
            lines = lines.split(';')
            for i in range(len(lines)):
                lon = lines[i].split(',')[0]
                lat = lines[i].split(',')[1]
                p_lon.append(lon)
                p_lat.append(lat)
                
        driver = ogr.GetDriverByName('ESRI Shapefile')
        ds = driver.CreateDataSource(outputfile)    #創建矢量數據集
        srs = osr.SpatialReference()
        srs.ImportFromEPSG(4326)
        
        layer = ds.CreateLayer('tempfile', srs, ogr.wkbPolygon)
        layer.CreateField(ogr.FieldDefn('lat', ogr.OFTReal))  #創建字段
        layer.CreateField(ogr.FieldDefn('lon', ogr.OFTReal))
        ring = ogr.Geometry(ogr.wkbLinearRing)
        feature = ogr.Feature(layer.GetLayerDefn())
        for i in range(len(lines)):
            feature.SetField('lon', p_lon[i])
            feature.SetField('lat', p_lat[i])
            ring.AddPoint(float(p_lon[i]), float(p_lat[i]))
            layer.CreateFeature(feature)  #每次都新增要素到圖層
            
        poly = ogr.Geometry(ogr.wkbPolygon)
        poly.AddGeometry(ring)
        feature.SetGeometry(poly)
        layer.CreateFeature(feature)
        del ds
        
        msg_box = QtWidgets.QMessageBox
        msg_box.information(self,"提醒","已成功生成shp文件。",msg_box.Yes | msg_box.No)  #提示功能完成

    使用Qt designer制作UI界面,使用text box控件來輸入文件路徑和輸出文件名,button控件來執行操作,執行成功則出現一個提示完成的窗口。生成.ui文件后,使用pyuic5 -o first.py firest.ui將.ui轉換成.py文件。

# -*- coding: utf-8 -*-
from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(210, 260, 71, 21))
        self.pushButton.setObjectName("pushButton")
        self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit.setGeometry(QtCore.QRect(180, 90, 161, 21))
        self.lineEdit.setObjectName("lineEdit")
        self.lineEdit_2 = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit_2.setGeometry(QtCore.QRect(180, 170, 161, 21))
        self.lineEdit_2.setObjectName("lineEdit_2")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(110, 90, 51, 21))
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(110, 170, 51, 20))
        self.label_2.setObjectName("label_2")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 18))
        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.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "開始"))
        self.label.setText(_translate("MainWindow", "輸入文件"))
        self.label_2.setText(_translate("MainWindow", "輸出文件名"))

    接着編寫run.py用於調用UI界面,這一步是重中之重,你可以讀不懂ui界面的代碼,但是你必須懂run.py的代碼。

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow
from PyQt5 import QtWidgets
from convert_shp import Ui_MainWindow
from osgeo import ogr, osr
 
class mwindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(mwindow, self).__init__()
        self.setupUi(self)
    
    def convert_to_shp(self):
        pass
    
    
if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = mwindow()
    w.pushButton.clicked.connect(w.convert_to_shp)    #把button和convert_to_shp事件鏈接到一起
    w.show()
    sys.exit(app.exec_())

3、Pyinstaller打包

    做好了run.py、main.py,我們可以用Pyinstaller打包成exe文件。pyinstaller -F demo.py

                                                             

    我們可以用pyinstaller -p a.py -p b.py打包多個文件。打包完成后,打開時遇到了module not found error的報錯,這里參考 這篇博客,在生成的.spec文件中修改hiddenimport列表,之后pyinstaller test.spec重新打包,dist文件夾中也就生成了唯一的exe文件。功能一切正常,唯一遺憾的是打包的exe文件有247mb之多,有點臃腫。我們輸入文件地址,輸出文件名稱,點擊開始,彈出“已經成功生成shp文件”的對話框,回到文件夾發現已經生成了shp/shx/dbf文件,用Arcgis打開矢量數據,如下圖。效果還算不錯。今天的實戰就到這里,下次盡可能將POI爬取功能實現,並用Pyqt5寫出界面。

                                          

Bug調試:

       AttributeError: 'mywindow' object has no attribute 'setCentralWidget'

       https://blog.csdn.net/oMoDao1/article/details/85285642

 

 




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM