矢量地图项

请选择平台: Android iOS JavaScript

查看示例

Maps JavaScript API 提供了两种不同的地图实现方式:光栅和矢量。光栅地图会将地图加载为基于像素的光栅图片图块网格,这些图块由 Google Maps Platform 服务器端生成,然后提供给您的 Web 应用。矢量地图由基于矢量的图块组成,这些图块在加载时使用 WebGL 在客户端绘制,WebGL 是一种 Web 技术,可让浏览器访问用户设备上的 GPU 以渲染 2D 和 3D 图形。

用户熟悉的 Google 地图就是矢量地图,与默认的光栅图块地图相比优势众多,最值得注意的是基于矢量的图像的锐度,以及在较高缩放级别添加了 3D 建筑。矢量地图支持以下功能:

矢量地图使用入门

倾斜和旋转

您可以在初始化矢量地图时加入 headingtilt 属性,并在地图上调用 setTiltsetHeading 方法,从而设置地图的倾斜度和旋转度(朝向)。以下示例将向地图添加一些按钮,这些按钮能够以程序化方式按 20 度的增量调整倾斜度和朝向。

TypeScript

function initMap(): void {
  const map = new google.maps.Map(
    document.getElementById("map") as HTMLElement,
    {
      center: {
        lat: 37.7893719,
        lng: -122.3942,
      },
      zoom: 16,
      heading: 320,
      tilt: 47.5,
      mapId: "90f87356969d889c",
    }
  );

  const buttons: [string, string, number, google.maps.ControlPosition][] = [
    ["Rotate Left", "rotate", 20, google.maps.ControlPosition.LEFT_CENTER],
    ["Rotate Right", "rotate", -20, google.maps.ControlPosition.RIGHT_CENTER],
    ["Tilt Down", "tilt", 20, google.maps.ControlPosition.TOP_CENTER],
    ["Tilt Up", "tilt", -20, google.maps.ControlPosition.BOTTOM_CENTER],
  ];

  buttons.forEach(([text, mode, amount, position]) => {
    const controlDiv = document.createElement("div");
    const controlUI = document.createElement("button");

    controlUI.classList.add("ui-button");
    controlUI.innerText = `${text}`;
    controlUI.addEventListener("click", () => {
      adjustMap(mode, amount);
    });
    controlDiv.appendChild(controlUI);
    map.controls[position].push(controlDiv);
  });

  const adjustMap = function (mode: string, amount: number) {
    switch (mode) {
      case "tilt":
        map.setTilt(map.getTilt()! + amount);
        break;
      case "rotate":
        map.setHeading(map.getHeading()! + amount);
        break;
      default:
        break;
    }
  };
}

declare global {
  interface Window {
    initMap: () => void;
  }
}
window.initMap = initMap;

JavaScript

function initMap() {
  const map = new google.maps.Map(document.getElementById("map"), {
    center: {
      lat: 37.7893719,
      lng: -122.3942,
    },
    zoom: 16,
    heading: 320,
    tilt: 47.5,
    mapId: "90f87356969d889c",
  });
  const buttons = [
    ["Rotate Left", "rotate", 20, google.maps.ControlPosition.LEFT_CENTER],
    ["Rotate Right", "rotate", -20, google.maps.ControlPosition.RIGHT_CENTER],
    ["Tilt Down", "tilt", 20, google.maps.ControlPosition.TOP_CENTER],
    ["Tilt Up", "tilt", -20, google.maps.ControlPosition.BOTTOM_CENTER],
  ];

  buttons.forEach(([text, mode, amount, position]) => {
    const controlDiv = document.createElement("div");
    const controlUI = document.createElement("button");

    controlUI.classList.add("ui-button");
    controlUI.innerText = `${text}`;
    controlUI.addEventListener("click", () => {
      adjustMap(mode, amount);
    });
    controlDiv.appendChild(controlUI);
    map.controls[position].push(controlDiv);
  });

  const adjustMap = function (mode, amount) {
    switch (mode) {
      case "tilt":
        map.setTilt(map.getTilt() + amount);
        break;
      case "rotate":
        map.setHeading(map.getHeading() + amount);
        break;
      default:
        break;
    }
  };
}

window.initMap = initMap;

CSS

/* 
 * Always set the map height explicitly to define the size of the div element
 * that contains the map. 
 */
#map {
  height: 100%;
}

/* 
 * Optional: Makes the sample page fill the window. 
 */
html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

