vue中使用高德地图实践

1,191 阅读2分钟

在高德控制台创建项目

创建项目,填写key名称,选择服务平台,完成后会生成key和安全秘钥jscode(安全秘钥使用说明)

安全秘钥使用方式:

1. JSAPI key搭配代理服务器并携带安全密钥转发(安全)

引入地图 JSAPI 脚本之前增加代理服务器设置脚本标签,设置代理服务器域名或地址,将下面示例代码中的「您的代理服务器域名或地址」替换为您的代理服务器域名或ip地址,其中_AMapService为代理请求固定前缀,不可省略或修改。(注意您这个设置必须是在  JSAPI 的脚本加载之前进行设置,否则设置无效。)

<script type="text/javascript"> 
    window._AMapSecurityConfig = { 
        serviceHost:'您的代理服务器域名或地址/_AMapService', 
        // 例如 :serviceHost:'http://1.1.1.1:80/_AMapService'
    }
</script> 
        
<script type="text/javascript" src="https://webapi.amap.com/maps?v=1.4.15&key=您申请的key值"></script>

代理服务器的设置: 以 Nginx 反向代理为例,参考以下三个location配置,进行反向代理设置,分别对应自定义地图、海外地图、Web服务,其中自定义地图和海外地图如果没有使用到相关功能也可以不设置。需要将以下配置中的“您的安全密钥”六个字替换为您刚刚获取的jscode安全密钥。如果您使用了多个key,需要在代理设置中根据 key来映射不同的安全密钥。

server { 
    listen 80; #nginx端口设置,可按实际端口修改 
    server_name 127.0.0.1; #nginx server_name 对应进行配置,可按实际添加或修改
    
    # 自定义地图服务代理
    location /_AMapService/v4/map/styles { 
        set $args "$args&jscode=您的安全密钥"; 
        proxy_pass https://webapi.amap.com/v4/map/styles;
    }
    
    # 海外地图服务代理 
    location /_AMapService/v3/vectormap { 
        set $args "$args&jscode=您的安全密钥"; 
        proxy_pass https://fmap01.amap.com/v3/vectormap; 
    }
    
    # Web服务API 代理 
    location /_AMapService/ { 
        set $args "$args&jscode=您的安全密钥"; 
        proxy_pass https://restapi.amap.com/; 
    }
}
2. JSAPI key搭配静态安全密钥以明文设置(不安全,建议开发环境用):

引入地图 JSAPI 脚本之前增加设置 JSAPI 安全密钥的脚本标签,并将您的安全密钥「您申请的安全密钥」替换为您的安全密钥;(注意这个设置必须是在  JSAPI 的脚本加载之前进行设置,否则设置无效。)

<script type="text/javascript">
    window._AMapSecurityConfig = {
        securityJsCode:'您申请的安全密钥'
    }
</script> 

<script type="text/javascript" src="https://webapi.amap.com/maps?v=1.4.15&key=您申请的key值"></script>

初始化地图

定义地图容器

<template>
    <div id="map-container"></div>
</template>    
    
<style scoped lang="less">
    #map-container {
      padding: 0;
      margin: 0;
      width: 100%;
      height: 600px;
    }
</style>

定义地图配置信息

//store/mapOption.js
export default {
  namespaced: true,
  state: {
    securityJsCode: '安全秘钥',//安全秘钥
    option: {
      key: '您的key',  //设置您的key
      version: "2.0",
      plugins: ['AMap.ToolBar', 'AMap.Driving', 'AMap.Geocoder','AMap.PolygonEditor'],//需要加载的插件
      AMapUI: {
        version: "1.1",
        plugins: [],
      },
      Loca: {
        version: "2.0"//版本
      },
    }
  }
}

使用AMapLoader来初始化渲染地图组件

//初始化地图
initAMap() {
  AMapLoader.load(this.$store.state.mapOption.option)
    .then((AMap) => {
      this.map = new AMap.Map("map-container", {
        viewMode: "3D",
        zoom: 14,
        zooms: [2, 22],
        mapStyle: 'amap://styles/darkblue',// 地图底色
        center: this.center//地图中心点
      });
    })
}

项目中比较常见的实践案例

图层

  1. 标准图层
  2. 卫星图层
  3. 路网图层
  4. 实时交通图层
  5. 楼块图层
  6. 室内图层
 //使用卫星图层举例
 const satellite = new AMap.TileLayer.Satellite();
 //添加图层
 map.addLayer(satellite);
 //移除图层
 map.removeLayer(satellite);
 //展示图层
 satellite.show();
 //移除
 satellite.hide();

