vue3结合高德地图实现圆、矩形、多边形的绘制和编辑

效果图
效果图
location-info.vue (使用map的vue文件)

<template>
  <BaseItem title="地理位置信息">
    <Row :gutter="[8, 8]">
      <Col :span="12">
        <FormItem prop="address" :rules="rule" label="地理位置">
          <Input v-model="currentInfo.address" disabled placeholder="请在地图上绘制区域">
            <template #iconAfter>
              <PositionFill class="icon-after" />
            </template>
          </Input>
        </FormItem>
      </Col>
    </Row>
    <Map @change="handleChange" ref="gridMapRef" @mapComplete="mapComplete" />
  </BaseItem>
</template>
<script setup>
import Map from "./map.vue";
......
</script>

map.vue

<template>
  <div id="locationMapContainer"></div>
  <div class="search-address">
    <Search
      @blur="locationDetailBlur"
      @keydown.enter="locationDetailBlur"
      :inputStyle="{ width: '394px' }"
      size="large"
      placeholder="搜索地图"
      v-model="searchAddress"
    />
  </div>
  <div class="draw-operation">
    <div>区域绘制</div>
    <div class="draw-icon-box">
      <svg-icon
        :iconClass="`icon_${key}_${stateSelected[key] ? 'usable' : 'disabled'}`"
        :disabled="!stateSelected[key] && areaFlag"
        size="32"
        v-for="key in GRID_DRAW_AREA_KEYS"
        :key="key"
        @click="handleDraw(key)"
      />
    </div>
    <div class="draw-btn-box" v-show="areaFlag">
      <Button primary shape="round" @click="handeEditDraw">{{
        isEdit ? "完成" : "编辑"
      }}</ori-button>
      <Button type="outline" shape="round" @click="handleClear">清除</ori-button>
    </div>
  </div>
</template>
<script setup>
import {
  onMounted,
  defineProps,
  createVNode,
  render,
  watch,
  onBeforeUnmount,
  defineExpose,
  defineEmits,
  nextTick,
  ref,
  reactive
} from "vue";
import AMapLoader from "@amap/amap-jsapi-loader";
import { useRouter } from "vue-router";
const router = useRouter();

const props = defineProps({
  point: {
    type: Array
  }
});
const BASE_OPTS = {
  strokeColor: "#FF33FF",
  strokeWeight: 6,
  strokeOpacity: 0.2,
  fillColor: "#1791fc",
  fillOpacity: 0.4,
  strokeStyle: "solid"
};
let marker = null;
let map = null;
let geocoder = null;
let instance = null;
let mouseTool = null;
let drawObj = {};
const searchAddress = ref("");
const areaFlag = ref(false);
const stateDraw = reactive({
  address: "",
  drawPoint: {
    type: "",
    points: [],
    radius: 0
  },
  centerPoint: null,
  area: null
});
const emits = defineEmits(["change", "mapComplete"]);

//区域绘制图形key
const GRID_DRAW_AREA_KEYS = ["square", "circle", "polygon"];

const stateSelected = reactive({
  square: false,
  circle: false,
  polygon: false
});

const isEdit = ref(false);

const initMap = () => {
  AMapLoader.load({
    key: "高德秘钥", 
    version: "2.0",
    plugins: [
      "AMap.Geocoder",
      "AMap.MouseTool",
      "AMap.PolygonEditor", //多边形编辑器
      "AMap.RectangleEditor", //矩形编辑器
      "AMap.CircleEditor", //圆编辑器
      "AMap.GeometryUtil" //空间数据计算的函数库,计算点线面的距离、长度、面积
    ]
  }).then((AMap) => {
    instance = AMap;
    map = new AMap.Map("locationMapContainer", {
      center: props.point,
      zoom: 16
    });
    // 初始化标记点
    if (props.point[0] != 0 && props.point[1] != 0) {
      getAddress(props.point);
    } else {
      map.setCenter([106.553882, 29.561466]);
    }
    initGeocoder();
    initMouseTool();
    map.on("complete", function () {
      // 地图加载完成后触发
      emits("mapComplete");
    });
  });
};

const initGeocoder = () => {
  geocoder = new instance.Geocoder({
    // city 指定进行编码查询的城市,支持传入城市名、adcode 和 citycode
    // city: "010"
  });
};

