Создайте простой локатор магазинов с помощью платформы Google Maps (JavaScript)

1. Прежде чем начать

Одной из наиболее распространённых функций веб-сайта является отображение карты Google , на которой отмечено одно или несколько местоположений компании, учреждения или другого физического объекта. Реализация таких карт может значительно различаться в зависимости от требований, таких как количество местоположений и частота их обновления.

В этой лабораторной работе вы рассмотрите простейший вариант использования — небольшое количество редко меняющихся локаций, например, поиск магазинов для сети магазинов. В этом случае вы можете использовать относительно простой подход без необходимости программирования на стороне сервера. Но это не значит, что вы не можете проявить творческий подход, и вы сможете это сделать, используя формат данных GeoJSON для хранения и отображения произвольной информации о каждом магазине на карте, а также для настройки маркеров и общего стиля самой карты.

Наконец, в качестве дополнительного бонуса вы можете использовать Cloud Shell для разработки и размещения своего локатора магазинов. Хотя использование этого инструмента не является обязательным, оно позволяет разрабатывать локатор магазинов с любого устройства с веб-браузером и делать его доступным для всех онлайн.

489628918395c3d0.png

Предпосылки

  • Базовые знания HTML и JavaScript

Что ты будешь делать?

  • Отобразить карту с указанием местоположений магазинов и информацией, сохраненной в формате GeoJSON.
  • Настройте маркеры и саму карту.
  • Отображать дополнительную информацию о магазине при нажатии на его маркер.
  • Добавьте на веб-страницу панель поиска с функцией автозаполнения мест.
  • Определите местоположение магазина, наиболее близкое к начальной точке, указанной пользователем.

2. Настройте

На шаге 3 следующего раздела включите следующие три API для этой кодовой лаборатории:

  • API JavaScript Карт
  • API мест
  • API матрицы расстояний

Начните работу с платформой Google Карт

Если вы ранее не использовали платформу Google Карт, следуйте руководству «Начало работы с платформой Google Карт» или посмотрите плейлист «Начало работы с платформой Google Карт», чтобы выполнить следующие шаги:

  1. Создайте платежный аккаунт.
  2. Создать проект.
  3. Включите API и SDK платформы Google Карт (перечислены в предыдущем разделе).
  4. Сгенерируйте ключ API.

Активировать Cloud Shell

В этой лабораторной работе вы будете использовать Cloud Shell — среду командной строки, работающую в Google Cloud, которая обеспечивает доступ к продуктам и ресурсам, работающим в Google Cloud, чтобы вы могли размещать и запускать свой проект полностью из веб-браузера.

Чтобы активировать Cloud Shell из Cloud Console, нажмите «Активировать Cloud Shell». 89665d8d348105cd.png (подготовка и подключение к среде займет всего несколько минут).

5f504766b9b3be17.png

Это откроет новую оболочку в нижней части браузера после возможного показа вступительного объявления.

d3bb67d514893d1f.png

После подключения к 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-страницы, которая послужит основой для остальной части кодовой лаборатории.

  1. На панели инструментов Cloud Shell нажмите «Запустить редактор». 996514928389de40.png чтобы открыть редактор кода в новой вкладке.

Этот веб-редактор кода позволяет легко редактировать файлы в Cloud Shell.

Скриншот 2017-04-19 в 10.22.48 AM.png

  1. Создайте новый каталог store-locator для вашего приложения в редакторе кода, нажав Файл > Новая папка .

НоваяПапка.png

  1. Назовите новую папку store-locator .

Далее вы создаете веб-страницу с картой.

  1. Создайте файл в каталоге store-locator с именем index.html .

3c257603da5ab524.png

  1. Поместите следующее содержимое в файл 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 .

  1. Замените текст YOUR_API_KEY во фрагменте кода на ключ API, который вы сгенерировали ранее в этой лабораторной работе.
  2. Наконец, создайте еще один файл с именем 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.

  1. Перейдите в Cloud Shell и введите следующее:
$ cd store-locator
$ python3 -m http.server 8080

Вы видите несколько строк журнала, показывающих, что вы действительно используете простой HTTP-сервер в Cloud Shell с веб-приложением, прослушивающим порт localhost 8080.

  1. Откройте вкладку веб-браузера с этим приложением, нажав «Веб-просмотр». 95e419ae763a1d48.png на панели инструментов Cloud Console и выберите Предварительный просмотр на порту 8080 .

47b06e5169eb5add.png

bdab1f021a3b91d5.png

При нажатии на этот пункт меню открывается новая вкладка в веб-браузере с содержимым HTML-кода, полученного с простого HTTP-сервера Python. Если всё прошло успешно, вы увидите карту с центром в Лондоне, Англия.

Чтобы остановить простой HTTP-сервер, нажмите Control+C в Cloud Shell.

4. Заполните карту данными GeoJSON.

Теперь взгляните на данные о магазинах. GeoJSON — это формат данных, представляющий простые географические объекты, такие как точки, линии или многоугольники на карте. Эти объекты также могут содержать произвольные данные. Это делает GeoJSON отличным кандидатом для представления магазинов, которые, по сути, представляют собой точки на карте с некоторыми дополнительными данными, такими как название магазина, часы работы и номер телефона. Самое главное, GeoJSON имеет первоклассную поддержку в Google Картах, что означает, что вы можете отправить документ GeoJSON на карту Google, и он будет корректно отображен на карте.

  1. Создайте новый файл с именем 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 .

  1. Теперь отредактируйте 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, используя ту же команду, что и раньше.

  1. Вернитесь в Cloud Shell и введите следующее:
$ python3 -m http.server 8080
  1. Нажмите «Предварительный просмотр в Интернете». 95e419ae763a1d48.png > Снова просмотрите порт 8080 , и вы увидите карту с маркерами, на которые можно нажать, чтобы просмотреть подробную информацию о каждом магазине, как показано в следующем примере. Прогресс!

