<template>
  <div>
    <div class="mapParent" id="mapParent"
      :style="{ backgroundColor: backgroundColor, height: height, width: width, lineHeight: height }">
      <span class="maptext">
        <span v-if="textShow">请选择地图</span>
        <br>
      </span>
    </div>
    <div id="tooltip" class="tooltip">
      <div class="tooltip-header">
        <i class="el-icon-location"></i>
        <span class="tooltip-title">添加目标点</span>
      </div>
      <div id="tooltip-content" class="tooltip-content"></div>
      <div class="tooltip-actions">
        <el-button type="primary" size="small" @click="addNowPoint" class="action-btn">
          <i class="el-icon-check"></i>
          添加点位
        </el-button>
        <el-button size="small" @click="clearNowPoint" class="action-btn">
          <i class="el-icon-close"></i>
          取消添加
        </el-button>
      </div>
    </div>
  </div>
</template>

<script>
import axios from "axios";
import "ol/ol.css";
import ol from 'ol';
import { Map, View } from "ol";
import Projection from "ol/proj/Projection";
import SrcXYZ from "ol/source/XYZ";
import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer";
import Overlay from 'ol/Overlay';
import {
  defaults as InteractionDefaults,
  DragRotateAndZoom, Draw, Modify, Snap
} from "ol/interaction";
import GeoJSON from "ol/format/GeoJSON";
import { Vector as VectorSource } from "ol/source";
import LineString from "ol/geom/LineString";
import Feature from "ol/Feature";
import { Icon, Style, Stroke, Fill, Text, Circle } from "ol/style";
import Point from "ol/geom/Point";
import MultiPoint from "ol/geom/MultiPoint";
import { fromLonLat, toLonLat } from 'ol/proj';
import { easeInOut, easeOut } from 'ol/easing';
import { getPointcloud } from '@/api/map';

