import ast
import csv
import math
import os
import re
import sqlite3
import subprocess
import tempfile
import time
from datetime import datetime

import numpy as np
import pandas as pd
import processing
from PyQt5.QtCore import QVariant, QThread, QCoreApplication, QAbstractTableModel, Qt, QModelIndex
from PyQt5.QtGui import QColor, QStandardItemModel, QStandardItem
from PyQt5.QtWidgets import QFileDialog, QStyledItemDelegate, QComboBox, QColorDialog, QPushButton, QWidget, \
    QAbstractItemView, QTableView, QItemDelegate, QTableWidgetItem, QHeaderView, QMessageBox
from pyproj import Geod, Transformer
from qgis._core import QgsFeatureRequest, QgsMapLayer, QgsWkbTypes, QgsCoordinateReferenceSystem, \
    QgsCoordinateTransform, QgsProject, QgsGeometry, QgsGeometryCollection, QgsVectorLayer, QgsFields, QgsField, \
    QgsFeature, Qgis, QgsPointXY, QgsUnitTypes, QgsDistanceArea, QgsProcessingContext, QgsSpatialIndex, \
    QgsProcessingFeedback, QgsSymbol, QgsPoint, QgsPolygon, QgsFillSymbol, QgsSingleSymbolRenderer, QgsFeatureRenderer, \
    QgsRenderContext, QgsEmbeddedSymbolRenderer, QgsRectangle
from qgis.utils import iface

from qgis.PyQt import QtWidgets, uic, QtCore
from qgis.PyQt.QtCore import pyqtSignal
from scipy.spatial import KDTree, cKDTree