.ui-button {
  background-color: #fff;
  border: 0;
  border-radius: 2px;
  box-shadow: 0 1px 4px -1px rgba(0, 0, 0, 0.3);
  margin: 10px;
  padding: 0 0.5em;
  font: 400 18px Roboto, Arial, sans-serif;
  overflow: hidden;
  height: 40px;
  cursor: pointer;
}
.ui-button:hover {
  background: rgb(235, 235, 235);
}

HTML

<html>
  <head>
    <title>Tilt and Rotation</title>

    <link rel="stylesheet" type="text/css" href="./style.css" />
    <script type="module" src="./index.js"></script>
  </head>
  <body>
    <div id="map"></div>

    <!-- 
      The `defer` attribute causes the script to execute after the full HTML
      document has been parsed. For non-blocking uses, avoiding race conditions,
      and consistent behavior across browsers, consider loading using Promises. See
      https://developers.google.com/maps/documentation/javascript/load-maps-js-api
      for more information.
      -->
    <script
      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initMap&v=weekly"
      defer
    ></script>
  </body>
</html>

试用示例

使用鼠标和键盘手势

如果已启用倾斜和旋转(航向)用户互动(通过编程或在 Google Cloud 控制台中),用户可以使用鼠标和键盘调整倾斜和旋转:

  • 使用鼠标,按住 Shift 键,然后按住鼠标左键并上下拖动调整倾斜度,左右拖动调整朝向。
  • 使用键盘,按住 Shift 键,然后使用向上键和向下键调整倾斜度,使用向右键和向左键调整朝向。

以程序化方式调整倾斜度和朝向

使用 setTilt()setHeading() 方法,能够以程序化方式调整矢量地图的倾斜度和朝向。朝向是指镜头面向的方向与正北方形成的顺时针角度,因此 map.setHeading(90) 会旋转地图,使东方成为向上的方向。倾斜角度从顶部测量,因此 map.setTilt(0) 代表垂直俯视,而 map.setTilt(45) 将产生倾斜视图。

  • 调用 setTilt() 可设置地图的倾斜角度。使用 getTilt() 可获取当前倾斜度值。
  • 调用 setHeading() 可设置地图的朝向。使用 getHeading() 可获取当前朝向值。

要更改地图中心,同时保持倾斜度和朝向不变,请使用 map.setCenter()map.panBy()

请注意,可以使用的角度范围因当前的缩放级别而异。系统将把超出此范围的值强制转化到当前允许的范围内。

您还可以使用 moveCamera 方法,以程序化方式更改朝向、倾斜度、中心和缩放级别。了解详情

对其他方法的影响

对地图应用倾斜或旋转后,其他 Maps JavaScript API 方法的行为会受到影响:

  • map.getBounds() 会始终返回包含可见区域的最小边界框;应用倾斜后,返回的边界所代表的区域可能大于地图视口的可见区域。
  • map.fitBounds() 会先将倾斜度和朝向重置为零,然后再拟合边界。
  • map.panToBounds() 会先将倾斜度和朝向重置为零,然后再平移边界。
  • map.setTilt() 接受任何值,但会根据当前地图缩放级别限制最大倾斜度。
  • map.setHeading() 接受任何值,并会对相应值进行修改,使其适合 [0, 360] 的范围。

控制镜头

使用 map.moveCamera() 函数一次更新镜头属性的任意组合。map.moveCamera() 接受包含要更新的所有镜头属性的单个参数。以下示例展示了如何调用 map.moveCamera() 以同时设置 centerzoomheadingtilt

map.moveCamera({
  center: new google.maps.LatLng(37.7893719, -122.3942),
  zoom: 16,
  heading: 320,
  tilt: 47.5
});

您可以调用 map.moveCamera() 以动画方式呈现镜头属性,并设置动画循环,如下所示:

const degreesPerSecond = 3;

function animateCamera(time) {
  // Update the heading, leave everything else as-is.
  map.moveCamera({
    heading: (time / 1000) * degreesPerSecond
  });

  requestAnimationFrame(animateCamera);
}

// Start the animation.
requestAnimationFrame(animateCamera);

相机位置

地图视图被建模成一个俯视某个平面的镜头。下列属性指定了镜头的位置(也进而决定了地图的渲染效果):目标(纬度/经度位置)方向角倾斜度缩放级别

镜头属性图

目标(位置)

镜头目标是地图中心的位置,以纬度和经度坐标形式指定。

纬度可以介于 -85 度(含)和 85 度(含)之间。不在此范围内的值会被调整为此范围内最接近的值。例如,如果将纬度指定为 100,系统会将该值设为 85。经度范围为 -180 度(含)到 180 度(含)。不在此范围内的值会被换算为 (-180, 180) 范围内的值。例如,480、840 和 1200 都会换算为 120 度。