const initMouseTool = () => {
  mouseTool = new instance.MouseTool(map);
  mouseTool.on("draw", (event) => {
    // event.obj 为绘制出来的覆盖物对象
    console.log(event);
    areaFlag.value = true;
    isEdit.value = false;
    drawObj = event;
    // 绘制完成后保存多边形实例
    geometrical = drawObj.obj;
    mouseTool.close();

    //获取区域绘制的坐标点、计算中心点、计算面积
    getPointsAndCenterAndArea();
  });
};

const locationDetailBlur = async () => {
  await nextTick();
  getLocation(searchAddress.value);
};

// 获取地理坐标信息
const getLocation = (str) => {
  if (marker) map.remove(marker);

  geocoder?.getLocation(str, (status, { info, geocodes }) => {
    if (status === "complete" && info === "OK") {
      // result中对应详细地理坐标信息
      const { lng, lat } = geocodes[0]?.location;
      stateDraw.centerPoint = [lng, lat];
      addMark(stateDraw.centerPoint);
    }
  });
};

//0:初始化/地图标点 1:中心点获取地址
const getAddress = async (point, type = 0) => {
  await geocoder?.getAddress(point, (status, { info, regeocode }) => {
    if (status === "complete" && info === "OK") {
      // result为对应的地理位置详细信息
      const { province, city, district } = regeocode.addressComponent;
      const detail = regeocode.formattedAddress.replace(province + city + district, "");
      const obj = {
        formattedAddress: regeocode.formattedAddress,
        location: [province, city || province, district],
        detail,
        point
      };
      if (type) {
        stateDraw.address = obj.formattedAddress;
        emits("change", { ...stateDraw });
      }
    }
  });
};

// 添加标记点
const addMark = (point) => {
  marker = new instance.Marker({
    position: point
  });
  map.clearMap();
  map.add(marker); // 添加到地图
  map.setCenter(point);
};

// 绘制矩形
const drawRectangle = (params = {}) => {
  mouseTool.rectangle({ ...BASE_OPTS, ...params });
};
//绘制圆
const drawCircle = (params = {}) => {
  mouseTool.circle({ ...BASE_OPTS, ...params });
};
//绘制多边形
const drawPolygon = (params = {}) => {
  mouseTool.polygon({ ...BASE_OPTS, ...params });
};

/**
 * 获取路径点、中心点坐标、计算面积
 */
const getPointsAndCenterAndArea = async () => {
  // 如果geometrical不存在,则直接返回
  if (!geometrical) return;
  // 如果绘制类型为多边形或者矩形,且存在路径点
  if (["polygon", "square"].includes(stateDraw.drawPoint.type) && geometrical.getPath()) {
    // 获取路径点数组
    const points = geometrical.getPath()?.map((point) => [point.lng, point.lat]);
    // 将路径点数组赋值给绘图状态的drawPoint属性的points属性
    stateDraw.drawPoint.points = points;
    // 初始化纬度总和和经度总和
    let sumLat = 0;
    let sumLng = 0;

    // 遍历路径点数组
    for (const point of points) {
      // 累加纬度总和
      sumLat += point[1];
      // 累加经度总和
      sumLng += point[0];
    }
    // 计算平均纬度和平均经度
    const avgLat = sumLat / points.length;
    const avgLng = sumLng / points.length;
    // 将平均经纬度坐标赋值给绘图状态的centerPoint属性
    stateDraw.centerPoint = [avgLng, avgLat];
    // 使用高德地图提供的GeometryUtil工具计算面积,保留两位数
    stateDraw.area = instance.GeometryUtil.ringArea(points)?.toFixed(2) || null;

    getAddress(stateDraw.centerPoint, 1);
  } else if (stateDraw.drawPoint.type === "circle") {
    // 圆形变化后,可以直接从geometrical获取圆心坐标
    let center = geometrical.getCenter();
    stateDraw.centerPoint = [center.lng, center.lat];
    stateDraw.drawPoint.radius = geometrical.getRadius();
    // 计算圆形面积(单位是平方米),保留两位数
    stateDraw.area = (Math.PI * Math.pow(stateDraw.drawPoint.radius, 2))?.toFixed(2) || null;

    getAddress(stateDraw.centerPoint, 1);
  }
};

const handleClear = () => {
  areaFlag.value = false;
  stateSelected.square = false;
  stateSelected.circle = false;
  stateSelected.polygon = false;
  mouseTool.close(true);
  geometricalEditor && geometricalEditor.close();
  resetDraw();
};

