街景服務服務

總覽

選取平台: Android iOS JavaScript

Google 街景服務可讓您在指定道路的涵蓋區域進行 360 度全景檢視。街景服務的 API 涵蓋區域與 Google 地圖應用程式 (https://maps.google.com/) 相同。您可以前往 Google 地圖網站查看目前支援街景服務的城市清單。

以下是街景服務圖片的範例。


Maps JavaScript API 提供街景服務,以便取得及操控在 Google 地圖街景服務中使用的圖像。此街景服務服務是在瀏覽器中原生支援的。

街景服務地圖的用法

雖然街景服務可在獨立的 DOM 元素中使用,但在地圖上顯示位置最為實用。根據預設,系統會在地圖上啟用街景服務,且系統會將街景服務衣夾人控制項整合至導覽 (縮放和平移) 控制項。如果將 streetViewControl 設為 false,即可在地圖的 MapOptions 中隱藏這個控制項。您也可以將 Map'streetViewControlOptions.position 屬性設為新的 ControlPosition,藉此變更街景服務控制項的預設位置。

街景服務衣夾人控制項可直接在地圖中查看街景服務全景。使用者按住按住衣夾人後,地圖就會更新,在支援街景服務的街道周圍顯示藍色外框,提供與 Google 地圖應用程式相似的使用者體驗。

使用者將衣夾人標記置於街道上時,地圖會更新,以顯示指定位置的街景服務全景。

街景服務全景

街景服務圖片支援使用 StreetViewPanorama 物件,此物件可提供街景服務街景檢視器的 API 介面。每個地圖都包含預設的街景服務全景,只要呼叫 地圖's getStreetView() 方法即可擷取該全景。將 streetViewControl 選項設為 true 來在地圖上新增街景服務控制項時,系統會自動將衣夾人控制項連結至這個預設的街景服務全景。

您也可以將 Map' 的 streetView 屬性明確設為建構該物件,藉此建立自己的 StreetViewPanorama 物件,並將地圖設為使用該物件來取代預設物件。如要修改預設行為 (例如在地圖和全景之間自動分享疊加層),請覆寫預設全景。(請參閱下方的街景服務中的疊加層)。

街景服務容器

您可能想要在單獨的 DOM 元素 (通常是 <div> 元素) 中顯示 StreetViewPanorama。只要在 StreetViewPanorama' 的建構函式中傳遞 DOM 元素即可。為了呈現最佳圖片,建議最小尺寸為 200 x 200 像素。

注意:雖然街景服務功能是專為搭配地圖使用所設計,但您不必這麼做。你可以使用不含地圖的獨立街景服務物件。

街景服務位置和檢視點 (POV)

StreetViewPanorama 建構函式也可讓您使用 StreetViewOptions 參數設定街景服務位置和視角。建構完成後,您可以對物件呼叫 setPosition()setPov(),以變更其位置和 POV。

街景服務位置會定義圖片相機焦點的位置,但不會定義該圖片的相機方向。為此,StreetViewPov 物件定義了兩個屬性:

  • heading (預設 0) 會定義相機擺放位置與實際北方的旋轉角度 (以度為單位)。標題是以順時針方向測量 (東方為 90 度)。
  • pitch (預設 0) 定義從相機的初始預設傾斜角度「角度」或「向下」的角度變化,這個角度通常是水平 (但不一定)。(舉例來說,如果圖片是從山丘上拍攝,其預設傾斜角度可能就不是水平)。俯仰角度的測量方式為正值 (向上提升 +90 度,與預設傾斜方向正切),以及負值 (向下至 -90 度,向下至預設傾斜角度)。

StreetViewPov 物件最常用於判斷街景服務相機的視角。此外,您也可以使用 StreetViewPanorama.getPhotographerPov() 方法判斷攝影師的視角 (通常是汽車或單車的面朝方向),

以下程式碼顯示波士頓地圖,其中包含芬威球場的初始檢視畫面。選取衣夾人並拖曳到地圖上的支援位置後,街景服務全景就會改變:

TypeScript

function initialize() {
  const fenway = { lat: 42.345573, lng: -71.098326 };
  const map = new google.maps.Map(
    document.getElementById("map") as HTMLElement,
    {
      center: fenway,
      zoom: 14,
    }
  );
  const panorama = new google.maps.StreetViewPanorama(
    document.getElementById("pano") as HTMLElement,
    {
      position: fenway,
      pov: {
        heading: 34,
        pitch: 10,
      },
    }
  );

  map.setStreetView(panorama);
}

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

JavaScript

function initialize() {
  const fenway = { lat: 42.345573, lng: -71.098326 };
  const map = new google.maps.Map(document.getElementById("map"), {
    center: fenway,
    zoom: 14,
  });
  const panorama = new google.maps.StreetViewPanorama(
    document.getElementById("pano"),
    {
      position: fenway,
      pov: {
        heading: 34,
        pitch: 10,
      },
    }
  );

  map.setStreetView(panorama);
}

window.initialize = initialize;

CSS

html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

#map,
#pano {
  float: left;
  height: 100%;
  width: 50%;
}

HTML

<html>
  <head>
    <title>Street View split-map-panes</title>
    <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>

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

    <!-- 
     The `defer` attribute causes the callback 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
     with https://www.npmjs.com/package/@googlemaps/js-api-loader.
    -->
    <script
      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initialize&v=weekly"
      defer
    ></script>
  </body>
</html>
查看範例

查看範例

行動裝置上的動態追蹤