覆盖物

  1. 点标记
//渲染标记点
let marker = null
marker = new AMap.Marker({
  position: [111.749180,40.842585],
  offset: new AMap.Pixel(0, 0)
})
 
// 自定义点标记内容
const markerContent = document.createElement("div");

// 点标记中的文本
const markerSpan = document.createElement("span");
markerSpan.className = 'marker';
markerSpan.style.backgroundColor = '#1791fc'
markerSpan.innerHTML = '点标记'
markerContent.appendChild(markerSpan);

marker.setContent(markerContent); //更新点标记内容
marker.setMap(map);
  1. 矢量图形

    (1)折线覆盖物 Polyline

    (2)多边形覆盖物 Polygon

    (3)圆形覆盖物 Circle

    (4)矩形覆盖物 Rectangle

    (5)椭圆形覆盖物 Ellipse

//多边形覆盖物 Polygon举例
const path = [
    [111.749180,40.842585],
    [110.749180,40.842585],
    [110.749180,41.842585],
    [111.749180,41.842585],
]
const polygon = new AMap.Polygon({
  path,
  strokeColor: '#1791fc',
  strokeWeight: 3,
  strokeOpacity: 1,
  fillOpacity: 0.4,
  fillColor: '#1791fc',
  zIndex: 50,
  bubble: true,
})

//将已有图层渲染到地图上
this.map.add([polygon]);
//自适应缩放
this.map.setFitView()

事件

//监听鼠标点击事件
this.map.on('click', (e) => {
    const {lng, lat} = e.lnglat
    //当前鼠标点击的经纬度
    const position = [lng, lat]

    //如果已经存在marker,更新数据
    if (this.marker) {
      this.marker.setPosition(position)
      return
    }
    
    //初始化一个marker
    this.marker = new AMap.Marker({
      icon: "//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png",
      position,
      offset: new AMap.Pixel(-13, -30)
    })

    this.marker.setMap(this.map);
})

几何计算

// 创建几何图层数据
export const getPolygonData = lnglatList => {
  return {
    type: "FeatureCollection",
    features: [
      {
        type: "Feature",
        id: 1043,
        properties: {"_draw_type": "fill"},
        geometry: {
          type: "Polygon",
          coordinates: [
            lnglatList
          ]
        },
      }
    ]
  }
}


//传入坐标数组计算两点之间距离
export const calculateDistance = lnglatList => {
  const pointStart = new AMap.LngLat(lnglatList[0][0], lnglatList[0][1])
  const pointEnd = new AMap.LngLat(lnglatList[1][0], lnglatList[1][1])

  const distance = pointStart.distance(pointEnd).toFixed(2) || 0
  return `标点距离:${distance} m`
}

//传入坐标数组计算封闭区域面积
export const calculateArea = lnglatList => {
  const area = AMap.GeometryUtil.ringArea(lnglatList).toFixed(2) || 0
  return `标点区域面积:${area} ㎡`
}

export default {
  getPolygonData,
  calculateDistance,
  calculateArea
}

逆向地理编码方法

const geocoder = new AMap.Geocoder({
  city: "呼和浩特市", //城市设为北京,默认:“全国”
});


geocoder.getLocation(address, (status, result) => {
  if (status === 'complete' && result.geocodes.length) {
    const lnglat = result.geocodes[0].location
    console.log('根据地址转换经纬度:',lnglat);
    
    this.marker.setPosition(lnglat);
    this.map.add(this.marker);
    this.map.setFitView(this.marker);
  } else {
    console.log('根据地址查询位置失败');
  }
});

多边形编辑器

<template>
  <div style="position: relative">
    <div id="map-draw"></div>
    <div class="input-card" style="width: 160px">
      <button class="btn" @click="setCenterMarker" style="margin-bottom: 5px">开始标记中心点</button>
      <button class="btn" @click="stopMarker" style="margin-bottom: 5px">结束标记中心点</button>
      <button class="btn" @click="startDraw" style="margin-bottom: 5px" v-if="canDraw">开始标注图层</button>
      <button class="btn" @click="stopDraw" style="margin-bottom: 5px">结束标注图层</button>
    </div>
  </div>
</template>

<script>
import AMapLoader from '@amap/amap-jsapi-loader';