const resetDraw = () => {
  stateDraw.address = "";
  stateDraw.drawPoint.points = [];
  stateDraw.drawPoint.type = "";
  stateDraw.drawPoint.radius = 0;
  stateDraw.centerPoint = null;
  stateDraw.area = null;
  emits("change", { ...stateDraw });
};

const handleDraw = (key) => {
  if (areaFlag.value) return;
  mouseTool.close(true);
  stateDraw.drawPoint.type = key;
  GRID_DRAW_AREA_KEYS.map((p) => (stateSelected[p] = p === key ? true : false));
  //绘制矩形
  if (key === "square") {
    drawRectangle();
  } else if (key === "circle") {
    //绘制圆
    drawCircle();
  } else {
    //绘制多边形
    drawPolygon();
  }
};

let geometrical = null;
let geometricalEditor = null;
const handeEditDraw = () => {
  isEdit.value = !isEdit.value;
  if (isEdit.value && drawObj.type === "draw") {
    if (stateSelected.square) {
      // 初始化矩阵编辑器
      geometricalEditor = new instance.RectangleEditor(map, geometrical);
    } else if (stateSelected.circle) {
      // 初始化圆编辑器
      geometricalEditor = new instance.CircleEditor(map, geometrical);
    } else {
      // 初始化多边形编辑器
      geometricalEditor = new instance.PolygonEditor(map, geometrical);
    }
    // 开启编辑模式
    geometricalEditor.open();
  }
  if (!isEdit.value && geometricalEditor) {
    // 关闭编辑模式
    geometricalEditor.close();
    //获取区域绘制的坐标点、计算中心点、计算面积
    getPointsAndCenterAndArea();

    // // 地图缩放到多边形范围
    // map.setCenter(point);
  }
};

//详情反显区域
const drawDetailGeometrical = (draw, center) => {
  let drawPoint = JSON.parse(draw || "{}");
  if (!Object.keys(drawPoint)?.length) return;
  if (["square", "polygon"].includes(drawPoint.type)) {
    //矩形和多边形绘制
    const polygon = new instance.Polygon({ path: drawPoint.points, ...BASE_OPTS });
    map.add(polygon);
    // 缩放地图到合适的视野级别
    drawPoint.type === "square" ? map.setFitView([polygon]) : map.setCenter(center);
  } else if (drawPoint.type === "circle") {
    //圆形绘制
    const circle = new instance.Circle({
      center,
      radius: drawPoint.radius,
      ...BASE_OPTS
    });
    map.add(circle);
    // 缩放地图到合适的视野级别
    map.setFitView([circle]);
  }
};

defineExpose({
  drawDetailGeometrical
});

watch(
  () => props.point,
  (newVal) => {
    if (map) {
      map.setCenter(newVal);
    }
  }
);

onMounted(() => {
  initMap();
});
onBeforeUnmount(() => {
  if (map) map.destroy();
});
</script>
<style lang="less" scoped>
#locationMapContainer {
  width: 100%;
  height: 423px;
  margin: 0 12px 16px;
}
.search-address {
  position: absolute;
  top: 88px;
  left: 32px;
  :deep {
    .input-inner {
      background: #fff;
      border-radius: 8px;
      box-shadow: 0 2px 4px 0 rgba(29, 33, 41, 0.28), 0 -1px 0 0 rgba(0, 0, 0, 0.02);
    }
    .ori-input.large .input-inner,
    .ori-input.large .ori-input__icon {
      height: 48px;
      line-height: 40px;
    }
    .ori-input.large .input-inner {
      padding-left: 52px;
    }
    .ori-input .ori-icon {
      font-size: 24px;
    }
    .ori-input .ori-input__inner .ori-input__icon.ori-input__icon-before {
      left: 16px;
    }
  }
}
.draw-operation {
  position: absolute;
  right: 28px;
  bottom: 32px;
  width: 160px;
  padding: 16px 16px 12px;
  font-weight: 500;
  font-size: 12px;
  line-height: 20px;
  background: #fcfcfc;
  border-radius: 8px;
  box-shadow: 0 0 16px -3px rgba(0, 0, 0, 0.5);
  .draw-icon-box {
    margin-top: 4px;
    .svg-icon {
      margin-right: 4px;
      cursor: pointer;
      &:hover {
        background: #e8edff;
        border-radius: 50%;
      }
      &[disabled="true"] {
        cursor: not-allowed;
        &:hover {
          background: transparent;
        }
      }
    }
  }
  .draw-btn-box {
    margin-top: 11px;
    .ori-button:first-child {
      margin-right: 6px;
    }
  }
}
</style>

