【天地图】绘制、删除点线面

发布于:2025-02-16 ⋅ 阅读:(33) ⋅ 点赞:(0)

实现效果图

在这里插入图片描述

地图组件完整代码


// 天地图组件
<template>
  <div class="map-container">
    <div id="mapCon"></div>
  </div>
</template>

<script>
import markerIcon from "@/assets/images/marker-icon.png";
let handler;
export default {
  name: "MapEle",
  props: {
    mapData: {
      type: Object,
      default: () => {
        return {
          pointData: [],
          lineData: {},
          rangeData: {},
        };
      },
    },
  },
  data() {
    return {
      map: null,
      zoom: 14,
      longitude: 117.119529,
      latitude: 36.650396,
    };
  },
  watch: {
    // 只关注mapData数据源即可,监听mapData数据源改变清空地图,重新打点\线\面
    mapData: {
      handler(newV, oldV) {
        this.map.clearOverLays();
        this.drawMap();
      },
      deep: true,
    },
  },
  methods: {
    initMap() {
      this.map = new T.Map("mapCon");
      this.map.centerAndZoom(
        new T.LngLat(this.longitude, this.latitude),
        this.zoom
      );
      this.drawMap();
    },
    drawMap() {
      this.drawPoint();
      this.drawLine();
      this.drawRange();
    },
    // 绘制点
    drawPoint() {
      this.mapData?.pointData?.forEach((item) => {
        var marker = new T.Marker(new T.LngLat(item[0], item[1]));
        this.map.addOverLay(marker);
        var label = new T.Label({
          text: item[2],
          position: new T.LngLat(item[0], item[1]),
          offset: new T.Point(0, 0),
          style: {
            fontSize: "14px",
            color: "#000",
            backgroundColor: "rgba(255, 255, 255, 0.8)",
            padding: "5px",
            borderRadius: "5px",
            border: "1px solid #ccc",
          },
        });
        this.map.addOverLay(label);
      });
    },
    // 绘制线
    drawLine() {
      if (this.mapData.lineData) {
        let coordinates;
        if (
          this.mapData.lineData.length > 0 &&
          Array.isArray(this.mapData.lineData[0])
        ) {
          coordinates = this.mapData.lineData[0].map(
            (coord) => new T.LngLat(coord[0], coord[1])
          );
        } else {
          coordinates = this.mapData.lineData.map(
            (coord) => new T.LngLat(coord[0], coord[1])
          );
        }

        if (coordinates.length > 1) {
          const polyline = new T.Polyline(coordinates, {
            color: "#ff0000",
            weight: 3,
          });
          this.map.addOverLay(polyline);
          this.map.panTo(coordinates[0]);
        }
      }
    },
    // 绘制面
    drawRange() {
      if (this.mapData.rangeData) {
        let coordinates;
        if (
          this.mapData.rangeData.length > 0 &&
          Array.isArray(this.mapData.rangeData[0])
        ) {
          coordinates = this.mapData.rangeData[0].map(
            (coord) => new T.LngLat(coord[0], coord[1])
          );
        } else {
          coordinates = this.mapData.rangeData.map(
            (coord) => new T.LngLat(coord[0], coord[1])
          );
        }

        if (coordinates.length > 2) {
          var polygon = new T.Polygon(coordinates, {
            color: "green",
            weight: 3,
            opacity: 0.5,
            fillColor: "red",
            fillOpacity: 0.5,
            strokeStyle: "solid",
          });

          this.map.addOverLay(polygon);

          if (coordinates.length > 0) {
            const center = this.getPolygonCenter(coordinates);
            // this.map.panTo(center);
          }
        }
      }
    },
    // 打点
    async handlePoint(i) {
      if (handler) handler.close(); // 如果已有打点工具打开,先关闭
      handler = new T.MarkTool(this.map, {
        follow: true,
        icon: new T.Icon({
          iconUrl: markerIcon,
          iconSize: new T.Point(25, 41),
        }),
      });
      handler.open(); // 打开打点工具

      handler.addEventListener("mouseup", async (e) => {
        try {
          // 获取打点位置的名称
          const locationName = await this.getLocationName(
            e.currentLnglat.lng,
            e.currentLnglat.lat
          );
          // 通知父组件打点完成
          this.$emit(
            "finishPoint",
            [e.currentLnglat.lng, e.currentLnglat.lat, locationName],
            i
          );
        } catch (error) {
          console.error("获取位置名称失败:", error);
        } finally {
          handler.close(); // 关闭打点工具
        }
      });
    },
    // 画线
    handleLine() {
      if (this.handler) this.handler.close(); // 如果已有绘线工具打开,先关闭
      this.handler = new T.PolylineTool(this.map, {
        color: "#ff0000", // 线的颜色
        weight: 3, // 线的宽度
      });
      this.handler.open(); // 打开绘线工具

      this.handler.addEventListener("draw", (e) => {
        // 将绘制的线的坐标转换为二维数组
        const lineCoordinates = e.currentLnglats.map((item) => [
          item.lng,
          item.lat,
        ]);
        // 发射事件,通知父组件绘线完成
        this.$emit("finishLine", lineCoordinates);
      });
    },
    // 画范围
    handleRange() {
      if (this.handler) this.handler.close(); // 如果已有绘图工具打开,先关闭
      this.handler = new T.PolygonTool(this.map, {
        color: "#ff0000", // 多边形边框颜色
        weight: 3, // 多边形边框宽度
      });
      this.handler.open(); // 打开绘图工具

      this.handler.addEventListener("draw", (e) => {
        // 将绘制的多边形的坐标转换为二维数组
        const polygonCoordinates = e.currentLnglats.map((item) => [
          item.lng,
          item.lat,
        ]);
        // 通知父组件绘制完成
        this.$emit("finishRange", polygonCoordinates);
      });
    },
    // 计算多边形的中心点
    getPolygonCenter(coordinates) {
      let sumLng = 0;
      let sumLat = 0;
      coordinates.forEach((coord) => {
        sumLng += coord.lng;
        sumLat += coord.lat;
      });
      const centerLng = sumLng / coordinates.length;
      const centerLat = sumLat / coordinates.length;
      return new T.LngLat(centerLng, centerLat);
    },
    // 根据经纬度获取当前地名称
    getLocationName(lng, lat) {
      const geocoder = new T.Geocoder();
      return new Promise((resolve, reject) => {
        geocoder.getLocation(new T.LngLat(lng, lat), (result) => {
          if (result.getStatus() === 0) {
            const address = result.getAddress();
            resolve(address); // address即为当前点名称
          } else {
            reject(result.getMsg());
          }
        });
      });
    },
  },
  mounted() {
    this.initMap();
  },
};
</script>