在支援裝置方向事件的裝置上,API 可讓使用者根據裝置的移動方式變更街景服務視角。使用者只要移動裝置,即可觀看周遭內容。這就是所謂的動作追蹤或裝置旋轉追蹤。

應用程式開發人員可以按照下列步驟變更預設行為:

  • 啟用或停用動作追蹤功能。根據預設,啟用追蹤追蹤功能的所有裝置都會啟用這項功能。下列範例會停用動作追蹤,但保持動作追蹤控制項可見。(請注意,使用者可以輕觸控制項來開啟動作追蹤)。
    var panorama = new google.maps.StreetViewPanorama(
        document.getElementById('pano'), {
          position: {lat: 37.869260, lng: -122.254811},
          pov: {heading: 165, pitch: 0},
          motionTracking: false
        });
    
  • 隱藏或顯示動作追蹤控制項。根據預設,控制項會顯示在支援動作追蹤的裝置上。使用者可以輕觸控制項來開啟或關閉動作追蹤。請注意,無論 motionTrackingControl 的值為何,如果裝置不支援動作追蹤,系統也不會顯示該控制項。

    下列範例會停用動作追蹤和動作追蹤控制項。在此情況下,使用者無法開啟動作追蹤功能:

    var panorama = new google.maps.StreetViewPanorama(
        document.getElementById('pano'), {
          position: {lat: 37.869260, lng: -122.254811},
          pov: {heading: 165, pitch: 0},
          motionTracking: false,
          motionTrackingControl: false
        });
    
  • 變更動作追蹤控制項的預設位置。根據預設,控制項會顯示在全景的右下方 (位置 RIGHT_BOTTOM)。控制項會將控制項的位置設為左下方:
    var panorama = new google.maps.StreetViewPanorama(
        document.getElementById('pano'), {
          position: {lat: 37.869260, lng: -122.254811},
          pov: {heading: 165, pitch: 0},
          motionTrackingControlOptions: {
            position: google.maps.ControlPosition.LEFT_BOTTOM
          }
        });
    

如要查看動作追蹤的運作情況,請在行動裝置上 (或任何支援裝置螢幕方向事件的裝置) 查看下列範例:


查看範例

街景服務中的疊加層

預設的 StreetViewPanorama 物件支援原生地圖疊加層的顯示功能。疊加層通常會顯示在「街道層級」,並會固定在 LatLng 個位置。(例如,在街景服務全景中的標記位置,錨點的錨點固定在地點的平面上)。

街景服務全景目前支援的疊加層類型僅限於 MarkerInfoWindow 和自訂 OverlayView。您在地圖上顯示的疊加層可能會視為 Map 物件取代,並呼叫 setMap() 並傳遞 StreetViewPanorama 做為引數 (而非地圖),以顯示在街景服務全景上。同樣地,只要呼叫 open() 並傳送 StreetViewPanorama() (而非地圖),即可在街景服務全景中開啟資訊視窗。

此外,使用預設 StreetViewPanorama 建立地圖時,系統會將在地圖上建立的所有標記自動與地圖的關聯街景服務全景分享 (前提是全景可見)。如要擷取預設的街景服務全景,請在 Map 物件上呼叫 getStreetView()。請注意,如果您已明確將地圖的 streetView 屬性設為自己建構的 StreetViewPanorama,就會覆寫預設全景。

以下範例顯示用來標示紐約市 Astor Place 周圍不同地點的標記。將螢幕切換成街景服務,顯示 StreetViewPanorama 中顯示的共用標記。

TypeScript

let panorama: google.maps.StreetViewPanorama;

function initMap(): void {
  const astorPlace = { lat: 40.729884, lng: -73.990988 };

  // Set up the map
  const map = new google.maps.Map(
    document.getElementById("map") as HTMLElement,
    {
      center: astorPlace,
      zoom: 18,
      streetViewControl: false,
    }
  );

  document
    .getElementById("toggle")!
    .addEventListener("click", toggleStreetView);

  // Set up the markers on the map
  const cafeMarker = new google.maps.Marker({
    position: { lat: 40.730031, lng: -73.991428 },
    map,
    icon: "https://chart.apis.google.com/chart?chst=d_map_pin_icon&chld=cafe|FFFF00",
    title: "Cafe",
  });

  const bankMarker = new google.maps.Marker({
    position: { lat: 40.729681, lng: -73.991138 },
    map,
    icon: "https://chart.apis.google.com/chart?chst=d_map_pin_icon&chld=dollar|FFFF00",
    title: "Bank",
  });

  const busMarker = new google.maps.Marker({
    position: { lat: 40.729559, lng: -73.990741 },
    map,
    icon: "https://chart.apis.google.com/chart?chst=d_map_pin_icon&chld=bus|FFFF00",
    title: "Bus Stop",
  });

  // We get the map's default panorama and set up some defaults.
  // Note that we don't yet set it visible.
  panorama = map.getStreetView()!; // TODO fix type
  panorama.setPosition(astorPlace);
  panorama.setPov(
    /** @type {google.maps.StreetViewPov} */ {
      heading: 265,
      pitch: 0,
    }
  );
}

function toggleStreetView(): void {
  const toggle = panorama.getVisible();

  if (toggle == false) {
    panorama.setVisible(true);
  } else {
    panorama.setVisible(false);
  }
}

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

JavaScript

let panorama;

