Содержание


Создание и развертывание в Bluemix приложения для поиска банкоматов

API Google Places и Google Maps упрощают поиск ближайших банкоматов и их нанесение на карту

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

Но служба Google Maps интересна и с точки зрения разработки. Как и многие другие продукты Google, она предоставляет ряд API, которые позволяют разработчикам создавать собственные приложения, используя ее данные. Например, API Google Places позволяет получать информацию о магазинах, предприятиях и учреждениях по их адресу, а API Google Maps – встраивать в веб-страницы статическую или интерактивную карту. Оба этих API, а также несколько других, можно использовать посредством распространенных языков программирования, таких как PHP и JavaScript.

В этом руководстве демонстрируется работа с API Google Places и показано, как создать простое приложение, использующее текущее местоположение пользователя для поиска ближайших банкоматов, и интегрировать эту информацию в веб-приложение на основе PHP. Затем объясняется, как использовать API Google Maps для быстрого создания статической или интерактивной карты, на которой эти банкоматы указаны с учетом вашего текущего местоположения. Естественно, при этом поддерживаются мобильные устройства, так что можно развернуть приложение в IBM® Bluemix® и сразу же начать работать с ним через планшет или смартфон.

Что нужно для создания приложения

  • Знакомство с HTML, CSS и jQuery и умение работать в среде разработки Apache/PHP.
  • Знакомство с основами работы с REST и с классами и объектами PHP, так как они используются в примере PHP-кода для этого руководства.
  • Учетная запись Google: Пример приложения, приведенный в этом руководстве, использует API Google Places для создания списка ближайших банкоматов и API Google Maps для построения статических и интерактивных карт. Для доступа к этим API нужна учетная запись Google, которую можно использовать для генерирования ключа API.

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

  • Bootstrap: для создания мобильных приложений я использую Bootstrap, платформу пользовательских интерфейсов на основе HTML5, специально предназначенную для сенсорных устройств, таких как планшеты и смартфоны. Bootstrap помогает быстро разработать пользовательский интерфейс с минимальными проблемами совместимости для платформы и браузеров.
  • Учетная запись Bluemix и инструмент командной строки CloudFoundry для загрузки своего приложения в Bluemix.

Запустить приложениеПолучить код

Шаг 1. Создание заготовки приложения.

В предположении наличия у вас всех необходимых инструментов, на первом шаге создадим заготовку приложения. Перейдите в корневой каталог документов веб-сервера (обычно /usr/local/apache/htdocs на Linux или C:\Program Files\Apache\htdocs на Windows) и создайте новый подкаталог для приложения. Назовите этот каталог atm.

shell> cd /usr/local/apache/htdocs
shell> mkdir atm

Ссылкой на этот каталог atm по ходу статьи будет служить $APP_ROOT. В каталоге $APP_ROOT создайте новый файл с именем index.php и заполните его следующим содержанием:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Find an ATM</title>
    <style>
    html, body, .tab-content, .tab-pane, #map {
      height: 100%;
    }
    #footer {
      text-align: center
    }
    </style>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap-theme.min.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <div id="content">

      <ul class="nav nav-tabs" role="tablist">
        <li class="active"><a href="#list" data-toggle="tab">List</a></li>
        <li><a href="#map" data-toggle="tab">Map</a></li>
      </ul>

      <div class="tab-content">
        <!-- list tab -->
        <div role="tabpanel" class="tab-pane active" id="list">
        </div>

        <!-- map tab -->
        <div role="tabpanel" class="tab-pane" id="map">
        </div>
      </div>

      <div id="footer">
        <img src="powered-by-google-on-white.png" /> <br />
        <a href="terms.html">Legal Notices</a>
      </div>  

    </div>

    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>    
  </body>
</html>

Эта страница-заготовка устанавливает ключевые компоненты, такие как Bootstrap и jQuery, из их CDN и создает базовый пользовательский интерфейс приложения. Как видите, этот интерфейс состоит из двух вкладок: для списка банкоматов и для карты, на которой отображается их местоположение. Пока при попытке обращения к этой странице через браузер вы увидите пустой интерфейс с вкладками.

