美文网首页
微信小程序 - 腾讯地图使用和选点连线计算距离

微信小程序 - 腾讯地图使用和选点连线计算距离

作者: 西半球_ | 来源:发表于2022-04-25 13:56 被阅读0次

    GitHub Demo 地址: jh-weapp-demo 实现一些常用效果、封装通用组件和工具类

    最近练习了下腾讯地图的使用,包含地图展示、定位、自定义标注和气泡、点聚合、拖动地图选点、逆地址解析、地图连线计算距离,标注编辑和删除,具体可扫码或在demo查看。

    效果图

    map1.png
    map2.gif
    map3.gif
    map4.gif

    实现方式

    地图展示

    地图使用的是腾讯地图Map组件。官方文档

    逆地址解析

    逆地址解析使用的是腾讯位置服务,可以通过拿到的经纬度获取到位置的一些信息

    地图选点

    地图选点有两种实现方式
    一种是使用小程序API wx.chooseLocation
    另一种通过小程序地图选点插件

    根据经纬度计算距离

    计算距离有两种实现方式
    一种是使用腾讯位置服务的距离算法
    一种根据经纬度计算地球两个经纬度的距离(haversine公式)

    注:

    腾讯位置服务需要注册,使用时需要在后台配置,需要下载对应js库文件,有调用频率限制
    小程序插件需要在小程序后台和小程序代码配置

    经纬度计算距离

    //给定的经度1,纬度1;经度2,纬度2. 计算2个经纬度之间的距离。
    //<returns>距离 (单位:米)</returns>
    //util的方法是通过读取类文件,然后通过映射获取的,所以需要在使用的page中加入
    //var util = require('../../../utils/util.js');相当于一个导入的过程。
    function distance(p1, p2) {
      //用haversine公式计算球面两点间的距离。
      //经纬度转换成弧度
      var lat1 = p1.latitude * Math.PI / 180;
      var lon1 = p1.longitude * Math.PI / 180;
      var lat2 = p2.latitude * Math.PI / 180;
      var lon2 = p2.longitude * Math.PI / 180;
      //差值
      var vLon = Math.abs(lon1 - lon2);
      var vLat = Math.abs(lat1 - lat2);
      //h is the great circle distance in radians, great circle就是一个球体上的切面,它的圆心即是球心的一个周长最大的圆。
      var v = Math.sin(vLat / 2);
      var v1 = Math.sin(vLon / 2);
      var h = v * v + Math.cos(lat1) * Math.cos(lat2) * v1 * v1;
      // 地球半径 平均值,米
      var distance = 2 * 6371000 * Math.asin(Math.sqrt(h));
      return distance;
    }
    module.exports = {//用于映射函数
      distance: distance,
    }
    

    地图展示和逆地址解析实现代码

    wxml
    <!-- <map id="myMap" style="width: 100%; height: 70vh;" latitude="40.040415" longitude="116.273511" scale="17"></map> -->
    
    
    <view class="map-container">
      <map id="myMap" class="map" latitude="{{latitude}}" longitude="{{longitude}}" show-location="{{isShowPosition}}" show-compass="{{isShowCompass}}" show-scale="{{isShowScale}}" markers="{{markers}}" scale="{{scale}}" bindregionchange="onMapChange" bindtap="onMapTap" bindmarkertap="onMapMarkTap" bindcallouttap="onMapCalloutTap">
    
        <!-- <cover-image class="centerImg" src="./images/ic_mark2.png" /> -->
    
        <!-- 带动画 -->
        <cover-image class="centerImg {{animation ? 'locationpicker-animation' : ''}}" src="./images/ic_mark2.png" bindanimationend="onMarkerAnimationend" />
    
        <cover-view class="locationBg" catchtap='onClickLocation'>
          <cover-image class="locationIcon" src="./images/ic_location.png" />
        </cover-view>
    
      </map>
    </view>
    
    <van-cell-group title="拖动地图选中的位置">
      <van-cell title="地址描述" label="{{address}}" />
      <van-cell title="位置描述" label="{{recommend}}" />
      <van-cell title="大致位置" label="{{rough}}" />
    </van-cell-group>
    
    <view style="padding-top: 50px;"></view>
    
    js
    var QQMapWX = require('./lib/qqmap-wx-jssdk1.2/qqmap-wx-jssdk.min.js');
    var qqmapsdk;
    
    const INIT_CALLOUT = {
      content: '天安门广场',
      padding: 10,
      display: 'ALWAYS',
      anchorY: -30,
      bgColor: '#ffffff',
      anchorY: -30,
      fontSize: 14,
      textAlign: 'center',
      // borderWidth: 2,
    };
    
    const INIT_MARKER = {
      id: 2,
      latitude: 39.903734,
      longitude: 116.397845,
      iconPath: './images/Marker3_Activated@3x.png',
      width: '34px',
      height: '34px',
      rotate: 0,
      alpha: 1,
      callout: INIT_CALLOUT,
    };
    
    Page({
    
      /**
       * 页面的初始数据
       */
      data: {
        latitude: 40.040415, // 纬度
        longitude: 116.273511, // 经度
        scale: 3, //默认16
        isShowScale: true,
        isShowCompass: true,
        isShowPosition: true,
        markers: [
          // 我的位置
          // {
          //   id: 0,
          //   iconPath: "./images/ic_mark1.png",
          //   latitude: 40.040415,
          //   longitude: 116.273511,
          //   width: 30,
          //   height: 30,
          //   title: '我的位置' //点击时显示,callout存在时将被忽略
          // },
    
          // 其它
          {
            ...INIT_MARKER
          },
          {
            id: 3,
            latitude: 40.040415,
            longitude: 116.273511,
            iconPath: './images/Marker1_Activated@3x.png',
            width: 30,
            height: 30,
            callout: {
              content: '腾讯总部大楼',
              padding: 10,
              borderRadius: 5,
              display: 'ALWAYS',
            },
            // 地图标记文字
            label: {
              content: '标记文字',
              color: '#f00',
              textAlign: 'center'
            }
          }
        ],
        animation: false,
        address: '',
        recommend: '',
        rough: '',
      },
    
      /**
       * 生命周期函数--监听页面加载
       */
      onLoad: function (options) {
    
        // 实例化API核心类
        // 开发文档:https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/jsSdkOverview
        qqmapsdk = new QQMapWX({
          key: '4M5BZ-ALKLU-BVXVL-B2HCC-DJSUS-WGFBH'
        });
    
        this.mapCtx = wx.createMapContext('myMap')
        // this.mapCtx.moveToLocation()
        // this.getCurrentLocation()
        // this.getCenterLngLat()
    
        // //更换定位图标。这个图标支持网络图片。
        // this.mapCtx.setLocMarkerIcon({
        //   iconPath: "./images/ic_mark1.png",
        // })
    
      },
    
      /**
       * 生命周期函数--监听页面显示
       */
      onShow: function () {
    
      },
      // 获取当前位置
      getCurrentLocation() {
        var that = this
        const lat = "markers[0].latitude";
        const log = "markers[0].longitude";
        // wgs84 返回 gps 坐标,gcj02 返回可用于 wx.openLocation 的坐标
        wx.getLocation({
          type: 'gcj02',
          success: (res) => {
            console.log('当前位置');
            console.log(res);
            that.setData({
              latitude: res.latitude,
              longitude: res.longitude,
              [lat]: res.latitude,
              [log]: res.longitude
            });
          },
          fail: (err) => {
            // wx.showToast({
            //   title: '为了不影响您的使用,请授权定位',
            //   icon: 'none'
            // })
          }
        });
      },
      // 点击定位按钮
      onClickLocation() {
        var that = this
        this.setData({
          scale: 16,
        })
        setTimeout(() => {
          that.mapCtx.moveToLocation()
        }, 500);
      },
      // 视野发生变化时触发
      // 监听拖动地图,拖动结束根据中心点更新页面
      onMapChange(e) {
        if (e.type == 'end' && (e.causedBy == 'scale' || e.causedBy == 'drag')) {
          this.getCenterLngLat()
          this.setData({
            animation: true,
          });
        }
      },
      // 点击地图
      onMapTap(e) {
        console.log('点击地图');
        console.log(e);
        wx.showToast({
          title: '点击地图',
          icon: 'none'
        })
      },
      // 点击标记点时触发,e.detail = {markerId}
      onMapMarkTap(e) {
        console.log('点击标记点');
        console.log(e);
        console.log(e.detail.markerId);
        wx.showToast({
          title: '点击标记点',
          icon: 'none'
        })
      },
      // 点击标记点对应的气泡时触发e.detail = {markerId}
      onMapCalloutTap(e) {
        console.log('点击气泡');
        console.log(e);
        console.log(e.detail.markerId);
        wx.showToast({
          title: '点击标记点对应的气泡',
          icon: 'none'
        })
      },
      onMarkerAnimationend() {
        this.setData({
          animation: false
        });
      },
    
      // 获取地图中心点的经纬度
      getCenterLngLat: function () {
        var that = this
        this.mapCtx.getCenterLocation({
          success: function (res) {
            console.log('获取中间点');
            console.log(res);
            that.reverseGeocoder(res.latitude, res.longitude)
          }
        })
      },
      // 逆地址解析
      reverseGeocoder(latitude, longitude) {
        var that = this
        qqmapsdk.reverseGeocoder({
          location: {
            latitude: latitude,
            longitude: longitude
          },
          success: function (res) {
            console.log('逆地址解析结果');
            console.log(res)
            var result = res.result
            console.log(result)
            // 地址描述
            let address = result.address
            // 位置描述
            let formatted_addresses = result.formatted_addresses
            // 经过腾讯地图优化过的描述方式, 更具人性化特点
            let recommend = formatted_addresses.recommend
            // 大致位置, 可用于对位置的粗略描述
            let rough = formatted_addresses.rough
            console.log(address)
            console.log(recommend)
            console.log(rough)
    
            that.setData({
              address: address,
              recommend: recommend,
              rough: rough,
            })
          },
        });
      }
    })
    

    地图标点连线计算距离实现代码

    wxml
    <view class="map-container">
      <map id="myMap" class="map" latitude="{{latitude}}" longitude="{{longitude}}" show-location="{{isShowPosition}}" show-compass="{{isShowCompass}}" show-scale="{{isShowScale}}" markers="{{markers}}" scale="{{scale}}" polygons="{{polygons}}" polyline="{{polyline}}" include-points="{{includePoints}}" enable-zoom bindregionchange="onMapChange" bindtap="onMapTap" bindmarkertap="onMapMarkTap" bindcallouttap="onMapCalloutTap">
    
        <!-- <cover-image class="centerImg" src="./images/ic_mark2.png" /> -->
    
        <cover-view class="center-bg" catchtap="onClickAddMarkerBtn">
          <cover-view class="center-top">确定打标</cover-view>
          <cover-view class="center-bottom"></cover-view>
        </cover-view>
    
        <!-- 定位按钮 -->
        <cover-view class="locationBg" catchtap='onClickLocation'>
          <cover-image class="locationIcon" src="./images/ic_location.png" />
        </cover-view>
    
        <!-- 大头针中间文字 -->
        <cover-view slot="callout">
          <cover-view wx:for="{{markers}}" wx:key='index' marker-id="{{item.id}}">
            <cover-view> {{ (item.id + 1) }}</cover-view>
          </cover-view>
        </cover-view>
    
      </map>
    
    </view>
    
    js
    const util = require('./util')
    
    Page({
    
      /**
       * 页面的初始数据
       */
      data: {
        latitude: 40.040415,
        longitude: 116.273511,
        scale: 16, //默认16
        isShowScale: true,
        isShowCompass: true,
        isShowPosition: true,
        markers: [
          // 我的位置
          // {
          //   id: 0,
          //   iconPath: "./images/ic_mark1.png",
          //   latitude: 40.040415,
          //   longitude: 116.273511,
          //   width: 30,
          //   height: 30,
          //   title: '我的位置' //点击时显示,callout存在时将被忽略
          // },
          // 其它
          // {
          //   id: 3,
          //   latitude: 40.040415,
          //   longitude: 116.273511,
          //   iconPath: './images/Marker1_Activated@3x.png',
          //   width: 30,
          //   height: 30,
          //   callout: {
          //     content: '腾讯总部大楼',
          //     padding: 10,
          //     borderRadius: 5,
          //     display: 'ALWAYS',
          //   },
          //   label: {
          //     content: '标记文字',
          //     color: '#f00',
          //     textAlign: 'center'
          //   }
          // }
        ],
    
        curPoints: [],
        polyline: '', // 路线
        polygons: '', // 多边形
        includePoints: '', // 缩放视野以包含所有给定的坐标点
        isSelectMarker: false,
        selectMarkerId: '',
      },
    
      /**
       * 生命周期函数--监听页面加载
       */
      onLoad: function (options) {
    
        this.mapCtx = wx.createMapContext('myMap')
        // this.mapCtx.moveToLocation()
        // this.getCurrentLocation()
        // this.getCenterLngLat()
    
        // //更换定位图标。这个图标支持网络图片。
        // this.mapCtx.setLocMarkerIcon({
        //   iconPath: "./images/ic_mark1.png",
        // })
    
      },
    
      /**
       * 生命周期函数--监听页面显示
       */
      onShow: function () {
    
      },
      // 获取当前位置
      getCurrentLocation() {
        var that = this
        const lat = "markers[0].latitude";
        const log = "markers[0].longitude";
        // wgs84 返回 gps 坐标,gcj02 返回可用于 wx.openLocation 的坐标
        wx.getLocation({
          type: 'gcj02',
          success: (res) => {
            console.log('当前位置');
            console.log(res);
            that.setData({
              latitude: res.latitude,
              longitude: res.longitude,
              [lat]: res.latitude,
              [log]: res.longitude
            });
          },
          fail: (err) => {
            // wx.showToast({
            //   title: '为了不影响您的使用,请授权定位',
            //   icon: 'none'
            // })
          }
        });
      },
      // 点击定位按钮
      onClickLocation() {
        this.setData({
          scale: 17
        });
        setTimeout(() => {
          this.mapCtx.moveToLocation()
        }, 200);
      },
      // 视野发生变化时触发
      // 监听拖动地图,拖动结束根据中心点更新页面
      onMapChange(e) {
        if (e.type == 'end' && (e.causedBy == 'scale' || e.causedBy == 'drag')) {
          this.getCenterLngLat()
          if (this.data.isSelectMarker) {
            setTimeout(() => {
              this.moveMarker(this.data.selectMarkerId, this.data.latitude, this.data.longitude)
            }, 500);
          }
        }
      },
      // 点击地图
      onMapTap(e) {
        console.log('点击地图');
        console.log(e);
      },
      // 点击标记点时触发,e.detail = {markerId}
      onMapMarkTap(e) {
        console.log('点击标记点');
        console.log(e.detail.markerId);
        this.selectMarker(e.detail.markerId)
      },
      // 点击标记点对应的气泡时触发e.detail = {markerId}
      onMapCalloutTap(e) {
        console.log('点击气泡');
        console.log(e.detail.markerId);
        this.selectMarker(e.detail.markerId)
      },
      onClickAddMarkerBtn() {
        this.addMarker(this.data.latitude, this.data.longitude)
      },
      // 获取地图中心点的经纬度
      getCenterLngLat: function () {
        var that = this
        this.mapCtx.getCenterLocation({
          success: function (res) {
            console.log('获取中间点');
            console.log(res);
            that.setData({
              latitude: res.latitude,
              longitude: res.longitude,
            });
          }
        })
      },
      // 编辑或删除选中标记
      selectMarker(markerId) {
        let tempMarkers = this.data.markers
        tempMarkers.forEach(function (item, index) {
          item.iconPath = "./images/ic_mark1.png"
          item.isSelect = false
          if (index == markerId) {
            item.isSelect = !item.isSelect
            item.iconPath = item.isSelect ? './images/Marker1_Activated@3x.png' : "./images/ic_mark1.png"
          }
        });
        let marker = this.data.markers[markerId]
        this.setData({
          latitude: marker.latitude,
          longitude: marker.longitude,
          markers: tempMarkers,
          isSelectMarker: true,
          selectMarkerId: markerId,
        });
        var that = this
        wx.showModal({
          title: '是否删除该标记点?',
          content: '确认将删除,取消可拖动地图更新标记点位置',
          success(res) {
            if (res.confirm) {
              console.log('用户点击确定')
              that.deleteMarker(markerId)
            } else if (res.cancel) {
              console.log('用户点击取消')
            }
          }
        })
      },
      cancelMarker() {
        let tempMarkers = this.data.markers
        tempMarkers.forEach(function (item, index) {
          item.iconPath = "./images/ic_mark1.png"
          item.isSelect = false
        });
        this.setData({
          markers: tempMarkers,
          isSelectMarker: false,
          selectMarkerId: "",
        });
      },
      // 添加一个标记点
      addMarker(latitude, longitude) {
        // 判断是否存在,相同经纬度不再添加
        let isExist = false
        this.data.markers.forEach(item => {
          if (item.latitude == latitude && item.longitude == longitude) {
            console.log('已存在,相同经纬度不再添加');
            isExist = true
            return
          }
        });
        if (isExist == true) {
          return
        }
        // 不存在。新增
        var mark = new Object(); //声明一个mark对象
        mark.id = this.data.markers.length;
        mark.longitude = longitude; //经度
        mark.latitude = latitude;
        mark.iconPath = "./images/ic_mark1.png";
        mark.width = 40;
        mark.height = 40;
        // mark.label = {
        //   fontSize: 14,
        //   anchorX: 0,
        //   anchorY: -35,
        //   content: mark.id + '',
        //   textAlign: 'center',
        //   color: '#000000',
        // }
        // mark.callout = {
        //   content: '腾讯总部大楼',
        //   padding: 10,
        //   borderRadius: 5,
        //   display: 'ALWAYS',
        // }
    
        // 自定义气泡窗口
        mark.customCallout = {
          anchorX: 0,
          anchorY: 25,
          display: 'ALWAYS',
        }
        mark.isSelect = false
    
        this.data.markers.push(mark)
        this.setData({
          markers: this.data.markers
        })
    
        // 在data中声明一个curPoints 来记录点击所有的点,在完成绘制的时候清空点。
        this.data.curPoints.push({
          longitude: longitude,
          latitude: latitude
        })
        // 添加线上的超过一个的点,每次把距离叠加上去
        if (this.data.curPoints.length > 1) {
          // console.log(this.data.curPoints)
          // 地图上用的polyline是一个线集合对象,如果只放一条线是无法看见的。
          var pl = [{
            points: this.data.curPoints,
            color: "#0066FF",
            width: 2,
            dottedLine: false,
          }];
          //更改界面数据
          this.setData({
            polyline: pl
          })
        }
        // 计算距离
        this.calculateDistance()
      },
      // 编辑标记点(移动位置)
      moveMarker(markerId, latitude, longitude) {
        console.log('编辑标记点(移动位置)');
        this.mapCtx.translateMarker({
          // 要平移的marker的id
          markerId: markerId,
          // 移动过程中是否自动旋转 marker
          autoRotate: false,
          // 动画持续时长,平移与旋转分别计算
          duration: 10,
          // 平移到的目的地,参数为经纬度
          destination: {
            latitude: latitude,
            longitude: longitude,
          },
          //平移动画后的回调函数
          animationEnd() {}
        })
    
        let tempMarkers = this.data.markers
        let marker = this.data.markers[markerId]
    
        let curPoints = this.data.curPoints
        curPoints.forEach(function (item, index) {
          if (item.latitude == marker.latitude && item.longitude == marker.longitude) {
            curPoints.splice(index, 1, {
              longitude: longitude,
              latitude: latitude
            })
          }
        });
    
        tempMarkers[markerId].latitude = latitude
        tempMarkers[markerId].longitude = longitude
    
        // 地图上用的polyline是一个线集合对象,如果只放一条线是无法看见的。
        var pl = [{
          points: curPoints,
          color: "#0066FF",
          width: 2,
          dottedLine: false,
        }];
        this.setData({
          polyline: curPoints.length > 1 ? pl : '',
          curPoints: curPoints,
          markers: tempMarkers,
        })
    
        // 计算距离
        this.calculateDistance()
        // 移动结束取消选中
        this.cancelMarker()
      },
      // 删除一个标记点
      deleteMarker(markerId) {
        let tempMarkers = this.data.markers
        let delMarker = tempMarkers[markerId]
        // console.log(delMarker);
        if (tempMarkers.length > markerId) {
          // 删除
          tempMarkers.splice(markerId, 1)
          // 重新排序,设置顺序
          tempMarkers.forEach(function (item, index) {
            item.id = index
          });
          let curPoints = this.data.curPoints
          curPoints.forEach(function (item, index) {
            if (item.latitude == delMarker.latitude && item.longitude == delMarker.longitude) {
              curPoints.splice(index, 1)
            }
          });
          // 地图上用的polyline是一个线集合对象,如果只放一条线是无法看见的。
          var pl = [{
            points: curPoints,
            color: "#0066FF",
            width: 2,
            dottedLine: false,
          }];
          this.setData({
            polyline: curPoints.length > 1 ? pl : '',
            curPoints: curPoints,
            markers: tempMarkers
          })
          // 计算距离
          this.calculateDistance()
        }
    
      },
      // 每次更新markers之后重新计算距离
      calculateDistance() {
        // this.calculateEachDistance()
        this.calculateAllDistance()
      },
      // 计算两个点之间的距离,个数大于1每个点下都显示距离,不显示总距离
      calculateEachDistance() {
        let curPoints = this.data.curPoints
        if (curPoints.length > 1) {
          var p2 = curPoints[curPoints.length - 1]
          var p1 = curPoints[curPoints.length - 2]
          // console.log(p1);
          // console.log(p2);
          let dis = 0
          dis += util.distance(p1, p2);
          let datas = Number(dis); //转为字符串
          let datas2 = datas.toFixed(2) + "米"; //保留两位小数
          var x = -(datas2.length * 1) //设置文字向左偏移
          let label = {
            fontSize: 14,
            anchorX: x,
            anchorY: 0,
            content: datas2,
            textAlign: 'center',
            color: '#000000',
          }
          let last = "markers[" + (this.data.markers.length - 1) + "].label";
          this.setData({
            [last]: label
          })
        } else if (curPoints.length == 1) {
          let label = {
            fontSize: 14,
            anchorX: x,
            anchorY: 0,
            content: '',
            textAlign: 'center',
            color: '#000000',
          }
          this.setData({
            ['markers[0].label']: label
          })
        }
      },
      // 计算所有点之间的总距离,按添加顺序依次计算两点之间距离,并在最后一个点上显示总距离
      calculateAllDistance() {
        // 添加线上的超过一个的点,每次把距离叠加上去
        let curPoints = this.data.curPoints
        if (curPoints.length > 1) {
          let dis = 0
          curPoints.forEach(function (item, index) {
            if (index < (curPoints.length - 1)) {
              var p1 = curPoints[index]
              var p2 = curPoints[index + 1]
              dis += util.distance(p1, p2);
            }
          });
          let datas = Number(dis); //转为字符串
          let datas2 = datas.toFixed(2) + "米"; //保留两位小数
          var x = -(datas2.length * 1) //设置文字向左偏移
          let label = {
            fontSize: 14,
            anchorX: x,
            anchorY: 0,
            content: datas2,
            textAlign: 'center',
            color: '#000000',
          }
          let last2 = "markers[" + (this.data.markers.length - 2) + "].label";
          let last = "markers[" + (this.data.markers.length - 1) + "].label";
          this.setData({
            [last2]: {},
            [last]: label
          })
        } else if (curPoints.length == 1) {
          let label = {
            fontSize: 14,
            anchorX: x,
            anchorY: 0,
            content: '',
            textAlign: 'center',
            color: '#000000',
          }
          this.setData({
            ['markers[0].label']: label
          })
        }
      }
    
    })
    

    相关文章

      网友评论

          本文标题:微信小程序 - 腾讯地图使用和选点连线计算距离

          本文链接:https://www.haomeiwen.com/subject/voytertx.html