function initMap() {
  const astorPlace = { lat: 40.729884, lng: -73.990988 };
  // Set up the map
  const map = new google.maps.Map(document.getElementById("map"), {
    center: astorPlace,
    zoom: 18,
    streetViewControl: false,
  });

  document.getElementById("toggle").addEventListener("click", toggleStreetView);

  // Set up the markers on the map
  const cafeMarker = new google.maps.Marker({
    position: { lat: 40.730031, lng: -73.991428 },
    map,
    icon: "https://chart.apis.google.com/chart?chst=d_map_pin_icon&chld=cafe|FFFF00",
    title: "Cafe",
  });
  const bankMarker = new google.maps.Marker({
    position: { lat: 40.729681, lng: -73.991138 },
    map,
    icon: "https://chart.apis.google.com/chart?chst=d_map_pin_icon&chld=dollar|FFFF00",
    title: "Bank",
  });
  const busMarker = new google.maps.Marker({
    position: { lat: 40.729559, lng: -73.990741 },
    map,
    icon: "https://chart.apis.google.com/chart?chst=d_map_pin_icon&chld=bus|FFFF00",
    title: "Bus Stop",
  });

  // We get the map's default panorama and set up some defaults.
  // Note that we don't yet set it visible.
  panorama = map.getStreetView(); // TODO fix type
  panorama.setPosition(astorPlace);
  panorama.setPov(
    /** @type {google.maps.StreetViewPov} */ {
      heading: 265,
      pitch: 0,
    }
  );
}

function toggleStreetView() {
  const toggle = panorama.getVisible();

  if (toggle == false) {
    panorama.setVisible(true);
  } else {
    panorama.setVisible(false);
  }
}

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;
}

#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;
}

#floating-panel {
  margin-left: -100px;
}

HTML

<html>
  <head>
    <title>Overlays Within Street View</title>
    <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>

    <link rel="stylesheet" type="text/css" href="./style.css" />
    <script type="module" src="./index.js"></script>
  </head>
  <body>
    <div id="floating-panel">
      <input type="button" value="Toggle Street View" id="toggle" />
    </div>
    <div id="map"></div>

    <!-- 
     The `defer` attribute causes the callback 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
     with https://www.npmjs.com/package/@googlemaps/js-api-loader.
    -->
    <script
      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initMap&v=weekly"
      defer
    ></script>
  </body>
</html>
查看範例

查看範例

街景服務事件

在街景服務之間移動或操控螢幕方向時,建議你監控幾個代表 StreetViewPanorama 狀態改變的事件:

  • 個別全景 ID 變更時,pano_changed 會觸發。此事件無法保證在觸發此事件時,全景中的任何關聯資料 (例如連結) 也已改變,此事件只表示全景 ID 已變更。請注意,全景 ID (可用來參照此全景) 在目前的瀏覽器工作階段中只有穩定版。
  • position_changed 會在全景的基本 (LatLng) 位置變更時觸發。旋轉全景不會觸發這個事件。請注意,由於全景 API 會自動與最接近的全景 ID 和全景 ID 建立關聯,因此您可以在不變更相關全景 ID 時變更全景的基礎位置。
  • 每當街景服務的 StreetViewPov 變更時,pov_changed 就會觸發。請注意,在位置和全景 ID 保持穩定的情況下,這個事件可能會觸發。
  • 每當街景服務連結變更時,links_changed 就會觸發。請注意,在 pano_changed 指定的全景 ID 變更後,此事件可能會以非同步方式觸發。
  • 每當街景服務的瀏覽權限變更時,visible_changed 就會觸發。請注意,在 pano_changed 指定的全景 ID 變更後,此事件可能會以非同步方式觸發。

以下程式碼說明系統如何處理這些事件,以收集基礎 StreetViewPanorama 的資料:

TypeScript

function initPano() {
  const panorama = new google.maps.StreetViewPanorama(
    document.getElementById("pano") as HTMLElement,
    {
      position: { lat: 37.869, lng: -122.255 },
      pov: {
        heading: 270,
        pitch: 0,
      },
      visible: true,
    }
  );

  panorama.addListener("pano_changed", () => {
    const panoCell = document.getElementById("pano-cell") as HTMLElement;

    panoCell.innerHTML = panorama.getPano();
  });

  panorama.addListener("links_changed", () => {
    const linksTable = document.getElementById("links_table") as HTMLElement;

    while (linksTable.hasChildNodes()) {
      linksTable.removeChild(linksTable.lastChild as ChildNode);
    }

    const links = panorama.getLinks();

    for (const i in links) {
      const row = document.createElement("tr");

      linksTable.appendChild(row);

      const labelCell = document.createElement("td");

      labelCell.innerHTML = "<b>Link: " + i + "</b>";

      const valueCell = document.createElement("td");

      valueCell.innerHTML = links[i].description as string;
      linksTable.appendChild(labelCell);
      linksTable.appendChild(valueCell);
    }
  });

  panorama.addListener("position_changed", () => {
    const positionCell = document.getElementById(
      "position-cell"
    ) as HTMLElement;

    (positionCell.firstChild as HTMLElement).nodeValue =
      panorama.getPosition() + "";
  });

  panorama.addListener("pov_changed", () => {
    const headingCell = document.getElementById("heading-cell") as HTMLElement;
    const pitchCell = document.getElementById("pitch-cell") as HTMLElement;

    (headingCell.firstChild as HTMLElement).nodeValue =
      panorama.getPov().heading + "";
    (pitchCell.firstChild as HTMLElement).nodeValue =
      panorama.getPov().pitch + "";
  });
}

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

JavaScript

