Использование Qt для работы с Google Maps API

Материал из Wiki.crossplatform.ru

Перейти к: навигация, поиск

Содержание

[править] Использование Qt для работы с Google Maps API

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

[править] Создание пользовательского элемента для отображения карты

Во-первых, нам необходимо создать пользовательский компонент на основе QWebView. Назовём этот компонент Map. Этот компонент имеет некоторые дополнительные возможности, которые позволяют манипулировать картой.

Метод geoCode, используя QNetworkAccessManager::get(const QNetworkRequest &request), запрашивает у Google Maps API координаты в формате CSV, по указанному адресу

void Map::geoCode(const QString& address)
{
    clearCoordinates();
    QString requestStr( tr("http://maps.google.com/maps/geo?q=%1&output=%2&key=%3")
            .arg(address)
            .arg("csv")
            .arg("GOOGLE_MAPS_KEY") );
 
    manager->get( QNetworkRequest(requestStr) );
    ++pendingRequests;
}

В методе replyFinished, который связан с сигналом NetworkManager::finished(QNetworkReply*), мы получаем ответ на наш запрос. В этом методе мы обрабатываем координаты, возвращаемые в csv формате от Google, сохраняем их, и испускаем сигнал reloadMap.

Ответ в формате csv состоит из четырех чисел, разделенных запятыми:

  1. код состояния HTTP;
  2. Точность;
  3. Широта;
  4. Долгота.
void Map::replyFinished(QNetworkReply *reply)
{
    QString replyStr( reply->readAll() );
    QStringList coordinateStrList = replyStr.split(",");
 
    if( coordinateStrList.size() == 4) {
        QPointF coordinate( coordinateStrList[2].toFloat(),coordinateStrList[3].toFloat() );
        coordinates << coordinate;
    }
 
    --pendingRequests;
    if( pendingRequests<1 )
        emit( reloadMap() );
}

Сигнал reloadMap соединён со слотом loadCoordinates . Этот метод использует возможности QtWebKit для исполнения в веб среде JavaScript функций, определённых в HTML, загруженном в наш компонент Map.

Метод GMap2.panTo(center) изменяет центральную точку карты на заданную. Если заданная точка видна на текущем виде карты, изменяет центральную точку с помощью плавного передвижения.

void Map::loadCoordinates()
{
    QStringList scriptStr;
    scriptStr << QString("map.panTo(new GLatLng(%1, %2));")
                         .arg(coordinates.last().x())
                         .arg(coordinates.last().y());
    page()->mainFrame()->evaluateJavaScript( scriptStr.join("\n") );
}

В HTML файле, загруженном в компонент Map, в качестве контейнера для "карты" (GMap2), указан DOM-елемент с фиксированными значениями.

<body onload="initialize()" onunload="GUnload()" topmargin="3" leftmargin="3">
  <!-- Регистрируем место на веб-странице, для отображения "карты". -->
  <div id="map" style="width: 462px; height: 343px;"></div>
</body>

Переопределим в нашем компоненте событие QWidget::resizeEvent(QResizeEvent *event) так, чтобы размер карты изменялся с изменением размера нашего компонента.

void Map::resizeEvent(QResizeEvent *event)
{
    if(page())
        page()->setViewportSize(event->size());
 
    int x = event->size().width();
    int y = event->size().height();
 
    QStringList scriptStr;
    scriptStr
        << "elem = document.getElementById(\"map\");"
        << QString("elem.style.width = '%1px';").arg(x-6)    /* где 6, это leftmargin*2 */
        << QString("elem.style.height = '%2px';").arg(y-6);  /* где 6, это topmargin*2  */
 
    page()->mainFrame()->evaluateJavaScript( scriptStr.join("\n") );
}

[править] Получение отклика в приложении при взаимодействии с картой

Метод void QWebFrame::addToJavaScriptWindowObject(const QString &name, QObject *object) делает доступным внутри среды исполнения JavaScript методы object объявленные как слоты.

void MainWindow::populateJavaScriptWindowObject()
{
    ui.map->page()->mainFrame()->addToJavaScriptWindowObject("mainWindow", this);
}

Чтобы наш объект остался доступным после загрузки нового URL, мы должны добавлять его в слоте, соединённом с сигналом QWebFrame::javaScriptWindowObjectCleared()

    connect(ui.map->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()),
            this, SLOT(populateJavaScriptWindowObject()));

Для примера, создадим слот, который выводит в текстовое поле, переданное ему сообщение.

void MainWindow::debugMessage(const QString& str)
{
    ui.textEdit->append(str);
}

Обратимся к созданному слоту из функции JavaScript

GEvent.addListener(marker, "click", function() {
  mainWindow.debugMessage("Вы нажали на первый маркер.");
});

Результат виден на рисунке

Файл:Qt+google_map.jpeg

