<!--
 * @Author: SunTao 328867980@qq.com
 * @Date: 2024-10-14 10:46:23
 * @LastEditors: SunTao 328867980@qq.com
 * @LastEditTime: 2024-10-17 10:33:59
 * @FilePath: \znxjxt-ui\src\views\xj\inspection\task-management\components\fssm-map.vue
 * @Description: 公共地图
-->

<template>
  <div class="map-container">
    <div ref="container" :id="`map-${mapId}`"></div>
    <div ref="mapController" class="control-container">
      <!-- 绘制图形 -->
      <div class="draw-map" v-if="showDraw">
        <i class="el-icon-edit" @click="drawMap()"></i>
        <i class="el-icon-delete" @click="deletedraw()"></i>
      </div>
      <!-- 切换底图控件 -->
      <div class="change-map" v-if="showChange">
        <i
          class="el-icon-picture-outline"
          :class="mapType === 'cva_c' ? 'change-map-click' : ''"
          @click="changeImg('cva_c')"
        ></i>
        <i
          class="el-icon-picture"
          :class="mapType === 'img_c' ? 'change-map-click' : ''"
          @click="changeImg('img_c')"
        ></i>
      </div>
      <!-- 放大缩小控件 -->
      <div class="bigButton map-btn" v-if="showZoom">
        <i class="el-icon-plus" @click="changeZoom(1, 0.5)"></i>
      </div>
      <div class="smallButton map-btn" v-if="showZoom">
        <i class="el-icon-minus" @click="changeZoom(-1, 0.5)"></i>
      </div>
    </div>
  </div>
</template>

<script>
import { Feature, Map, View } from "ol";
import XYZ from "ol/source/XYZ";
import { Tile as TileLayer } from "ol/layer";
import { defaults as defaultControls } from "ol/control";
import VectorLayer from "ol/layer/Vector";
import { Vector as VectorSource } from "ol/source";
import {
  Draw,
  Modify,
  Select,
  Snap,
  defaults as defaultInteractions,
} from "ol/interaction";
import * as styleExports from "ol/style";
import { Polygon } from "ol/geom";

