一切就绪!

着手开发前,请先阅读我们的开发者文档

激活 Google Maps JavaScript API

为帮助您起步,我们将引导您在 Google Developers Console 中先完成几项任务:

  1. 创建或选择项目
  2. 激活 Google Maps JavaScript API 及相关服务
  3. 创建相应密钥
继续

地图类型

地图类型

本文档讨论的是您可以使用 Google Maps JavaScript API 显示的地图类型。此 API 使用 MapType 对象保留这些地图的相关信息。MapType 是一个接口,用于定义地图图块的显示形式和使用方法,以及坐标系从屏幕坐标转换到世界坐标(地图上)的方式。每个 MapType 都必须包含多个用于处理图块的检索和释放的方法,以及定义图块视觉行为的属性。

地图类型在 Maps JavaScript API 中的内部工作方式属于高级主题。大多数开发者可以仅使用下面所述的基本地图类型。但是,您也可以使用自定义地图类型定义自己的地图图块,或使用样式化地图修改现有地图类型的呈现形式。提供自定义地图类型时,您需要了解如何修改地图的地图类型注册表

基本地图类型

Google Maps JavaScript API 内提供了四种类型的地图。除了您熟悉的“绘制”道路地图图块外,Maps JavaScript API 还可支持其他地图类型。

Maps JavaScript API 中提供了下列地图类型:

  • roadmap:用于显示默认的道路地图视图。这是默认地图类型。
  • satellite:用于显示 Google 地球卫星图片
  • hybrid:用于同时显示正常视图和卫星视图
  • terrain:用于基于地面信息显示物理地图。

您可以通过 Map 修改正在使用的地图类型,方法是设置其 mapTypeId 属性,可通过在构造函数内设置其 Map options 对象,或调用地图的 setMapTypeId() 方法来实现。mapTypeID 属性默认使用 roadmap

构建时设置 mapTypeId

var myLatlng = new google.maps.LatLng(-34.397, 150.644);
var mapOptions = {
  zoom: 8,
  center: myLatlng,
  mapTypeId: 'satellite'
};
var map = new google.maps.Map(document.getElementById('map'),
    mapOptions);

动态地修改 mapTypeId

map.setMapTypeId('terrain');

请注意,您实际上并没有直接设置地图的地图类型,而是将其 mapTypeId 设置为引用使用标识符的 MapType。Maps JavaScript API 使用了地图类型注册表(详见下文)来管理这些引用。

45 度图像

Google Maps JavaScript API 针对特定位置支持特殊的 45 度图像。这种高分辨率图像可提供朝向各基本方向(东南西北)的俯瞰视图。对于支持的地图类型,这些图像还可提供更高的缩放级别。

下图显示的是加利福尼亚州圣塔克鲁兹市海滩步道的 45 度透视视图:

satellitehybrid 地图类型在高缩放级别下支持 45 度图像(如有)。如果用户放大的位置拥有此类图像,则这些地图类型将自动通过以下方式更改其视图:

  • 以当前位置为中心的 45 度透视视图将替代卫星图像或混合图像。默认情况下,此类视图会朝向北方。如果用户缩小地图,则地图会重新显示默认的卫星图像或混合图像。
  • 旋转控件提供倾斜和旋转选项组合。如果 rotateControltrue,则当 45 度图像可用时将显示一个倾斜控件。此倾斜控件允许用户将图像倾斜 45 度。
  • 将图像倾斜后,会显示一个钩子,允许用户按顺时针方向将视图旋转 90 度。

缩小显示 45 度图像的地图类型将会还原每一个更改,并重新构建原始地图类型。

启用和禁用 45 度图像

您可以通过在 Map 对象上调用 setTilt(0) 来禁用 45 度图像。要启用支持的地图类型的 45 度图像,请调用 setTilt(45)

MapgetTilt() 方法将始终反映地图上所显示的当前倾斜度;如果您在地图上设置了倾斜度后又将其删除(例如通过缩小地图的方式),则地图的 getTilt() 方法将返回 0

以下示例显示的是波特兰市中心的 45 度视图,或:

function initMap() {
  var map = new google.maps.Map(document.getElementById('map'), {
    center: {lat: 36.964, lng: -122.015},
    zoom: 18,
    mapTypeId: 'satellite'
  });
  map.setTilt(45);
}
<div id="map"></div>
/* 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;
}
 <!-- Replace the value of the key parameter with your own API key. -->
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap">
</script>
function initMap() {
  var map = new google.maps.Map(document.getElementById('map'), {
    center: {lat: 36.964, lng: -122.015},
    zoom: 18,
    mapTypeId: 'satellite'
  });
  map.setTilt(45);
}

查看示例 (aerial-simple.html)

旋转 45 度图像

45 度图像实际上包含各个基本方向(东南西北)的图像集。在您的地图显示 45 度图像时,您可以将图像对准某个基本方向,方法是在 Map 对象上调用 setHeading(),并向其传递一个数值,表示偏离北方的角度。

以下示例中显示了一张航拍地图,在您点击按钮后它会每 3 秒钟自动旋转一次:

var map;

function initMap() {
  map = new google.maps.Map(document.getElementById('map'), {
    center: {lat: 45.518, lng: -122.672},
    zoom: 18,
    mapTypeId: 'satellite',
    heading: 90,
    tilt: 45
  });
}

function rotate90() {
  var heading = map.getHeading() || 0;
  map.setHeading(heading + 90);
}

function autoRotate() {
  // Determine if we're showing aerial imagery.
  if (map.getTilt() !== 0) {
    window.setInterval(rotate90, 3000);
  }
}
<div id="floating-panel"><input type="button" value="Auto Rotate" onclick="autoRotate();"></div>
<div id="map"></div>
/* 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;
}
#floating-panel {
  position: absolute;
  top: 10px;
  left: 25%;
  z-index: 5;
  background-color: #fff;
  padding: 5px;
  border: 1px solid #999;
  text-align: center;
  font-family: 'Roboto','sans-serif';
  line-height: 30px;
  padding-left: 10px;
}
 <!-- Replace the value of the key parameter with your own API key. -->
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap">
</script>
var map;

function initMap() {
  map = new google.maps.Map(document.getElementById('map'), {
    center: {lat: 45.518, lng: -122.672},
    zoom: 18,
    mapTypeId: 'satellite',
    heading: 90,
    tilt: 45
  });
}

function rotate90() {
  var heading = map.getHeading() || 0;
  map.setHeading(heading + 90);
}

function autoRotate() {
  // Determine if we're showing aerial imagery.
  if (map.getTilt() !== 0) {
    window.setInterval(rotate90, 3000);
  }
}

查看示例 (aerial-rotation.html)

修改地图类型注册表

地图的 mapTypeId 是一种字符串标识符,用于将 MapType 与一个唯一值进行关联。每个 Map 对象都会保留一个 MapTypeRegistry,其中包含该地图可用的 MapType 的集合。该注册表用于选择地图的类型,例如,地图 MapType 控件中可用的地图类型。

您无法直接读取地图类型注册表,而是应该通过以下方法对注册表进行修改:添加自定义地图类型并将它们与您所选择的字符串标识符进行关联。您无法修改或更改基本地图类型(但您可以通过更改与该地图关联的 mapTypeControlOptions 的显示形式将这些类型从地图中删除)。

以下代码将地图设置为仅显示其 mapTypeControlOptions 中的两种地图类型,并修改了注册表,以将此标识符的关联添加到 MapType 接口的实际实现中。:我们有意未在之前的代码中记录自定义地图类型本身的创建。有关构建地图类型的信息,请参阅下面的样式化地图自定义地图类型

// Modify the control to only display two maptypes, the
// default ROADMAP and the custom 'mymap'.
// Note that because this is simply an association, we
// don't need to modify the MapTypeRegistry beforehand.

var MY_MAPTYPE_ID = 'mymaps';

var mapOptions = {
  zoom: 12,
  center: brooklyn,
  mapTypeControlOptions: {
     mapTypeIds: ['roadmap', MY_MAPTYPE_ID]
  },
  mapTypeId: MY_MAPTYPE_ID
};

// Create our map. This creation will implicitly create a
// map type registry.
map = new google.maps.Map(document.getElementById('map'),
    mapOptions);

// Create your custom map type using your own code.
// (See below.)
var myMapType = new MyMapType();

// Set the registry to associate 'mymap' with the
// custom map type we created, and set the map to
// show that map type.
map.mapTypes.set(MY_MAPTYPE_ID, myMapType);

样式化地图

StyledMapType 让您可以自定义 Google 标准基本地图的呈现形式,并更改道路、公园和建筑物区域等元素的视觉显示,以体现不同于默认地图类型中所使用的样式。

如需了解有关 StyledMapType 的详细信息,请参阅样式化地图指南。

自定义地图类型

Google Maps JavaScript API 支持自定义地图类型的显示和管理,让您可以实现自己的地图图像或图块叠层。

Maps JavaScript API 中提供了以下几种可能的地图类型实现:

  • 标准图块集,其中所包含的图像共同构成了完整的制图地图。这些图块集也称为基本地图类型。这些地图类型的行为和表现类似于现有的默认地图类型:roadmapsatellitehybridterrain。您可以将自定义地图类型添加到地图的 mapTypes 数组中,以允许 Maps JavaScript API 中的 UI 将您的自定义地图类型视为标准地图类型(例如,将自定义地图类型加入到 MapType 控件中)。
  • 图像图块叠层,显示在现有基本地图类型之上。通常,这些地图类型用于扩充现有地图类型以显示更多信息,并往往受限于特定位置和/或缩放级别。请注意,这些图块可以是透明的,以便您将功能添加到现有地图中。
  • 非图像的地图类型,允许您在最基础的级别处理地图信息的显示。

以上每个选项均依赖于创建一个类以实现 MapType 接口。此外, ImageMapType 类提供了某些内置行为以简化图像 MapType 的创建过程。

在我们解释用于实现 MapType 的类之前,必须先了解 Google 地图是如何确定坐标以及要显示的地图部分。对于任何基本或叠层 MapType,您需要实现类似的逻辑。

地图坐标

Google Maps JavaScript API 使用以下几种坐标系:

  • 纬度和经度值,唯一对应地球上的一个点。(Google 使用世界大地测量系统 WGS84 标准。)
  • 世界坐标,唯一对应地图上的一个点。
  • 图块坐标,对应特定缩放级别地图上的特定图块。

世界坐标

每当 Google Maps JavaScript API 需要将世界位置转换为地图(屏幕)上的位置时,首先需要将纬度和经度值转换为“世界”坐标。此转换过程使用地图投影来完成。为此,Google 地图使用墨卡托投影法。您也可以定义您自己的投影以实现 google.maps.Projection 接口。(请注意,Maps JavaScript API 中的接口并不是您“子类化”的类,而是您自己定义的类的简单规范。)

为便于计算像素坐标(见下文),我们假定缩放级别为 0 的地图为具有基本图块尺寸的单个图块。然后,我们在缩放级别 0 定义像素坐标对应的世界坐标,使用投影将纬度和经度转换为此基本图块上的像素位置。该世界坐标为从地图投影原点到特定位置测量的浮点值。请注意,由于该值为浮点值,因此,可能比显示的地图图像的当前分辨率更精确。换言之,世界坐标与当前缩放级别无关。

Google 地图中的世界坐标是以墨卡托投影的原点(即地图西北角,经度为 180 度,纬度约 85 度)为起点测量的,在 x 方向上朝东(向右)增大,在 y 方向上朝南(向下)增大。由于基本的墨卡托 Google 地图图块为 256 x 256 像素,因此,可用的世界坐标空间为 {0-256}, {0-256}(见下文。)

请注意,墨卡托投影法的经度方向宽度有限,但维度方向高度无限。我们利用墨卡托投影法在大约 +/-85 度下“裁剪”基本地图图像,使得到的地图呈方形,从而简化图块选择逻辑。请注意,投影可能在基本地图的可用坐标空间之外生成世界坐标,例如,如果您在离极点非常近的地方绘制。

像素坐标

世界坐标反映的是指定投影上的绝对位置,但我们需要将该坐标转换为像素坐标,以确定指定缩放级别的“像素”偏移量。像素坐标采用以下公式进行计算:

pixelCoordinate = worldCoordinate * 2zoomLevel

请注意,根据上述公式,每个增大的缩放级别在 xy 方向上均为原来的两倍大。因此,缩放级别每增大一级,分辨率为前一个级别的四倍。例如,在缩放级别 1,地图包含 4 个 256x256 像素图块,因而像素空间为 512x512。在缩放级别 19,地图上的每个 xy 像素可使用 0 到 256 * 219 之间的值进行引用

由于世界坐标是建立在地图的图块大小基础上的,因此,像素坐标的整数部分的作用是标识该位置在当前缩放级别下的确切像素。请注意,对于缩放级别 0,像素坐标等于世界坐标。

现在,我们可以在每个缩放级别精确表示地图上的每个位置。Google Maps JavaScript API 在地图中心构造指定缩放级别的视口(以 LatLng 形式),以及包含 DOM 元素的大小,并将此边界框转换为像素坐标。然后,API 从逻辑上确定位于指定像素边界内的所有地图图块。每个地图图块均使用图块坐标进行引用,该坐标大大简化了地图图像的显示。

图块坐标

Google Maps JavaScript API 可能无法在最广泛的较高缩放级别下加载所有地图图像;相反,Maps JavaScript API 将各个缩放级别的图像分解成一组地图图块,这些图块逻辑上按照应用程序能识别的顺序排列。当地图滚动至新的位置,或者切换到新的缩放级别时,Maps JavaScript API 会使用像素坐标确定所需的图块,然后将这些值转换为一组要检索的图块。这些图块坐标采用逻辑上易于确定哪个图块包含任意指定点图像的架构进行分配。

Google 地图中的图块从与像素原点相同的位置进行编号。为了便于 Google 实现墨卡托投影法,原点图块始终位于地图的西北角,x 值从西向东增加,y 值从北向南增加。利用基于该原点的 x,y 坐标为图块建立索引。例如,在缩放级别 2,当地球被划分成 16 个图块时,每一个图库都可以通过唯一的 x,y 对加以引用:

请注意,您可以通过按图块大小划分像素坐标并取结果的整数部分,生成当前缩放级别的图块坐标,它属于副产品。

下例显示伊利诺州芝加哥市在不同缩放级别的坐标 – LatLng 值、世界坐标、像素坐标以及图块坐标:

查看示例 (map-coordinates.html)

MapType 接口

自定义地图类型必须实现 MapType 接口。此接口指定某些属性和方法,以允许 API 在确定需要在当前视区和缩放级别中显示地图图块时可以发起对地图类型的请求。您可处理这些请求,以确定要加载的图块。

:您可以创建自己的类以实现此接口,或者如果您有兼容的图像,则可以使用已实现此接口的 ImageMapType 类。)

实现 MapType 接口的类要求您定义和填充以下属性:

  • tileSize(必填)指定(类型为 google.maps.Size)图块的大小。图块大小必须为矩形,但不一定为正方形。
  • maxZoom(必填)指定显示该地图类型图块的最大缩放级别。
  • minZoom(可选)指定显示该地图类型图块的最小缩放级别。默认情况下,该值为 0,表示不存在最小缩放级别。
  • name(可选)指定该地图类型的名称。仅当您希望在 MapType 控件中可选择此地图类型时,该属性才是必需的。(请参见下文的添加 MapType 控件)。
  • alt(可选)指定该地图类型的替换文本,该替换文本将以悬停文本的形式显示。仅当您希望在 MapType 控件中可选择此地图类型时,该属性才是必需的。(请参见下文的添加 MapType 控件)。

此外,实现 MapType 接口的类还必须实现以下方法:

  • getTile()(必需),对于指定视区,每当 API 确定地图需要显示新的图块时调用此方法。getTile() 方法必须具有以下签名:

    getTile(tileCoord:Point,zoom:number,ownerDocument:Document):Node

    API 会根据 MapTypetileSizeminZoommaxZoom 属性以及地图的当前视区和缩放级别来决定是否需要调用 getTile()。在已传递坐标、缩放级别和要附加图块图像的 DOM 元素的情况下,此方法的处理程序应返回 HTML 元素。

  • releaseTile()(可选),每当 API 确定地图需要删除不在视图范围内的图块时,调用此方法。该方法必须拥有以下签名:

    releaseTile(tile:Node)

    通常,您应删除添加到地图后附加到地图图块的任何元素。例如,如果您在地图图块叠层中附加了事件侦听器,则应在此删除这些侦听器。

getTile() 方法在确定指定可视区域内要载入的图块时是主控制器。

基本地图类型

采用此方式构建的地图类型可以单独使用,也可以作为叠层与其他地图类型结合使用。单独的地图类型称为基本地图类型。您可能希望 API 像对待现有的其他任何基本地图类型(ROADMAPTERRAIN 等)一样对待此类自定义 MapType。为此,可将您的自定义 MapType 添加到 MapmapTypes 属性。此属性类型为 MapTypeRegistry

以下代码创建一个基本 MapType 以显示地图的图块坐标,并绘制图块轮廓:

/*
 * This demo demonstrates how to replace default map tiles with custom imagery.
 * In this case, the CoordMapType displays gray tiles annotated with the tile
 * coordinates.
 *
 * Try panning and zooming the map to see how the coordinates change.
 */

