Serviço Street View

Visão geral

Selecione a plataforma: Android iOS JavaScript

O Google Street View oferece vistas panorâmicas em 360 graus de vias designadas por toda a área de cobertura. A cobertura da API Street View é a mesma do aplicativo Google Maps (https://maps.google.com/). A lista de cidades que atualmente têm o serviço está disponível no site do Google Maps.

Confira abaixo um exemplo de imagem do Street View.


A API Maps JavaScript oferece um serviço Street View para recuperar e processar as imagens usadas no Street View do Google Maps. Esse serviço tem suporte nativo no navegador.

Uso de mapas do Street View

O Street View pode ser usado em um elemento DOM independente, mas é mais útil quando indica um local em um mapa. Por padrão, o Street View é ativado nos mapas, e um controle "Pegman" dele aparece integrado aos controles de navegação (zoom e movimentação). Para ocultar o Pegman no objeto MapOptions do mapa, defina streetViewControl como false. Também é possível mudar a posição padrão dele definindo a propriedade streetViewControlOptions.position de Map como um novo objeto ControlPosition.

Com o controle "Pegman" do Street View, os panoramas do Street View aparecem diretamente no mapa. Quando o usuário clica no Pegman e mantém o botão pressionado, o mapa é atualizado para mostrar contornos azuis nas ruas que têm o Street View ativado, oferecendo uma experiência do usuário semelhante à do app Google Maps.

Quando a pessoa solta o marcador em uma rua, o mapa é atualizado para apresentar um panorama do Street View da localização indicada.

Panoramas do Street View

As imagens do Street View têm suporte por causa do objeto StreetViewPanorama, que oferece uma interface de API a um "visualizador" do serviço. Cada mapa contém um panorama padrão do Street View, que pode ser recuperado chamando o método getStreetView() do mapa. Configure a opção streetViewControl como truepara adicionar um controle do Street View ao mapa. Assim, você conecta automaticamente o controle "Pegman" a esse panorama padrão.

Você também pode criar seu próprio objeto StreetViewPanorama para o mapa usar, em vez do padrão. Para isso, defina explicitamente a propriedade streetView como esse objeto construído. Se você quiser modificar o comportamento padrão, como o compartilhamento automático das sobreposições entre o mapa e o panorama, substitua o panorama padrão. Consulte Sobreposições no Street View abaixo.

Contêineres do Street View

Se quiser mostrar um objeto StreetViewPanorama dentro de outro elemento DOM, geralmente um <div>, transmita o elemento DOM dentro do construtor do StreetViewPanorama. Para exibição ideal das imagens, recomendamos o tamanho mínimo de 200 por 200 pixels.

Observação: embora o Street View tenha sido desenvolvido para uso conjunto com um mapa, isso não é obrigatório. É possível usar um objeto Street View independe, sem um mapa.

Locais e ponto de vista (POV) do Street View

Com o construtor StreetViewPanorama, você também pode definir locais e pontos de vista usando o parâmetro StreetViewOptions. Chame setPosition() e setPov() no objeto após a construção para alterar o local e o POV.

O local do Street View define o posicionamento do foco da câmera em uma imagem, mas não a orientação dela para essa imagem. Para isso, o objeto StreetViewPov define duas propriedades:

  • heading (padrão 0): especifica o ângulo de rotação (em graus) em torno do local da câmera em relação ao norte absoluto. As direções são medidas no sentido horário (90 graus corresponde ao leste absoluto).
  • pitch (padrão 0): especifica a variação angular para cima ou para baixo em relação à inclinação inicial padrão da câmera, que costuma ser plana e horizontal. Por exemplo, uma imagem capturada em uma colina provavelmente tem um valor padrão de inclinação que não é horizontal. Os ângulos de inclinação são medidos com valores positivos olhando para cima (até +90 graus diretamente para cima e ortogonal à inclinação padrão) e valores negativos olhando para baixo (até -90 graus diretamente para baixo e ortogonal à inclinação padrão).

O objeto StreetViewPov é frequentemente usado para determinar o ponto de vista da câmera do Street View. Com o método StreetViewPanorama.getPhotographerPov(), você também pode determinar o ponto de vista do fotógrafo, que geralmente é a direção do carro ou triciclo.

O código a seguir mostra um mapa de Boston com uma vista inicial do Fenway Park. Selecione e arraste o Pegman até um local permitido no mapa para alterar o panorama do Street View:

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.
      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=initialize&v=weekly"
      defer
    ></script>
  </body>
</html>
Exemplo

Testar amostra

Rastreamento de movimento em dispositivos móveis

Em aparelhos que permitem eventos de orientação de dispositivo, a API oferece aos usuários a capacidade de mudar o ponto de vista do Street View de acordo com o movimento do aparelho. Os usuários podem olhar em volta movimentando os dispositivos. Isso é denominado rastreamento de movimento ou de rotação do dispositivo.

Como desenvolvedor de apps, você pode alterar o comportamento padrão da seguinte forma:

  • Ativar ou desativar a função de rastreamento de movimento. Por padrão, ela é ativada em qualquer dispositivo compatível. O exemplo a seguir desativa esse recurso, mas deixa o controle visível. O usuário pode ativar o rastreamento de movimento tocando no controle.
    var panorama = new google.maps.StreetViewPanorama(
        document.getElementById('pano'), {
          position: {lat: 37.869260, lng: -122.254811},
          pov: {heading: 165, pitch: 0},
          motionTracking: false
        });
    
  • Ocultar ou mostrar o controle de rastreamento de movimento. Por padrão, ele é mostrado em dispositivos compatíveis com o recurso. O usuário pode tocar no controle para ativar ou desativar o rastreamento. O controle nunca aparece em dispositivos incompatíveis, independentemente do valor de motionTrackingControl.

    O exemplo a seguir desativa tanto o rastreamento de movimento quanto o controle. Neste caso, o usuário não consegue ativar o recurso:

    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
        });
    
  • Mudar a posição padrão do controle de rastreamento de movimento. Por padrão, o controle aparece no canto inferior direito do panorama (posição RIGHT_BOTTOM). No exemplo a seguir, a posição dele muda para o canto inferior esquerdo:
    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
          }
        });
    

