Обзор
Google Просмотр улиц – это сервис, предоставляющий доступ к круговым панорамам множества разных мест по всему миру. Область покрытия API этого сервиса такая же, как и у Google Карт (https://maps.google.com/
). Список поддерживаемых городов доступен на сайте Google Карт.
Ниже показан пример изображения Просмотра улиц.
Maps JavaScript API с помощью сервиса "Просмотр улиц" может получать и обрабатывать изображения из Google Карт и Просмотра улиц. Сервис "Просмотр улиц" поддерживается встроенными возможностями браузера.
Использование карт с Просмотром улиц
Хотя сервис "Просмотр улиц" можно использовать в отдельном элементе DOM, результат будет лучше, если определить для него местоположение на карте. По умолчанию сервис "Просмотр улиц" включен. При этом на панели навигации (масштабирование и панорамирование) есть значок человечка. Этот элемент управления можно скрыть с помощью объекта MapOptions
, установив для свойства streetViewControl
значение false
. Кроме того, можно изменить положение человечка по умолчанию, присвоив свойству streetViewControlOptions.position
объекта Map
новое значение ControlPosition
.
Значок человечка позволяет просматривать панорамы в Просмотре улиц непосредственно на карте. Когда пользователь нажимает и удерживает его, улицы с панорамами обводятся голубым контуром, как в приложении Google Карты.
Когда пользователь перетаскивает человечка на улицу, на экране появляется соответствующая этому месту панорама в Просмотре улиц.
Панорамы в Просмотре улиц
Панорамы поддерживаются за счет использования объекта StreetViewPanorama
, который предоставляет интерфейс API средству просмотра сервиса "Просмотр улиц". Каждая карта содержит панораму в Просмотре улиц по умолчанию, которую можно извлечь, вызвав для карты метод getStreetView()
. Если добавить на карту элемент управления Просмотра улиц, установив для свойства streetViewControl
значение true
, то человечек на карте будет связан с панорамой по умолчанию.
Также можно создать собственный объект StreetViewPanorama
и настроить карту для его использования вместо объекта по умолчанию, указав его в качестве значения свойства streetView
. Вы можете заменить стандартную панораму, если хотите изменить поведение по умолчанию (например, чтобы наложения на карте и в панораме не были общими). Подробнее читайте в разделе Наложения в Просмотре улиц ниже.
Контейнеры Просмотра улиц
При желании вы можете отобразить объект StreetViewPanorama
в отдельном элементе DOM (как правило, в <div>
).
Для этого просто передайте элемент DOM в конструкторе объекта StreetViewPanorama
. Чтобы картинка была качественной, используйте изображения с разрешением не менее 200 х 200 пикселей.
Примечание. Хотя Просмотр улиц рассчитан на работу с картой, вы можете использовать его объекты отдельно от нее.
Местоположение и точка обзора в Просмотре улиц
Конструктор StreetViewPanorama
также позволяет устанавливать местоположение и точку обзора для Просмотра улиц с помощью параметра StreetViewOptions
. Чтобы изменить их, вызовите после создания объекта методы
setPosition()
и setPov()
.
Местоположение определяет фокус камеры, но не определяет ее ориентацию: для нее предусмотрены два свойства объекта StreetViewPov
.
heading
(по умолчанию0
) – определяет угол поворота вокруг позиции камеры в градусах относительно истинного севера. Направление измеряется по часовой стрелке (90° соответствует истинному востоку).pitch
(по умолчанию0
) – определяет отклонение угла вверх или вниз от исходного угла наклона камеры по умолчанию, который часто (но не всегда) совершенно горизонтален. Например, при съемке панорамы на холме угол наклона по умолчанию не будет горизонтальным. Положительное значение угла наклона означает направление вверх (до +90° вверх перпендикулярно углу наклона по умолчанию), а отрицательное – вниз (до -90° вниз перпендикулярно углу наклона по умолчанию).
Объект StreetViewPov
чаще всего используется для определения точки обзора камеры Просмотра улиц. С помощью метода StreetViewPanorama.getPhotographerPov()
можно также указать точку обзора фотографа, соответствующую ориентации автомобиля или велотрайка во время съемки.
Следующий код отображает карту Бостона с видом на парк Фенвей. Если выбрать человечка и перетащить его в поддерживаемую точку на карте, панорама изменится.
TypeScript
function initialize() { const fenway = { lat: 42.345573, lng: -71.098326 }; const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { center: fenway, zoom: 14, } ); const panorama = new google.maps.StreetViewPanorama( document.getElementById("pano") as HTMLElement, { position: fenway, pov: { heading: 34, pitch: 10, }, } ); map.setStreetView(panorama); } declare global { interface Window { initialize: () => void; } } window.initialize = initialize;
JavaScript
function initialize() { const fenway = { lat: 42.345573, lng: -71.098326 }; const map = new google.maps.Map(document.getElementById("map"), { center: fenway, zoom: 14, }); const panorama = new google.maps.StreetViewPanorama( document.getElementById("pano"), { position: fenway, pov: { heading: 34, pitch: 10, }, }, ); map.setStreetView(panorama); } window.initialize = initialize;
CSS
html, body { height: 100%; margin: 0; padding: 0; } #map, #pano { float: left; height: 100%; width: 50%; }
HTML
<html> <head> <title>Street View split-map-panes</title> <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script> <link rel="stylesheet" type="text/css" href="./style.css" /> <script type="module" src="./index.js"></script> </head> <body> <div id="map"></div> <div id="pano"></div> <!-- The `defer` attribute causes the 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 позволяет обновлять точку обзора в Просмотре улиц вслед за перемещением устройства (если оно поддерживает события изменения ориентации). Держа устройство в руке и поворачиваясь вокруг своей оси, пользователи видят, как меняется панорама. Это называется отслеживанием движения, или отслеживанием вращения устройства.
Разработчики приложений могут менять поведение по умолчанию следующими способами:
- Включать и отключать отслеживание движения (по умолчанию оно включено на всех поддерживающих эту функцию устройствах). Следующий код отключает отслеживание движения, но элемент управления функцией остается видимым
(нажав на него, пользователь может включить отслеживание движения).
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
. Например, маркеры для перемещения панорамы в Просмотре улиц привязаны к горизонтальной поверхности относительно точки обзора.
В настоящее время панорамы в Просмотре улиц поддерживают типы наложений Marker
, InfoWindow
и OverlayView
. Наложения на карте можно показать в Просмотре улиц, если применить ее вместо объекта Map
. Для этого вызовите метод setMap()
и передайте вместо карты объект StreetViewPanorama
. Таким же образом в панорамах в Просмотре улиц можно открывать информационные окна, вызывая метод open()
и передавая объект StreetViewPanorama()
вместо карты.
Кроме того, при создании карты с объектом StreetViewPanorama
по умолчанию любые маркеры на карте будут автоматически использоваться на панораме, связанной с этой картой (если эта панорама видна). Чтобы извлечь панораму по умолчанию в Просмотре улиц, вызовите метод getStreetView()
объекта Map
. Обратите внимание, что если вы явно зададите в свойстве streetView
вашу собственную панораму StreetViewPanorama
, то она будет применена вместо панорамы по умолчанию.
В следующем примере показаны маркеры, обозначающие различные места в районе Астор Плейс в Нью-Йорке (США). Переключитесь на Просмотр улиц, чтобы вывести общие маркеры, отображаемые внутри объекта 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> <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script> <link rel="stylesheet" type="text/css" href="./style.css" /> <script type="module" src="./index.js"></script> </head> <body> <div id="floating-panel"> <input type="button" value="Toggle Street View" id="toggle" /> </div> <div id="map"></div> <!-- The `defer` attribute causes the 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>
Примеры кода
События Просмотра улиц
Вы можете отслеживать несколько событий, которые указывают на изменение состояния StreetViewPanorama
при навигации или изменении ориентации в Просмотре улиц:
pano_changed
– вызывается при изменении идентификатора отдельной панорамы. Это событие не указывает на изменение каких-либо данных панорамы (например, ссылок) в момент его возникновения. Оно лишь указывает на то, что идентификатор панорамы изменился. Идентификатор панорамы (который можно использовать для ссылки на эту панораму) остается постоянным только в текущем сеансе браузера.position_changed
– вызывается при изменении базового положения (LatLng
) панорамы. При повороте панорамы это событие не возникает. Вы можете изменить позицию панорамы, не меняя ее идентификатор, поскольку API автоматически привязывает ближайший идентификатор панорамы к позиции панорамы.pov_changed
– вызывается при изменении объектаStreetViewPov
в Просмотре улиц. Это событие может возникнуть, даже если позиция и идентификатор панорамы остаются без изменений.links_changed
– вызывается при изменении ссылок в Просмотре улиц. Обратите внимание, что это событие может вызываться асинхронно после изменения идентификатора панорамы, указанного с помощью событияpano_changed
.visible_changed
– вызывается при изменении видимости в Просмотре улиц. Обратите внимание, что это событие может вызываться асинхронно после изменения идентификатора панорамы, указанного с помощью событияpano_changed
.
Следующий код демонстрирует, как можно обработать эти события для сбора данных о базовом объекте StreetViewPanorama
:
TypeScript
function initPano() { const panorama = new google.maps.StreetViewPanorama( document.getElementById("pano") as HTMLElement, { position: { lat: 37.869, lng: -122.255 }, pov: { heading: 270, pitch: 0, }, visible: true, } ); panorama.addListener("pano_changed", () => { const panoCell = document.getElementById("pano-cell") as HTMLElement; panoCell.innerHTML = panorama.getPano(); }); panorama.addListener("links_changed", () => { const linksTable = document.getElementById("links_table") as HTMLElement; while (linksTable.hasChildNodes()) { linksTable.removeChild(linksTable.lastChild as ChildNode); } const links = panorama.getLinks(); for (const i in links) { const row = document.createElement("tr"); linksTable.appendChild(row); const labelCell = document.createElement("td"); labelCell.innerHTML = "<b>Link: " + i + "</b>"; const valueCell = document.createElement("td"); valueCell.innerHTML = links[i].description as string; linksTable.appendChild(labelCell); linksTable.appendChild(valueCell); } }); panorama.addListener("position_changed", () => { const positionCell = document.getElementById( "position-cell" ) as HTMLElement; (positionCell.firstChild as HTMLElement).nodeValue = panorama.getPosition() + ""; }); panorama.addListener("pov_changed", () => { const headingCell = document.getElementById("heading-cell") as HTMLElement; const pitchCell = document.getElementById("pitch-cell") as HTMLElement; (headingCell.firstChild as HTMLElement).nodeValue = panorama.getPov().heading + ""; (pitchCell.firstChild as HTMLElement).nodeValue = panorama.getPov().pitch + ""; }); } declare global { interface Window { initPano: () => void; } } window.initPano = initPano;
JavaScript
function initPano() { const panorama = new google.maps.StreetViewPanorama( document.getElementById("pano"), { position: { lat: 37.869, lng: -122.255 }, pov: { heading: 270, pitch: 0, }, visible: true, }, ); panorama.addListener("pano_changed", () => { const panoCell = document.getElementById("pano-cell"); panoCell.innerHTML = panorama.getPano(); }); panorama.addListener("links_changed", () => { const linksTable = document.getElementById("links_table"); while (linksTable.hasChildNodes()) { linksTable.removeChild(linksTable.lastChild); } const links = panorama.getLinks(); for (const i in links) { const row = document.createElement("tr"); linksTable.appendChild(row); const labelCell = document.createElement("td"); labelCell.innerHTML = "<b>Link: " + i + "</b>"; const valueCell = document.createElement("td"); valueCell.innerHTML = links[i].description; linksTable.appendChild(labelCell); linksTable.appendChild(valueCell); } }); panorama.addListener("position_changed", () => { const positionCell = document.getElementById("position-cell"); positionCell.firstChild.nodeValue = panorama.getPosition() + ""; }); panorama.addListener("pov_changed", () => { const headingCell = document.getElementById("heading-cell"); const pitchCell = document.getElementById("pitch-cell"); headingCell.firstChild.nodeValue = panorama.getPov().heading + ""; pitchCell.firstChild.nodeValue = panorama.getPov().pitch + ""; }); } window.initPano = initPano;
CSS
/* * Always set the map height explicitly to define the size of the div element * that contains the map. */ #map { height: 100%; } /* * Optional: Makes the sample page fill the window. */ html, body { height: 100%; margin: 0; padding: 0; } #floating-panel { position: absolute; top: 10px; left: 25%; z-index: 5; background-color: #fff; padding: 5px; border: 1px solid #999; text-align: center; font-family: "Roboto", "sans-serif"; line-height: 30px; padding-left: 10px; } #pano { width: 50%; height: 100%; float: left; } #floating-panel { width: 45%; height: 100%; float: right; text-align: left; overflow: auto; position: static; border: 0px solid #999; }
HTML
<html> <head> <title>Street View Events</title> <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script> <link rel="stylesheet" type="text/css" href="./style.css" /> <script type="module" src="./index.js"></script> </head> <body> <div id="pano"></div> <div id="floating-panel"> <table> <tr> <td><b>Position</b></td> <td id="position-cell"> </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 Картах. Его вид можно изменить, задав значениеStreetViewAddressControlOptions
в полеaddressControlOptions
.fullscreenControl
– позволяет открыть панораму в Просмотре улиц в полноэкранном режиме. Его вид можно изменить, задав значениеFullscreenControlOptions
в полеfullscreenControlOptions
.motionTrackingControl
– позволяет включить или отключить отслеживание движения на мобильном устройстве. Отображается только на устройствах, поддерживающих события изменения ориентации. По умолчанию показан в правом нижнем углу панорамы. Его положение можно изменить с помощью объектаMotionTrackingControlOptions
. Подробнее читайте в разделе Отслеживание движения.linksControl
– служит для вывода стрелок перемещения по панораме.- Close – кнопка для выхода из режима Просмотра улиц. Этот элемент управления можно включить или отключить, задав для свойства
enableCloseButton
значениеtrue
илиfalse
.
В примере ниже показано, как изменить отображаемые элементы управления на панораме в Просмотре улиц и убрать ссылки с экрана:
TypeScript
function initPano() { // Note: constructed panorama objects have visible: true // set by default. const panorama = new google.maps.StreetViewPanorama( document.getElementById("map") as HTMLElement, { position: { lat: 42.345573, lng: -71.098326 }, addressControlOptions: { position: google.maps.ControlPosition.BOTTOM_CENTER, }, linksControl: false, panControl: false, enableCloseButton: false, } ); } declare global { interface Window { initPano: () => void; } } window.initPano = initPano;
JavaScript
function initPano() { // Note: constructed panorama objects have visible: true // set by default. const panorama = new google.maps.StreetViewPanorama( document.getElementById("map"), { position: { lat: 42.345573, lng: -71.098326 }, addressControlOptions: { position: google.maps.ControlPosition.BOTTOM_CENTER, }, linksControl: false, panControl: false, enableCloseButton: false, }, ); } window.initPano = initPano;
CSS
/* * Always set the map height explicitly to define the size of the div element * that contains the map. */ #map { height: 100%; } /* * Optional: Makes the sample page fill the window. */ html, body { height: 100%; margin: 0; padding: 0; }
HTML
<html> <head> <title>Street View Controls</title> <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script> <link rel="stylesheet" type="text/css" href="./style.css" /> <script type="module" src="./index.js"></script> </head> <body> <div id="map"></div> <!-- The `defer` attribute causes the 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>
Примеры кода
Прямой доступ к данным Просмотра улиц
Доступность данных Просмотра улиц можно определить программным способом. Также можно получить информацию об определенных панорамах, не совершая действий непосредственно с картой или панорамой. Это можно сделать с помощью объекта StreetViewService
, который предоставляет интерфейс для данных, хранящихся в сервисе "Просмотр улиц".
Запросы к сервису "Просмотр улиц"
Доступ к сервису "Просмотр улиц" осуществляется асинхронно, поскольку интерфейсу Google Maps API требуется отправить вызов на внешний сервер. По этой причине необходимо передавать метод обратного вызова, который будет выполняться по завершении запроса и обрабатывать результаты.
Инициировать запрос к StreetViewService
можно с помощью методов StreetViewPanoRequest
или StreetViewLocationRequest
.
Запрос StreetViewPanoRequest
возвращает данные панорамы по уникальному идентификатору панорамы. Идентификаторы остаются постоянными, только пока существуют изображения соответствующей панорамы.
Запрос StreetViewLocationRequest
ищет данные панорамы для определенной точки с помощью следующих параметров:
location
– задает географические координаты (широту и долготу) для поиска панорамы.preference
– задает предпочтение при выборе панорамы (ближайшая к указанной точке или лучшая внутри заданного радиуса).radius
– задает радиус в метрах от центра или указанных координат, внутри которого нужно искать панораму. Если радиус не задан, по умолчанию используется значение 50 м.source
– указывает источник панорам для поиска. Допустимые значения:default
– источники Просмотра улиц по умолчанию (поиск не ограничен заданными источниками);outdoor
– только уличные фотографии (могут быть недоступны для указанного местоположения).
Ответы сервиса "Просмотр улиц"
Методу getPanorama()
требуется функция обратного вызова, которая бы выполнялась при возвращении результатов из сервиса "Просмотр улиц". Она возвращает набор данных панорамы в объекте StreetViewPanoramaData
и код StreetViewStatus
, обозначающий статус запроса, в указанном порядке.
Спецификация объекта StreetViewPanoramaData
содержит метаданные о панораме в Просмотре улиц в следующей форме:
{ "location": { "latLng": LatLng, "description": string, "pano": string }, "copyright": string, "links": [{ "heading": number, "description": string, "pano": string, "roadColor": string, "roadOpacity": number }], "tiles": { "worldSize": Size, "tileSize": Size, "centerHeading": number } }
Обратите внимание, что этот объект данных не является объектом StreetViewPanorama
. Чтобы создать объект Просмотра улиц с помощью этих данных, потребуется создать объект StreetViewPanorama
и вызвать метод setPano()
, передав идентификатор, указанный в возвращаемом поле location.pano
.
Код status
может возвратить одно из указанных ниже значений.
OK
– служба обнаружила подходящую панораму.ZERO_RESULTS
– служба не может найти подходящую панораму по заданным критериям.UNKNOWN_ERROR
– запрос не может быть обработан по неизвестной причине.
Показанный ниже код создает объект StreetViewService
, отвечающий на клики по карте и создающий маркеры, при нажатии на которые показывается StreetViewPanorama
для этого местоположения. Код использует содержимое объекта StreetViewPanoramaData
, возвращаемого службой.
TypeScript
/* * Click the map to set a new location for the Street View camera. */ let map: google.maps.Map; let panorama: google.maps.StreetViewPanorama; function initMap(): void { const berkeley = { lat: 37.869085, lng: -122.254775 }; const sv = new google.maps.StreetViewService(); panorama = new google.maps.StreetViewPanorama( document.getElementById("pano") as HTMLElement ); // Set up the map. map = new google.maps.Map(document.getElementById("map") as HTMLElement, { center: berkeley, zoom: 16, streetViewControl: false, }); // Set the initial Street View camera to the center of the map sv.getPanorama({ location: berkeley, radius: 50 }).then(processSVData); // Look for a nearby Street View panorama when the map is clicked. // getPanorama will return the nearest pano when the given // radius is 50 meters or less. map.addListener("click", (event) => { sv.getPanorama({ location: event.latLng, radius: 50 }) .then(processSVData) .catch((e) => console.error("Street View data not found for this location.") ); }); } function processSVData({ data }: google.maps.StreetViewResponse) { const location = data.location!; const marker = new google.maps.Marker({ position: location.latLng, map, title: location.description, }); panorama.setPano(location.pano as string); panorama.setPov({ heading: 270, pitch: 0, }); panorama.setVisible(true); marker.addListener("click", () => { const markerPanoID = location.pano; // Set the Pano to use the passed panoID. panorama.setPano(markerPanoID as string); panorama.setPov({ heading: 270, pitch: 0, }); panorama.setVisible(true); }); } declare global { interface Window { initMap: () => void; } } window.initMap = initMap;
JavaScript
/* * Click the map to set a new location for the Street View camera. */ let map; let panorama; function initMap() { const berkeley = { lat: 37.869085, lng: -122.254775 }; const sv = new google.maps.StreetViewService(); panorama = new google.maps.StreetViewPanorama( document.getElementById("pano"), ); // Set up the map. map = new google.maps.Map(document.getElementById("map"), { center: berkeley, zoom: 16, streetViewControl: false, }); // Set the initial Street View camera to the center of the map sv.getPanorama({ location: berkeley, radius: 50 }).then(processSVData); // Look for a nearby Street View panorama when the map is clicked. // getPanorama will return the nearest pano when the given // radius is 50 meters or less. map.addListener("click", (event) => { sv.getPanorama({ location: event.latLng, radius: 50 }) .then(processSVData) .catch((e) => console.error("Street View data not found for this location."), ); }); } function processSVData({ data }) { const location = data.location; const marker = new google.maps.Marker({ position: location.latLng, map, title: location.description, }); panorama.setPano(location.pano); panorama.setPov({ heading: 270, pitch: 0, }); panorama.setVisible(true); marker.addListener("click", () => { const markerPanoID = location.pano; // Set the Pano to use the passed panoID. panorama.setPano(markerPanoID); panorama.setPov({ heading: 270, pitch: 0, }); panorama.setVisible(true); }); } window.initMap = initMap;
CSS
/* * Always set the map height explicitly to define the size of the div element * that contains the map. */ #map { height: 100%; } /* * Optional: Makes the sample page fill the window. */ html, body { height: 100%; margin: 0; padding: 0; }
HTML
<html> <head> <title>Directly Accessing Street View Data</title> <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script> <link rel="stylesheet" type="text/css" href="./style.css" /> <script type="module" src="./index.js"></script> </head> <body> <div id="map" style="width: 45%; height: 100%; float: left"></div> <div id="pano" style="width: 45%; height: 100%; float: left"></div> <!-- The `defer` attribute causes the 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>
Примеры кода
Пользовательские панорамы в Просмотре улиц
Maps JavaScript API поддерживает показ пользовательских ппанорам в объекте StreetViewPanorama
. С помощью пользовательских панорам можно показывать любые виды: интерьеры зданий, пейзажи и т. д. Вы даже можете связать пользовательские панорамы с существующими панорамами Google в Просмотре улиц.
Для настройки изображений для пользовательской панорамы необходимо выполнить описанные ниже шаги.
- Создайте базовое панорамное изображение для каждой пользовательской панорамы. Это изображение должно иметь достаточное разрешение для самого высокого уровня масштабирования.
- (Рекомендуется) На основе базового изображения создайте набор фрагментов панорамы для различных коэффициентов масштабирования.
- Создайте ссылки между пользовательскими панорамами.
- (Необязательно) Назначьте "входные" панорамы из существующих изображений Google в Просмотре улиц и настройте ссылки между пользовательским и стандартным набором изображений.
- Добавьте метаданные для каждого изображения панорамы в объекте
StreetViewPanoramaData
. - Реализуйте метод, который определяет данные и изображения для пользовательской панорамы, и обозначьте этот метод в качестве пользовательского обработчика в объекте
StreetViewPanorama
.
Подробнее эта процедура описана ниже.
Создание пользовательских панорам
Каждая панорама в Просмотре улиц представляет собой изображение или набор изображений, которые обеспечивают полный круговой обзор из одной точки.
Объект StreetViewPanorama
использует изображения с равнопромежуточной проекцией. Такая проекция содержит 360-градусный горизонтальный обзор (полный оборот) и 180-градусный вертикальный обзор (от направления строго вверх до направления строго вниз). Соотношение сторон у такого изображения составляет 2:1. Панорама с полным обзором представлена ниже.
Обычно изображения панорам создаются из нескольких фотографий, сделанных из одного положения и соединенных в специальной программе для "склеивания". При съемке таких изображений камера устанавливается в одном местоположении, из которого делается несколько снимков. Полученная 360-градусная панорама определяет проекцию на сфере с помощью переноса изображения на двухмерную поверхность этой сферы.
Для разделения изображения на вертикальные фрагменты панорама проецируется на сферу с прямоугольной системой координат. Изображения предоставляются на основе вычисленных координат фрагмента.
Создание пользовательских фрагментов панорам
Сервис "Просмотр улиц" также поддерживает разные уровни детализации изображений с помощью элемента масштабирования стандартного изображения. Обычно доступно пять уровней масштабирования любого изображения панорамы. Если бы для всех уровней масштабирования использовалось одно изображение, оно должно было бы иметь довольно большой размер (что замедлило бы работу приложения) или очень низкое разрешение (от чего пострадало бы качество изображения для крупного масштаба). Чтобы избежать этого, применяйте метод, сходный с используемым для обработки фрагментов Google Карт, – он обеспечивает выдачу изображения с нужным разрешением для каждого уровня масштабирования.
При первоначальной загрузке StreetViewPanorama
по умолчанию показывается изображение, составляющее 25 % от горизонтальной ширины панорамы при коэффициенте масштабирования 1 (дуга 90°). Этот вид примерно соответствует полю зрения человека. При отдалении ширина дуги увеличивается, а при приближении – уменьшается (поле зрения становится уже). StreetViewPanorama
автоматически рассчитывает подходящее поле обзора для выбранного коэффициента масштабирования, после чего выбирает снимки с нужным разрешением (набор фрагментов, размеры которого примерно соответствуют полю обзора по горизонтали). Уровням масштабирования в Просмотре улиц соответствуют следующие поля обзора:
Уровень масштабирования в Просмотре улиц | Поле обзора (градусы) |
---|---|
0 | 180 |
1 (по умолчанию) | 90 |
2 | 45 |
3 | 22,5 |
4 | 11,25 |
Размер изображения панорамы в Просмотре улиц полностью зависит от экранного размера (ширины) контейнера Просмотра улиц. Если указать более широкий контейнер, служба будет использовать одно и тоже поле обзора для любого указанного уровня масштабирования (хотя может и выбирать листы, более подходящие для соответствующего разрешения).
Поскольку каждая панорама представляет собой равнопромежуточную проекцию, создавать фрагменты достаточно просто. Проекция – изображение с соотношением сторон 2:1, поэтому проще использовать фрагменты с таким же соотношением (хотя квадратные фрагменты могут обеспечить более высокую производительность на картах с квадратным полем обзора).
Один фрагмент 2:1 заключает в себе всю панораму с углом обзора 360° и коэффициентом масштабирования 0 (базовое изображение). Количество фрагментов равно 4уровень_масштабирования (то есть на уровне 2 панорама состоит из 16 фрагментов). Примечание. Уровни масштабирования фрагментов не совпадают с уровнями масштабирования в интерфейсе Просмотра улиц (в последнем случае сначала выбирается зона обзора, а затем подходящий фрагмент).
Обычно фрагментам присваивают имена, чтобы на них можно было ссылаться в коде. Схема именования описана в разделе ниже.
Обработка запросов пользовательских панорам
Для работы с пользовательской панорамой вызовите метод поставщика пользовательских панорам 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
, если вы хотите показывать пользовательскую панораму, поскольку в этом случае сервис "Просмотр улиц" отправит запрос для получения стандартных изображений, доступных для этого места. Вместо этого задайте местоположение в поле location.latLng
объекта StreetViewPanoramaData
.
В следующем примере показана пользовательская панорама офиса Google в Сиднее. Обратите внимание, что в примере не используется ни карта, ни стандартные изображения Просмотра улиц.
TypeScript
function initPano() { // Set up Street View and initially set it visible. Register the // custom panorama provider function. Set the StreetView to display // the custom panorama 'reception' which we check for below. const panorama = new google.maps.StreetViewPanorama( document.getElementById("map") as HTMLElement, { pano: "reception", visible: true } ); panorama.registerPanoProvider(getCustomPanorama); } // Return a pano image given the panoID. function getCustomPanoramaTileUrl( pano: string, zoom: number, tileX: number, tileY: number ): string { return ( "https://developers.google.com/maps/documentation/javascript/examples/full/images/" + "panoReception1024-" + zoom + "-" + tileX + "-" + tileY + ".jpg" ); } // Construct the appropriate StreetViewPanoramaData given // the passed pano IDs. function getCustomPanorama(pano: string): google.maps.StreetViewPanoramaData { if (pano === "reception") { return { location: { pano: "reception", description: "Google Sydney - Reception", }, links: [], // The text for the copyright control. copyright: "Imagery (c) 2010 Google", // The definition of the tiles for this panorama. tiles: { tileSize: new google.maps.Size(1024, 512), worldSize: new google.maps.Size(2048, 1024), // The heading in degrees at the origin of the panorama // tile set. centerHeading: 105, getTileUrl: getCustomPanoramaTileUrl, }, }; } // @ts-ignore TODO fix typings return null; } declare global { interface Window { initPano: () => void; } } window.initPano = initPano;
JavaScript
function initPano() { // Set up Street View and initially set it visible. Register the // custom panorama provider function. Set the StreetView to display // the custom panorama 'reception' which we check for below. const panorama = new google.maps.StreetViewPanorama( document.getElementById("map"), { pano: "reception", visible: true }, ); panorama.registerPanoProvider(getCustomPanorama); } // Return a pano image given the panoID. function getCustomPanoramaTileUrl(pano, zoom, tileX, tileY) { return ( "https://developers.google.com/maps/documentation/javascript/examples/full/images/" + "panoReception1024-" + zoom + "-" + tileX + "-" + tileY + ".jpg" ); } // Construct the appropriate StreetViewPanoramaData given // the passed pano IDs. function getCustomPanorama(pano) { if (pano === "reception") { return { location: { pano: "reception", description: "Google Sydney - Reception", }, links: [], // The text for the copyright control. copyright: "Imagery (c) 2010 Google", // The definition of the tiles for this panorama. tiles: { tileSize: new google.maps.Size(1024, 512), worldSize: new google.maps.Size(2048, 1024), // The heading in degrees at the origin of the panorama // tile set. centerHeading: 105, getTileUrl: getCustomPanoramaTileUrl, }, }; } // @ts-ignore TODO fix typings return null; } window.initPano = initPano;
CSS
/* * Always set the map height explicitly to define the size of the div element * that contains the map. */ #map { height: 100%; } /* * Optional: Makes the sample page fill the window. */ html, body { height: 100%; margin: 0; padding: 0; }
HTML
<html> <head> <title>Custom Street View Panoramas</title> <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script> <link rel="stylesheet" type="text/css" href="./style.css" /> <script type="module" src="./index.js"></script> </head> <body> <div id="map"></div> <!-- The `defer` attribute causes the 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
).
Код ниже добавляет еще одну стрелку к изображению в дополнение к стандартным стрелкам навигации Просмотра улиц. Эта стрелка указывает на офис Google в Сиднее и содержит ссылку на пользовательские изображения.
TypeScript
let panorama: google.maps.StreetViewPanorama; // StreetViewPanoramaData of a panorama just outside the Google Sydney office. let outsideGoogle: google.maps.StreetViewPanoramaData; // StreetViewPanoramaData for a custom panorama: the Google Sydney reception. function getReceptionPanoramaData(): google.maps.StreetViewPanoramaData { return { location: { pano: "reception", // The ID for this custom panorama. description: "Google Sydney - Reception", latLng: new google.maps.LatLng(-33.86684, 151.19583), }, links: [ { heading: 195, description: "Exit", pano: (outsideGoogle.location as google.maps.StreetViewLocation).pano, }, ], copyright: "Imagery (c) 2010 Google", tiles: { tileSize: new google.maps.Size(1024, 512), worldSize: new google.maps.Size(2048, 1024), centerHeading: 105, getTileUrl: function ( pano: string, zoom: number, tileX: number, tileY: number ): string { return ( "https://developers.google.com/maps/documentation/javascript/examples/full/images/" + "panoReception1024-" + zoom + "-" + tileX + "-" + tileY + ".jpg" ); }, }, }; } function initPanorama() { panorama = new google.maps.StreetViewPanorama( document.getElementById("street-view") as HTMLElement, { pano: (outsideGoogle.location as google.maps.StreetViewLocation).pano } ); // Register a provider for the custom panorama. panorama.registerPanoProvider( (pano: string): google.maps.StreetViewPanoramaData => { if (pano === "reception") { return getReceptionPanoramaData(); } // @ts-ignore TODO fix typings return null; } ); // Add a link to our custom panorama from outside the Google Sydney office. panorama.addListener("links_changed", () => { if ( panorama.getPano() === (outsideGoogle.location as google.maps.StreetViewLocation).pano ) { panorama.getLinks().push({ description: "Google Sydney", heading: 25, pano: "reception", }); } }); } function initMap(): void { // Use the Street View service to find a pano ID on Pirrama Rd, outside the // Google office. new google.maps.StreetViewService() .getPanorama({ location: { lat: -33.867386, lng: 151.195767 } }) .then(({ data }: google.maps.StreetViewResponse) => { outsideGoogle = data; initPanorama(); }); } declare global { interface Window { initMap: () => void; } } window.initMap = initMap;
JavaScript
let panorama; // StreetViewPanoramaData of a panorama just outside the Google Sydney office. let outsideGoogle; // StreetViewPanoramaData for a custom panorama: the Google Sydney reception. function getReceptionPanoramaData() { return { location: { pano: "reception", // 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> <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script> <link rel="stylesheet" type="text/css" href="./style.css" /> <script type="module" src="./index.js"></script> </head> <body> <div id="street-view"></div> <!-- The `defer` attribute causes the 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>