/**
 * @constructor
 * @implements {google.maps.MapType}
 */
function CoordMapType(tileSize) {
  this.tileSize = tileSize;
}

CoordMapType.prototype.maxZoom = 19;
CoordMapType.prototype.name = 'Tile #s';
CoordMapType.prototype.alt = 'Tile Coordinate Map Type';

CoordMapType.prototype.getTile = function(coord, zoom, ownerDocument) {
  var div = ownerDocument.createElement('div');
  div.innerHTML = coord;
  div.style.width = this.tileSize.width + 'px';
  div.style.height = this.tileSize.height + 'px';
  div.style.fontSize = '10';
  div.style.borderStyle = 'solid';
  div.style.borderWidth = '1px';
  div.style.borderColor = '#AAAAAA';
  div.style.backgroundColor = '#E5E3DF';
  return div;
};

function initMap() {
  var map = new google.maps.Map(document.getElementById('map'), {
    zoom: 10,
    center: {lat: 41.850, lng: -87.650},
    streetViewControl: false,
    mapTypeId: 'coordinate',
    mapTypeControlOptions: {
      mapTypeIds: ['coordinate', 'roadmap'],
      style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
    }
  });

  map.addListener('maptypeid_changed', function() {
    var showStreetViewControl = map.getMapTypeId() !== 'coordinate';
    map.setOptions({
      streetViewControl: showStreetViewControl
    });
  });

  // Now attach the coordinate map type to the map's registry.
  map.mapTypes.set('coordinate',
                   new CoordMapType(new google.maps.Size(256, 256)));
}
<div id="map"></div>
/* 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;
}
 <!-- Replace the value of the key parameter with your own API key. -->
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap">
</script>
/*
 * This demo demonstrates how to replace default map tiles with custom imagery.
 * In this case, the CoordMapType displays gray tiles annotated with the tile
 * coordinates.
 *
 * Try panning and zooming the map to see how the coordinates change.
 */

