Overlays Within Street View

Sample code for Google Maps Platform JavaScript API author(s): Justin Poehnelt

by wintlu

HTML

<!doctype html>
<!--
 @license
 Copyright 2019 Google LLC. All Rights Reserved.
 SPDX-License-Identifier: Apache-2.0
-->
<html>
  <head>
    <title>Overlays Within Street View</title>

    <!-- jsFiddle will insert css and js -->
  </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 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&libraries=marker&v=weekly"
      defer
    ></script>
  </body>
</html>

CSS

/* Ensure map fills the window */
html, body {
  height: 100%;
  margin: 0;
  padding: 0;
}

#map {
  height: 100%;
}

/* --- Custom Marker Styles --- */

.custom-marker {
  position: absolute;
  cursor: pointer;
  /* Center the marker's bottom tip on the coordinates */
  transform: translate(-50%, -100%); 
}

/* The visual pin */
.marker-pin {
  width: 50px;
  height: 50px;
  background: #ff0000;
  border-radius: 50% 50% 50% 0;
  transform: rotate(-45deg);
  box-shadow: 0 0 10px rgba(0,0,0,0.5);
  display: flex;
  justify-content: center;
  align-items: center;
  animation: bounce 1s infinite alternate; 
}

/* Inner white dot */
.marker-pin::after {
  content: '';
  width: 20px;
  height: 20px;
  background: white;
  border-radius: 50%;
}

/* Pulse effect on the ground */
.marker-pulse {
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 20px;
  height: 10px;
  background: rgba(255, 0, 0, 0.5);
  border-radius: 50%;
  animation: pulse 1s infinite alternate;
  z-index: -1;
}

@keyframes bounce {
  0% { transform: rotate(-45deg) translateY(0); }
  100% { transform: rotate(-45deg) translateY(-10px); }
}

@keyframes pulse {
  0% { transform: translateX(-50%) scale(1); opacity: 0.8; }
  100% { transform: translateX(-50%) scale(2); opacity: 0.2; }
}

JavaScript

function initMap() {
  const bankPos = { lat: 40.729681, lng: -73.991138 };

  const map = new google.maps.Map(document.getElementById("map"), {
    zoom: 19,
    center: bankPos,
  });

  // --- 1. DEFINE THE SVG ICON (The "Zero Lag" Solution) ---
  // This SVG string recreates your red pin design.
  // We use this instead of an HTML div so it renders IN the 3D world.
  const pinSvg = `
    <svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 50 50">
      <path d="M 25,50 C 10,35 0,25 0,18 C 0,8 8,0 25,0 C 42,0 50,8 50,18 C 50,25 40,35 25,50 Z" fill="#ff0000"/>
      <circle cx="25" cy="18" r="8" fill="white"/>
    </svg>`;

  // Convert the SVG string into a data URL
  const iconUrl = 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(pinSvg);

  const iconOptions = {
    url: iconUrl,
    scaledSize: new google.maps.Size(60, 60), // Size of the marker
    anchor: new google.maps.Point(30, 60),    // Anchor at bottom-center (x=width/2, y=height)
  };

  // --- 2. SETUP STREET VIEW ---
  const svService = new google.maps.StreetViewService();
  const panorama = map.getStreetView();

  // Find the nearest OUTDOOR panorama
  svService.getPanorama({
    location: bankPos,
    radius: 50,
    source: google.maps.StreetViewSource.OUTDOOR
  }).then(({ data }) => {
    panorama.setPano(data.location.pano);
    panorama.setPov({
      heading: 265,
      pitch: 0,
    });
  }).catch((err) => {
    console.error("No outdoor street view found:", err);
  });

  // --- 3. CREATE THE NATIVE MARKERS ---
  
  // Marker for the 2D Map
  new google.maps.Marker({
    position: bankPos,
    map: map,
    icon: iconOptions,
    title: "2D Map Marker"
  });

  // Marker for the 3D Street View (This will now be perfectly synced)
  new google.maps.Marker({
    position: bankPos,
    map: panorama, // Attach directly to the panorama
    icon: iconOptions,
    title: "Street View Marker"
  });

 ...