Пустой интерфейс с вкладками
Пустой интерфейс с вкладками

Не переживайте! Эта страница скоро заполнится.

Шаг 2. Получение ключа API

Чтобы использовать API Google Places, нужно зарегистрировать веб-приложение в Google.

  1. Войдите в Google, используя свои учетные данные Google, и зайдите в Google Developers Console.
  2. Создайте новый проект, присвойте ему имя и включите доступ к Google Places API, Google Static Maps API и Google Maps JavaScript API, как показано ниже. Ознакомьтесь с условиями использования каждого из этих API-интерфейсов. Включенные API-интерфейсы
    Включенные API-интерфейсы
  3. В окне учетных данных запишите ключ доступа к API (см. рисунок). Он потребуется для авторизации всех запросов приложения к API. Экран реквизитов доступа с ключом API
    Экран реквизитов доступа с ключом API

Готово? Теперь можно выйти из личного кабинета Google.

Шаг 3. Обзор API Google Places

Прежде чем начать разработку приложений с помощью API Google Places, нужно понять, как он работает. Как и многие другие веб-API, он использует протокол HTTP и ожидает HTTP-запросов в назначенной конечной точке. По получении запроса сервер API отвечает документом JSON, содержащим запрошенные данные. Затем эти данные можно проанализировать с помощью серверного языка программирования (например, PHP или Perl) или инструментария на стороне клиента (например, jQuery или AngularJS) и извлечь из них сведения для интеграции в веб-страницу.

Чтобы понять, как работает API Google Places, попробуем его на поиске ресторанов в районе Ковент-Гарден в Лондоне (координаты на карте 51.5117316 N,-0.1232697 W): введите в браузер следующий URL-адрес, изменив его в соответствии со своим ключом API, полученным на предыдущем шаге.

https://maps.googleapis.com/maps/api/place/nearbysearch/json?key=YOUR_API_KEY&location=51.5117316,-0.1232697&rankby=distance&types=restaurant

Вот пример результата:

Пример кода
Пример кода

Как видно из рисунка, API Places отвечает на запрос документом JSON, содержащим список ресторанов в районе с указанными координатами. Для каждого результата в ответ входят наименование, тип, координаты, адрес и уникальный идентификатор заведения. Имеется также целый ряд других полей; см. документацию API Places.

Шаг 4. Включение интерфейса поиска

Теперь, когда вы знаете, как работает API, можно приступить к созданию приложения. Первым шагом будет использование API геопозиционирования HTML5, который включен в большинство современных браузеров для определения текущего местоположения пользователя. Добавьте в файл index.php следующий код:

<!DOCTYPE html>
<html lang="en">
  <!-- snip -->
  <body>
    <div id="content">

    <?php
    // если широты или долготы нет,
    // Попробуем поиск с помощью средств геопозиционирования браузера
    if (empty($latitude) || empty($longitude)) {
    ?>
    <script>
    $(document).ready(function() {

      if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(handle_geo_query, handle_error);
      }

      function handle_geo_query(location) {
        window.location = '?latitude=' + location.coords.latitude + 
          '&longitude=' + location.coords.longitude;
      }

      function handle_error(e) {
        alert('An error occurred during geo-location.');
      }
    });
    </script>
    <?php
    exit;
    }
    ?>

    </div>
  </body>
</html>

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

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

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

После перезагрузки страницы значения широты и долготы, определенные в предыдущем коде, используются для запроса к API Google Places с последующим преобразованием результата в отформатированный список. Вот соответствующий раздел кода:

<!DOCTYPE html>
<html lang="en">
  <!-- snip -->
  <body>
    <div id="content">

      <?php
      // ключ API Google Maps/Places
      $apiKey = 'YOUR_API_KEY';

      // переменные из строки URL-запроса
      $latitude = isset($_GET['latitude']) ? trim($_GET['latitude']) : null;
      $longitude = isset($_GET['longitude']) ? trim($_GET['longitude']) : null;

      // создание URL-запросов к API с требуемыми параметрами
      $placesApiUrl = 'https://maps.googleapis.com/maps/api/place/nearbysearch/json?key=' . 
         $apiKey . '&location=' . sprintf('%f,%f', $latitude, $longitude) . 
         '&rankby=distance&types=atm';   

      // отправка запроса в API Places (для группы банкоматов или одного выбранного банкомата)    
      $ch = curl_init();
      curl_setopt_array($ch, array(
        CURLOPT_RETURNTRANSFER => 1,
        CURLOPT_SSL_VERIFYPEER => 1,  
        CURLOPT_SSL_VERIFYHOST => 2,  
        CURLOPT_URL => $placesApiUrl,
      ));
      $places = json_decode(curl_exec($ch));
      curl_close($ch);
      $results = isset($places->result) ? $places->result : $places->results;
      ?>

      <?php if (count($results)): ?>
      <div class="alert alert-success" role="alert">
        <?php echo count($results); ?> ATM(s) found.
      </div>
      <?php else: ?>
      <div class="alert alert-warning" role="alert">No ATM(s) found.</div>    
      <?php endif; ?>

      <ul class="nav nav-tabs" role="tablist">
        <li class="active"><a href="#list" data-toggle="tab">List</a></li>
        <li><a href="#map" data-toggle="tab">Map</a></li>
        <a href="index.php" class="btn btn-success">Refresh</a>
      </ul>

      <div class="tab-content">
        <!-- list tab -->
        <div role="tabpanel" class="tab-pane active" id="list">
          <ul class="list-group">
          <?php
          // перебор возвращенного списка банкоматов
          // создание данных для маркеров на карте
          // отображение наименования, метки и расположения каждого банкомата
          $c = 65;
          foreach ($results as $place) {
            $label = chr($c);
            $markers[] = "markers=color:red|label:" . $label . "|" . 
              $place->geometry->location->lat . "," . $place->geometry->location->lng;
            $c++;
          ?>
            <li class="list-group-item">
              <h3>
                <span class="label label-danger"><?php echo $label; ?></span>
                <span class="label label-default"><?php echo $place->name; ?></span>
              </h3>
              <p><?php echo $place->vicinity; ?></p>
            </li>
          <?php
          }
          ?>
            <li class="list-group-item">
              <?php echo implode(',', $places->html_attributions); ?>
            </li>
          </ul>
        </div>

        <!-- map tab -->
        <div role="tabpanel" class="tab-pane" id="map">
        </div>
      </div>
    </div>
  </body>
</html>

Первые несколько строк кода создают новый запрос к API с использованием формата, описанного в предыдущем разделе. Ключ API и координаты текущего местоположения пользователя интерполируются в URL-запрос, а результат фильтруется для отображения только банкоматов с параметром types. Наконец, результаты ранжируются по расстоянию, так чтобы первым в списке значился ближайший банкомат.

Для передачи запроса используются PHP-функции curl, а результат в коде JSON декодируется в PHP-объект с помощью функции json_decode(). Теперь легко перебрать множество результатов и представить их в виде HTML-списка в одной из двух заготовленных вкладок. Этот список для каждого элемента содержит наименование банкомата, его местоположение и автоматически генерируемое буквенное обозначение. Вот пример результата:

Пример списка банкоматов
Пример списка банкоматов

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

Шаг 5. Добавление статической карты

На предыдущем шаге мы получили список банкоматов. Теперь эти банкоматы нужно отобразить на карте, чтобы пользователь мог легко найти их из своего текущего местоположения. Самый простой способ – сделать это с помощью API Google Static Maps, который в ответ на запрос выдает картографическое изображение указанного места с маркерами по указанным координатам.

Чтобы понять, как работает API Google Static Maps, используем его для получения карты района Ковент-Гарден в Лондоне, указав те же координаты, что и выше. Введите в браузер следующий URL-адрес, изменив его в соответствии со своим ключом API:

https://maps.googleapis.com/maps/api/staticmap?key=YOUR_API_KEY&size=640x480&maptype=roadmap&zoom=15&scale=2&markers=51.5117316,-0.1232697

Вот пример результата:

Изображение карты API Static Maps
Изображение карты API Static Maps

Как видите, API Static Maps отвечает на запрос изображением, содержащим карту по указанным координатам. В URL-запросе указаны размер изображения, коэффициент увеличения и масштаб, и можно также добавить маркеры, включив в URL-запрос параметр markers с их координатами. Имеется также целый ряд других параметров для управления отображением карты; см. документацию Static Maps API.

Теперь, когда вы знаете, как работает этот процесс, довольно легко расширить предыдущий PHP-код, добавив статические карты. Большая часть тяжелой работы уже выполнена; все, что нужно, – это сделать второй запрос к конечной точке Static Maps API и вставить полученное изображение во вторую вкладку. Вот отредактированный код:

<!DOCTYPE html>
<html lang="en">
  <!-- snip -->
  <body>
    <div id="content">

      <?php
      // ключ API Google Maps/Places
      $apiKey = 'YOUR_API_KEY';

      // переменные из строки URL-запроса
      $latitude = isset($_GET['latitude']) ? trim($_GET['latitude']) : null;
      $longitude = isset($_GET['longitude']) ? trim($_GET['longitude']) : null;

      // создание URL-запросов к API с требуемыми параметрами
      $placesApiUrl = 'https://maps.googleapis.com/maps/api/place/nearbysearch/json?key=' . 
        $apiKey . '&location=' . sprintf('%f,%f', $latitude, $longitude) .
        '&rankby=distance&types=atm';   
      $mapsApiUrl = 'https://maps.googleapis.com/maps/api/staticmap?key=' . $apiKey . 
        '&size=640x480&maptype=roadmap&scale=2&markers=color:green|' . 
        sprintf('%f,%f', $latitude, $longitude);

      // отправка запроса в API Places (для группы банкоматов или одного выбранного банкомата)    
      $ch = curl_init();
      curl_setopt_array($ch, array(
        CURLOPT_RETURNTRANSFER => 1,
        CURLOPT_SSL_VERIFYPEER => 1,  
        CURLOPT_SSL_VERIFYHOST => 2,  
        CURLOPT_URL => $placesApiUrl,
      ));
      $places = json_decode(curl_exec($ch));
      curl_close($ch);
      $results = isset($places->result) ? $places->result : $places->results;
      ?>

      <?php if (count($results)): ?>
      <div class="alert alert-success" role="alert">
        <?php echo count($results); ?> ATM(s) found.
      </div>
      <?php else: ?>
      <div class="alert alert-warning" role="alert">No ATM(s) found.</div>    
      <?php endif; ?>

      <ul class="nav nav-tabs" role="tablist">
        <li class="active"><a href="#list" data-toggle="tab">List</a></li>
        <li><a href="#map" data-toggle="tab">Map</a></li>
        <a href="index.php" class="btn btn-success">Refresh</a>
      </ul>

      <div class="tab-content">
        <!-- list tab -->
        <div role="tabpanel" class="tab-pane active" id="list">
          <ul class="list-group">
          <?php
          // перебор возвращенного списка банкоматов
          // создание данных для маркеров на карте
          // отображение наименования, метки и расположения каждого банкомата
          $c = 65;
          foreach ($results as $place) {
            $label = chr($c);
            $markers[] = "markers=color:red|label:" . $label . "|" . 
              $place->geometry->location->lat . "," . $place->geometry->location->lng;
            $c++;
          ?>
            <li class="list-group-item">
              <h3>
                <span class="label label-danger"><?php echo $label; ?></span>
                <span class="label label-default"><?php echo $place->name; ?></span>
              </h3>
              <p><?php echo $place->vicinity; ?></p>
            </li>
          <?php
          }
          ?>
            <li class="list-group-item">
              <?php echo implode(',', $places->html_attributions); ?>
            </li>
          </ul>
        </div>

        <!-- map tab -->
        <div role="tabpanel" class="tab-pane" id="map">
          <img class="img-responsive" id="mapImage"
            src="<?php echo $mapsApiUrl; ?>&<?php echo implode('&', $markers); ?>" />
        </div>
      </div>

    </div>

  </body>
