使用天地图绘制、删除点线面
- 实现效果图
- 地图组件完整代码
- 使用地图组件完整代码
实现效果图
地图组件完整代码
// 天地图组件
<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-dialogv-if="visible":title="title":visible.sync="visible"width="90vw"custom-class="dialog-class"><el-row :gutter="20"><el-col :span="12"><MapEleref="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-inputv-model="form.name"placeholder="请输入名称"/> </el-form-item></el-col><el-col :span="24"><el-form-item label="起点位置" prop="startLocation"><el-inputv-model="form.startLocation"placeholder="请选择起点位置"readonlydisabled><template slot="append"><el-buttonicon="el-icon-location-outline"@click="drawPoint(0)"></el-button><el-dividerdirection="vertical"class="divider-class"></el-divider><el-buttonicon="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-inputv-model="form.endLocation"placeholder="请选择终点位置"readonlydisabled><template slot="append"><el-buttonicon="el-icon-location-outline"@click="drawPoint(1)"></el-button><el-dividerdirection="vertical"class="divider-class"></el-divider><el-buttonicon="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-inputv-model="form.pipelayer"disabledplaceholder="请绘制线"><template slot="append"><el-buttonicon="el-icon-edit-outline"@click="drawLine"></el-button><el-dividerdirection="vertical"class="divider-class"></el-divider><el-buttonicon="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-inputv-model="form.piperange"disabledplaceholder="请绘制区域"><template slot="append"><el-buttonicon="el-icon-edit-outline"@click="drawRange"></el-button><el-dividerdirection="vertical"class="divider-class"></el-divider><el-buttonicon="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-inputv-model="form.altitude"placeholder="请输入高度差"/> </el-form-item></el-col><el-col :span="24"><el-form-item label="面积(㎡)" prop="heatArea"><el-inputv-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() {// 判斷當前用戶是否有企业标识 qiyeif (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