一、前言
工作中经常用到GIS数据,需要将数据可视化的呈现,分发给其他用户。使用专业的地理信息软件,比如ArcGis,Qgis等软件太重了,而且不好分发。所以经常使用Python的Folium库,可以方便的将GIS文件转成可视化网页。
因为经常要转换不同类型的数据,更改部分代码,于是萌生了能不能把这部分经常用到的Python代码,做个小工具,方便工作中使用。于是花了半个月的时间学习了Pyside6,做个GUI界面。
二、做了啥
1.小工具:
先看看编译后的工具界面
2.举个用例
先准备好要可视化的Excel文件,必须包含带地理坐标信息的字段。
点击工具添加文件,设置图层名称,选择坐标字段。
如果有多个要叠加显示的,继续添加表格。最多支持3个文件。
点击开始可视化,选择保存路径。结束!
看看结果:
三、上代码
通过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了吧。分享给有用的到朋友。