使用pyside6制作的小工具(几何文件转成可视化HTML)

1,290 阅读4分钟

一、前言

工作中经常用到GIS数据,需要将数据可视化的呈现,分发给其他用户。使用专业的地理信息软件,比如ArcGis,Qgis等软件太重了,而且不好分发。所以经常使用Python的Folium库,可以方便的将GIS文件转成可视化网页。

因为经常要转换不同类型的数据,更改部分代码,于是萌生了能不能把这部分经常用到的Python代码,做个小工具,方便工作中使用。于是花了半个月的时间学习了Pyside6,做个GUI界面。

二、做了啥

1.小工具:

先看看编译后的工具界面 image.png

2.举个用例

先准备好要可视化的Excel文件,必须包含带地理坐标信息的字段。
image.png 点击工具添加文件,设置图层名称,选择坐标字段。 image.png

如果有多个要叠加显示的,继续添加表格。最多支持3个文件。 image.png 点击开始可视化,选择保存路径。结束! 看看结果:

image.png

三、上代码

通过pyside6-designer设计主界面,在python中定义主界面类,以及添加文件、删除文件、导出文件的插槽方法。

class MyWidget(QtWidgets.QWidget):
def __init__(self):
    super().__init__()
    
    self.ui = Ui_Form()#实例化UI对象
    self.ui.setupUi(self)#初始化
    
    self.setWindowTitle("地理表格转可视化网页")
    
    header = CheckBoxHeader()
    self.ui.tableWidget.setHorizontalHeader(header) # 设置头复选框
    header.select_all_clicked.connect(header.change_state)  # 行表头复选框单击信号与槽
    self.ui.tableWidget.setSelectionBehavior(QAbstractItemView.SelectRows)  # 设置整行选中
    
    self.ui.tableWidget.setColumnWidth(0, 421)
    self.ui.tableWidget.setColumnWidth(1, 121)
    self.ui.tableWidget.setColumnWidth(2, 121)
    
@QtCore.Slot()#槽函数用它装饰
def openDialog(self): 
   add_dialog = AddDialogWidget()
   add_dialog.setWindowTitle("选择Excel文件") 
   
   
   if add_dialog.exec():
       
       if add_dialog.filename != '' and add_dialog.layername != '':
           filename=QTableWidgetItem(add_dialog.filename)
           layername=QTableWidgetItem(add_dialog.layername)
           geocol=QTableWidgetItem(add_dialog.geocol)
           
           rowIndex=self.ui.tableWidget.rowCount()
           if rowIndex < 3:
               self.ui.tableWidget.setRowCount(rowIndex+1)
               self.ui.tableWidget.setItem(rowIndex, 0, filename)
               self.ui.tableWidget.item(rowIndex, 0).setTextAlignment(Qt.AlignCenter | Qt.AlignCenter)
               self.ui.tableWidget.setItem(rowIndex, 1, layername)
               self.ui.tableWidget.item(rowIndex, 1).setTextAlignment(Qt.AlignCenter | Qt.AlignCenter)
               self.ui.tableWidget.setItem(rowIndex, 2, geocol)
               self.ui.tableWidget.item(rowIndex, 2).setTextAlignment(Qt.AlignCenter | Qt.AlignCenter)
          
               checkbox=QCheckBox()
               all_header_checkbox.append(checkbox) 
               self.ui.tableWidget.setCellWidget(rowIndex, 0, checkbox)
               
               self.ui.tableWidget.show()
           else:
               QMessageBox.critical(add_dialog, '错误', '最多支持3个文件!')
       else:
           QMessageBox.warning(add_dialog, '警告', '必须选择带几何坐标字段的文件,并设置图层名!')
def deleteCheck(self):
    row_box_list = []
    # 获取选中数据
    for i in range(self.ui.tableWidget.rowCount()):
        print(i)
        try:
            if self.ui.tableWidget.cellWidget(i, 0).isChecked() is True:
                row_box_list.append(i)
                row_box_list.sort(reverse=True)  # 将数据进行降序
                
        except:
            continue
    for j in row_box_list:
        print(-1*j)
        self.ui.tableWidget.removeRow(j)  # 删除选中行数据
        all_header_checkbox.pop(j)  # 重新构建check box列表