c4507f7d3ea18439.png

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 для местоположения магазина.

Перед тем как приступить к развертыванию, вам необходимо выполнить несколько шагов:

  1. Установите правильное значение для переменной apiKey , заменив строку 'YOUR_API_KEY' в app.js на ваш собственный ключ API, полученный ранее (тот же, который вы вставили в index.html , оставив кавычки нетронутыми).
  2. Выполните следующие команды в 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
  1. Для предварительного просмотра готового локатора магазинов выполните следующую команду:.
$ python3 -m http.server 8080

При перезагрузке предварительного просмотра вы должны увидеть что-то вроде этой карты с пользовательским стилем, пользовательскими изображениями маркеров, улучшенным форматированием информационного окна и изображением Street View для каждого местоположения:

3d8d13da126021dd.png

6. Получите пользовательский ввод

Пользователи локаторов магазинов обычно хотят знать, какой магазин находится ближе всего к ним или с какого адреса они планируют начать свой маршрут. Добавьте строку поиска Place Autocomplete, чтобы пользователь мог легко ввести начальный адрес. Place Autocomplete предоставляет функцию опережающего ввода, похожую на функцию автозаполнения в других строках поиска Google, но все подсказки — это места из платформы Google Карт.

  1. Вернитесь к редактированию 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>

Панель поиска автозаполнения и выдвижная панель изначально скрыты до тех пор, пока они не понадобятся.

  1. Теперь добавьте виджет автозаполнения на карту в конце функции 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 автозаполнения в правый верхний угол карты и указывает, какие поля должны быть возвращены для каждого места в ответе.

  1. Перезагрузите сервер и обновите предварительный просмотр, выполнив следующую команду:.
$ python3 -m http.server 8080

Теперь в правом верхнем углу карты должен появиться виджет автозаполнения, который покажет вам адреса в Великобритании, соответствующие введенному вами тексту.

5163f34a03910187.png

Теперь вам нужно обработать момент, когда пользователь выбирает прогноз из виджета автозаполнения, и использовать это местоположение в качестве основы для расчета расстояний до ваших магазинов.

  1. Добавьте следующий код в конец 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 развивает эту концепцию, находя оптимальные пары между несколькими возможными пунктами отправления и пунктами назначения с учётом времени в пути и расстояния. В данном случае, чтобы помочь пользователю найти ближайший магазин к выбранному адресу, вы указываете один пункт отправления и массив адресов магазинов в качестве пунктов назначения.

  1. Добавьте новую функцию в 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 , чтобы определить порядок отображения магазинов.

  1. Добавьте новую функцию в 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;
}
  1. Перезагрузите сервер и обновите предварительный просмотр, выполнив следующую команду.
$ python3 -m http.server 8080
  1. Наконец, введите адрес в Великобритании в строку поиска автозаполнения и нажмите на одну из предложенных опций.

Карта должна быть центрирована на этом адресе, и должна появиться боковая панель со списком магазинов в порядке удалённости от выбранного адреса. Один из примеров представлен ниже:

489628918395c3d0.png

8. Необязательно: разместите свою веб-страницу на хостинге.

До этого момента вы просматривали карту только при активном HTTP-сервере Python. Чтобы просматривать карту за пределами активного сеанса Cloud Shell или делиться URL-адресом карты с другими пользователями, рассмотрите возможность размещения веб-страницы в Cloud Storage . Cloud Storage — это онлайн-сервис для хранения файлов и доступа к данным в инфраструктуре Google. Сервис сочетает в себе производительность и масштабируемость Google Cloud с передовыми функциями безопасности и совместного доступа. Он также предлагает бесплатный тарифный план , что делает его отличным решением для размещения вашего простого локатора магазинов.

В облачном хранилище файлы хранятся в контейнерах, которые аналогичны каталогам на вашем компьютере. Чтобы разместить веб-страницу, сначала необходимо создать контейнер. Вам нужно выбрать уникальное имя для контейнера, возможно, используя своё имя в качестве его части.

  1. Как только вы определитесь с именем, выполните следующую команду в Cloud Shell:
$ gsutil mb gs://yourname-store-locator

gsutil — это инструмент для взаимодействия с облачным хранилищем. Команда mb расшифровывается как «make bucket» (создать контейнер). Подробнее обо всех доступных командах, включая те, которые вы используете, см. в разделе «Инструмент gsutil» .

По умолчанию ваши контейнеры и файлы, размещённые в облачном хранилище, являются закрытыми. Однако для вашего локатора хранилищ вам нужно сделать все файлы общедоступными, чтобы они были доступны всем пользователям интернета. Вы можете сделать каждый файл общедоступным после загрузки, но это будет утомительно. Вместо этого вы можете просто установить уровень доступа по умолчанию для созданного контейнера, и все файлы, которые вы в него загружаете, унаследуют этот уровень доступа.

  1. Выполните следующую команду, заменив yourname-store-locator на имя, которое вы выбрали для своего контейнера:
$ gsutil defacl ch -u AllUsers:R gs://yourname-store-locator
  1. Теперь вы можете загрузить все свои файлы в текущем каталоге (в настоящее время только файлы 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 на основе браузера
  • Размещение сайта в облачном хранилище

Узнать больше

Какие еще практические занятия вы хотели бы увидеть?

Визуализация данных на картах Подробнее о настройке стиля моих карт Создание 3D-взаимодействий на картах

Нужная вам кодлаб-программа не указана выше? Запросите её в новом выпуске здесь .

Если вы хотите глубже изучить код, загляните в репозиторий исходного кода по адресу https://github.com/googlecodelabs/google-maps-simple-store-locator .