function initPano() {
  const panorama = new google.maps.StreetViewPanorama(
    document.getElementById("pano"),
    {
      position: { lat: 37.869, lng: -122.255 },
      pov: {
        heading: 270,
        pitch: 0,
      },
      visible: true,
    }
  );

  panorama.addListener("pano_changed", () => {
    const panoCell = document.getElementById("pano-cell");

    panoCell.innerHTML = panorama.getPano();
  });
  panorama.addListener("links_changed", () => {
    const linksTable = document.getElementById("links_table");

    while (linksTable.hasChildNodes()) {
      linksTable.removeChild(linksTable.lastChild);
    }

    const links = panorama.getLinks();

    for (const i in links) {
      const row = document.createElement("tr");

      linksTable.appendChild(row);

      const labelCell = document.createElement("td");

      labelCell.innerHTML = "<b>Link: " + i + "</b>";

      const valueCell = document.createElement("td");

      valueCell.innerHTML = links[i].description;
      linksTable.appendChild(labelCell);
      linksTable.appendChild(valueCell);
    }
  });
  panorama.addListener("position_changed", () => {
    const positionCell = document.getElementById("position-cell");

    positionCell.firstChild.nodeValue = panorama.getPosition() + "";
  });
  panorama.addListener("pov_changed", () => {
    const headingCell = document.getElementById("heading-cell");
    const pitchCell = document.getElementById("pitch-cell");

    headingCell.firstChild.nodeValue = panorama.getPov().heading + "";
    pitchCell.firstChild.nodeValue = panorama.getPov().pitch + "";
  });
}

window.initPano = initPano;

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;
}

#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;
}

#pano {
  width: 50%;
  height: 100%;
  float: left;
}

#floating-panel {
  width: 45%;
  height: 100%;
  float: right;
  text-align: left;
  overflow: auto;
  position: static;
  border: 0px solid #999;
}

HTML

<html>
  <head>
    <title>Street View Events</title>
    <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>

    <link rel="stylesheet" type="text/css" href="./style.css" />
    <script type="module" src="./index.js"></script>
  </head>
  <body>
    <div id="pano"></div>
    <div id="floating-panel">
      <table>
        <tr>
          <td><b>Position</b></td>
          <td id="position-cell">&nbsp;</td>
        </tr>
        <tr>
          <td><b>POV Heading</b></td>
          <td id="heading-cell">270</td>
        </tr>
        <tr>
          <td><b>POV Pitch</b></td>
          <td id="pitch-cell">0.0</td>
        </tr>
        <tr>
          <td><b>Pano ID</b></td>
          <td id="pano-cell">&nbsp;</td>
        </tr>
        <table id="links_table"></table>
      </table>
    </div>

    <!-- 
     The `defer` attribute causes the callback 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
     with https://www.npmjs.com/package/@googlemaps/js-api-loader.
    -->
    <script
      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initPano&v=weekly"
      defer
    ></script>
  </body>
</html>
查看範例

查看範例

街景服務控制項

顯示 StreetViewPanorama 時,根據預設,全景圖上會顯示各種控制項。您可以在 StreetViewPanoramaOptions 中將適當的欄位設為 truefalse,藉此啟用或停用這些控制項:

  • panControl 可讓您旋轉全景。根據預設,這個控制項會顯示為標準整合式指南針和平移控制項。您可以在 panControlOptions 欄位提供 PanControlOptions,藉此變更控制項的位置。
  • zoomControl 可讓您在圖片中縮放。根據預設,這個控制項會顯示在全景右下角。 您可以在 zoomControlOptions 欄位提供 ZoomControlOptions,藉此修改控制項的外觀。
  • addressControl 提供顯示關聯位置地址的文字重疊元素,並提供一個連結,供您在 Google 地圖中開啟該地點。您可以在 addressControlOptions 欄位提供 StreetViewAddressControlOptions,藉此修改控制項的外觀。
  • fullscreenControl 會提供以全螢幕模式開啟街景服務的選項。您可以在 fullscreenControlOptions 欄位提供 FullscreenControlOptions,藉此修改控制項的外觀。
  • motionTrackingControl 提供啟用或停用行動裝置上的動作追蹤功能的選項。這個控制項只會顯示在支援裝置螢幕方向事件的裝置上。根據預設,控制項會顯示在全景右下角。您可以提供 MotionTrackingControlOptions 來變更控制項的位置。詳情請參閱動作追蹤一節。
  • linksControl 提供圖片上的導覽箭頭,以便瀏覽相鄰的全景圖片。
  • 「關閉」控制項可讓使用者關閉街景服務檢視器。只要將 enableCloseButton 設為 truefalse,即可啟用或停用「關閉」控制項。

以下範例會修改相關街景服務內顯示的控制項,並移除該檢視的連結:

TypeScript

function initPano() {
  // Note: constructed panorama objects have visible: true
  // set by default.
  const panorama = new google.maps.StreetViewPanorama(
    document.getElementById("map") as HTMLElement,
    {
      position: { lat: 42.345573, lng: -71.098326 },
      addressControlOptions: {
        position: google.maps.ControlPosition.BOTTOM_CENTER,
      },
      linksControl: false,
      panControl: false,
      enableCloseButton: false,
    }
  );
}

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

JavaScript

function initPano() {
  // Note: constructed panorama objects have visible: true
  // set by default.
  const panorama = new google.maps.StreetViewPanorama(
    document.getElementById("map"),
    {
      position: { lat: 42.345573, lng: -71.098326 },
      addressControlOptions: {
        position: google.maps.ControlPosition.BOTTOM_CENTER,
      },
      linksControl: false,
      panControl: false,
      enableCloseButton: false,
    }
  );
}

