198 lines
8.8 KiB
Python
198 lines
8.8 KiB
Python
|
|
import logging # 导入自定义日志工具
|
||
|
|
from qgis._core import QgsScaleBarSettings, QgsLayoutExporter, QgsCoordinateTransform, QgsCoordinateReferenceSystem, \
|
||
|
|
QgsPointXY, QgsGeometry, QgsRectangle
|
||
|
|
|
||
|
|
|
||
|
|
class MapUtils:
|
||
|
|
"""出图工具类(封装地图缩放、文本更新、比例尺调整、导出等功能)"""
|
||
|
|
|
||
|
|
def __init__(self, config, project, layout):
|
||
|
|
self.config = config # 配置参数
|
||
|
|
self.project = project # QGIS项目对象
|
||
|
|
self.layout = layout # 布局对象(用于地图排版)
|
||
|
|
self.imap = layout.itemById("Map") # 获取ID为"Map"的地图项
|
||
|
|
|
||
|
|
# 缩放
|
||
|
|
def Zoom(self, rule, data):
|
||
|
|
logging.info(data) # 记录缩放参数日志
|
||
|
|
zoom = MapZoom(self.project, self.layout, self.imap) # 创建缩放操作对象
|
||
|
|
# 2024 Zoom转换(将数字规则映射为方法名)
|
||
|
|
# NO("10", "不缩放"),
|
||
|
|
# PAN("11", "平移"),
|
||
|
|
# LAYER("12", "单图层"),
|
||
|
|
# M_LAYER("13", "多图层"),
|
||
|
|
# DISTANCE("14", "距离"),
|
||
|
|
# M_LAYER2("15", "按图层合并缩放");
|
||
|
|
if rule == "11":
|
||
|
|
rule = "FlatToCenter" # 平移至中心点
|
||
|
|
elif rule == "12":
|
||
|
|
rule = "Layer" # 单图层缩放
|
||
|
|
elif rule == "13":
|
||
|
|
rule = "LayerIntersect" # 多图层相交缩放
|
||
|
|
elif rule == "14":
|
||
|
|
rule = "CenterDistance" # 中心距离缩放
|
||
|
|
elif rule == "15":
|
||
|
|
rule = "LayerMerged" # 多图层合并缩放
|
||
|
|
else:
|
||
|
|
rule = "FlatToCenter" # 默认平移至中心点
|
||
|
|
eval("zoom." + rule + "(data)") # 动态调用对应缩放方法
|
||
|
|
|
||
|
|
# 文本更新
|
||
|
|
def Update(self, model, key):
|
||
|
|
label = self.layout.itemById(key) # 获取布局中ID为key的文本标签
|
||
|
|
if (label != None):
|
||
|
|
label.setText(model[key]) # 更新标签文本为model中key对应的值
|
||
|
|
|
||
|
|
# 比例尺更新
|
||
|
|
def UpdateScale(self):
|
||
|
|
ScaleBar = self.layout.itemById("ScaleBar") # 获取ID为"ScaleBar"的比例尺控件
|
||
|
|
if ScaleBar == None:
|
||
|
|
logging.warning("比例尺不存在或控件标识不等于 ScaleBar") # 日志警告
|
||
|
|
return
|
||
|
|
# 设置比例尺段大小模式为"适应宽度"
|
||
|
|
ScaleBar.setSegmentSizeMode(QgsScaleBarSettings.SegmentSizeMode.SegmentSizeFitWidth)
|
||
|
|
ScaleBar.setMaximumBarWidth(70) # 最大宽度
|
||
|
|
ScaleBar.setMinimumBarWidth(40) # 最小宽度
|
||
|
|
|
||
|
|
# 布局设置器
|
||
|
|
def QgsLayoutSettings(self):
|
||
|
|
dpi = self.config["qgis"]["exportDpi"] # 设置dpi
|
||
|
|
# 设置多个导出格式
|
||
|
|
settings = {
|
||
|
|
'PDF': QgsLayoutExporter.PdfExportSettings(),
|
||
|
|
'PNG': QgsLayoutExporter.ImageExportSettings(),
|
||
|
|
'JPG': QgsLayoutExporter.ImageExportSettings(),
|
||
|
|
'SVG': QgsLayoutExporter.SvgExportSettings()
|
||
|
|
}
|
||
|
|
|
||
|
|
for img_format in ['PNG', 'JPG']:
|
||
|
|
settings[img_format].dpi = dpi
|
||
|
|
settings['JPG'].jpegQuality = 85
|
||
|
|
|
||
|
|
return settings['PNG']
|
||
|
|
|
||
|
|
# 导出图片
|
||
|
|
def Export(self, path):
|
||
|
|
try:
|
||
|
|
qle = QgsLayoutExporter(self.layout) # 创建布局导出器
|
||
|
|
# 布局设置
|
||
|
|
setting = self.QgsLayoutSettings()
|
||
|
|
# 导出为图片
|
||
|
|
res = qle.exportToImage(path, setting)
|
||
|
|
except Exception as e:
|
||
|
|
raise Exception(f"导出失败... {str(e)}")
|
||
|
|
|
||
|
|
|
||
|
|
class MapZoom:
|
||
|
|
"""地图缩放操作类(封装多种缩放逻辑)"""
|
||
|
|
|
||
|
|
def __init__(self, project, layout, imap):
|
||
|
|
self.project = project # QGIS项目对象
|
||
|
|
self.layout = layout # 布局对象
|
||
|
|
self.imap = imap # 地图项对象
|
||
|
|
|
||
|
|
# 设置坐标系转换
|
||
|
|
def SetSrc(self, crs):
|
||
|
|
qct = QgsCoordinateTransform() # 坐标系转换对象
|
||
|
|
qct.setDestinationCrs(self.imap.crs()) # 目标坐标系为地图当前坐标系
|
||
|
|
if crs == None:
|
||
|
|
qct.setSourceCrs(QgsCoordinateReferenceSystem("EPSG:4326")) # 源坐标系默认WGS84(经纬度)
|
||
|
|
else:
|
||
|
|
qct.setSourceCrs(crs) # 自定义源坐标系
|
||
|
|
return qct
|
||
|
|
|
||
|
|
# 平移至中心点
|
||
|
|
def FlatToCenter(self, data):
|
||
|
|
logging.info("平移至中心点")
|
||
|
|
qct = self.SetSrc(None) # 源坐标系为WGS84
|
||
|
|
point = QgsPointXY(float(data["X"]), float(data["Y"])) # 解析中心点坐标(经纬度)
|
||
|
|
qgm = QgsGeometry.fromWkt(point.asWkt()) # 转换为QGIS几何对象
|
||
|
|
# 坐标转换(从源坐标系到地图坐标系)
|
||
|
|
qgm.transform(qct, QgsCoordinateTransform.TransformDirection.ForwardTransform)
|
||
|
|
cpoint = qgm.asPoint() # 转换后的中心点
|
||
|
|
# 保持当前地图宽度和高度,以新中心点创建矩形范围
|
||
|
|
qr = QgsRectangle.fromCenterAndSize(cpoint, self.imap.extent().width(), self.imap.extent().height())
|
||
|
|
self.imap.zoomToExtent(qr) # 缩放至该范围(实现平移)
|
||
|
|
|
||
|
|
# 中心距离缩放(以中心点为圆心,指定距离为半径缩放)
|
||
|
|
def CenterDistance(self, data):
|
||
|
|
logging.info("中心距离")
|
||
|
|
qct = self.SetSrc(None)
|
||
|
|
point = QgsPointXY(float(data["X"]), float(data["Y"])) # 中心点坐标
|
||
|
|
qgm = QgsGeometry.fromWkt(point.asWkt())
|
||
|
|
qgm.transform(qct, QgsCoordinateTransform.TransformDirection.ForwardTransform)
|
||
|
|
# 缓冲距离(单位:公里,转换为米后缓冲),并扩大10%范围
|
||
|
|
box = qgm.buffer(float(data["value"]) / 1000, 100).boundingBox() # 平面 4326
|
||
|
|
# box = qgm.buffer(float(data["value"])/111,100).boundingBox()#投影坐标系下的缓冲计算
|
||
|
|
self.imap.zoomToExtent(box.buffered(box.width() * 0.1))
|
||
|
|
|
||
|
|
# 按单图层缩放(缩放至指定图层范围)
|
||
|
|
def Layer(self, data):
|
||
|
|
logging.info("按图层缩放")
|
||
|
|
layers = self.project.mapLayersByName(data["value"]) # 根据名称获取图层
|
||
|
|
if len(layers) == 0:
|
||
|
|
logging.warning("图层不存在:" + data["value"]) # 图层不存在时警告
|
||
|
|
return
|
||
|
|
layer = layers[0]
|
||
|
|
if layer.featureCount() == 0.0: # 图层无要素时,默认平移至中心点
|
||
|
|
self.FlatToCenter(data)
|
||
|
|
return
|
||
|
|
|
||
|
|
qct = self.SetSrc(layer.crs()) # 源坐标系为图层坐标系
|
||
|
|
# 将图层范围转换为几何对象
|
||
|
|
qgm = QgsGeometry.fromWkt(layer.extent().asWktPolygon())
|
||
|
|
qgm.transform(qct, QgsCoordinateTransform.TransformDirection.ForwardTransform) # 转换到地图坐标系
|
||
|
|
box = qgm.boundingBox() # 获取范围矩形
|
||
|
|
self.imap.zoomToExtent(box.buffered(box.width() * 0.1)) # 扩大10%范围后缩放
|
||
|
|
|
||
|
|
# 多图层合并缩放(缩放至多个图层的合并范围)
|
||
|
|
def LayerMerged(self, data):
|
||
|
|
logging.info("多图层合并")
|
||
|
|
layers = data["value"].split(",") # 图层名称以逗号分隔
|
||
|
|
box = None
|
||
|
|
for lay in layers:
|
||
|
|
temp = self.project.mapLayersByName(lay)
|
||
|
|
if len(temp) == 0:
|
||
|
|
logging.warning("图层不存在:" + lay)
|
||
|
|
else:
|
||
|
|
layer = temp[0]
|
||
|
|
qct = self.SetSrc(layer.crs())
|
||
|
|
qgm = QgsGeometry.fromWkt(layer.extent().asWktPolygon())
|
||
|
|
qgm.transform(qct, QgsCoordinateTransform.TransformDirection.ForwardTransform)
|
||
|
|
if box == None:
|
||
|
|
box = qgm.boundingBox() # 初始化范围
|
||
|
|
else:
|
||
|
|
box.combineExtentWith(qgm.boundingBox()) # 合并范围
|
||
|
|
if box != None:
|
||
|
|
self.imap.zoomToExtent(box.buffered(box.width() * 0.1)) # 扩大10%范围后缩放
|
||
|
|
|
||
|
|
# 多图层相交缩放(缩放至多个图层的相交范围)
|
||
|
|
def LayerIntersect(self, data):
|
||
|
|
logging.info("多图层相交")
|
||
|
|
layers = data["value"].split(",") # 图层名称以逗号分隔
|
||
|
|
box = None
|
||
|
|
for lay in layers:
|
||
|
|
temp = self.project.mapLayersByName(lay)
|
||
|
|
if len(temp) == 0:
|
||
|
|
logging.warning("图层不存在:" + lay)
|
||
|
|
else:
|
||
|
|
layer = temp[0]
|
||
|
|
qct = self.SetSrc(layer.crs())
|
||
|
|
layer.selectAll() # 选中图层所有要素
|
||
|
|
geom = None
|
||
|
|
# 合并选中要素的几何范围
|
||
|
|
for feature in layer.selectedFeatureIds():
|
||
|
|
if geom == '':
|
||
|
|
geom = layer.getGeometry(feature)
|
||
|
|
else:
|
||
|
|
geom = geom.combine(layer.getGeometry(feature))
|
||
|
|
qgm = QgsGeometry.fromWkt(geom.asWkt())
|
||
|
|
qgm.transform(qct, QgsCoordinateTransform.TransformDirection.ForwardTransform)
|
||
|
|
if box == None:
|
||
|
|
box = qgm.boundingBox() # 初始化范围
|
||
|
|
else:
|
||
|
|
box = box.intersection(qgm) # 计算相交范围
|
||
|
|
if box != None:
|
||
|
|
box = box.boundingBox()
|
||
|
|
self.imap.zoomToExtent(box.buffered(box.width() * 0.1)) # 扩大10%范围后缩放
|