<template>
  <div>
    <div class="home-header">
      <img @click="drawStartPoint" src="@/assets/image/start.png" />
      <img @click="drawEndPoint" src="@/assets/image/end.png" />
      <img @click="drawViaPoint" src="@/assets/image/add.png" />
      <img @click="drawAvoidPoint" src="@/assets/image/avoidP.png" />
      <img @click="drawAvoidArea" src="@/assets/image/updown.png" />
      <div @click="clear" class="sure">清除</div>
      <div @click="calculateShortestPath" class="sure">确定</div>
      <!-- <div class="control-panel">
        <button @click="drawStartPoint">绘制起点</button>
        <button @click="drawEndPoint">绘制终点</button>
        <button @click="drawViaPoint">绘制途径点</button>
        <button @click="drawAvoidPoint">绘制避让点</button>
        <button @click="drawAvoidArea">绘制避让区域</button>
        <button @click="calculateShortestPath">计算最短路径</button>
        <button @click="clear">清除所有</button>
      </div> -->
    </div>
    <div class="home">
      <div class="main-container">
        <div class="control-panel">
          <div class="title">参数</div>
          <el-form
            @submit.native.prevent="calculateShortestPath"
            label-width="100px"
            label-position="left"
            size="mini"
            :model="form"
          >
            <el-form-item label="起点">
              <el-input v-model="form.startPoint" @blur="pointsChange('startPoint')"></el-input>
            </el-form-item>
            <el-form-item label="终点">
              <el-input v-model="form.endPoint" @blur="pointsChange('endPoint')"></el-input>
            </el-form-item>
            <el-form-item label="途径点">
              <div v-for="(item, index) in form.viaPoints" :key="index">
                <el-input v-model="item.points" placeholder="" @blur="pointsChange('viaPoints', item)"></el-input>
              </div>
            </el-form-item>
            <el-form-item label="避让点">
              <div v-for="(item, index) in form.avoidPoints" :key="index">
                <el-input v-model="item.points" placeholder="" @blur="pointsChange('avoidPoints', item)"></el-input>
              </div>
            </el-form-item>
            <el-form-item label="避让区域">
              <div v-for="(item, index) in form.avoidAreas" :key="index">
                <el-input
                  type="textarea"
                  v-model="item.points"
                  :rows="4"
                  placeholder=""
                  @blur="pointsChange('avoidAreas', item)"
                ></el-input>
              </div>
            </el-form-item>
            <!-- <el-form-item>
              <el-button type="primary" @click="calculateShortestPath">计算最短路径</el-button>
              <el-button @click="clear">清除所有</el-button>
            </el-form-item> -->
          </el-form>
          <input type="file" ref="fileInput" @change="handleFileUpload" style="display: none;" />
          <div class="importJson" @click="triggerFileUpload">导入json文件</div>
        </div>
        <div class="control-panel">
          <div class="title">机动属性</div>
          <el-form
            @submit.native.prevent="calculateShortestPath"
            label-width="100px"
            label-position="left"
            size="mini"
          > 
            <el-form-item label="宽度">
              <el-input v-model="inputform.width"></el-input>
            </el-form-item>
            <el-form-item label="载重（吨）">
              <el-input v-model="inputform.load" placeholder=""></el-input>
            </el-form-item>
            <el-form-item label="最小转弯半径">
              <el-input v-model="inputform.minTurnRadius" placeholder=""></el-input>
            </el-form-item>
          </el-form>
          <div class="importJson" @click="openDialog">数据选择</div>
        </div>
      </div>
      <div id="map"></div>
      <!-- <div id="mapbox"></div> -->
      <div class="main-container" style="width: 452px;">
        <div class="control-panel" style="width: 452px;">
        <div style="font-size: 14px;margin-bottom: 10px;">
            详细路线：<br/>
            起点（{{form.startPoint}}），
            途径点（{{form.viaPoints.length > 0 && form.viaPoints[0].points ? form.viaPoints.map(item => item.points).join(";") : ''}}),
            避让点（{{form.avoidPoints.length > 0 && form.avoidPoints[0].points ? form.avoidPoints.map(item => item.points).join(";") : ''}}),
            终点（{{form.endPoint}}）,
            避让区域（{{form.avoidAreas.length > 0 && form.avoidAreas[0].points ? form.avoidAreas.map(item => item.points).join(";") : ''}})。<br/>
            装备参数:最大车辆宽度{{inputform.width || 0}},最小转弯半径{{inputform.minTurnRadius || 0}}，最大车辆载重{{inputform.load || 0}}吨
          </div>
          <vxe-table
            ref="xTable"
            :data="infoList"
          >
            <vxe-column field="编码" title="编码" width="65px"></vxe-column>
            <vxe-column field="名称" title="名称" width="80px"></vxe-column>
            <vxe-column field="宽度" title="路宽"></vxe-column>
            <vxe-column field="曲率半" title="曲率"></vxe-column>
            <vxe-column field="载重吨" title="载重"></vxe-column>
            <vxe-column field="水深" title="水深"></vxe-column>
            <vxe-column field="净空高" title="净空高" width="65px"></vxe-column>
          </vxe-table>
        </div>
      </div>
      <el-dialog :visible.sync="dialogVisible" title="机动属性" width="600px">
        <vxe-table
          height="300px"
          :data="tableData"
          :row-config="{ isCurrent: true, isHover: true }"
          :checkbox-config="{ checkField: 'checked', highlight: true }"
          @checkbox-change="handleSelectionChange"
        >
          <vxe-column type="checkbox" width="50"></vxe-column>
          <vxe-column field="width" title="宽度"></vxe-column>
          <vxe-column field="load" title="载重（吨）"></vxe-column>
          <vxe-column field="minTurnRadius" title="最小转弯半径"></vxe-column>
        </vxe-table>
        <div slot="footer" class="dialog-footer">
          <el-button @click="dialogVisible = false">取消</el-button>
          <el-button type="primary" @click="confirmSelection">确定</el-button>
        </div>
      </el-dialog>
    </div>
  </div>