window.initPano = initPano;

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;
}

HTML

<html>
  <head>
    <title>Street View Controls</title>
    <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>

    <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 callback 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
     with https://www.npmjs.com/package/@googlemaps/js-api-loader.
    -->
    <script
      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initPano&v=weekly"
      defer
    ></script>
  </body>
</html>
查看範例

查看範例

直接存取街景服務資料

您可能想透過程式判斷街景服務資料是否可用,或傳回特定全景的資訊,而不必直接操控地圖/全景。您可以使用 StreetViewService 物件執行此操作,該介面可提供儲存在 Google 街景服務服務中的資料的介面。

街景服務服務要求

由於 Google Maps API 必須呼叫外部伺服器,因此存取街景服務服務的方式並非同步。因此,您必須傳遞回呼方法,以便在要求完成時執行。這個回呼方法會處理結果。

您可以使用 StreetViewPanoRequestStreetViewLocationRequestStreetViewService 發出要求。

使用 StreetViewPanoRequest 的要求會識別全景 ID,並提供特定識別 ID。請注意,這些參考 ID 只在該全景的生命週期內穩定。

使用 StreetViewLocationRequest 的要求,使用以下參數搜尋指定位置的全景資料:

  • location 會指定搜尋全景的位置 (經緯度)。
  • preference 會設定要在半徑範圍內找到的全景偏好設定:最接近提供的位置,或是半徑範圍內的最佳全景。
  • radius 會設定半徑 (以公尺為單位),用於搜尋以指定緯度和經度為中心的全景。如未提供,則預設為 50。
  • source 會指定要搜尋的全景來源。有效值如下:
    • default 會使用街景服務的預設來源;搜尋次數不限於特定來源。
    • outdoor 只會限制在戶外搜尋搜尋結果。 請注意,室外全景可能不適用於特定位置。

街景服務服務回應

從「街景服務」服務擷取結果時,函式 getPanorama() 需要「回呼」函式才能執行。這個回呼函式會依序傳回 StreetViewPanoramaData 物件中的一組全景資料,以及表示要求狀態的 StreetViewStatus 程式碼。

StreetViewPanoramaData 物件規格包含以下形式的街景服務全景中繼資料:

{
  "location": {
    "latLng": LatLng,
    "description": string,
    "pano": string
  },
  "copyright": string,
  "links": [{
      "heading": number,
      "description": string,
      "pano": string,
      "roadColor": string,
      "roadOpacity": number
    }],
  "tiles": {
    "worldSize": Size,
    "tileSize": Size,
    "centerHeading": number
  }
}

請注意,此資料物件並非 StreetViewPanorama 物件本身。如要使用這項資料建立街景服務物件,您必須建立 StreetViewPanorama 並呼叫 setPano(),並使用所傳回 location.pano 欄位中的 ID 傳遞該物件。

status 程式碼可能會傳回下列其中一個值:

  • OK 表示服務找到相符的全景。
  • ZERO_RESULTS 表示服務找不到與所傳遞條件相符的全景。
  • UNKNOWN_ERROR 表示無法處理確切的街景服務要求,但確切原因不明。

下方程式碼會建立 StreetViewService,用於回應使用者點擊地圖上的標記,以便在使用者按一下時顯示標記的位置 StreetViewPanorama。程式碼會使用服務傳回的 StreetViewPanoramaData 內容。

TypeScript

/*
 * Click the map to set a new location for the Street View camera.
 */

let map: google.maps.Map;

let panorama: google.maps.StreetViewPanorama;

function initMap(): void {
  const berkeley = { lat: 37.869085, lng: -122.254775 };
  const sv = new google.maps.StreetViewService();

  panorama = new google.maps.StreetViewPanorama(
    document.getElementById("pano") as HTMLElement
  );

  // Set up the map.
  map = new google.maps.Map(document.getElementById("map") as HTMLElement, {
    center: berkeley,
    zoom: 16,
    streetViewControl: false,
  });

  // Set the initial Street View camera to the center of the map
  sv.getPanorama({ location: berkeley, radius: 50 }).then(processSVData);

  // Look for a nearby Street View panorama when the map is clicked.
  // getPanorama will return the nearest pano when the given
  // radius is 50 meters or less.
  map.addListener("click", (event) => {
    sv.getPanorama({ location: event.latLng, radius: 50 })
      .then(processSVData)
      .catch((e) =>
        console.error("Street View data not found for this location.")
      );
  });
}

function processSVData({ data }: google.maps.StreetViewResponse) {
  const location = data.location!;

  const marker = new google.maps.Marker({
    position: location.latLng,
    map,
    title: location.description,
  });

  panorama.setPano(location.pano as string);
  panorama.setPov({
    heading: 270,
    pitch: 0,
  });
  panorama.setVisible(true);

  marker.addListener("click", () => {
    const markerPanoID = location.pano;

    // Set the Pano to use the passed panoID.
    panorama.setPano(markerPanoID as string);
    panorama.setPov({
      heading: 270,
      pitch: 0,
    });
    panorama.setVisible(true);
  });
}

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

JavaScript

/*
 * Click the map to set a new location for the Street View camera.
 */
let map;
let panorama;