export default {
  name: "FssmMap",
  props: {
    // 接收传过来得中心点
    centerPoint: {
      type: Object,
      default: () => {
        return { lat: 123.30297096718999, lon: 41.87942945541742 };
      },
    },
    // 接受传过来得地图层级
    zoom: {
      type: String,
      default: "9",
    },
    // 是否显示绘图功能
    showDraw: {
      type: Boolean,
      default: false,
    },
    // 是否显示地图放大缩小
    showZoom: {
      type: Boolean,
      default: false,
    },
    // 是否显示切换地图底层按钮
    showChange: {
      type: Boolean,
      default: false,
    },
    // 用于区分同一个页面多个地图的id
    mapId: {
      type: String,
      default: "0",
    },
    // 接受传过来的绘制点位数组
    editCoordinates: {
      type: Array,
      default: () => [],
    },
  },
  data() {
    return {
      // 地图实例
      instance: new Map(),
      // 点击地图类型
      mapType: "cva_c",
      // 地图图层
      mapLayers: [],
      // 绘制图层
      draw: null,
      source: new VectorSource(),
      // 绘制图形保存的点位
      drawMarkers: [],
    };
  },
  watch: {
    editCoordinates: {
      handler(val) {
        this.$nextTick(() => {
          const aa = val.map((item) => {
            return [item[0] * 1, item[1] * 1];
          });
          this.drawMarkers = aa;
          const map = this.instance.get("map");
          // 增加绘图层
          const drawLayer = new VectorLayer({
            source: this.source,
            id: "draw",
          });
          const feature = new Feature({
            geometry: new Polygon([aa]),
          });
          drawLayer.getSource().addFeature(feature);
          map.addLayer(drawLayer);
        });
      },
      deep: true,
      immediate: true,
    },
  },
  mounted() {
    this.initMap();
  },
  created() {
    // this.changeImg("cva_c");
  },
  methods: {
    /* 初始化openlayer地图 */
    initMap() {
      const tianditu_vec_c = new TileLayer({
        title: "矢量底图",
        id: "vec_c",
        source: new XYZ({
          url: "http://t{0-7}.tianditu.com/DataServer?x={x}&y={y}&l={z}&T=vec_c&tk=c691040443c68cda625755c5c3e2acc3",
          projection: "EPSG:4326",
        }),
        visible: true,
      });
      const tianditu_cva_c = new TileLayer({
        title: "矢量地图",
        id: "cva_c",
        source: new XYZ({
          url: "http://t{0-7}.tianditu.com/DataServer?x={x}&y={y}&l={z}&T=cva_c&tk=c691040443c68cda625755c5c3e2acc3",
          projection: "EPSG:4326",
        }),
        visible: true,
      });
      const tianditu_img_c = new TileLayer({
        title: "影像地图",
        id: "img_c",
        source: new XYZ({
          url: "http://t{0-7}.tianditu.com/DataServer?x={x}&y={y}&l={z}&T=img_c&tk=c691040443c68cda625755c5c3e2acc3",
          projection: "EPSG:4326",
        }),
        visible: false,
      });
      const map = new Map({
        target: `map-${this.mapId}`,
        controls: defaultControls({
          zoom: false,
          attribution: false,
          rotate: false,
        }),
        view: new View({
          center: [this.centerPoint.lat, this.centerPoint.lon], //中心点经纬度
          zoom: this.zoom, //图层缩放大小
          projection: "EPSG:4326",
        }),
        layers: [tianditu_vec_c, tianditu_img_c, tianditu_cva_c],
      });
      // 图层点击事件
      map.on("singleclick", (e) => {
        const featureClick = map.forEachFeatureAtPixel(
          map.getEventPixel(e.originalEvent),
          (feature) => {
            return feature;
          }
        );
        // 如果有图层则返回图层点击
        if (featureClick) {
          this.$emit("feature-click", featureClick);
        } else {
          // 没有图层则返回地图点击事件
          this.$emit("map-click", e);
        }
      });
      // 图层双击事件
      map.on("dblclick", (e) => {
        const featureDblclick = map.forEachFeatureAtPixel(
          map.getEventPixel(e.originalEvent),
          (feature) => {
            return feature;
          }
        );
        if (featureDblclick) {
          this.$emit("feature-dblclick", featureDblclick);
        }
      });
      // 鼠标移入事件
      map.on("pointermove", (e) => {
        const feature = map.forEachFeatureAtPixel(
          map.getEventPixel(e.originalEvent),
          (mapFeature) => {
            return mapFeature;
          }
        );
        // 线、面要素不做鼠标移入样式修改
        if (feature) {
          if (feature.getGeometry()?.getType() === "Point") {
            map.getTargetElement().style.cursor = "pointer";
            this.$emit("pointer-move", feature);
          } else {
            map.getTargetElement().style.cursor = "auto";
          }
        } else {
          map.getTargetElement().style.cursor = "auto";
        }
      });
      this.instance.set("map", map);
      this.instance.set("overlay-list", []);
    },
    /* 绘制地图 */
    drawMap() {
      if (this.drawMarkers.length < 1) {
        const map = this.instance.get("map");
        // 增加绘图层
        const drawLayer = new VectorLayer({
          source: this.source,
          id: "draw",
        });
        map.addLayer(drawLayer);
        // 增加snap吸附功能 ，还有就是snap交互可以在编辑和画features时候保持拓扑结构
        const snap = new Snap({
          source: this.source,
        });
        map.addInteraction(snap);
        // 添加修改元素使得绘制的多边形可以编辑
        const modify = new Modify({ source: this.source });
        map.addInteraction(modify);
        this.draw = new Draw({
          source: this.source,
          type: "Polygon",
          style: new styleExports.Style({
            image: new styleExports.Circle({
              radius: 5,
              fill: new styleExports.Fill({
                color: "blue",
              }),
            }),
            stroke: new styleExports.Stroke({
              color: "blue",
              width: 2,
            }),
            fill: new styleExports.Fill({
              color: "rgba(0, 0, 255, 0.2)",
            }),
          }),
        });
        map.addInteraction(this.draw);
        this.draw.on("drawend", (e) => {
          this.drawend(e);
        });
        modify.on("modifyend", (e) => {
          e.features.forEach((feature) => {
            this.drawMarkers = feature.getGeometry().getCoordinates().flat();
            this.$emit("endEoordinate", this.drawMarkers);
          });
        });
      }
    },
    /* 监听绘制完成的事件 */
    drawend(event) {
      const feature = event.feature;
      const geometry = feature.getGeometry();
      // 获取区域点位
      const coordinates = geometry.getCoordinates()[0];
      this.drawMarkers = coordinates;
      // 在 drawend 结束后检查是否有交叉的线段
      if (coordinates.length > 3 && this.isSelfCrossing(coordinates)) {
        this.$modal.msgWarning("线段交叉，请重新绘制");
        this.deletedraw();
        this.drawMap();
        // map.removeInteraction(this.draw);
      } else {
        const map = this.instance.get("map");
        map.removeInteraction(this.draw);
      }
      this.$emit("endEoordinate", this.drawMarkers);
    },
    /* 检测交叉点位方法 */
    isSelfCrossing(coordinates) {
      for (let i = 0; i < coordinates.length - 1; i++) {
        const segment1 = [coordinates[i], coordinates[i + 1]];
        for (let j = i + 2; j < coordinates.length - 1; j++) {
          const segment2 = [coordinates[j], coordinates[j + 1]];

          if (this.doSegmentsCross(segment1, segment2)) {
            return true;
          }
        }
      }
      return false;
    },
    /* 检测交叉线段 */
    doSegmentsCross(segment1, segment2) {
      const [p1, p2] = segment1;
      const [q1, q2] = segment2;

      function crossProduct(p, q) {
        return p[0] * q[1] - p[1] * q[0];
      }

      function subtractPoints(p, q) {
        return [p[0] - q[0], p[1] - q[1]];
      }

      const r = subtractPoints(p2, p1);
      const s = subtractPoints(q2, q1);
      const uNumerator = crossProduct(subtractPoints(q1, p1), r);
      const denominator = crossProduct(r, s);

      if (denominator === 0) {
        return false; // 线段平行或共线
      }

      const u = uNumerator / denominator;
      const t = crossProduct(subtractPoints(q1, p1), s) / denominator;

      // 判断是否相交并且不是共线
      return t > 0 && t < 1 && u > 0 && u < 1;
    },
    /* 删除绘制功能 */
    deletedraw() {
      const map = this.instance.get("map");
      map.removeInteraction(this.draw);
      const [layer] = map.getAllLayers().filter((item) => {
        return item.get("id") === "draw";
      });
      map.removeLayer(layer);
      this.draw = null;
      this.source = new VectorSource();
      this.drawMarkers = [];
      this.$emit("endEoordinate", []);
    },
    /* 修改地图底图样式 */
    changeImg(item) {
      if (this.mapType !== item) {
        this.mapType = item;
        this.$nextTick(() => {
          if (item === "img_c") {
            const layer = this.instance
              .get("map")
              .getAllLayers()
              .filter((itemInfo) => {
                return (
                  itemInfo.get("id") === "cva_c" ||
                  itemInfo.get("id") === "vec_c"
                );
              });
            layer.forEach((it) => {
              it.setVisible(false);
            });
            const layerDisabled = this.instance
              .get("map")
              .getAllLayers()
              .filter((itemInfo) => {
                return itemInfo.get("id") === "img_c";
              });
            layerDisabled.forEach((it) => {
              it.setVisible(true);
            });
          } else if (item === "cva_c") {
            const layer = this.instance
              .get("map")
              .getAllLayers()
              .filter((itemInfo) => {
                return (
                  itemInfo.get("id") === "cva_c" ||
                  itemInfo.get("id") === "vec_c"
                );
              });
            layer.forEach((it) => {
              it.setVisible(true);
            });
            const layerDisabled = this.instance
              .get("map")
              .getAllLayers()
              .filter((itemInfo) => {
                return itemInfo.get("id") === "img_c";
              });
            layerDisabled.forEach((it) => {
              it.setVisible(false);
            });
          }
        });
      }
    },
    /**
     * @description: 切换当前位置
     * @param {Array} position 中心点位置
     * @param {number} zoom 地图缩放等级
     * @param {number} rotation 地图旋转角度
     * @param {number} duration 地图垂直深度
     * @return
     */
    changePosition(position, zoom, rotation, duration) {
      const map = this.instance.get("map");
      if (map) {
        map.getView().animate({
          center: position, // 地图中心点位
          zoom: zoom || 12, // 地图缩放等级
          rotation: rotation || undefined, // 旋转角度
          duration: duration || 1000, // 动画加载时间
        });
      }
    },

    /**
     * @description: 清空地图图层
     * @return
     */
    clearMapFeature() {
      const map = this.instance.get("map");
      const [layer] = map.getAllLayers().filter((item) => item.get("type"));
      map.removeLayer(layer);
    },

    /**
     * @description: 调整地图缩放等级
     * @return
     */
    changeZoom(type = 1, zoomStep) {
      const map = this.instance.get("map");
      if (map) {
        const step = zoomStep || 0.5;
        // 获取当前地图缩放等级
        const currentZoom = map.getView().getZoom();
        map.getView().animate({
          zoom: currentZoom + step * type,
        });
      }
    },
  },
};
</script>

