Обзор
Google Street View предоставляет панорамные 360-градусные виды с обозначенных дорог на всей территории покрытия. API Street View охватывает ту же территорию, что и приложение Google Maps ( https://maps.google.com/ ). Список городов, поддерживаемых в настоящее время функцией Street View, доступен на веб-сайте Google Maps .
Ниже представлен пример изображения из Street View.
API JavaScript для Google Maps предоставляет сервис Street View для получения и обработки изображений, используемых в Google Maps Street View. Этот сервис Street View поддерживается браузером нативно.
Использование карты Street View
Хотя режим просмотра улиц можно использовать в отдельном DOM-элементе , он наиболее полезен для указания местоположения на карте. По умолчанию режим просмотра улиц включен на карте, и элемент управления «Pegman» для просмотра улиц отображается интегрированным в элементы управления навигацией (масштабирование и перемещение). Вы можете скрыть этот элемент управления в MapOptions карты, установив streetViewControl в значение false . Вы также можете изменить положение элемента управления «Просмотр улиц» по умолчанию, установив свойство streetViewControlOptions.position Map в новое значение ControlPosition .
Элемент управления Street View Pegman позволяет просматривать панорамы Street View непосредственно на карте. Когда пользователь нажимает и удерживает Pegman, карта обновляется, отображая синие контуры улиц, поддерживающих Street View, что обеспечивает удобство использования, аналогичное приложению Google Maps.
Когда пользователь устанавливает маркер Pegman на улицу, карта обновляется, отображая панораму Street View указанного места.
Панорамные виды улиц
Изображения Street View поддерживаются с помощью объекта StreetViewPanorama , который предоставляет API-интерфейс для «просмотрщика» Street View. Каждая карта содержит панораму Street View по умолчанию, которую можно получить, вызвав метод getStreetView() карты. Когда вы добавляете элемент управления Street View на карту, установив для его параметра streetViewControl значение true , вы автоматически подключаете элемент управления Pegman к этой панораме Street View по умолчанию.
Вы также можете создать собственный объект StreetViewPanorama и настроить карту на использование его вместо стандартного, явно установив свойство streetView карты на этот созданный объект. Вы можете переопределить стандартную панораму, если хотите изменить поведение по умолчанию, например, автоматическое совместное использование наложений между картой и панорамой. (См. раздел «Наложения в Street View» ниже.)
Контейнеры для просмотра улиц
Вместо этого вы можете отобразить StreetViewPanorama внутри отдельного DOM-элемента, часто это элемент <div> . Просто передайте этот DOM-элемент в конструктор StreetViewPanorama . Для оптимального отображения изображений мы рекомендуем минимальный размер 200 на 200 пикселей.
Примечание: Хотя функциональность Street View разработана для использования совместно с картой, это не обязательно. Вы можете использовать автономный объект Street View без карты.
Места для просмотра улиц и точка зрения (POV)
Конструктор StreetViewPanorama также позволяет задавать местоположение и точку обзора Street View с помощью параметра StreetViewOptions . После создания объекта вы можете вызвать setPosition() и setPov() чтобы изменить его местоположение и точку обзора.
Параметр «Местоположение в режиме Street View» определяет положение фокуса камеры для изображения, но не определяет ориентацию камеры для этого изображения. Для этой цели объект StreetViewPov определяет два свойства:
-
heading(по умолчанию0) определяет угол поворота вокруг точки наблюдения камеры в градусах относительно истинного севера. Направление измеряется по часовой стрелке (90 градусов — это истинный восток). -
pitch(по умолчанию0) определяет отклонение угла «вверх» или «вниз» от исходного угла наклона камеры по умолчанию, который часто (но не всегда) является горизонтальным. (Например, изображение, сделанное на холме, скорее всего, будет иметь угол наклона по умолчанию, который не является горизонтальным.) Углы наклона измеряются положительными значениями при взгляде вверх (до +90 градусов прямо вверх и перпендикулярно углу наклона по умолчанию) и отрицательными значениями при взгляде вниз (до -90 градусов прямо вниз и перпендикулярно углу наклона по умолчанию).
Объект StreetViewPov чаще всего используется для определения точки обзора камеры Street View. Вы также можете определить точку обзора фотографа — как правило, направление, в котором был направлен автомобиль или трицикл — с помощью метода StreetViewPanorama.getPhotographerPov() .
Приведенный ниже код отображает карту Бостона с начальным видом на стадион Фенвей Парк. Выбор фигурки Pegman и ее перетаскивание в поддерживаемое место на карте изменит панораму Street View:
Машинопись
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>Попробуйте образец
Отслеживание движений на мобильных устройствах
На устройствах, поддерживающих события ориентации устройства, API предоставляет пользователям возможность изменять точку обзора Street View в зависимости от движения устройства. Пользователи могут осматриваться, перемещая свои устройства. Это называется отслеживанием движения или отслеживанием вращения устройства.
Разработчик приложения может изменить поведение по умолчанию следующим образом:
- Включение или отключение функции отслеживания движения. По умолчанию отслеживание движения включено на любом устройстве, которое его поддерживает. В следующем примере отслеживание движения отключено, но элемент управления отслеживанием движения остается видимым. (Обратите внимание, что пользователь может включить отслеживание движения, коснувшись элемента управления.)
var panorama = new google.maps.StreetViewPanorama( document.getElementById('pano'), { position: {lat: 37.869260, lng: -122.254811}, pov: {heading: 165, pitch: 0}, motionTracking: false });
Скрыть или показать элемент управления отслеживанием движения. По умолчанию этот элемент управления отображается на устройствах, поддерживающих отслеживание движения. Пользователь может коснуться элемента управления, чтобы включить или выключить отслеживание движения. Обратите внимание, что элемент управления никогда не появится, если устройство не поддерживает отслеживание движения, независимо от значения параметра
motionTrackingControl.В приведенном ниже примере отключены как отслеживание движения, так и элемент управления отслеживанием движения. В этом случае пользователь не сможет включить отслеживание движения:
var panorama = new google.maps.StreetViewPanorama( document.getElementById('pano'), { position: {lat: 37.869260, lng: -122.254811}, pov: {heading: 165, pitch: 0}, motionTracking: false, motionTrackingControl: false });
- Измените положение элемента управления отслеживанием движения по умолчанию. По умолчанию элемент управления отображается в правом нижнем углу панорамы (позиция
RIGHT_BOTTOM). В следующем примере положение элемента управления устанавливается в левом нижнем углу:var panorama = new google.maps.StreetViewPanorama( document.getElementById('pano'), { position: {lat: 37.869260, lng: -122.254811}, pov: {heading: 165, pitch: 0}, motionTrackingControlOptions: { position: google.maps.ControlPosition.LEFT_BOTTOM } });
Чтобы увидеть отслеживание движения в действии, просмотрите следующий пример на мобильном устройстве (или любом другом устройстве, поддерживающем события изменения ориентации экрана):
Наложения в режиме просмотра улиц
Объект StreetViewPanorama по умолчанию поддерживает отображение наложений карты. Наложения обычно отображаются на «уровне улицы» с привязкой к координатам LatLng . (Например, маркеры будут отображаться с хвостами, привязанными к горизонтальной плоскости местоположения в панораме Street View.)
В настоящее время на панорамах Street View поддерживаются только следующие типы наложений: Marker , InfoWindow и пользовательские OverlayView . Наложения, отображаемые на карте, можно отобразить на панораме Street View, используя панораму в качестве замены объекта Map , вызвав setMap() и передав в качестве аргумента объект StreetViewPanorama вместо карты. Аналогичным образом, информационные окна можно открыть внутри панорамы Street View, вызвав open() , передав в качестве аргумента объект StreetViewPanorama() вместо карты.
Кроме того, при создании карты с панорамой StreetViewPanorama по умолчанию все маркеры, созданные на карте, автоматически передаются связанной с картой панораме Street View, при условии, что эта панорама видна. Чтобы получить панораму Street View по умолчанию, вызовите getStreetView() для объекта Map . Обратите внимание, что если вы явно установите свойство streetView карты на StreetViewPanorama собственной разработки, вы переопределите панораму по умолчанию.
В следующем примере показаны маркеры, обозначающие различные места вокруг площади Астор-Плейс в Нью-Йорке. Переключитесь в режим просмотра улиц, чтобы отобразить общие маркеры в панораме StreetViewPanorama .
Машинопись
let panorama: google.maps.StreetViewPanorama; let innerMap: google.maps.Map; async function initMap() { // Request needed libraries. const { Map } = (await google.maps.importLibrary( 'maps' )) as google.maps.MapsLibrary; // Set the location of Astor Place. const astorPlace = { lat: 40.729884, lng: -73.990988 }; const mapElement = document.querySelector( 'gmp-map' ) as google.maps.MapElement; innerMap = mapElement.innerMap; document .getElementById('streetview-toggle-button')! .addEventListener('click', toggleStreetView); const cafeIcon = document.createElement('img'); cafeIcon.src = new URL('./public/cafe_icon.svg', import.meta.url).href; const dollarIcon = document.createElement('img'); dollarIcon.src = new URL('./public/bank_icon.svg', import.meta.url).href; const busIcon = document.createElement('img'); busIcon.src = new URL('./public/bus_icon.svg', import.meta.url).href; // Set up the markers on the map const cafeMarker = new google.maps.Marker({ position: { lat: 40.730031, lng: -73.991428 }, map: innerMap, title: 'Cafe', icon: cafeIcon.src, }); const bankMarker = new google.maps.Marker({ position: { lat: 40.729681, lng: -73.991138 }, map: innerMap, title: 'Bank', icon: dollarIcon.src, }); const busMarker = new google.maps.Marker({ position: { lat: 40.729559, lng: -73.990741 }, map: innerMap, 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 = innerMap.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); } } initMap();
JavaScript
let panorama; let innerMap; async function initMap() { // Request needed libraries. const { Map } = (await google.maps.importLibrary('maps')); // Set the location of Astor Place. const astorPlace = { lat: 40.729884, lng: -73.990988 }; const mapElement = document.querySelector('gmp-map'); innerMap = mapElement.innerMap; document .getElementById('streetview-toggle-button') .addEventListener('click', toggleStreetView); const cafeIcon = document.createElement('img'); cafeIcon.src = new URL('./public/cafe_icon.svg', import.meta.url).href; const dollarIcon = document.createElement('img'); dollarIcon.src = new URL('./public/bank_icon.svg', import.meta.url).href; const busIcon = document.createElement('img'); busIcon.src = new URL('./public/bus_icon.svg', import.meta.url).href; // Set up the markers on the map const cafeMarker = new google.maps.Marker({ position: { lat: 40.730031, lng: -73.991428 }, map: innerMap, title: 'Cafe', icon: cafeIcon.src, }); const bankMarker = new google.maps.Marker({ position: { lat: 40.729681, lng: -73.991138 }, map: innerMap, title: 'Bank', icon: dollarIcon.src, }); const busMarker = new google.maps.Marker({ position: { lat: 40.729559, lng: -73.990741 }, map: innerMap, 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 = innerMap.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); } } initMap(); export {};
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; } #streetview-toggle-button { height: 40px; display: flex; align-items: center; justify-content: center; padding: 0 17px; border: none; background: white; cursor: pointer; border-radius: 2px; box-shadow: 0 1px 4px -1px rgba(0, 0, 0, 0.3); margin: 10px 0px 10px -2px; font-family: Roboto, Arial, sans-serif; font-size: 18px; font-weight: 400; color: rgb(86, 86, 86); } #streetview-toggle-button:hover { background: #f4f4f4; color: #000; }
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>
<!-- prettier-ignore -->
<script>(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})
({key: "AIzaSyA6myHzS10YXdcazAFalmXvDkrYCp5cLc8", v: "weekly"});</script>
</head>
<body>
<gmp-map map-id="DEMO_MAP_ID" center="40.729884, -73.990988" zoom="18">
<input type="button" value="Toggle Street View" id="streetview-toggle-button" slot="control-block-start-inline-start" />
</gmp-map>
</body>
</html>Попробуйте образец
Мероприятия в режиме просмотра улиц
При переключении между режимами Street View или изменении его ориентации может потребоваться отслеживать несколько событий, указывающих на изменения состояния StreetViewPanorama :
- Событие
pano_changedсрабатывает всякий раз, когда изменяется идентификатор отдельной панорамы. Это событие не гарантирует, что какие-либо связанные данные в панораме (например, ссылки) также изменились к моменту его срабатывания; это событие лишь указывает на изменение идентификатора панорамы. Обратите внимание, что идентификатор панорамы (который можно использовать для ссылки на эту панораму) остается стабильным только в рамках текущей сессии браузера. - Событие
position_changedсрабатывает всякий раз, когда изменяется базовое (LatLng) положение панорамы. Вращение панорамы не вызовет это событие. Обратите внимание, что вы можете изменить базовое положение панорамы, не изменяя связанный с ней ID панорамы, поскольку API автоматически свяжет ближайший ID панорамы с ее положением. - Событие
pov_changedсрабатывает всякий раз, когда изменяетсяStreetViewPovв Street View. Обратите внимание, что это событие может срабатывать, даже если позиция и идентификатор панорамы остаются стабильными. - Событие
links_changedсрабатывает всякий раз, когда изменяются ссылки в Street View. Обратите внимание, что это событие может срабатывать асинхронно после изменения идентификатора панорамы, указанного черезpano_changed. - Событие
visible_changedсрабатывает всякий раз, когда изменяется видимость Street View. Обратите внимание, что это событие может срабатывать асинхронно после изменения идентификатора панорамы, указанного вpano_changed.
Приведенный ниже код демонстрирует, как можно обрабатывать эти события для сбора данных о базовой панораме StreetViewPanorama :
Машинопись
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>Попробуйте образец
Элементы управления режимом просмотра улиц
При отображении панорамы StreetViewPanorama по умолчанию на ней появляются различные элементы управления. Вы можете включить или отключить эти элементы управления, установив для соответствующих полей в параметре StreetViewPanoramaOptions значение true или false :
- Элемент
panControlпозволяет вращать панораму. По умолчанию он отображается как стандартный встроенный компас и элемент управления панорамированием. Вы можете изменить положение элемента управления, указавPanControlOptionsв полеpanControlOptions. - Элемент
zoomControlпозволяет масштабировать изображение. По умолчанию он отображается в правом нижнем углу панорамы. Вы можете изменить внешний вид элемента управления, указавZoomControlOptionsв полеzoomControlOptions. - Элемент
addressControlотображает текстовое наложение с адресом соответствующего местоположения и содержит ссылку для открытия этого местоположения в Google Maps. Вы можете изменить внешний вид элемента управления, указавStreetViewAddressControlOptionsв полеaddressControlOptions. - Элемент
fullscreenControlпредоставляет возможность открыть Street View в полноэкранном режиме. Вы можете изменить внешний вид элемента управления, указавFullscreenControlOptionsв полеfullscreenControlOptions. - Элемент
motionTrackingControlпозволяет включать или отключать отслеживание движения на мобильных устройствах. Этот элемент управления отображается только на устройствах, поддерживающих события ориентации экрана. По умолчанию элемент управления располагается в правом нижнем углу панорамы. Вы можете изменить положение элемента управления, указавMotionTrackingControlOptions. Для получения дополнительной информации см. раздел об отслеживании движения . - Элемент
linksControlотображает на изображении направляющие стрелки для перехода к соседним панорамным изображениям. - Кнопка «Закрыть» позволяет пользователю закрыть окно просмотра Street View. Вы можете включить или отключить кнопку «Закрыть», установив
enableCloseButtonвtrueилиfalse.
В следующем примере изменяются элементы управления, отображаемые в соответствующем окне Street View, и удаляются ссылки этого окна:
Машинопись
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>Попробуйте образец
Прямой доступ к данным Street View
Возможно, вам потребуется программно определять доступность данных Street View или получать информацию о конкретных панорамах, не прибегая к прямому манипулированию картой/панорамой. Это можно сделать с помощью объекта StreetViewService , который предоставляет интерфейс к данным, хранящимся в сервисе Google Street View.
Запросы на обслуживание сервиса Street View
Доступ к сервису Street View осуществляется асинхронно, поскольку API Google Maps должен обращаться к внешнему серверу. По этой причине необходимо передать метод обратного вызова , который будет выполнен после завершения запроса. Этот метод обратного вызова обрабатывает результат.
Вы можете отправлять запросы к StreetViewService , используя StreetViewPanoRequest или StreetViewLocationRequest .
Запрос с использованием StreetViewPanoRequest возвращает панорамные данные, которым присвоен уникальный идентификатор, однозначно определяющий панораму. Обратите внимание, что эти идентификаторы остаются неизменными только в течение всего срока существования изображений данной панорамы.
Запрос с использованием StreetViewLocationRequest выполняет поиск панорамных данных в указанном месте, используя следующие параметры:
-
locationуказывает местоположение (широту и долготу), где будет производиться поиск панорамы. -
preferenceзадает предпочтительный вариант панорамы в пределах заданного радиуса: ближайшую к указанному местоположению или лучшую в пределах радиуса. -
radiusзадает радиус в метрах, в пределах которого следует искать панораму, центрированную на заданных широте и долготе. По умолчанию равно 50, если не указано. -
sourceуказывает источник панорам для поиска. Допустимые значения:-
defaultиспользуются стандартные источники для просмотра улиц; поиск не ограничивается конкретными источниками. - Функция
outdoorограничивает поиск коллекциями изображений на открытом воздухе. Обратите внимание, что панорамные изображения на открытом воздухе могут отсутствовать в указанном местоположении.
-
Ответы службы Street View
Функция getPanorama() требует наличия функции обратного вызова , которая будет выполняться после получения результата от сервиса Street View. Эта функция обратного вызова возвращает набор панорамных данных в объекте StreetViewPanoramaData и код StreetViewStatus , обозначающий статус запроса, именно в таком порядке.
Спецификация объекта StreetViewPanoramaData содержит метаданные о панораме Street View следующего вида:
{ "location": { "latLng": LatLng, "description": string, "pano": string }, "copyright": string, "links": [{ "heading": number, "description": string, "pano": string, "roadColor": string, "roadOpacity": number }], "tiles": { "worldSize": Size, "tileSize": Size, "centerHeading": number } }
Обратите внимание, что этот объект данных сам по себе не является объектом StreetViewPanorama . Чтобы создать объект Street View, используя эти данные, вам потребуется создать объект StreetViewPanorama и вызвать метод setPano() , передав ему идентификатор, указанный в возвращаемом поле location.pano .
Код status может возвращать одно из следующих значений:
-
OKозначает, что сервис обнаружил подходящую панораму. -
ZERO_RESULTSозначает, что сервис не смог найти панораму, соответствующую заданным критериям. -
UNKNOWN_ERRORуказывает на то, что запрос на просмотр улиц не удалось обработать, хотя точная причина неизвестна.
Приведенный ниже код создает StreetViewService , который реагирует на щелчки пользователя на карте, создавая маркеры, при нажатии на которые отображается панорама StreetViewPanorama для данного местоположения. Код использует содержимое объекта StreetViewPanoramaData , возвращаемого сервисом.
Машинопись
/* * 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>Попробуйте образец
Создание панорамных снимков улиц на заказ.
API JavaScript Maps поддерживает отображение пользовательских панорам в объекте StreetViewPanorama . Используя пользовательские панорамы, вы можете отображать интерьеры зданий, виды из живописных мест или что угодно, что вы только можете себе представить. Вы даже можете связать эти пользовательские панорамы с существующими панорамами Street View от Google.
Для создания набора пользовательских панорамных изображений необходимо выполнить следующие шаги:
- Для каждой пользовательской панорамы создайте базовое панорамное изображение. Это базовое изображение должно иметь максимальное разрешение, с помощью которого вы хотите предоставлять увеличенные изображения.
- (Необязательно, но рекомендуется) Создайте набор панорамных фрагментов с разными уровнями масштабирования на основе базового изображения.
- Создавайте связи между вашими пользовательскими панорамами.
- (Необязательно) Укажите «начальные» панорамы в существующих изображениях Google Street View и настройте ссылки между пользовательским набором и стандартным набором.
- Определите метаданные для каждого панорамного изображения в объекте
StreetViewPanoramaData. - Реализуйте метод, определяющий пользовательские данные и изображения панорамы, и назначьте этот метод в качестве пользовательского обработчика в объекте
StreetViewPanorama.
В следующих разделах этот процесс объясняется.
Создание пользовательских панорам
Каждая панорама Street View представляет собой изображение или набор изображений, обеспечивающих полный обзор на 360 градусов из одной точки. Объект StreetViewPanorama использует изображения, соответствующие эквиректакулярной (Plate Carrée) проекции. Такая проекция содержит 360 градусов горизонтального обзора (полный круговой обзор) и 180 градусов вертикального обзора (от вертикального положения вверх до вертикального положения вниз). В результате получается изображение с соотношением сторон 2:1. Полная круговая панорама показана ниже.

Панорамные изображения обычно получают путем съемки нескольких фотографий с одной точки и их последующего сшивания с помощью программного обеспечения для создания панорам. (Более подробную информацию можно найти в разделе «Сравнение приложений для сшивания фотографий» в Википедии.) Такие изображения должны быть получены с одной точки съемки («камеры»), с которой делается каждый из панорамных снимков. Полученная панорама на 360 градусов затем может быть использована для проекции на сферу, при этом изображение будет свернуто на двухмерную поверхность сферы.

Рассмотрение панорамы как проекции на сферу с прямоугольной системой координат выгодно при разделении изображения на прямоугольные фрагменты и отображении изображений на основе вычисленных координат фрагментов.
Создание пользовательских панорамных плиток
Street View также поддерживает различные уровни детализации изображения с помощью элемента управления масштабированием, который позволяет увеличивать и уменьшать масштаб по умолчанию. Как правило, Street View предоставляет пять уровней разрешения масштабирования для любого панорамного изображения. Если бы вы полагались на одно панорамное изображение для всех уровней масштабирования, такое изображение либо обязательно было бы довольно большим и значительно замедляло бы работу вашего приложения, либо имело бы настолько низкое разрешение на более высоких уровнях масштабирования, что вы бы отображали изображение с плохой пикселизацией. К счастью, однако, мы можем использовать аналогичный шаблон проектирования, используемый для отображения фрагментов карты Google на разных уровнях масштабирования, чтобы обеспечить соответствующее разрешение изображений для панорам на каждом уровне масштабирования.
При первой загрузке StreetViewPanorama по умолчанию отображается изображение, занимающее 25% (90 градусов дуги) горизонтальной ширины панорамы при уровне масштабирования 1. Этот вид примерно соответствует обычному полю зрения человека. Уменьшение масштаба по умолчанию обеспечивает более широкую дугу, а увеличение масштаба сужает поле зрения до меньшей дуги. StreetViewPanorama автоматически рассчитывает соответствующее поле зрения для выбранного уровня масштабирования, а затем выбирает изображения, наиболее подходящие для этого разрешения, выбирая набор тайлов, приблизительно соответствующий размерам горизонтального поля зрения. Следующие поля зрения соответствуют уровням масштабирования Street View:
| Уровень масштабирования Street View | Поле зрения (градусы) |
|---|---|
| 0 | 180 |
| 1 (по умолчанию) | 90 |
| 2 | 45 |
| 3 | 22.5 |
| 4 | 11.25 |
Обратите внимание, что размер изображения, отображаемого в режиме Street View, полностью зависит от размера экрана (ширины) контейнера Street View. Если вы укажете более широкий контейнер, сервис все равно будет предоставлять то же поле зрения для любого заданного уровня масштабирования, хотя он может выбрать фрагменты, более подходящие для этого разрешения.
Поскольку каждая панорама представляет собой равноугольную проекцию, создание панорамных фрагментов относительно просто. Так как проекция обеспечивает изображение с соотношением сторон 2:1, фрагменты с таким соотношением проще использовать, хотя квадратные фрагменты могут обеспечить лучшую производительность на квадратных картах (поскольку поле зрения будет квадратным).
Для тайлов 2:1 одно изображение, охватывающее всю панораму, представляет собой весь панорамный «мир» (базовое изображение) на уровне масштабирования 0, при этом каждый последующий уровень масштабирования предлагает 4 тайла zoomLevel . (Например, на уровне масштабирования 2 вся панорама состоит из 16 тайлов.) Примечание: уровни масштабирования в режиме мозаичного отображения Street View не совпадают напрямую с уровнями масштабирования, заданными с помощью элемента управления Street View; вместо этого уровни масштабирования элемента управления Street View выбирают поле зрения (FoV), из которого выбираются соответствующие тайлы.

Как правило, вам потребуется присваивать имена фрагментам изображений, чтобы их можно было выбирать программно. Такая схема именования описана ниже в разделе «Обработка пользовательских запросов панорамы» .
Обработка запросов на создание панорамных изображений по индивидуальному заказу
Для использования пользовательской панорамы вызовите метод StreetViewPanorama.registerPanoProvider() , указав имя метода поставщика вашей пользовательской панорамы. Метод поставщика панорамы должен возвращать объект StreetViewPanoramaData и иметь следующую сигнатуру:
Function(pano):StreetViewPanoramaData
Объект StreetViewPanoramaData имеет следующий вид:
{ copyright: string, location: { description: string, latLng: google.maps.LatLng, pano: string }, tiles: { tileSize: google.maps.Size, worldSize: google.maps.Size, heading: number, getTileUrl: Function }, links: [ description: string, heading: number, pano: string, roadColor: string, roadOpacity: number ] }
Отобразите пользовательскую панораму следующим образом:
- Установите для свойства
StreetViewPanoramaOptions.panoпользовательское значение. - Вызовите метод
StreetViewPanorama.registerPanoProvider()чтобы предоставить пользовательскую функцию для создания панорам. - Реализуйте собственную функцию обработки панорам для работы с указанным значением
pano. - Создайте объект
StreetViewPanoramaData. - Установите свойство
StreetViewTileData.getTileUrlравным имени пользовательской функции поставщика тайлов, которую вы укажете. Например,getCustomPanoramaTileUrl. - Реализуйте собственную функцию поставщика тайлов, как показано в примерах ниже.
- Возвращает объект
StreetViewPanoramaData.
Примечание: Не следует напрямую задавать position на StreetViewPanorama , если вы хотите отображать пользовательские панорамы, поскольку такая позиция укажет службе Street View запросить стандартные изображения Street View, расположенные рядом с этим местом. Вместо этого задайте эту позицию в поле location.latLng объекта StreetViewPanoramaData .
В следующем примере отображается пользовательская панорама офиса Google в Сиднее. Обратите внимание, что в этом примере не используется карта или стандартные изображения Street View:
Машинопись
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>Попробуйте образец
Пользовательский поставщик панорамы возвращает соответствующий фрагмент, учитывая переданный идентификатор панорамы, уровень масштабирования и координаты фрагмента панорамы. Поскольку выбор изображения зависит от этих переданных значений, полезно присваивать изображениям имена, которые можно выбрать программно, pano _ zoom _ tileX _ tileY .png
В следующем примере, помимо стандартных стрелок навигации Street View, к изображению добавляется еще одна стрелка, указывающая на Google Sydney и ведущая к пользовательским изображениям:
Машинопись
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>