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

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

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

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

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

489628918395c3d0.png

Предпосылки

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

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

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

2. Настройте

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

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

Начало работы с платформой Google Карт

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

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

Активировать облачную оболочку

В этой лаборатории кода вы используете 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.png

  1. Создайте новый каталог store-locator для своего приложения в редакторе кода, щелкнув File > New Folder .

Новая Папка.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, включает использование библиотеки мест для функции автозаполнения, которую вы добавите позже, и указывает имя функции JavaScript, которая запускается после загрузки API JavaScript Карт, а именно 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, а веб-приложение прослушивает локальный порт 8080.

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

47b06e5169eb5add.png

bdab1f021a3b91d5.png

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

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

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

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

  1. Создайте новый файл с именем stores.json и вставьте в него следующий код:

магазины.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. Как скучно! Приправьте его собственным стилем карты, маркерами, логотипами и изображениями просмотра улиц.

Вот новая версия 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 . Используйте Control+C , чтобы остановить простой HTTP-сервер, если он запущен.
$ 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

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

3d8d13da126021dd.png

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

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

  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. Список ближайших магазинов

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

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

Пользователь ожидает увидеть список магазинов, упорядоченных от ближайшего к дальнему. Заполните список на боковой панели для каждого магазина, используя список, возвращенный из функции 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-адресом карты с другими, рассмотрите возможность использования облачного хранилища для размещения веб-страницы. Облачное хранилище — это веб-служба онлайн-хранилища файлов для хранения и доступа к данным в инфраструктуре Google. Сервис сочетает в себе производительность и масштабируемость Google Cloud с расширенными возможностями безопасности и совместного использования. Он также предлагает бесплатный уровень , который отлично подходит для размещения вашего простого локатора магазинов.

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

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

gsutil — это инструмент для взаимодействия с облачным хранилищем. Команда mb творчески означает «сделать ведро». Дополнительные сведения обо всех доступных командах, включая те, которые вы используете, см. в разделе Инструмент 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 , который вы создали в начале этого руководства:

  • Откройте страницу настроек в облачной консоли.
  • Щелкните Выбрать проект .
  • Выберите проект, который вы создали в начале этого руководства, и нажмите « Открыть ».
  • Введите идентификатор проекта и нажмите Завершить работу .

9. Поздравления

Поздравляем! Вы выполнили эту лабораторную работу.

Что вы узнали

Учить больше

Какие еще кодлабы вы хотели бы увидеть?

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

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

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