方向角(方向)

镜头方向角指定了罗盘方向,以相对于正北方(对应于地图的顶部边缘)的角度来表示。从地图中心到顶部边缘画一条垂直线,镜头朝向相对于正北方的角度(以度为单位)即为方向角。

方向角为 0 表示地图顶部指向正北。方向角为 90 表示地图顶部指向正东方(罗盘上为 90 度)。方向角为 180 表示地图顶部指向正南方。

您可以使用 Maps API 更改地图的方向角。例如,驾车的用户通常会旋转道路地图使其与他们的行驶方向一致,而远足者使用地图和罗盘时通常会调整地图方向,使地图上有一条垂直线指向北方。

倾斜度(视角)

倾斜度是指镜头在地图中心位置正上方圆弧上所处的位置,以相对于天底(相机正下方)的角度来表示。值为 0 时,镜头竖直朝下。值大于 0 时,镜头按指定角度朝地平线倾斜。更改视角时,地图会以透视法呈现:远处的地图项显示得较小,而近处的地图项显示得较大。以下插图展示了这种情况。

在下面的图片中,视角为 0 度。第一张图片是视角为 0 度时的示意图;位置 1 是镜头位置,位置 2 是当前的地图位置。生成的地图如下所示。

镜头视角为 0 度、缩放级别为 18 的地图的屏幕截图。
以镜头的默认视角显示的地图。
显示镜头默认位置(在地图位置正上方,角度为 0)的示意图。
镜头的默认视角。

以下图片中的视角为 45 度。请注意,镜头沿圆弧移到了正上(0 度)与地面(90 度)中间的位置,也就是位置 3。镜头仍指向地图的中心点,但现在位置 4 处的线表示的区域会显示出来。

镜头视角为 45 度、缩放级别为 18 的地图的屏幕截图。
以 45 度视角显示的地图。
显示镜头的视角设置为 45 度、缩放级别仍设置为 18 的示意图。
45 度镜头视角。

此屏幕截图中的地图仍与原始地图采用同一个中心点,但地图顶部显示的地图项增加了。当您将角度增加到 45 度以上时,镜头和地图位置之间的地图项会按比例显示得较大,而地图位置远处的地图项会按比例显示得较小,从而产生三维效果。

缩放级别

镜头的缩放级别决定了地图的比例。缩放级别越大,屏幕上显示的信息越详细;缩放级别越小,屏幕上显示的世界范围越广。

缩放级别的值不必是整数。地图允许的缩放级别范围取决于多个因素,包括目标、地图类型和屏幕尺寸。超出该范围的任何数值都将转换为最接近的有效值(可以是最小缩放级别或最大缩放级别)。以下列表显示了您在每个缩放级别看到的地图的大致详细程度:

  • 1:世界
  • 5:大陆/洲
  • 10:城市
  • 15:街道
  • 20:建筑物
以下图片显示了不同缩放级别的视觉外观:
缩放级别为 5 的地图的屏幕截图
缩放级别为 5 的地图。
缩放级别为 15 的地图的屏幕截图
缩放级别为 15 的地图。
缩放级别为 20 的地图的屏幕截图
缩放级别为 20 的地图。

小数缩放

矢量地图支持小数缩放,可让您使用小数值(而非整数)进行缩放。虽然光栅地图和矢量地图都支持小数缩放,但对于矢量地图,该功能默认处于启用状态,而对于光栅地图,则默认处于停用状态。使用 isFractionalZoomEnabled 地图选项可启用和停用小数缩放。

以下示例展示了如何在初始化地图时启用小数缩放:

map = new google.maps.Map(document.getElementById('map'), {
  center: {lat: -34.397, lng: 150.644},
  zoom: 8,
  isFractionalZoomEnabled: true
});

您还可以通过设置 isFractionalZoomEnabled 地图选项来启用和停用小数缩放,如下所示:

// Using map.set
map.set('isFractionalZoomEnabled', true);

// Using map.setOptions
map.setOptions({isFractionalZoomEnabled: true});

您可以设置监听器来检测是否启用了小数缩放;如果您尚未明确将 isFractionalZoomEnabled 设置为 truefalse,此方法会非常有用。以下示例代码会检查小数缩放是否已启用:

map.addListener('isfractionalzoomenabled_changed', () => {
  const isFractionalZoomEnabled = map.get('isFractionalZoomEnabled');
  if (isFractionalZoomEnabled === false) {
    console.log('not using fractional zoom');
  } else if (isFractionalZoomEnabled === true) {
    console.log('using fractional zoom');
  } else {
    console.log('map not done initializing yet');
  }
});