Fork me on GitHub

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

 

 



posted @ 2020-05-25 22:20  Rser_ljw  阅读(1352)  评论(0编辑  收藏  举报