Tính năng bản đồ vectơ

Chọn nền tảng: Android iOS JavaScript

Xem mẫu

API Maps JavaScript cung cấp hai cách triển khai bản đồ: đường quét và vectơ. Bản đồ đường quét tải bản đồ dưới dạng lưới các ô hình ảnh đường quét dựa trên pixel do phía máy chủ của Google Maps Platform tạo ra, sau đó phân phát đến ứng dụng web của bạn. Bản đồ vectơ bao gồm các ô dựa trên vectơ được vẽ tại thời điểm tải trên phía máy khách bằng WebGL, một công nghệ web cho phép trình duyệt truy cập vào GPU trên thiết bị của người dùng để kết xuất đồ hoạ 2D và 3D.

Bản đồ vectơ cũng chính là bản đồ Google mà người dùng của bạn quen sử dụng và mang lại một số lợi thế so với bản đồ ô đường quét mặc định, đáng chú ý nhất là độ sắc nét của hình ảnh dựa trên vectơ và việc bổ sung các toà nhà 3D ở mức thu phóng gần. Bản đồ vectơ hỗ trợ các tính năng sau:

Bắt đầu sử dụng Bản đồ vectơ

Nghiêng và xoay

Bạn có thể đặt độ nghiêng và độ xoay (tiêu đề) trên bản đồ vectơ bằng cách thêm các thuộc tính headingtilt khi khởi chạy bản đồ và bằng cách gọi các phương thức setTiltsetHeading trên bản đồ. Ví dụ sau đây thêm một số nút vào bản đồ cho thấy việc điều chỉnh độ nghiêng và hướng theo cách lập trình theo mức tăng 20 độ.

TypeScript

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

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

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

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

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

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

JavaScript

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

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

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

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

window.initMap = initMap;

CSS

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

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

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

HTML

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

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

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

Thử mẫu

Sử dụng cử chỉ chuột và bàn phím

Nếu bạn đã bật tính năng tương tác của người dùng với độ nghiêng và xoay (tiêu đề) (bằng cách lập trình hoặc trong Google Cloud Console), thì người dùng có thể điều chỉnh độ nghiêng và xoay bằng chuột và bàn phím:

  • Sử dụng chuột, giữ phím shift, sau đó nhấp và kéo chuột lên và xuống để điều chỉnh độ nghiêng, phải và trái để điều chỉnh hướng.
  • Sử dụng bàn phím, giữ phím shift, sau đó sử dụng phím mũi tên lên và xuống để điều chỉnh độ nghiêng, cũng như phím mũi tên phải và trái để điều chỉnh hướng.

Điều chỉnh độ nghiêng và hướng theo phương thức lập trình

Sử dụng các phương thức setTilt()setHeading() để điều chỉnh độ nghiêng và hướng trên bản đồ vectơ theo phương thức lập trình. Heading là hướng máy ảnh theo chiều kim đồng hồ bắt đầu từ hướng bắc, vì vậy map.setHeading(90) sẽ xoay bản đồ sao cho hướng đông hướng lên trên. Góc nghiêng được đo từ thiên đỉnh, vì vậy, map.setTilt(0) đang nhìn thẳng xuống, trong khi map.setTilt(45) sẽ dẫn đến chế độ xem xiên.

  • Gọi setTilt() để đặt góc nghiêng của bản đồ. Sử dụng getTilt() để lấy giá trị độ nghiêng hiện tại.
  • Gọi setHeading() để đặt tiêu đề của bản đồ. Sử dụng getHeading() để lấy giá trị tiêu đề hiện tại.

Để thay đổi tâm bản đồ trong khi vẫn giữ nguyên độ nghiêng và hướng, hãy sử dụng map.setCenter() hoặc map.panBy().

Xin lưu ý rằng phạm vi góc có thể sử dụng thay đổi theo mức thu phóng hiện tại. Các giá trị nằm ngoài phạm vi này sẽ được xếp vào phạm vi hiện được cho phép.

Bạn cũng có thể sử dụng phương thức moveCamera để thay đổi tiêu đề, độ nghiêng, tâm và thu phóng theo phương thức lập trình. Tìm hiểu thêm.

Tác động đến các phương thức khác

Khi bạn nghiêng hoặc xoay bản đồ, hành vi của các phương thức API JavaScript Maps khác sẽ bị ảnh hưởng:

  • map.getBounds() luôn trả về hộp giới hạn nhỏ nhất bao gồm vùng hiển thị; khi độ nghiêng được áp dụng, các giới hạn được trả về có thể đại diện cho một vùng lớn hơn vùng hiển thị của khung nhìn bản đồ.
  • map.fitBounds() sẽ đặt lại độ nghiêng và hướng về 0 trước khi điều chỉnh giới hạn.
  • map.panToBounds() sẽ đặt lại độ nghiêng và hướng về 0 trước khi kéo các giới hạn.
  • map.setTilt() chấp nhận mọi giá trị, nhưng hạn chế độ nghiêng tối đa dựa trên mức thu phóng bản đồ hiện tại.
  • map.setHeading() chấp nhận mọi giá trị và sẽ sửa đổi giá trị đó để phù hợp với phạm vi [0, 360].