</html>

Как видите, в эту редакцию входит дополнительный запрос к Static Maps API. Помимо стандартных параметров size, zoom и scale, URL-запрос включает в себя несколько значений параметра markers. Первое – это зеленый маркер, текущее местоположение пользователя. За ним следует массив $markers, каждый элемент которого создает красный маркер, отображаемый в соответствующем месте расположения банкомата. Метка каждого маркера ATM соответствует его автоматически генерируемому буквенному обозначению, что упрощает пользователю поиск банкомата в списке по его местоположению на карте.

Полученное изображение помещается на страницу второй вкладки. Вот как это выглядит:

Пример второй вкладки
Пример второй вкладки

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

Это изменение реализовано в следующей редакции предыдущего кода:

<!DOCTYPE html>
<html lang="en">
  <!-- snip -->
  <body>
    <div id="content">

      <?php
      // ключ API Google Maps/Places
      $apiKey = 'YOUR_API_KEY';

      // variables from URL request string
      $latitude = isset($_GET['latitude']) ? trim($_GET['latitude']) : null;
      $longitude = isset($_GET['longitude']) ? trim($_GET['longitude']) : null;
      $selected = isset($_GET['selected']) ? trim($_GET['selected']) : null;

      // создание URL-запросов к API с требуемыми параметрами
      $placesApiUrl = 'https://maps.googleapis.com/maps/api/place/nearbysearch/json?key=' . 
        $apiKey . '&location=' . sprintf('%f,%f', $latitude, $longitude) .
        '&rankby=distance&types=atm';   
      $mapsApiUrl = 'https://maps.googleapis.com/maps/api/staticmap?key=' . $apiKey .
       '&size=640x480&maptype=roadmap&scale=2&markers=color:green|' . 
        sprintf('%f,%f', $latitude, $longitude);

      // отправка запроса в API Places (для группы банкоматов или одного выбранного банкомата)    
      $ch = curl_init();
      curl_setopt_array($ch, array(
        CURLOPT_RETURNTRANSFER => 1,
        CURLOPT_SSL_VERIFYPEER => 1,  
        CURLOPT_SSL_VERIFYHOST => 2,  
        CURLOPT_URL => $placesApiUrl,
      ));
      $places = json_decode(curl_exec($ch));
      curl_close($ch);
      $results = isset($places->result) ? $places->result : $places->results;
      ?>

      <?php if (count($results) && empty($selected)): ?>
      <div class="alert alert-success" role="alert">
        <?php echo count($results); ?> ATM(s) found.
      </div>
      <?php elseif (count($results) && !empty($selected)): ?>
      <div class="alert alert-success" role="alert">Selected ATM found.</div>
      <?php else: ?>
      <div class="alert alert-warning" role="alert">No ATM(s) found.</div>    
      <?php endif; ?>

      <ul class="nav nav-tabs" role="tablist">
        <li class="active"><a href="#list" data-toggle="tab">List</a></li>
        <li><a href="#map" data-toggle="tab">Map</a></li>
        <a href="index.php" class="btn btn-success">Refresh</a>
      </ul>

      <div class="tab-content">
        <!-- list tab -->
        <div role="tabpanel" class="tab-pane active" id="list">
          <ul class="list-group">
          <?php
          // перебор возвращенного списка банкоматов
          // создание данных для маркеров на карте
          // отображение наименования, метки и расположения каждого банкомата
          $c = 65;
          foreach ($results as $place) {
            $label = chr($c);
            if (!empty($selected)) {
              $selArr = explode(':', $selected);
              $label = $selArr[0];
              $selectedId = $selArr[1];
              if ($place->place_id != $selectedId) {
                continue;
              }
            }
            $markers[] = "markers=color:red|label:" . $label . "|" . 
              $place->geometry->location->lat . "," . $place->geometry->location->lng;
            $c++;
          ?>
            <li class="list-group-item">
              <h3>
                <span class="label label-danger"><?php echo $label; ?></span>
                <span class="label label-default"><?php echo $place->name; ?></span>
              </h3>
              <p><?php echo $place->vicinity; ?></p>
              <a href="?latitude=<?php echo $latitude; ?>&longitude=<?php echo $longitude; ?>
                &selected=<?php echo $label; ?>:<?php echo $place->place_id; ?>#map">
                Pinpoint on map</a>
            </li>
          <?php
          }
          ?>
            <li class="list-group-item">
              <?php echo implode(',', $places->html_attributions); ?>
            </li>
          </ul>
        </div>

        <!-- map tab -->
        <div role="tabpanel" class="tab-pane" id="map">
          <img class="img-responsive" id="mapImage"
            src="<?php echo $mapsApiUrl; ?>&<?php echo implode('&', $markers); ?>" />
        </div>
      </div>

    </div>
  </body>