/**
 * @constructor
 * @implements {google.maps.MapType}
 */
function CoordMapType(tileSize) {
  this.tileSize = tileSize;
}

CoordMapType.prototype.maxZoom = 19;
CoordMapType.prototype.name = 'Tile #s';
CoordMapType.prototype.alt = 'Tile Coordinate Map Type';

CoordMapType.prototype.getTile = function(coord, zoom, ownerDocument) {
  var div = ownerDocument.createElement('div');
  div.innerHTML = coord;
  div.style.width = this.tileSize.width + 'px';
  div.style.height = this.tileSize.height + 'px';
  div.style.fontSize = '10';
  div.style.borderStyle = 'solid';
  div.style.borderWidth = '1px';
  div.style.borderColor = '#AAAAAA';
  div.style.backgroundColor = '#E5E3DF';
  return div;
};

function initMap() {
  var map = new google.maps.Map(document.getElementById('map'), {
    zoom: 10,
    center: {lat: 41.850, lng: -87.650},
    streetViewControl: false,
    mapTypeId: 'coordinate',
    mapTypeControlOptions: {
      mapTypeIds: ['coordinate', 'roadmap'],
      style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
    }
  });

  map.addListener('maptypeid_changed', function() {
    var showStreetViewControl = map.getMapTypeId() !== 'coordinate';
    map.setOptions({
      streetViewControl: showStreetViewControl
    });
  });

  // Now attach the coordinate map type to the map's registry.
  map.mapTypes.set('coordinate',
                   new CoordMapType(new google.maps.Size(256, 256)));
}