function initMap() {
  const berkeley = { lat: 37.869085, lng: -122.254775 };
  const sv = new google.maps.StreetViewService();

  panorama = new google.maps.StreetViewPanorama(
    document.getElementById("pano")
  );
  // Set up the map.
  map = new google.maps.Map(document.getElementById("map"), {
    center: berkeley,
    zoom: 16,
    streetViewControl: false,
  });
  // Set the initial Street View camera to the center of the map
  sv.getPanorama({ location: berkeley, radius: 50 }).then(processSVData);
  // Look for a nearby Street View panorama when the map is clicked.
  // getPanorama will return the nearest pano when the given
  // radius is 50 meters or less.
  map.addListener("click", (event) => {
    sv.getPanorama({ location: event.latLng, radius: 50 })
      .then(processSVData)
      .catch((e) =>
        console.error("Street View data not found for this location.")
      );
  });
}

function processSVData({ data }) {
  const location = data.location;
  const marker = new google.maps.Marker({
    position: location.latLng,
    map,
    title: location.description,
  });

  panorama.setPano(location.pano);
  panorama.setPov({
    heading: 270,
    pitch: 0,
  });
  panorama.setVisible(true);
  marker.addListener("click", () => {
    const markerPanoID = location.pano;

    // Set the Pano to use the passed panoID.
    panorama.setPano(markerPanoID);
    panorama.setPov({
      heading: 270,
      pitch: 0,
    });
    panorama.setVisible(true);
  });
}

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;
}

HTML

<html>
  <head>
    <title>Directly Accessing Street View Data</title>
    <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>

    <link rel="stylesheet" type="text/css" href="./style.css" />
    <script type="module" src="./index.js"></script>
  </head>
  <body>
    <div id="map" style="width: 45%; height: 100%; float: left"></div>
    <div id="pano" style="width: 45%; height: 100%; float: left"></div>

    <!-- 
     The `defer` attribute causes the callback 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
     with https://www.npmjs.com/package/@googlemaps/js-api-loader.
    -->
    <script
      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initMap&v=weekly"
      defer
    ></script>
  </body>
</html>
查看範例

查看範例

提供自訂街景服務全景

Maps JavaScript API 支援在 StreetViewPanorama 物件中顯示自訂全景。使用自訂全景時,你可以顯示建築物內部、風景地點的景象,或是想像力中的任何事物。您甚至可以將這些自訂全景連結至 Google 現有的街景服務全景。

設定一組自訂全景圖像的步驟如下:

  • 為每個自訂全景圖建立基本全景圖片。此基本圖片必須是您想要以縮放方式提供的最高解析度圖片。
  • (建議選用) 利用基本圖片建立一組不同縮放等級的全景圖塊。
  • 建立自訂全景圖之間的連結。
  • (選用) 在 Google 現有的街景服務圖像中標示「進入」全景,並自訂連結至自訂組合至標準集的連結。
  • StreetViewPanoramaData 物件中的每個全景圖片定義中繼資料。
  • 實作決定自訂全景資料和圖片的方法,並在 StreetViewPanorama 物件中將該方法指定為自訂處理常式。

以下章節說明此程序。

建立自訂全景圖

每個街景服務全景都由一或多張圖片組成,提供單一位置完整的 360 度環景。 StreetViewPanorama 物件會使用採用等距長方形投影的圖片。這類投影包含 360 度的水平視圖 (完整的環景) 與 180 度的垂直視圖 (由正上方到正下方)。這些欄位會產生長寬比為 2:1 的圖片。完整的環景全景圖如下所示。