def excelToHtml(self): #表格输出到html可视化
    rowIndex=self.ui.tableWidget.rowCount()
    if rowIndex < 1:QMessageBox.information(self, '信息', '请先添加文件!')
    else : 
        try:
            filename, _ = QFileDialog.getSaveFileName(self, "Save File", ",/","网页文件(*.html)")
            if filename:
                # colIndex=self.ui.tableWidget.columnCount()
                url1 ='http://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}'
                url2 = "http://webst04.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}"
                locations=[39.97279,116.44772]
                
                colors=['orange','gray','purple']
            
                for i in range(rowIndex):
                    x=self.ui.tableWidget.item(i,0).text()
                    y=self.ui.tableWidget.item(i,1).text()
                    z=self.ui.tableWidget.item(i,2).text()
               
                    df=pd.read_excel(x)
                    df[z]=df[z].apply(wkt.loads)
                    gdf=gpd.GeoDataFrame(df,geometry=z,crs="EPSG:4326")
                    li=list(df.columns)
                    li.remove(z)
                    
            
                    #获取第一个表格中地理字段中第一行图形的坐标
                    if i==0:
                        box=gdf[z][0].bounds
                        y=(box[1]+box[3])/2
                        x=(box[0]+box[2])/2
                        locations=[y,x]
                
                        m = folium.Map(location=locations, zoom_start = 14, tiles = None, prefer_canvas=True)
                    
                    layer = folium.FeatureGroup(y,show=True).add_to(m) 
                    
                    #设置显示样式并
                    if gdf.geom_type[0] == 'Point' or gdf.geom_type[0] == 'MultiPoint':
                        l=folium.GeoJson(
                            gdf,
                            marker=folium.CircleMarker(radius=3,color='blue',fill=True,fill_color="#3186cc"),
                            tooltip=folium.GeoJsonTooltip(fields=li, localize=True)
                        ).add_to(layer)
                        l.add_to(layer)
                    elif gdf.geom_type[0] == 'Polygon' or gdf.geom_type[0] == 'MultiPolygon':
                        style1={'fillOpacity': 0.1,
                                        'weight': 2,
                                        'fillColor': colors[i],
                                        'color':'red'
                                        } 
                        l=folium.GeoJson(
                            gdf,
                            style_function=lambda x:style1,
                            tooltip=folium.GeoJsonTooltip(fields=li, localize=True)
                        )
                        l.add_to(layer)
                        
                    elif gdf.geom_type[0] == 'LineString' or gdf.geom_type[0] == 'MultiLineString':
                        style2 ={'fillOpacity': 0.2,
                                         'weight': 2,
                                         'fillColor': colors[i],
                                         'color':'green'
                                         }
                        l=folium.GeoJson(
                            gdf,
                            style_function=lambda x:style2,
                            tooltip=folium.GeoJsonTooltip(fields=li, localize=True)
                        )
                        l.add_to(layer)
                    
                    
                folium.TileLayer(tiles = url1, name = "高德地图", attr='amap', control = True,show=True).add_to(m)
                folium.TileLayer(tiles = url2, name = "高德卫星", attr='gsitemap', control = True).add_to(m)
                folium.LayerControl(position='topleft', collapsed=False).add_to(m)
                m.add_child(MeasureControl())
                m.save(filename)
                QMessageBox.information(self, '信息', '可视化网页将转换到_'+filename+'_找到文件用网页打开即可查看地图!')
        except:
            QMessageBox.critical(self, '错误', '请检查文件几何坐标字段是否设置正确!')
            
            

定义弹窗界面类,以及弹窗中的文本框,下拉框内容。

class AddDialogWidget(QDialog):
def **init**(self, parent=None):
super().**init**(parent)
self.ui = Ui\_Dialog()#实例化UI对象
self.ui.setupUi(self)#初始化
@QtCore.Slot()
def openFile(self):
    directory = QFileDialog.getOpenFileName(self, "Find Files", ",/","表格excel文件(*.xlsx *.xls)")
    
    if directory:
        self.ui.lineEdit_file.setText(directory[0])
        
def getCol(self):
    data=pd.read_excel(self.ui.lineEdit_file.text())
    self.ui.comboBox_geoCol.addItems(data.columns.values)
        
@property
def filename(self):
    return self.ui.lineEdit_file.text()

@property
def layername(self):
    return self.ui.lineEdit_layer.text()

@property
def geocol(self):
    return self.ui.comboBox_geoCol.currentText()

主界面表格tableWidget第一行前设置复选框。自定义全选信号。

class CheckBoxHeader(QHeaderView):
"""自定义表头类"""
\# 自定义 复选框全选信号
select\_all\_clicked = Signal(bool)
\# 这4个变量控制列头复选框的样式,位置以及大小
\_x\_offset = 0
\_y\_offset = 0
\_width = 20
\_height = 20
    def __init__(self, orientation=Qt.Horizontal, parent=None):
        super(CheckBoxHeader, self).__init__(orientation, parent)
        self.isOn = False
        self.setStyleSheet("min-height:36px")

    def paintSection(self, painter, rect, logicalIndex):
        painter.save()
        super(CheckBoxHeader, self).paintSection(painter, rect, logicalIndex)
        painter.restore()

        self._y_offset = int((rect.height() - self._width) / 2.)

        if logicalIndex == 0:
            option = QStyleOptionButton()
            option.rect = QRect(rect.x() + self._x_offset, rect.y() + self._y_offset, self._width, self._height)
            option.state = QStyle.State_Enabled | QStyle.State_Active
            if self.isOn:
                option.state |= QStyle.State_On
            else:
                option.state |= QStyle.State_Off
            self.style().drawControl(QStyle.CE_CheckBox, option, painter)

    def mousePressEvent(self, event):
        index = self.logicalIndexAt(event.pos())
        if 0 == index:
            x = self.sectionPosition(index)
            if x + self._x_offset < event.pos().x() < x + self._x_offset + self._width and self._y_offset < event.pos().y() < self._y_offset + self._height:
                if self.isOn:
                    self.isOn = False
                else:
                    self.isOn = True
                    # 当用户点击了行表头复选框,发射 自定义信号 select_all_clicked()
                self.select_all_clicked.emit(self.isOn)

                self.updateSection(0)
        super(CheckBoxHeader, self).mousePressEvent(event)

    # 自定义信号 select_all_clicked 的槽方法
    def change_state(self, isOn):
        # 如果行表头复选框为勾选状态
        if isOn:
            # 将所有的复选框都设为勾选状态
            for i in all_header_checkbox:
                i.setCheckState(Qt.Checked)
        else:
            for i in all_header_checkbox:
                i.setCheckState(Qt.Unchecked)

if **name** == "**main**":
app = QtWidgets.QApplication(sys.argv)
widget = MyWidget()
widget.show()
sys.exit(app.exec())\`

四、小工具链接

第一次写GUI界面,自己反复边测边改,应该是没有卡点BUG了吧。分享给有用的到朋友。

下载链接,提取码:8888