<style scoped lang="scss">
.map-container {
  position: relative;
  #mapCon {
    height: 70vh;
  }
}
</style>

使用地图组件完整代码

// 编辑地图信息弹窗
<template>
  <div class="container-box">
    <el-dialog
      v-if="visible"
      :title="title"
      :visible.sync="visible"
      width="90vw"
      custom-class="dialog-class"
    >
      <el-row :gutter="20">
        <el-col :span="12">
          <MapEle
            ref="mapEleRef"
            :mapData="mapData"
            @finishPoint="finishPoint"
            @finishLine="finishLine"
            @finishRange="finishRange"
          />
        </el-col>
        <el-col :span="12">
          <el-form ref="form" :model="form" :rules="rules" label-width="140px">
            <el-row :gutter="20">
              <el-col :span="24">
                <el-form-item label="名称" prop="name">
                  <el-input
                    v-model="form.name"
                    placeholder="请输入名称"
                  /> </el-form-item
              ></el-col>
              <el-col :span="24">
                <el-form-item label="起点位置" prop="startLocation">
                  <el-input
                    v-model="form.startLocation"
                    placeholder="请选择起点位置"
                    readonly
                    disabled
                  >
                    <template slot="append">
                      <el-button
                        icon="el-icon-location-outline"
                        @click="drawPoint(0)"
                      ></el-button>
                      <el-divider
                        direction="vertical"
                        class="divider-class"
                      ></el-divider>
                      <el-button
                        icon="el-icon-delete"
                        @click="delPoint(0)"
                      ></el-button> </template
                  ></el-input> </el-form-item
              ></el-col>
              <el-col :span="24">
                <el-form-item label="终点位置" prop="endLocation">
                  <el-input
                    v-model="form.endLocation"
                    placeholder="请选择终点位置"
                    readonly
                    disabled
                  >
                    <template slot="append">
                      <el-button
                        icon="el-icon-location-outline"
                        @click="drawPoint(1)"
                      ></el-button>
                      <el-divider
                        direction="vertical"
                        class="divider-class"
                      ></el-divider>
                      <el-button
                        icon="el-icon-delete"
                        @click="delPoint(1)"
                      ></el-button>
                    </template> </el-input></el-form-item
              ></el-col>
              <el-col :span="24">
                <el-form-item label="线" prop="pipelayer">
                  <el-input
                    v-model="form.pipelayer"
                    disabled
                    placeholder="请绘制线"
                  >
                    <template slot="append">
                      <el-button
                        icon="el-icon-edit-outline"
                        @click="drawLine"
                      ></el-button>
                      <el-divider
                        direction="vertical"
                        class="divider-class"
                      ></el-divider>
                      <el-button
                        icon="el-icon-delete"
                        @click="delLine"
                      ></el-button>
                    </template> </el-input></el-form-item
              ></el-col>
              <el-col :span="24">
                <el-form-item label="区域" prop="piperange">
                  <el-input
                    v-model="form.piperange"
                    disabled
                    placeholder="请绘制区域"
                    ><template slot="append">
                      <el-button
                        icon="el-icon-edit-outline"
                        @click="drawRange"
                      ></el-button>
                      <el-divider
                        direction="vertical"
                        class="divider-class"
                      ></el-divider>
                      <el-button
                        icon="el-icon-delete"
                        @click="delRange"
                      ></el-button>
                    </template>
                  </el-input> </el-form-item
              ></el-col>
              <el-col :span="24">
                <el-form-item label="高度差" prop="altitude">
                  <el-input
                    v-model="form.altitude"
                    placeholder="请输入高度差"
                  /> </el-form-item
              ></el-col>
              <el-col :span="24">
                <el-form-item label="面积(㎡)" prop="heatArea">
                  <el-input
                    v-model="form.heatArea"
                    placeholder="请输入面积"
                  /> </el-form-item
              ></el-col>
            </el-row>
          </el-form>
        </el-col>
      </el-row>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" v-if="!enterprise" @click="submitForm"
          >确 定</el-button
        >
        <el-button @click="cancel">取 消</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { cloneDeep as _cloneDeep } from "lodash";