全景圖片通常以單一位置拍攝多張相片,並透過全景軟體加以拼接。(詳情請參閱維基百科的 # 相片拼接應用程式比較)。這類圖片應共用一個「相機」目標,並從中擷取多張全景圖片。接著,可使用產生的 360 度全景圖像,在球賽上定義圖片,而圖片會嵌入球體的 2D 表面。

在將圖片分割為矩形的圖塊,並根據運算的圖塊座標提供圖片時,將全景視為使用矩形座標座標系統的投影。

建立自訂全景地圖方塊

街景服務也支援使用縮放控制項,提供不同等級的圖片細節,讓您能從預設檢視中縮放地圖。一般來說,「街景服務」能為任何全景圖像提供 5 種縮放解析度。假使您仰賴單一全景圖片來提供所有縮放等級,那麼圖片的尺寸就必須相當大,且速度明顯變慢,或者在高縮放等級下,解析度過低可能會使像素化不佳。幸好,我們可以使用類似的設計模式,以不同縮放等級提供 Google 地圖圖塊,以便針對各個縮放等級的全景提供適當的解析度圖像。

根據預設,StreetViewPanorama 首次載入時,會顯示縮放等級為 1 的全景橫向廣度 25% (90 度) 的圖片。這個檢視表大致符合一般人視野。大幅縮小「淡出」預設檢視畫面。基本上,這個檢視畫面會提供更寬的弧形,同時放大會縮小檢視畫面的欄位範圍。StreetViewPanorama 會自動計算所選縮放等級的適當檢視區塊欄位,接著透過選取大致符合水平檢視區塊尺寸的圖塊組合,選取最適合該解析度的圖像。下列街景服務欄位為街景服務縮放等級對應:

街景服務縮放等級 視野 (角度)
0 180 秒
1 (預設) 90
2 45
3 22.5
4 11.25

請注意,街景服務中顯示的圖片大小完全取決於街景服務容器的螢幕大小 (寬度)。如果您提供較寬的容器,服務仍會針對任何指定的縮放等級提供相同的視野,但有可能會選取更適合該解析度的圖塊。

由於每個全景都包含一個等距矩形投影,因此建立全景圖塊相對簡單。投影功能提供 2:1 長寬比的圖片,使用正方形長寬比 2:1 的圖塊更容易使用,因為正方形圖塊在正方形地圖上可帶來更好的成效 (因為視野為方形)。

如果是 2:1 圖塊,涵蓋整個全景的單一圖片就代表縮放等級 0 的完整全景 (世界圖片),每個增加的縮放等級都會提供 4 個 zoomLevel 圖塊。(例如,縮放等級為 2 時,整個全景是由 16 個圖塊組成)。注意:街景服務圖塊的縮放等級,不會與使用街景服務控制項提供的縮放等級直接相符;而是會由街景服務控制項縮放等級選取適當的圖塊 (FoV)。

一般來說,您需要為圖片圖塊命名,以便透過程式選取。下方處理自訂全景要求一節說明瞭這類命名配置。

處理自訂全景圖要求

如要使用自訂全景,請呼叫 StreetViewPanorama.registerPanoProvider(),並指定自訂全景提供者方法的名稱。全景提供者方法必須傳回 StreetViewPanoramaData 物件,且具備下列簽章:

Function(pano):StreetViewPanoramaData

StreetViewPanoramaData 是以下形式的物件:

{
  copyright: string,
  location: {
    description: string,
    latLng: google.maps.LatLng,
    pano: string
  },
  tiles: {
    tileSize: google.maps.Size,
    worldSize: google.maps.Size,
    heading: number,
    getTileUrl: Function
  },
  links: [
    description: string,
    heading: number,
    pano: string,
    roadColor: string,
    roadOpacity: number
  ]
}

顯示自訂全景,如下所示:

注意:如果想顯示自訂全景,請勿在 StreetViewPanorama 上直接設定 position,因為這類位置會指示街景服務服務要求該位置附近的預設街景服務圖像。請改為在自訂 StreetViewPanoramaData 物件和 location.latLng 欄位中設定這個位置。

以下範例顯示 Google 雪梨辦公室的自訂全景。請注意,此範例未使用地圖或預設的街景服務圖像:

TypeScript

function initPano() {
  // Set up Street View and initially set it visible. Register the
  // custom panorama provider function. Set the StreetView to display
  // the custom panorama 'reception' which we check for below.
  const panorama = new google.maps.StreetViewPanorama(
    document.getElementById("map") as HTMLElement,
    { pano: "reception", visible: true }
  );

  panorama.registerPanoProvider(getCustomPanorama);
}

// Return a pano image given the panoID.
function getCustomPanoramaTileUrl(
  pano: string,
  zoom: number,
  tileX: number,
  tileY: number
): string {
  return (
    "https://developers.google.com/maps/documentation/javascript/examples/full/images/" +
    "panoReception1024-" +
    zoom +
    "-" +
    tileX +
    "-" +
    tileY +
    ".jpg"
  );
}

// Construct the appropriate StreetViewPanoramaData given
// the passed pano IDs.
function getCustomPanorama(pano: string): google.maps.StreetViewPanoramaData {
  if (pano === "reception") {
    return {
      location: {
        pano: "reception",
        description: "Google Sydney - Reception",
      },
      links: [],
      // The text for the copyright control.
      copyright: "Imagery (c) 2010 Google",
      // The definition of the tiles for this panorama.
      tiles: {
        tileSize: new google.maps.Size(1024, 512),
        worldSize: new google.maps.Size(2048, 1024),
        // The heading in degrees at the origin of the panorama
        // tile set.
        centerHeading: 105,
        getTileUrl: getCustomPanoramaTileUrl,
      },
    };
  }
  // @ts-ignore TODO fix typings
  return null;
}

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

JavaScript

function initPano() {
  // Set up Street View and initially set it visible. Register the
  // custom panorama provider function. Set the StreetView to display
  // the custom panorama 'reception' which we check for below.
  const panorama = new google.maps.StreetViewPanorama(
    document.getElementById("map"),
    { pano: "reception", visible: true }
  );

  panorama.registerPanoProvider(getCustomPanorama);
}

// Return a pano image given the panoID.
function getCustomPanoramaTileUrl(pano, zoom, tileX, tileY) {
  return (
    "https://developers.google.com/maps/documentation/javascript/examples/full/images/" +
    "panoReception1024-" +
    zoom +
    "-" +
    tileX +
    "-" +
    tileY +
    ".jpg"
  );
}

// Construct the appropriate StreetViewPanoramaData given
// the passed pano IDs.
function getCustomPanorama(pano) {
  if (pano === "reception") {
    return {
      location: {
        pano: "reception",
        description: "Google Sydney - Reception",
      },
      links: [],
      // The text for the copyright control.
      copyright: "Imagery (c) 2010 Google",
      // The definition of the tiles for this panorama.
      tiles: {
        tileSize: new google.maps.Size(1024, 512),
        worldSize: new google.maps.Size(2048, 1024),
        // The heading in degrees at the origin of the panorama
        // tile set.
        centerHeading: 105,
        getTileUrl: getCustomPanoramaTileUrl,
      },
    };
  }
  // @ts-ignore TODO fix typings
  return null;
}

window.initPano = initPano;

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;
}

HTML

<html>
  <head>
    <title>Custom Street View Panoramas</title>
    <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>

    <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 callback 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
     with https://www.npmjs.com/package/@googlemaps/js-api-loader.
    -->
    <script
      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initPano&v=weekly"
      defer
    ></script>
  </body>
</html>
查看範例

查看範例