[править] Приложение: index.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
    <title>Google Maps JavaScript API Example: Simple Map</title>
 
    <!--
     Загрузка API Карт Google.
       V - номер используемой версии API карт Google. В данном случае используем вторую версию.
           Можно выбрать версии 2.х или 2.s. 2.х - последняя версия, включающая все новейшие функции,
           однако, может быть нестабильной. Функции версии 2.х переносятся в версию v=2, если не были
           обнаружены ошибки. Версия 2.s - стабильная версия. Обновляется редко, раз в 2-3 месяца,
           и может отстовать по функционалу.
       hl - язык домена.
       GOOGLE_MAPS_KEY - ключ API карт. Нужно подставить собственный ключ.
    -->
    <script src="http://maps.google.com/maps?file=api&v=2&amp&key=GOOGLE_MAPS_KEY&hl=ru"
            type="text/javascript"></script>
 
    <script type="text/javascript">
    var map;
 
    //JavaScript-функция для создания объекта "карта".
    function initialize() {
      if (GBrowserIsCompatible()) {
        /*
	 Создаём новый экземпляр "карты".
         Указываем DOM-узел на странице в качестве контейнера для "карты".
	*/
        map = new GMap2(document.getElementById("map"));
 
	/*
         Устанавливает центр "карты", коэффициент масштаба и тип "карты".
         Этот Метод должен быть вызван первым, сразу после создания "карты", для установки её состояния.
         Вызов других операций после создания "карты" является ошибкой, пока не вызван этот метод.
 
	 Коэффициент масштаба может принимать значения от 0 (самый низкий коэффициент,
         при котором на одном изображении представлена карта мира)
         до 19 (максимальный коэффициент — вплоть до отображения отдельных зданий).
 
         Cписок типов карт, которые поддерживаются в настоящее время:
           G_NORMAL_MAP    — обычная двумерная карта. Используется по умолчанию.
           G_SATELLITE_MAP — фотографическая карта.
           G_HYBRID_MAP    — комбинация фотографий и слоя обычной карты с наиболее важными объектами (дорогами, названиями городов).
           G_PHYSICAL_MAP  — физическая карта с информацией о рельефе местности.
        */        
	map.setCenter(new GLatLng(51.5333, 46.0345), 13);
 
        //Добавляет поведение и элементы управления по умолчанию.
        map.setUIToDefault();
 
        //Запрещает изменять масштаб по двойному нажатию кнопки мыши.
        map.disableDoubleClickZoom();
 
        //Создаём три точки с географическими координатами (широта и долгота).
        var point  = new GLatLng(51.5333, 46.0345);
        var point1 = new GLatLng(51.5318, 46.0304);
        var point2 = new GLatLng(51.5714, 45.9719);
 
	/*
         Создаём три маркера, которые отмечают некоторое положение на "карте".
         Маркер имеет параметр latlng, который указывает на географическое положение места,
         где маркер расположен на "карте", и значок icon. В случае если icon не установлен в конструкторе,
         используется значок по умолчанию — G_DEFAULT_ICON.
	*/
        var marker  = new GMarker(point);
        var marker1 = new GMarker(point1);
        var marker2 = new GMarker(point2);
 
        //Добавляем наши маркеры на "карту" в виде наложенной графики.
        map.addOverlay(marker);
        map.addOverlay(marker1);
        map.addOverlay(marker2);
 
	/*
         При взаимодействии пользователя с браузером с помощью мыши и клавиатуры создаются события,
         распространяющиеся в DOM. Программы, ожидающие определенные события JavaScript,
         регистрируют для них приемники событий и при получении таких событий выполняют тот или иной код.
         API Карт Google дополняет эту модель собственными событиями объектов Карт.
	*/
	/*
         Регистрируем обработчики событий "click" наших маркеров.
         Для подписки на уведомления об этих событиях служит статический метод GEvent.addListener().
         Методу передаются объект, принимаемое событие и вызываемая при наступлении указанного события функция.
	*/
        GEvent.addListener(marker, "click", function() {
          // обращение к объекту добавленного через addToJavaScriptWindowObject
          mainWindow.debugMessage("Вы нажали на первый маркер.");
        });
 
        GEvent.addListener(marker1, "click", function() {
          mainWindow.debugMessage("Вы нажали на второй маркер.");
        });
 
        GEvent.addListener(marker2, "click", function() {
          mainWindow.debugMessage("Вы нажали на третий маркер.");
        });
      }
    }
    </script>
  </head>
 
  <body onload="initialize()" onunload="GUnload()" topmargin="3" leftmargin="3">
    <!-- Регистрируем место на веб-странице, для отображения "карты". -->
    <div id="map" style="width: 462px; height: 343px;"></div>
  </body>
</html>

Исходный код примера можно взять тут.

[править] Используемые ресурсы

  1. Документация API Карт Google
  2. Putting QtWebKit to use with Google Maps
  3. How to show city locations in a map using QWebKit and Google Maps API

[править] Обсуждение

Обсудить на форуме...