基于uni-app+vue3实现的微信小程序地图范围限制与单点标记功能实现指南

一、功能概述

本文将分步骤讲解如何使用uni-app框架在微信小程序中实现以下功能:

  1. 显示基础地图

  2. 绘制特定区域范围(以郑州市为例)

  3. 实现点击地图添加标记点

  4. 限制标记点只能在指定区域内添加

  5. 显示选中位置的坐标信息

    二、分步骤实现

    步骤1:搭建基础地图

    1.1 添加map组件

    在页面template中添加map组件:

    html 复制代码
    <template>
      <view class="container">
        <map
          id="map"
          style="width: 100%; height: 80vh"
          :latitude="center.latitude"
          :longitude="center.longitude"
          :show-location="true"
          :enable-zoom="true"
          :enable-scroll="true"
          @tap="handleMapTap"
        ></map>
      </view>
    </template>

    1.2 设置地图中心点

    在script部分设置地图初始中心点(xxx位置):

    javascript 复制代码
    <script setup>
    import { ref } from 'vue'
    
    const center = ref({
      latitude: 34.747,   // 纬度
      longitude: 113.625  // 经度
    })
    </script>

    步骤2:绘制郑州市范围

    2.1 定义郑州市边界坐标

    javascript 复制代码
    const zhengzhouPolygon = [
      {latitude: 34.936, longitude: 112.842}, // 西北角
      {latitude: 34.936, longitude: 114.023}, // 东北角
      {latitude: 34.524, longitude: 114.023}, // 东南角
      {latitude: 34.524, longitude: 112.842}, // 西南角
      {latitude: 34.936, longitude: 112.842}  // 闭合多边形
    ]

    2.2 添加polygons属性到map组件 绘制限制范围

    javascript 复制代码
    const polygons = ref([{
      points: zhengzhouPolygon,
      strokeWidth: 2,
      strokeColor: "#1E90FF",
      fillColor: "#1E90FF22"
    }])

    更新map组件:

    html 复制代码
    <map
      ...
      :polygons="polygons"
    ></map>

    步骤3:实现点击添加标记功能

    3.1 初始化markers和选中点

    javascript 复制代码
    const markers = ref([])
    const selectedPoint = ref(null)
    const isInZhengzhou = ref(false)

    3.2 实现点击事件处理

    javascript 复制代码
    const handleMapTap = (e) => {
      const { latitude, longitude } = e.detail
      selectedPoint.value = { latitude, longitude }
      
      // 判断是否在郑州范围内
      isInZhengzhou.value = isPointInPolygon(
        {latitude, longitude}, 
        zhengzhouPolygon
      )
      
      if (isInZhengzhou.value) {
        // 在范围内,添加标记
        markers.value = [{
          id: 1,
          latitude,
          longitude,
          iconPath: '/static/location.png', // 替换为你的标记图标路径
          width: 30,
          height: 30,
          title: "选择的位置"
        }]
      } else {
        // 不在范围内,清除标记并提示
        markers.value = []
        uni.showToast({
          title: "请选择郑州市范围内的位置",
          icon: "none",
          duration: 2000
        })
      }
    }

    步骤4:实现点在多边形内判断(射线法)

    javascript 复制代码
    function isPointInPolygon(point, polygon) {
      const x = point.longitude, y = point.latitude
      let inside = false
      for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
        const xi = polygon[i].longitude, yi = polygon[i].latitude
        const xj = polygon[j].longitude, yj = polygon[j].latitude
        
        const intersect = ((yi > y) !== (yj > y)) &&
          (x < (xj - xi) * (y - yi) / (yj - yi) + xi)
        if (intersect) inside = !inside
      }
      return inside
    }

    步骤5:添加信息显示区域

    html 复制代码
    <view class="info-box" v-if="selectedPoint">
      <text>已选位置:</text>
      <text>纬度: {{selectedPoint.latitude.toFixed(6)}}</text>
      <text>经度: {{selectedPoint.longitude.toFixed(6)}}</text>
      <text v-if="!isInZhengzhou" class="error">当前位置不在郑州范围内!</text>
    </view>

    添加样式:

    css 复制代码
    <style scoped>
    .container {
      padding: 20rpx;
    }
    
    .info-box {
      margin-top: 20rpx;
      padding: 20rpx;
      background-color: #f5f5f5;
      border-radius: 10rpx;
    }
    
    .info-box text {
      display: block;
      margin: 10rpx 0;
      font-size: 28rpx;
    }
    
    .error {
      color: #ff0000;
      font-weight: bold;
    }
    </style>

    三、完整代码

    javascript 复制代码
    <template>
      <view class="container">
        <map
          id="map"
          style="width: 100%; height: 80vh"
          :latitude="center.latitude"
          :longitude="center.longitude"
          :polygons="polygons"
          :markers="markers"
          @tap="handleMapTap"
          :show-location="true"
          :enable-zoom="true"
          :enable-scroll="true"
        ></map>
        
        <view class="info-box" v-if="selectedPoint">
          <text>已选位置:</text>
          <text>纬度: {{selectedPoint.latitude.toFixed(6)}}</text>
          <text>经度: {{selectedPoint.longitude.toFixed(6)}}</text>
          <text v-if="!isInZhengzhou" class="error">当前位置不在郑州范围内!</text>
        </view>
      </view>
    </template>
    
    <script setup>
    import { ref } from 'vue'
    
    // 郑州市边界坐标
    const zhengzhouPolygon = [
      {latitude: 34.936, longitude: 112.842},
      {latitude: 34.936, longitude: 114.023},
      {latitude: 34.524, longitude: 114.023},
      {latitude: 34.524, longitude: 112.842},
      {latitude: 34.936, longitude: 112.842}
    ]
    
    // 地图中心点(郑州二七塔)
    const center = ref({
      latitude: 34.747,
      longitude: 113.625
    })
    
    // 多边形配置
    const polygons = ref([{
      points: zhengzhouPolygon,
      strokeWidth: 2,
      strokeColor: "#1E90FF",
      fillColor: "#1E90FF22"
    }])
    
    // 标记点
    const markers = ref([])
    const selectedPoint = ref(null)
    const isInZhengzhou = ref(false)
    
    // 判断点是否在多边形内
    function isPointInPolygon(point, polygon) {
      const x = point.longitude, y = point.latitude
      let inside = false
      for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
        const xi = polygon[i].longitude, yi = polygon[i].latitude
        const xj = polygon[j].longitude, yj = polygon[j].latitude
        
        const intersect = ((yi > y) !== (yj > y)) &&
          (x < (xj - xi) * (y - yi) / (yj - yi) + xi)
        if (intersect) inside = !inside
      }
      return inside
    }
    
    // 处理地图点击
    const handleMapTap = (e) => {
      const { latitude, longitude } = e.detail
      selectedPoint.value = { latitude, longitude }
      
      isInZhengzhou.value = isPointInPolygon(
        {latitude, longitude}, 
        zhengzhouPolygon
      )
      
      if (isInZhengzhou.value) {
        markers.value = [{
          id: 1,
          latitude,
          longitude,
          iconPath: '/static/location.png',
          width: 30,
          height: 30,
          title: "选择的位置"
        }]
      } else {
        markers.value = []
        uni.showToast({
          title: "请选择郑州市范围内的位置",
          icon: "none",
          duration: 2000
        })
      }
    }
    </script>
    
    <style scoped>
    .container {
      padding: 20rpx;
    }
    
    .info-box {
      margin-top: 20rpx;
      padding: 20rpx;
      background-color: #f5f5f5;
      border-radius: 10rpx;
    }
    
    .info-box text {
      display: block;
      margin: 10rpx 0;
      font-size: 28rpx;
    }
    
    .error {
      color: #ff0000;
      font-weight: bold;
    }
    </style>

    通过以上步骤,我们完整实现了一个限制区域范围的单点标记功能。开发者可以根据实际需求调整区域范围或扩展更多功能。。。ps:markers中的iconpath 如果不传 会展示系统默认的标记点,如果要根据经纬度获取地名则需要申请对接地图的接口才能实现

    四、实现效果