<style lang="scss" scoped>
.map-container {
  width: 100%;
  height: 100%;
  position: relative;

  #map-0 {
    width: 100%;
    height: 100%;
  }

  #map-1 {
    width: 100%;
    height: 100%;
  }
  // 放大缩小控件
  .control-container {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    position: absolute;
    left: 1rem;
    top: 1rem;

    .draw-map {
      width: 1.5rem;
      height: 3rem;
      background-color: rgb(198, 216, 216);
      display: flex;
      align-items: center;
      justify-content: center;
      flex-direction: column;

      i {
        cursor: pointer;
        padding: 0.1rem 0;
        font-size: 1.2rem;
      }

      .change-map-click {
        background-color: rgb(240, 240, 240);
      }
    }

    .change-map {
      display: flex;
      align-items: center;
      justify-content: center;
      flex-direction: column;
      width: 1.5rem;
      height: 3rem;
      background-color: rgb(198, 216, 216);

      i {
        cursor: pointer;
        padding: 0.1rem 0;
        font-size: 1.2rem;
      }

      .change-map-click {
        background-color: rgb(240, 240, 240);
      }
    }

    .map-btn {
      width: 1.5rem;
      background-color: rgb(198, 216, 216);
      height: 1.5rem;
      display: flex;
      margin: 0.1rem 0;
      align-items: center;
      justify-content: center;

      i {
        cursor: pointer;
        padding: 0.1rem 0;
        font-size: 1.2rem;
      }
    }
  }
}
</style>