Présentation
Google Street View offre des vues panoramiques à 360 degrés à partir des routes sélectionnées dans sa zone de couverture. La couverture de l'API de Street View est identique à celle de l'application Google Maps (https://maps.google.com/
). La liste des villes actuellement acceptées pour Street View est disponible sur le site Web Google Maps.
Voici un exemple d'image Street View.
L'API Maps JavaScript fournit un service Street View permettant d'obtenir et de manipuler les images utilisées dans Google Maps Street View. Ce service Street View est pris en charge de façon native dans le navigateur.
Utilisation des cartes Street View
Bien que Street View puisse être utilisé au sein d'un élément DOM autonome, il est plus utile lorsqu'il indique un lieu sur une carte. Par défaut, Street View est activé sur une carte et une commande Pegman Street View est intégrée dans les commandes de navigation (zoom et panorama). Vous pouvez masquer cette commande dans les MapOptions
de la carte en définissant streetViewControl
sur false
. Vous pouvez également changer la position par défaut de la commande Street View en définissant la propriété streetViewControlOptions.position
de Map
sur une nouvelle valeur ControlPosition
.
La commande Pegman Street View vous permet d'afficher des panoramas Street View directement sur la carte. Lorsque l'utilisateur clique sur la commande Pegman et maintient le bouton de la souris enfoncé, la carte est mise à jour pour afficher un contour bleu autour des rues pour lesquelles Street View est activé, lui offrant ainsi une expérience semblable à celle de l'application Google Maps.
Lorsque l'utilisateur place le marqueur Pegman sur une rue, la carte est mise à jour pour afficher un panorama Street View du lieu indiqué.
Panoramas Street View
Les images Street View sont prises en charge via l'utilisation de l'objet StreetViewPanorama
, lequel fournit une interface API aux utilisateurs de Street View. Chaque carte contient un panorama Street View par défaut, que vous pouvez récupérer en appelant la méthode getStreetView()
de la carte. Lorsque vous ajoutez une commande Street View à la carte en définissant son option streetViewControl
sur true
, vous associez automatiquement la commande Pegman à ce panorama Street View par défaut.
Vous pouvez également créer votre propre objet StreetViewPanorama
et configurer la carte pour qu'elle utilise cet objet plutôt que l'objet par défaut, en définissant explicitement la propriété streetView
sur cet objet construit. Il peut être judicieux d'annuler le panorama par défaut si vous souhaitez modifier le comportement par défaut, comme le partage automatique des superpositions entre la carte et le panorama (voir la section Superpositions dans Street View ci-dessous).
Conteneurs Street View
À la place, il peut être plus judicieux d'afficher un StreetViewPanorama
dans un élément DOM séparé, souvent un élément <div>
.
Il vous suffit de transmettre l'élément DOM au constructeur du StreetViewPanorama
. Pour un affichage optimal des images, nous recommandons une taille minimale de 200 x 200 pixels.
Remarque : Bien que la fonctionnalité Street View soit conçue pour être utilisée conjointement avec une carte, cela n'est pas obligatoire. Vous pouvez utiliser un objet Street View autonome sans carte.
Lieux et point de vue Street View
Le constructeur StreetViewPanorama
vous permet également de définir un lieu et un point de vue (POV) Street View en utilisant le paramètre StreetViewOptions
. Vous pouvez appeler
setPosition()
et setPov()
sur l'objet après la construction pour modifier son lieu et son point de vue.
Le lieu Street View définit la position de l'appareil photo pour une image, mais pas son orientation pour cette image. À cette fin, l'objet StreetViewPov
définit deux propriétés :
heading
(0
par défaut) définit l'angle de rotation en degrés autour de l'appareil photo par rapport au nord géographique. Les caps sont mesurés en sens horaire (90 indique l'est géographique).pitch
(0
par défaut) définit l'écart de l'angle vers le "haut" ou le "bas" à partir de l'inclinaison par défaut initiale de l'appareil photo, qui correspond généralement (mais pas toujours) à un angle horizontal plat. Par exemple, une image prise sur une colline présentera probablement une inclinaison par défaut qui n'est pas horizontale. Les angles d'inclinaison sont exprimés avec des valeurs positives vers le haut (jusqu'à +90 degrés vers le haut et à angle droit par rapport à l'inclinaison par défaut) et des valeurs négatives vers le bas (jusqu'à -90 vers le bas et à angle droit par rapport à l'inclinaison par défaut).
L'objet StreetViewPov
sert généralement à déterminer le point de vue de l'appareil photo Street View. Vous pouvez également déterminer le point de vue du photographe (en général, la direction à laquelle faisait face la voiture ou le tricycle) via la méthode StreetViewPanorama.getPhotographerPov()
.
Le code suivant affiche une carte de Boston avec une vue initiale de Fenway Park. Si vous sélectionnez la commande Pegman et la faites glisser sur un lieu pris en charge de la carte, cela modifie le panorama 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> <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 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=initialize&v=weekly" defer ></script> </body> </html>
Essayer l'exemple
Suivi du mouvement sur les appareils mobiles
Sur les smartphones et tablettes compatibles avec les événements d'orientation de l'appareil, l'API permet de changer le point de vue Street View en fonction du mouvement de l'appareil. Les utilisateurs peuvent ainsi regarder autour d'eux en bougeant leur appareil. C'est ce qu'on appelle le suivi du mouvement ou le suivi de rotation de l'appareil.
En tant que développeur d'applications, vous pouvez modifier le comportement par défaut comme suit :
- Activer ou désactiver la fonctionnalité de suivi du mouvement. Par défaut, le suivi du mouvement est activé sur tous les appareils qui prennent en charge cette fonctionnalité. L'exemple suivant désactive le suivi du mouvement, mais laisse la commande de suivi du mouvement visible.
(Notez que l'utilisateur peut activer le suivi du mouvement en appuyant sur la commande.)
var panorama = new google.maps.StreetViewPanorama( document.getElementById('pano'), { position: {lat: 37.869260, lng: -122.254811}, pov: {heading: 165, pitch: 0}, motionTracking: false });
-
Masquer ou afficher la commande de suivi du mouvement. Par défaut, la commande est affichée sur les appareils qui prennent en charge le suivi du mouvement. L'utilisateur peut appuyer sur la commande pour activer ou désactiver le suivi du mouvement. Notez que la commande ne sera jamais affichée si l'appareil ne prend pas en charge le suivi du mouvement, quelle que soit la valeur de
motionTrackingControl
.L'exemple suivant désactive le suivi du mouvement et masque la commande associée. Dans cet exemple, l'utilisateur ne peut donc pas activer le suivi du mouvement :
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 });
- Modifier la position par défaut de la commande de suivi du mouvement. Par défaut, elle s'affiche en bas à droite du panorama (position
RIGHT_BOTTOM
). L'exemple suivant définit la position de la commande en bas à gauche :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 } });
Pour voir le suivi du mouvement en action, affichez l'exemple suivant sur un appareil mobile (ou tout autre appareil prenant en charge les événements d'orientation de l'appareil) :
Superpositions dans Street View
L'objet StreetViewPanorama
par défaut est compatible avec l'affichage natif des superpositions de carte.
Les superpositions apparaissent en général au "niveau de la rue", ancrées à des positions LatLng
. (Des repères s'affichent avec leur "pointe" ancrée sur le plan horizontal du lieu dans le panorama Street View, par exemple.)
Actuellement, les types de superpositions pris en charge sur les panoramas Street View sont limités aux Marker
et InfoWindow
, ainsi qu'aux OverlayView
personnalisées. Les superpositions que vous affichez sur une carte peuvent être affichées sur un panorama Street View en traitant le panorama comme un substitut de l'objet Map
, en appelant la méthode setMap()
et en transmettant StreetViewPanorama
en tant qu'argument plutôt qu'en tant que carte. Il est également possible d'ouvrir des fenêtres d'info dans un panorama Street View en appelant open()
et en transmettant le StreetViewPanorama()
plutôt qu'une carte.
Par ailleurs, lors de la création d'une carte avec un StreetViewPanorama
par défaut, tout repère créé sur la carte est automatiquement partagé avec le panorama Street View associé à la carte, sous réserve que le panorama soit visible. Pour récupérer le panorama Street View par défaut, appelez getStreetView()
sur l'objet Map
. Notez que si vous définissez explicitement la propriété streetView
de la carte sur un StreetViewPanorama
que vous avez vous-même construit, vous ignorez le panorama par défaut.
L'exemple suivant illustre des marqueurs indiquant différents lieux autour d'Astor Place, à New York. Basculez l'affichage sur Street View pour faire apparaître les marqueurs partagés affichés dans le 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); const cafeIcon = document.createElement("img"); cafeIcon.src = "https://developers.google.com/maps/documentation/javascript/examples/full/images/cafe_icon.svg"; const dollarIcon = document.createElement("img"); dollarIcon.src = "https://developers.google.com/maps/documentation/javascript/examples/full/images/bank_icon.svg"; const busIcon = document.createElement("img"); busIcon.src = "https://developers.google.com/maps/documentation/javascript/examples/full/images/bus_icon.svg"; // Set up the markers on the map const cafeMarker = new google.maps.Marker({ position: { lat: 40.730031, lng: -73.991428 }, map, title: "Cafe", icon: cafeIcon.src, }); const bankMarker = new google.maps.Marker({ position: { lat: 40.729681, lng: -73.991138 }, map, title: "Bank", icon: dollarIcon.src, }); const busMarker = new google.maps.Marker({ position: { lat: 40.729559, lng: -73.990741 }, map, title: "Bus Stop", icon: busIcon.src, }); // 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); const cafeIcon = document.createElement("img"); cafeIcon.src = "https://developers.google.com/maps/documentation/javascript/examples/full/images/cafe_icon.svg"; const dollarIcon = document.createElement("img"); dollarIcon.src = "https://developers.google.com/maps/documentation/javascript/examples/full/images/bank_icon.svg"; const busIcon = document.createElement("img"); busIcon.src = "https://developers.google.com/maps/documentation/javascript/examples/full/images/bus_icon.svg"; // Set up the markers on the map const cafeMarker = new google.maps.Marker({ position: { lat: 40.730031, lng: -73.991428 }, map, title: "Cafe", icon: cafeIcon.src, }); const bankMarker = new google.maps.Marker({ position: { lat: 40.729681, lng: -73.991138 }, map, title: "Bank", icon: dollarIcon.src, }); const busMarker = new google.maps.Marker({ position: { lat: 40.729559, lng: -73.990741 }, map, title: "Bus Stop", icon: busIcon.src, }); // 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> <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 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>
Essayer l'exemple
Événements Street View
Lorsque vous naviguez dans Street View ou manipulez son orientation, vous pourriez vouloir surveiller plusieurs événements indiquant que des changements ont été apportés à l'état du StreetViewPanorama
:
pano_changed
se déclenche chaque fois que l'identifiant individuel du panorama est modifié. Cet événement ne signifie pas que toute donnée associée dans le panorama (comme les liens) a également été modifiée au moment de son déclenchement. Il indique seulement qu'un identifiant de panorama a été modifié. Notez que l'identifiant de panorama (que vous pouvez utiliser pour référencer ce panorama) n'est conservé que pendant la session en cours du navigateur.position_changed
se déclenche chaque fois que la position (LatLng
) sous-jacente du panorama est modifiée. Faire pivoter un panorama ne déclenche pas cet événement. Notez qu'il est possible de changer la position sous-jacente d'un panorama sans modifier l'identifiant de panorama associé, étant donné que l'API associe automatiquement l'identifiant de panorama le plus proche à la position du panorama.pov_changed
se déclenche chaque fois que leStreetViewPov
de Street View est modifié. Notez que cet événement peut se déclencher même si la position et l'identifiant de panorama restent stables.links_changed
se déclenche chaque fois que les liens de Street View sont modifiés. Notez que cet événement peut se déclencher de manière asynchrone après une modification dans l'identifiant du panorama, indiquée viapano_changed
.visible_changed
se déclenche chaque fois que la visibilité de Street View est modifiée. Notez que cet événement peut se déclencher de manière asynchrone après une modification dans l'identifiant du panorama, indiquée viapano_changed
.
Le code suivant illustre la façon dont ces événements peuvent être gérés pour collecter des données sur l'objet StreetViewPanorama
sous-jacent :
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> <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"> </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"> </td> </tr> <table id="links_table"></table> </table> </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=initPano&v=weekly" defer ></script> </body> </html>
Essayer l'exemple
Commandes Street View
Lorsqu'un StreetViewPanorama
est affiché, différentes commandes sont présentes sur le panorama par défaut. Vous pouvez les activer ou désactiver ces contrôles en définissant les champs appropriés dans les StreetViewPanoramaOptions
sur true
ou false
:
panControl
permet de faire pivoter un panorama. Cette commande est affichée par défaut sous la forme d'une boussole standard intégrée et d'une commande de panorama. Vous pouvez modifier sa position en indiquantPanControlOptions
dans le champpanControlOptions
.zoomControl
permet de zoomer sur l'image. Cette commande apparaît par défaut dans la partie inférieure droite du panorama. Vous pouvez modifier son aspect en indiquantZoomControlOptions
dans le champzoomControlOptions
.addressControl
fournit une superposition de texte indiquant l'adresse du lieu associé, accompagnée d'un lien pour l'ouvrir dans Google Maps. Vous pouvez modifier son aspect en indiquantStreetViewAddressControlOptions
dans le champaddressControlOptions
.fullscreenControl
permet d'ouvrir Street View en mode plein écran. Vous pouvez modifier son aspect en indiquantFullscreenControlOptions
dans le champfullscreenControlOptions
.motionTrackingControl
permet d'activer ou de désactiver le suivi du mouvement sur les appareils mobiles. Cette commande n'est affichée que sur les appareils qui prennent en charge les événements d'orientation de l'appareil. Par défaut, elle est affichée en bas à droite du panorama. Vous pouvez modifier sa position en indiquantMotionTrackingControlOptions
. Pour en savoir plus, consultez la section sur le suivi du mouvement.linksControl
affiche des flèches de direction sur l'image pour accéder aux images panoramiques adjacentes.- La commande Fermer permet à l'utilisateur de fermer la vue Street View. Vous pouvez l'activer ou la désactiver en définissant
enableCloseButton
surtrue
oufalse
.
L'exemple suivant modifie les commandes affichées dans la vue Street View associée et supprime les liens de la vue :
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> <link rel="stylesheet" type="text/css" href="./style.css" /> <script type="module" src="./index.js"></script> </head> <body> <div id="map"></div> <!-- The `defer` attribute causes the script to execute after the full HTML document has been parsed. For non-blocking uses, avoiding race conditions, and consistent behavior across browsers, consider loading using Promises. See https://developers.google.com/maps/documentation/javascript/load-maps-js-api for more information. --> <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initPano&v=weekly" defer ></script> </body> </html>
Essayer l'exemple
Accéder directement aux données Street View
Il peut être utile de déterminer par programmation la disponibilité des données Street View, ou d'afficher des informations sur des panoramas en particulier, sans exiger la manipulation directe d'une carte/d'un panorama. Pour ce faire, vous pouvez utiliser l'objet StreetViewService
, qui fournit une interface vers les données stockées dans le service Street View de Google.
Requêtes au service Street View
L'API Google Maps devant appeler un serveur externe, l'accès au service Street View est asynchrone. Pour cette raison, vous devez transmettre une méthode de rappel à exécuter à la fin de la requête. Cette méthode de rappel traite le résultat.
Vous pouvez envoyer des requêtes au StreetViewService
à l'aide de StreetViewPanoRequest
ou de StreetViewLocationRequest
.
Une requête avec StreetViewPanoRequest
renvoie des données de panorama en fonction d'un ID de référence qui identifie de manière unique le panorama. Notez que ces identifiants de référence ne sont stables que pendant la durée de vie des images du panorama en question.
Une requête avec StreetViewLocationRequest
recherche des données de panorama à un emplacement spécifié, à l'aide des paramètres suivants :
location
spécifie le lieu (latitude et longitude) dans lequel rechercher un panorama.preference
définit une préférence pour le panorama à rechercher dans le rayon : celui le plus proche de l'emplacement spécifié ou le meilleur dans ce rayon.radius
définit un rayon, spécifié en mètres, dans lequel rechercher un panorama, centré sur la latitude et la longitude données. Lorsqu'il n'est pas fourni, la valeur par défaut est 50.source
spécifie la source des panoramas à rechercher. Voici les valeurs possibles :default
utilise les sources par défaut pour Street View. Les recherches ne sont pas limitées à des sources spécifiques.outdoor
limite les recherches aux collections extérieures. Notez qu'il est possible que les panoramas extérieurs ne soient pas disponibles pour le lieu spécifié.
Réponses du service Street View
La fonction getPanorama()
nécessite une fonction de rappel à exécuter au moment de la récupération du résultat auprès du service Street View. Cette fonction de rappel renvoie un ensemble de données de panorama dans un objet StreetViewPanoramaData
et un code StreetViewStatus
précisant le statut de la requête, dans cet ordre.
Les spécifications d'un objet StreetViewPanoramaData
contiennent des métadonnées sur un panorama Street View, sous la forme suivante :
{ "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 } }
Notez que cet objet de données n'est pas lui-même un objet StreetViewPanorama
. Pour créer un objet Street View en utilisant ces données, il vous faudrait créer un StreetViewPanorama
et appeler setPano()
en lui transmettant l'ID indiqué dans le champ location.pano
renvoyé.
Le code status
peut renvoyer l'une des valeurs suivantes :
OK
indique que le service a trouvé un panorama correspondant.ZERO_RESULTS
indique que le service n'a pu trouver aucun panorama correspondant avec les critères transmis.UNKNOWN_ERROR
indique qu'une requête Street View n'a pas pu être traitée, bien que la raison exacte soit inconnue.
Le code suivant crée un StreetViewService
qui répond aux clics de l'utilisateur sur une carte en créant des repères qui, lorsque l'on clique dessus, affichent un StreetViewPanorama
pour le lieu sélectionné. Le code utilise le contenu de l'objet StreetViewPanoramaData
renvoyé par le service.
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> <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 script to execute after the full HTML document has been parsed. For non-blocking uses, avoiding race conditions, and consistent behavior across browsers, consider loading using Promises. See https://developers.google.com/maps/documentation/javascript/load-maps-js-api for more information. --> <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initMap&v=weekly" defer ></script> </body> </html>
Essayer l'exemple
Fournir des panoramas Street View personnalisés
L'API Maps JavaScript prend en charge l'affichage de panoramas personnalisés au sein de l'objet StreetViewPanorama
. En utilisant des panoramas personnalisés, vous pouvez afficher l'intérieur de bâtiments, des vues de lieux pittoresques ou tout ce que vous pouvez imaginer. Vous pouvez même créer des liens entre ces panoramas personnalisés et les panoramas Street View existants de Google.
Les étapes pour créer un jeu d'images panoramiques personnalisées sont les suivantes :
- Créez une image panoramique de base pour chaque panorama personnalisé. Cette image de base doit offrir la résolution la plus haute à laquelle vous souhaitez que l'utilisateur puisse zoomer.
- (Facultatif, mais recommandé) Créez un ensemble de tuiles panoramiques à différents niveaux de zoom pour l'image de base.
- Créez des liens entre vos panoramas personnalisés.
- (Facultatif) Désignez des panoramas d'"entrée" avec les images Street View existantes de Google et personnalisez les liens depuis/vers l'ensemble personnalisé et l'ensemble standard.
- Définissez les métadonnées de chaque image de panorama au sein d'un objet
StreetViewPanoramaData
. - Implémentez une méthode permettant d'identifier les données et images du panorama personnalisé et désignez cette méthode comme gestionnaire personnalisé dans l'objet
StreetViewPanorama
.
Les sections suivantes décrivent ce processus.
Créer des panoramas personnalisés
Chaque panorama Street View est une image, ou une série d'images, représentant une vue complète à 360 degrés d'un même lieu.
L'objet StreetViewPanorama
utilise des images conformes à la projection équirectangulaire (Plate Carrée), qui contient une vue horizontale à 360 degrés (tour d'horizon complet) et une vue verticale à 180 degrés (du zénith au nadir). Ce champ de vision se traduit par une image avec un rapport hauteur-largeur de 2:1. Un panorama complet à 360 degrés est illustré ci-dessous.
Les images panoramiques sont généralement obtenues en prenant plusieurs photos à partir d'une même position et en les assemblant au moyen d'un logiciel de panorama. Pour plus d'informations, reportez-vous à l'article Comparison of photo stitching applications sur Wikipédia. Chaque image individuelle composant l'image panoramique doit être prise avec l'appareil photo situé au même endroit. Le panorama à 360 degrés ainsi obtenu définit une projection sur une sphère, l'image recouvrant la surface à deux dimensions de cette sphère.
Traiter le panorama en tant que projection sur une sphère avec un système de coordonnées rectilignes s'avère avantageux pour diviser les images en tuiles rectilignes et les afficher en fonction de coordonnées de tuile calculées.
Créer des tuiles de panorama personnalisées
Street View prend également en charge différents niveaux de détails des images via l'utilisation d'une commande de zoom qui vous permet d'agrandir et de réduire la vue par défaut. En règle générale, Street View fournit cinq niveaux de résolution de zoom pour n'importe quelle image de panorama donnée. Si vous n'aviez qu'une seule image de panorama pour tous les niveaux de zoom, cette image devrait soit être volumineuse au risque de considérablement ralentir votre application, soit avoir une résolution médiocre au risque d'apparaître pixélisée aux niveaux de zoom supérieurs. Heureusement, nous pouvons utiliser un modèle de conception similaire pour afficher des tuiles de carte Google à différents niveaux de zoom et ainsi fournir une résolution d'image adaptée à chaque niveau de zoom pour les panoramas.
Lorsqu'un objet StreetViewPanorama
est chargé pour la première fois, il affiche par défaut une image représentant 25 % (90 degrés d'un arc) du champ visuel horizontal du panorama au niveau de zoom 1. Cette vue correspond approximativement au champ de vision humain standard Faire un zoom arrière sur cette vue par défaut fournit un arc plus large, tandis que faire un zoom avant réduit le champ de vision à un arc plus petit. Le StreetViewPanorama
calcule automatiquement le champ de vision approprié pour le niveau de zoom sélectionné, puis sélectionne les images les mieux adaptées à cette résolution en choisissant une tuile correspondant approximativement aux dimensions du champ de vision horizontal. Voici un tableau des champs de vision avec leur niveau de zoom Street View correspondant :
Niveau de zoom Street View | Champ de vision (en degrés) |
---|---|
0 | 180 |
1 (par défaut) | 90 |
2 | 45 |
3 | 22,5 |
4 | 11,25 |
Notez que la taille de l'image affichée dans Street View dépend entièrement de la taille d'écran (largeur) du conteneur Street View. Si vous spécifiez un conteneur plus large, le service continue de fournir le même champ de vision pour n'importe quel niveau de zoom donné, bien qu'il puisse sélectionner des tuiles mieux adaptées à cette résolution.
Étant donné que chaque panorama consiste en une projection équirectangulaire, créer des tuiles de panorama est relativement simple. Comme la projection fournit une image avec un rapport hauteur-largeur de 2:1, il est plus simple d'utiliser des tuiles offrant le même rapport, bien que des tuiles carrées puissent offrir une meilleure performance sur les cartes carrées (dans la mesure où le champ de vision sera lui-même carré).
Pour les tuiles offrant un rapport de 2:1, une seule image englobant tout le panorama représente le "monde" entier du panorama (l'image de base) au niveau de zoom 0, avec chaque niveau de zoom supérieur proposant des tuiles 4zoomLevel. (par exemple, au niveau de zoom 2, le panorama consiste en 16 tuiles). Remarque : les niveaux de zoom dans le système de tuiles de Street View ne correspondent pas directement aux niveaux de zoom fournis en utilisant la commande Street View ; à la place, les niveaux de zoom de la commande Street View sélectionnent un champ de vision à partir duquel les tuiles appropriées sont sélectionnées.
En général, il est recommandé de nommer vos tuiles d'image de manière à pouvoir les sélectionner par programmation. Ce schéma de dénomination est abordé plus bas dans la section Gérer les requêtes de panorama personnalisé.
Gérer les requêtes de panorama personnalisé
Pour utiliser un panorama personnalisé, appelez StreetViewPanorama.registerPanoProvider()
en spécifiant le nom de votre méthode de fournisseur de panorama personnalisé. La méthode du fournisseur de panoramas doit renvoyer un objet StreetViewPanoramaData
et présenter la signature suivante :
Function(pano):StreetViewPanoramaData
Un StreetViewPanoramaData
est un objet au format suivant :
{ 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 ] }
Affichez un panorama personnalisé comme suit :
- Définissez la propriété
StreetViewPanoramaOptions.pano
sur une valeur personnalisée. - Appelez
StreetViewPanorama.registerPanoProvider()
pour fournir une fonction de fournisseur de panorama personnalisé. - Implémentez votre fonction de fournisseur de panoramas personnalisés pour gérer la valeur
pano
spécifiée. - Créez un objet
StreetViewPanoramaData
. - Définissez la propriété
StreetViewTileData.getTileUrl
sur le nom d'une fonction de fournisseur de tuiles personnalisées que vous renseignez. Par exemple,getCustomPanoramaTileUrl
. - Implémentez votre fonction de fournisseur de tuiles personnalisées, comme indiqué dans les exemples ci-dessous.
- Renvoyez l'objet
StreetViewPanoramaData
.
Remarque : Ne définissez pas directement une position
sur l'objet StreetViewPanorama
lorsque vous souhaitez afficher des panoramas personnalisés, car une telle position indiquera au service Street View d'utiliser l'image Street View par défaut à proximité du lieu en question. Définissez plutôt cette position dans le champ location.latLng
de l'objet StreetViewPanoramaData
personnalisé.
L'exemple suivant montre un panorama personnalisé du bureau de Google à Sydney. Notez que cet exemple n'utilise pas de carte ni d'images Street View par défaut :
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> <link rel="stylesheet" type="text/css" href="./style.css" /> <script type="module" src="./index.js"></script> </head> <body> <div id="map"></div> <!-- The `defer` attribute causes the script to execute after the full HTML document has been parsed. For non-blocking uses, avoiding race conditions, and consistent behavior across browsers, consider loading using Promises. See https://developers.google.com/maps/documentation/javascript/load-maps-js-api for more information. --> <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initPano&v=weekly" defer ></script> </body> </html>
Essayer l'exemple
Le fournisseur de panorama personnalisé renvoie la tuile appropriée en fonction de l'ID de panorama transmis, du niveau de zoom et des coordonnées de tuile du panorama.
Étant donné que la sélection des images dépend des valeurs transmises, il s'avère utile de nommer les images qui peuvent être sélectionnées par programmation en fonction de ces valeurs. Par exemple, en utilisant le format pano_zoom_tileX_tileY.png
.
L'exemple suivant ajoute également une autre flèche à l'image (en plus des flèches de navigation par défaut de Street View) qui pointe vers le bureau de Google à Sydney et renvoie vers les images personnalisées :
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", // 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.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> <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 script to execute after the full HTML document has been parsed. For non-blocking uses, avoiding race conditions, and consistent behavior across browsers, consider loading using Promises. See https://developers.google.com/maps/documentation/javascript/load-maps-js-api for more information. --> <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initMap&v=weekly" defer ></script> </body> </html>