</template>

<script>
import router from '@/router';

export default {
  data() {
    return {
      dialogVisible: false,
      tableData: [],
      multipleSelection: [],
      inputform: {
        width: '',
        load: '',
        minTurnRadius: ''
      },
      form: {
        startPoint: '',
        endPoint: '',
        viaPoints: [{ points: '', time: '' }],
        avoidPoints: [{ points: '', time: '' }],
        avoidAreas: [{ points: '', time: '' }],
      },
      viewer: null,
      graphicLayer: null,
      polygonZAM: null,
      pointQD: null,
      pointZD: null,
      shortestPathLayer: null,
      infoList: [],
      roadNetworkLayer: null,
      roadNetworkGeoJSON: null,
      mapOptions: null,
      viaPoints: [], // 途径点
      avoidPoints: [], // 避让点
      avoidAreas: [], // 避让区域
      roadNetworkGeoJSONBuild: null,
    }
  },
  async mounted() {
    console.log('go')
    this.viewer = null
    await this.getMapOption()
    this.$nextTick(async () => {
      await this.initMap()
    })
  },
  beforeDestroy() {
    this.destroyMap()
  },
  methods: {
    destroyMap() {
      // if (this.viewer) {
      //   this.viewer.destroy();
      //   this.viewer = null;
      // }
      this.clear()
    },
    async getMapOption() {
      await fetch('./config/map.json')
        .then((response) => {
          return response.json()
        })
        .then((data) => {
          this.mapOptions = data.map3d
        })
        .catch((error) => {})
    },
    async initMap() {
      this.viewer = new window.mars3d.Map('map', {
        ...this.mapOptions,
        scene: {
          center: {
            lat: 27.729862392917948,
            lng: 114.27980291774088,
            alt: 3808,
            heading: 5,
            pitch: -35
          }
        },
      } || {})
      this.graphicLayer = new window.mars3d.layer.GraphicLayer()
      this.viewer.addLayer(this.graphicLayer)

      this.shortestPathLayer = new window.mars3d.layer.GraphicLayer()
      this.viewer.addLayer(this.shortestPathLayer)
      this.loadShapefile()
      // 添加地图点击事件监听，用于结束绘制
      this.viewer.on(mars3d.EventType.dblClick, (event) => {
        // 如果正在绘制，点击地图可以结束绘制（除了绘制点）
        this.graphicLayer.stopDraw()
      })
    },
    clear() {
      // 清除起点
      if (this.pointQD) {
        this.pointQD.remove()
        this.pointQD = null
        this.form.startPoint = ''
      }

      // 清除终点
      if (this.pointZD) {
        this.pointZD.remove()
        this.pointZD = null
        this.form.endPoint = ''
      }
      if (this.viaPoints.length > 0) {
        // 清除途径点
        this.viaPoints.forEach((point) => {
          point.remove()
        })
        this.viaPoints = []
        this.form.viaPoints = [{ points: '', time: '' }]
      }

      if (this.avoidPoints.length > 0) {
        // 清除避让点
        this.avoidPoints.forEach((point) => {
          point.remove()
        })
        this.avoidPoints = []
        this.form.avoidPoints = [{ points: '', time: '' }]
      }

      if (this.avoidAreas.length > 0) {
        // 清除避让区域
        this.avoidAreas.forEach((area) => {
          area.remove()
        })
        this.avoidAreas = []
        this.form.avoidAreas = [{ points: '', time: '' }]
      }
      if (this.shortestPathLayer) {
        // 清除最短路径图层
        this.shortestPathLayer.clear()
        this.infoList = []
      }
    },
    async loadShapefile() {
      try {
        // 1. 读取本地 SHP（浏览器）
        // const shpBuffer = await fetch('./config/map/data/dlfs_lines.zip').then(r => r.arrayBuffer());
        // const shpJson = await shp(shpBuffer);   // {features: [...]}

        // 1. 读取本地 jeojson
        const shpBuffer = await fetch('./config/dao.geojson')
          .then((response) => {
            return response.json()
          })
          .then((data) => {
            return data
          })
          .catch((error) => {})

        // 2. 处理 GeoJSON 数据
        this.roadNetworkGeoJSON = shpBuffer

        /// 3. 将 GeoJSON 数据添加到 graphicLayer
        this.roadNetworkGeoJSON.features.forEach((feature) => {
          const positions = feature.geometry.coordinates[0]
          const graphic = new window.mars3d.graphic.PolylineEntity({
            positions: positions,
            style: {
              color: '#FF0000',
              width: 2,
              outline: false,
            },
          })
          this.graphicLayer.addGraphic(graphic)
        })
        this.roadNetworkGeoJSONBuild = this.buildGraph(this.roadNetworkGeoJSON)
      } catch (error) {
        console.error('加载 Shapefile 数据失败:', error)
      }
    },
    // 弹框
    async openDialog() {
      await fetch('./data/minTurnRadius.json')
      .then((response) => {
        return response.json()
      })
      .then((data) => {
        this.tableData = data
      })
      .catch((error) => {})
      this.dialogVisible = true;
    },
    handleSelectionChange({ records }) {
      this.multipleSelection = records;
    },
    confirmSelection() {
      if (this.multipleSelection.length === 0) {
        this.$message.warning('请至少选择一行数据');
        return;
      }
      const maxValues = this.multipleSelection.reduce((acc, item) => {
        acc.width = Math.max(acc.width, item.width);
        acc.load = Math.max(acc.load, item.load);
        acc.minTurnRadius = Math.max(acc.minTurnRadius, item.minTurnRadius);
        return acc;
      }, { width: 0, load: 0, minTurnRadius: 0 });
      this.inputform.width = maxValues.width;
      this.inputform.load = maxValues.load;
      this.inputform.minTurnRadius = maxValues.minTurnRadius;
      this.dialogVisible = false;
    },
    // 导入json文件
    triggerFileUpload() {
      this.$refs.fileInput.click();
    },
    handleFileUpload(event) {
      const file = event.target.files[0];
      if (file) {
        const reader = new FileReader();
        reader.onload = (e) => {
          try {
            const uploadedJson = JSON.parse(e.target.result);
            this.importJson(uploadedJson);
          } catch (error) {
            console.error('解析 JSON 文件失败:', error);
            this.$message.error('解析 JSON 文件失败');
          }
        };
        reader.readAsText(file);
      }
    },
    async importJson(uploadedJson) {
      if (!uploadedJson) {
        this.$message.warning('请先选择一个 JSON 文件');
        return;
      };
      // 清除现有数据
      this.clear();
      // 加载起点
      if (uploadedJson.startPoint) {
        this.form.startPoint = uploadedJson.startPoint
        this.addPointToMap('startPoint', this.form.startPoint);
      }

      // 加载终点
      if (uploadedJson.endPoint) {
        this.form.endPoint = uploadedJson.endPoint
        this.addPointToMap('endPoint', this.form.endPoint);
      }

      // 加载途径点
      if (uploadedJson.viaPoints) {
        this.form.viaPoints = uploadedJson.viaPoints
        uploadedJson.viaPoints.forEach(viaPoint => {
          this.addPointToMap('viaPoints', viaPoint, viaPoint.time);
        });
      }

      // 加载避让点
      if (uploadedJson.avoidPoints) {
        this.form.avoidPoints = uploadedJson.avoidPoints
        uploadedJson.avoidPoints.forEach(avoidPoint => {
          this.addPointToMap('avoidPoints', avoidPoint, avoidPoint.time);
        });
      }

      // 加载避让区域
      if (uploadedJson.avoidAreas) {
        this.form.avoidAreas = uploadedJson.avoidAreas
        uploadedJson.avoidAreas.forEach(avoidArea => {
          const time = Date.now();
          this.addPolygonToMap('avoidAreas', avoidArea, avoidArea.time);
        });
      }

    },
    addPointToMap(type, point, time) {
      const coords = time ? point.points.split(',').map(Number) : point.split(',').map(Number);
      const graphic = new window.mars3d.graphic.PointEntity({
        position: new window.mars3d.LngLatPoint(coords[0], coords[1]),
        style: {
          pixelSize: 10,
          color: type === 'startPoint' ? 'red' : type === 'endPoint' ? 'red' : type === 'viaPoints' ? 'blue' : 'orange',
          label: {
            text: type === 'startPoint' ? '起点' : type === 'endPoint' ? '终点' : type === 'viaPoints' ? '途径点' : '避让点',
            font_size: 20,
            color: '#ffffff',
            outline: true,
            outlineColor: '#000000',
            pixelOffsetY: -20,
          },
          time: time
        },
      });
      this.graphicLayer.addGraphic(graphic);

      if (type === 'startPoint') {
        this.pointQD = graphic;
      } else if (type === 'endPoint') {
        this.pointZD = graphic;
      } else if (type === 'viaPoints') {
        this.viaPoints.push(graphic);
      } else if (type === 'avoidPoints') {
        this.avoidPoints.push(graphic);
      }
    },
    addPolygonToMap(type, area, time) {
      const coords = JSON.parse(area.points);
      const graphic = new window.mars3d.graphic.PolygonEntity({
        positions: coords.map(coord => new window.mars3d.LngLatPoint(coord[0], coord[1])),
        style: {
          color: 'red',
          opacity: 0.4,
          clampToGround: true,
          outline: true,
          outlineWidth: 1,
          outlineColor: '#ffffff',
          time: time
        },
      });
      this.graphicLayer.addGraphic(graphic);

      if (type === 'avoidAreas') {
        this.avoidAreas.push(graphic);
      }
    },
    // 输入框失去焦点 反向编辑点
    pointsChange(type, row) {
      if (type === 'startPoint') {
        if ((!this.form.startPoint || this.form.startPoint == '' || this.form.startPoint == null || this.form.startPoint == undefined) && this.pointQD) {
          this.pointQD.remove()
          this.pointQD = null;
          return
        }
        this.updatePointPosition(this.pointQD, this.form.startPoint);
      } else if (type === 'endPoint') {
        if ((!this.form.endPoint || this.form.endPoint == '' || this.form.endPoint == null || this.form.endPoint == undefined) && this.pointZD) {
          this.pointZD.remove()
          this.pointZD = null;
          return
        }
        this.updatePointPosition(this.pointZD, this.form.endPoint);
      } else if (type === 'viaPoints') {
          if (!row.points) {
          const graphic = this.viaPoints.find(viaPoint => viaPoint.style.time === row.time);
          if (this.form.viaPoints.length === 1 && this.form.viaPoints[0].points === '') {
            // 如果只剩下一个空项，不删除图形，清空输入框值
            this.form.viaPoints[0].points = '';
            graphic?.remove();
            this.viaPoints = this.viaPoints.filter(viaPoint => viaPoint.style.time !== row.time);
          } else {
            this.form.viaPoints = this.form.viaPoints.filter(viaPoint => viaPoint.time !== row.time);
          }
          graphic?.remove();
          this.viaPoints = this.viaPoints.filter(viaPoint => viaPoint.style.time !== row.time);
        } else {
          const graphic = this.viaPoints.find(viaPoint => viaPoint.style.time === row.time);
          this.updatePointPosition(graphic, row.points);
        }
      } else if (type === 'avoidPoints') {
        if (!row.points) {
          const graphic = this.avoidPoints.find(avoidPoint => avoidPoint.style.time === row.time);
          if (this.form.avoidPoints.length === 1 && this.form.avoidPoints[0].points === '') {
            // 如果只剩下一个空项，不删除图形，清空输入框值
            this.form.avoidPoints[0].points = '';
          } else {
            this.form.avoidPoints = this.form.avoidPoints.filter(avoidPoint => avoidPoint.time !== row.time);
          }
          graphic?.remove();
          this.avoidPoints = this.avoidPoints.filter(avoidPoint => avoidPoint.style.time !== row.time);
        } else {
          const graphic = this.avoidPoints.find(avoidPoint => avoidPoint.style.time === row.time);
          this.updatePointPosition(graphic, row.points);
        }
      } else if (type === 'avoidAreas') {
        if (!row.points) {
          const graphic = this.avoidAreas.find(avoidArea => avoidArea.style.time === row.time);
          if (this.form.avoidAreas.length === 1 && this.form.avoidAreas[0].points === '') {
            // 如果只剩下一个空项，不删除图形，清空输入框值
            this.form.avoidAreas[0].points = '';
          } else {
            this.form.avoidAreas = this.form.avoidAreas.filter(avoidArea => avoidArea.time !== row.time);
          }
          graphic?.remove();
          this.avoidAreas = this.avoidAreas.filter(avoidArea => avoidArea.style.time !== row.time);
        } else {
          const graphic = this.avoidAreas.find(avoidArea => avoidArea.style.time === row.time);
          this.updatePolygonPosition(graphic, row.points);
        }
      }
    },
    handleEmptyArray(type) {
      if (type === 'viaPoints' && this.form.viaPoints.length === 1 && this.form.viaPoints[0].points === '') {
        this.form.viaPoints = [];
      } else if (type === 'avoidPoints' && this.form.avoidPoints.length === 1 && this.form.avoidPoints[0].points === '') {
        this.form.avoidPoints = [];
      } else if (type === 'avoidAreas' && this.form.avoidAreas.length === 1 && this.form.avoidAreas[0].points === '') {
        this.form.avoidAreas = [];
      }
    },
    updatePointPosition(graphic, pointsStr) {
      if (!graphic) return;
      const coords = pointsStr.split(',').map(Number);
      if (coords.length === 2) {
        graphic.position = new window.mars3d.LngLatPoint(coords[0], coords[1]);
      }
    },
    updatePolygonPosition(graphic, pointsStr) {
      if (!graphic) return;
      try {
        const coords = JSON.parse(pointsStr);
        graphic.positions = coords.map(coord => new window.mars3d.LngLatPoint(coord[0], coord[1]));
      } catch (error) {
        graphic.remove();
      }
    },
    drawStartPoint() {
      if (this.pointQD) {
        this.pointQD.remove()
        this.pointQD = null
        this.form.startPoint = ''
      }
      this.graphicLayer.startDraw({
        type: 'point',
        style: {
          pixelSize: 10,
          color: 'red',
          label: {
            text: '起点',
            font_size: 20,
            color: '#ffffff',
            outline: true,
            outlineColor: '#000000',
            pixelOffsetY: -20,
          },
        },
        success: (graphic) => {
          this.pointQD = graphic
          this.form.startPoint = [graphic.toGeoJSON().geometry.coordinates[0],graphic.toGeoJSON().geometry.coordinates[1]].join(",");
        },
      })
    },
    drawEndPoint() {
      if (this.pointZD) {
        this.pointZD.remove()
        this.pointZD = null
        this.form.endPoint = ''
      }
      this.graphicLayer.startDraw({
        type: 'point',
        style: {
          pixelSize: 10,
          color: 'red',
          label: {
            text: '终点',
            font_size: 20,
            color: '#ffffff',
            outline: true,
            outlineColor: '#000000',
            pixelOffsetY: -20,
          },
        },
        success: (graphic) => {
          this.pointZD = graphic
          this.form.endPoint = [graphic.toGeoJSON().geometry.coordinates[0],graphic.toGeoJSON().geometry.coordinates[1]].join(",");
        },
      })
    },
    // 途径点
    drawViaPoint() {
      const time = Date.now();
      this.graphicLayer.startDraw({
        type: 'point',
        style: {
          pixelSize: 10,
          color: 'blue',
          label: {
            text: '途径点',
            font_size: 20,
            color: '#ffffff',
            outline: true,
            outlineColor: '#000000',
            pixelOffsetY: -20,
          },
          time: time
        },
        success: (graphic) => {
          this.viaPoints.push(graphic)
          const points = [graphic.toGeoJSON().geometry.coordinates[0],graphic.toGeoJSON().geometry.coordinates[1]].join(",")
          if(this.form.viaPoints.length == 1 && this.form.viaPoints[0].points == '' && !this.form.viaPoints[0].points){
            this.form.viaPoints[0].points = points
            this.form.viaPoints[0].time = time
          } else {
            this.form.viaPoints.push({points,time})
          }
        },
      })
    },
    // 避让点
    drawAvoidPoint() {
      const time = Date.now();
      this.graphicLayer.startDraw({
        type: 'point',
        style: {
          pixelSize: 10,
          color: 'orange',
          label: {
            text: '避让点',
            font_size: 20,
            color: '#ffffff',
            outline: true,
            outlineColor: '#000000',
            pixelOffsetY: -20,
          },
          time: time
        },
        success: (graphic) => {
          this.avoidPoints.push(graphic)
          const points = [graphic.toGeoJSON().geometry.coordinates[0],graphic.toGeoJSON().geometry.coordinates[1]].join(",")
          if(this.form.avoidPoints.length == 1 && this.form.avoidPoints[0].points == '' && !this.form.avoidPoints[0].points){
            this.form.avoidPoints[0].points = points
            this.form.avoidPoints[0].time = time
          } else {
            this.form.avoidPoints.push({points, time})
          }
        },
      })
    },
    // 避让区域
    drawAvoidArea() {
      const time = Date.now();
      this.graphicLayer.startDraw({
        type: 'polygon',
        drawEndEventType: window.mars3d.EventType.dblClick,
        style: {
          color: '#ff0000',
          opacity: 0.4,
          clampToGround: true,
          outline: true,
          outlineWidth: 1,
          outlineColor: '#ffffff',
          time: time
        },
        success: (graphic) => {
          this.avoidAreas.push(graphic)
          const avoidAreasGeoJSON = graphic.toGeoJSON()
          const points = JSON.stringify(avoidAreasGeoJSON.geometry.coordinates[0])
          if(this.form.avoidAreas.length == 1 && this.form.avoidAreas[0].points == '' && !this.form.avoidAreas[0].points){
            this.form.avoidAreas[0].points = points
            this.form.avoidAreas[0].time = time
          } else {
            this.form.avoidAreas.push({points, time})
          }
        },
      })
    },
    // 1. 构建路网拓扑图（经纬度坐标直接处理
    buildGraph(geojson) {
      const graph = {}
      const nodeCoords = {} // 存储节点经纬度

      geojson.features.forEach((road) => {
        const startNode = road.properties.FNODE_
        const endNode = road.properties.TNODE_
        const coords = road.geometry.coordinates

        // 记录节点经纬度（取首尾点）
        if (!nodeCoords[startNode]) {
          nodeCoords[startNode] = coords[0][0]
        }
        if (!nodeCoords[endNode]) {
          nodeCoords[endNode] = coords[coords.length - 1][0]
        }
        // 计算边权重（距离，单位：米）
        const distance =
          turf.distance(
            turf.point(coords[0][0]),
            turf.point(coords[coords.length - 1][0]),
            {units: 'kilometers'}
          ) * 1000

        // 构建邻接表（双向图）
        graph[startNode] = graph[startNode] || {}
        graph[startNode][endNode] = distance

        graph[endNode] = graph[endNode] || {}
        graph[endNode][startNode] = distance
      })
      return {graph, nodeCoords}
    },
    // 2. 匹配最近路网节点（经纬度坐标）
    findNearestNode(pointCoord, nodeCoords) {
      let nearestNode = null
      let minDist = 10000

      for (const [node, coord] of Object.entries(nodeCoords)) {
        const dist = turf.distance(turf.point(pointCoord), turf.point(coord), {
          units: 'meters',
        })

        if (dist < minDist) {
          minDist = dist
          nearestNode = node
        }
      }
      return nearestNode
    },
    // 3. 路径规划主函数（经纬度坐标输入） - 支持途径点 + 避让点/区域
    async planRoute(
      startCoord,
      endCoord,
      viaPoints = [],
      avoidObstacles = null
    ) {
      const {graph, nodeCoords} = this.roadNetworkGeoJSONBuild

      // 按顺序组合点：起点 -> 途径点 -> 终点
      const points =
        viaPoints && viaPoints.length > 0
          ? [startCoord, viaPoints[0].geometry.coordinates, endCoord]
          : [startCoord, endCoord]
      const fullPath = []
      const infoList = []

      for (let i = 0; i < points.length - 1; i++) {
        const segmentStart = points[i]
        const segmentEnd = points[i + 1]

        // 匹配最近节点
        const startNode = this.findNearestNode(segmentStart, nodeCoords)
        const endNode = this.findNearestNode(segmentEnd, nodeCoords)

        if (!startNode || !endNode) {
          console.log('无法匹配到路网节点，请检查坐标是否在路网范围内')
          continue
        }
        // 构建临时 graph
        const tempGraph = JSON.parse(JSON.stringify(graph))

        // 删除避让点节点
        if (avoidObstacles) {
          const obstacleNodes = []
          for (const [node, coord] of Object.entries(nodeCoords)) {
            const pt = turf.point(coord)
            avoidObstacles.features.forEach((ob) => {
              if (turf.booleanPointInPolygon(pt.geometry, ob.geometry))
                obstacleNodes.push(node)
            })
          }
          obstacleNodes.forEach((node) => delete tempGraph[node])

          // 删除与避让区域相交的边
          for (const [node, edges] of Object.entries(tempGraph)) {
            for (const targetNode of Object.keys(edges)) {
              if (!tempGraph[targetNode]) {
                // 避让点节点已经删除
                delete tempGraph[node][targetNode]
                continue
              }
              const line = turf.lineString([
                nodeCoords[node],
                nodeCoords[targetNode],
              ])
              avoidObstacles.features.forEach((area) => {
                if (turf.booleanCrosses(line, area)) {
                  delete tempGraph[node][targetNode]
                }
              })
            }
          }
        }

        // Dijkstra 计算最短路径
        const pathNodes = dijkstra.find_path(tempGraph, startNode, endNode)
        if (!pathNodes || pathNodes.length === 0) {
          console.log('未找到可行路径段:', i)
          continue
        }

        // 生成路径几何
        for (let j = 0; j < pathNodes.length - 1; j++) {
          const currentNode = pathNodes[j]
          const nextNode = pathNodes[j + 1]
          const segment = this.roadNetworkGeoJSON.features.find(
            (f) =>
              (f.properties.FNODE_ == currentNode &&
                f.properties.TNODE_ == nextNode) ||
              (f.properties.FNODE_ == nextNode &&
                f.properties.TNODE_ == currentNode)
          )
          if (segment) {
            fullPath.push(...segment.geometry.coordinates[0])
            infoList.push(segment)
          }
        }
      }

      return {fullPath, infoList}
    },
    async calculateShortestPath() {
      if (!this.pointQD || !this.pointZD || !this.roadNetworkGeoJSON) {
        this.$message.warning('请先加载路网数据并绘制障碍面、起点和终点！')
        return
      }
      this.shortestPathLayer.clear()
      this.infoList = []
      const startPoint = turf.point(
        this.pointQD.toGeoJSON().geometry.coordinates
      ).geometry.coordinates // 起点
      const endPoint = turf.point(this.pointZD.toGeoJSON().geometry.coordinates)
        .geometry.coordinates // 终点
      // 途径点
      const viaPointsGeoJSON =
        this.viaPoints.map((point) => point.toGeoJSON()) || []
      const viaPointsTurf = viaPointsGeoJSON.map((p) =>
        turf.point(p.geometry.coordinates)
      )
      // 避让点
      const avoidPointsGeoJSON =
        this.avoidPoints.map((point) => point.toGeoJSON()) || []
      const avoidPointsPolygons = avoidPointsGeoJSON.map((point) => {
        return turf.circle(
          turf.point(point.geometry.coordinates),
          10, // 半径10米（根据需求调整）
          {steps: 32, units: 'meters'} // 显式指定单位
        )
      })
      // 避让区域
      const avoidAreasGeoJSON =
        this.avoidAreas.map((area) => area.toGeoJSON({closure: true})) || []
      const obstaclesGeoJSON = turf.featureCollection([
        ...avoidPointsPolygons,
        ...avoidAreasGeoJSON,
      ])
      const route = await this.planRoute(
        startPoint,
        endPoint,
        viaPointsTurf,
        obstaclesGeoJSON
      )
      this.drawPath(route)
    },

    // 单独的路径绘制方法
    drawPath(path) {
      const positions = path
      const polyline = new window.mars3d.graphic.PolylinePrimitive({
        positions: positions.fullPath,
        style: {
          clampToGround: true,
          color: '#55ff33',
          width: 8,
        },
      })
      this.shortestPathLayer.addGraphic(polyline)
      this.infoList = path.infoList.map(item => item.properties)
    },
  },
}
</script>