Acesse o exemplo a seguir em um dispositivo móvel (ou um dispositivo compatível com eventos de orientação) para conferir o rastreamento de movimento em ação:


Exemplo

Sobreposições no Street View

O objeto StreetViewPanorama padrão é compatível com a exibição nativa de sobreposições do mapa. Geralmente, elas aparecem no nível da rua, ancoradas em posições de LatLng. Por exemplo, os marcadores aparecem com os pontos ancorados no plano horizontal do local dentro do panorama do Street View.

Atualmente, apenas os tipos de sobreposição Marker, InfoWindow e OverlayView personalizados são compatíveis com panoramas do Street View. As sobreposições mostradas em um mapa poderão aparecer em um panorama se ele for tratado como um substituto do objeto Map, chamando setMap() e transmitindo StreetViewPanorama como um argumento em vez de um mapa. As janelas de informações também podem ser abertas em um panorama do Street View. Para isso, chame open(), transmitindo StreetViewPanorama() em vez de um mapa.

Além disso, na criação de um mapa com um StreetViewPanorama padrão, todos os marcadores criados são compartilhados automaticamente com o panorama do Street View associado ao mapa, contanto que o panorama seja visível. Para recuperar o panorama padrão, chame getStreetView() no objeto Map. Ao definir explicitamente a propriedade streetView do mapa como StreetViewPanorama da sua própria construção, você substitui o panorama padrão.

O exemplo a seguir mostra marcadores indicando várias localizações perto de Astor Place, cidade de Nova York. Troque a exibição para Street View e mostre os marcadores que aparecem no 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.
      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>
Exemplo

Testar amostra

Eventos do Street View