export default {
  name: "MapDraw",
  data() {
    return {
      map: null,
      center: [111.748814, 40.842127],
      polygon: null,
      canDraw: true,
      currentPolygon: null,
      polygonList: [],
    }
  },
 
  mounted() {
    this.initAMap();
  },
  methods: {
    //结束绘制
    stopDraw() {
      if (this.polygon) {
        this.polygon.close()

        //保存当前标注图层数据
        const params = {}
        this.$https.fetchPut('服务端地址', params)
          .then(data => {
               //TODO 成功回调逻辑
          })
          .catch(err => {
            console.log(err);
          });
      }
    },

    //开始绘制
    startDraw() {
      if (!this.canDraw) {
        return
      }
      this.canDraw = false
      this.polygon.setTarget();
      this.polygon.open();
    },

    //获取平级网格
    getGridList() {
      this.$https.fetchGet('服务端地址', {})
        .then(res => {
          if (res.data.code === 200) {
            res.data.data
              .filter(gird => gird.calloutData)
              .forEach(grid => {
                const polygon = new AMap.Polygon({
                  path: JSON.parse(grid.calloutData),
                  strokeColor: grid.gridColor || '#1791fc',
                  strokeWeight: 6,
                  strokeOpacity: 0.2,
                  fillOpacity: 0.4,
                  fillColor: grid.gridColor || '#1791fc',
                  zIndex: 50,
                  bubble: true,
                })

                if (Number(grid.gridId) !== Number(this.gridId)) {
                  this.polygonList.push(polygon)
                } else {
                  this.currentPolygon = polygon
                  this.canDraw = false

                  this.marker = marker
                  // 吸附
                  this.polygon.addAdsorbPolygons([polygon])
                  //将已有图层渲染到地图上
                  this.map.add([polygon]);

                  //绑定双击事件
                  this.currentPolygon.on('dblclick', () => {
                    this.polygon.setTarget(this.currentPolygon);
                    this.polygon.open();
                    this.map.setFitView()
                  })
                }
              })

            if (this.polygonList.length === 0) {
              return
            }
            // 吸附
            this.polygon.addAdsorbPolygons(this.polygonList)
            //将已有图层渲染到地图上
            this.map.add(this.polygonList);
            //自适应缩放
            this.map.setFitView()
          }
        })
        .catch(err => {
          console.log(err);
        });
    },

    //初始化地图
    initAMap() {
      AMapLoader.load(this.$store.state.mapOption.option)
        .then((AMap) => {
          this.map = new AMap.Map("map-draw", {
            viewMode: "3D",
            zoom: 14,
            zooms: [2, 22],
            center: this.center,
          });

          //初始化一个多边形编辑器
          this.polygon = new AMap.PolygonEditor(this.map);

          //调用close之后触发该事件,target即为编辑后的覆盖物对象
          this.polygon.on('end', (e) => {
            this.currentPolygon = e.target._opts.path

            // 双击当前图层可再次编辑
            e.target.on('dblclick', () => {
              this.polygon.setTarget(e.target);
              this.polygon.open();
            })
          })

          // 获取平级网格列表
          this.getGridList()
        })
    }
  }
}
</script>

<style scoped lang="less">
#map-draw {
  padding: 0;
  margin: 0;
  width: 100%;
  height: 600px;
}

.input-card {
  display: flex;
  flex-direction: column;
  min-width: 0;
  word-wrap: break-word;
  background-color: #fff;
  background-clip: border-box;
  border-radius: .25rem;
  width: 25rem;
  border-width: 0;
  border-radius: 0.4rem;
  box-shadow: 0 2px 6px 0 rgba(114, 124, 245, .5);
  position: absolute;
  bottom: 1rem;
  right: 1rem;
  -ms-flex: 1 1 auto;
  flex: 1 1 auto;
  padding: 0.75rem 1.25rem;
}

.btn {
  display: inline-block;
  font-weight: 400;
  text-align: center;
  white-space: nowrap;
  vertical-align: middle;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  border: 1px solid transparent;
  transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out;
  background-color: transparent;
  background-image: none;
  color: #25A5F7;
  border-color: #25A5F7;
  padding: .25rem .5rem;
  line-height: 1.5;
  border-radius: 1rem;
  -webkit-appearance: button;
  cursor: pointer;
}

.btn:hover {
  color: #fff;
  background-color: #25A5F7;
  border-color: #25A5F7
}

.btn:hover {
  text-decoration: none
}
</style>