地圖類型
本文件討論可以使用 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可顯示混合的一般檢視與衛星檢視hybrid可根據地形資訊顯示實體地圖
設定 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° 影像。這種高解析度影像提供面對每個主要方向(東、南、西、北)的視角檢視。對於支援的地圖類型,這些影像提供更高的縮放層級。
下圖顯示位於加州聖塔克魯茲 Broadwalk 的 45° 視角檢視:
satellite 與 hybrid 地圖類型支援高縮放層級(若可用)的 45° 影像。如果使用者放大檢視提供這種影像的位置,這些地圖類型會自動以下列方式修改它們的檢視:
- 衛星或混合影像會以目前位置做為中心,以 45° 視角的影像取代。根據預設,這類檢視的方向是朝北。如果使用者縮小視圖,會再次顯示預設的衛星或混合影像。
- 旋轉控制項會提供傾斜和旋轉選項的組合。如果
rotateControl為true,則可使用 45° 影像時會出現傾斜控制項。傾斜控制項可讓使用者將影像傾斜 45° 角。 - 當影像傾斜時,會出現一個勾勾,讓使用者以順時針方向將檢視旋轉 90°。
從顯示 45° 影像的地圖類型放大會個別回復這些變更,重新建立原始的地圖類型。
啟用和停用 45° 影像
在 Map 物件上呼叫 setTilt(0) 即可停用 45° 影像。如果要為支援的地圖類型啟用 45° 影像,請呼叫 setTilt(45)。
Map 的 getTilt() 方法一律會反映出地圖上目前顯示的傾斜度;如果您在地圖上設定傾斜,之後又移除該傾斜(例如,縮小地圖),則地圖的 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);
}
旋轉 45° 影像
45° 影像實際上是由每個主要方向(東、南、西、北)的影像集合所組成。地圖顯示 45° 影像之後,您可以在 Map 物件上呼叫 setHeading(),傳入一個以從北方傾斜角度表示的數值,引導影像朝向其中一個主要方向。
下列範例顯示一個空照圖,按一下按鈕之後,每三秒鐘就會自動旋轉地圖:
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);
}
}
修改地圖類型登錄
地圖的 mapTypeId 是字串識別碼,使用唯一值與 MapType 關聯。每個 Map 物件維護一個 MapTypeRegistry,其中包含該地圖可用的 MapType 的集合。這個登錄的用途是選取地圖類型,例如 Map 的 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 內有數個可能的地圖類型實作:
- 標準地圖方塊集,由共同構成完整地圖製圖的影像組成。這些地圖方塊集也稱為基本地圖類型。這些地圖類型的操作行為就像現有的預設地圖類型一樣:
roadmap、satellite、hybrid與terrain。您可以將自訂地圖類型新增到 Map 的mapTypes陣列,允許 Maps JavaScript API 內的 UI 將您的自訂地圖類型當做標準地圖類型(例如,將它包含在 MapType 控制項內)。 - 影像地圖方塊「疊加層」,顯示在現有基本地圖類型的上層。這些地圖類型一般是用來增強現有地圖類型以顯示其他資訊,通常只限於特定位置和/或縮放層級。請注意,這些地圖方塊可能是透明的,可讓您將功能新增到現有地圖。
- 非影像地圖類型,可讓您操作最基本層級的地圖資訊的顯示。
這些選項個別都需要建立實作 MapType 介面的類別。此外, ImageMapType 類別提供一些內建行為,簡化了影像 MapType 的建立。
在我們說明實作 MapType 的類別之前,必須先瞭解 Google 地圖如何判斷座標以及如何決定要顯示地圖哪些部分。您需要為任何基本或疊加層 MapType 實作類似的邏輯。
地圖座標
Google Maps JavaScript API 使用數種座標系統。
- 參照世界上唯一某個點的緯度與經度值。(Google 使用 World Geodetic System 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
從上述的等式中,請注意在 x 軸與 y 軸兩個方向上每個增加的縮放層級都是兩倍大。因為,每個更高縮放層級包含的解析度是前一個層級的四倍。例如,在縮放層級 1,地圖由 4 個 256x256 像素地圖方塊組成,因而產生 512x512 的像素空間。在縮放層級 19,地圖上每個 x 和 y 像素,可以使用介於 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 值、世界座標、像素座標及地圖方塊座標。
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):NodeAPI 判斷它是否需要呼叫
getTile(),取決於MapType的tileSize、minZoom和maxZoom屬性,以及地圖目前的檢視點和縮放層級。這個方法的處理常式會傳回一個 HTML 元素,其中包含傳遞的座標、縮放層級和 DOM 元素,上面附加了地圖方塊影像。 -
releaseTile()(選擇性),只要 API 判斷地圖需要移除離開檢視的地圖方塊時,就會呼叫這個方法。這個方法必須包含下列簽章:releaseTile(tile:Node)您通常要在新增物件到地圖時處理移除附加到地圖方塊的任何元素。例如,如果您將事件接聽程式附加到地圖方塊疊加層,則應該在這裡移除它們。
getTile() 方法是判斷指定檢視點中要載入哪些地圖方塊的主要控制項。
基本地圖類型
以這種方式建構的地圖類型可以獨立使用,或與其他地圖類型組合使用成為疊加層。獨立地圖類型就稱為「基本地圖類型」。您可能希望讓 API 將這類自訂的 MapType 視為與任何其他現有的基本地圖類型 (ROADMAP、TERRAIN 等等)一樣。如果要這樣做,請將您自訂的 MapType 新增到 Map 的 mapTypes 屬性。這個屬性是 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)));
}
疊加層地圖類型
有些地圖類型的設計是在現有地圖類型的上層使用。這類地圖類型可能是對使用者指出搜尋點或顯示其他資訊的透明圖層。
在這些情況中,您不希望將地圖類型視為個別實體。而是要使用 Map 的 overlayMapTypes 屬性,直接將地圖類型新增到現有的 MapType。這個屬性包含 MapType 的 MVCArray。所有地圖類型(基本和疊加層)會在 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 做為基本地圖類型可能耗時費力。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};
}
投影
地球是三維空間的圓球體(大約而言),而地圖是二維空間的平面。您在 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 由一個地圖方塊組成的地圖,它的地圖方塊大小與世界大小是完全一樣的。)
您在 MapType 的 tileSize 屬性內定義基本地圖方塊大小。您在投影的 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'}
}]
};
