Tài liệu này thảo luận về các loại bản đồ mà bạn có thể hiển thị bằng Maps JavaScript API. API này sử dụng một đối tượng MapType
để lưu giữ thông tin về những bản đồ này. MapType
là một giao diện xác định cách hiển thị và sử dụng các ô bản đồ, cũng như cách chuyển đổi hệ toạ độ từ toạ độ màn hình sang toạ độ thế giới (trên bản đồ). Mỗi MapType
phải chứa một số phương thức để xử lý việc truy xuất và phát hành các ô, cũng như các thuộc tính xác định hành vi trực quan của ô.
Cơ chế hoạt động bên trong của các loại bản đồ trong Maps JavaScript API là một chủ đề nâng cao. Hầu hết nhà phát triển đều có thể sử dụng các loại bản đồ cơ bản được lưu ý bên dưới. Tuy nhiên, bạn cũng có thể sửa đổi cách trình bày các loại bản đồ hiện có bằng cách sử dụng Bản đồ có kiểu hoặc xác định ô bản đồ của riêng bạn bằng cách sử dụng các loại bản đồ tuỳ chỉnh. Khi cung cấp các loại bản đồ tuỳ chỉnh, bạn sẽ cần hiểu cách sửa đổi Sổ đăng ký loại bản đồ của bản đồ.
Các loại bản đồ cơ bản
Có 4 loại bản đồ trong Maps JavaScript API. Ngoài các ô bản đồ đường đi "được vẽ" quen thuộc, Maps JavaScript API còn hỗ trợ các loại bản đồ khác.
API JavaScript của Maps cung cấp các loại bản đồ sau:
roadmap
hiển thị chế độ xem bản đồ đường bộ mặc định. Đây là loại bản đồ mặc định.satellite
hiển thị hình ảnh vệ tinh của Google Earth.hybrid
hiển thị kết hợp giữa chế độ xem thông thường và chế độ xem vệ tinh.terrain
hiển thị bản đồ thực tế dựa trên thông tin địa hình.
Bạn sửa đổi loại bản đồ đang được Map
sử dụng bằng cách đặt thuộc tính mapTypeId
của loại bản đồ đó, trong hàm khởi tạo bằng cách đặt đối tượng Map options
hoặc bằng cách gọi phương thức setMapTypeId()
của bản đồ. Thuộc tính mapTypeID
mặc định là roadmap
.
Đặt mapTypeId
khi xây dựng:
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);
Sửa đổi mapTypeId
một cách linh động:
map.setMapTypeId('terrain');
Xin lưu ý rằng bạn không thực sự đặt trực tiếp loại bản đồ của bản đồ, mà thay vào đó, bạn đặt mapTypeId
để tham chiếu đến một MapType
bằng cách sử dụng giá trị nhận dạng.
API JavaScript của Maps sử dụng một sổ đăng ký loại bản đồ (được giải thích bên dưới) để quản lý các thông tin tham chiếu này.
Hình ảnh 45°
Maps JavaScript API hỗ trợ hình ảnh đặc biệt ở góc 45° cho một số vị trí. Hình ảnh có độ phân giải cao này cung cấp chế độ xem phối cảnh theo từng hướng chính (Bắc, Nam, Đông, Tây). Những hình ảnh này có ở mức thu phóng cao hơn đối với các loại bản đồ được hỗ trợ.
Hình ảnh sau đây cho thấy chế độ xem phối cảnh 45° của Thành phố New York:
Các loại bản đồ satellite
và hybrid
hỗ trợ hình ảnh 45° ở mức thu phóng cao (12 trở lên) khi có sẵn. Nếu người dùng phóng to một vị trí có hình ảnh như vậy, các loại bản đồ này sẽ tự động thay đổi chế độ xem theo cách sau:
- Hình ảnh vệ tinh hoặc hình ảnh kết hợp sẽ được thay thế bằng hình ảnh có góc nhìn 45°, tập trung vào vị trí hiện tại. Theo mặc định, các chế độ xem như vậy sẽ hướng về phía bắc. Nếu người dùng thu nhỏ, hình ảnh vệ tinh hoặc hình ảnh kết hợp mặc định sẽ xuất hiện lại. Hành vi này sẽ khác nhau tuỳ thuộc vào mức thu phóng và giá trị của
tilt
: - Giữa mức thu phóng 12 và 18, bản đồ cơ sở từ trên xuống (0°) sẽ hiển thị theo mặc định, trừ phi
tilt
được đặt thành 45. - Ở mức thu phóng từ 18 trở lên, bản đồ cơ sở 45° sẽ hiển thị, trừ phi bạn đặt
tilt
thành 0. - Nút điều khiển xoay sẽ xuất hiện. Nút xoay cung cấp các lựa chọn cho phép người dùng bật/tắt chế độ nghiêng và xoay khung hiển thị theo gia số 90° theo một trong hai hướng. Để ẩn chế độ xoay, hãy đặt
rotateControl
thànhfalse
.
Khi bạn thu nhỏ một loại bản đồ hiển thị hình ảnh 45°, mỗi thay đổi này sẽ được khôi phục, thiết lập lại các loại bản đồ ban đầu.
Bật và tắt hình ảnh 45°
Bạn có thể tắt hình ảnh 45° bằng cách gọi setTilt(0)
trên đối tượng Map
. Để bật hình ảnh 45° cho các loại bản đồ được hỗ trợ, hãy gọi setTilt(45)
. Phương thức getTilt()
của Map
sẽ luôn phản ánh tilt
hiện tại đang hiển thị trên bản đồ; nếu bạn đặt một tilt
trên bản đồ rồi sau đó xoá tilt
đó (ví dụ: bằng cách thu nhỏ bản đồ), phương thức getTilt()
của bản đồ sẽ trả về 0
.
Lưu ý quan trọng: Hình ảnh 45 độ chỉ được hỗ trợ trên bản đồ raster; bạn không thể sử dụng hình ảnh này với bản đồ vectơ.
Ví dụ sau đây cho thấy chế độ xem 45° của Thành phố New York:
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;
Dùng thử mẫu
Xoay hình ảnh 45°
Hình ảnh 45° thực ra bao gồm một tập hợp hình ảnh cho mỗi hướng chính (Bắc, Nam, Đông, Tây). Sau khi bản đồ hiển thị hình ảnh ở góc 45°, bạn có thể định hướng hình ảnh theo một trong các hướng chính bằng cách gọi setHeading()
trên đối tượng Map
, truyền một giá trị số được biểu thị bằng độ từ hướng Bắc.
Ví dụ sau đây cho thấy một bản đồ trên không và tự động xoay bản đồ sau mỗi 3 giây khi người dùng nhấp vào nút:
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;
Dùng thử mẫu
Sửa đổi sổ đăng ký loại bản đồ
mapTypeId
của bản đồ là một giá trị nhận dạng chuỗi được dùng để liên kết MapType
với một giá trị duy nhất. Mỗi đối tượng Map
duy trì một MapTypeRegistry
chứa tập hợp các MapType
có sẵn cho bản đồ đó. Sổ đăng ký này được dùng để chọn các loại bản đồ có trong chế độ điều khiển MapType của Bản đồ, chẳng hạn.
Bạn không đọc trực tiếp từ sổ đăng ký loại bản đồ. Thay vào đó, bạn sửa đổi sổ đăng ký bằng cách thêm các loại bản đồ tuỳ chỉnh và liên kết chúng với một chuỗi nhận dạng mà bạn chọn. Bạn không thể sửa đổi hoặc thay đổi các loại bản đồ cơ bản (mặc dù bạn có thể xoá các loại bản đồ này khỏi bản đồ bằng cách thay đổi giao diện của mapTypeControlOptions
được liên kết với bản đồ).
Đoạn mã sau đây đặt bản đồ chỉ hiển thị 2 loại bản đồ trong mapTypeControlOptions
của bản đồ và sửa đổi sổ đăng ký để thêm mối liên kết với mã nhận dạng này vào quá trình triển khai thực tế của giao diện 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);
Bản đồ được tạo mẫu
StyledMapType
cho phép bạn tuỳ chỉnh cách trình bày các bản đồ cơ sở tiêu chuẩn của Google, thay đổi cách hiển thị trực quan của các phần tử như đường, công viên và khu vực đô thị để phản ánh một kiểu khác với kiểu được dùng trong loại bản đồ mặc định. StyledMapType
chỉ ảnh hưởng đến loại bản đồ roadmap
mặc định.
Để biết thêm thông tin về StyledMapType
, hãy xem phần Sử dụng các khai báo kiểu JSON được nhúng.
Các loại bản đồ tuỳ chỉnh
Maps JavaScript API hỗ trợ việc hiển thị và quản lý các loại bản đồ tuỳ chỉnh, cho phép bạn triển khai hình ảnh bản đồ hoặc lớp phủ ô của riêng mình.
Có một số cách triển khai loại bản đồ có thể có trong Maps JavaScript API:
- Tập hợp ô tiêu chuẩn bao gồm những hình ảnh tạo thành bản đồ địa lý hoàn chỉnh. Các nhóm ô này còn được gọi là các loại bản đồ cơ sở. Các loại bản đồ này hoạt động và có hành vi giống như các loại bản đồ mặc định hiện có:
roadmap
,satellite
,hybrid
vàterrain
. Bạn có thể thêm kiểu bản đồ tuỳ chỉnh vào mảngmapTypes
của Map để cho phép giao diện người dùng trong Maps JavaScript API coi kiểu bản đồ tuỳ chỉnh của bạn là một kiểu bản đồ tiêu chuẩn (bằng cách đưa kiểu đó vào chế độ kiểm soát MapType, chẳng hạn). - Lớp phủ ô hình ảnh hiển thị ở trên các loại bản đồ cơ sở hiện có. Nhìn chung, các loại bản đồ này được dùng để tăng cường một loại bản đồ hiện có nhằm hiển thị thông tin bổ sung và thường bị giới hạn ở các vị trí và/hoặc mức thu phóng cụ thể. Xin lưu ý rằng các ô này có thể trong suốt, cho phép bạn thêm các đối tượng vào bản đồ hiện có.
- Các loại bản đồ không phải là hình ảnh, cho phép bạn thao tác với việc hiển thị thông tin bản đồ ở cấp cơ bản nhất.
Mỗi lựa chọn này đều dựa vào việc tạo một lớp triển khai giao diện MapType
. Ngoài ra, lớp
ImageMapType
cung cấp một số hành vi tích hợp sẵn để đơn giản hoá việc tạo các loại bản đồ hình ảnh.
Giao diện MapType
Trước khi tạo các lớp triển khai MapType
, bạn cần hiểu cách Google Maps xác định toạ độ và quyết định hiển thị những phần nào của bản đồ. Bạn cần triển khai logic tương tự cho mọi loại bản đồ cơ sở hoặc bản đồ lớp phủ.
Đọc hướng dẫn về cách ánh xạ toạ độ và ô.
Các loại bản đồ tuỳ chỉnh phải triển khai giao diện MapType
. Giao diện này chỉ định một số thuộc tính và phương thức nhất định cho phép API bắt đầu các yêu cầu đối với(các) loại bản đồ của bạn khi API xác định rằng cần hiển thị các ô bản đồ trong khung nhìn và mức thu phóng hiện tại. Bạn xử lý các yêu cầu này để quyết định tải ô nào.
Lưu ý: Bạn có thể tạo lớp riêng để triển khai giao diện này. Ngoài ra, nếu có hình ảnh tương thích, bạn có thể sử dụng lớp
ImageMapType
. Lớp này đã triển khai giao diện này.
Các lớp triển khai giao diện MapType
yêu cầu bạn xác định và điền sẵn các thuộc tính sau:
tileSize
(bắt buộc) chỉ định kích thước của ô (thuộc loạigoogle.maps.Size
). Kích thước phải là hình chữ nhật mặc dù không cần phải là hình vuông.maxZoom
(bắt buộc) chỉ định mức thu phóng tối đa để hiển thị các ô của loại bản đồ này.minZoom
(không bắt buộc) chỉ định mức thu phóng tối thiểu để hiển thị ô của loại bản đồ này. Theo mặc định, giá trị này là0
, cho biết không có mức thu phóng tối thiểu.name
(không bắt buộc) chỉ định tên cho loại bản đồ này. Thuộc tính này chỉ cần thiết nếu bạn muốn có thể chọn loại bản đồ này trong một chế độ điều khiển MapType. (Xem phần Các lựa chọn điều khiển.)alt
(không bắt buộc) chỉ định văn bản thay thế cho loại bản đồ này, xuất hiện dưới dạng văn bản di chuột. Thuộc tính này chỉ cần thiết nếu bạn muốn có thể chọn loại bản đồ này trong một chế độ điều khiển MapType. (Xem phần Các lựa chọn kiểm soát.)
Ngoài ra, các lớp triển khai giao diện MapType
cần triển khai các phương thức sau:
-
getTile()
(bắt buộc) được gọi bất cứ khi nào API xác định rằng bản đồ cần hiển thị các ô mới cho khung hiển thị đã cho. Phương thứcgetTile()
phải có chữ ký sau:getTile(tileCoord:Point,zoom:number,ownerDocument:Document):Node
API này xác định xem có cần gọi
getTile()
hay không dựa trên các thuộc tínhtileSize
,minZoom
vàmaxZoom
củaMapType
cũng như chế độ xem hiện tại và mức thu phóng của bản đồ. Trình xử lý cho phương thức này sẽ trả về một phần tử HTML cho một toạ độ, mức thu phóng và phần tử DOM đã truyền mà bạn sẽ thêm hình ảnh ô vào. -
releaseTile()
(không bắt buộc) được gọi bất cứ khi nào API xác định rằng bản đồ cần xoá một ô vì ô đó nằm ngoài khung hiển thị. Phương thức này phải có chữ ký sau:releaseTile(tile:Node)
Bạn thường phải xử lý việc xoá mọi phần tử được đính kèm vào các ô bản đồ khi thêm vào bản đồ. Ví dụ: nếu đã đính kèm trình nghe sự kiện vào lớp phủ ô bản đồ, bạn nên xoá chúng tại đây.
Phương thức getTile()
đóng vai trò là bộ điều khiển chính để xác định những ô cần tải trong một khung hiển thị nhất định.
Các loại bản đồ cơ sở
Các loại bản đồ mà bạn tạo theo cách này có thể hoạt động độc lập hoặc kết hợp với các loại bản đồ khác dưới dạng lớp phủ. Các loại bản đồ độc lập được gọi là các loại bản đồ cơ sở. Bạn có thể muốn API xử lý các MapType
tuỳ chỉnh như xử lý mọi loại bản đồ cơ bản hiện có khác (ROADMAP
, TERRAIN
, v.v.). Để làm như vậy, hãy thêm MapType
tuỳ chỉnh vào thuộc tính mapTypes
của Map
. Thuộc tính này thuộc loại MapTypeRegistry
.
Đoạn mã sau đây tạo một MapType
cơ sở để hiển thị toạ độ ô của bản đồ và vẽ đường viền của các ô:
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;
Dùng thử mẫu
Các loại bản đồ lớp phủ
Một số loại bản đồ được thiết kế để hoạt động trên các loại bản đồ hiện có. Các loại bản đồ như vậy có thể có các lớp trong suốt cho biết các địa điểm yêu thích hoặc cho người dùng thấy dữ liệu bổ sung.
Trong những trường hợp này, bạn không muốn loại bản đồ được coi là một thực thể riêng biệt mà là một lớp phủ.
Bạn có thể thực hiện việc này bằng cách thêm loại bản đồ vào MapType
hiện có ngay bằng cách sử dụng thuộc tính overlayMapTypes
của Map
. Thuộc tính này chứa một MVCArray
của MapType
. Tất cả các loại bản đồ (cơ sở và lớp phủ) đều được kết xuất trong lớp mapPane
. Các loại bản đồ lớp phủ sẽ hiển thị ở trên cùng của bản đồ cơ sở mà chúng được đính kèm, theo thứ tự xuất hiện trong mảng Map.overlayMapTypes
(các lớp phủ có giá trị chỉ mục cao hơn sẽ hiển thị trước các lớp phủ có giá trị chỉ mục thấp hơn).
Ví dụ sau đây giống với ví dụ trước, ngoại trừ việc chúng ta đã tạo một lớp phủ ô MapType
ở trên cùng của loại bản đồ ROADMAP
:
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;
Dùng thử mẫu
Các loại bản đồ hình ảnh
Việc triển khai một MapType
để đóng vai trò là loại bản đồ cơ sở có thể là một việc tốn thời gian và công sức. API này cung cấp một lớp đặc biệt triển khai giao diện MapType
cho các loại bản đồ phổ biến nhất: các loại bản đồ bao gồm các ô được tạo thành từ các tệp hình ảnh riêng lẻ.
Lớp này, lớp ImageMapType
, được tạo bằng một quy cách đối tượng ImageMapTypeOptions
xác định các thuộc tính bắt buộc sau:
tileSize
(bắt buộc) chỉ định kích thước của ô (thuộc loạigoogle.maps.Size
). Kích thước phải là hình chữ nhật mặc dù không cần phải là hình vuông.getTileUrl
(bắt buộc) chỉ định hàm, thường được cung cấp dưới dạng một hàm theo nghĩa đen nội tuyến, để xử lý việc chọn ô hình ảnh thích hợp dựa trên toạ độ thế giới và mức thu phóng được cung cấp.
Đoạn mã sau đây triển khai một ImageMapType
cơ bản bằng cách sử dụng các ô mặt trăng của Google. Ví dụ này sử dụng một hàm chuẩn hoá để đảm bảo các ô lặp lại dọc theo trục x, nhưng không lặp lại dọc theo trục y của bản đồ.
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;
Dùng thử mẫu
Dự đoán
Trái Đất là một quả cầu ba chiều (xấp xỉ), trong khi bản đồ là một bề mặt phẳng hai chiều. Bản đồ mà bạn thấy trong API JavaScript của Maps, giống như mọi bản đồ phẳng của Trái Đất, là một phép chiếu của quả cầu đó lên một bề mặt phẳng. Nói một cách đơn giản nhất, phép chiếu có thể được xác định là một mối liên kết giữa các giá trị vĩ độ/kinh độ với các toạ độ trên bản đồ của phép chiếu.
Các phép chiếu trong API JavaScript của Maps phải triển khai giao diện Projection
. Quá trình triển khai Projection
không chỉ cung cấp một mối liên kết từ hệ toạ độ này sang hệ toạ độ khác mà còn cung cấp một mối liên kết hai chiều. Tức là bạn phải xác định cách dịch từ toạ độ Trái Đất (các đối tượng LatLng
) sang hệ thống toạ độ thế giới của lớp Projection
và từ hệ thống toạ độ thế giới trở lại toạ độ Trái Đất.
Google Maps sử dụng phép chiếu Mercator để tạo bản đồ từ dữ liệu địa lý và chuyển đổi các sự kiện trên bản đồ thành toạ độ địa lý. Bạn có thể lấy phép chiếu này bằng cách gọi getProjection()
trên Map
(hoặc bất kỳ loại MapType
cơ sở tiêu chuẩn nào). Đối với hầu hết các trường hợp sử dụng, Projection
tiêu chuẩn này sẽ đủ, nhưng bạn cũng có thể xác định và sử dụng các phép chiếu tuỳ chỉnh của riêng mình.
Triển khai một phép chiếu
Khi triển khai một phép chiếu tuỳ chỉnh, bạn sẽ cần xác định một số điều sau:
- Công thức để lập bản đồ toạ độ vĩ độ và kinh độ vào mặt phẳng Descartes và công thức tương ứng để lập bản đồ từ mặt phẳng Descartes sang toạ độ vĩ độ và kinh độ. (Giao diện
Projection
chỉ hỗ trợ các phép biến đổi thành toạ độ trực giao.) - Kích thước ô cơ sở. Tất cả các thẻ thông tin đều phải có hình chữ nhật.
- "Kích thước thế giới" của bản đồ sử dụng bộ ô cơ sở ở mức thu phóng 0. Xin lưu ý rằng đối với những bản đồ chỉ có một ô ở mức thu phóng 0, kích thước thế giới và kích thước ô cơ sở là giống nhau.
Phép biến đổi toạ độ trong phép chiếu
Mỗi phép chiếu cung cấp 2 phương thức dịch giữa 2 hệ toạ độ này, cho phép bạn chuyển đổi giữa toạ độ địa lý và toạ độ thế giới:
- Phương thức
Projection.fromLatLngToPoint()
chuyển đổi giá trịLatLng
thành một toạ độ thế giới. Phương thức này được dùng để định vị lớp phủ trên bản đồ (và để định vị chính bản đồ). - Phương thức
Projection.fromPointToLatLng()
chuyển đổi một toạ độ thế giới thành giá trịLatLng
. Phương thức này dùng để chuyển đổi các sự kiện (chẳng hạn như lượt nhấp) xảy ra trên bản đồ thành toạ độ địa lý.
Google Maps giả định rằng các phép chiếu là phép chiếu thẳng.
Nhìn chung, bạn có thể sử dụng phép chiếu cho 2 trường hợp: tạo bản đồ thế giới hoặc tạo bản đồ của một khu vực địa phương. Trong trường hợp trước, bạn cũng phải đảm bảo rằng phép chiếu của bạn cũng là phép chiếu thẳng và bình thường ở mọi kinh độ. Một số phép chiếu (đặc biệt là phép chiếu hình nón) có thể "chuẩn theo địa phương" (tức là chỉ hướng bắc) nhưng lại lệch so với hướng bắc thực; ví dụ: bản đồ càng nằm xa so với một số kinh độ tham chiếu. Bạn có thể sử dụng phép chiếu như vậy trên cục bộ, nhưng hãy lưu ý rằng phép chiếu này chắc chắn không chính xác và lỗi biến đổi sẽ ngày càng rõ ràng hơn khi bạn đi lệch khỏi kinh độ tham chiếu.
Lựa chọn ô bản đồ trong phần Dự đoán
Phép chiếu không chỉ hữu ích cho việc xác định vị trí của các vị trí hoặc lớp phủ, mà còn cho việc định vị chính các ô bản đồ.
Maps JavaScript API hiển thị bản đồ cơ sở bằng cách sử dụng giao diện MapType
. Giao diện này phải khai báo cả thuộc tính projection
để xác định phép chiếu của bản đồ và phương thức getTile()
để truy xuất các ô bản đồ dựa trên các giá trị toạ độ ô. Toạ độ ô dựa trên cả kích thước ô cơ bản (phải là hình chữ nhật) và "kích thước thế giới" của bản đồ. Đây là kích thước tính bằng pixel của thế giới bản đồ ở mức thu phóng 0. (Đối với các bản đồ bao gồm một ô ở mức thu phóng 0, kích thước ô và kích thước thế giới là giống hệt nhau.)
Bạn xác định kích thước ô cơ sở trong thuộc tính tileSize
của MapType
. Bạn xác định kích thước thế giới một cách gián tiếp trong các phương thức fromLatLngToPoint()
và fromPointToLatLng()
của phép chiếu.
Vì việc chọn hình ảnh phụ thuộc vào các giá trị được truyền này, nên bạn nên đặt tên cho những hình ảnh có thể được chọn theo chương trình dựa trên các giá trị được truyền đó, chẳng hạn như map_zoom_tileX_tileY.png
.
Ví dụ sau đây xác định một ImageMapType
bằng cách sử dụng phép chiếu
Gall-Peters:
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;