1. Прежде чем начать
Одной из наиболее распространённых функций веб-сайта является отображение карты Google , на которой отмечено одно или несколько местоположений компании, учреждения или другого физического объекта. Реализация таких карт может значительно различаться в зависимости от требований, таких как количество местоположений и частота их обновления.
В этой лабораторной работе вы рассмотрите простейший вариант использования — небольшое количество редко меняющихся локаций, например, поиск магазинов для сети магазинов. В этом случае вы можете использовать относительно простой подход без необходимости программирования на стороне сервера. Но это не значит, что вы не можете проявить творческий подход, и вы сможете это сделать, используя формат данных GeoJSON для хранения и отображения произвольной информации о каждом магазине на карте, а также для настройки маркеров и общего стиля самой карты.
Наконец, в качестве дополнительного бонуса вы можете использовать Cloud Shell для разработки и размещения своего локатора магазинов. Хотя использование этого инструмента не является обязательным, оно позволяет разрабатывать локатор магазинов с любого устройства с веб-браузером и делать его доступным для всех онлайн.
Предпосылки
- Базовые знания HTML и JavaScript
Что ты будешь делать?
- Отобразить карту с указанием местоположений магазинов и информацией, сохраненной в формате GeoJSON.
- Настройте маркеры и саму карту.
- Отображать дополнительную информацию о магазине при нажатии на его маркер.
- Добавьте на веб-страницу панель поиска с функцией автозаполнения мест.
- Определите местоположение магазина, наиболее близкое к начальной точке, указанной пользователем.
2. Настройте
На шаге 3 следующего раздела включите следующие три API для этой кодовой лаборатории:
- API JavaScript Карт
- API мест
- API матрицы расстояний
Начните работу с платформой Google Карт
Если вы ранее не использовали платформу Google Карт, следуйте руководству «Начало работы с платформой Google Карт» или посмотрите плейлист «Начало работы с платформой Google Карт», чтобы выполнить следующие шаги:
- Создайте платежный аккаунт.
- Создать проект.
- Включите API и SDK платформы Google Карт (перечислены в предыдущем разделе).
- Сгенерируйте ключ API.
Активировать Cloud Shell
В этой лабораторной работе вы будете использовать Cloud Shell — среду командной строки, работающую в Google Cloud, которая обеспечивает доступ к продуктам и ресурсам, работающим в Google Cloud, чтобы вы могли размещать и запускать свой проект полностью из веб-браузера.
Чтобы активировать Cloud Shell из Cloud Console, нажмите «Активировать Cloud Shell». (подготовка и подключение к среде займет всего несколько минут).
Это откроет новую оболочку в нижней части браузера после возможного показа вступительного объявления.
После подключения к Cloud Shell вы увидите, что вы уже аутентифицированы и что проекту уже присвоен идентификатор проекта, выбранный вами во время настройки.
$ gcloud auth list Credentialed Accounts: ACTIVE ACCOUNT * <myaccount>@<mydomain>.com
$ gcloud config list project [core] project = <YOUR_PROJECT_ID>
Если по какой-то причине проект не установлен, выполните следующую команду:
$ gcloud config set project <YOUR_PROJECT_ID>
3. «Привет, мир!» с картой
Начните разработку с карты
В Cloud Shell вы начинаете с создания HTML-страницы, которая послужит основой для остальной части кодовой лаборатории.
- На панели инструментов Cloud Shell нажмите «Запустить редактор».
чтобы открыть редактор кода в новой вкладке.
Этот веб-редактор кода позволяет легко редактировать файлы в Cloud Shell.
- Создайте новый каталог
store-locator
для вашего приложения в редакторе кода, нажав Файл > Новая папка .
- Назовите новую папку
store-locator
.
Далее вы создаете веб-страницу с картой.
- Создайте файл в каталоге
store-locator
с именемindex.html
.
- Поместите следующее содержимое в файл
index.html
:
index.html
<html>
<head>
<title>Store Locator</title>
<style>
#map {
height: 100%;
}
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<!-- The div to hold the map -->
<div id="map"></div>
<script src="app.js"></script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initMap&solution_channel=GMP_codelabs_simplestorelocator_v1_a">
</script>
</body>
</html>
Это HTML-страница, отображающая карту. Она содержит CSS-код, обеспечивающий отображение карты на всей странице, тег <div>
для размещения карты и пару тегов <script>
. Первый тег script загружает JavaScript-файл app.js
, содержащий весь код JavaScript. Второй тег script загружает ключ API, включает использование библиотеки Places для функции автозаполнения, которую вы добавите позже, и указывает имя функции JavaScript, которая выполняется после загрузки Maps JavaScript API, а именно initMap
.
- Замените текст
YOUR_API_KEY
во фрагменте кода на ключ API, который вы сгенерировали ранее в этой лабораторной работе. - Наконец, создайте еще один файл с именем
app.js
со следующим кодом:
app.js
function initMap() {
// Create the map.
const map = new google.maps.Map(document.getElementById('map'), {
zoom: 7,
center: { lat: 52.632469, lng: -1.689423 },
});
}
Это минимально необходимый код для создания карты. Вы передаёте ссылку на тег <div>
для размещения карты, а также указываете центр и уровень масштабирования.
Чтобы протестировать это приложение, вы можете запустить простой HTTP-сервер Python в Cloud Shell.
- Перейдите в Cloud Shell и введите следующее:
$ cd store-locator $ python3 -m http.server 8080
Вы видите несколько строк журнала, показывающих, что вы действительно используете простой HTTP-сервер в Cloud Shell с веб-приложением, прослушивающим порт localhost 8080.
- Откройте вкладку веб-браузера с этим приложением, нажав «Веб-просмотр».
на панели инструментов Cloud Console и выберите Предварительный просмотр на порту 8080 .
При нажатии на этот пункт меню открывается новая вкладка в веб-браузере с содержимым HTML-кода, полученного с простого HTTP-сервера Python. Если всё прошло успешно, вы увидите карту с центром в Лондоне, Англия.
Чтобы остановить простой HTTP-сервер, нажмите Control+C
в Cloud Shell.
4. Заполните карту данными GeoJSON.
Теперь взгляните на данные о магазинах. GeoJSON — это формат данных, представляющий простые географические объекты, такие как точки, линии или многоугольники на карте. Эти объекты также могут содержать произвольные данные. Это делает GeoJSON отличным кандидатом для представления магазинов, которые, по сути, представляют собой точки на карте с некоторыми дополнительными данными, такими как название магазина, часы работы и номер телефона. Самое главное, GeoJSON имеет первоклассную поддержку в Google Картах, что означает, что вы можете отправить документ GeoJSON на карту Google, и он будет корректно отображен на карте.
- Создайте новый файл с именем
stores.json
и вставьте в него следующий код:
stores.json
{
"type": "FeatureCollection",
"features": [{
"geometry": {
"type": "Point",
"coordinates": [-0.1428115,
51.5125168
]
},
"type": "Feature",
"properties": {
"category": "patisserie",
"hours": "10am - 6pm",
"description": "Modern twists on classic pastries. We're part of a larger chain of patisseries and cafes.",
"name": "Josie's Patisserie Mayfair",
"phone": "+44 20 1234 5678",
"storeid": "01"
}
},
{
"geometry": {
"type": "Point",
"coordinates": [-2.579623,
51.452251
]
},
"type": "Feature",
"properties": {
"category": "patisserie",
"hours": "10am - 6pm",
"description": "Come and try our award-winning cakes and pastries. We're part of a larger chain of patisseries and cafes.",
"name": "Josie's Patisserie Bristol",
"phone": "+44 117 121 2121",
"storeid": "02"
}
},
{
"geometry": {
"type": "Point",
"coordinates": [
1.273459,
52.638072
]
},
"type": "Feature",
"properties": {
"category": "patisserie",
"hours": "10am - 6pm",
"description": "Whatever the occasion, whether it's a birthday or a wedding, Josie's Patisserie has the perfect treat for you. We're part of a larger chain of patisseries and cafes.",
"name": "Josie's Patisserie Norwich",
"phone": "+44 1603 123456",
"storeid": "03"
}
},
{
"geometry": {
"type": "Point",
"coordinates": [-1.9912838,
50.8000418
]
},
"type": "Feature",
"properties": {
"category": "patisserie",
"hours": "10am - 6pm",
"description": "A gourmet patisserie that will delight your senses. We're part of a larger chain of patisseries and cafes.",
"name": "Josie's Patisserie Wimborne",
"phone": "+44 1202 343434",
"storeid": "04"
}
},
{
"geometry": {
"type": "Point",
"coordinates": [-2.985933,
53.408899
]
},
"type": "Feature",
"properties": {
"category": "patisserie",
"hours": "10am - 6pm",
"description": "Spoil yourself or someone special with our classic pastries. We're part of a larger chain of patisseries and cafes.",
"name": "Josie's Patisserie Liverpool",
"phone": "+44 151 444 4444",
"storeid": "05"
}
},
{
"geometry": {
"type": "Point",
"coordinates": [-1.689423,
52.632469
]
},
"type": "Feature",
"properties": {
"category": "patisserie",
"hours": "10am - 6pm",
"description": "Come and feast your eyes and tastebuds on our delicious pastries and cakes. We're part of a larger chain of patisseries and cafes.",
"name": "Josie's Patisserie Tamworth",
"phone": "+44 5555 55555",
"storeid": "06"
}
},
{
"geometry": {
"type": "Point",
"coordinates": [-3.155305,
51.479756
]
},
"type": "Feature",
"properties": {
"category": "patisserie",
"hours": "10am - 6pm",
"description": "Josie's Patisserie is family-owned, and our delectable pastries, cakes, and great coffee are renowed. We're part of a larger chain of patisseries and cafes.",
"name": "Josie's Patisserie Cardiff",
"phone": "+44 29 6666 6666",
"storeid": "07"
}
},
{
"geometry": {
"type": "Point",
"coordinates": [-0.725019,
52.668891
]
},
"type": "Feature",
"properties": {
"category": "cafe",
"hours": "8am - 9:30pm",
"description": "Oakham's favorite spot for fresh coffee and delicious cakes. We're part of a larger chain of patisseries and cafes.",
"name": "Josie's Cafe Oakham",
"phone": "+44 7777 777777",
"storeid": "08"
}
},
{
"geometry": {
"type": "Point",
"coordinates": [-2.477653,
53.735405
]
},
"type": "Feature",
"properties": {
"category": "cafe",
"hours": "8am - 9:30pm",
"description": "Enjoy freshly brewed coffe, and home baked cakes in our homely cafe. We're part of a larger chain of patisseries and cafes.",
"name": "Josie's Cafe Blackburn",
"phone": "+44 8888 88888",
"storeid": "09"
}
},
{
"geometry": {
"type": "Point",
"coordinates": [-0.211363,
51.108966
]
},
"type": "Feature",
"properties": {
"category": "cafe",
"hours": "8am - 9:30pm",
"description": "A delicious array of pastries with many flavours, and fresh coffee in an snug cafe. We're part of a larger chain of patisseries and cafes.",
"name": "Josie's Cafe Crawley",
"phone": "+44 1010 101010",
"storeid": "10"
}
},
{
"geometry": {
"type": "Point",
"coordinates": [-0.123559,
50.832679
]
},
"type": "Feature",
"properties": {
"category": "cafe",
"hours": "8am - 9:30pm",
"description": "Grab a freshly brewed coffee, a decadent cake and relax in our idyllic cafe. We're part of a larger chain of patisseries and cafes.",
"name": "Josie's Cafe Brighton",
"phone": "+44 1313 131313",
"storeid": "11"
}
},
{
"geometry": {
"type": "Point",
"coordinates": [-3.319575,
52.517827
]
},
"type": "Feature",
"properties": {
"category": "cafe",
"hours": "8am - 9:30pm",
"description": "Come in and unwind at this idyllic cafe with fresh coffee and home made cakes. We're part of a larger chain of patisseries and cafes.",
"name": "Josie's Cafe Newtown",
"phone": "+44 1414 141414",
"storeid": "12"
}
},
{
"geometry": {
"type": "Point",
"coordinates": [
1.158167,
52.071634
]
},
"type": "Feature",
"properties": {
"category": "cafe",
"hours": "8am - 9:30pm",
"description": "Fresh coffee and delicious cakes in an snug cafe. We're part of a larger chain of patisseries and cafes.",
"name": "Josie's Cafe Ipswich",
"phone": "+44 1717 17171",
"storeid": "13"
}
}
]
}
Это очень большой объём данных, но при внимательном изучении вы увидите, что это просто одна и та же структура, повторяющаяся для каждого магазина. Каждый магазин представлен Point
GeoJSON вместе со своими координатами и дополнительными данными, содержащимися в ключе properties
. Примечательно, что GeoJSON позволяет включать в ключ properties
ключи с произвольными именами. В этой практической работе такими ключами являются: category
, hours
, description
, name
и phone
.
- Теперь отредактируйте
app.js
так, чтобы он загрузил GeoJSON изstores.js
на вашу карту.
app.js
function initMap() {
// Create the map.
const map = new google.maps.Map(document.getElementById('map'), {
zoom: 7,
center: {lat: 52.632469, lng: -1.689423},
});
// Load the stores GeoJSON onto the map.
map.data.loadGeoJson('stores.json', {idPropertyName: 'storeid'});
const apiKey = 'YOUR_API_KEY';
const infoWindow = new google.maps.InfoWindow();
// Show the information for a store when its marker is clicked.
map.data.addListener('click', (event) => {
const category = event.feature.getProperty('category');
const name = event.feature.getProperty('name');
const description = event.feature.getProperty('description');
const hours = event.feature.getProperty('hours');
const phone = event.feature.getProperty('phone');
const position = event.feature.getGeometry().get();
const content = `
<h2>${name}</h2><p>${description}</p>
<p><b>Open:</b> ${hours}<br/><b>Phone:</b> ${phone}</p>
`;
infoWindow.setContent(content);
infoWindow.setPosition(position);
infoWindow.setOptions({pixelOffset: new google.maps.Size(0, -30)});
infoWindow.open(map);
});
}
В примере кода вы загрузили GeoJSON-файл на карту, вызвав loadGeoJson
и передав имя JSON-файла. Вы также определили функцию, которая будет запускаться при каждом щелчке по маркеру. Эта функция затем может получить доступ к дополнительным данным магазина, маркер которого был щёлкнут, и использовать эту информацию в отображаемом информационном окне. Чтобы протестировать это приложение, вы можете запустить простой HTTP-сервер Python, используя ту же команду, что и раньше.
- Вернитесь в Cloud Shell и введите следующее:
$ python3 -m http.server 8080
- Нажмите «Предварительный просмотр в Интернете».
> Снова просмотрите порт 8080 , и вы увидите карту с маркерами, на которые можно нажать, чтобы просмотреть подробную информацию о каждом магазине, как показано в следующем примере. Прогресс!
5. Настройте карту
Вы почти у цели. У вас есть карта со всеми маркерами магазинов и дополнительной информацией, которая отображается при нажатии. Но она выглядит как любая другая карта Google. Как скучно! Добавьте к ней особый стиль, маркеры, логотипы и изображения Street View.
Вот новая версия app.js
с добавленным пользовательским стилем:
app.js
const mapStyle = [{
'featureType': 'administrative',
'elementType': 'all',
'stylers': [{
'visibility': 'on',
},
{
'lightness': 33,
},
],
},
{
'featureType': 'landscape',
'elementType': 'all',
'stylers': [{
'color': '#f2e5d4',
}],
},
{
'featureType': 'poi.park',
'elementType': 'geometry',
'stylers': [{
'color': '#c5dac6',
}],
},
{
'featureType': 'poi.park',
'elementType': 'labels',
'stylers': [{
'visibility': 'on',
},
{
'lightness': 20,
},
],
},
{
'featureType': 'road',
'elementType': 'all',
'stylers': [{
'lightness': 20,
}],
},
{
'featureType': 'road.highway',
'elementType': 'geometry',
'stylers': [{
'color': '#c5c6c6',
}],
},
{
'featureType': 'road.arterial',
'elementType': 'geometry',
'stylers': [{
'color': '#e4d7c6',
}],
},
{
'featureType': 'road.local',
'elementType': 'geometry',
'stylers': [{
'color': '#fbfaf7',
}],
},
{
'featureType': 'water',
'elementType': 'all',
'stylers': [{
'visibility': 'on',
},
{
'color': '#acbcc9',
},
],
},
];
function initMap() {
// Create the map.
const map = new google.maps.Map(document.getElementById('map'), {
zoom: 7,
center: {lat: 52.632469, lng: -1.689423},
styles: mapStyle,
});
// Load the stores GeoJSON onto the map.
map.data.loadGeoJson('stores.json', {idPropertyName: 'storeid'});
// Define the custom marker icons, using the store's "category".
map.data.setStyle((feature) => {
return {
icon: {
url: `img/icon_${feature.getProperty('category')}.png`,
scaledSize: new google.maps.Size(64, 64),
},
};
});
const apiKey = 'YOUR_API_KEY';
const infoWindow = new google.maps.InfoWindow();
// Show the information for a store when its marker is clicked.
map.data.addListener('click', (event) => {
const category = event.feature.getProperty('category');
const name = event.feature.getProperty('name');
const description = event.feature.getProperty('description');
const hours = event.feature.getProperty('hours');
const phone = event.feature.getProperty('phone');
const position = event.feature.getGeometry().get();
const content = `
<img style="float:left; width:200px; margin-top:30px" src="img/logo_${category}.png">
<div style="margin-left:220px; margin-bottom:20px;">
<h2>${name}</h2><p>${description}</p>
<p><b>Open:</b> ${hours}<br/><b>Phone:</b> ${phone}</p>
<p><img src="https://maps.googleapis.com/maps/api/streetview?size=350x120&location=${position.lat()},${position.lng()}&key=${apiKey}&solution_channel=GMP_codelabs_simplestorelocator_v1_a"></p>
</div>
`;
infoWindow.setContent(content);
infoWindow.setPosition(position);
infoWindow.setOptions({pixelOffset: new google.maps.Size(0, -30)});
infoWindow.open(map);
});
}
Вот что вы добавили:
- Переменная
mapStyle
содержит всю информацию для стилизации карты. (В качестве дополнительного бонуса вы даже можете создать свой собственный стиль, если захотите.) - Используя метод
map.data.setStyle
, вы применили пользовательские маркеры — разные для каждойcategory
из GeoJSON. - Вы изменили переменную
content
, включив логотип (снова используяcategory
из GeoJSON) и изображение Street View для местоположения магазина.
Перед тем как приступить к развертыванию, вам необходимо выполнить несколько шагов:
- Установите правильное значение для переменной
apiKey
, заменив строку'YOUR_API_KEY'
вapp.js
на ваш собственный ключ API, полученный ранее (тот же, который вы вставили вindex.html
, оставив кавычки нетронутыми). - Выполните следующие команды в Cloud Shell, чтобы загрузить изображение маркера и логотипа. Убедитесь, что вы находитесь в каталоге
store-locator
. Если простой HTTP-сервер запущен, остановите его сочетаниемControl+C
.
$ mkdir -p img; cd img $ wget https://github.com/googlecodelabs/google-maps-simple-store-locator/raw/master/src/img/icon_cafe.png $ wget https://github.com/googlecodelabs/google-maps-simple-store-locator/raw/master/src/img/icon_patisserie.png $ wget https://github.com/googlecodelabs/google-maps-simple-store-locator/raw/master/src/img/logo_cafe.png $ wget https://github.com/googlecodelabs/google-maps-simple-store-locator/raw/master/src/img/logo_patisserie.png
- Для предварительного просмотра готового локатора магазинов выполните следующую команду:.
$ python3 -m http.server 8080
При перезагрузке предварительного просмотра вы должны увидеть что-то вроде этой карты с пользовательским стилем, пользовательскими изображениями маркеров, улучшенным форматированием информационного окна и изображением Street View для каждого местоположения:
6. Получите пользовательский ввод
Пользователи локаторов магазинов обычно хотят знать, какой магазин находится ближе всего к ним или с какого адреса они планируют начать свой маршрут. Добавьте строку поиска Place Autocomplete, чтобы пользователь мог легко ввести начальный адрес. Place Autocomplete предоставляет функцию опережающего ввода, похожую на функцию автозаполнения в других строках поиска Google, но все подсказки — это места из платформы Google Карт.
- Вернитесь к редактированию
index.html
, чтобы добавить стили для строки поиска автодополнения и соответствующей боковой панели результатов. Не забудьте заменить ключ API, если вы вставили его поверх старого кода.
index.html
<html>
<head>
<title>Store Locator</title>
<style>
#map {
height: 100%;
}
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
/* Styling for Autocomplete search bar */
#pac-card {
background-color: #fff;
border-radius: 2px 0 0 2px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
box-sizing: border-box;
font-family: Roboto;
margin: 10px 10px 0 0;
-moz-box-sizing: border-box;
outline: none;
}
#pac-container {
padding-top: 12px;
padding-bottom: 12px;
margin-right: 12px;
}
#pac-input {
background-color: #fff;
font-family: Roboto;
font-size: 15px;
font-weight: 300;
margin-left: 12px;
padding: 0 11px 0 13px;
text-overflow: ellipsis;
width: 400px;
}
#pac-input:focus {
border-color: #4d90fe;
}
#title {
color: #fff;
background-color: #acbcc9;
font-size: 18px;
font-weight: 400;
padding: 6px 12px;
}
.hidden {
display: none;
}
/* Styling for an info pane that slides out from the left.
* Hidden by default. */
#panel {
height: 100%;
width: null;
background-color: white;
position: fixed;
z-index: 1;
overflow-x: hidden;
transition: all .2s ease-out;
}
.open {
width: 250px;
}
.place {
font-family: 'open sans', arial, sans-serif;
font-size: 1.2em;
font-weight: 500;
margin-block-end: 0px;
padding-left: 18px;
padding-right: 18px;
}
.distanceText {
color: silver;
font-family: 'open sans', arial, sans-serif;
font-size: 1em;
font-weight: 400;
margin-block-start: 0.25em;
padding-left: 18px;
padding-right: 18px;
}
</style>
</head>
<body>
<!-- The div to hold the map -->
<div id="map"></div>
<script src="app.js"></script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initMap&solution_channel=GMP_codelabs_simplestorelocator_v1_a">
</script>
</body>
</html>
Панель поиска автозаполнения и выдвижная панель изначально скрыты до тех пор, пока они не понадобятся.
- Теперь добавьте виджет автозаполнения на карту в конце функции
initMap
вapp.js
, прямо перед закрывающейся фигурной скобкой.
app.js
// Build and add the search bar
const card = document.createElement('div');
const titleBar = document.createElement('div');
const title = document.createElement('div');
const container = document.createElement('div');
const input = document.createElement('input');
const options = {
types: ['address'],
componentRestrictions: {country: 'gb'},
};
card.setAttribute('id', 'pac-card');
title.setAttribute('id', 'title');
title.textContent = 'Find the nearest store';
titleBar.appendChild(title);
container.setAttribute('id', 'pac-container');
input.setAttribute('id', 'pac-input');
input.setAttribute('type', 'text');
input.setAttribute('placeholder', 'Enter an address');
container.appendChild(input);
card.appendChild(titleBar);
card.appendChild(container);
map.controls[google.maps.ControlPosition.TOP_RIGHT].push(card);
// Make the search bar into a Places Autocomplete search bar and select
// which detail fields should be returned about the place that
// the user selects from the suggestions.
const autocomplete = new google.maps.places.Autocomplete(input, options);
autocomplete.setFields(
['address_components', 'geometry', 'name']);
Код ограничивает подсказки автозаполнения только адресами (поскольку автозаполнение мест может сопоставлять названия организаций и административные местоположения) и ограничивает возвращаемые адреса только адресами в Великобритании. Добавление этих необязательных спецификаций сократит количество символов, которые пользователю необходимо ввести, чтобы сузить область поиска и отобразить нужный адрес. Затем он перемещает созданный вами div
автозаполнения в правый верхний угол карты и указывает, какие поля должны быть возвращены для каждого места в ответе.
- Перезагрузите сервер и обновите предварительный просмотр, выполнив следующую команду:.
$ python3 -m http.server 8080
Теперь в правом верхнем углу карты должен появиться виджет автозаполнения, который покажет вам адреса в Великобритании, соответствующие введенному вами тексту.
Теперь вам нужно обработать момент, когда пользователь выбирает прогноз из виджета автозаполнения, и использовать это местоположение в качестве основы для расчета расстояний до ваших магазинов.
- Добавьте следующий код в конец
initMap
вapp.js
после кода, который вы только что вставили.
app.js
// Set the origin point when the user selects an address
const originMarker = new google.maps.Marker({map: map});
originMarker.setVisible(false);
let originLocation = map.getCenter();
autocomplete.addListener('place_changed', async () => {
originMarker.setVisible(false);
originLocation = map.getCenter();
const place = autocomplete.getPlace();
if (!place.geometry) {
// User entered the name of a Place that was not suggested and
// pressed the Enter key, or the Place Details request failed.
window.alert('No address available for input: \'' + place.name + '\'');
return;
}
// Recenter the map to the selected address
originLocation = place.geometry.location;
map.setCenter(originLocation);
map.setZoom(9);
console.log(place);
originMarker.setPosition(originLocation);
originMarker.setVisible(true);
// Use the selected address as the origin to calculate distances
// to each of the store locations
const rankedStores = await calculateDistances(map.data, originLocation);
showStoresList(map.data, rankedStores);
return;
});
Код добавляет прослушиватель, чтобы при нажатии пользователем на одну из подсказок карта центрировалась на выбранном адресе и устанавливала начало координат в качестве основы для расчёта расстояния. Расчёт расстояния реализуется на следующем шаге.
7. Перечислите ближайшие магазины
API Directions работает во многом подобно запросу маршрутов в приложении Google Карты: ввод одного пункта отправления и одного пункта назначения позволяет получить маршрут между ними. API Distance Matrix развивает эту концепцию, находя оптимальные пары между несколькими возможными пунктами отправления и пунктами назначения с учётом времени в пути и расстояния. В данном случае, чтобы помочь пользователю найти ближайший магазин к выбранному адресу, вы указываете один пункт отправления и массив адресов магазинов в качестве пунктов назначения.
- Добавьте новую функцию в
app.js
под названиемcalculateDistances
.
app.js
async function calculateDistances(data, origin) {
const stores = [];
const destinations = [];
// Build parallel arrays for the store IDs and destinations
data.forEach((store) => {
const storeNum = store.getProperty('storeid');
const storeLoc = store.getGeometry().get();
stores.push(storeNum);
destinations.push(storeLoc);
});
// Retrieve the distances of each store from the origin
// The returned list will be in the same order as the destinations list
const service = new google.maps.DistanceMatrixService();
const getDistanceMatrix =
(service, parameters) => new Promise((resolve, reject) => {
service.getDistanceMatrix(parameters, (response, status) => {
if (status != google.maps.DistanceMatrixStatus.OK) {
reject(response);
} else {
const distances = [];
const results = response.rows[0].elements;
for (let j = 0; j < results.length; j++) {
const element = results[j];
const distanceText = element.distance.text;
const distanceVal = element.distance.value;
const distanceObject = {
storeid: stores[j],
distanceText: distanceText,
distanceVal: distanceVal,
};
distances.push(distanceObject);
}
resolve(distances);
}
});
});
const distancesList = await getDistanceMatrix(service, {
origins: [origin],
destinations: destinations,
travelMode: 'DRIVING',
unitSystem: google.maps.UnitSystem.METRIC,
});
distancesList.sort((first, second) => {
return first.distanceVal - second.distanceVal;
});
return distancesList;
}
Функция вызывает API Distance Matrix, используя переданное ей начало координат как единственное начало координат, а местоположения магазинов — как массив пунктов назначения. Затем она формирует массив объектов, хранящих идентификатор магазина, расстояние, выраженное в удобочитаемой строке, расстояние в метрах в виде числового значения, и сортирует массив.
Пользователь ожидает увидеть список магазинов, упорядоченный от ближайшего к самому дальнему. Заполните боковую панель для каждого магазина, используя список, возвращаемый функцией calculateDistances
, чтобы определить порядок отображения магазинов.
- Добавьте новую функцию в
app.js
под названиемshowStoresList
.
app.js
function showStoresList(data, stores) {
if (stores.length == 0) {
console.log('empty stores');
return;
}
let panel = document.createElement('div');
// If the panel already exists, use it. Else, create it and add to the page.
if (document.getElementById('panel')) {
panel = document.getElementById('panel');
// If panel is already open, close it
if (panel.classList.contains('open')) {
panel.classList.remove('open');
}
} else {
panel.setAttribute('id', 'panel');
const body = document.body;
body.insertBefore(panel, body.childNodes[0]);
}
// Clear the previous details
while (panel.lastChild) {
panel.removeChild(panel.lastChild);
}
stores.forEach((store) => {
// Add store details with text formatting
const name = document.createElement('p');
name.classList.add('place');
const currentStore = data.getFeatureById(store.storeid);
name.textContent = currentStore.getProperty('name');
panel.appendChild(name);
const distanceText = document.createElement('p');
distanceText.classList.add('distanceText');
distanceText.textContent = store.distanceText;
panel.appendChild(distanceText);
});
// Open the panel
panel.classList.add('open');
return;
}
- Перезагрузите сервер и обновите предварительный просмотр, выполнив следующую команду.
$ python3 -m http.server 8080
- Наконец, введите адрес в Великобритании в строку поиска автозаполнения и нажмите на одну из предложенных опций.
Карта должна быть центрирована на этом адресе, и должна появиться боковая панель со списком магазинов в порядке удалённости от выбранного адреса. Один из примеров представлен ниже:
8. Необязательно: разместите свою веб-страницу на хостинге.
До этого момента вы просматривали карту только при активном HTTP-сервере Python. Чтобы просматривать карту за пределами активного сеанса Cloud Shell или делиться URL-адресом карты с другими пользователями, рассмотрите возможность размещения веб-страницы в Cloud Storage . Cloud Storage — это онлайн-сервис для хранения файлов и доступа к данным в инфраструктуре Google. Сервис сочетает в себе производительность и масштабируемость Google Cloud с передовыми функциями безопасности и совместного доступа. Он также предлагает бесплатный тарифный план , что делает его отличным решением для размещения вашего простого локатора магазинов.
В облачном хранилище файлы хранятся в контейнерах, которые аналогичны каталогам на вашем компьютере. Чтобы разместить веб-страницу, сначала необходимо создать контейнер. Вам нужно выбрать уникальное имя для контейнера, возможно, используя своё имя в качестве его части.
- Как только вы определитесь с именем, выполните следующую команду в Cloud Shell:
$ gsutil mb gs://yourname-store-locator
gsutil — это инструмент для взаимодействия с облачным хранилищем. Команда mb
расшифровывается как «make bucket» (создать контейнер). Подробнее обо всех доступных командах, включая те, которые вы используете, см. в разделе «Инструмент gsutil» .
По умолчанию ваши контейнеры и файлы, размещённые в облачном хранилище, являются закрытыми. Однако для вашего локатора хранилищ вам нужно сделать все файлы общедоступными, чтобы они были доступны всем пользователям интернета. Вы можете сделать каждый файл общедоступным после загрузки, но это будет утомительно. Вместо этого вы можете просто установить уровень доступа по умолчанию для созданного контейнера, и все файлы, которые вы в него загружаете, унаследуют этот уровень доступа.
- Выполните следующую команду, заменив
yourname-store-locator
на имя, которое вы выбрали для своего контейнера:
$ gsutil defacl ch -u AllUsers:R gs://yourname-store-locator
- Теперь вы можете загрузить все свои файлы в текущем каталоге (в настоящее время только файлы
index.html
иapp.js
) с помощью следующей команды:
$ gsutil -h "Cache-Control:no-cache" cp * gs://yourname-store-locator
Теперь у вас должна быть веб-страница с картой онлайн. URL для её просмотра будет следующим: http://storage.googleapis.com/yourname-store-locator/index.html , где вместо yourname-store-locator снова указано выбранное вами ранее имя контейнера.
Очистка
Самый простой способ очистить все ресурсы, созданные в этом проекте, — закрыть проект Google Cloud , который вы создали в начале этого руководства:
- Откройте страницу настроек в Cloud Console.
- Нажмите Выбрать проект .
- Выберите проект, который вы создали в начале этого урока, и нажмите «Открыть».
- Введите идентификатор проекта и нажмите «Завершить работу» .
9. Поздравления
Поздравляем! Вы завершили эту лабораторную работу.
Что вы узнали
- Добавление карты с индивидуальным стилем с помощью Maps JavaScript API
- Загрузка слоя данных на карту в формате GeoJSON
- Использование Street View Static API для отображения изображения Street View на веб-странице.
- Использование библиотеки Places для добавления на страницу панели поиска автозаполнения Places
- Использование сервиса Distance Matrix для расчета нескольких расстояний с помощью одного вызова API
- Управление и тестирование проектов веб-разработки в Google Cloud Platform с использованием интерфейса командной строки Cloud Shell на основе браузера
- Размещение сайта в облачном хранилище
Узнать больше
- Изучите другой способ размещения веб-карты с помощью Google App Engine в кодовой лаборатории «Картографирование метро Нью-Йорка» .
- Изучите больше практических работ по платформе Google Карт , например, по созданию сервиса поиска предприятий поблизости .
- Помогите нам создать контент, который будет вам наиболее полезен, ответив на вопрос ниже:
Какие еще практические занятия вы хотели бы увидеть?
Нужная вам кодлаб-программа не указана выше? Запросите её в новом выпуске здесь .
Если вы хотите глубже изучить код, загляните в репозиторий исходного кода по адресу https://github.com/googlecodelabs/google-maps-simple-store-locator .