本文件說明可使用 Maps JavaScript API 顯示的地圖類型。API 會使用 MapType
物件保留這些地圖的相關資訊。MapType
是一個介面,可以定義地圖圖塊的顯示與使用方式,以及將座標系統從螢幕座標轉換成地圖上的世界座標。每個 MapType
都必須包含一些方法,用於處理圖塊的擷取與釋出作業,以及提供定義視覺行為的屬性。
Maps JavaScript API 中地圖類型的內部運作方式屬於進階主題。多數開發人員會使用下方所述的基本地圖類型,不過,您也可以使用樣式化地圖修改現有地圖類型的呈現方式,或使用自訂地圖類型定義自己的地圖圖塊。請先瞭解如何修改地圖的地圖類型登錄,再提供自訂地圖類型。
基本地圖類型
Maps JavaScript API 中提供四種類型的地圖。除了常見的「繪製」道路地圖圖塊以外,Maps JavaScript API 也支援其他地圖類型。
Maps JavaScript API 提供下列地圖類型:
roadmap
會顯示預設道路地圖檢視。這是預設的地圖類型。satellite
會顯示 Google 地球衛星影像。hybrid
會顯示一般檢視和衛星檢視的混合畫面。terrain
會根據地形資訊顯示實際地圖。
您可以透過設定 mapTypeId
屬性,修改 Map
使用的地圖類型,具體作法是在建構函式中設定 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');
請注意,實際上您並非直接設定地圖的地圖類型,而是使用 ID 將 mapTypeId
設為參照 MapType
。Maps JavaScript API 會使用下方所述的地圖類型登錄管理這些參照。
45° 圖像
Maps JavaScript API 可呈現特定位置的特殊 45° 圖像。這張高解析度圖像提供朝向每個方位 (東西南北) 的透視檢視畫面。在支援的地圖類型中,這些影像都能提供更高的縮放等級。
下圖顯示紐約市的 45° 透視檢視畫面:
satellite
和 hybrid
地圖類型支援高倍縮放等級 (12 以上) 的 45° 圖像 (如有提供)。如果使用者放大的地點包含這類圖像,這些地圖類型會透過下列方式自動變更檢視畫面:
- 衛星圖像和混合圖像會由 45° 透視圖像取代,並以目前地點為中心。這類檢視畫面預設為朝向北方。如果使用者縮小地圖,預設衛星圖像或混合圖像就會再次顯示。地圖行為會因縮放等級和
tilt
的值而有所不同: - 根據預設,縮放等級介於 12 到 18 時,除非
tilt
設為 45,否則會顯示俯瞰的基本地圖 (0°)。 - 縮放等級為 18 以上時,除非
tilt
設為 0,否則會顯示 45° 基本地圖。 - 旋轉控制項變為可見狀態。使用者可以使用旋轉控制項提供的選項切換傾斜度,以及沿任一方向以 90° 為增量旋轉檢視畫面。如要隱藏旋轉控制項,請將
rotateControl
設為false
。
如果縮小顯示 45° 圖像的地圖類型,會還原上述所有變更,重新建立原本的地圖類型。
啟用及停用 45° 圖像
針對 Map
物件呼叫 setTilt(0)
,即可停用 45° 圖像。如要在支援的地圖類型上啟用 45° 圖像功能,請呼叫 setTilt(45)
。Map
的 getTilt()
方法一律會反映地圖上目前顯示的 tilt
。如果您在地圖上設定 tilt
,之後又移除該 tilt
(例如縮小地圖),地圖的 getTilt()
方法會傳回 0
。
重要事項:45° 圖像僅支援光柵地圖,無法用於向量地圖。
以下範例顯示紐約市的 45° 檢視畫面:
TypeScript
function initMap(): void { const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { center: { lat: 40.76, lng: -73.983 }, zoom: 15, mapTypeId: "satellite", } ); map.setTilt(45); } declare global { interface Window { initMap: () => void; } } window.initMap = initMap;
JavaScript
function initMap() { const map = new google.maps.Map(document.getElementById("map"), { center: { lat: 40.76, lng: -73.983 }, zoom: 15, mapTypeId: "satellite", }); map.setTilt(45); } window.initMap = initMap;
測試範例程式碼
查看範例。
旋轉 45° 圖像
45° 圖像實際上是由每個方位 (東西南北) 的圖片集合所組成。只要地圖顯示 45° 圖像,您便可針對 Map
物件呼叫 setHeading()
,並傳遞相對於北方的角度數值,藉此將圖像設為朝向任一方位。
以下範例是一幅空照圖,使用者按下按鈕後,地圖每隔 3 秒會自動旋轉一次:
TypeScript
let map: google.maps.Map; function initMap(): void { map = new google.maps.Map(document.getElementById("map") as HTMLElement, { center: { lat: 40.76, lng: -73.983 }, zoom: 15, mapTypeId: "satellite", heading: 90, tilt: 45, }); // add listener to button document.getElementById("rotate")!.addEventListener("click", autoRotate); } function rotate90(): void { const heading = map.getHeading() || 0; map.setHeading(heading + 90); } function autoRotate(): void { // Determine if we're showing aerial imagery. if (map.getTilt() !== 0) { window.setInterval(rotate90, 3000); } } declare global { interface Window { initMap: () => void; } } window.initMap = initMap;
JavaScript
let map; function initMap() { map = new google.maps.Map(document.getElementById("map"), { center: { lat: 40.76, lng: -73.983 }, zoom: 15, mapTypeId: "satellite", heading: 90, tilt: 45, }); // add listener to button document.getElementById("rotate").addEventListener("click", autoRotate); } function rotate90() { const 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); } } window.initMap = initMap;
測試範例程式碼
查看範例。
修改地圖類型登錄
地圖的 mapTypeId
是字串 ID,可用於將 MapType
與唯一值建立關聯。每個 Map
物件都有一個 MapTypeRegistry
,其中包含一組該地圖可使用的 MapType
。這項登錄可用來選取地圖類型,例如地圖的 MapType 控制項中可使用的地圖類型。
您無法直接讀取地圖類型登錄,而是必須新增自訂地圖類型,並與您選擇的字串 ID 建立關聯,才能修改登錄。您無法修改或變更基本地圖類型 (但可以變更地圖相關 mapTypeControlOptions
的呈現方式,從地圖中移除這個類型)。
下列程式碼會將地圖設為只在地圖的 mapTypeControlOptions
中顯示兩種地圖類型,且會修改登錄,將這個 ID 的關聯加進 MapType
介面的實際導入作業中。
// Modify the control to only display two maptypes, the // default ROADMAP and the custom 'mymap'. // Note that because this is 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
,請參閱樣式化地圖指南。
自訂地圖類型
Maps JavaScript API 支援顯示及管理自訂地圖類型,可供您導入自己的地圖圖像或圖塊疊加層。
Maps JavaScript API 提供多種可導入的地圖類型:
- 標準「圖塊集」,由共同構成完整地圖的圖像所組成。圖塊集又稱為「基本」地圖類型。這種地圖類型的行為及運作方式與現有預設地圖類型 (
roadmap
、satellite
、hybrid
和terrain
) 相同。您可以將自訂地圖類型新增至地圖的mapTypes
陣列中,這樣就能讓 Maps JavaScript API 中的使用者介面,將自訂地圖類型視為標準地圖類型 (例如透過將自訂地圖類型加進 MapType 控制項中)。 - 影像圖塊「疊加層」,顯示在現有基本地圖類型上方。一般而言,這種地圖類型是用來補充現有地圖類型不足的部分,可顯示額外資訊且範圍通常限於特定位置和/或縮放等級。請注意,這些圖塊可能是透明圖塊,以便您在現有地圖上新增地圖項目。
- 非影像式地圖類型,您可以在最基礎的層級處理地圖資訊的呈現方式。
您必須建立用於導入 MapType
介面的類別,才能使用上述所有選項。此外,ImageMapType
類別提供一些內建行為,可以簡化圖像地圖類型的建立過程。
MapType
介面
請務必先瞭解 Google 地圖如何判斷座標,以及決定要顯示地圖的哪些部分,再建立導入 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 會依據
MapType
的tileSize
、minZoom
與maxZoom
屬性,以及地圖的目前可視區域與縮放等級,判斷是否需要呼叫getTile()
。此方法的處理常式會根據傳遞的座標、縮放等級,以及要附加圖塊圖片的 DOM 元素,傳回 HTML 元素。 -
releaseTile()
(選用):API 判斷地圖需要移除不在可視區域內的圖塊時,就會呼叫這個方法。此方法必須包含下列簽章:releaseTile(tile:Node)
一般而言,在地圖中加入地圖圖塊時,需要移除附加在圖塊中的任何元素。舉例來說,如果您在地圖圖塊疊加層中附加事件監聽器,應該在此移除。
getTile()
方法可做為主要控制器,用於判斷特定可視區域內需載入哪些圖塊。
基本地圖類型
您依照此方法建構的地圖類型可獨立使用,也可做為疊加層與其他地圖類型結合使用。獨立地圖類型稱為「基本地圖類型」。您可能希望 API 將這類自訂 MapType
與任何其他現有的基本地圖類型 (ROADMAP
、TERRAIN
等) 一視同仁。如要這樣做,請將自訂 MapType
新增至 Map
的 mapTypes
屬性。此屬性的類型為 MapTypeRegistry
。
下方程式碼會建立基本 MapType
來顯示地圖圖塊座標,以及繪製圖塊輪廓:
TypeScript
/* * 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. */ class CoordMapType { tileSize: google.maps.Size; maxZoom = 19; name = "Tile #s"; alt = "Tile Coordinate Map Type"; constructor(tileSize: google.maps.Size) { this.tileSize = tileSize; } getTile( coord: google.maps.Point, zoom: number, ownerDocument: Document ): HTMLElement { const div = ownerDocument.createElement("div"); div.innerHTML = String(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; } releaseTile(tile: HTMLElement): void {} } function initMap(): void { const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { zoom: 10, center: { lat: 41.85, lng: -87.65 }, streetViewControl: false, mapTypeId: "coordinate", mapTypeControlOptions: { mapTypeIds: ["coordinate", "roadmap"], style: google.maps.MapTypeControlStyle.DROPDOWN_MENU, }, } ); map.addListener("maptypeid_changed", () => { const showStreetViewControl = (map.getMapTypeId() as string) !== "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)) ); } declare global { interface Window { initMap: () => void; } } window.initMap = initMap;
JavaScript
/* * 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. */ class CoordMapType { tileSize; maxZoom = 19; name = "Tile #s"; alt = "Tile Coordinate Map Type"; constructor(tileSize) { this.tileSize = tileSize; } getTile(coord, zoom, ownerDocument) { const div = ownerDocument.createElement("div"); div.innerHTML = String(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; } releaseTile(tile) {} } function initMap() { const map = new google.maps.Map(document.getElementById("map"), { zoom: 10, center: { lat: 41.85, lng: -87.65 }, streetViewControl: false, mapTypeId: "coordinate", mapTypeControlOptions: { mapTypeIds: ["coordinate", "roadmap"], style: google.maps.MapTypeControlStyle.DROPDOWN_MENU, }, }); map.addListener("maptypeid_changed", () => { const 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)), ); } window.initMap = initMap;
測試範例程式碼
疊加層地圖類型
部分地圖類型的設計是為了在現有地圖類型上方運作。這種地圖類型可能含有透明圖層,用來表示搜尋點或向使用者顯示額外資料。
在這種情況下,您會希望系統將地圖類型視為疊加層,而不是個別實體。只要使用 Map
的 overlayMapTypes
屬性,直接將地圖類型新增至現有的 MapType
,即可達到這項效果。這個屬性包含 MapType
的 MVCArray
。所有地圖類型 (基本和疊加層) 都會在 mapPane
圖層內進行算繪。疊加層地圖類型會顯示在所附加的基本地圖上方,順序與在 Map.overlayMapTypes
陣列中的顯示順序相同 (索引值較高的疊加層會顯示在索引值較低的疊加層前方)。
以下範例與前一個範例完全相同,唯一不同之處是我們在 ROADMAP
地圖類型上方建立圖塊疊加層 MapType
:
TypeScript
/* * 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. */ class CoordMapType implements google.maps.MapType { tileSize: google.maps.Size; alt: string|null = null; maxZoom: number = 17; minZoom: number = 0; name: string|null = null; projection: google.maps.Projection|null = null; radius: number = 6378137; constructor(tileSize: google.maps.Size) { this.tileSize = tileSize; } getTile( coord: google.maps.Point, zoom: number, ownerDocument: Document ): HTMLElement { const div = ownerDocument.createElement("div"); div.innerHTML = String(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; } releaseTile(tile: Element): void {} } function initMap(): void { const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { zoom: 10, center: { lat: 41.85, lng: -87.65 }, } ); // 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. const coordMapType = new CoordMapType(new google.maps.Size(256, 256)) map.overlayMapTypes.insertAt( 0, coordMapType ); } declare global { interface Window { initMap: () => void; } } window.initMap = initMap;
JavaScript
/* * 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. */ class CoordMapType { tileSize; alt = null; maxZoom = 17; minZoom = 0; name = null; projection = null; radius = 6378137; constructor(tileSize) { this.tileSize = tileSize; } getTile(coord, zoom, ownerDocument) { const div = ownerDocument.createElement("div"); div.innerHTML = String(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; } releaseTile(tile) {} } function initMap() { const map = new google.maps.Map(document.getElementById("map"), { zoom: 10, center: { lat: 41.85, lng: -87.65 }, }); // 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. const coordMapType = new CoordMapType(new google.maps.Size(256, 256)); map.overlayMapTypes.insertAt(0, coordMapType); } window.initMap = initMap;
測試範例程式碼
影像地圖類型
導入 MapType
做為基本地圖類型的做法可能非常耗時又費神。API 針對最常見的地圖類型提供導入 MapType
介面的特殊類別,這種地圖類型由包含單一圖片檔的圖塊所組成。
此類別 (ImageMapType
類別) 使用定義下列必要欄位屬性的 ImageMapTypeOptions
物件規格建構而成:
tileSize
(必要) 指定圖塊尺寸 (類型為google.maps.Size
)。尺寸必須是矩形,但不必是正方形。getTileUrl
(必要) 指定函式 (通常以內嵌函式常值形式提供),以根據提供的世界座標及縮放等級,處理適用影像圖塊的選擇作業。
下方程式碼會導入使用 Google 月球圖塊的基本 ImageMapType
。這個範例使用正規化函式,確保圖塊會沿著地圖上的 x 軸重複顯示,而不是 y 軸。
TypeScript
function initMap(): void { const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { center: { lat: 0, lng: 0 }, zoom: 1, streetViewControl: false, mapTypeControlOptions: { mapTypeIds: ["moon"], }, } ); const moonMapType = new google.maps.ImageMapType({ getTileUrl: function (coord, zoom): string { const normalizedCoord = getNormalizedCoord(coord, zoom); if (!normalizedCoord) { return ""; } const bound = Math.pow(2, zoom); return ( "https://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, // @ts-ignore TODO 'radius' does not exist in type 'ImageMapTypeOptions' 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) { const y = coord.y; let 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 const 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 }; } declare global { interface Window { initMap: () => void; } } window.initMap = initMap;
JavaScript
function initMap() { const map = new google.maps.Map(document.getElementById("map"), { center: { lat: 0, lng: 0 }, zoom: 1, streetViewControl: false, mapTypeControlOptions: { mapTypeIds: ["moon"], }, }); const moonMapType = new google.maps.ImageMapType({ getTileUrl: function (coord, zoom) { const normalizedCoord = getNormalizedCoord(coord, zoom); if (!normalizedCoord) { return ""; } const bound = Math.pow(2, zoom); return ( "https://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, // @ts-ignore TODO 'radius' does not exist in type 'ImageMapTypeOptions' 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) { const y = coord.y; let 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 const 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 }; } window.initMap = initMap;
測試範例程式碼
投影
地球概略上來說是一個 3D 球體,而地圖則是 2D 平面。您在 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 地圖假設投影為直線。
一般來說,您會在建立世界地圖或區域地圖時使用投影。如要建立世界地圖,您必須確定投影也是直線,而且在所有經度上皆正常。舉例來說,有些投影 (特別是圓錐投影) 可能會「局部正常」(即指向北方),但卻偏離正北方。在此情況下,地圖就必須另外再以某個參照經度做為相對的定位基準。您可以在局部使用這類投影,但請注意,投影必會出現不精確的狀況,且偏離參照經度愈遠的位置,轉換錯誤就會變得愈明顯。
投影中的地圖圖塊選擇項目
投影不只可以在判定位置或疊加層位置時使用,也可用於設定地圖圖塊本身的位置。Maps JavaScript API 會使用 MapType
介面算繪基本地圖,而此介面必須宣告 projection
屬性指定地圖投影方式,並宣告 getTile()
方法來根據圖塊座標值擷取地圖圖塊。地圖圖塊座標會以基本圖塊尺寸 (必須是矩形),和地圖的「世界尺寸」(亦即縮放等級為 0 時,地圖世界的像素尺寸) 為基礎 (如果地圖的縮放等級為 0 且由一個圖塊組成,則地圖的圖塊尺寸與世界尺寸相同)。
基本圖塊尺寸可以在 MapType
的 tileSize
屬性中定義。您可以在投影的 fromLatLngToPoint()
與 fromPointToLatLng()
方法中,隱含定義世界尺寸。
系統是根據傳遞的值選取圖片,因此建議您根據這些傳遞的值 (如 map_zoom_tileX_tileY.png
),為可透過程式輔助方式選取的圖片命名。
以下範例使用高爾-彼得斯投影定義 ImageMapType
:
TypeScript
// This example defines an image map type using the Gall-Peters // projection. // https://en.wikipedia.org/wiki/Gall%E2%80%93Peters_projection function initMap(): void { // Create a map. Use the Gall-Peters map type. const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { 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. const coordsDiv = document.getElementById("coords") as HTMLElement; map.controls[google.maps.ControlPosition.TOP_CENTER].push(coordsDiv); map.addListener("mousemove", (event: google.maps.MapMouseEvent) => { coordsDiv.textContent = "lat: " + Math.round(event.latLng!.lat()) + ", " + "lng: " + Math.round(event.latLng!.lng()); }); // Add some markers to the map. map.data.setStyle((feature) => { return { title: feature.getProperty("name") as string, optimized: false, }; }); map.data.addGeoJson(cities); } let gallPetersMapType; function initGallPeters() { const GALL_PETERS_RANGE_X = 800; const GALL_PETERS_RANGE_Y = 512; // Fetch Gall-Peters tiles stored locally on our server. gallPetersMapType = new google.maps.ImageMapType({ getTileUrl: function (coord, zoom) { const scale = 1 << zoom; // Wrap tiles horizontally. const x = ((coord.x % scale) + scale) % scale; // Don't wrap tiles vertically. const y = coord.y; if (y < 0 || y >= scale) return ""; 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), minZoom: 0, maxZoom: 1, name: "Gall-Peters", }); // Describe the Gall-Peters projection used by these tiles. gallPetersMapType.projection = { fromLatLngToPoint: function (latLng) { const 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) { const x = point.x / GALL_PETERS_RANGE_X; const 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. const cities = { type: "FeatureCollection", features: [ { type: "Feature", geometry: { type: "Point", coordinates: [-87.65, 41.85] }, properties: { name: "Chicago" }, }, { type: "Feature", geometry: { type: "Point", coordinates: [-149.9, 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.5] }, 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" }, }, ], }; declare global { interface Window { initMap: () => void; } } window.initMap = initMap;
JavaScript
// 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. const 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. const coordsDiv = document.getElementById("coords"); map.controls[google.maps.ControlPosition.TOP_CENTER].push(coordsDiv); map.addListener("mousemove", (event) => { coordsDiv.textContent = "lat: " + Math.round(event.latLng.lat()) + ", " + "lng: " + Math.round(event.latLng.lng()); }); // Add some markers to the map. map.data.setStyle((feature) => { return { title: feature.getProperty("name"), optimized: false, }; }); map.data.addGeoJson(cities); } let gallPetersMapType; function initGallPeters() { const GALL_PETERS_RANGE_X = 800; const GALL_PETERS_RANGE_Y = 512; // Fetch Gall-Peters tiles stored locally on our server. gallPetersMapType = new google.maps.ImageMapType({ getTileUrl: function (coord, zoom) { const scale = 1 << zoom; // Wrap tiles horizontally. const x = ((coord.x % scale) + scale) % scale; // Don't wrap tiles vertically. const y = coord.y; if (y < 0 || y >= scale) return ""; 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), minZoom: 0, maxZoom: 1, name: "Gall-Peters", }); // Describe the Gall-Peters projection used by these tiles. gallPetersMapType.projection = { fromLatLngToPoint: function (latLng) { const 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) { const x = point.x / GALL_PETERS_RANGE_X; const 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. const cities = { type: "FeatureCollection", features: [ { type: "Feature", geometry: { type: "Point", coordinates: [-87.65, 41.85] }, properties: { name: "Chicago" }, }, { type: "Feature", geometry: { type: "Point", coordinates: [-149.9, 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.5] }, 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" }, }, ], }; window.initMap = initMap;