import ast
import math
import os
import sqlite3
import subprocess
import tempfile

import numpy as np
import pandas as pd
from pyproj import Transformer
from qgis.PyQt import QtWidgets, uic, QtCore
from qgis.PyQt.QtCore import QVariant, QThread
from qgis.PyQt.QtCore import pyqtSignal
from qgis.PyQt.QtWidgets import QFileDialog, QColorDialog, QPushButton, QTableWidgetItem, QHeaderView, QMessageBox
from qgis.core import QgsCoordinateReferenceSystem, \
    QgsCoordinateTransform, QgsProject, QgsGeometry, QgsVectorLayer, QgsFields, QgsField, \
    QgsFeature, QgsPointXY, QgsFillSymbol, QgsEmbeddedSymbolRenderer, QgsRectangle
from qgis.utils import iface
from scipy.spatial import cKDTree

from .cyanlove_config_read_intfile import readconfig

FORM_CLASS, _ = uic.loadUiType(os.path.join(
    os.path.dirname(__file__), 'cyanlove_import_create_sector_geometry_base.ui'))


class cyanlove_import_create_sector_geometry(QtWidgets.QDockWidget, FORM_CLASS):
    closingPlugin = pyqtSignal()

    def __init__(self, parent=None):
        super(cyanlove_import_create_sector_geometry, self).__init__(parent)
        # 定义默认配置
        self.default_data_4g = [
            ['F', 25, 35, '#00FA9A'],
            ['D', 35, 30, '#00FA9A'],
            ['E', 45, 25, '#00FA9A'],
            ['A', 55, 20, '#00FA9A'],
            ['FDD1800', 75, 10, '#6495ED'],
            ['FDD900', 65, 5, '#6495ED'],
        ]
        self.default_data_5g = [
            ['2.6G', 28, 38, '#FF9F40'],
            ['4.9G', 38, 33, '#6495ED'],
            ['700M', 78, 12, '#00FA9A']
        ]

        # 先初始化空的数据结构
        self.data_4g = []
        self.data_5g = []
        self.current_data = []
        self.export_thread = None
        self.export_thread2 = None  # 添加第二个线程的引用
        self.is_drawing = False  # 绘制线程状态标志
        self.is_importing = False  # 导入线程状态标志
        self.iface = iface
        self.canvas = iface.mapCanvas()
        self.setupUi(self)
        self.setFloating(True)
        self.property_dict = None  # 存储搜索出来的名称，图元中心经纬度
        self.is_running = False  # 线程状态标志
        self.setWindowFlags(QtCore.Qt.Dialog)  # 使对话框始终在前
        # 设置为模态窗口
        self.setWindowModality(QtCore.Qt.ApplicationModal)  # 或者使用 Qt.WindowModal

        self.btn_draw_lte_nr.clicked.connect(self.btn_draw_sector_geometry)
        self.btn_export_muban.clicked.connect(self.btn_export_muban_excel)
        self.btn_import_muban.clicked.connect(self.btn_import_muban_excel)
        self.checkBox_select_wangluo.stateChanged.connect(self.oncheckBox_elect_wangluo)  # 连接信号和槽函数

        # 新增的四个按钮连接
        self.btn_delete_col.clicked.connect(self.btn_delete_colset)
        self.btn_add_col.clicked.connect(self.btn_add_colset)
        self.btn_sav_xml.clicked.connect(self.btn_sav_xmlset)
        self.btn_reset_default.clicked.connect(self.btn_reset_defaultset)  # 恢复默认设置按钮

        # 初始化时加载配置
        self.load_config()
        self.tableview_init()

    def load_config(self):
        """加载4G和5G的配置"""
        try:
            # 加载4G配置
            sector_4g_config_str = readconfig.read_ini_file('Settings', 'sector_4g_geometry_config')
            if sector_4g_config_str:
                self.data_4g = ast.literal_eval(sector_4g_config_str)
                print(f"加载4G配置: {self.data_4g}")
            else:
                # 如果没有保存的配置，使用默认4G配置
                self.data_4g = self.default_data_4g.copy()
                print("使用默认4G配置")

            # 加载5G配置
            sector_5g_config_str = readconfig.read_ini_file('Settings', 'sector_5g_geometry_config')
            if sector_5g_config_str:
                self.data_5g = ast.literal_eval(sector_5g_config_str)
                print(f"加载5G配置: {self.data_5g}")
            else:
                # 如果没有保存的配置，使用默认5G配置
                self.data_5g = self.default_data_5g.copy()
                print("使用默认5G配置")

            # 设置当前数据为4G数据
            self.current_data = self.data_4g

        except Exception as e:
            print(f"加载配置失败: {e}")
            # 如果加载失败，使用默认配置
            self.data_4g = self.default_data_4g.copy()
            self.data_5g = self.default_data_5g.copy()
            self.current_data = self.data_4g

    def oncheckBox_elect_wangluo(self):
        print(f"5G模式: {self.checkBox_select_wangluo.isChecked()}")
        if self.checkBox_select_wangluo.isChecked():
            # 切换到5G数据
            self.current_data = self.data_5g
        else:
            # 切换到4G数据
            self.current_data = self.data_4g

        self.tableview_init()

    def tableview_init(self):
        self.tableWidget.setColumnCount(4)  # 四列
        self.tableWidget.setHorizontalHeaderLabels(['Color_Set', '长度', '宽度', '颜色'])

        # 清除现有数据
        self.tableWidget.setRowCount(0)

        # 填充数据
        self.tableWidget.setRowCount(len(self.current_data))
        for row_idx, row_data in enumerate(self.current_data):
            for col_idx, value in enumerate(row_data):
                if col_idx == 3:  # 颜色列，添加按钮
                    button = QPushButton("")
                    button.setStyleSheet(f"background-color: {value}")
                    button.clicked.connect(lambda checked, row=row_idx: self.change_color(row))
                    self.tableWidget.setCellWidget(row_idx, col_idx, button)
                else:
                    self.tableWidget.setItem(row_idx, col_idx, QTableWidgetItem(str(value)))

        # 设置列宽自适应，所有列均可以拉伸
        for i in range(4):
            self.tableWidget.setColumnWidth(i, 100)  # 设置初始宽度为100
            self.tableWidget.horizontalHeader().setMinimumSectionSize(100)  # 设置每一列的最小宽度为100

        header = self.tableWidget.horizontalHeader()
        header.setSectionResizeMode(0, QHeaderView.ResizeToContents)  # Color_Set 列根据内容自适应
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)  # 长度列根据内容自适应
        header.setSectionResizeMode(2, QHeaderView.ResizeToContents)  # 宽度列根据内容自适应
        header.setSectionResizeMode(3, QHeaderView.Stretch)  # 颜色列填充剩余空间

    def change_color(self, row):
        # 弹出颜色对话框
        color = QColorDialog.getColor()

        if color.isValid():
            # 获取颜色按钮并改变背景色
            button = self.tableWidget.cellWidget(row, 3)
            button.setStyleSheet(f"background-color: {color.name()}")
            # 更新当前数据
            self.current_data[row][3] = color.name()
            print(f"修改第{row}行颜色为: {color.name()}")

    def btn_delete_colset(self):
        # 获取当前选中的行
        current_row = self.tableWidget.currentRow()
        if current_row >= 0:  # 检查是否有选中的行
            # 删除该行
            self.tableWidget.removeRow(current_row)
            # 更新数据列表以反映删除操作
            deleted_item = self.current_data.pop(current_row)
            self.label_show.setText(f"删除行成功: {deleted_item[0]}")
            print(f"删除行: {deleted_item}")
        else:
            self.label_show.setText("没有选中的行可删除。")

    def btn_add_colset(self):
        # 在表格末尾添加新行
        row_count = self.tableWidget.rowCount()
        self.tableWidget.insertRow(row_count)  # 插入新行
        color_set = "New"
        length_value = 50
        width_value = 30
        color = "#FFFFFF"

        # 填充新行的数据
        self.tableWidget.setItem(row_count, 0, QTableWidgetItem(str(color_set)))
        self.tableWidget.setItem(row_count, 1, QTableWidgetItem(str(length_value)))
        self.tableWidget.setItem(row_count, 2, QTableWidgetItem(str(width_value)))

        # 创建颜色按钮
        button = QPushButton("")
        button.setStyleSheet(f"background-color: {color}")
        button.clicked.connect(lambda checked, row=row_count: self.change_color(row))
        self.tableWidget.setCellWidget(row_count, 3, button)  # 颜色按钮列

        # 更新当前数据列表
        new_row = [color_set, length_value, width_value, color]
        self.current_data.append(new_row)
        self.label_show.setText("添加行成功！")
        print(f"添加行: {new_row}")

    def btn_sav_xmlset(self):
        try:
            # 首先从表格获取最新数据
            current_table_data = self.get_all_data()
            print(f"保存前表格数据: {current_table_data}")

            # 更新当前数据
            self.current_data.clear()
            self.current_data.extend(current_table_data)

            # 根据当前模式保存到对应的配置
            if self.checkBox_select_wangluo.isChecked():
                # 保存5G配置
                config_data = str(self.current_data)
                readconfig.write_ini_file('Settings', 'sector_5g_geometry_config', config_data)
                # 同时更新data_5g
                self.data_5g = self.current_data.copy()
                self.label_show.setText('5G配置保存成功!')
                print(f"保存5G配置: {self.data_5g}")
            else:
                # 保存4G配置
                config_data = str(self.current_data)
                readconfig.write_ini_file('Settings', 'sector_4g_geometry_config', config_data)
                # 同时更新data_4g
                self.data_4g = self.current_data.copy()
                self.label_show.setText('4G配置保存成功!')
                print(f"保存4G配置: {self.data_4g}")
        except Exception as e:
            self.label_show.setText(f'保存配置失败: {str(e)}')
            print(f"保存配置失败: {e}")

    def btn_reset_defaultset(self):
        """恢复默认设置"""
        try:
            # 弹出确认对话框
            reply = QMessageBox.question(self, '确认恢复',
                                         '确定要恢复默认设置吗？这将清除所有自定义配置。',
                                         QMessageBox.Yes | QMessageBox.No,
                                         QMessageBox.No)

            if reply == QMessageBox.Yes:
                # 根据当前模式恢复对应的默认配置
                if self.checkBox_select_wangluo.isChecked():
                    # 恢复5G默认配置
                    self.data_5g = self.default_data_5g.copy()
                    self.current_data = self.data_5g
                    # 删除保存的5G配置，让系统使用默认值
                    readconfig.write_ini_file('Settings', 'sector_5g_geometry_config', '')
                    self.label_show.setText('5G配置已恢复默认设置!')
                    print("恢复5G默认配置")
                else:
                    # 恢复4G默认配置
                    self.data_4g = self.default_data_4g.copy()
                    self.current_data = self.data_4g
                    # 删除保存的4G配置，让系统使用默认值
                    readconfig.write_ini_file('Settings', 'sector_4g_geometry_config', '')
                    self.label_show.setText('4G配置已恢复默认设置!')
                    print("恢复4G默认配置")

                # 刷新表格显示
                self.tableview_init()

        except Exception as e:
            self.label_show.setText(f'恢复默认设置失败: {str(e)}')
            print(f"恢复默认设置失败: {e}")

    def get_all_data(self):
        """从表格获取所有数据（确保获取的是最新数据）"""
        all_data = []
        row_count = self.tableWidget.rowCount()
        for row in range(row_count):
            row_data = self.get_row_data(row)
            all_data.append(row_data)
        print(f"从表格获取的数据: {all_data}")
        return all_data

    def get_row_data(self, row):
        """获取单行数据"""
        row_data = []
        for col in range(4):  # 4列
            item = self.tableWidget.item(row, col)
            if item:
                row_data.append(item.text())
            else:
                # 对于按钮列，获取按钮的背景色或其他相关信息
                button = self.tableWidget.cellWidget(row, col)
                if button:
                    # 获取按钮的背景色作为颜色列的值
                    button_color = button.styleSheet().split(':')[-1].strip()
                    row_data.append(button_color)
        return row_data

    def btn_export_muban_excel(self):
        # 检查是否有导入线程正在运行
        if self.is_importing:
            QMessageBox.warning(self, '警告', '正在导入数据，请稍后再试！')
            return

        options = QFileDialog.Options()  # 创建文件对话框选项
        options |= QFileDialog.ReadOnly  # 可以设置一些选项，例如只读
        file_name, _ = QFileDialog.getSaveFileName(self,
                                                   "选择文件",
                                                   "",
                                                   "Excel文件 (*.xlsx);;所有文件 (*)",  # 可以添加其他过滤器
                                                   options=options)  # 传递 options 参数
        if file_name:
            # 指定表头
            columns = ['地市', 'LTE基站名称', 'LTE小区名称', 'LTE基站标识', 'LTE小区标识', 'PCI', '经度', '纬度',
                       '基站类型', 'Color_Set', '频点', '方位角', 'TAC']
            if self.checkBox_select_wangluo.isChecked():
                columns = ['地市', 'NR基站名称', 'NR小区名称', 'NR基站标识', 'NR小区标识', 'PCI', '经度', '纬度',
                           '基站类型', 'Color_Set', '频点', '方位角', 'TAC']

            # 创建一个空的 DataFrame，只有表头，没有数据
            df = pd.DataFrame(columns=columns)
            # 导出为 Excel 文件，只包含表头
            df.to_excel(file_name, index=False)
            self.label_show.setText('模板导出完毕！')
        else:
            self.label_show.setText('操作已经取消!')

    def btn_import_muban_excel(self):
        # 检查是否有绘制线程正在运行
        if self.is_drawing:
            QMessageBox.warning(self, '警告', '正在绘制扇区，请稍后再试！')
            return

        options = QFileDialog.Options()  # 创建文件对话框选项
        options |= QFileDialog.ReadOnly  # 可以设置一些选项，例如只读
        file_name, _ = QFileDialog.getOpenFileName(self,
                                                   "选择文件",
                                                   "",
                                                   "Excel文件 (*.xlsx);;所有文件 (*)",  # 可以添加其他过滤器
                                                   options=options)  # 传递 options 参数
        if file_name:
            tbname = "SSJ_LTE工参模板表"
            if self.checkBox_select_wangluo.isChecked():
                tbname = "SSJ_NR工参模板表"
            try:
                self.label_show.setText(f"导入中...")
                self.progressBar.setValue(0)
                QtWidgets.QApplication.processEvents()
                # 启动导出线程
                self.export_thread2 = ExportThread2(tbname, file_name)
                self.export_thread2.updatelabel.connect(self.update_label)
                self.export_thread2.finished.connect(self.on_import_finished)  # 连接完成信号
                self.is_importing = True  # 设置导入标志
                self.export_thread2.start()  # 启动线程

            except Exception as e:
                self.label_show.setText(f"读取文件时出错: {e}")
                print(f"读取文件时出错: {e}")

    def on_import_finished(self):
        """导入完成后的清理工作"""
        self.is_importing = False
        self.export_thread2 = None

    # 绘制图形
    def btn_draw_sector_geometry(self):
        # 检查是否有正在运行的线程
        if self.is_drawing:
            QMessageBox.warning(self, '警告', '已有绘制任务正在运行，请等待完成后再试！')
            return

        # 弹出确认对话框
        reply = QMessageBox.question(self, '确认绘制',
                                     '确定要开始绘制扇区吗？\n这将清除现有的扇区图层并重新绘制。',
                                     QMessageBox.Yes | QMessageBox.No,
                                     QMessageBox.No)

        if reply != QMessageBox.Yes:
            self.label_show.setText("已取消绘制操作")
            return
        self.label_show.setText('操作已经取消!')
        tbname = "SSJ_LTE工参模板表"
        layName = "SSJ_LTE扇区图"
        if self.checkBox_select_wangluo.isChecked():
            tbname = "SSJ_NR工参模板表"
            layName = "SSJ_NR扇区图"

        # 创建内存图层
        layers = QgsProject.instance().mapLayersByName(layName)
        if layers:
            for layer in layers:
                QgsProject.instance().removeMapLayer(layer.id())
        memory_layer = QgsVectorLayer("Polygon?crs=EPSG:4326", layName, "memory")
        # 其他参数

        spin_box_data = self.get_all_data()  # 数据

        spin_box_width = self.mQgsSpinBox_width.value()  # 线宽度
        spin_box_color = self.mColorButton_line.color().name()  # 线颜色
        spin_box_midu_checked = self.checkBox_midu.isChecked()  # 密度
        spin_box_touming_checked = self.checkBox_touming.isChecked()  # 透明
        # 确保使用现有的进度条
        self.progressBar.setRange(0, 100)  # 设置进度条范围
        self.progressBar.setValue(0)  # 初始化进度条值
        self.progressBar.show()  # 显示进度条 (如果之前是隐藏状态)

        # 定义坐标参考系（用于投影转换）
        source_crs = QgsCoordinateReferenceSystem("EPSG:4326")  # WGS84
        # 推荐使用 EPSG:3857（Web墨卡托）或根据数据范围选择UTM带，此处使用3857通用性较强
        target_crs = QgsCoordinateReferenceSystem("EPSG:3857")

        # 启动线程，传入CRS对象
        self.export_thread = ExportThread(
            memory_layer, tbname, spin_box_data,
            spin_box_width, spin_box_color,
            spin_box_midu_checked, spin_box_touming_checked,
            source_crs, target_crs
        )
        self.export_thread.progress.connect(self.update_progress)
        self.export_thread.updatelabel.connect(self.update_label)
        self.export_thread.layerAdded.connect(lambda: QgsProject.instance().addMapLayer(memory_layer, True))
        self.export_thread.finished.connect(self.on_draw_finished)
        self.is_drawing = True
        self.export_thread.start()

    def on_draw_finished(self):
        """绘制完成后的清理工作"""
        self.is_drawing = False
        self.export_thread = None

    def update_label(self, value):
        self.label_show.setText(value)  # 更新值

    def update_progress(self, value):
        self.progressBar.setValue(value)  # 更新进度条的值