<style scoped>
.home-header {
  height: 60px;
  line-height: 60px;
  display: flex;
  align-items: center;
  padding-left: 34px;
  box-sizing: border-box;
  background: #abc6bc;
}
.home-header img {
  height: 24px;
  width: 24px;
  margin-right: 12px;
  cursor: pointer;
}
.sure {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  /* padding: 4px 12px; */
  gap: 10px;
  background: #176363;
  border-radius: 4px;
  width: 54px;
  height: 24px;
  color: #ffffff;
  font-weight: 400;
  font-size: 14px;
  margin-right: 10px;
  cursor: pointer;
}
.dialog-body {
  max-height: 500px;
}
.home {
  display: flex;
  height: calc(100% - 60px);
  background: #abc6bc;
}
.main-container {
  width: 340px;
  height: 100%;
  background: #d4e5db;
  overflow: hidden;
  overflow-y: auto;
  scrollbar-width: none; /* 隐藏滚动条（Firefox） */
}

.main-container::-webkit-scrollbar {
  display: none; /* 隐藏滚动条（Chrome, Safari, Edge） */
}

.control-panel {
  width: 340px;
  padding: 20px 26px;
  background-size: cover;
  box-sizing: border-box;
}

.control-panel .title {
  /* text-align: center; */
  margin-bottom: 10px;
  color: #1c1c1c;
}
.importJson {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  background: #2f705f;
  border-radius: 4px;
  color: #fff;
  width: 260px;
  height: 24px;
  font-size: 14px;
  text-align: center;
  margin-left: 20px;
  cursor: pointer;
}

.form-group {
  margin-bottom: 10px;
}

.form-group input,
.form-group textarea {
  width: 100%;
  padding: 5px;
  border-radius: 3px;
  border: none;
}

.form-actions {
  display: flex;
  justify-content: space-between;
}

#map,
#mapbox,
.el-main {
  flex: 1;
  width: calc(100vw- 792px);
  height: 100%;
}
</style>