<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 id="tooltip-content"></div> <!-- 用于动态内容 -->
      <el-button style="width: 40%; position: absolute; bottom: 10px; margin: 0;" size="mini"
        @click="addNowPoint">添加点位</el-button>
      <el-button style="width: 40%; position: absolute; bottom: 10px; margin: 0; left: 55%;" size="mini"
        @click="clearNowPoint">取消添加</el-button>
    </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 } from "ol/style";
import Point from "ol/geom/Point";
import { fromLonLat, toLonLat } from 'ol/proj';
import { easeInOut, easeOut } from 'ol/easing';

export default {
  name: 'OlMap',
  props: {
    // 是否展示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
    },
    // 最小缩放等级
    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
    }
  },
  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,
      // 下面四个数据用于记录当前地图中各类元素当前最大id值(绘图新元素起始id使用)
      maxPointIdNum: 0,
      maxLineIdNum: 0,
      maxBowNum: 0,
      maxPolygonNum: 0,
      selectedFeatureId: '',  // 用于存储选中的 Feature id
      previewFeature: 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'
    }
  },

  watch: {
    pointSwitch(newVal, oldVal) {
      if (newVal !== oldVal) {
        // 如果数据变动则重新加载
        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, 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'; // 恢复默认鼠标样式
      }
    }
  },

  mounted() {
    this.localPointEvent = (this.pointEvent.toString() == "function(){}" || this.pointEvent.toString() == "function _default() {}") ? this.defaultPointClick : this.pointEvent
    this.loadMap();
  },
  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() {
      this.initMapHaveDate();
      this.initRoad(this.geojson_data);
    },
    //加载地图数据
    loadMap() {
      let time = new Date().getTime();
      let configJsonUrl = this.url + "/tilemap/config.json?" + time;
      let mapJsonUrl = this.url + "/shapefile/map.json?" + time
      axios.get(configJsonUrl).then((res) => {
        this.config_json = res.data;
        axios.get(mapJsonUrl).then((res) => {
          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);
      this.initRobot(); // 加载robot图标
    },

    initMap(config_json) {
      if (!config_json || config_json == "") {
        return;
      }
      var map_layers_cnt = config_json["Layers"].length;
      var map_res = config_json["Resolution"];
      var map_ori_x = config_json["OriginX"];
      var map_ori_y = config_json["OriginY"];

      var tile_param = {
        layer_cnt: map_layers_cnt,
        min_zoom: this.minZoom,
        projection: {
          extent: [
            map_ori_x,
            map_ori_y,
            map_ori_x + 2 ** map_layers_cnt * 256 * map_res,
            map_ori_y + 2 ** map_layers_cnt * 256 * map_res,
          ],
        },
      };

      // 创建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 }),
      });
      // 初始化位姿初始化所需的变量
      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 style='margin-top: 0; margin-bottom: 5px; text-align: center; font-size: 15px'>添加目标点</p>";
          tooltipContent.innerHTML += `<p style="color: red; text-align: center; margin-bottom: 35px; margin-top: 5px; font-size: 14px">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]
        }
        // 导航页面初始化位置逻辑
        if (this.poseInitEnable) {
          let feature = this.map.forEachFeatureAtPixel(evt.pixel, (feature) => feature);
          let coord;
          if (feature && feature.getGeometry().getType() == 'Point') {
            // 如果不为空并且是点位,则直接取这个点位的坐标
            coord = feature.getGeometry().getCoordinates();
          } else {
            // 选取画布坐标
            coord = evt.coordinate;
          }
          if (!isDrawing) {
            // 记录起点
            startPoint = coord;
            isDrawing = true;
          } else {
            // 结束绘制，计算航向角并清理预览
            this.completeDrawing(startPoint, coord);
            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;

            // 动态预览虚线
            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(216,30,6, 1)',
                    width: 4,
                  }),
                })
              );
              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 angle = Math.atan2(dy, dx);

                    // 箭头的偏移量（箭头位置前移）
                    const offsetFactor = 20; // 向前移动的像素距离，调整为更大的值
                    const arrowTipX = endX + offsetFactor * Math.cos(angle);
                    const arrowTipY = endY + offsetFactor * Math.sin(angle);

                    // 绘制线段（去掉箭头部分前的线段，确保箭头前没有多余线段）
                    context.beginPath();
                    context.moveTo(startX, startY);
                    context.lineTo(arrowTipX, arrowTipY);
                    context.strokeStyle = 'rgba(216,30,6,1)';
                    context.lineWidth = 4;
                    context.stroke();

                    // 绘制箭头（调整箭头起始位置，避免露出前面的一段线）
                    const arrowLength = 20; // 增大箭头长度
                    const arrowWidth = 10; // 增大箭头宽度
                    context.beginPath();
                    context.moveTo(arrowTipX, arrowTipY);
                    context.lineTo(
                      arrowTipX - arrowLength * Math.cos(angle - Math.PI / 6),
                      arrowTipY - arrowLength * Math.sin(angle - Math.PI / 6)
                    );
                    context.lineTo(
                      arrowTipX - arrowLength * Math.cos(angle + Math.PI / 6),
                      arrowTipY - arrowLength * Math.sin(angle + Math.PI / 6)
                    );
                    context.lineTo(arrowTipX, arrowTipY);
                    context.fillStyle = 'rgba(216,30,6,1)';
                    context.fill();
                  },
                })
              );
              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;
        }
      });

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

      // 创建一个图层，专门用于展示瓦片地图，这里的关键是要设置好url，这个url就是LD导航系统提供的获取瓦片对应的接口
      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);
            }
          },
          wrapX: true,
          projection: new Projection({
            extent: tile_param.projection.extent,
          }),
        }),
      });
      this.map.addLayer(this.baseLayer);
    },

    initRoad(geojson_data) {
      if (!geojson_data || geojson_data == "") {
        return;
      }
      // 创建一个矢量图层，用于显示地图路网(点、线、面)
      this.roadmap_src = new VectorSource();
      this.map.addLayer(
        new VectorLayer({
          source: this.roadmap_src,
          style: this.customizedStyle,
        })
      );
      // 将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_")
        ) {
          // 获取离散化的贝塞尔曲线，genBezierPointsByControlPoints函数《GeoJSON路网数据结构说明》文档中有，这里不再重复
          var newBezierPoints = this.genBezierPointsByControlPoints(
            features[idx].getGeometry().getCoordinates(),
            0.01
          );
          // 将MultiPoint替换为贝塞尔LineString
          features[idx].setGeometry(new LineString(newBezierPoints));
        }
      }
      // 将features添加到图层
      this.roadmap_src.addFeatures(features);
      // 路线加载完毕后将当前地图的所有元素回执到父组件
      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: 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,
        })
      );
    },
    // 无人车位姿更新 (查询完毕无人车实时信息后调用此方法)
    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的定位方式
      });
      // 设置ID，便于后续操作
      overlay.set("id", type + "-" + id);
      // 添加Overlay到地图
      this.map.addOverlay(overlay);
    },

    // 删除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: "#ff9800", // 在这里改线条颜色
            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');
      });
    },
    // 绘制直线
    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);
        // 检查并创建线段的起点和终点
        coordinates.forEach((coord, index) => {
          let pointId = this.getConnectedPointId(coord); // 判断是否已有点
          if (!pointId) {
            // 补充z坐标
            coord.push(0);
            this.createPointAtCoordinate(coord, 'l'); // 创建新点
          }
          // 记录起点和终点的点ID到线的属性中（可选）
          if (index === 0) {
            feature.set('startid', pointId || 'p_' + (this.maxPointIdNum));
          } else if (index === coordinates.length - 1) {
            feature.set('endid', pointId || 'p_' + (this.maxPointIdNum));
          }
        });
        // 使用 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 type 当前触发此方式(l:绘制线段中 b:绘制曲线中) ,如果设置此参数则在绘制点位完毕后自动继续调用原来的绘制模式,不传递则不调用
     * @param data 当前点位非手动绘制时需要额外增加的参数[{数据名:数据}],可传递多个
     */
    createPointAtCoordinate(targetCoord, type, 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;
          }
        }
      }
      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');
      if (type == 'l') {
        this.drawLine();
      }
    },

    // (绘制线段)判断坐标是否接近已有点并返回该点的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;
        // 获取起点和终点的坐标
        const startCoord = coordinates[0];
        startCoord.push(0);
        const endCoord = coordinates[coordinates.length - 1];
        endCoord.push(0);
        // 检查起点和终点是否已经存在点，如果没有则生成新的点
        let startPointId = this.getConnectedPointId(startCoord);
        let endPointId = this.getConnectedPointId(endCoord);
        // 如果起点没有对应的点，生成新的点
        if (!startPointId) {
          this.createPointAtCoordinate(startCoord, 'b');
          // 将当前点位信息置入线段的起始和结束点位处
          feature.set('startid', startPointId || 'p_' + (this.maxPointIdNum));
        } else {
          feature.set('startid', startPointId);
        }
        // 如果终点没有对应的点，生成新的点
        if (!endPointId) {
          this.createPointAtCoordinate(endCoord, 'b');
          // 将当前点位信息置入线段的起始和结束点位处 
          feature.set('endid', 'p_' + (this.maxPointIdNum));
        } else {
          feature.set('endid', endPointId);
        }
        // 获取离散化的贝塞尔曲线
        var newBezierPoints = this.genBezierPointsByControlPoints(coordinates, 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', '')
      }
      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;
      }
      // 获取元素
      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) {
      // 移动点位
      let feature = this.roadmap_src.getFeatureById(id);
      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();
            // 如果点位是线段的起点
            if (startId === id) {
              const endPoint = lineCoordinates[lineCoordinates.length - 1]; // 终点坐标
              const newLineCoordinates = [position]; // 新起点坐标
              // 遍历剩余的点，更新坐标（排除终点坐标）
              for (let i = 1; i < lineCoordinates.length - 1; i++) {
                newLineCoordinates.push([lineCoordinates[i][0] + deltaX, lineCoordinates[i][1] + deltaY]);
              }
              // 添加终点坐标不做修改
              newLineCoordinates.push(endPoint);
              // 更新线段geometry
              lineGeometry.setCoordinates(newLineCoordinates);
              lineFeature.setStyle(this.customizedStyle(lineFeature));  // 确保重新设置样式，包含箭头
            }
            // 如果点位是线段的终点
            else if (endId === id) {
              const startPoint = lineCoordinates[0]; // 起点坐标
              const newLineCoordinates = [startPoint]; // 新起点坐标
              // 遍历剩余的点，更新坐标
              for (let i = 1; i < lineCoordinates.length; i++) {
                newLineCoordinates.push([lineCoordinates[i][0] + deltaX, lineCoordinates[i][1] + deltaY]);
              }
              // 更新线段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) {
      let snapeFeature = this.roadmap_src.getFeatureById(id);
      let style = this.styleForPolygon(snapeFeature)
      snapeFeature.setStyle(style);
    },
    // 完成绘制方法
    completeDrawing(start, end) {
      // 计算航向角
      const bearing = this.calculateBearing(start, end);
      // 移除预览虚线
      if (this.previewFeature) {
        this.roadmap_src.removeFeature(this.previewFeature);
        this.previewFeature = null;
      }
      this.$emit('initNavigationResult', start, bearing.toFixed(2),)
    },
    // 计算航向角
    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;
    }
  },
}
</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-color: rgba(255, 255, 255, 1);
  color: rgb(0, 0, 0);
  padding: 5px;
  border-radius: 4px;
  white-space: nowrap;
  display: none;
  box-shadow: 0 0 15px rgba(0, 0, 0, 0.1);
  width: 200px;
  padding: 10px;
}
</style>