Se quiser, enquanto navega no Street View ou processa a orientação, você poderá monitorar vários eventos que indicam alterações no estado do StreetViewPanorama:

  • pano_changed é disparado sempre que o ID de um panorama individual muda. Esse evento não garante que todos os dados associados no panorama (como os links) também mudem no acionamento. Ele apenas indica a mudança do ID. O ID de panorama (que pode ser usado como referência) só é estável dentro da sessão atual do navegador.
  • position_changed é disparado sempre que a posição subjacente (LatLng) do panorama muda. A rotação não aciona este evento. É possível mudar apenas a posição subjacente de um panorama, não o ID vinculado, porque a API associa automaticamente o ID mais próximo à posição do panorama.
  • pov_changed é disparado sempre que o StreetViewPov do Street View muda. Esse evento pode ser acionado enquanto a posição e o ID do panorama permanecem estáveis.
  • links_changed é disparado sempre que os links do Street View mudam. O disparo desse evento poderá ser assíncrono após pano_changed indicar uma mudança no ID do panorama.
  • visible_changed é disparado sempre que a visibilidade do Street View muda O disparo desse evento poderá ser assíncrono após pano_changed indicar uma mudança no ID do panorama.

O código a seguir ilustra como processar esses eventos para coletar dados sobre o StreetViewPanorama subjacente:

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.
      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=initPano&v=weekly"
      defer
    ></script>
  </body>
</html>
Exemplo

Testar amostra

Controles do Street View

Ao mostrar um StreetViewPanorama, por padrão, vários controles aparecem no panorama. Eles pode ser ativados ou desativados configurando os campos apropriados no StreetViewPanoramaOptions como true ou false:

  • Com panControl, é possível girar o panorama. Ele aparece por padrão como um controle que integra bússola e movimentação. Para mudar a posição do controle, informe as PanControlOptions no campo panControlOptions.
  • Com zoomControl, é possível aplicar zoom à imagem. Ele aparece por padrão perto do canto inferior direito do panorama. Para mudar a aparência do controle, informe as ZoomControlOptions no campo zoomControlOptions.
  • addressControl oferece uma sobreposição textual que indica o endereço do local associado e um link para abrir no Google Maps. Para mudar a aparência do controle, informe as StreetViewAddressControlOptions no campo addressControlOptions.
  • fullscreenControl oferece a opção de abrir o Street View no modo de tela cheia. Para mudar a aparência do controle, informe as FullscreenControlOptions no campo fullscreenControlOptions.
  • motionTrackingControl oferece a opção de ativar ou desativar o rastreamento de movimento em dispositivos móveis. Ele aparece apenas em dispositivos compatíveis com eventos de orientação de dispositivo. Por padrão, o controle é mostrado no canto inferior esquerdo do panorama. Informe as MotionTrackingControlOptions para mudar a posição dele. Consulte mais informações na seção sobre rastreamento de movimento.
  • linksControl mostra setas de guia na imagem para acessar imagens de panorama adjacentes.
  • O usuário fecha o visualizador do Street View no controle "Fechar", que é ativado ou desativado ao definir enableCloseButton como true ou false.

O exemplo a seguir altera os controles mostrados no Street View associado e remove os links da visualização:

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.
      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=initPano&v=weekly"
      defer
    ></script>
  </body>
</html>
Exemplo

Testar amostra

Acesso direto aos dados do Street View

Se você quiser, de forma programática, determinar a disponibilidade de dados do Street View ou retornar informações sobre panoramas específicos sem exigir a manipulação direta de um mapa/panorama, use o objeto StreetViewService. Ele disponibiliza uma interface para os dados armazenados no serviço Street View do Google.

Solicitações do serviço Street View

O acesso ao serviço Street View é assíncrono, porque a API Google Maps precisa chamar um servidor externo. Portanto, é necessário transmitir um método de callback a ser executado na conclusão da solicitação. É ele que processa o resultado.

Inicie as solicitações para o StreetViewService usando StreetViewPanoRequest ou StreetViewLocationRequest.

Com StreetViewPanoRequest, são retornados dados com base em um ID de referência que identifica o panorama de forma exclusiva. Esses IDs só são estáveis durante o ciclo de vida das imagens do panorama.

Solicitações com StreetViewLocationRequest procuram dados do panorama em um local específico usando os seguintes parâmetros:

  • location: especifica o local (latitude e longitude) para procurar um panorama.
  • preference: define como preferência encontrar o panorama mais próximo do local fornecido ou o melhor dentro do raio.
  • radius: define um raio, em metros, para procurar um panorama, com o centro na latitude e longitude fornecidas. Quando não informado, é usado o padrão 50.
  • source: especifica a origem dos panoramas a serem pesquisados. Os valores válidos são:
    • default: usa as fontes padrão do Street View, sem limitar a pesquisa a origens específicas.
    • outdoor: limita as pesquisas a coleções ao ar livre. É possível que não existam panoramas externos para o local especificado.