查看示例 (maptype-base.html)

叠层地图类型

某些地图类型设计为在现有地图类型上使用。此类地图类型可能具有透明图层,以指示兴趣点或向用户显示其他数据。

在这些情况下,您不会希望将地图类型视为单独的实体。相反,您可以使用 MapoverlayMapTypes 属性,将地图类型直接添加到现有 MapType 中。此属性包含 MapTypeMVCArray。所有地图类型(基本和叠层)均在 mapPane 层中渲染。叠层地图类型将按照在 Map.overlayMapTypes 数组中的出现顺序,显示在附加的任何基本地图之上。

除了在 ROADMAP 地图类型上创建了一个图块叠层 MapType 外,以下示例与上一个示例基本相同:

/*
 * This demo illustrates the coordinate system used to display map tiles in the
 * API.
 *
 * Tiles in Google Maps are numbered from the same origin as that for
 * pixels. For Google's implementation of the Mercator projection, the origin
 * tile is always at the northwest corner of the map, with x values increasing
 * from west to east and y values increasing from north to south.
 *
 * Try panning and zooming the map to see how the coordinates change.
 */

/** @constructor */
function CoordMapType(tileSize) {
  this.tileSize = tileSize;
}

CoordMapType.prototype.getTile = function(coord, zoom, ownerDocument) {
  var div = ownerDocument.createElement('div');
  div.innerHTML = coord;
  div.style.width = this.tileSize.width + 'px';
  div.style.height = this.tileSize.height + 'px';
  div.style.fontSize = '10';
  div.style.borderStyle = 'solid';
  div.style.borderWidth = '1px';
  div.style.borderColor = '#AAAAAA';
  return div;
};

function initMap() {
  var map = new google.maps.Map(document.getElementById('map'), {
    zoom: 10,
    center: {lat: 41.850, lng: -87.650}
  });

  // Insert this overlay map type as the first overlay map type at
  // position 0. Note that all overlay map types appear on top of
  // their parent base map.
  map.overlayMapTypes.insertAt(
      0, new CoordMapType(new google.maps.Size(256, 256)));
}
<div id="map"></div>
/* 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;
}
 <!-- Replace the value of the key parameter with your own API key. -->
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap">
</script>
/*
 * This demo illustrates the coordinate system used to display map tiles in the
 * API.
 *
 * Tiles in Google Maps are numbered from the same origin as that for
 * pixels. For Google's implementation of the Mercator projection, the origin
 * tile is always at the northwest corner of the map, with x values increasing
 * from west to east and y values increasing from north to south.
 *
 * Try panning and zooming the map to see how the coordinates change.
 */

/** @constructor */
function CoordMapType(tileSize) {
  this.tileSize = tileSize;
}

CoordMapType.prototype.getTile = function(coord, zoom, ownerDocument) {
  var div = ownerDocument.createElement('div');
  div.innerHTML = coord;
  div.style.width = this.tileSize.width + 'px';
  div.style.height = this.tileSize.height + 'px';
  div.style.fontSize = '10';
  div.style.borderStyle = 'solid';
  div.style.borderWidth = '1px';
  div.style.borderColor = '#AAAAAA';
  return div;
};

function initMap() {
  var map = new google.maps.Map(document.getElementById('map'), {
    zoom: 10,
    center: {lat: 41.850, lng: -87.650}
  });

  // Insert this overlay map type as the first overlay map type at
  // position 0. Note that all overlay map types appear on top of
  // their parent base map.
  map.overlayMapTypes.insertAt(
      0, new CoordMapType(new google.maps.Size(256, 256)));
}

查看示例 (maptype-overlay.html)

图像地图类型

实现 MapType 以充当基本地图类型既耗时又耗力。API 为最常用的地图类型提供了实现 MapType 接口的特殊类:包含图块的地图类型构成单个图像文件。

此类即 ImageMapType 类,使用 ImageMapTypeOptions 对象规范构建,该对象规范定义了以下必需属性:

  • tileSize(必填)指定(类型为 google.maps.Size)图块的大小。图块大小必须为矩形,但不一定为正方形。
  • getTileUrl(必需)指定函数(通常作为内联函数字面量提供),以根据提供的世界坐标和缩放级别选择恰当的图像图块。

以下代码使用 Google 的月球图块实现基本 ImageMapType。该示例利用归一化函数以确保图块沿着地图的 x 轴重复,而不是沿着 y 轴重复。