iconClass对应的图标:
矩阵选中、禁用图标:
icon_square_usable.svg

<svg t="1709778697403" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1992" width="200" height="200"><path d="M512 512m-512 0a512 512 0 1 0 1024 0 512 512 0 1 0-1024 0Z" fill="#E8EDFF" p-id="1993"></path><path d="M256 256h512v512H256z" fill="#CBD4FC" p-id="1994"></path></svg>

icon_square_disabled.svg

<svg t="1709778726235" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2139" width="200" height="200"><path d="M512 518.516364m-496.484848 0a496.484848 496.484848 0 1 0 992.969696 0 496.484848 496.484848 0 1 0-992.969696 0Z" fill="#FFFFFF" fill-opacity="0" p-id="2140"></path><path d="M263.757576 270.273939h496.484848v496.484849H263.757576z" fill="#F2F3F5" p-id="2141"></path></svg>

圆选中、禁用图标:
icon_circle_usable.svg

<svg t="1709778742996" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2284" width="200" height="200"><path d="M512 512m-512 0a512 512 0 1 0 1024 0 512 512 0 1 0-1024 0Z" fill="#E8EDFF" p-id="2285"></path><path d="M224 224m288 0l0 0q288 0 288 288l0 0q0 288-288 288l0 0q-288 0-288-288l0 0q0-288 288-288Z" fill="#CBD4FC" p-id="2286"></path></svg>

icon_circle_disabled.svg

<svg t="1709778755766" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2433" width="200" height="200"><path d="M232.727273 239.243636m279.272727 0l0 0q279.272727 0 279.272727 279.272728l0 0q0 279.272727-279.272727 279.272727l0 0q-279.272727 0-279.272727-279.272727l0 0q0-279.272727 279.272727-279.272728Z" fill="#F2F3F5" p-id="2434"></path></svg>

多边形选中、禁用图标:
icon_polygon_usable.svg

<svg t="1709778777761" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2579" width="200" height="200"><path d="M512 512m-512 0a512 512 0 1 0 1024 0 512 512 0 1 0-1024 0Z" fill="#E8EDFF" p-id="2580"></path><path d="M670.016 241.664L826.08 512l-157.024 272H405.44L244.512 314.752l425.504-73.088z" fill="#CBD4FC" p-id="2581"></path></svg>

icon_polygon_disabled.svg

<svg t="1709778790882" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2726" width="200" height="200"><path d="M512 518.516364m-496.484848 0a496.484848 496.484848 0 1 0 992.969696 0 496.484848 496.484848 0 1 0-992.969696 0Z" fill="#FFFFFF" fill-opacity="0" p-id="2727"></path><path d="M665.227636 256.372364L816.562424 518.516364l-152.265697 263.757575H408.669091L252.617697 327.245576l412.609939-70.873212z" fill="#F2F3F5" p-id="2728"></path></svg>
  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue是一款流行的JavaScript框架,而高德API是一组非常强大的地图API。将二者结合起来使用,可以实现许多有趣的地图功能,比如地图矩形编辑半径搜索等。 想要实现地图矩形编辑半径搜索,需要先引入高德地图API,并在Vue组件进行相关的配置和操作。 首先,需要在Vue组件引入高德地图API,并在对应的生命周期方法初始化地图。可以通过在mounted方法引入高德地图JSAPI,并在初始化方法配置地图相关的选项来完成地图的初始化。 接下来,需要在地图添加形或矩形图元来实现编辑。可以通过调用高德地图API的circle和rectangle方法来添加形和矩形图元,同时可以绑定相关的事件监听器来实现交互操作。例如,可以添加鼠标点击事件监听器,在点击图元时弹出相应的信息窗口。 为了实现半径搜索,需要在地图添加相应的搜索功能。可以利用高德地图JSAPI的search方法来进行搜索操作,并将搜索结果展示在地图上。例如,在搜索框输入关键词后,可以调用高德地图API的search方法进行搜索,并将搜索结果展示在地图上,同时高亮显示形或矩形图元内的相关地点。 总之,利用Vue高德地图API可以很方便地实现地图矩形编辑半径搜索等有趣的功能。只需要在Vue组件进行相应的配置和操作即可。同时,还可以结合其他工具和技术来进一步实现更复杂的地图应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值