自訂全景提供者會根據傳遞的全景 ID、縮放等級和全景圖塊座標,傳回適當的圖塊。由於圖片選項取決於傳遞的值,因此為這些傳遞值命名 (例如 pano_zoom_tileX_tileY.png) 時,可以使用程式為圖片命名。

以下範例除了預設的街景服務導覽箭頭之外,還會在圖片中加入另一個箭頭,指向 Google 雪梨,並連結至自訂圖像:

TypeScript

let panorama: google.maps.StreetViewPanorama;

// StreetViewPanoramaData of a panorama just outside the Google Sydney office.
let outsideGoogle: google.maps.StreetViewPanoramaData;

// StreetViewPanoramaData for a custom panorama: the Google Sydney reception.
function getReceptionPanoramaData(): google.maps.StreetViewPanoramaData {
  return {
    location: {
      pano: "reception", // The ID for this custom panorama.
      description: "Google Sydney - Reception",
      latLng: new google.maps.LatLng(-33.86684, 151.19583),
    },
    links: [
      {
        heading: 195,
        description: "Exit",
        pano: (outsideGoogle.location as google.maps.StreetViewLocation).pano,
      },
    ],
    copyright: "Imagery (c) 2010 Google",
    tiles: {
      tileSize: new google.maps.Size(1024, 512),
      worldSize: new google.maps.Size(2048, 1024),
      centerHeading: 105,
      getTileUrl: function (
        pano: string,
        zoom: number,
        tileX: number,
        tileY: number
      ): string {
        return (
          "https://developers.google.com/maps/documentation/javascript/examples/full/images/" +
          "panoReception1024-" +
          zoom +
          "-" +
          tileX +
          "-" +
          tileY +
          ".jpg"
        );
      },
    },
  };
}

function initPanorama() {
  panorama = new google.maps.StreetViewPanorama(
    document.getElementById("street-view") as HTMLElement,
    { pano: (outsideGoogle.location as google.maps.StreetViewLocation).pano }
  );
  // Register a provider for the custom panorama.
  panorama.registerPanoProvider(
    (pano: string): google.maps.StreetViewPanoramaData => {
      if (pano === "reception") {
        return getReceptionPanoramaData();
      }
      // @ts-ignore TODO fix typings
      return null;
    }
  );

  // Add a link to our custom panorama from outside the Google Sydney office.
  panorama.addListener("links_changed", () => {
    if (
      panorama.getPano() ===
      (outsideGoogle.location as google.maps.StreetViewLocation).pano
    ) {
      panorama.getLinks().push({
        description: "Google Sydney",
        heading: 25,
        pano: "reception",
      });
    }
  });
}

function initMap(): void {
  // Use the Street View service to find a pano ID on Pirrama Rd, outside the
  // Google office.
  new google.maps.StreetViewService()
    .getPanorama({ location: { lat: -33.867386, lng: 151.195767 } })
    .then(({ data }: google.maps.StreetViewResponse) => {
      outsideGoogle = data;
      initPanorama();
    });
}

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

JavaScript

let panorama;
// StreetViewPanoramaData of a panorama just outside the Google Sydney office.
let outsideGoogle;

// StreetViewPanoramaData for a custom panorama: the Google Sydney reception.
function getReceptionPanoramaData() {
  return {
    location: {
      pano: "reception",
      description: "Google Sydney - Reception",
      latLng: new google.maps.LatLng(-33.86684, 151.19583),
    },
    links: [
      {
        heading: 195,
        description: "Exit",
        pano: outsideGoogle.location.pano,
      },
    ],
    copyright: "Imagery (c) 2010 Google",
    tiles: {
      tileSize: new google.maps.Size(1024, 512),
      worldSize: new google.maps.Size(2048, 1024),
      centerHeading: 105,
      getTileUrl: function (pano, zoom, tileX, tileY) {
        return (
          "https://developers.google.com/maps/documentation/javascript/examples/full/images/" +
          "panoReception1024-" +
          zoom +
          "-" +
          tileX +
          "-" +
          tileY +
          ".jpg"
        );
      },
    },
  };
}

function initPanorama() {
  panorama = new google.maps.StreetViewPanorama(
    document.getElementById("street-view"),
    { pano: outsideGoogle.location.pano }
  );
  // Register a provider for the custom panorama.
  panorama.registerPanoProvider((pano) => {
    if (pano === "reception") {
      return getReceptionPanoramaData();
    }
    // @ts-ignore TODO fix typings
    return null;
  });
  // Add a link to our custom panorama from outside the Google Sydney office.
  panorama.addListener("links_changed", () => {
    if (panorama.getPano() === outsideGoogle.location.pano) {
      panorama.getLinks().push({
        description: "Google Sydney",
        heading: 25,
        pano: "reception",
      });
    }
  });
}

function initMap() {
  // Use the Street View service to find a pano ID on Pirrama Rd, outside the
  // Google office.
  new google.maps.StreetViewService()
    .getPanorama({ location: { lat: -33.867386, lng: 151.195767 } })
    .then(({ data }) => {
      outsideGoogle = data;
      initPanorama();
    });
}

window.initMap = initMap;

CSS

html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

#street-view {
  height: 100%;
}

HTML

<html>
  <head>
    <title>Custom Street View Panorama Tiles</title>
    <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>

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

    <!-- 
     The `defer` attribute causes the callback 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
     with https://www.npmjs.com/package/@googlemaps/js-api-loader.
    -->
    <script
      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initMap&v=weekly"
      defer
    ></script>
  </body>
</html>
查看範例

查看範例