function initMap() {
  var map = new google.maps.Map(document.getElementById('map'), {
    center: {lat: 0, lng: 0},
    zoom: 1,
    streetViewControl: false,
    mapTypeControlOptions: {
      mapTypeIds: ['moon']
    }
  });

  var moonMapType = new google.maps.ImageMapType({
    getTileUrl: function(coord, zoom) {
        var normalizedCoord = getNormalizedCoord(coord, zoom);
        if (!normalizedCoord) {
          return null;
        }
        var bound = Math.pow(2, zoom);
        return '//mw1.google.com/mw-planetary/lunar/lunarmaps_v1/clem_bw' +
            '/' + zoom + '/' + normalizedCoord.x + '/' +
            (bound - normalizedCoord.y - 1) + '.jpg';
    },
    tileSize: new google.maps.Size(256, 256),
    maxZoom: 9,
    minZoom: 0,
    radius: 1738000,
    name: 'Moon'
  });

  map.mapTypes.set('moon', moonMapType);
  map.setMapTypeId('moon');
}

// Normalizes the coords that tiles repeat across the x axis (horizontally)
// like the standard Google map tiles.
function getNormalizedCoord(coord, zoom) {
  var y = coord.y;
  var x = coord.x;

  // tile range in one direction range is dependent on zoom level
  // 0 = 1 tile, 1 = 2 tiles, 2 = 4 tiles, 3 = 8 tiles, etc
  var tileRange = 1 << zoom;

  // don't repeat across y-axis (vertically)
  if (y < 0 || y >= tileRange) {
    return null;
  }

  // repeat across x-axis
  if (x < 0 || x >= tileRange) {
    x = (x % tileRange + tileRange) % tileRange;
  }

  return {x: x, y: y};
}
<div id="map"></div>
/* 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;
}
 <!-- Replace the value of the key parameter with your own API key. -->
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap">
</script>
function initMap() {
  var map = new google.maps.Map(document.getElementById('map'), {
    center: {lat: 0, lng: 0},
    zoom: 1,
    streetViewControl: false,
    mapTypeControlOptions: {
      mapTypeIds: ['moon']
    }
  });

  var moonMapType = new google.maps.ImageMapType({
    getTileUrl: function(coord, zoom) {
        var normalizedCoord = getNormalizedCoord(coord, zoom);
        if (!normalizedCoord) {
          return null;
        }
        var bound = Math.pow(2, zoom);
        return '//mw1.google.com/mw-planetary/lunar/lunarmaps_v1/clem_bw' +
            '/' + zoom + '/' + normalizedCoord.x + '/' +
            (bound - normalizedCoord.y - 1) + '.jpg';
    },
    tileSize: new google.maps.Size(256, 256),
    maxZoom: 9,
    minZoom: 0,
    radius: 1738000,
    name: 'Moon'
  });

  map.mapTypes.set('moon', moonMapType);
  map.setMapTypeId('moon');
}

// Normalizes the coords that tiles repeat across the x axis (horizontally)
// like the standard Google map tiles.
function getNormalizedCoord(coord, zoom) {
  var y = coord.y;
  var x = coord.x;

  // tile range in one direction range is dependent on zoom level
  // 0 = 1 tile, 1 = 2 tiles, 2 = 4 tiles, 3 = 8 tiles, etc
  var tileRange = 1 << zoom;

  // don't repeat across y-axis (vertically)
  if (y < 0 || y >= tileRange) {
    return null;
  }

  // repeat across x-axis
  if (x < 0 || x >= tileRange) {
    x = (x % tileRange + tileRange) % tileRange;
  }

  return {x: x, y: y};
}

查看示例 (maptype-image.html)

投影

地球是一个三维球体(近似说法),而地图则是一个二维平面。您在 Google Maps JavaScript API 中看到的地图与其他的地球平面地图一样,都是地球在平面上的投影。简单来说,投影可定义为纬度/经度值在投影地图的坐标上的映射。

Maps JavaScript API 中的投影必须实现 Projection 接口。Projection 实现必须能提供坐标系之间的单向映射和双向映射。也就是说,您必须定义地球坐标 (LatLng) 和 Projection世界坐标系之间双向转换的方法。Google 地图使用墨卡托投影法来根据地理数据创建地图,并将地图上的事件转换为地理坐标。您可以通过在 Map(或任何标准的基本 MapType)上调用 getProjection() 以获取该投影。对于大部分用途来说,该标准 Projection 已经足够,不过您也可以定义和使用您自己的自定义投影。

实现投影

在实现自定义投影时,您需要定义以下内容:

  • 用于实现纬度和经度坐标与笛卡尔平面之间双向映射的公式。(Projection 接口仅支持向直线坐标的转换)。
  • 基本图块大小。所有图块必须为矩形。
  • 缩放级别为 0 且使用基本图块集的地图的“世界大小”。请注意,对于缩放级别为 0 且仅由一个图块构成的地图,其世界大小和基本图块大小是相同的。

投影中的坐标转换

每个投影都提供了两种方法,让您可以在地理坐标和世界坐标这两个坐标系之间进行转换:

  • Projection.fromLatLngToPoint() 方法将 LatLng 值转换为世界坐标。此方法用于在地图上定位叠层(同时定位地图本身)。
  • Projection.fromPointToLatLng() 方法将世界坐标转换为 LatLng 值。此方法用于将地图上发生的事件(如点击)转换为地理坐标。

Google 地图假设投影是直线的。

通常,您可以在两种情况下使用投影:创建世界地图或创建局部区域地图。在前一种情况下,您应确保投影在所有经度上都为直线且与经度垂直。某些投影(尤其是圆锥投影)可能为“局部垂直”(即指向北方),例如,当该地图定位相对于某些参考经度较远时就会偏离正北。您可以在局部区域使用此类投影,但请注意,该投影肯定是不精确的,且越偏离参考经度,转换误差就会越明显。

投影中的地图图块选择

投影不仅可用于确定位置或叠层的位置,还可用于定位地图图块本身。Google Maps JavaScript API 使用 MapType 接口来呈现基本地图,该接口必须同时声明 projection 属性(用于识别地图的投影)和 getTile() 方法(用于根据图块坐标值检索地图图块)。图块坐标以您的基本图块大小(必须为矩形)和地图的“世界大小”(缩放级别为 0 时的地图世界的像素大小)为基础。(对于缩放级别为 0 且仅由一个图块构成的地图,其图块大小和世界大小是相等的)。

您可以在 MapTypetileSize 属性内定义基本图块大小。在投影的 fromLatLngToPoint()fromPointToLatLng() 方法中隐式定义世界大小。

由于需要根据这些传递的值选择图像,因此,为图像命名可方便系统基于给定的传递值以编程方式选择图像,如 map_zoom_tileX_tileY.png

以下示例定义了一个使用 Gall-Peters 投影法的 ImageMapType

// This example defines an image map type using the Gall-Peters
// projection.
// https://en.wikipedia.org/wiki/Gall%E2%80%93Peters_projection

function initMap() {
  // Create a map. Use the Gall-Peters map type.
  var map = new google.maps.Map(document.getElementById('map'), {
    zoom: 0,
    center: {lat: 0, lng: 0},
    mapTypeControl: false
  });

  initGallPeters();
  map.mapTypes.set('gallPeters', gallPetersMapType);
  map.setMapTypeId('gallPeters');

  // Show the lat and lng under the mouse cursor.
  var coordsDiv = document.getElementById('coords');
  map.controls[google.maps.ControlPosition.TOP_CENTER].push(coordsDiv);
  map.addListener('mousemove', function(event) {
    coordsDiv.textContent =
        'lat: ' + Math.round(event.latLng.lat()) + ', ' +
        'lng: ' + Math.round(event.latLng.lng());
  });

  // Add some markers to the map.
  map.data.setStyle(function(feature) {
    return {
      title: feature.getProperty('name'),
      optimized: false
    };
  });
  map.data.addGeoJson(cities);
}

var gallPetersMapType;
function initGallPeters() {
  var GALL_PETERS_RANGE_X = 800;
  var GALL_PETERS_RANGE_Y = 512;

  // Fetch Gall-Peters tiles stored locally on our server.
  gallPetersMapType = new google.maps.ImageMapType({
    getTileUrl: function(coord, zoom) {
      var scale = 1 << zoom;

      // Wrap tiles horizontally.
      var x = ((coord.x % scale) + scale) % scale;

      // Don't wrap tiles vertically.
      var y = coord.y;
      if (y < 0 || y >= scale) return null;

      return 'https://developers.google.com/maps/documentation/' +
             'javascript/examples/full/images/gall-peters_' + zoom +
             '_' + x + '_' + y + '.png';
    },
    tileSize: new google.maps.Size(GALL_PETERS_RANGE_X, GALL_PETERS_RANGE_Y),
    isPng: true,
    minZoom: 0,
    maxZoom: 1,
    name: 'Gall-Peters'
  });

  // Describe the Gall-Peters projection used by these tiles.
  gallPetersMapType.projection = {
    fromLatLngToPoint: function(latLng) {
      var latRadians = latLng.lat() * Math.PI / 180;
      return new google.maps.Point(
          GALL_PETERS_RANGE_X * (0.5 + latLng.lng() / 360),
          GALL_PETERS_RANGE_Y * (0.5 - 0.5 * Math.sin(latRadians)));
    },
    fromPointToLatLng: function(point, noWrap) {
      var x = point.x / GALL_PETERS_RANGE_X;
      var y = Math.max(0, Math.min(1, point.y / GALL_PETERS_RANGE_Y));

      return new google.maps.LatLng(
          Math.asin(1 - 2 * y) * 180 / Math.PI,
          -180 + 360 * x,
          noWrap);
    }
  };
}

// GeoJSON, describing the locations and names of some cities.
var cities = {
  type: 'FeatureCollection',
  features: [{
    type: 'Feature',
    geometry: {type: 'Point', coordinates: [-87.650, 41.850]},
    properties: {name: 'Chicago'}
  }, {
    type: 'Feature',
    geometry: {type: 'Point', coordinates: [-149.900, 61.218]},
    properties: {name: 'Anchorage'}
  }, {
    type: 'Feature',
    geometry: {type: 'Point', coordinates: [-99.127, 19.427]},
    properties: {name: 'Mexico City'}
  }, {
    type: 'Feature',
    geometry: {type: 'Point', coordinates: [-0.126, 51.500]},
    properties: {name: 'London'}
  }, {
    type: 'Feature',
    geometry: {type: 'Point', coordinates: [28.045, -26.201]},
    properties: {name: 'Johannesburg'}
  }, {
    type: 'Feature',
    geometry: {type: 'Point', coordinates: [15.322, -4.325]},
    properties: {name: 'Kinshasa'}
  }, {
    type: 'Feature',
    geometry: {type: 'Point', coordinates: [151.207, -33.867]},
    properties: {name: 'Sydney'}
  }, {
    type: 'Feature',
    geometry: {type: 'Point', coordinates: [0, 0]},
    properties: {name: '0°N 0°E'}
  }]
};
<div id="map"></div>
<div id="coords"></div>
#coords {
  background-color: black;
  color: white;
  padding: 5px;
}
<!-- Replace the value of the key parameter with your own API key. -->
<script async defer src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap"></script>
// This example defines an image map type using the Gall-Peters
// projection.
// https://en.wikipedia.org/wiki/Gall%E2%80%93Peters_projection

function initMap() {
  // Create a map. Use the Gall-Peters map type.
  var map = new google.maps.Map(document.getElementById('map'), {
    zoom: 0,
    center: {lat: 0, lng: 0},
    mapTypeControl: false
  });

  initGallPeters();
  map.mapTypes.set('gallPeters', gallPetersMapType);
  map.setMapTypeId('gallPeters');

  // Show the lat and lng under the mouse cursor.
  var coordsDiv = document.getElementById('coords');
  map.controls[google.maps.ControlPosition.TOP_CENTER].push(coordsDiv);
  map.addListener('mousemove', function(event) {
    coordsDiv.textContent =
        'lat: ' + Math.round(event.latLng.lat()) + ', ' +
        'lng: ' + Math.round(event.latLng.lng());
  });

  // Add some markers to the map.
  map.data.setStyle(function(feature) {
    return {
      title: feature.getProperty('name'),
      optimized: false
    };
  });
  map.data.addGeoJson(cities);
}

var gallPetersMapType;
function initGallPeters() {
  var GALL_PETERS_RANGE_X = 800;
  var GALL_PETERS_RANGE_Y = 512;

  // Fetch Gall-Peters tiles stored locally on our server.
  gallPetersMapType = new google.maps.ImageMapType({
    getTileUrl: function(coord, zoom) {
      var scale = 1 << zoom;

      // Wrap tiles horizontally.
      var x = ((coord.x % scale) + scale) % scale;

      // Don't wrap tiles vertically.
      var y = coord.y;
      if (y < 0 || y >= scale) return null;

      return 'https://developers.google.com/maps/documentation/' +
             'javascript/examples/full/images/gall-peters_' + zoom +
             '_' + x + '_' + y + '.png';
    },
    tileSize: new google.maps.Size(GALL_PETERS_RANGE_X, GALL_PETERS_RANGE_Y),
    isPng: true,
    minZoom: 0,
    maxZoom: 1,
    name: 'Gall-Peters'
  });

  // Describe the Gall-Peters projection used by these tiles.
  gallPetersMapType.projection = {
    fromLatLngToPoint: function(latLng) {
      var latRadians = latLng.lat() * Math.PI / 180;
      return new google.maps.Point(
          GALL_PETERS_RANGE_X * (0.5 + latLng.lng() / 360),
          GALL_PETERS_RANGE_Y * (0.5 - 0.5 * Math.sin(latRadians)));
    },
    fromPointToLatLng: function(point, noWrap) {
      var x = point.x / GALL_PETERS_RANGE_X;
      var y = Math.max(0, Math.min(1, point.y / GALL_PETERS_RANGE_Y));

      return new google.maps.LatLng(
          Math.asin(1 - 2 * y) * 180 / Math.PI,
          -180 + 360 * x,
          noWrap);
    }
  };
}

// GeoJSON, describing the locations and names of some cities.
var cities = {
  type: 'FeatureCollection',
  features: [{
    type: 'Feature',
    geometry: {type: 'Point', coordinates: [-87.650, 41.850]},
    properties: {name: 'Chicago'}
  }, {
    type: 'Feature',
    geometry: {type: 'Point', coordinates: [-149.900, 61.218]},
    properties: {name: 'Anchorage'}
  }, {
    type: 'Feature',
    geometry: {type: 'Point', coordinates: [-99.127, 19.427]},
    properties: {name: 'Mexico City'}
  }, {
    type: 'Feature',
    geometry: {type: 'Point', coordinates: [-0.126, 51.500]},
    properties: {name: 'London'}
  }, {
    type: 'Feature',
    geometry: {type: 'Point', coordinates: [28.045, -26.201]},
    properties: {name: 'Johannesburg'}
  }, {
    type: 'Feature',
    geometry: {type: 'Point', coordinates: [15.322, -4.325]},
    properties: {name: 'Kinshasa'}
  }, {
    type: 'Feature',
    geometry: {type: 'Point', coordinates: [151.207, -33.867]},
    properties: {name: 'Sydney'}
  }, {
    type: 'Feature',
    geometry: {type: 'Point', coordinates: [0, 0]},
    properties: {name: '0°N 0°E'}
  }]
};

查看示例 (map-projection-simple.html)

发送以下问题的反馈:

此网页
Google Maps JavaScript API
Google Maps JavaScript API
需要帮助?请访问我们的支持页面