基于Google Map的路线规划

3,500 阅读7分钟

本文将主要介绍一下关于google map的常用API,最后通过常用API实现一个自定义行程路线的小例子。

一、调用地图

1. 获取API密钥

要使用Google Map API,必须具有API密钥,具体信息请查看官网

2. 初始化地图

2.1 引入地图

通过script标签引入

<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"></script>

通过jsonp的方式调用地图api

jsonp('https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY', () => {
    initMap()
})

不过呢,官网提供的API地址对于国内用户会存在梯子的问题,所幸,还有两个不需要梯子的API网址:

 1. http://maps.google.cn/maps/api/js?key=${apiKey}
 2. http://ditu.google.cn/maps/api/js?key=${apiKey}
2.2 创建地图
// 坐标点
let position = { lat: -25.344, lng: 131.036 }
// 创建地图
let map = new google.maps.Map(
    // 绑定到指定DOM节点
    el,
    {
        zoom: 7, 
        center: position
    })
2.3 配置参数

google.maps.Map的第二个参数即为地图的配置参数,常用参数如下:

{
    // 地图缩放比例
    zoom,
    // 地图中心点位置
    center,
    // 是否显示街景控制按钮
    streetViewControl,
    // 常用控件位置
    mapTypeControlOptions: {
      position: google.maps.ControlPosition.TOP_RIGHT
    },
    zoomControlOptions: {
      position: google.maps.ControlPosition.RIGHT_TOP
    },
    fullscreenControl: {
      position: google.maps.ControlPosition.RIGHT_TOP
    },
    ...
}

既然可以初始化配置参数,那,又该如何动态修改配置参数呢?对于zoomcenter,我们可以通过直接调用相应的API进行修改:setZoomsetCenter。那对于其他参数可以通过setOptions进行修改。控件的具体详情请查看官网

this.map.setOptions({
    zoom: 9
    streetViewControl: false,
    ...
})

二、标记

标记可识别地图上的位置。默认情况下,标记使用标准图像。标记可以显示自定义图像,在这种情况下,它们通常称为“图标”。标记和图标是类型的对象 Marker。您可以在标记的构造函数中设置自定义图标,也可以通过调用标记来设置自定义图标setIcon()。

1. 添加标记

通过google.maps.Marker构造函数生成指定的标记:

let marker = new google.maps.Marker({
    // 标记位置
    position: LatLng,
    // 附加到指定地图,未指定时,不会添加到任何地方
    map: map,
    // 工具提示
    title: 'ABC'
})

// 添加到指定地图
marker.setMap(map)

2. 删除标记

从地图上删除指定标记,同样需要调用setMap方法:

marker.setMap(null)

不过此操作只是把指定地图上的标记删除了,依旧可以通过setMap(map)添加到指定地图中,若想真正删除当前标记,需设置标记为null

marker.setMap(null)
marker = null

3. 自定义标记

如果只能显示默认的标记那肯定就不好玩了,那接下来我们可以通过icon设置指定的自定义图标:

let marker = new google.maps.Marker({
    position: LanLng,
    map: map,
    // 自定义图像的url
    icon: imageURL
})

如果说只是图像的url路径,肯定还是不过瘾的,接下来可以进行更加复杂的设置:

let marker = new google.maps.Marker({
    position: LanLng,
    map: map,
    icon: {
        url: 'icon路径',
        // icon图片大小
        size: new google.maps.Size(30, 46),
        // 从icon图片起点坐标进行截取
        origin: new google.maps.Point(0, 0),
        // 偏移当前坐标点(LanLng)的位置大小
        anchor: new google.maps.Point(0, 46)
    }
})

4. 修改标记

通过setIcon进行标记的动态修改:

marker.setIcon({
    url,
    size,
    ...
})

5. 完全自定义标记

具体可以通过下面的demo展示。

标记的详情请查看官网API

三、形状

形状是地图上的一个对象,与纬度/经度坐标相关联。可以使用以下形状: 线,多边形, 圆形和矩形。您还可以配置形状,以便用户可以编辑或拖动它们。

接下来,我们主要介绍一下折线的API,其他形状详情请查看官网

1. 添加折线

通过 google.maps.Polyline 构造函数生成指定的折线,该函数常用参数如下:

  • strokeColor 指定格式的十六进制HTML颜色"#FFFFFF",不支持命名的颜色。
  • strokeOpacity 取值范围:[0.0, 1.0],1.0以确定线条颜色的不透明度。默认值为1.0。
  • strokeWeight 线条的宽度(以像素为单位)。
// 1. 初始化折线
let polyline = new google.maps.Polyline({
    strokeColor: '#FF0000',
    strokeOpacity: 0.8,
    strokeWeight: 5
})

// 2. 以现有坐标点生成折线
let latLng = [
    {lat: 37.772, lng: -122.214},
    {lat: 21.291, lng: -157.821}
]

let polyline = new google.maps.Polyline({
    path: latLng,
    strokeColor: '#FF0000',
    strokeOpacity: 0.8,
    strokeWeight: 5
})

polyline.setMap(map)

2. 删除折线

折线的删除方式和上述标记的删除方式一样,通过setMap进行添加和删除,同样也只是在地图中移除折线,只有将折线本身设置为null才是真正的删除。

// 从地图中移除
polyline.setMap(null)
// 删除折线本身
polyline = null

3. 添加标记到折线中