Điều khiển camera

Sử dụng hàm map.moveCamera() để cập nhật mọi tổ hợp thuộc tính camera cùng một lúc. map.moveCamera() chấp nhận một tham số duy nhất chứa tất cả các thuộc tính máy ảnh cần cập nhật. Ví dụ sau đây cho thấy cách gọi map.moveCamera() để thiết lập center, zoom, headingtilt cùng một lúc:

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

Bạn có thể tạo ảnh động cho các thuộc tính của máy ảnh bằng cách gọi map.moveCamera() với một vòng lặp ảnh động, như minh hoạ dưới đây:

const degreesPerSecond = 3;

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

  requestAnimationFrame(animateCamera);
}

// Start the animation.
requestAnimationFrame(animateCamera);

Vị trí máy ảnh

Chế độ xem bản đồ được mô phỏng như một máy ảnh nhìn xuống mặt phẳng. Vị trí của máy ảnh (và do đó là việc kết xuất bản đồ) được xác định bằng các thuộc tính sau: mục tiêu (vĩ độ/kinh độ của địa điểm), độ lệch, độ nghiêngtỷ lệ thu phóng.

Sơ đồ thuộc tính của máy ảnh

Mục tiêu (vị trí)

Mục tiêu của máy ảnh là vị trí của tâm bản đồ, được chỉ định dưới dạng tọa độ vĩ độ và kinh độ.

Vĩ độ có thể nằm trong khoảng từ -85 đến 85 độ. Các giá trị trên hoặc dưới phạm vi này sẽ được cố định thành giá trị gần nhất trong phạm vi này. Ví dụ: việc chỉ định vĩ độ 100 sẽ đặt giá trị thành 85. Kinh độ nằm trong khoảng từ -180 đến 180 độ. Các giá trị cao hơn hoặc thấp hơn phạm vi này sẽ được gói sao cho nằm trong phạm vi (-180, 180). Ví dụ: 480, 840 và 1200 đều sẽ được gói thành 120 độ.

Phương vị (hướng)

Độ lệch của máy ảnh chỉ định hướng la bàn, được đo bằng độ từ hướng bắc thực, tương ứng với cạnh trên cùng của bản đồ. Nếu bạn vẽ một đường dọc từ tâm bản đồ đến cạnh trên cùng của bản đồ, thì góc phương vị sẽ tương ứng với hướng của máy ảnh (được đo bằng độ) so với hướng bắc thực.

Giá trị phương vị 0 có nghĩa là đầu bản đồ chỉ về hướng bắc thực. Giá trị góc phương vị 90 có nghĩa là đầu bản đồ chỉ về hướng đông (90 độ trên la bàn). Giá trị 180 có nghĩa là đầu bản đồ chỉ về phía nam.

API Maps cho phép bạn thay đổi hướng của bản đồ. Ví dụ: người lái xe thường xoay bản đồ đường để căn chỉnh với hướng di chuyển của họ, trong khi người đi bộ đường dài sử dụng bản đồ và la bàn thường định hướng bản đồ sao cho đường dọc chỉ về hướng bắc.

Nghiêng (góc nhìn)

Độ nghiêng xác định vị trí của máy ảnh trên một vòng cung ngay phía trên vị trí trung tâm của bản đồ, được đo bằng độ từ nadir (hướng chỉ thẳng xuống dưới máy ảnh). Giá trị 0 tương ứng với máy ảnh hướng thẳng xuống. Các giá trị lớn hơn 0 tương ứng với một máy ảnh được hướng về chân trời theo số độ được chỉ định. Khi bạn thay đổi góc nhìn, bản đồ sẽ xuất hiện theo phối cảnh, với các đối tượng ở xa sẽ xuất hiện nhỏ hơn và các đối tượng ở gần sẽ xuất hiện lớn hơn. Các hình minh hoạ sau đây minh hoạ điều này.

Trong các hình ảnh bên dưới, góc nhìn là 0 độ. Hình ảnh đầu tiên cho thấy sơ đồ của điều này; vị trí 1 là vị trí máy ảnh và vị trí 2 là vị trí bản đồ hiện tại. Bản đồ thu được sẽ xuất hiện bên dưới.