export default {
  name: 'OlMap',
  props: {
    mapName: {
      type: String,
      required: true
    },
    // 是否展示pointSwitch按钮
    pointSwitchShow: {
      type: Boolean,
      default: true
    },
    // Switch按钮的默认状态
    pointSwitchStatus: {
      type: Boolean,
      default: false
    },
    // 是否显示中心的文字
    textShow: {
      type: Boolean,
      default: true
    },
    // 显示的文字内容
    content: {
      type: String,
      default: ""
    },
    // 背景色
    backgroundColor: {
      type: String,
      default: "#d5d8e1"
    },
    // 高度
    height: {
      type: String,
      default: "365px"
    },
    // 高度
    width: {
      type: String,
      default: "365px"
    },
    // 地图缩放等级
    zoom: {
      type: Number,
      default: 2
    },
    // 是否显示默认控件（缩放按钮等）
    showDefaultControls: {
      type: Boolean,
      default: true
    },
    // 最小缩放等级
    minZoom: {
      type: Number,
      default: 1
    },
    // 点位数字显示控制
    pointSwitch: {
      type: Boolean,
      default: false
    },
    // 地图显示与否
    baseLayerShow: {
      type: Boolean,
      default: true
    },
    // 是否开启点位添加模式
    pointSelectionEnabled: {
      type: Boolean,
      default: false
    },
    // 是否开启元素选择模式(绘图功能页面)
    pointDraSelectEnabled: {
      type: Boolean,
      default: false
    },
    // 是否启用了位姿初始化
    poseInitEnable: {
      type: Boolean,
      default: false
    },
    /**
     * 点击点位时触发的事件，
     * @param deptId: 防区编码
     * @param robotCode: 无人车编号
     * @param mapName: 地图名称
     * @param point: 点位
     * @param routeIndex: 路线索引
     * @param coord: {x:0, y: 0}
     * default: (deptId, robotCode, mapName, point, routeIndex, coord), 父组件传入方法时请按照此参数顺序设置
     */
    pointEvent: {
      type: Function,
      default: () => { }
    },
    // 位姿信息
    robotPoseData: {
      type: Object,
      required: false,
      default: function () {
        return {
          x: 0,
          y: 0,
          angle: 0
        };
      }
    },
    // 只展示系统已经录入的节点
    existPointShow: {
      type: Boolean,
      default: false
    },
    // 路线的index索引
    routeIndex: {
      type: Number,
      default: -1
    },
    // 是否启用跟随模式
    isRobotFollow: {
      type: Boolean,
      default: false
    },
    // 坐标标定参数
    poseCalibrationIndex: {
      type: Number,
      default: 0
    },
    // 是否显示机器人图标
    isShowRobot: {
      type: Boolean,
      default: true
    },
    // 是否显示点云
    showPointcloud: {
      type: Boolean,
      default: false
    },
    // 是否显示路网线段
    showRoadNetwork: {
      type: Boolean,
      default: true
    }
  },
  data() {
    return {
      fileUrl: process.env.VUE_APP_MAP_FILE_URL, // 地图文件地址url
      map: null,
      config_json: "",
      geojson_data: "",
      robot_src: null,
      icon_src: null,
      pose: { x: 0, y: 0, angle: 0 }, // 位姿信息
      oldRobotCode: "",
      aiAlarmList: [], // ai告警列表
      // 点位点击时的调用方法
      localPointEvent: null,
      play: false,
      pathTimerId: null, // 路径播放定时器
      baseLayer: null,
      // 该地图的所有点位
      pointAll: [],
      queryParams: {
        deptId: null,
        mapName: '',
        pageSize: 200
      },
      // 当前点击位置的画布位置信息
      currentPlace: [],
      // 当前点击位置的坐标数据信息
      currentCoordinate: [],
      maxIndex: 0,
      MarkVectorSource: null,
      icon: {
        mark: require("@/assets/icons/olmap/mouseIcon.png"),
        point: require("@/assets/icons/olmap/normal_point.png")
      },
      // 注意,下方是绘制地图图形的工具参数
      draw: null,
      snap: null,
      roadmap_src: null,
      roadmapLayer: null, // 路网图层引用
      // 下面四个数据用于记录当前地图中各类元素当前最大id值(绘图新元素起始id使用)
      maxPointIdNum: 0,
      maxLineIdNum: 0,
      maxBowNum: 0,
      maxPolygonNum: 0,
      selectedFeatureId: '',  // 用于存储选中的 Feature id
      previewFeature: null, // 动态线段(位姿初始化)
      calibrationList:[],
      // 轨迹相关
      trajectorySource: null, // 轨迹图层source
      currentTrajectory: null, // 当前轨迹数据
      trajectoryProgress: 0, // 轨迹进度
      // 点云相关
      pointcloudLayer: null, // 点云图层
      pointcloudTimer: null // 点云更新定时器
    }
  },
  computed: {
    url() {
      // return this.fileUrl + this.deptId + "/" + "map" + "/" + this.robotCode + "/" + this.mapName;
      // return 'http://101.35.49.102:9000/prod/102/map/HJ-326/sh02'
      return  'http://192.168.0.102:8086'
    }
  },

  watch: {
    pointSwitch(newVal, oldVal) {
      console.log('pointSwitch watch触发:', { newVal, oldVal });
      if (newVal !== oldVal) {
        // 🚨 临时禁用自动重新加载，防止清除用户数据
        console.warn('🚨 pointSwitch变化，但已禁用自动重新加载以保护用户数据');
        // this.reloadMapCanvas(); // 临时注释掉
      }
    },
    baseLayerShow(newVal, oldVal) {
      if (newVal !== oldVal) {
        // 如果数据变动则重新加载
        const isVisible = this.baseLayer.getVisible();
        this.baseLayer.setVisible(!isVisible);
      }
    },
    // 导航页面的选择模式,基本的地图选点,打点
    pointSelectionEnabled(newVal, oldVal) {
      // 开启选择模式ui
      if (newVal) {
        const mapElement = document.querySelector('.tileMap'); // 地图视图的 DOM 元素
        mapElement.style.cursor = 'pointer'; // 设置鼠标样式为手
      } else {
        const mapElement = document.querySelector('.tileMap');
        mapElement.style.cursor = 'default'; // 恢复默认鼠标样式
      }
    },
    // 绘制页面的选择功能, 更多的处理逻辑
    pointDraSelectEnabled(newVal, oldVal) {
      if (newVal) {
        const mapElement = document.querySelector('.tileMap'); // 地图视图的 DOM 元素
        mapElement.style.cursor = 'pointer'; // 设置鼠标样式为手
      } else {
        const mapElement = document.querySelector('.tileMap');
        mapElement.style.cursor = 'default'; // 恢复默认鼠标样式
      }
    },
    isRobotFollow(newVal, oldVal) {
      // 如果开启位置跟随
      if (newVal) {
        this.followRotation();
      } else {
        // 关闭位置跟随
        this.map.getView().setRotation(0);
      }
    },
    robotPoseData: {
      handler(newVal, oldVal) {
        this.robotPoseUpdateCallback(newVal.x, newVal.y, newVal.angle)
      },
      deep: true
    },
  poseCalibrationIndex(index) {
      if (index < 0) {
        const id = Math.abs(index);
        this.removeCalibrationById(id);
      } else {
        const newId = this.calibrationList.length + 1;
        const point = { id: newId, x: this.robotPoseData.x, y: this.robotPoseData.y };
        this.calibrationList.push(point);
        this.refreshCalibrationOverlays();
      }
    },
/*     poseCalibrationIndex(index, oldVal) {
      // 如果传递负值则删除标定点
      if (index < 0) {
        let id = Math.abs(index) // 求绝对值
        this.removeIconHtmlById('calibration-' + id);
        return;
      }
      // 添加坐标标定点
      this.addHtmlIcon(index, this.robotPoseData.x, this.robotPoseData.y, '', 'calibration');
    }, */
    // 位姿初始化
    poseInitEnable(newVal, oldVal) {
      if (newVal) {
        const mapElement = document.querySelector('.tileMap'); // 地图视图的 DOM 元素
        mapElement.style.cursor = 'pointer'; // 设置鼠标样式为手
      } else {
        const mapElement = document.querySelector('.tileMap');
        mapElement.style.cursor = 'default'; // 恢复默认鼠标样式
      }
    },
    // 点云显示控制
    showPointcloud(newVal) {
      if (this.pointcloudLayer) {
        this.pointcloudLayer.setVisible(newVal);
        if (newVal) {
          // 开启点云时启动定时器
          this.startPointcloudUpdate();
        } else {
          // 关闭点云时清除定时器
          this.stopPointcloudUpdate();
          // 清空点云数据
          const features = this.pointcloudLayer.getSource().getFeatures();
          if (features.length > 0) {
            features[0].setGeometry(new MultiPoint([]));
          }
        }
      }
    },
    // 路网显示控制
    showRoadNetwork(newVal, oldVal) {
      console.log(`showRoadNetwork watch触发: ${oldVal} -> ${newVal}`);
      // 使用图层级别的可见性控制，而不是修改样式
      if (this.roadmapLayer) {
        this.roadmapLayer.setVisible(newVal);
        console.log(`✅ 路网图层可见性已设置为: ${newVal}`);
      } else {
        console.warn(`⚠️ roadmapLayer 不存在，无法设置可见性`);
      }
    }
  },

  mounted() {
    this.localPointEvent = (this.pointEvent.toString() == "function(){}" || this.pointEvent.toString() == "function _default() {}") ? this.defaultPointClick : this.pointEvent
    this.loadMap();
    console.log("mapName地图组件:", this.mapName);
  },
  methods: {
    // 点击添加点位的操作
    addNowPoint() {
      // 追加当前选择的点位数据到数据列表
      this.$emit("addNowPoint", this.currentCoordinate, this.currentPlace);
      this.hideToop();
      this.addHtmlIcon(this.currentCoordinate[0], this.currentCoordinate[1], this.currentCoordinate[2], this.icon.mark, "pose");
    },
    // 点击取消点位的操作
    clearNowPoint() {
      this.hideToop();
      this.currentCoordinate = this.currentPlace = [];
    },
    // 默认的点位点击时的调用方法
    defaultPointClick() {

    },
    // 重新加载画布
    reloadMapCanvas() {
      console.warn('🚨 reloadMapCanvas被调用!', {
        hasGeojsonData: !!this.geojson_data,
        currentFeatureCount: this.roadmap_src ? this.roadmap_src.getFeatures().length : 0,
        stackTrace: new Error().stack
      });
      this.initMapHaveDate();
      this.initRoad(this.geojson_data);
    },
    //加载地图数据
    loadMap() {
      console.warn('🚨 loadMap被调用!', {
        mapName: this.mapName,
        hasExistingData: this.roadmap_src ? this.roadmap_src.getFeatures().length : 0,
        stackTrace: new Error().stack
      });
      
      let time = new Date().getTime();
      let configJsonUrl = this.url + '/v1/tilemap/details?map=' + this.mapName + '&' + time;
      // let mapJsonUrl = this.url + "/shapefile/map.json?" + time
      let mapJsonUrl = this.url + '/v1/roadmap/geojson/?map=' + this.mapName + '&' + time;
      axios.get(configJsonUrl).then((res) => {
        // console.log("获取到无人车关联的地图信息:", res.data);
        this.config_json = res.data;
        axios.get(mapJsonUrl).then((res) => {
          console.log('loadMap: 收到路网数据');
          this.geojson_data = res.data;
          this.initMapHaveDate();
          this.initRoad(this.geojson_data);
        }).catch(() => {
          console.log("未获取到地图路径信息!");
        });
      }).catch(() => {
        this.setMapText();
        console.log("未获取到无人车关联的地图信息!");
      });
    },

    // 设置中心的文字
    setMapText(str = '') {
      str = str == "" ? '加载地图错误' : str;
      let obj = document.getElementById("mapParent");
      if (obj) {
        obj.innerHTML = "<span class='maptext' style='font-size: 20px;color: grey;height: 100%'>" +
          (this.textShow ? ("<span>" + str + "</span><br>") : ("<span></span>")) +
          "</span>";
      }
    },

    initMapHaveDate() {
      if (!this.config_json || this.config_json == "") {
        return;
      }
      var mapNode = document.getElementById("tileMap");
      if (mapNode) {
        document.getElementById("mapParent").removeChild(mapNode);
      }
      document.getElementById("mapParent").innerHTML = "<div id='tileMap' class='tileMap' style='height:" + this.height + "'></div>";
      this.initMap(this.config_json);
      if(this.isShowRobot){
        this.initRobot(); // 加载robot图标
      }
    },

    initMap(config_json) {      
      if (!config_json || config_json == "") {
        return;
      }

      var tile_param = {
        layer_cnt:config_json.layer_cnt,
        min_zoom: config_json.min_zoom,
        max_zoom: config_json.max_zoom,
        projection: config_json.projection

      };
/*       var tile_param = {
        layer_cnt:5,
        projection: {
          extent: 
            [
                -122.3002398071289,
                -178.81439428710937,
                287.29976019287113,
                230.78560571289066
            ]
        },
         min_zoom: 1,
         max_zoom:7
      } */

      // 创建openlayers Map对象，target是html中地图<div>对应的id
      this.map = new Map({
        target: "tileMap",
        layers: [],
        view: null,
        // interactions: InteractionDefaults().extend([new DragRotateAndZoom()]),
        // 关闭点击放大放小交互
        interactions: new InteractionDefaults({ doubleClickZoom: false }),
        // 根据prop决定是否显示默认控件
        controls: this.showDefaultControls ? undefined : [],
      });
      // 初始化位姿初始化所需的变量
      let startPoint = null; // 起点坐标
      let isDrawing = false;
      // 设置地图的点击事件
      let that = this
      this.map.on('click', (evt) => {
        // 开启点位选择模式则执行下面逻辑(地图选择功能,添加目标点等)
        // todo 这一部分后期优化一下, 直接传递数组进来用于控制启停那些事件
        if (this.pointSelectionEnabled) {
          // 获取画布
          let pixel = evt.pixel;
          // 获取元素数据
          let feature = this.map.forEachFeatureAtPixel(evt.pixel, (feature) => feature);
          // 获取元素的坐标
          let xy;
          if (feature && (feature.values_.id || feature.getId()) && feature.getGeometry().getType() == 'Point') {
            // 如果不为空并且是点位,则直接取这个点位的坐标 todo 如果是点位则只有一条xyz如果是线则是一组
            // (feature.getGeometry().getType() === 'LineString') 也可以判断类型
            xy = feature.getGeometry().getCoordinates();
          } else {
            // 选取画布坐标
            xy = evt.coordinate;
          }
          let longitude = pixel[0];
          let latitude = pixel[1];
          // 获取动态内容的容器
          const tooltipContent = document.getElementById('tooltip-content');
          // 清空动态内容
          tooltipContent.innerHTML = '';
          // 添加新的内容
          tooltipContent.innerHTML += `<p>确认在此位置添加目标点？</p>`;
          tooltipContent.innerHTML += `<p>X: ${xy[0].toFixed(3)}, Y: ${xy[1].toFixed(3)}</p>`;
          // 设置 tooltip 的位置和显示
          const tooltip = document.getElementById('tooltip');
          tooltip.style.display = 'block';
          tooltip.style.left = longitude + 70 + 'px';
          tooltip.style.top = latitude + 'px';
          // 将当前点击点位的画面坐标和地理信息坐标记录
          this.maxIndex++;
          that.currentCoordinate = [];
          that.currentCoordinate.push(...xy);
          that.currentCoordinate.unshift(this.maxIndex);
          that.currentPlace = pixel;
          // currentCoordinate此数组为[id,x,y,z]
        }
        // 导航页面初始化位置逻辑（参考robot_map_editor实现）
        if (this.poseInitEnable) {
          let feature = this.map.forEachFeatureAtPixel(evt.pixel, (feature) => feature);
          let coord;
          // 修复：只有在 feature 存在且有 getId 方法时才调用
          let nid = (feature && feature.getId) ? feature.getId() : null;
          if (feature && feature.getGeometry().getType() == 'Point') {
            // 如果不为空并且是点位,则直接取这个点位的坐标
            coord = feature.getGeometry().getCoordinates();
          } else {
            // 选取画布坐标（包括空白区域和其他类型的feature）
            coord = evt.coordinate;
          }
          if (!isDrawing) {
            // 第一次点击：记录起点
            startPoint = coord;
            isDrawing = true;
            // 显示提示信息
            this.$message({
              message: '起点已设置，请点击第二个位置确定机器人朝向（或右键取消）',
              type: 'info',
              duration: 3000
            });
          } else {
            // 第二次点击：结束绘制，计算航向角并发送
            this.completeDrawing(startPoint, coord, nid);
            // 清理预览要素
            if (this.arrowFeature) {
              this.roadmap_src.removeFeature(this.arrowFeature);
              this.arrowFeature = null;
            }
            isDrawing = false;
            startPoint = null;
          }
        }
        // 编辑绘图页面元素选择模式逻辑
        if (this.pointDraSelectEnabled) {
          let feature = this.map.forEachFeatureAtPixel(evt.pixel, (feature) => feature);
          this.editElementSelectHandle(feature);
        }
      });
      // 箭头鼠标移动事件
      this.map.on('pointermove', (evt) => {
        // 导航页面位姿初始化的预览箭头
        if (this.poseInitEnable) {
          if (isDrawing && startPoint) {
            const endPoint = evt.coordinate;

            // 动态预览虚线（参考robot_map_editor的实现风格）
            if (!this.previewFeature) {
              // 创建线段 Feature - 使用渐变色和更醒目的样式
              const line = new LineString([startPoint, endPoint]);
              this.previewFeature = new Feature(line);
              this.previewFeature.setStyle(
                new Style({
                  stroke: new Stroke({
                    color: 'rgba(255, 50, 50, 0.8)',  // 更鲜艳的红色
                    width: 5,
                    lineDash: [10, 5],  // 添加虚线效果，更易识别
                  }),
                })
              );
              this.roadmap_src.addFeature(this.previewFeature);

              // 创建自定义箭头 Feature - 改进箭头绘制逻辑
              this.arrowFeature = new Feature({
                geometry: new LineString([startPoint, endPoint]),
              });
              this.arrowFeature.setStyle(
                new Style({
                  renderer: (pixelCoordinates, state) => {
                    const context = state.context;

                    // 起点和终点的像素坐标
                    const [startX, startY] = pixelCoordinates[0];
                    const [endX, endY] = pixelCoordinates[1];

                    // 计算箭头方向
                    const dx = endX - startX;
                    const dy = endY - startY;
                    const length = Math.sqrt(dx * dx + dy * dy);
                    
                    // 如果线段太短，不绘制箭头
                    if (length < 10) return;
                    
                    const angle = Math.atan2(dy, dx);

                    // 箭头参数（参考robot_map_editor的比例）
                    const arrowLength = 25;  // 箭头长度
                    const arrowAngle = Math.PI / 6;  // 箭头张角（30度）
                    
                    // 箭头尖端位置（在终点）
                    const tipX = endX;
                    const tipY = endY;
                    
                    // 计算箭头两侧的点
                    const leftX = tipX - arrowLength * Math.cos(angle - arrowAngle);
                    const leftY = tipY - arrowLength * Math.sin(angle - arrowAngle);
                    const rightX = tipX - arrowLength * Math.cos(angle + arrowAngle);
                    const rightY = tipY - arrowLength * Math.sin(angle + arrowAngle);

                    // 绘制实心箭头
                    context.beginPath();
                    context.moveTo(tipX, tipY);
                    context.lineTo(leftX, leftY);
                    context.lineTo(rightX, rightY);
                    context.closePath();
                    context.fillStyle = 'rgba(255, 50, 50, 0.9)';
                    context.fill();
                    
                    // 添加箭头边框，使其更清晰
                    context.strokeStyle = 'rgba(200, 0, 0, 1)';
                    context.lineWidth = 2;
                    context.stroke();
                    
                    // 在起点绘制一个小圆点作为起始标记
                    context.beginPath();
                    context.arc(startX, startY, 6, 0, 2 * Math.PI);
                    context.fillStyle = 'rgba(50, 150, 255, 0.8)';
                    context.fill();
                    context.strokeStyle = 'rgba(0, 100, 200, 1)';
                    context.lineWidth = 2;
                    context.stroke();
                  },
                })
              );
              this.roadmap_src.addFeature(this.arrowFeature);
            } else {
              // 更新动态虚线坐标
              this.previewFeature.getGeometry().setCoordinates([startPoint, endPoint]);

              // 更新箭头的坐标（箭头样式自动重绘）
              this.arrowFeature.getGeometry().setCoordinates([startPoint, endPoint]);
            }
          }
        }
      });
      // 箭头鼠标右键事件（取消初始化）
      this.map.on('contextmenu', (evt) => {
        // 如果正在初始化,则取消初始化
        if (this.poseInitEnable && isDrawing) {
          // 阻止右键菜单弹出
          evt.preventDefault();
          // 删除虚线和箭头
          if (this.previewFeature) {
            this.roadmap_src.removeFeature(this.previewFeature);
            this.previewFeature = null;
          }
          if (this.arrowFeature) {
            this.roadmap_src.removeFeature(this.arrowFeature);
            this.arrowFeature = null;
          }
          // 重置绘制状态
          isDrawing = false;
          startPoint = null;
          // 显示取消提示
          this.$message({
            message: '位姿初始化已取消',
            type: 'warning',
            duration: 2000
          });
        }
      });

      // 设置地图视图相关的参数, 这里就用到了使用http获取到的瓦片地图参数
      this.map.setView(
        new View({
          projection: new Projection({ extent: tile_param.projection.extent }),
          // center: [config_json.RotateCenterX, config_json.RotateCenterY],
          center: [0, 0],
          zoom: tile_param.min_zoom,
          minZoom: tile_param.min_zoom,
          maxZoom: tile_param.max_zoom,
        })
      );

      // 创建一个图层，专门用于展示瓦片地图，这里的关键是要设置好url，这个url就是LD导航系统提供的获取瓦片对应的接口
      this.baseLayer = new TileLayer({
        source: new SrcXYZ({
          url: this.url+ '/v1/tilemap/tile/'+this.mapName+'/{z}/{x}/{y}.png',
          wrapX: true,
          projection: new Projection({extent: tile_param.projection.extent})
        }),
      });
      this.map.addLayer(this.baseLayer);
      
      // 创建点云图层
      this.initPointcloudLayer();
      
      // this.map.addLayer(
      //       new TileLayer({
      //           source: new SrcXYZ({
      //               url: this.url+ '/v1/tilemap/tile/'+this.mapName+'/{z}/{x}/{y}.png',
      //               wrapX: true,
      //               projection: new Projection({extent: tile_param.projection.extent})
      //           })
      //       })
      //   );
/*       this.baseLayer = new TileLayer({
        source: new SrcXYZ({
          tileUrlFunction: ($xyz) => {
            // 引入离线瓦片
            var tile_zoom = $xyz[0] - 1;
            var map_col_total = config_json["Layers"][tile_zoom]["ColCount"];
            var map_row_total = config_json["Layers"][tile_zoom]["RowCount"];
            var std_row_total = 2 ** (tile_zoom + 1);

            var tile_col = $xyz[1];
            var tile_row = $xyz[2];
            var offset_row = std_row_total - 1 - tile_row;
            var map_row = map_row_total - 1 - offset_row;
            if (
              tile_col < 0 ||
              map_row < 0 ||
              tile_col > map_col_total - 1 ||
              map_row > map_row_total - 1
            ) {
              return;
            } else {
              var index = tile_col * map_row_total + map_row;
              // 从json文件中获取瓦片路径
              var tile_path = config_json["Layers"][tile_zoom]["Images"][index]["Path"];
              return (this.url + "/tilemap/" + tile_path);
              // return "http://8.148.78.124:10001/v1/tilemap/tile/demo/4/2/10"
            }
          },
          wrapX: true,
          projection: new Projection({
            extent: tile_param.projection.extent,
          }),
        }),
      });
      this.map.addLayer(this.baseLayer); */
    },

    initRoad(geojson_data) {
      console.log('initRoad被调用，当前数据:', {
        hasGeojsonData: !!geojson_data,
        geojsonDataType: typeof geojson_data,
        hasRoadmapSrc: !!this.roadmap_src,
        currentFeatureCount: this.roadmap_src ? this.roadmap_src.getFeatures().length : 0,
        stackTrace: new Error().stack
      });
      
      if (!geojson_data || geojson_data == "") {
        console.log('initRoad: 没有数据，退出');
        return;
      }
      
      // 🛡️ 严格保护：如果已经有用户创建/编辑的数据，绝对不能清除
      if (this.roadmap_src && this.roadmap_src.getFeatures().length > 0) {
        console.warn('🛡️ 保护模式：地图已有数据，禁止重新加载以避免清除用户编辑的数据');
        console.log('当前特征数量:', this.roadmap_src.getFeatures().length);
        console.log('特征ID列表:', this.roadmap_src.getFeatures().map(f => f.getId()));
        
        // 🚨 紧急保护：即使被调用也绝对不能执行任何清除操作
        console.error('🚨 严重警告：试图在有数据时重新初始化地图，已被阻止！');
        return;
      }
      
      // 🛡️ 双重保护：检查是否在编辑模式下（edit.vue页面）
      if (this.$parent && this.$parent.$options.name === 'EditPage') {
        console.warn('🛡️ 编辑模式保护：检测到编辑页面，额外保护机制启动');
      }
      
      console.log('initRoad: 开始初始化路网数据');
      
      // 如果roadmap_src已经存在，先清除现有features，但保留source对象
      if (this.roadmap_src) {
        console.log('initRoad: 清除现有features');
        this.roadmap_src.clear();
        // 确保图层可见性与prop一致
        if (this.roadmapLayer) {
          this.roadmapLayer.setVisible(this.showRoadNetwork);
          console.log(`路网图层可见性更新为: ${this.showRoadNetwork}`);
        }
      } else {
        // 只有在第一次初始化时才创建新的VectorSource和图层
        console.log('initRoad: 创建新的VectorSource和图层');
        this.roadmap_src = new VectorSource();
        this.roadmapLayer = new VectorLayer({
          source: this.roadmap_src,
          style: this.customizedStyle,
          visible: this.showRoadNetwork // 使用prop的初始值
        });
        this.map.addLayer(this.roadmapLayer);
        console.log(`路网图层初始可见性: ${this.showRoadNetwork}`);
      }
      // 将geojson_data转换成openlayers的features
      var features = new GeoJSON().readFeatures(geojson_data);
      // 批量补充正确的格式id(原有地图将id设置到了values内部,导致使用id获取元素无法获取)
      features.forEach(feature => {
        // 从 values_ 中获取 id
        const innerId = feature.values_.id;
        // 使用 setId() 将 values_ 中的 id 设置到外层 id
        if (!feature.getId()) {
          feature.setId(innerId);
        }
      });
      // 实际上这里还有一些工作需要做，比如根据贝塞尔曲线控制点生成贝塞尔曲线，可参考《GeoJSON路网数据结构说明》
      // 1. 移除自动生成的可行驶区域
      features = features.filter((f) => {
        if (!f.getProperties().id || f.getProperties().id === "s_auto" || f.getProperties().id.startsWith('s_')) return false;
        return true;
      });
      // 2. 贝塞尔曲线处理
      for (var idx in features) {
        if (
          features[idx].getGeometry().getType() === "MultiPoint" &&
          features[idx].getProperties().id.startsWith("b_")
        ) {
          // 🔧 保存原始控制点（来自GeoJSON的MultiPoint坐标）
          const controlPoints = features[idx].getGeometry().getCoordinates();
          features[idx].set('bezierControlPoints', controlPoints);
          
          // 获取离散化的贝塞尔曲线，genBezierPointsByControlPoints函数
          var newBezierPoints = this.genBezierPointsByControlPoints(
            controlPoints,
            0.01
          );
          // 将MultiPoint替换为贝塞尔LineString
          features[idx].setGeometry(new LineString(newBezierPoints));
        }
      }
      
      // 🔧 3. 修复普通 LineString 中的中间点问题（加载旧数据时的兼容性处理）
      console.log('开始修复 LineString 中的中间点...');
      let fixedLineCount = 0;
      for (var idx in features) {
        const feature = features[idx];
        const featureId = feature.getProperties().id;
        
        // 只处理普通直线（l_开头），不处理贝塞尔曲线（b_开头）
        if (feature.getGeometry().getType() === "LineString" && featureId && featureId.startsWith("l_")) {
          const coordinates = feature.getGeometry().getCoordinates();
          
          // 如果线段包含超过2个点（即有中间点）
          if (coordinates.length > 2) {
            console.warn(`检测到线段 ${featureId} 包含 ${coordinates.length} 个点，正在修复为只包含起点和终点`);
            
            // 只保留起点和终点
            const fixedCoordinates = [
              coordinates[0],  // 起点
              coordinates[coordinates.length - 1]  // 终点
            ];
            
            // 更新线段的坐标
            feature.getGeometry().setCoordinates(fixedCoordinates);
            fixedLineCount++;
            
            console.log(`✅ 线段 ${featureId} 已修复: ${coordinates.length} 个点 -> 2 个点`);
          }
        }
      }
      
      if (fixedLineCount > 0) {
        console.warn(`⚠️ 共修复了 ${fixedLineCount} 条包含中间点的线段`);
        console.warn(`💡 提示: 这些线段已自动修复，请保存地图以更新服务器数据`);
      } else {
        console.log('✅ 所有 LineString 格式正确，无需修复');
      }
      
      // 将features添加到图层
      this.roadmap_src.addFeatures(features);
      
      // 确保路网图层的可见性与prop一致（最终保障）
      if (this.roadmapLayer) {
        this.roadmapLayer.setVisible(this.showRoadNetwork);
        console.log(`路网加载完成，图层可见性: ${this.showRoadNetwork}`);
      }
      
      // 路线加载完毕后将当前地图的所有元素回执到父组件
      this.elementRoadInitEnd(features);
    },

    // 模式切换逻辑
    togglePointSelection() {
      this.pointSelectionEnabled = !this.pointSelectionEnabled;
      if (this.pointSelectionEnabled) {
        this.$message.success('点位选择模式已开启');
      } else {
        this.$message.info('点位选择模式已关闭');
      }
    },

    /**
     * 贝塞尔曲线
     */
    genBezierPointsByControlPoints(controlPoints, step = 0.05) {
      var bezierPoints = [];
      var n = controlPoints.length - 1;
      var x = 0,
        y = 0;
      for (var i = 0; i < 1; i += step) {
        x = 0;
        y = 0;
        for (let idx = 0; idx < controlPoints.length; idx++) {
          const coord = controlPoints[idx];
          if (!idx) {
            x += coord[0] * Math.pow(1 - i, n - idx) * Math.pow(i, idx);
            y += coord[1] * Math.pow(1 - i, n - idx) * Math.pow(i, idx);
          } else {
            x +=
              (this.factorial(n) /
                this.factorial(idx) /
                this.factorial(n - idx)) *
              coord[0] *
              Math.pow(1 - i, n - idx) *
              Math.pow(i, idx);
            y +=
              (this.factorial(n) /
                this.factorial(idx) /
                this.factorial(n - idx)) *
              coord[1] *
              Math.pow(1 - i, n - idx) *
              Math.pow(i, idx);
          }
        }
        bezierPoints.push([x, y]);
      }
      bezierPoints.push(controlPoints[n]);
      return bezierPoints;
    },

    factorial(num) {
      return num <= 1 ? 1 : num * this.factorial(num - 1);
    },

    // robot 位置信息映射
    initRobot() {
      
      var robot_feature = new Feature({
        geometry: new Point([this.robotPoseData.x, this.robotPoseData.y]),
      });
      robot_feature.setStyle(
        new Style({
          image: new Icon({
            src: require("@/assets/icons/olmap/robot.png"),
            rotateWithView: true,
            rotation: 0   // 设置一个比轨迹线大的值
            // rotation: Math.PI - this.robotPoseData.angle
          }),
        })
      );

      // 给feature设置一个id，后面搜索会用到
      robot_feature.setId("robot");

      // 创建source，并将生成好的无人车feature添加进来
      this.robot_src = new VectorSource({});
      this.robot_src.addFeature(robot_feature);

      // 将source添加到layer
      this.map.addLayer(
        new VectorLayer({
          source: this.robot_src,
          zIndex: 10 
        })
      );
    },
    // 无人车位姿更新 (查询完毕无人车实时信息后调用此方法)
    robotPoseUpdateCallback(x, y, yaw) {
      // 这就是前面需要设置id的目的，可以根据id在对应的source中找到对应的feature
      try {
        var robot_feature = this.robot_src.getFeatureById("robot");
      } catch (error) {
        return;
      }

      if (robot_feature === null) {
        return;
      }
      // 更新航向角
      robot_feature
        .getStyle()
        .getImage()
        .setRotation(Math.PI - yaw); // 解释,由于原始图标朝向左边,因此需要用Math.PI减方位角
      // 更新位置
      robot_feature.getGeometry().setCoordinates([x, y]);
      // 如果开启了位置跟随需要持续调整跟随状态
      if (this.isRobotFollow) {
        this.followRotation();
      }
    },
    // 跟随无人车图标
    followRotation() {
      var robot_feature = this.robot_src.getFeatureById("robot");
      // 获取图标的方向，单位是弧度（以北为基准的方向角）
      const direction = robot_feature.getStyle().getImage().getRotation();
      // 获取图标的坐标 
      const center = robot_feature.getGeometry().getCoordinates();
      // 设置地图的中心为图标的位置
      this.map.getView().setCenter(center);
      // 为地图视图添加旋转动画，确保动画代替立即调用setRotation
      this.map.getView().animate({
        rotation: -direction,  // 旋转角度（使用图标的朝向）
        duration: 1000,        // 动画持续时间：1秒
        easing: easeInOut,     // 缓动效果：渐进渐出
      });
    },
    /**
     * 增加icon图标
     * @param {String} [type="ailarm"] 前缀类型,默认ai告警
     * @param {Number} id id
     * @param {Number} x 坐标
     * @param {Number} y y坐标
     * @param {String} iconSrc 图标地址
     * @param {String} scale 缩放比例
     */
    initIcon(id, x, y, iconSrc, type = "", scale = 0.15, zIndex) {
      // 创建一个新的图标Feature
      var icon_feature = new Feature({
        geometry: new Point([x, y]),
      });

      // 设置图标样式
      icon_feature.setStyle(
        new Style({
          image: new Icon({
            src: iconSrc,             // 图标的路径
            rotateWithView: true,     // 是否随着视图旋转
            rotation: 0,              // 图标的初始旋转角度
            scale: scale,             // 图标的缩放比例
          }),
        })
      );
      // 给feature设置一个id，点击事件会用到
      icon_feature.setId(type + "-" + id);
      // 创建一个source，并将生成的图标feature添加到source
      this.icon_src = new VectorSource({});
      this.icon_src.addFeature(icon_feature);
      // 创建一个VectorLayer，如果传入了zIndex，则设置zIndex，否则不设置
      let icon_layer = new VectorLayer({
        source: this.icon_src,
      });
      // 如果传入了zIndex，则设置图层的zIndex
      if (zIndex !== undefined) {
        icon_layer.setZIndex(zIndex);
      }
      // 将图标图层添加到地图中
      this.map.addLayer(icon_layer);
    },
    // 拓展的html元素类型图标(可自己拓展,创建好div后调用initIconHtml生成)
    addHtmlIcon(id, x, y, iconSrc, type = "") {
      const iconDiv = document.createElement("div");
      iconDiv.className = "map-icon";
      iconDiv.style.width = "22px";
      iconDiv.style.height = "22px";
      iconDiv.style.borderRadius = "50%";
      iconDiv.style.border = "2px solid #ffffff";
      iconDiv.style.backgroundColor = "#4096BD";
      iconDiv.style.backgroundSize = "cover";
      iconDiv.style.position = "relative";  // 确保内部绝对定位元素不会影响外部尺寸
      iconDiv.style.overflow = "hidden";  // 防止溢出

      // 创建一个数字，并将其添加到 iconDiv 中
      const numberSpan = document.createElement("span");
      numberSpan.textContent = id;  // 设置居中的数字内容
      numberSpan.style.color = "#ffffff"; // 数字颜色
      numberSpan.style.fontSize = "14px"; // 数字字体大小
      numberSpan.style.fontWeight = "bold"; // 数字加粗
      numberSpan.style.position = "absolute";  // 绝对定位
      numberSpan.style.top = "50%";  // 垂直居中
      numberSpan.style.left = "50%";  // 水平居中
      numberSpan.style.transform = "translate(-50%, -50%)";  // 通过 transform 精确居中
      numberSpan.style.lineHeight = "22px";  // 设置行高来确保文本垂直居中
      numberSpan.style.whiteSpace = "nowrap";  // 防止文本换行
      iconDiv.appendChild(numberSpan);
      this.initIconHtml(iconDiv, x, y, id, type);
    },
    // 加载html元素类型到图标模式
    initIconHtml(div, x, y, id, type) {
      // 创建Overlay并将div挂载到地图
      const overlay = new Overlay({
        element: div,
        position: [x, y],
        positioning: "center-center", // 定义div的定位方式
        stopEvent: false, // 允许事件穿透
      });
      // 设置ID，便于后续操作
      // overlay.set("id", type + "-" + id);
      overlay.set("id", `calibration-${id}`);
      overlay.set("type", type);
      // 添加Overlay到地图
      this.map.addOverlay(overlay);
    },

    // 删除标定点并重排编号
    removeCalibrationById(id) {
      // 从数据中删除并重排 id
      this.calibrationList = this.calibrationList.filter(item => item.id !== id);
      this.calibrationList.forEach((item, idx) => { item.id = idx + 1; });

      // 刷新覆盖物
      this.refreshCalibrationOverlays();
    },
    // 刷新所有标定点覆盖物
    // 清空所有并重绘（安全删除方式）
    refreshCalibrationOverlays() {
      // 1) 先把当前 overlays 复制到数组中（避免在遍历时修改集合导致跳过）
      const all = this.map.getOverlays().getArray().slice();

      // 2) 找到所有需要删除的 overlay（通过 type 或 id 前缀判断）
      const toRemove = all.filter(ov => {
        try {
          return ov && typeof ov.get === 'function' &&
            (ov.get('type') === 'calibration' ||
             (typeof ov.get('id') === 'string' && ov.get('id').startsWith('calibration-')));
        } catch (e) {
          return false;
        }
      });

      // 3) 统一删除
      toRemove.forEach(ov => this.map.removeOverlay(ov));

      // 4) 重新绘制（按 this.calibrationList 顺序）
      this.calibrationList.forEach(item => {
        this.addHtmlIcon(item.id, item.x, item.y, '', 'calibration');
      });

      // debug: 打印当前 overlay id 列表，方便排查
      console.log('calibrationList:', JSON.stringify(this.calibrationList));
      console.log('overlay ids:', this.map.getOverlays().getArray().map(o => o.get && o.get('id')));
    },


    // 删除html绘制的图标
    removeIconHtmlById(id) {
      const overlays = this.map.getOverlays(); // 获取所有 Overlay
      if (!overlays) {
        return;
      }
      overlays.forEach((overlay) => {
        if (!overlay || typeof overlay.get !== 'function') {
          return;
        }
        if (overlay.get("id") === id) {
          this.map.removeOverlay(overlay); // 移除匹配的 Overlay
        }
      });
    },
    // customizedStyle实现
    // openlayers在渲染点、线、面feature的时候，会调用此函数，传入feature对象，返回style实例对象
    customizedStyle(feature) {
      // 根据不同的类型，返回不同的style
      switch (feature.getGeometry().getType()) {
        case "Point":
          return this.styleForPoint(feature);
        case "LineString":
          return this.styleForLine(feature);
        case "Polygon":
          return this.styleForPolygon(feature);
        default:
          return null;
      }
    },

    // 点样式设置, 如果存在pointList点位列表，则判断加载的点在不在列表中，如果不在，则不加载名称
    // 如果existPointShow为真，则只加载系统录入过的节点
    styleForPoint(feature) {
      let id = feature.values_.id;
      let point = this.pointAll.find(item => item.point == id.substring(2));
      if (this.existPointShow && id.startsWith("p_") && this.pointAll.length > 0 && !point) {
        return;
      }

      // 加载名称
      let textInfo = ""
      if (this.pointSwitch && point) {
        textInfo = feature.getProperties().id.slice(2) + "-" + point.pointName;
      } else if (this.pointSwitch) {
        textInfo = feature.getProperties().id.slice(2);
      }
      let that_ = this;
      return [
        new Style({
          // 用图片(小圆圈)表示一个点
          image: new Icon({
            src: require("@/assets/icons/olmap/normal_point.png"),
            anchor: [0.5, 0.5],
          }),
          text: new Text({
            font: 'normal 14px 黑体',
            // // 对其方式
            textAlign: 'center',
            // 基准线
            textBaseline: 'middle',
            offsetY: -15, // -10
            offsetX: 0,
            // 文本填充样式
            fill: new Fill({
              color: 'rgb(12, 239, 23, 0.8)'
            }),
            // padding: [1, 1, 0, 1],
            text: [textInfo, 'bold 14px sans-serif'],
            backgroundFill: new Fill({
              color: that_.pointSwitch ? 'rgb(255,101,71, 1.0)' : 'rgb(255,101,71, 0.0)'
            }),
          }),
        }),
      ];
    },

    // 线段样式设置
    styleForLine(feature) {
      // 在线上贴上方向箭头，显示路线方向
      const direct = feature.getProperties().direct - 100;
      const s2e = (direct & 12) > 0; // end to start
      const e2s = (direct & 3) > 0; // start to end

      // 根据道路方向匹配响应的箭头图标
      var img = "";
      if (s2e && !e2s)
        // 起点到终点单向道
        img = require("@/assets/icons/olmap/dir_forward.png");
      else if (!s2e && e2s)
        // 终点到起点单向道
        img = require("@/assets/icons/olmap/dir_backward.png");  // 大红点修改为箭头
      // img = require("@/image/dir_backward_red.png");
      // 双向道
      else img = require("@/assets/icons/olmap/dir_two_way.png");

      // 箭头图标需要贴到整条线段的最中间位置，且方向指向和该点的切线方向
      var coords = feature.getGeometry().getCoordinates();

      // 找到整条线段最中间的两个点
      var startIdx = Math.floor(coords.length / 2) - 1;
      var endIdx = Math.ceil(coords.length / 2);
      var start = coords[startIdx];
      var end = coords[endIdx];

      // 根据这两个点算出在该位置的切线方向，这个方向就是箭头需要对准的方向
      var dx = end[0] - start[0];
      var dy = end[1] - start[1];
      var rotation = Math.atan2(dy, dx);

      return [
        // 线段样式
        new Style({
          stroke: new Stroke({
            color: "#00BFFF", // 在这里改线条颜色
            width: 6, // 这里改线条宽度
          }),
        }),

        // 方向图标样式
        new Style({
          geometry: new Point([end[0] - dx / 2, end[1] - dy / 2]), // 显示在线条中心位置
          // 贴图
          image: new Icon({
            src: img,
            anchor: [0.75, 0.5],
            rotateWithView: true,
            rotation: -rotation, // 箭头朝向
          }),
        }),
      ];
    },
    // 区域样式设置
    styleForPolygon(feature) {
      // 从 feature 的属性中获取颜色值
      let color = feature.getProperties().color || '#7EFFFA'; // 默认颜色
      // 获取透明度属性（假设透明度值在 0 到 255 之间）
      let transparency = feature.getProperties().transparent;
      transparency = (transparency >= 0 && transparency <= 255) ? transparency : 128;  // 默认透明度为 128（中等透明）
      // 根据颜色和透明度拼接出带透明度的颜色，透明度值直接使用透明度（0-255）范围
      const transparentColor = this.hexToRGBA(color, transparency);
      return [
        new Style({
          // 区域颜色填充
          fill: new Fill({
            color: transparentColor, // 使用带透明度的颜色
          }),
          // 区域名称展示
          text: new Text({
            text: feature.getProperties().name || '',
            font: '16px serif',
            fill: new Fill({
              color: '#000',  // 文字颜色
            }),
            stroke: new Stroke({
              color: '#fff',  // 文字边框颜色
              width: 3,       // 边框宽度
            }),
          }),
        }),
      ];
    },
    // 辅助方法：将颜色和透明度转为 RGBA 格式的字符串
    hexToRGBA(hex, transparency) {
      // 将 hex 颜色转为 rgba 格式
      if (hex.startsWith('#')) {
        hex = hex.slice(1);  // 去掉 '#' 字符
      }

      // 如果是短格式的 hex 颜色 (#RGB)，则扩展为完整的六位 (#RRGGBB)
      if (hex.length === 3) {
        hex = hex.split('').map(c => c + c).join('');
      }

      // 提取 RGB 颜色值
      let r = parseInt(hex.slice(0, 2), 16);
      let g = parseInt(hex.slice(2, 4), 16);
      let b = parseInt(hex.slice(4, 6), 16);

      // 将透明度从 0-255 范围转换为 0-1 范围
      const alpha = transparency / 255;

      // 返回 RGBA 格式的字符串
      return `rgba(${r}, ${g}, ${b}, ${alpha})`;
    },
    // 匹配icon
    getAlarmIcon(type) {
      if (this.iconSrc.hasOwnProperty(type)) {
        return this.iconSrc[type];
      }
      return this.iconSrc["default"];
    },
    // 隐藏选择弹窗
    hideToop() {
      const tooltip = document.getElementById('tooltip');
      tooltip.style.display = 'none';
    },
    // 重置id下标
    restIdNum() {
      this.maxIndex = 0;
    },
    // 开始绘制点
    drawPoint() {
      this.clearDraw();  // 清除之前的绘制
      this.draw = null;
      this.draw = new Draw({
        source: this.roadmap_src,
        type: 'Point',
      });
      // 创建Snap交互，启用吸附到最近的点或者线
      this.snap = new Snap({
        source: this.roadmap_src,         // 吸附到roadmap_src中的点和线
        pixelTolerance: 15,           // 设置吸附的灵敏度，15是合适的值
      });
      this.map.addInteraction(this.draw);
      this.map.addInteraction(this.snap);
      this.draw.on('drawend', (event) => {
        const feature = event.feature;
        // 为每个绘制的点设置唯一的 ID
        const featureId = 'p_' + (this.maxPointIdNum + 1);  // 使用时间戳生成唯一 ID
        feature.setId(featureId);
        feature.set('id', featureId);
        let xy = feature.getGeometry().getCoordinates()
        console.log('绘制的点 ID:', featureId);
        console.log('绘制的点坐标:', feature.getGeometry().getCoordinates());
        // 将当前绘的点显示在图上
        this.roadmap_src.addFeature(feature);
        feature.setStyle(this.customizedStyle(feature));
        // 点位数据回执父组件记录
        this.elementRoadDrawEnd(feature, 'p');
      });
    },
    
    // 确保坐标有效，处理null值并确保有z坐标
    ensureValidCoordinate(coord) {
      // 确保坐标至少有x和y
      if (!coord || coord.length < 2) {
        console.warn('坐标数据无效:', coord);
        return coord;
      }
      
      // 处理x和y坐标的null值
      if (coord[0] === null || coord[0] === undefined) {
        coord[0] = 0;
      }
      if (coord[1] === null || coord[1] === undefined) {
        coord[1] = 0;
      }
      
      // 确保有z坐标，如果没有或为null，则设为0
      if (coord.length < 3) {
        coord.push(0);
      } else if (coord[2] === null || coord[2] === undefined) {
        coord[2] = 0;
      }
      
      return coord;
    },
    
    // 绘制直线
    drawLine() {
      this.clearDraw(); // 清除之前的绘制
      // 创建绘制交互，用于绘制线段
      this.draw = null;
      this.draw = new Draw({
        source: this.roadmap_src,
        type: 'LineString',
        maxPoints: 2, // 限制线段的点数为 2，每次只绘制一条线段
      });
      // 创建Snap交互，启用吸附到最近的点或者线
      this.snap = new Snap({
        source: this.roadmap_src, // 吸附到 roadmap_src 中的点和线
        pixelTolerance: 10, // 设置吸附的灵敏度
      });
      this.map.addInteraction(this.draw);
      this.map.addInteraction(this.snap);
      // 监听绘制完成事件
      this.draw.on('drawend', (event) => {
        const feature = event.feature;
        const coordinates = feature.getGeometry().getCoordinates();
        // 为绘制的线设置唯一的 ID
        const featureId = 'l_' + (this.maxLineIdNum + 1);
        feature.setId(featureId);
        feature.set('id', featureId);
        
        // 检查并创建线段的起点和终点，并记录它们的ID
        let startPointId = null;
        let endPointId = null;
        
        // 只处理起点和终点（第一个和最后一个坐标）
        const startCoord = coordinates[0];
        const endCoord = coordinates[coordinates.length - 1];
        
        // 处理起点
        let startPointFeature = null;
        startPointId = this.getConnectedPointId(startCoord);
        if (!startPointId) {
          // 确保坐标有正确的z值，处理null值
          this.ensureValidCoordinate(startCoord);
          // 创建新点，并获取新创建点的ID
          startPointId = this.createPointAtCoordinate(startCoord);
        }
        startPointFeature = this.roadmap_src.getFeatureById(startPointId);
        
        // 处理终点
        let endPointFeature = null;
        endPointId = this.getConnectedPointId(endCoord);
        if (!endPointId) {
          // 确保坐标有正确的z值，处理null值
          this.ensureValidCoordinate(endCoord);
          // 创建新点，并获取新创建点的ID
          endPointId = this.createPointAtCoordinate(endCoord);
        }
        endPointFeature = this.roadmap_src.getFeatureById(endPointId);
        
        // 检查是否起点和终点相同（避免创建0长度的线段）
        if (startPointId === endPointId) {
          console.warn('起点和终点相同，不创建线段');
          this.map.removeInteraction(this.draw);
          this.drawLine();
          return;
        }
        
        // 检查是否已存在相同起点终点的线段
        let lineExist = false;
        const featuresAtStart = this.roadmap_src.getFeaturesAtCoordinate(startPointFeature.getGeometry().getCoordinates());
        for (let i = 0; i < featuresAtStart.length; i++) {
          const existingFeature = featuresAtStart[i];
          if (existingFeature.getGeometry().getType() !== 'LineString') continue;
          
          const props = existingFeature.getProperties();
          if (((props.startid === startPointId) && (props.endid === endPointId)) ||
              ((props.endid === startPointId) && (props.startid === endPointId))) {
            console.warn('已存在相同起点终点的线段，不创建新线段');
            lineExist = true;
            break;
          }
        }
        
        if (lineExist) {
          this.map.removeInteraction(this.draw);
          this.drawLine();
          return;
        }
        
        // 🔧 关键修复：重新设置线段坐标为只包含起点和终点
        // 使用实际点的坐标，而不是绘制过程中的所有中间点
        feature.getGeometry().setCoordinates([
          startPointFeature.getGeometry().getCoordinates(),
          endPointFeature.getGeometry().getCoordinates()
        ]);
        
        // 设置线段的起点ID和终点ID
        feature.set('startid', startPointId);
        feature.set('endid', endPointId);
        
        // 使用 customizedStyle 设置样式
        this.roadmap_src.addFeature(feature);
        feature.setStyle(this.customizedStyle(feature));
        this.elementRoadDrawEnd(feature, 'l'); // 数据回执父组件
        // 重新启动绘制交互，使用户可以连续绘制新的线段
        this.map.removeInteraction(this.draw); // 移除当前绘制交互
        this.drawLine(); // 重新初始化绘制功能
      });
    },

    /**
     * 在指定坐标创建点-绘线和曲线子功能
     * @param targetCoord 绘制的点位xy
     * @param data 当前点位非手动绘制时需要额外增加的参数[{数据名:数据}],可传递多个
     * @returns {string} 返回创建的点的ID，如果点已存在则返回null
     */
    createPointAtCoordinate(targetCoord, data = []) {
      // 判断当前坐标是否有点位,有则不创建
      const features = this.roadmap_src.getFeatures();
      for (const feature of features) {
        // 获取 Feature 的坐标
        const geometry = feature.getGeometry();
        if (geometry && geometry.getType() === 'Point') {
          const coord = geometry.getCoordinates();
          // 判断是否与目标坐标相同
          if (coord[0] === targetCoord[0] && coord[1] === targetCoord[1]) {
            this.$message({
              message: '目标绘制点已存在点位',
              type: 'warning'
            });
            return null;
          }
        }
      }
      const pointFeature = new Feature({
        geometry: new Point(targetCoord)
      });
      const pointId = 'p_' + (this.maxPointIdNum + 1);
      pointFeature.set('id', pointId);
      pointFeature.setId(pointId);
      if (Array.isArray(data) && data.length > 0) {
        data.forEach(item => {
          Object.entries(item).forEach(([key, value]) => {
            pointFeature.set(key, value);
          });
        });
      }
      this.roadmap_src.addFeature(pointFeature);
      pointFeature.setStyle(this.customizedStyle(pointFeature));  // 自定义点样式
      // 点位数据回执父组件记录
      this.elementRoadDrawEnd(pointFeature, 'p');
      // 返回创建的点的ID
      return pointId;
    },

    // (绘制线段)判断坐标是否接近已有点并返回该点的ID-绘线和曲线子功能
    getConnectedPointId(coord) {
      let closestPointId = '';
      const tolerance = 0.01;  // 设置容忍度，控制点坐标接近的范围（可以根据需求调整）
      // 遍历所有已有的点，检查坐标是否接近
      this.roadmap_src.forEachFeature((feature) => {
        if (feature.getGeometry().getType() === 'Point') {
          const pointCoord = feature.getGeometry().getCoordinates();
          if (this.isCoordsClose(pointCoord, coord, tolerance)) {
            closestPointId = feature.getId();  // 找到相连的点ID
          }
        }
      });
      return closestPointId;
    },
    // (绘制线段)判断两个坐标是否接近-绘线和曲线子功能
    isCoordsClose(coord1, coord2, tolerance) {
      const dx = Math.abs(coord1[0] - coord2[0]);
      const dy = Math.abs(coord1[1] - coord2[1]);
      return dx <= tolerance && dy <= tolerance;
    },
    // 绘制曲线
    drawCurve() {
      this.clearDraw();  // 清除之前的绘制
      this.draw = null;
      this.draw = new Draw({
        source: this.roadmap_src,
        type: 'LineString',  // 绘制曲线，实际上是用 LineString 表示
      });
      // 创建Snap交互，启用吸附到最近的点或者线
      this.snap = new Snap({
        source: this.roadmap_src,         // 吸附到roadmap_src中的点和线
        pixelTolerance: 15,           // 设置吸附的灵敏度，15是合适的值
      });
      this.map.addInteraction(this.draw);
      this.map.addInteraction(this.snap);

      // 监听绘制完成事件
      this.draw.on('drawend', (event) => {
        const coordinates = event.feature.getGeometry().getCoordinates();
        let feature = event.feature;
        
        // 曲线至少需要3个点（起点、控制点、终点）
        if (coordinates.length < 3) {
          console.warn('贝塞尔曲线需要至少3个点');
          return;
        }
        
        // 获取起点和终点的坐标
        const startCoord = coordinates[0];
        this.ensureValidCoordinate(startCoord);
        const endCoord = coordinates[coordinates.length - 1];
        this.ensureValidCoordinate(endCoord);
        
        // 检查起点和终点是否已经存在点，如果没有则生成新的点
        let startPointId = this.getConnectedPointId(startCoord);
        let endPointId = this.getConnectedPointId(endCoord);
        
        let startPointFeature = null;
        let endPointFeature = null;
        
        // 如果起点没有对应的点，生成新的点
        if (!startPointId) {
          startPointId = this.createPointAtCoordinate(startCoord);
        }
        startPointFeature = this.roadmap_src.getFeatureById(startPointId);
        
        // 如果终点没有对应的点，生成新的点
        if (!endPointId) {
          endPointId = this.createPointAtCoordinate(endCoord);
        }
        endPointFeature = this.roadmap_src.getFeatureById(endPointId);
        
        // 检查是否起点和终点相同
        if (startPointId === endPointId) {
          console.warn('贝塞尔曲线的起点和终点相同，不创建曲线');
          return;
        }
        
      // 🔧 关键修复：使用实际点的坐标更新控制点数组
      // 将起点和终点替换为实际点的坐标（保持3D坐标）
      const controlPoints = [...coordinates];
      controlPoints[0] = startPointFeature.getGeometry().getCoordinates();
      controlPoints[controlPoints.length - 1] = endPointFeature.getGeometry().getCoordinates();
      
      // 设置起点ID和终点ID
      feature.set('startid', startPointId);
      feature.set('endid', endPointId);
      
      // 🔧 保存原始控制点，用于后续GeoJSON导出
      // 这是关键：robot_map_editor使用单独的bguide数据源，我们使用properties保存
      feature.set('bezierControlPoints', controlPoints);
      
      // 获取离散化的贝塞尔曲线
      var newBezierPoints = this.genBezierPointsByControlPoints(controlPoints, 0.01);
      // 将MultiPoint替换为贝塞尔LineString
      feature.setGeometry(new LineString(newBezierPoints));
      // 为曲线设置唯一的 ID
      const featureId = 'b_' + (this.maxBowNum + 1);
      feature.setId(featureId);
      feature.set('id', featureId);
      // 将曲线添加到地图图层
      this.roadmap_src.addFeature(feature);
      // 设置样式
      feature.setStyle(this.customizedStyle(feature));
      // 点位数据回执父组件记录
      this.elementRoadDrawEnd(feature, 'b');
      });
    },
    // 绘制面
    drawPolygon() {
      this.clearDraw();  // 清除之前的绘制
      this.draw = null;
      this.draw = new Draw({
        source: this.roadmap_src,
        type: 'Polygon',
      });
      // 创建Snap交互，启用吸附到最近的点或者线
      this.snap = new Snap({
        source: this.roadmap_src,         // 吸附到roadmap_src中的点和线
        pixelTolerance: 15,           // 设置吸附的灵敏度，15是合适的值
      });
      this.map.addInteraction(this.draw);
      this.map.addInteraction(this.snap);
      this.draw.on('drawend', (event) => {
        const feature = event.feature;
        // 为每个绘制的面设置唯一的 ID
        const featureId = 's_' + (this.maxPolygonNum + 1);
        feature.setId(featureId);
        feature.set('id', featureId);
        console.log('绘制的面 ID:', featureId);
        console.log('绘制的面坐标:', feature.getGeometry().getCoordinates());
        this.roadmap_src.addFeature(feature);
        feature.setStyle(this.customizedStyle(feature));
        // 点位数据回执父组件记录
        this.elementRoadDrawEnd(feature, 's');
      });
    },
    // 关闭绘制
    clearDraw() {
      if (this.draw) {
        this.map.removeInteraction(this.draw);
        this.draw = null;  // 清空绘制交互实例
      }
    },
    // 路线加载完毕的回调
    elementRoadInitEnd(features) {
      // 初始化计数
      let data = features.reduce((acc, item) => {
        const id = item.values_.id || getId(); // 获取 id
        const [prefix, number] = id.split("_"); // 拆分前缀和数字
        const num = parseInt(number, 10); // 将数字部分转换为整数
        // 更新每个前缀的最大值
        if (!acc[prefix] || acc[prefix] < num) {
          acc[prefix] = num;
        }
        return acc;
      }, {});
      // 保证只更新存在的元素id
      if (Object.hasOwn(data, 'p')) this.maxPointIdNum = data.p;
      if (Object.hasOwn(data, 'l')) this.maxLineIdNum = data.l;
      if (Object.hasOwn(data, 'b')) this.maxBowNum = data.b;
      if (Object.hasOwn(data, 's')) this.maxPolygonNum = data.s;
      // 将当前点位集合发送父组件
      this.$emit('elementRoadInitEnd', features)
    },
    // 绘制元素完毕的回调
    elementRoadDrawEnd(feature, type) {
      // 定义一个映射表，将类型与计数变量对应起来
      const typeMapping = {
        p: 'maxPointIdNum',
        l: 'maxLineIdNum',
        b: 'maxBowNum',
        s: 'maxPolygonNum'
      };
      // 如果类型存在于映射表中，则对应计数变量加 1
      if (typeMapping[type]) {
        this[typeMapping[type]]++;
      }
      // 将当前新绘制的点位发送给父组件
      this.$emit('elementRoadDrawEnd', feature);
    },
    // 移除点线面
    removeElement(id) {
      if (!this.roadmap_src) {
        console.log("请先初始化画布!");
        this.$emit('removeElementResult', '')
        return;
      }
      const feature = this.roadmap_src.getFeatureById(id);
      if (feature) {
        // 从 VectorSource 中移除该 Feature
        this.roadmap_src.removeFeature(feature);
        this.$emit('removeElementResult', id)
        // 删除查看元素打的标记
        if (this.icon_src) {
          let oldFeature = this.icon_src.getFeatureById('selectEle-' + 1);
          if (oldFeature) this.icon_src.removeFeature(oldFeature);
        }
        // 判断是不是移除的当前选中元素, 如果是则清空当前选中
        if (this.selectedFeatureId == id) {
          this.selectedFeatureId = '';
        }
      } else {
        this.$emit('removeElementResult', '')
        console.log('此元素不存在');
      }
    },
    // 选择元素显示
    selectShowEle(id) {
      if (!id) {
        console.log("请指定id!");
        return;
      }
      if (!this.roadmap_src) {
        console.log("请先初始化画布!");
        return;
      }   
      // 获取元素
      let feature = this.roadmap_src.getFeatureById(id);
      if (!feature) return;
      // 获取位置
      let position = feature.getGeometry().getCoordinates();
      // 点位坐标如果时点类型则直接取,如果是非点类型元素需要额外处理
      if (id.startsWith('l')) {
        // 线段则取当前线段位置的40%处打点, 避免覆盖线路的指针图标
        position = this.calculateCenterCoordinate(position[0], position[1], 0.4);
      }
      if (id.startsWith('b')) {
        // 弧形取最中间数据
        let count = position.length;
        position = position[Math.ceil(count / 2)]
      }
      if (id.startsWith('s')) {
        // 多边形取质心
        position = this.calculateCentroid(position);
      }
      // 获取这个图标有没有被创建, 有则移动位置, 没有再创建,避免消耗
      if (!this.icon_src) {
        // 如果未初始化则直接打标记
        // 在此位置打一个标记
        this.initIcon(1, position[0], position[1] + 0.8, this.icon.mark, 'selectEle', 0.12, 0);
        this.selectedFeatureId = id;
      } else {
        // 获取id对应的Feature
        let oldFeature = this.icon_src.getFeatureById('selectEle-' + 1);
        if (!oldFeature) {
          // 如果没有找到Feature，则打一个新标记
          this.initIcon(1, position[0], position[1] + 0.8, this.icon.mark, 'selectEle', 0.12, 0);
          this.selectedFeatureId = id;
        } else {
          // 如果找到Feature并且id相同，则删除Feature
          if (id == this.selectedFeatureId) {
            this.icon_src.removeFeature(oldFeature);
            this.selectedFeatureId = ''; // 重置选中的Feature
            return;
          } else {
            // 否则，更新Feature的位置
            oldFeature.getGeometry().setCoordinates([position[0], position[1] + 0.8]);
            this.selectedFeatureId = id;
          }
        }
      }
      // 将该元素显示在画布中心
      // this.map.getView().animate({
      //   center: position,  // 目标位置
      //   duration: 500,     // 动画时长（单位：毫秒）
      //   easing: easeOut,  // 缓动函数，使得动画效果平滑
      // });
      // 回调父组件将当前元素直接返回, 父组件不必要再次检索元素
      this.$emit('selectShowEleResult', feature)
    },
    // 多边形质心计算公式
    calculateCentroid(coords) {
      // 如果 coords 是一个嵌套数组，先提取最内层的坐标数组
      if (Array.isArray(coords[0]) && Array.isArray(coords[0][0])) {
        coords = coords[0]; // 提取内层坐标
      }
      let n = coords.length;
      let area = 0;
      let cx = 0;
      let cy = 0;
      for (let i = 0; i < n; i++) {
        let j = (i + 1) % n; // 下一个顶点，循环回到第一个顶点
        let x1 = coords[i][0];
        let y1 = coords[i][1];
        let x2 = coords[j][0];
        let y2 = coords[j][1];
        let crossProduct = x1 * y2 - x2 * y1;
        area += crossProduct;
        cx += (x1 + x2) * crossProduct;
        cy += (y1 + y2) * crossProduct;
      }
      area = area / 2;
      cx = cx / (6 * area);
      cy = cy / (6 * area);
      return [cx, cy];
    },
    /**
     * 
     * @param coord1 起点坐标
     * @param coord2 终点坐标
     * @param ratio 需要取的点在线段的比例(默认中心点)
     */
    // 线段位置对应的坐标计算公式
    calculateCenterCoordinate(coord1, coord2, ratio = 0.5) {
      const x1 = coord1[0], y1 = coord1[1];
      const x2 = coord2[0], y2 = coord2[1];
      const x = x1 + ratio * (x2 - x1);
      const y = y1 + ratio * (y2 - y1);
      return [x, y];
    },
    // 编辑页面的元素选择模式点击事件处理
    editElementSelectHandle(feature) {
      // 调用显示并回执数据到父组件
      if (feature) {
        this.selectShowEle(feature.getId());
      }
    },
    /**
     * 点位坐标更新
     * @param id id
     * @param poitision 坐标
     */
    pointPositionUpdate(id, position) {
      if (!this.roadmap_src) {
        console.log("请先初始化画布!");
        return;
      }
      // 移动点位
      let feature = this.roadmap_src.getFeatureById(id);
      if (!feature) {
        console.log(`未找到ID为 ${id} 的元素!`);
        return;
      }
      const geometry = feature.getGeometry();
      const coordinates = geometry.getCoordinates();
      // 获取原坐标
      const originalX = coordinates[0];
      const originalY = coordinates[1];
      // 计算偏移量
      const deltaX = position[0] - originalX;
      const deltaY = position[1] - originalY;
      // 更新点位坐标
      coordinates[0] = position[0];
      coordinates[1] = position[1];
      geometry.setCoordinates(coordinates);
      // 每次移动更新图标位置()
      let iconFeature = this.icon_src.getFeatureById('selectEle-' + 1);
      if (iconFeature) {
        iconFeature.getGeometry().setCoordinates([coordinates[0], coordinates[1] + 0.8]);
      }
      // 遍历所有线段(包括曲线)，检查该点位是否为起点或终点
      this.roadmap_src.getFeatures().forEach(lineFeature => {
        // 点位相连的直线处理逻辑
        const startId = lineFeature.get('startid');
        const endId = lineFeature.get('endid');
        if (lineFeature.getId().startsWith('l')) {
          // 判断该点位是否为起点或终点
          if (startId === id || endId === id) {
            const lineGeometry = lineFeature.getGeometry();
            const lineCoordinates = lineGeometry.getCoordinates();
            
            // 🔧 关键修复：确保线段只包含起点和终点两个坐标
            // 获取起点和终点的feature
            const startPointFeature = this.roadmap_src.getFeatureById(startId);
            const endPointFeature = this.roadmap_src.getFeatureById(endId);
            
            if (startPointFeature && endPointFeature) {
              // 使用实际点的坐标重新设置线段
              const newLineCoordinates = [
                startPointFeature.getGeometry().getCoordinates(),
                endPointFeature.getGeometry().getCoordinates()
              ];
              // 更新线段geometry
              lineGeometry.setCoordinates(newLineCoordinates);
              lineFeature.setStyle(this.customizedStyle(lineFeature));  // 确保重新设置样式，包含箭头
            }
          }
        } else if (lineFeature.getId().startsWith('b')) {
          const controlPoints = lineFeature.getGeometry().getCoordinates(); // 获取当前贝塞尔曲线的控制点
          const updatedControlPoints = controlPoints.map(point => [...point]);
          // 根据修改的点位置更新控制点
          if (startId === id) {
            updatedControlPoints[0] = position; // 更新起始点
          } else if (endId === id) {
            updatedControlPoints[updatedControlPoints.length - 1] = position; // 更新结束点
          }
          // 如果控制点发生了变化，生成新的贝塞尔曲线坐标
          if (startId === id || endId === id) {
            const newBezierPoints = this.genBezierPointsByControlPoints(updatedControlPoints, 0.01);
            // 更新贝塞尔曲线的几何坐标
            lineFeature.getGeometry().setCoordinates(newBezierPoints);
            lineFeature.setStyle(this.customizedStyle(lineFeature));  // 确保重新设置样式，包含箭头
          }
        }
      });
    },
    // 修改多边形的颜色和透明度
    editSnapeColor(id) {
      if (!this.roadmap_src) {
        console.log("请先初始化画布!");
        return;
      }
      let snapeFeature = this.roadmap_src.getFeatureById(id);
      if (!snapeFeature) {
        console.log(`未找到ID为 ${id} 的多边形元素!`);
        return;
      }
      let style = this.styleForPolygon(snapeFeature)
      snapeFeature.setStyle(style);
    },
    // 完成绘制方法
    completeDrawing(start, end, nid) {  
      // 参考robot_map_editor的角度计算方式
      // 计算旋转角度 (注意：参数顺序是 atan2(x, y)，与常规的 atan2(y, x) 不同)
      const rotation = Math.atan2(end[0] - start[0], end[1] - start[1]);
      // 转换为机器人坐标系的yaw角 (Math.PI / 2.0 - rotation)
      const yaw = Math.PI / 2.0 - rotation;
      
      // 移除预览虚线和箭头
      if (this.previewFeature) {
        this.roadmap_src.removeFeature(this.previewFeature);
        this.previewFeature = null;
      }
      
      // 发送初始化结果，传递弧度值
      this.$emit('initNavigationResult', start, yaw, nid)
    },
    
    // 计算航向角（保留用于其他用途，但初始化不使用此方法）
    calculateBearing(start, end) {
      const dx = end[0] - start[0];
      const dy = end[1] - start[1];
      const angle = Math.atan2(dy, dx) * (180 / Math.PI);
      return angle < 0 ? angle + 360 : angle;
    },
    
    // 初始化轨迹图层
    initTrajectoryLayer() {
      if (!this.trajectorySource) {
        this.trajectorySource = new VectorSource();
        this.map.addLayer(
          new VectorLayer({
            source: this.trajectorySource,
            style: null, // 在绘制轨迹时动态设置样式
            zIndex: 5 // 设置较高的z-index确保轨迹显示在其他元素上方
          })
        );
      }
    },
    
    // 绘制轨迹
    drawTrajectory(trajectory, options = {}) {
      console.log("开始绘制轨迹:", trajectory, options);
      
      if (!trajectory || trajectory.length === 0) {
        console.warn("轨迹数据为空");
        return;
      }
      
      // 初始化轨迹图层
      this.initTrajectoryLayer();
      
      // 清除之前的轨迹（无论是规划路径还是实时轨迹都需要清除旧的）
      this.clearTrajectory();
      
      // 保存轨迹数据
      this.currentTrajectory = trajectory;
      
      this.trajectoryProgress = options.currentProgress || 0;
      
      // 确保进度不超过轨迹点数
      if (this.trajectoryProgress >= trajectory.length) {
        this.trajectoryProgress = trajectory.length - 1;
      }
      
      try {
        // 获取机器人当前位置（如果有的话）
        const robotPos = options.robotPosition || [0, 0];
        
        // 计算机器人在轨迹上的精确投影位置
        const projectionInfo = this.findRobotProjectionOnTrajectory(trajectory, robotPos);
        
        if (projectionInfo && projectionInfo.segmentIndex >= 0) {
          // 基于机器人实际位置实时绘制轨迹
          this.drawRealtimeTrajectoryFromRobotPosition(trajectory, robotPos, projectionInfo);
        } else {
          // 回退到基于进度索引的绘制方式
          this.drawTrajectoryByProgress(trajectory);
        }
        
        // 添加起点标记
        if (trajectory.length > 0) {
          this.addTrajectoryMarker(trajectory[0], {
            text: '起点',
            color: '#ffffff',
            backgroundColor: 'rgba(0, 255, 0, 0.8)'
          });
        }
        
        // 添加终点标记
        if (trajectory.length > 1) {
          this.addTrajectoryMarker(trajectory[trajectory.length - 1], {
            text: '终点',
            color: '#ffffff',
            backgroundColor: 'rgba(255, 0, 0, 0.8)'
          });
        }
        
        // 如果有当前位置标记，添加当前位置
        // if (this.trajectoryProgress > 0 && this.trajectoryProgress < trajectory.length) {
        //   this.addTrajectoryMarker(trajectory[this.trajectoryProgress], {
        //     text: '当前',
        //     color: '#ffff00',
        //     backgroundColor: 'rgba(255, 255, 0, 0.8)'
        //   });
        // }
        
        console.log("轨迹绘制完成，总点数:", trajectory.length, "当前进度:", this.trajectoryProgress);
        
      } catch (error) {
        console.error("绘制轨迹失败:", error);
      }
    },
    
    // 找到机器人在轨迹上的投影位置
    findRobotProjectionOnTrajectory(trajectory, robotPos) {
      if (!trajectory || trajectory.length < 2) return null;
      
      let minDistance = Infinity;
      let bestProjection = null;
      
      for (let i = 0; i < trajectory.length - 1; i++) {
        const segmentStart = trajectory[i];
        const segmentEnd = trajectory[i + 1];
        
        // 计算投影点
        const projection = this.projectPointToLineSegment(robotPos, segmentStart, segmentEnd);
        
        if (projection.distance < minDistance) {
          minDistance = projection.distance;
          bestProjection = {
            point: projection.point,
            distance: projection.distance,
            segmentIndex: i,
            parameter: this.getParameterOnSegment(projection.point, segmentStart, segmentEnd)
          };
        }
      }
      
      // 放宽距离限制，提高轨迹绘制的实时性
      // 只要能找到合理的投影点就返回，距离限制适当放宽
      if (bestProjection && bestProjection.distance < 10.0) {
        console.log(`找到机器人投影点: 线段${bestProjection.segmentIndex}, 距离${bestProjection.distance.toFixed(2)}m`);
        return bestProjection;
      }
      
      console.log(`机器人距离轨迹太远: ${bestProjection ? bestProjection.distance.toFixed(2) : 'N/A'}m, 使用回退绘制方式`);
      return null;
    },
    
    // 计算点到线段的投影
    projectPointToLineSegment(point, lineStart, lineEnd) {
      const [px, py] = point;
      const [x1, y1] = lineStart;
      const [x2, y2] = lineEnd;
      
      const A = px - x1;
      const B = py - y1;
      const C = x2 - x1;
      const D = y2 - y1;
      
      const dot = A * C + B * D;
      const lenSq = C * C + D * D;
      
      if (lenSq === 0) {
        return {
          point: [x1, y1],
          distance: Math.sqrt(A * A + B * B)
        };
      }
      
      let param = dot / lenSq;
      param = Math.max(0, Math.min(1, param));
      
      const projectionX = x1 + param * C;
      const projectionY = y1 + param * D;
      
      const distance = Math.sqrt(
        Math.pow(px - projectionX, 2) + Math.pow(py - projectionY, 2)
      );
      
      return {
        point: [projectionX, projectionY],
        distance: distance
      };
    },
    
    // 获取投影点在线段上的参数(0-1)
    getParameterOnSegment(projectionPoint, segmentStart, segmentEnd) {
      const [px, py] = projectionPoint;
      const [x1, y1] = segmentStart;
      const [x2, y2] = segmentEnd;
      
      if (Math.abs(x2 - x1) > Math.abs(y2 - y1)) {
        return (px - x1) / (x2 - x1);
      } else {
        return (py - y1) / (y2 - y1);
      }
    },
    
    // 基于机器人实际位置实时绘制轨迹
    drawRealtimeTrajectoryFromRobotPosition(trajectory, robotPos, projectionInfo) {
      console.log(`实时轨迹绘制: 机器人位置(${robotPos[0].toFixed(2)}, ${robotPos[1].toFixed(2)}), 投影在线段${projectionInfo.segmentIndex}`);
      
      // 1. 绘制已走过的轨迹：从起点到机器人当前位置
      const completedPoints = [];
      
      // 添加起点到投影线段起点的所有点
      for (let i = 0; i <= projectionInfo.segmentIndex; i++) {
        completedPoints.push(trajectory[i]);
      }
      
      // 添加机器人在轨迹上的投影点作为已走过轨迹的终点
      completedPoints.push(projectionInfo.point);
      
      if (completedPoints.length >= 2) {
        console.log(`绘制实时已走过轨迹: 起点到机器人投影位置, ${completedPoints.length}个点`);
        this.drawTrajectorySegment(completedPoints, {
          color: '#00ff00', // 绿色
          width: 4,
          lineDash: [10, 5],
          type: 'completed'
        });
      }
      
      // 2. 绘制未走过的轨迹：从机器人当前位置到终点
      const remainingPoints = [];
      
      // 从机器人投影点开始
      remainingPoints.push(projectionInfo.point);
      
      // 添加投影线段的终点
      if (projectionInfo.segmentIndex < trajectory.length - 1) {
        remainingPoints.push(trajectory[projectionInfo.segmentIndex + 1]);
      }
      
      // 添加后续所有点直到终点
      for (let i = projectionInfo.segmentIndex + 2; i < trajectory.length; i++) {
        remainingPoints.push(trajectory[i]);
      }
      
      if (remainingPoints.length >= 2) {
        console.log(`绘制实时未走过轨迹: 机器人投影位置到终点, ${remainingPoints.length}个点`);
        this.drawTrajectorySegment(remainingPoints, {
          color: '#ff0000', // 红色
          width: 4,
          lineDash: [10, 5],
          type: 'remaining'
        });
      }
      
      // 3. 添加机器人当前位置标记
      this.addRealtimeRobotMarker(robotPos, projectionInfo.point);
    },
    
    // 添加机器人实时位置标记
    addRealtimeRobotMarker(robotActualPos, robotProjectionPos) {
      // 在投影点显示一个小的当前位置标记
      const marker = new Feature({
        geometry: new Point(robotProjectionPos)
      });
      
      marker.setStyle(new Style({
        image: new Icon({
          src: 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(`
            <svg width="16" height="16" xmlns="http://www.w3.org/2000/svg">
              <circle cx="8" cy="8" r="6" fill="#ffff00" stroke="#ff6600" stroke-width="2"/>
              <circle cx="8" cy="8" r="2" fill="#ff6600"/>
            </svg>
          `),
          scale: 1
        }),
        text: new Text({
          text: '当前',
          font: 'bold 10px Arial',
          fill: new Fill({
            color: '#333333'
          }),
          backgroundFill: new Fill({
            color: 'rgba(255, 255, 255, 0.8)'
          }),
          padding: [2, 4, 2, 4],
          offsetY: -25
        })
      }));
      
      marker.setId(`trajectory-current-position-${Date.now()}`);
      this.trajectorySource.addFeature(marker);
    },
    
    // 基于进度索引的回退绘制方式
    drawTrajectoryByProgress(trajectory) {
      // 绘制已走过的轨迹（绿色）
      if (this.trajectoryProgress > 0) {
        const completedTrajectory = trajectory.slice(0, this.trajectoryProgress + 1);
        console.log(`绘制已完成轨迹: 点${0}到点${this.trajectoryProgress}, 共${completedTrajectory.length}个点`);
        this.drawTrajectorySegment(completedTrajectory, {
          color: '#00ff00', // 绿色
          width: 4,
          lineDash: [10, 5],
          type: 'completed'
        });
      }
      
      // 绘制未走过的轨迹（红色）
      if (this.trajectoryProgress < trajectory.length - 1) {
        const remainingTrajectory = trajectory.slice(this.trajectoryProgress);
        console.log(`绘制剩余轨迹: 点${this.trajectoryProgress}到点${trajectory.length-1}, 共${remainingTrajectory.length}个点`);
        this.drawTrajectorySegment(remainingTrajectory, {
          color: '#ff0000', // 红色
          width: 4,
          lineDash: [10, 5],
          type: 'remaining'
        });
      }
    },
    
    // 绘制轨迹段
    drawTrajectorySegment(trajectorySegment, style) {
      if (!trajectorySegment || trajectorySegment.length < 2) {
        return;
      }
      
      // 创建线段几何
      const lineString = new LineString(trajectorySegment);
      const feature = new Feature({
        geometry: lineString
      });
      
      // 设置样式
      feature.setStyle(new Style({
        stroke: new Stroke({
          color: style.color,
          width: style.width,
          lineDash: style.lineDash
        })
      }));
      
      // 设置feature id以便后续管理
      feature.setId(`trajectory-${style.type}-${Date.now()}`);
      
      // 添加到图层
      this.trajectorySource.addFeature(feature);
    },
    
    // 添加轨迹标记点
    addTrajectoryMarker(position, options) {
      const marker = new Feature({
        geometry: new Point(position)
      });
      
      marker.setStyle(new Style({
        text: new Text({
          text: options.text,
          font: 'bold 12px Arial',
          fill: new Fill({
            color: options.color
          }),
          backgroundFill: new Fill({
            color: options.backgroundColor
          }),
          padding: [3, 6, 3, 6],
          offsetY: -20
        }),
        image: new Icon({
          src: 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(`
            <svg width="12" height="12" xmlns="http://www.w3.org/2000/svg">
              <circle cx="6" cy="6" r="5" fill="${options.color}" stroke="white" stroke-width="1"/>
            </svg>
          `),
          scale: 1
        })
      }));
      
      marker.setId(`trajectory-marker-${options.text}-${Date.now()}`);
      this.trajectorySource.addFeature(marker);
    },
    
    // 清除轨迹
    clearTrajectory() {
      if (this.trajectorySource) {
        this.trajectorySource.clear();
      }
      this.currentTrajectory = null;
      this.trajectoryProgress = 0;
    },
    
    // 更新轨迹进度
    updateTrajectoryProgress(newProgress, robotPosition = null) {
      if (!this.currentTrajectory || newProgress === this.trajectoryProgress) {
        return;
      }
      
      console.log(`更新轨迹进度: ${this.trajectoryProgress} -> ${newProgress}，轨迹总长度: ${this.currentTrajectory.length}`);
      
      this.trajectoryProgress = newProgress;
      
      // 清除现有轨迹特征（但保留轨迹数据）
      if (this.trajectorySource) {
        this.trajectorySource.clear();
      }
      
      // 使用传入的机器人位置或默认值
      const currentRobotPos = robotPosition || [this.robotPoseData?.x || 0, this.robotPoseData?.y || 0];
      
      // 重新绘制轨迹以反映新的进度
      this.drawTrajectory(this.currentTrajectory, {
        currentProgress: this.trajectoryProgress,
        forceRedraw: true,
        robotPosition: currentRobotPos
      });
    },
    
    // 实时更新轨迹绘制（基于机器人当前位置）
    updateRealtimeTrajectory(robotPosition) {
      if (!this.currentTrajectory || !this.trajectorySource) {
        return;
      }
      
      // 清除现有的轨迹线段和位置标记（但保留起点终点标记）
      const features = this.trajectorySource.getFeatures();
      features.forEach(feature => {
        const featureId = feature.getId();
        if (featureId && (
          featureId.startsWith('trajectory-completed-') || 
          featureId.startsWith('trajectory-remaining-') ||
          featureId.startsWith('trajectory-current-position-')
        )) {
          this.trajectorySource.removeFeature(feature);
        }
      });
      
      // 基于当前机器人位置重新计算并绘制轨迹
      const projectionInfo = this.findRobotProjectionOnTrajectory(this.currentTrajectory, robotPosition);
      
      if (projectionInfo && projectionInfo.segmentIndex >= 0) {
        // 使用实时绘制方法
        this.drawRealtimeTrajectoryFromRobotPosition(this.currentTrajectory, robotPosition, projectionInfo);
      } else {
        console.log('机器人位置无法投影到轨迹上，跳过实时更新');
      }
    },

    // === 路网导入导出相关方法 ===
    
    // 清空所有地图元素
    clearAllElements() {
      if (!this.roadmap_src) {
        console.log("请先初始化画布!");
        return false;
      }
      
      try {
        // 清空所有features
        this.roadmap_src.clear();
        
        // 清空选中状态
        this.selectedFeatureId = '';
        
        // 清空相关图标
        if (this.icon_src) {
          this.icon_src.clear();
        }
        
        console.log('所有地图元素已清空');
        return true;
      } catch (error) {
        console.error('清空地图元素失败:', error);
        return false;
      }
    },

    // 导入GeoJSON特征到地图
    async importGeoJsonFeatures(geoJsonData) {
      if (!this.roadmap_src) {
        throw new Error("请先初始化画布!");
      }

      if (!geoJsonData || !geoJsonData.features) {
        throw new Error("无效的GeoJSON数据");
      }

      try {
        // 使用GeoJSON格式读取器
        const geojsonFormat = new GeoJSON();
        
        // 解析GeoJSON数据为OpenLayers Feature对象
        const features = geojsonFormat.readFeatures(geoJsonData, {
          featureProjection: this.map.getView().getProjection()
        });

        // 设置每个feature的ID（如果没有的话）
        features.forEach(feature => {
          if (!feature.getId() && feature.get('id')) {
            feature.setId(feature.get('id'));
          }
        });

        // 批量添加features到地图
        this.roadmap_src.addFeatures(features);

        // 触发特征初始化完成事件，通知父组件更新数据
        if (features.length > 0) {
          this.$emit('elementRoadInitEnd', features);
        }

        console.log(`成功导入${features.length}个地图元素`);
        return features;
      } catch (error) {
        console.error('导入GeoJSON特征失败:', error);
        throw new Error(`导入失败: ${error.message}`);
      }
    },

    // 根据坐标创建点位（为导入的实时位姿点提供支持）
    createPointAtCoordinateForImport(coordinates, name = '', extraData = []) {
      if (!this.roadmap_src) {
        console.log("请先初始化画布!");
        return;
      }

      try {
        // 确保坐标有效，处理null值
        this.ensureValidCoordinate(coordinates);
        
        // 生成新的点位ID
        const existingPoints = this.roadmap_src.getFeatures().filter(f => 
          f.getId() && f.getId().startsWith('p_')
        );
        const maxId = existingPoints.length > 0 ? 
          Math.max(...existingPoints.map(f => parseInt(f.getId().split('_')[1]) || 0)) : 0;
        const newId = `p_${maxId + 1}`;

        // 创建点位feature
        const pointFeature = new Feature({
          geometry: new Point(coordinates),
          id: newId,
          name: name || `点位${maxId + 1}`
        });
        
        pointFeature.setId(newId);

        // 设置额外属性
        if (extraData && extraData.length > 0) {
          extraData.forEach(data => {
            Object.keys(data).forEach(key => {
              pointFeature.set(key, data[key]);
            });
          });
        }

        // 添加到地图
        this.roadmap_src.addFeature(pointFeature);

        // 触发绘制完成事件
        this.$emit('elementRoadDrawEnd', pointFeature);

        console.log(`成功创建点位: ${newId}`, coordinates);
        return pointFeature;
      } catch (error) {
        console.error('创建点位失败:', error);
        throw error;
      }
    },

    // 刷新地图显示
    refresh() {
      if (this.map) {
        // 强制重新渲染地图
        this.map.render();
        
        // 如果需要，也可以触发视图更新
        this.map.updateSize();
        
        console.log('地图已刷新');
      }
    },
    
    // 手动设置路网可见性（用于调试）
    setRoadNetworkVisible(visible) {
      if (this.roadmapLayer) {
        this.roadmapLayer.setVisible(visible);
        console.log(`手动设置路网图层可见性: ${visible}`);
        console.log(`当前图层状态:`, {
          visible: this.roadmapLayer.getVisible(),
          featureCount: this.roadmap_src ? this.roadmap_src.getFeatures().length : 0
        });
      } else {
        console.warn('路网图层不存在');
      }
    },

    // ==================== 点云相关方法 ====================
    
    // 初始化点云图层
    initPointcloudLayer() {
      // 创建点云图层
      this.pointcloudLayer = new VectorLayer({
        source: new VectorSource({
          features: [new Feature({
            geometry: new MultiPoint([]),
            name: 'PointCloud'
          })]
        }),
        style: new Style({
          image: new Circle({
            radius: 1.8,
            fill: new Fill({ color: '#FF0000' })
          })
        }),
        visible: this.showPointcloud,
        zIndex: 10 // 确保点云图层显示在地图之上
      });
      
      // 添加到地图
      this.map.addLayer(this.pointcloudLayer);
      
      // 如果默认开启点云，则启动更新
      if (this.showPointcloud) {
        this.startPointcloudUpdate();
      }
      
      console.log('点云图层已初始化');
    },
    
    // 启动点云更新定时器
    startPointcloudUpdate() {
      // 先清除已有的定时器
      this.stopPointcloudUpdate();
      
      // 启动新的定时器，每800ms更新一次
      this.pointcloudTimer = setInterval(() => {
        // 如果图层不可见，则不更新
        if (!this.pointcloudLayer || !this.pointcloudLayer.getVisible()) {
          return;
        }
        
        this.updatePointcloud();
      }, 800);
      
      console.log('点云更新定时器已启动');
    },
    
    // 停止点云更新定时器
    stopPointcloudUpdate() {
      if (this.pointcloudTimer) {
        clearInterval(this.pointcloudTimer);
        this.pointcloudTimer = null;
        console.log('点云更新定时器已停止');
      }
    },
    
    // 更新点云数据
    updatePointcloud() {
      getPointcloud().then(response => {
        if (!this.pointcloudLayer) return;
        
        const pointcloud = response;
        
        // 解析点云数据
        // 数据格式：每12字节一个点 (3个float32: x, y, z)
        const pointsCount = Math.floor(pointcloud.byteLength / 12);
        
        if (pointsCount === 0) return;
        
        // 创建Float32Array视图来读取数据
        const float32View = new Float32Array(pointcloud, 0);
        const coordinates = [];
        
        // 提取所有点的坐标（只使用x, y，忽略z）
        for (let i = 0; i < pointsCount; i++) {
          const x = float32View[3 * i];
          const y = float32View[3 * i + 1];
          // const z = float32View[3 * i + 2]; // 2D地图不需要z坐标
          coordinates.push([x, y]);
        }
        
        // 更新点云图层的几何数据
        const features = this.pointcloudLayer.getSource().getFeatures();
        if (features.length > 0) {
          features[0].setGeometry(new MultiPoint(coordinates));
        }
      }).catch(error => {
        // 静默处理错误，避免控制台刷屏
        // console.error('获取点云数据失败:', error);
      });
    }
  },
  
  beforeDestroy() {
    // 清除点云更新定时器
    this.stopPointcloudUpdate();
    
    // 清除其他定时器
    if (this.pathTimerId) {
      clearInterval(this.pathTimerId);
      this.pathTimerId = null;
    }
  }
}
</script>