from .cyanlove_config_read_intfile import readconfig
from .cyanlove_config_csv_excel_read import CyanloveConfigCSVExcelRead

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.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):
        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)

    def btn_import_muban_excel(self):

        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_thread = ExportThread2(tbname, file_name)
                self.export_thread.updatelabel.connect(self.update_label)
                self.export_thread.start()  # 启动线程

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

    # 绘制图形
    def btn_draw_sector_geometry(self):
        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()  # 显示进度条 (如果之前是隐藏状态)
        # 启动导出线程
        self.export_thread = ExportThread(memory_layer, tbname, spin_box_data, spin_box_width, spin_box_color,
                                          spin_box_midu_checked,
                                          spin_box_touming_checked)
        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.start()  # 启动线程

    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)  # 声明一个更新label新增信号
    layerAdded = pyqtSignal()  # 声明一个图层新增信号

    def __init__(self, memory_layer, tbname, spin_box_data, spin_box_width, spin_box_color, spin_box_midu_checked,
                 spin_box_touming_checked):
        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.dicts = []

    def run(self) -> None:
        try:
            # 转为字典使用
            result_dict = {item[0]: item[1:] for item in self.spin_box_data}

            db_path = readconfig.read_ini_file('Settings', 'sqlite_栅格分析')
            conn = sqlite3.connect(db_path, timeout=600)
            df = pd.read_sql(f"SELECT *,经度 || '_' || 纬度 as idsetssj  FROM [{self.tbname}]", conn)  # 替换为你的表名

            if self.spin_box_midu_checked:
                self.updatelabel.emit("进度：计算最近邻...")
                df_unique = df.drop_duplicates(subset=['idsetssj', '经度', '纬度'])
                # 每个经纬度最近的20个平均距离
                self.dicts = self.get_kdstree_Nearest(df_unique, 5)

            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()  # 更新图层的字段
            countdf = len(df)  # 获取数据框的行数

            for index, row in df.iterrows():
                key1 = row["idsetssj"]
                longitude = self.safe_convert_to_float(row["经度"], default=0)
                latitude = self.safe_convert_to_float(row["纬度"], default=0)
                typecell = row["基站类型"]
                angle = self.safe_convert_to_float(row["方位角"], default=0)
                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:
                    distince = self.dicts[key1]
                    if distince < 500:
                        sector_length = int(result_dict[colorset][0])  # 长度
                    if 500 <= distince < 1000:
                        sector_length = int(result_dict[colorset][0]) + 100  # 长度
                    if 1000 <= distince < 2000:
                        sector_length = int(result_dict[colorset][0]) + 200  # 长度
                    if distince >= 2000:
                        sector_length = int(result_dict[colorset][0]) + 300  # 长度

                #  如果选择透明
                if self.spin_box_touming_checked:
                    sector_color = "#00000000"
                # # 绘制
                feature = QgsFeature(fields)  # 创建新特征
                # 扇区绘制
                if typecell in ["室分", "室内"]:
                    if self.spin_box_midu_checked:
                        distince = self.dicts[key1]
                        if distince < 500:
                            sector_geometry = self.create_rect(longitude, latitude, 30, 30)
                            feature.setGeometry(sector_geometry)
                        if 500 <= distince < 1000:
                            sector_geometry = self.create_rect(longitude, latitude, 50, 50)
                            feature.setGeometry(sector_geometry)
                        if 1000 <= distince < 2000:
                            sector_geometry = self.create_rect(longitude, latitude, 80, 80)
                            feature.setGeometry(sector_geometry)
                        if distince >= 2000:
                            sector_geometry = self.create_rect(longitude, latitude, 100, 100)
                            feature.setGeometry(sector_geometry)
                    else:
                        sector_geometry = self.create_rect(longitude, latitude, 30, 30)
                        feature.setGeometry(sector_geometry)
                else:
                    sector_geometry = self.create_sector(longitude, latitude, angle, sector_length, sector_width)
                    feature.setGeometry(sector_geometry)
                # 创建QgsSymbol并设置颜色

                embedded_symbol = QgsFillSymbol.createSimple({
                    "color": f"{sector_color}",
                    "outline_color": f"{self.spin_box_color}",
                    "outline_width": str(self.spin_box_width),  # 外边框宽度
                })

                # 设置嵌入式符号到要素
                feature.setEmbeddedSymbol(embedded_symbol)

                for column in df.columns:
                    if column != "idsetssj":
                        feature[column] = row[column]  # 填充特征属性

                provider.addFeatures([feature])  # 将特征添加到数据提供者

                # 计算并发出进度信号
                progress_value = int((index + 1) / countdf * 100)  # 计算进度百分比
                self.progress.emit(progress_value)  # 发出进度信号

            renderer = QgsEmbeddedSymbolRenderer(
                defaultSymbol=QgsFillSymbol.createSimple({
                    "color": '#00FA9A',  # 填充颜色
                    "outline_color": "#000000",  # 外边框颜色（黑色）
                    "outline_width": '1',  # 外边框宽度
                }))

            self.memory_layer.setRenderer(renderer)
            self.layerAdded.emit()  # 发出图层新增信号
            self.updatelabel.emit(f"绘制完成！")
        except Exception as e:
            print(f"错误：{str(e)})")
            self.updatelabel.emit(f"错误：{str(e)})")

    @staticmethod
    def safe_convert_to_float(value, default=0):
        """
        尝试将值转换为浮点数，如果失败则返回默认值。
        :param value: 要转换的值
        :param default: 转换失败时的默认值
        :return: 转换后的整数或默认值
        """
        try:
            return float(value)
        except (ValueError, TypeError):
            return default

    @staticmethod
    def create_sector(x, y, angle, sector_length, sector_width, num_samples=50):
        """
        创建一个扇区，给定中心点坐标 (x, y)、扇区长度、角度和扇区宽度
        :param x: 中心点经度（度，WGS84坐标系下的经度）
        :param y: 中心点纬度（度，WGS84坐标系下的纬度）
        :param sector_length: 扇区半径长度（米）
        :param angle: 扇区的方位角（与北方的夹角，单位：度）
        :param sector_width: 扇区宽度（从方位角两侧延伸的角度，单位：度）
        :param num_samples: 计算扇区边界的点数
        :return: 扇区多边形（QgsGeometry）
        """
        # 定义源坐标系（WGS84，度单位）
        source_crs = QgsCoordinateReferenceSystem("EPSG:4326")  # WGS 84（经纬度坐标系）

        # 定义目标坐标系（假设使用 UTM 或其他米制坐标系）
        target_crs = QgsCoordinateReferenceSystem("EPSG:32648")

        # 中心点在 WGS84 坐标系中的位置
        center = QgsPointXY(x, y)

        # 使用 CRS 转换将经纬度坐标转换为米制坐标系
        transform1 = QgsCoordinateTransform(source_crs, target_crs, QgsProject.instance())

        center_m = transform1.transform(center)

        # 将方位角和宽度转换为弧度
        begin_angle = math.radians(90 - angle - sector_width)
        end_angle = math.radians(90 - angle + sector_width)

        points = []
        # 计算扇区的边界点
        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))  # 使用 QgsPointXY

        # 添加中心点
        points.insert(0, center_m)

        # 将点从米制坐标系转换回 WGS84 坐标系
        wgs84_points = []
        transform2 = QgsCoordinateTransform(target_crs, source_crs, QgsProject.instance())
        for point in points:
            # 将每个点转换回 WGS84
            transformed_point = transform2.transform(point)
            wgs84_points.append(QgsPointXY(transformed_point))

        # 创建多边形
        polygon = [wgs84_points]  # 将转换后的点放入一个列表中以创建多边形

        # 使用 QgsGeometry 创建多边形
        geometry = QgsGeometry.fromPolygonXY(polygon)

        return geometry

    @staticmethod
    def create_rect(x, y, width, height):

        # 定义源坐标系（WGS84，度单位）
        source_crs = QgsCoordinateReferenceSystem("EPSG:4326")  # WGS 84（经纬度坐标系）

        # 定义目标坐标系（假设使用 UTM 或其他米制坐标系）
        target_crs = QgsCoordinateReferenceSystem("EPSG:32648")

        # 中心点
        center = QgsPointXY(x, y)

        # 使用 CRS 转换将经纬度坐标转换为米制坐标系
        transform1 = QgsCoordinateTransform(source_crs, target_crs, QgsProject.instance())
        center_m = transform1.transform(center)

        # 计算矩形的边界
        xmin = center_m.x() - width / 2
        xmax = center_m.x() + width / 2
        ymin = center_m.y() - height / 2
        ymax = center_m.y() + height / 2

        # 创建矩形
        rect = QgsRectangle(xmin, ymin, xmax, ymax)

        # 转换回 WGS84 坐标
        transform2 = QgsCoordinateTransform(target_crs, source_crs, QgsProject.instance())

        # 获取矩形的四个角点
        top_left = transform2.transform(QgsPointXY(rect.xMinimum(), rect.yMaximum()))
        top_right = transform2.transform(QgsPointXY(rect.xMaximum(), rect.yMaximum()))
        bottom_left = transform2.transform(QgsPointXY(rect.xMinimum(), rect.yMinimum()))
        bottom_right = transform2.transform(QgsPointXY(rect.xMaximum(), rect.yMinimum()))

        # 创建多边形几何
        polygon = [[top_left, top_right, bottom_right, bottom_left, top_left]]  # 闭合多边形
        geometry = QgsGeometry.fromPolygonXY(polygon)

        return geometry

    @staticmethod
    def get_kdstree_Nearest(df, numberk):
        # 定义投影转换器（WGS84 -> UTM 坐标）
        transformer = Transformer.from_crs("EPSG:4326", "EPSG:32648", always_xy=True)  # 32648为中国大部分区域的UTM投影

        # 经纬度转换为 UTM 坐标
        utm_coords = df.apply(lambda row: transformer.transform(row["经度"], row["纬度"]), axis=1)
        utm_coords = np.array(list(utm_coords))

        # 构建 KDTree 并查询最近邻
        tree = cKDTree(utm_coords)
        distances, indices = tree.query(utm_coords, k=numberk)

        # 计算平均最近邻距离（排除与自己坐标完全相同的数据）
        results = {}
        for i, dd in enumerate(df["idsetssj"]):
            # 排除与自己相同的点（距离为 0 的点）
            valid_distances = [dist for idx, dist in zip(indices[i], distances[i]) if dist > 0]
            # 取前 3 个最近邻点
            nearest_3_distances = valid_distances[:20]
            # 计算平均值
            avg_distance = np.mean(nearest_3_distances)
            results[dd] = avg_distance

        return results


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

    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)}")