</html>

Обратите внимание, что каждый элемент списка теперь включает в себя гиперссылку с надписью «Определить на карте». Гиперссылка перезагружает страницу со всей той же информацией, что и прежде, но в URL-запросе содержится дополнительный параметр selected с идентификатором уникального местоположения банкомата. Если в URL присутствует этот параметр selected, код дополнительно фильтрует возвращенный результирующий набор, включая в него только выбранные ATM и соответственно генерирует только один маркер банкомата. В этом случае изображение карты содержит только текущее местоположение пользователя и этот выбранный банкомат.

Вот как это выглядит:

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

Шаг 6. Добавление интерактивной карты

У статической карты есть один недостаток: ее нельзя увеличивать и уменьшать, поворачивать, или нажимать на элементы на карте, чтобы получить дополнительную информацию. Альтернативой служит API Google Maps JavaScript, который позволяет создавать интерактивные карты, реагирующие на события касания или нажатия кнопки с помощью JavaScript.

Чтобы использовать API Google Maps JavaScript, нужно включить файл исходного кода JavaScript с помощью следующей строки:

<script type="text/javascript"
src="https://maps.googleapis.com/maps/api/js?key=[YOUR_API_KEY]"></script>

Затем можно создать новый объект карты следующим образом:

<script>
  var mapOptions = {
    zoom: 18,
    center: new google.maps.LatLng(51.5117316,-0.1232697),
    mapTypeId: google.maps.MapTypeId.ROADMAP
  };

  var map = new google.maps.Map(document.getElementById('map'), mapOptions);  
</script>

И добавить маркер на объекте карты:

<script>
var marker = new google.maps.Marker({
  position: new google.maps.LatLng(51.5117316,-0.1232697),
  map: map,
  title: 'My Location',
});    
marker.setMap(map);   
</script>

Это приведет к созданию новой карты с центром в Ковент-Гарден и масштабом 18. Карта автоматически присоединяется к элементу DOM map, который должен присутствовать в коде HTML-страницы.

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