Respostas do serviço Street View

A função getPanorama() precisa da execução de uma função de callback ao recuperar um resultado do serviço Street View. Essa função de callback retorna um conjunto de dados do panorama dentro de um objeto StreetViewPanoramaData e um código StreetViewStatus que indica o status da solicitação, nessa ordem.

A especificação de um objeto StreetViewPanoramaData contém metadados de um panorama do Street View nesta forma:

{
  "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
  }
}

Este objeto de dados não é propriamente um objeto StreetViewPanorama. Para criar um objeto Street View usando esses dados, crie um StreetViewPanorama e chame setPano(), transmitindo o ID fornecido no campo location.pano retornado.

O código status pode retornar um destes valores:

  • OK: indica que o serviço encontrou um panorama correspondente.
  • ZERO_RESULTS: indica que o serviço não encontrou um panorama correspondente com os critérios fornecidos.
  • UNKNOWN_ERROR: indica que uma solicitação do Street View não foi processada, mas o motivo exato não é conhecido.

O código a seguir cria um StreetViewService que responde aos cliques dos usuários em um mapa criando marcadores. Clique neles para mostrar um StreetViewPanorama desse local. O código usa o conteúdo de StreetViewPanoramaData retornado pelo serviço.

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.
      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>
Exemplo

Testar amostra

Apresentar panoramas personalizados do Street View

A API Maps JavaScript permite mostrar panoramas personalizados no objeto StreetViewPanorama. Com os panoramas personalizados, é possível mostrar o interior de edifícios, vistas de locais cênicos ou o que mais você imaginar. Esses panoramas personalizados podem até ser vinculados a panoramas atuais do Street View.

Configurar um conjunto de imagens de panorama personalizadas envolve estas etapas:

  • Crie uma imagem panorâmica de base para cada panorama personalizado. Essa imagem precisa estar na resolução máxima que você pretende disponibilizar com zoom.
  • (Opcional, mas recomendado) Crie um conjunto de blocos panorâmicos em níveis de zoom diferentes usando a imagem básica.
  • Crie links entre os panoramas personalizados.
  • (Opcional) Atribua panoramas "de entrada" em imagens atuais do Street View e personalize links do conjunto sob medida para o padrão e vice-versa.
  • Defina os metadados de cada imagem de panorama dentro de um objeto StreetViewPanoramaData.
  • Implemente um método que determina os dados e as imagens do panorama personalizado e defina esse método como o manipulador sob medida dentro do objeto StreetViewPanorama.

As seções a seguir explicam o processo.

Criar panoramas personalizados

Cada panorama do Street View é uma imagem ou um conjunto de imagens que oferece uma visualização completa em 360 graus de um único local. O objeto StreetViewPanorama usa imagens que estão em conformidade com a projeção equirretangular (Plate Carrée). Essa projeção contém 360 graus de visualização horizontal (uma volta completa) e 180 graus de visualização vertical (de cima para baixo). Esses campos de visão resultam em uma imagem com proporção de 2:1. Confira abaixo um panorama de volta completa.

Panorama de uma rua urbana

Geralmente, para fazer as imagens de panorama, várias fotos da mesma posição são tiradas e agrupadas usando um software de panorama. Para mais informações, consulte Comparison of photo stitching applications (em inglês) na Wikipédia. Essas imagens devem compartilhar um local único de "câmera", onde é tirada cada imagem do panorama. O panorama de 360 graus resultante define então a projeção em uma esfera, que tem toda a superfície bidimensional coberta pela imagem.

Esfera com o panorama de uma rua na superfície

É vantajoso tratar o panorama como uma projeção em uma esfera com um sistema de coordenadas retilíneas quando você divide a imagem em blocos retilíneos e veicula com base nas coordenadas de bloco calculadas.

Criar blocos de panoramas personalizados