想要在折线中添加标记,那我们不得不介绍一下getPath(),其返回type数组MVCArray。可以通过一下方法操作折线变化:

  • getAt(index) 返回索引值index对应的LatLng对象(index从0开始)。
  • insertAt(index) 在索引值index处插入一个Latlng对象(即插入标记),其后面标记点向后依次移动(类似数组splice操作)。
  • removeAt(index) 删除索引值index处的Latlng对象。
// 在当前折线尾部添加一个坐标点
let path = poly.getPath()

path.push(latLng)

4. 将标记在折线中删除

// 获取删除项在折线的索引值index
let path = poly.getPath()

path.removeAt(index)

四、地图小例子(实现自定义行程路线规划)

下面,我们将实现一个小的例子:路线规划。

1. 生成城市标记

首先,将城市添加到地图中,生成标记。

// 定义存储标记的表
const ALL_MARKERS = new Map()
1.1 定义生成标记的方法
// 定义生成标记的方法
const addLatLngToMap = (lat, lng) => {
    // 生成坐标点
    let latLng = new google.maps.LatLng(lat, lng)

    let marker = new google.maps.Marker({
        position: latLng,
        map: this.map,
        icon: {
            url: '默认图像',
            // icon图片大小
            size: new google.maps.Size(30, 42),
            // 从图片起点坐标进行截取
            origin: new google.maps.Point(0, 0)
        }
    })
    // 添加到表中
    ALL_MARKERS.set('城市名称', marker)
}
1.2 为生成标记绑定事件
// 绑定事件
const addClickEvengtToMarker = (marker) => {
    let listener = marker.addListener('click', e => {
        ...
        // 移除事件
        google.maps.event.removeListener(listener)
    })
}

有时候,我们可能想要通过自动触发标记的绑定事件,那我们可以通过trigger方法来实现:

google.maps.event.trigger(marker, 'click')

看到这里,大家可能会想:一般地图上的标记不仅显示一个图像,同时还可以显示当前城市名称,那这个功能,我们就可以用上述提到的完全自定义标记来实现。

1.3 完全自定义标记

首先官网给我们提供了一个简单的例子,通过自定义弹出层OverlayView来实现,根据官网要求,在用户自定义弹出层时,必须实现以下三个方法:onAdd()draw()onRemove()。优化代码如下:

// 自定义弹出层
const createPopupClass = () => {
  /*
  * A customized popup on the map.
  * @param {!google.maps.LatLng} position
  * @param {!Element} content The bubble div.
  * @constructor
  * @extends {google.maps.OverlayView}
  */
  class Popup extends google.maps.OverlayView {
    constructor(position, content) {
      super()
      // 坐标点位置
      this.position = position
      // 自定义弹出层内容
      this.containerDiv = content
      // 若想阻止地图中缩放,点击等事件冒泡到到弹出层上的,可以通过`preventMapHitsAndGesturesFrom`进行阻止
      google.maps.OverlayView.preventMapHitsAndGesturesFrom(this.containerDiv)
    }
  
    onAdd () {
      // 将弹出层添加到 MapPanes 界面
      this.getPanes().floatPane.appendChild(this.containerDiv)
    }
  
    onRemove () {
      // setMap(null) 时,清除当前弹出层
      if (this.containerDiv.parentElement) {
        this.containerDiv.parentElement.removeChild(this.containerDiv)
      }
    }
  
    draw () {
      // 地图缩放时,动态计算弹出层位置
      let divPosition = this.getProjection().fromLatLngToDivPixel(this.position)
  
      // Hide the popup when it is far out of view.
      let display =
          Math.abs(divPosition.x) < 4000 && Math.abs(divPosition.y) < 4000 ?
          'block' :
          'none'
  
      if (display === 'block') {
        this.containerDiv.style.left = divPosition.x + 'px'
        this.containerDiv.style.top = divPosition.y + 'px'
      }
      if (this.containerDiv.style.display !== display) {
        this.containerDiv.style.display = display
      }
    }
  }

  return Popup
}

使用自定义弹出层类,同时要注意,在创建弹出层时,一定为其增加position: absolute,否则弹出层位置会出现偏差。

const addPopupToMap = (lat, lng) => {
    let Popup = createPopupClass()
    // 1. 创建弹出层
    let content = document.createElement('div')
    content.className = 'customize-map__popup-container'
    content.innerHTML = name
    
    let popup = new Popup(
      new google.maps.LatLng(lat, lng),
      content
    )

    popup.setMap(map)
}
.customize-map__popup-container{
    cursor: grab;
    position: absolute;
    background-color: white;
    padding: 2px 3px;
    border-radius: 4px;
    min-width: 30px;
    max-width: 200px;
    text-align: center;
    border: 1px solid rgb(153, 153, 153);
    user-select: none;
}

2. 将指定城市添加到路线中

通过Polyline生成指定路线,同时点击城市标记进行添加。

2.1 初始化路线
let poly = new google.maps.Polyline({
    strokeColor: 'rgb(251, 109, 72)',
    strokeOpacity: 0.8,
    strokeWeight: 5
})

直接点击想要添加的城市标记,便可以把当前城市添加到路线(即添加到地图折线中),同时,为了把添加和为添加的城市做区分,可以通过setIcon进行修改标记图像。

3. 从路线中删除指定城市

若想删除城市,我们可以遍历获取当前城市在路线中的位置,然后通过removeAt进行删除。

this.poly.getPath().removeAt(index)