<!DOCTYPE html>
<html lang="en">
  <!-- snip -->
  <body>
    <div id="content">

      <?php
      // ключ API Google Maps/Places
      $apiKey = 'YOUR_API_KEY';

      // переменные из строки URL-запроса
      $latitude = isset($_GET['latitude']) ? trim($_GET['latitude']) : null;
      $longitude = isset($_GET['longitude']) ? trim($_GET['longitude']) : null;
      $selected = isset($_GET['selected']) ? trim($_GET['selected']) : null;

      // создание URL-запроса к API с требуемыми параметрами
      $placesApiUrl = 'https://maps.googleapis.com/maps/api/place/nearbysearch/json?key=' . 
        $apiKey . '&location=' . sprintf('%f,%f', $latitude, $longitude) .
        '&rankby=distance&types=atm';   

      // отправка запроса в API Places (для группы банкоматов или одного выбранного банкомата)    
      $ch = curl_init();
      curl_setopt_array($ch, array(
        CURLOPT_RETURNTRANSFER => 1,
        CURLOPT_SSL_VERIFYPEER => 1,  
        CURLOPT_SSL_VERIFYHOST => 2,  
        CURLOPT_URL => $placesApiUrl,
      ));
      $places = json_decode(curl_exec($ch));
      curl_close($ch);
      $results = isset($places->result) ? $places->result : $places->results;
      ?>

      <?php if (count($results) && empty($selected)): ?>
      <div class="alert alert-success" role="alert">
        <?php echo count($results); ?> ATM(s) found.
      </div>
      <?php elseif (count($results) && !empty($selected)): ?>
      <div class="alert alert-success" role="alert">Selected ATM found.</div>
      <?php else: ?>
      <div class="alert alert-warning" role="alert">No ATM(s) found.</div>    
      <?php endif; ?>

      <ul class="nav nav-tabs" role="tablist">
        <li class="active"><a href="#list" data-toggle="tab">List</a></li>
        <li><a href="#map" data-toggle="tab">Map</a></li>
        <a href="index.php" class="btn btn-success">Refresh</a>
      </ul>

      <div class="tab-content">
        <!-- ATMs as list -->
        <div role="tabpanel" class="tab-pane active" id="list">
          <ul class="list-group">

          <?php
          // перебор возвращенного списка банкоматов
          // создание данных для маркеров на карте
          // отображение наименования, метки и расположения каждого банкомата
          $c = 65;
          foreach ($results as $place) {
            $label = chr($c);
            if (!empty($selected)) {
              $selArr = explode(':', $selected);
              $label = $selArr[0];
              $selectedId = $selArr[1];
              if ($place->place_id != $selectedId) {
                continue;
              }
            }

            $markers[] = array(
              'title' => $label . ': ' . $place->name,
              'lat' => $place->geometry->location->lat,
              'lng' => $place->geometry->location->lng,
            );
            $c++;
          ?>
            <li class="list-group-item">
              <h3>
                <span class="label label-danger"><?php echo $label; ?></span>
                <span class="label label-default"><?php echo $place->name; ?></span>
              </h3>
              <p><?php echo $place->vicinity; ?></p>
              <a href="?latitude=<?php echo $latitude; ?>&longitude=<?php echo $longitude; ?>
                &selected=<?php echo $label; ?>:<?php echo $place->place_id; ?>#map">
                Pinpoint on map</a>
            </li>

          <?php
          }
          ?>
            <li class="list-group-item">
              <?php echo implode(',', $places->html_attributions); ?>
            </li>
          </ul>
        </div>

        <!-- ATMs on map -->
        <div role="tabpanel" class="tab-pane" id="map">

        </div>
      </div>

    </div>

    <script type="text/javascript" 
      src="https://maps.googleapis.com/maps/api/js?key=<?php echo $apiKey; ?>"></script>
    <script>
    $(document).ready(function() {

      var mapOptions = {
        zoom: 18,
        center: new google.maps.LatLng(<?php printf('%f,%f', $latitude, $longitude); ?>),
        mapTypeId: google.maps.MapTypeId.ROADMAP
      };

      var map = new google.maps.Map(document.getElementById('map'), mapOptions);  

      var marker = new google.maps.Marker({
        position: new google.maps.LatLng(<?php printf('%f,%f', $latitude, $longitude); ?>),
        map: map,
        title: 'Current Location',
        icon: {
          path: google.maps.SymbolPath.CIRCLE,
          scale: 10,
          strokeColor: 'green',
        }

      });    
      marker.setMap(map);    
      var infowindow = new google.maps.InfoWindow({
          content: 'None'
      });
      <?php foreach ($markers as $marker): ?>
      var marker = new google.maps.Marker({
          position: new google.maps.LatLng(
            <?php echo $marker['lat']; ?>, <?php echo $marker['lng']; ?>
          ),
          map: map,
          title: '<?php echo $marker['title']; ?>'
      });
      marker.setMap(map);    
      google.maps.event.addListener(marker, 'click', function() {
        infowindow.setContent('<strong>' + this.getTitle() + '</strong>');
        infowindow.open(map,this);      
      });     
      <?php endforeach; ?>
      $("a[href='#map']").on('shown.bs.tab', function(){
        google.maps.event.trigger(map, 'resize');
      });  

    });
    </script>
  </body>