O Street View também permite vários níveis de detalhes de imagem usando o controle de zoom, que permite ampliar e reduzir a exibição padrão. Geralmente, ele oferece cinco níveis de resolução de zoom para qualquer imagem de panorama. Para usar uma única imagem em todos os níveis de zoom, ela teria que ser muito grande, o que retardaria significativamente o aplicativo, ou teria uma resolução tão baixa nos níveis mais altos de zoom que ficaria com qualidade insatisfatória. Felizmente, podemos usar um padrão semelhante ao que mostra blocos de mapas do Google em vários níveis de zoom para disponibilizar imagens com a resolução adequada para panoramas em todos os níveis.

Quando um StreetViewPanorama é carregado pela primeira vez, o padrão é mostrar uma imagem composta de 25% (90 graus de arco) da amplitude horizontal do panorama em zoom nível 1. Isso corresponde mais ou menos a um campo de visão humana normal. Ao diminuir o zoom, é mostrado um arco maior. Aumentar o zoom estreita o campo de visão para um arco menor. O StreetViewPanorama calcula automaticamente o campo de visão apropriado para o nível de zoom selecionado e escolhe as imagens mais adequadas para a resolução. Para isso, usa um conjunto de blocos com dimensões próximas do campo de visão horizontal. Esta é a relação entre os campos de visão e os níveis de zoom do Street View:

Nível de zoom do Street View Campo de visão (graus)
0 180
1 (padrão) 90
2 45
3 22,5
4 11,25

O tamanho da imagem mostrada no Street View depende totalmente do tamanho da tela (largura) do contêiner dele. Para contêineres mais largos, o serviço continua disponibilizando o mesmo campo de visão para qualquer nível de zoom, mas escolhe blocos mais adequados à resolução.

Criar blocos de panorama é relativamente fácil, porque cada panorama é composto de uma projeção equirretangular. Como a projeção fornece uma imagem com proporção de 2:1, é mais fácil usar blocos com essa mesma proporção. Blocos quadrados oferecem melhor performance em mapas do mesmo formato (porque o campo de visão é quadrado).

Em blocos 2:1, uma única imagem que engloba o panorama inteiro representa o "mundo" dele (a imagem base) no nível de zoom 0, sendo que cada nível de zoom maior oferece blocos de 4zoomLevel. Por exemplo, no nível 2, o panorama inteiro consiste em 16 blocos. Observação: os níveis de zoom na criação de blocos do Street View não correspondem diretamente àqueles fornecidos pelo controle do Street View. Os níveis de zoom dele selecionam um campo de visão (FoV, na sigla em inglês) em que os blocos certos são selecionados.

Panorama de uma cidade dividida em blocos

É importante dar um nome aos blocos de imagem para que possam ser selecionados programaticamente. O esquema de nomenclatura é discutido abaixo, em Processar solicitações de panoramas personalizados.

Processar solicitações de panoramas personalizados

Para usar um panorama personalizado, chame StreetViewPanorama.registerPanoProvider(), especificando o nome do método do provedor. O método retorna um objeto StreetViewPanoramaData e tem a seguinte assinatura:

Function(pano):StreetViewPanoramaData

Um StreetViewPanoramaData é um objeto nesta forma:

{
  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
  ]
}

Para mostrar um panorama personalizado:

Observação: não defina diretamente um position no StreetViewPanorama quando você quiser exibir panoramas personalizados. Por exemplo, uma posição instrui o serviço Street View a solicitar as imagens padrão do Street View próximo ao local. Em vez disso, defina essa posição no campo location.latLng do objeto StreetViewPanoramaData personalizado.

O exemplo a seguir apresenta um panorama personalizado do escritório do Google em Sydney. Ele não usa mapas nem imagens padrão do Street View:

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.
      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=initPano&v=weekly"
      defer
    ></script>
  </body>
</html>
Exemplo

Testar amostra

O provedor de panoramas personalizados retorna o bloco apropriado com base no ID transmitido, no nível de zoom e nas coordenadas do bloco do panorama. Como a seleção da imagem depende desses valores transmitidos, é útil nomear as imagens que podem ser selecionadas de forma programática com base nos valores passados, como pano_zoom_tileX_tileY.png.

O exemplo a seguir adiciona outra seta à imagem, além das setas de navegação padrão do Street View. Ela aponta para o Google em Sydney e vincula às imagens personalizadas:

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.
      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>
Exemplo

Testar amostra