<style rel="stylesheet/scss" lang="scss" scoped>
.mapParent {
  /* margin-top: 10px; */
  background-color: #d5d8e1;
  height: 100%;
  text-align: center !important;
  border-radius: 5px;
}

.maptext {
  font-size: 20px;
  color: rgb(156, 156, 156);
  height: 100%;
}

.path-play {
  z-index: 999;
  position: absolute;
  bottom: 10px;
  left: 10px;
  padding: 2px 5px;
  background-color: aliceblue;
  border-radius: 10px;
  cursor: pointer;
}

.form-ai-alarm {
  margin: 0px;
}

.tooltip {
  position: absolute;
  background: white;
  border-radius: 12px;
  display: none;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
  width: 240px;
  overflow: hidden;
  border: 1px solid #e2e8f0;
  z-index: 1000;
  
  .tooltip-header {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
    padding: 12px 16px;
    display: flex;
    align-items: center;
    gap: 8px;
    
    .tooltip-title {
      font-size: 14px;
      font-weight: 600;
      margin: 0;
    }
    
    i {
      font-size: 16px;
    }
  }
  
  .tooltip-content {
    padding: 16px;
    background: #f8fafc;
    
    p {
      margin: 0;
      padding: 8px 12px;
      background: white;
      border-radius: 6px;
      border: 1px solid #e2e8f0;
      text-align: center;
      
      &:first-child {
        font-size: 13px;
        color: #4a5568;
        margin-bottom: 8px;
      }
      
      &:last-child {
        font-family: 'Courier New', monospace;
        color: #e53e3e;
        font-weight: 600;
        font-size: 14px;
        background: #fff5f5;
        border-color: #fed7d7;
      }
    }
  }
  
  .tooltip-actions {
    padding: 12px 16px;
    background: white;
    display: flex;
    gap: 8px;
    border-top: 1px solid #e2e8f0;
    
    .action-btn {
      flex: 1;
      border-radius: 6px;
      font-weight: 500;
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 4px;
      
      &.el-button--primary {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        border: none;
        
        &:hover {
          opacity: 0.9;
          transform: translateY(-1px);
        }
      }
      
      &:not(.el-button--primary) {
        background: #f7fafc;
        border: 1px solid #e2e8f0;
        color: #4a5568;
        
        &:hover {
          background: #edf2f7;
          border-color: #cbd5e0;
          transform: translateY(-1px);
        }
      }
      
      i {
        font-size: 12px;
      }
    }
  }
}
</style>