</html>

Многое из этого покажется вам знакомым по предыдущей, статической версии кода. Основное отличие связано с тем, как создается карта: вместо запроса к API Google Static Maps, который возвращает изображение, эта версия кода загружает API Google Maps JavaScript и использует его для создания и визуализации карты с требуемыми маркерами. Ниже перечислены основные изменения:

  • во-первых, массив $markers заполняется иначе, так что каждый элемент представляет собой один из банкоматов, возвращаемых API Google Places. Каждый элемент массива содержит информацию об имени банкомата, его широте и долготе;
  • затем код JavaScript в конце страницы, используя эту широту и долготу, создает новый объект Map, который передается в URL-запросе, и помещает его на вторую вкладку страницы. Он также создает объект InfoWindow, который содержит информацию о выбранном банкомате в любой заданной точке;
  • наконец, PHP-цикл foreach() перебирает массив $markers, обрабатывает места расположения банкоматов и для каждого местоположения банкомата создает JavaScript-объект Marker. Затем эти объекты Marker присоединяются к Map, причем каждому назначается прослушиватель событий click. Когда Marker получает событие click, объект прослушивателя обновляет содержание InfoWindow, вставляя имя выбранного ATM, и отображает его.

Вот как это выглядит:

Шаг 7. Развертывание в Bluemix

Теперь, когда код приложения готов, на последнем шаге его нужно развернуть. Конечно, если сделать это локально, то все будет в порядке - приложение можно будет использовать как обычно. Однако чтобы развернуть его в Bluemix, вам понадобится учетная запись Bluemix и придется скачать и установить клиент командной строки Cloud Foundry. Для завершения процесса развертывания выполните следующие действия.

  1. Создайте манифест приложения. Файл манифеста приложения указывает Bluemix, как развертывать приложение. В частности, он указывает среде выполнения PHP (buildpack-пакет), что следует использовать. Создайте этот файл в $APP_ROOT/manifest.yml, заполнив его следующей информацией.
    ---
    applications:
    - name: atm-finder-[random-number]
    memory: 256M
    instances: 1
    host: atm-finder-[random-number]
    buildpack: https://github.com/dmikusa-pivotal/cf-php-build-pack.git

    Не забудьте отредактировать имена хоста и приложения, чтобы сделать их уникальными, – изменив их или добавив случайное число. Я использую buildpack-пакет CloudFoundry PHP, хотя есть и другие варианты.

  2. Войдите в Bluemix и разверните приложение. Для входа Bluemix с помощью имени пользователя и пароля IBM используйте интерфейс командной строки cf:
    shell> cf api https://api.ng.bluemix.net
    shell> cf login

    Перейдите в каталог $APP_ROOT и опубликуйте приложение в Bluemix:

    shell> cf push

    Вот пример того, что вы увидите в ходе этого процесса.

Теперь ваше приложение должно быть развернуто Bluemix. Чтобы запустить его, введите в браузер адрес хоста из манифеста своего приложения — например, http://atm-finder-[случайное число].mybluemix.net. Или нажмите кнопку Запустить приложение в начале этой статьи, чтобы попробовать демо-приложение.

Заключение

Как демонстрирует эта статья, Bluemix обеспечивает прочный фундамент для создания и развертывания мобильных приложений на облачной платформе. Добавьте PHP, Bootstrap и jQuery, и у вас будет надежный инструментарий для создания динамических интерактивных мобильных веб-приложений.

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


Ресурсы для скачивания


Похожие темы

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Облачные вычисления, Мобильные приложения
ArticleID=1016516
ArticleTitle=Создание и развертывание в Bluemix приложения для поиска банкоматов
publish-date=10012015