Ảnh chụp màn hình bản đồ có máy ảnh được đặt ở góc nhìn 0 độ, ở mức thu phóng 18.
Bản đồ hiển thị với góc xem mặc định của máy ảnh.
Sơ đồ cho thấy vị trí mặc định của máy ảnh, ngay phía trên vị trí trên bản đồ, ở góc 0 độ.
Góc nhìn mặc định của máy ảnh.

Trong hình ảnh bên dưới, góc nhìn là 45 độ. Lưu ý rằng máy ảnh di chuyển một nửa dọc theo một vòng cung giữa vị trí thẳng đứng (0 độ) và mặt đất (90 độ) đến vị trí 3. Máy ảnh vẫn đang hướng vào điểm trung tâm của bản đồ, nhưng khu vực được biểu thị bằng đường kẻ ở vị trí 4 hiện đã hiển thị.

Ảnh chụp màn hình bản đồ có máy ảnh được đặt ở góc nhìn 45 độ, ở mức thu phóng 18.
Bản đồ hiển thị với góc nhìn 45 độ.
Sơ đồ cho thấy góc nhìn của máy ảnh được đặt thành 45 độ, với mức thu phóng vẫn được đặt thành 18.
Góc nhìn của máy ảnh là 45 độ.

Bản đồ trong ảnh chụp màn hình này vẫn được căn giữa ở cùng một điểm như trong bản đồ ban đầu, nhưng có nhiều đối tượng hơn xuất hiện ở đầu bản đồ. Khi bạn tăng góc ngoài 45 độ, các đối tượng giữa máy ảnh và vị trí trên bản đồ sẽ xuất hiện lớn hơn theo tỷ lệ, trong khi các đối tượng ngoài vị trí trên bản đồ sẽ xuất hiện nhỏ hơn theo tỷ lệ, tạo ra hiệu ứng ba chiều.

Zoom (thu phóng)

Mức thu phóng của máy ảnh xác định tỷ lệ của bản đồ. Ở các cấp độ thu phóng lớn hơn, bạn có thể thấy nhiều chi tiết hơn trên màn hình, trong khi ở các cấp độ thu phóng nhỏ hơn, bạn có thể thấy nhiều cảnh vật hơn trên màn hình.

Cấp độ thu phóng không cần phải là số nguyên. Phạm vi mức thu phóng mà bản đồ cho phép phụ thuộc vào một số yếu tố, bao gồm cả mục tiêu, loại bản đồ và kích thước màn hình. Mọi số nằm ngoài phạm vi này sẽ được chuyển đổi thành giá trị hợp lệ gần nhất tiếp theo, có thể là mức thu phóng tối thiểu hoặc mức thu phóng tối đa. Danh sách sau đây cho biết mức độ chi tiết gần đúng mà bạn có thể thấy ở mỗi cấp độ thu phóng:

  • 1: Thế giới
  • 5: Lục địa/châu lục
  • 10: Thành phố
  • 15: Đường phố
  • 20: Toà nhà
Các hình ảnh sau đây cho thấy hình thức hiển thị của các mức thu phóng:
Ảnh chụp màn hình bản đồ ở mức thu phóng 5
Một bản đồ ở mức thu phóng 5.
Ảnh chụp màn hình bản đồ ở mức thu phóng 15
Một bản đồ ở mức thu phóng 15.
Ảnh chụp màn hình bản đồ ở mức thu phóng 20
Bản đồ ở mức thu phóng 20.

Thu phóng theo tỷ lệ

Bản đồ vectơ hỗ trợ tính năng thu phóng theo tỷ lệ, cho phép bạn thu phóng bằng các giá trị phân số thay vì số nguyên. Mặc dù cả bản đồ đường quét và vectơ đều hỗ trợ tính năng thu phóng theo tỷ lệ, nhưng tính năng này được bật theo mặc định cho bản đồ vectơ và tắt theo mặc định cho bản đồ đường quét. Sử dụng tuỳ chọn bản đồ isFractionalZoomEnabled để bật và tắt tính năng thu phóng phân đoạn.

Ví dụ sau đây cho thấy cách bật tính năng thu phóng theo tỷ lệ khi khởi tạo bản đồ:

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

Bạn cũng có thể bật và tắt tính năng thu phóng theo tỷ lệ bằng cách đặt tuỳ chọn bản đồ isFractionalZoomEnabled như minh hoạ dưới đây:

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

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

Bạn có thể đặt trình nghe để phát hiện xem tính năng thu phóng theo tỷ lệ có được bật hay không; tính năng này hữu ích nhất nếu bạn chưa đặt rõ ràng isFractionalZoomEnabled thành true hoặc false. Mã ví dụ sau đây sẽ kiểm tra xem bạn đã bật tính năng thu phóng theo tỷ lệ hay chưa:

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