import { updateApi, getGateway } from "@/api/basicData/mainPipeNetwork.js";
import MapEle from "@/components/MapEle";
export default {
  components: { MapEle },
  data() {
    return {
      title: "",
      visible: false,
      defaultForm: {
        name: undefined,
        startLocation: undefined,
        endLocation: undefined,
        startLongitude: undefined,
        startLatitude: undefined,
        endLongitude: undefined,
        endLatitude: undefined,
        altitude: undefined,
        heatArea: undefined,
        pipelayer: undefined,
        piperange: undefined,
      },
      rules: {
        name: [
          { required: true, message: "主网名称不能为空", trigger: "blur" },
        ],
        startLocation: [
          { required: true, message: "起点位置不能为空", trigger: "blur" },
        ],
        endLocation: [
          { required: true, message: "终点位置不能为空", trigger: "blur" },
        ],
        pipelayer: [{ required: true, message: "线不能为空", trigger: "blur" }],
        piperange: [
          { required: true, message: "区域不能为空", trigger: "blur" },
        ],
        altitude: [
          { required: true, message: "高度差不能为空", trigger: "blur" },
        ],
        heatArea: [
          { required: true, message: "面积不能为空", trigger: "blur" },
        ],
      },
      form: {},
      enterprise: false, // 企业标识
      mapVisible: false,
      mapFlag: 1,
      mapData: {
        pointData: [],
        lineData: {
          coordinates: [],
        },
        rangeData: {
          coordinates: [],
        },
      },
    };
  },
  created() {
    // 判斷當前用戶是否有企业标识 qiye
    if (
      this.$store.state.user?.roles.length &&
      this.$store.state.user?.roles.indexOf("qiye") >= 0
    ) {
      this.enterprise = true;
    }
  },
  methods: {
    // 绘制点,这里的i标识起点(0)和终点(1)(根据个人情况使用)
    drawPoint(i) {
      // 绘制当前点时先将之前点清空,再绘制
      this.delPoint(i);
      this.$nextTick(() => {
        this.$refs.mapEleRef.handlePoint(i);
      });
    },
    // 绘制线
    drawLine() {
      this.delLine();
      this.$nextTick(() => {
        this.$refs.mapEleRef.handleLine();
      });
    },
    // 绘制面
    drawRange() {
      this.delRange();
      this.$nextTick(() => {
        this.$refs.mapEleRef.handleRange();
      });
    },
    // 删除点
    delPoint(i) {
      // 获取要删除的点的经纬度
      const { longitude, latitude } =
        i === 1
          ? {
              longitude: this.form.endLongitude,
              latitude: this.form.endLatitude,
            }
          : {
              longitude: this.form.startLongitude,
              latitude: this.form.startLatitude,
            };

      // 从 mapData.pointData 中移除对应的点
      this.mapData.pointData = this.mapData.pointData.filter((item) => {
        return !(item[0] === longitude && item[1] === latitude);
      });

      // 清空表单中的位置信息
      if (i === 1) {
        this.form.endLocation = "";
        this.form.endLongitude = "";
        this.form.endLatitude = "";
      } else {
        this.form.startLocation = "";
        this.form.startLongitude = "";
        this.form.startLatitude = "";
      }
    },
    // 删除线
    delLine() {
      this.form.pipelayer = "";
      this.$forceUpdate();
      this.mapData.lineData = [];
    },
    // 删除面
    delRange() {
      this.form.piperange = "";
      this.$forceUpdate();
      this.mapData.rangeData = [];
    },
    // 绘制完点后触发的方法
    finishPoint(arr, i) {
      // 将点的坐标和名称保存到 mapData.pointData 中
      this.mapData.pointData.push(arr);

      // 根据索引 i 更新表单中的起点或终点信息
      const updateForm = (location, longitude, latitude) => {
        this.form[location] = arr[2];
        this.form[longitude] = arr[0];
        this.form[latitude] = arr[1];
      };

      if (i === 1) {
        updateForm("endLocation", "endLongitude", "endLatitude");
      } else {
        updateForm("startLocation", "startLongitude", "startLatitude");
      }
    },
    // 绘制完线后触发的方法
    finishLine(arr) {
      this.mapData.lineData = [arr];
      this.form.pipelayer = JSON.stringify(arr);
      this.$forceUpdate();
    },
    // 绘制完面后触发的方法
    finishRange(arr) {
      this.mapData.rangeData = [arr];
      this.form.piperange = JSON.stringify(arr);
      this.$forceUpdate();
    },
    // 打开编辑页面
    async open(row) {
      try {
        // 获取地图数据
        const res = await this.getMapData(row);
        const tempData = JSON.parse(res?.data);

        // 初始化地图数据
        this.mapData = {
          pointData: [
            [row.startLongitude, row.startLatitude, row.startLocation],
            [row.endLongitude, row.endLatitude, row.endLocation],
          ],
          lineData: tempData?.features?.[0]?.pipelayer?.coordinates,
          rangeData: tempData?.features?.[0]?.piperange?.coordinates,
        };

        // 初始化表单数据
        this.form = _cloneDeep(this.defaultForm);
        this.form = { ...row };
        this.form.pipelayer = JSON.stringify(
          tempData?.features?.[0]?.pipelayer?.coordinates[0] || []
        );
        this.form.piperange = JSON.stringify(
          tempData?.features?.[0]?.piperange?.coordinates[0] || []
        );

        // 设置对话框标题和可见性
        this.title = "修改";
        this.visible = true;
      } catch (error) {
        console.error("打开对话框时出错:", error);
      }
    },
    // 获取地图线和区域数据
    getMapData(row) {
      return new Promise((resolve, reject) => {
        getGateway({ bh: row.code })
          .then((response) => {
            resolve(response);
          })
          .catch((error) => {
            reject(error);
          });
      });
    },
    // 关闭弹窗页`
    cancel() {
      this.visible = false;
    },
    // 提交
    submitForm() {
      this.$refs["form"].validate((valid) => {
        if (valid) {
          // 构建提交参数
          const params = {
            ...this.form,
            layer: {
              pipeLayerVar: this.form.pipelayer,
              pipeRangeVar: `[${this.form.piperange}]`,
            },
          };

          // 调用更新接口
          updateApi(params)
            .then((response) => {
              this.$modal.msgSuccess("修改成功");
              this.cancel();
              this.$emit("refreshList");
            })
            .catch((error) => {
              this.$modal.msgError("修改失败:" + error.message);
            });
        } else {
          this.$modal.msgWarning("表单验证失败,请检查输入内容");
        }
      });
    },
  },
};
</script>

<style scoped lang="scss">
.container-box {
  ::v-deep .el-dialog.dialog-class {
    height: auto;
    height: 90vh;
    overflow-y: auto;
  }
  ::v-deep .el-dialog__body {
    height: 75vh !important;
  }
}

.divider-class {
  margin: 0 20px;
}
</style>

文档:http://lbs.tianditu.gov.cn/api/js4.0/examples.html


网站公告

今日签到

点亮在社区的每一天
去签到