class ExportThread(QThread):
    progress = pyqtSignal(int)
    updatelabel = pyqtSignal(str)
    layerAdded = pyqtSignal()
    finished = pyqtSignal()

    def __init__(self, memory_layer, tbname, spin_box_data, spin_box_width, spin_box_color,
                 spin_box_midu_checked, spin_box_touming_checked, source_crs, target_crs):
        super().__init__()
        self.memory_layer = memory_layer
        self.tbname = tbname
        self.spin_box_data = spin_box_data
        self.spin_box_width = spin_box_width
        self.spin_box_color = spin_box_color
        self.spin_box_midu_checked = spin_box_midu_checked
        self.spin_box_touming_checked = spin_box_touming_checked
        self.source_crs = source_crs      # QgsCoordinateReferenceSystem("EPSG:4326")
        self.target_crs = target_crs      # 例如 QgsCoordinateReferenceSystem("EPSG:3857")
        self.dicts = {}

    def run(self):
        try:
            self.updatelabel.emit("进度：开始绘制...")
            result_dict = {item[0]: item[1:] for item in self.spin_box_data}

            # ---------- 1. 读取数据 ----------
            db_path = readconfig.read_ini_file('Settings', 'sqlite_栅格分析')
            conn = sqlite3.connect(db_path, timeout=600)
            # 为了保留所有属性，仍 SELECT * （若字段过多可显式指定）
            if self.tbname == "SSJ_LTE工参模板表":
                sql = "SELECT * FROM [SSJ_LTE工参模板表]"
            else:
                sql = "SELECT * FROM [SSJ_NR工参模板表]"
            df = pd.read_sql(sql, conn)
            conn.close()

            # 生成唯一标识（经纬度组合）
            df['idsetssj'] = df['经度'].astype(str) + '_' + df['纬度'].astype(str)

            # ---------- 2. 计算最近邻密度（若勾选） ----------
            if self.spin_box_midu_checked:
                self.updatelabel.emit("进度：计算最近邻...")
                df_unique = df.drop_duplicates(subset=['idsetssj'])
                self.dicts = self.get_kdstree_Nearest(df_unique, 5)

            # ---------- 3. 准备坐标转换器（线程内只创建一次） ----------
            transform1 = QgsCoordinateTransform(self.source_crs, self.target_crs, QgsProject.instance())
            transform2 = QgsCoordinateTransform(self.target_crs, self.source_crs, QgsProject.instance())

            # ---------- 4. 批量生成要素 ----------
            self.updatelabel.emit("进度：绘制中...")
            provider = self.memory_layer.dataProvider()
            fields = QgsFields()
            for column in df.columns:
                if column != "idsetssj":
                    fields.append(QgsField(column, QVariant.String))
            provider.addAttributes(fields)
            self.memory_layer.updateFields()

            features = []
            total = len(df)
            progress_step = max(1, total // 100)  # 每1%更新一次

            for index, row in df.iterrows():
                if self.isInterruptionRequested():
                    self.updatelabel.emit("绘制已取消！")
                    return

                # 获取中心点（经纬度）
                lon = float(row["经度"])
                lat = float(row["纬度"])
                center_wgs84 = QgsPointXY(lon, lat)
                # 转到UTM/投影坐标系
                center_m = transform1.transform(center_wgs84)

                typecell = row["基站类型"]
                angle = float(row["方位角"])
                colorset = row["Color_Set"]
                sector_length = int(result_dict[colorset][0])
                sector_width = int(result_dict[colorset][1])
                sector_color = result_dict[colorset][2]

                # 密度修正
                if self.spin_box_midu_checked:
                    key = row["idsetssj"]
                    dist = self.dicts.get(key, 0)
                    if dist < 500:
                        sector_length = int(result_dict[colorset][0])
                    elif dist < 1000:
                        sector_length = int(result_dict[colorset][0]) + 100
                    elif dist < 2000:
                        sector_length = int(result_dict[colorset][0]) + 200
                    else:
                        sector_length = int(result_dict[colorset][0]) + 300

                if self.spin_box_touming_checked:
                    sector_color = "#00000000"

                # 构建多边形点列表（UTM坐标系）
                if typecell in ["室分", "室内"]:
                    if self.spin_box_midu_checked:
                        dist = self.dicts.get(row["idsetssj"], 0)
                        if dist < 500:
                            w = h = 30
                        elif dist < 1000:
                            w = h = 50
                        elif dist < 2000:
                            w = h = 80
                        else:
                            w = h = 100
                    else:
                        w = h = 30
                    points_utm = self.create_rect_points(center_m, w, h)
                else:
                    points_utm = self.create_sector_points(center_m, angle, sector_length, sector_width, num_samples=30)

                # 创建多边形几何（UTM）
                polygon_geom = QgsGeometry.fromPolygonXY([points_utm])
                # 整体转换到WGS84
                polygon_geom.transform(transform2)

                # 创建要素
                feature = QgsFeature(fields)
                feature.setGeometry(polygon_geom)
                # 符号
                embedded_symbol = QgsFillSymbol.createSimple({
                    "color": sector_color,
                    "outline_color": self.spin_box_color,
                    "outline_width": str(self.spin_box_width),
                })
                feature.setEmbeddedSymbol(embedded_symbol)
                # 填充属性（跳过idsetssj）
                for column in df.columns:
                    if column != "idsetssj":
                        feature[column] = row[column]
                features.append(feature)

                # 进度信号（每1%）
                if (index + 1) % progress_step == 0 or (index + 1) == total:
                    progress_value = int((index + 1) / total * 100)
                    self.progress.emit(progress_value)

            # ---------- 5. 批量提交所有要素 ----------
            provider.addFeatures(features)
            self.memory_layer.updateExtents()

            # 渲染器
            renderer = QgsEmbeddedSymbolRenderer(
                defaultSymbol=QgsFillSymbol.createSimple({
                    "color": '#00FA9A',
                    "outline_color": "#000000",
                    "outline_width": '1',
                }))
            self.memory_layer.setRenderer(renderer)

            self.layerAdded.emit()
            self.updatelabel.emit("绘制完成！")

        except Exception as e:
            self.updatelabel.emit(f"错误：{str(e)}")
            print(f"错误：{str(e)}")
        finally:
            self.finished.emit()

    # ---------- 几何构建辅助（仅返回点列表，不转换坐标） ----------
    @staticmethod
    def create_sector_points(center_m, angle, sector_length, sector_width, num_samples=30):
        """返回UTM坐标系下的扇区点列表（包括中心点）"""
        begin_angle = math.radians(90 - angle - sector_width)
        end_angle = math.radians(90 - angle + sector_width)
        points = [QgsPointXY(center_m.x(), center_m.y())]  # 中心点
        for i in range(num_samples + 1):
            theta = begin_angle + (end_angle - begin_angle) * (i / num_samples)
            px = center_m.x() + sector_length * math.cos(theta)
            py = center_m.y() + sector_length * math.sin(theta)
            points.append(QgsPointXY(px, py))
        return points

    @staticmethod
    def create_rect_points(center_m, width, height):
        """返回UTM坐标系下的矩形四个角点（闭合）"""
        xmin = center_m.x() - width / 2
        xmax = center_m.x() + width / 2
        ymin = center_m.y() - height / 2
        ymax = center_m.y() + height / 2
        return [
            QgsPointXY(xmin, ymin),
            QgsPointXY(xmax, ymin),
            QgsPointXY(xmax, ymax),
            QgsPointXY(xmin, ymax),
            QgsPointXY(xmin, ymin)   # 闭合
        ]

    # ---------- KDTree最近邻计算（不变） ----------
    @staticmethod
    def get_kdstree_Nearest(df, numberk):
        transformer = Transformer.from_crs("EPSG:4326", "EPSG:32648", always_xy=True)
        utm_coords = df.apply(lambda row: transformer.transform(row["经度"], row["纬度"]), axis=1)
        utm_coords = np.array(list(utm_coords))
        tree = cKDTree(utm_coords)
        distances, indices = tree.query(utm_coords, k=numberk)
        results = {}
        for i, dd in enumerate(df["idsetssj"]):
            valid_distances = [dist for idx, dist in zip(indices[i], distances[i]) if dist > 0]
            nearest_3_distances = valid_distances[:20]
            avg_distance = np.mean(nearest_3_distances) if nearest_3_distances else 0
            results[dd] = avg_distance
        return results


class ExportThread2(QThread):
    updatelabel = pyqtSignal(str)  # 声明一个更新label新增信号
    finished = pyqtSignal()  # 添加完成信号

    def __init__(self, tbname, file_name):
        super().__init__()
        self.file_name = file_name
        self.tbname = tbname

    def run(self) -> None:
        self.updatelabel.emit('开始读取到内存中...')

        try:
            db_path = readconfig.read_ini_file('Settings', 'sqlite_栅格分析')
            # 连接到 SQLite 数据库
            conn = sqlite3.connect(db_path)
            cursor = conn.cursor()
            df = pd.read_excel(self.file_name, sheet_name=0)  # 使用0读取第一个工作表
            # 数据有效性检测
            self.updatelabel.emit('检查数据有效性...')
            # 要检查的列
            columns_to_check = ['经度', '纬度', '方位角']

            # 确定使用的列名
            if 'NR小区名称' in df.columns:
                cell_name_column = 'NR小区名称'
            elif 'LTE小区名称' in df.columns:
                cell_name_column = 'LTE小区名称'
            else:
                self.updatelabel.emit("数据框中缺少 'NR小区名称' 或 'LTE小区名称' 列。")
            # 存储错误行
            error_rows = {}

            for column in columns_to_check:
                # 尝试转换为数字类型
                converted = pd.to_numeric(df[column], errors='coerce')
                # 找出NaN的行索引
                error_indices = converted[converted.isna()].index.tolist()
                if error_indices:
                    # 获取对应NR小区名称
                    nr_cell_names = df.loc[error_indices, cell_name_column].tolist()
                    error_rows[column] = {
                        'indices': error_indices,
                        'nr_cell_names': nr_cell_names
                    }

            # 打印结果
            if error_rows:
                with tempfile.NamedTemporaryFile(delete=False, suffix='.txt', mode='w') as temp_file:
                    for column, info in error_rows.items():
                        temp_file.write(f"数字列错误：对应的NR小区名称: {info['nr_cell_names']}")  # 写入一些数据

                        # 使用默认文本编辑器打开临时文件
                temp_file_path = temp_file.name  # 获取临时文件路径
                if os.path.exists(temp_file_path):
                    print(temp_file_path)
                    subprocess.run(['notepad.exe', temp_file_path])  # Windows上调用记事本打开文件
                self.updatelabel.emit(f"存在经纬度/方位角非数字列错误！")
            else:
                print("无错误，导入中...")
                df.to_sql(self.tbname, conn, if_exists='replace', index=False)
                self.updatelabel.emit(f"写入数据库完毕！")

            # 关闭连接
            conn.close()

        except Exception as e:
            self.updatelabel.emit(f"{str(e)}")
            print(f"{str(e)}")
        finally:
            self.finished.emit()  # 发出完成信号