summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/src/tutorials/maps.qdoc2147
-rw-r--r--src/location/maps/qgeomappingmanager.cpp26
-rw-r--r--src/location/maps/qgeomappingmanager.h1
-rw-r--r--src/location/maps/qgeomappingmanagerengine.cpp35
-rw-r--r--src/location/maps/qgeomappingmanagerengine.h3
-rw-r--r--src/location/maps/qgeomappingmanagerengine_p.h3
6 files changed, 0 insertions, 2215 deletions
diff --git a/doc/src/tutorials/maps.qdoc b/doc/src/tutorials/maps.qdoc
deleted file mode 100644
index ba2aa0c7..00000000
--- a/doc/src/tutorials/maps.qdoc
+++ /dev/null
@@ -1,2147 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** GNU Free Documentation License
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file.
-**
-** Other Usage
-** Alternatively, this file may be used in accordance with the terms
-** and conditions contained in a signed written agreement between you
-** and Nokia.
-**
-**
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/*!
- \page tutorials-mapsdemo.html
-
-
- \contentspage Maps Demo Tutorial
- \startpage Maps Demo Tutorial
-
- \title Maps Demo Tutorial
-
- \brief An introduction to the Location API, showing how to develop a simple
- map search and navigation application.
-
- In this tutorial, you will learn about the basic components of the Location
- API, including
-
- \list
- \o Drawing and interacting with maps;
- \o Managing map objects;
- \o Search and routing services; and
- \o Receiving position updates.
- \endlist
-
- We will be developing the map search and navigation application shown below:
-
- \image mapsdemo-finished.png
-
- Tutorial contents:
-
- \list 1
- \o \l{Part 1 - The Map Widget}
- \o \l{Part 2 - Searching for locations}
- \o \l{Part 3 - Listening to satellites}
- \o \l{Part 4 - Stopping for directions}
- \o \l{Part 5 - Tuning for mobile devices}
- \endlist
-*/
-
-/*!
- \page tutorials-mapsdemo-part1.html
-
- \previouspage {Maps Demo Tutorial}
- \contentspage {Maps Demo Tutorial} {Contents}
- \nextpage Part 2 - Searching for locations
- \startpage Maps Demo Tutorial
-
- \title Part 1 - The Map Widget
-
- To begin with, we will start defining the map widget, which is the central part of
- the application's user interface. Enough of the map widget will be defined here to
- work satisfactorily on most desktop platforms -- full consideration for mobile use
- will be made later along with other parts of the application.
-
- \section2 The very basics
-
- The Location module provides the QGraphicsGeoMap which is a simple, easy way to insert
- maps into a QGraphicsScene. Since we're going to be extending the map later, we'll
- create a subclass of QGraphicsGeoMap called \c GeoMap, as below:
-
- \code
-class GeoMap : public QGraphicsGeoMap
-{
- Q_OBJECT
-
-public:
- GeoMap(QGeoMappingManager *manager, MapsWidget *mapsWidget);
- ~GeoMap();
-
-private:
- MapsWidget *mapsWidget;
-};
-
-GeoMap::GeoMap(QGeoMappingManager *manager, MapsWidget *mapsWidget) :
- QGraphicsGeoMap(manager), mapsWidget(mapsWidget)
-{
-}
- \endcode
-
- And next we define a QWidget subclass, MapsWidget, which handles the creation of
- QGraphicsView and QGraphicsScene to put the GeoMap into. We make use of the Pimpl
- idiom on this class, since (as we will see) it will grow later to have a large
- complement of private data members, and some of these have naming conflicts with
- public methods.
-
- \code
-class MapsWidgetPrivate;
-class MapsWidget : public QWidget
-{
- Q_OBJECT
-
-public:
- MapsWidget(QWidget *parent = 0);
- ~MapsWidget();
-
-public slots:
- void initialize(QGeoMappingManager *manager);
-
-private:
- MapsWidgetPrivate *d;
-};
- \endcode
-
- We perform the creation of the QGraphicsScene and GeoMap in the initialize() method:
-
- \code
-class MapsWidgetPrivate
-{
-public:
- GeoMap *map;
- QGraphicsView *view;
-};
-
-void MapsWidget::initialize(QGeoMappingManager *manager)
-{
- d->map = new GeoMap(manager, this);
-
- QGraphicsScene *sc = new QGraphicsScene;
- sc->addItem(d->map);
-
- d->map->resize(300, 480);
-
- d->view = new QGraphicsView(sc, this);
- d->view->setVisible(true);
- d->view->setInteractive(true);
-
- d->map->setCenter(QGeoCoordinate(-27.5796, 153.1));
- d->map->setZoomLevel(15);
-}
- \endcode
-
- Doing this in the constructor, while possible, is not the preferred approach, as
- the QGeoMappingManager may not be available until the user has chosen it, or until a
- network connection is available. This is especially important in mobile
- environments, as we'll see later.
-
- To get an instance of QGeoMappingManager we use the list of service providers
- available in QGeoServiceProvider::availableServiceProviders(). Service providers
- provide the ability to fetch and draw maps, search for locations, get directions,
- and a variety of other tasks.
-
- To test out the MapsWidget we just wrote, we can simply get the first available
- service provider in the main() function, as follows:
-
- \code
-int main(int argc, char *argv[])
-{
- QApplication a(argc, argv);
-
- MapsWidget w;
- w.show();
-
- QList<QString> providers = QGeoServiceProvider::availableServiceProviders();
- QGeoServiceProvider *serviceProvider = new QGeoServiceProvider(providers[0]);
-
- w.initialize(serviceProvider->mappingManager());
-
- return a.exec();
-}
- \endcode
-
- If you compile and run the code so far, you should see a window appear containing
- a street map of Eight Mile Plains, in Queensland, Australia, rendered by your
- platform's default geo service provider.
-
- \image mapsdemo-verybasic.png
-
- \section2 Pan & zoom
-
- Next we'll add some basic pan and zoom capability to the map widget. Like most other
- classes in Qt, QGraphicsGeoMap allows mouse and keyboard events to be handled by
- private methods.
-
- Into the private section of the GeoMap declaration we add:
- \code
-bool panActive;
-
-void mousePressEvent(QGraphicsSceneMouseEvent *event);
-void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
-void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
- \endcode
-
- And their definitions:
-
- \code
-void GeoMap::mousePressEvent(QGraphicsSceneMouseEvent *event)
-{
- panActive = true;
- event->accept();
-}
-
-void GeoMap::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
-{
- panActive = false;
- event->accept();
-}
-
-void GeoMap::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
-{
- if (panActive) {
- QPointF delta = event->lastPos() - event->pos();
- pan(delta.x(), delta.y());
- }
- event->accept();
-}
- \endcode
-
- These three short methods are enough to add basic panning support to the map.
- The panning method is a simple mouse-locked one, and moving long distances on
- a touch screen with it can get quite tedious. Many map applications now make
- use of "kinetic" panning for a better user experience, especially on touch
- devices, but in the interests of simplicity, we'll save that for other examples.
-
- Next, to add zoom support on the mouse scrollwheel:
-
- \code
-void GeoMap::wheelEvent(QGraphicsSceneWheelEvent *event)
-{
- qreal panx = event->pos().x() - size().width() / 2.0;
- qreal pany = event->pos().y() - size().height() / 2.0;
- pan(panx, pany);
- if (event->delta() > 0) { // zoom in
- if (zoomLevel() < maximumZoomLevel()) {
- setZoomLevel(zoomLevel() + 1);
- }
- } else { // zoom out
- if (zoomLevel() > minimumZoomLevel()) {
- setZoomLevel(zoomLevel() - 1);
- }
- }
- pan(-panx, -pany);
- event->accept();
-}
- \endcode
-
- This method is a little more complicated. To provide a suitable zoom feel, we
- have to actually combine panning with zooming, so that the user's point of
- interest (the mouse cursor) remains in the same part of the view. So, we
- actually pan the mouse cursor's location into the center, then adjust
- the zoom level, then pan back at the end.
-
- \section2 Map icons
-
- Another important basic feature is the ability to render icons on the map to
- represent points of interest. The QGeoMapPixmapObject class provides most of
- the functionality necessary to achieve this, and we'll use a subclass of it
- in similar vein to our GeoMap, above.
-
- For our application, we want to deal with 6 different kinds of icons:
- \list
- \o A "my location" icon
- \o "Search" icons for search results
- \o User waypoints for direction routes
- \o Start points for directions
- \o End points for directions
- \o "Path" markers for individual steps in the direction route
- \endlist
-
- Once again we make use of the Pimpl idiom to separate the private data
- members from the interface:
-
- \code
-class MarkerPrivate;
-class Marker : public QGeoMapPixmapObject
-{
- Q_OBJECT
-public:
- enum MarkerType {
- MyLocationMarker,
- SearchMarker,
- WaypointMarker,
- StartMarker,
- EndMarker,
- PathMarker
- };
-
- explicit Marker(MarkerType type);
-
- inline MarkerType markerType() const { return m_type; }
- void setMarkerType(MarkerType type);
-
-private:
- MarkerPrivate *d;
-
-};
- \endcode
-
- So we can construct Marker instances of different types, but we need QPixmaps
- to represent each one. In our implementation we will simply use a \c switch
- statement to map MarkerTypes to QPixmaps.
-
- \code
-class MarkerPrivate
-{
-public:
- Marker::MarkerType type;
-};
-
-Marker::Marker(MarkerType type) :
- QGeoMapPixmapObject()
-{
- setMarkerType(type);
-}
-
-void Marker::setMarkerType(MarkerType type)
-{
- QString filename;
- QPoint offset;
- int scale;
-
- d->type = type;
-
- switch (d->type) {
- case MyLocationMarker:
- filename = ":/icons/mylocation.png";
- break;
- case SearchMarker:
- filename = ":/icons/searchmarker.png";
- break;
- case WaypointMarker:
- filename = ":/icons/waypointmarker.png";
- break;
- case StartMarker:
- filename = ":/icons/startmarker.png";
- break;
- case EndMarker:
- filename = ":/icons/endmarker.png";
- break;
- case PathMarker:
- filename = ":/icons/pathmarker.png";
- break;
- }
-
- if (d->type == MyLocationMarker) {
- offset = QPoint(-13,-13);
- scale = 25;
- } else {
- offset = QPoint(-15, -36);
- scale = 30;
- }
-
- setOffset(offset);
- setPixmap(QPixmap(filename).scaledToWidth(scale, Qt::SmoothTransformation));
-}
- \endcode
-
- The icon PNG images can be found in the \c examples/mapsdemo/icons
- directory in the QtLocation source tree. All we have to do to have this
- working is simply add the PNG icons to a \c .qrc file and add it to the
- project.
-
- The QGraphicsGeoMap::addMapObject method is used to add markers to a map.
- We can add a call to create a marker at our starting point into
- MapsWidget::initialize() as a demonstration:
-
- \code
-// in MapsWidget::initialize()
-Marker *me = new Marker(Marker::MyLocationMarker);
-me->setCoordinate(QGeoCoordinate(-27.5796, 153.1));
-geoMap->addMapObject(me);
- \endcode
-
- Build and start the application, and we now have a "My Location" icon in
- the centre of the initial view.
-
- This now concludes the basic functionality of the map widget. We'll be
- making a few modifications and improvements to it as we go along, but
- the basic skeleton will remain the same.
-
- Next, we'll add a basic GUI around the map widget, and the ability
- to search for locations like addresses.
-*/
-
-/*!
- \page tutorials-mapsdemo-part2.html
-
- \previouspage Part 1 - The Map Widget
- \contentspage {Maps Demo Tutorial} {Contents}
- \nextpage Part 3 - Listening to satellites
- \startpage Maps Demo Tutorial
-
- \title Part 2 - Searching for locations
-
- Now that we have a basic map widget, we want to add the capability
- to search for addresses and locations and create markers for them
- on the map.
-
- \section2 Search classes
-
- Searching in the Location API is handled by use of the QGeoSearchManager,
- which we obtain in similar fashion to the MappingManager (in main() in
- part 1). As we want to create markers for search results and then
- be able to remove them for the next search (or perhaps other operations),
- we need some way to organise collections of markers.
-
- To do this, we introduce a new class, MarkerManager:
-
- \code
-class MarkerManagerPrivate;
-class MarkerManager : public QObject
-{
- Q_OBJECT
-public:
- explicit MarkerManager(QGeoSearchManager *sm, QObject *parent=0);
- ~MarkerManager();
-
-public slots:
- void setMap(QGraphicsGeoMap *map);
- void setMyLocation(QGeoCoordinate coord);
- void search(QString query);
- void removeSearchMarkers();
-
-signals:
- void searchError(QGeoSearchReply::Error error, QString errorString);
- void searchFinished();
-
-private:
- MarkerManagerPrivate *d;
-
-private slots:
- void replyFinished(QGeoSearchReply *reply);
-};
- \endcode
-
- The MarkerManager tracks both the "My Location" marker and a list of search
- result markers. Implementing the My Location portion is nothing new:
-
- \code
-class MarkerManagerPrivate
-{
-public:
- Marker *myLocation;
- QList<Marker*> searchMarkers;
-
- QGraphicsGeoMap *map;
- QGeoSearchManager *searchManager;
-
- QSet<QGeoSearchReply*> forwardReplies;
-};
-
-MarkerManager::MarkerManager(QGeoSearchManager *searchManager, QObject *parent) :
- QObject(parent),
- d(new MarkerManagerPrivate)
-{
- d->myLocation = new Marker(Marker::MyLocationMarker);
- d->searchManager = searchManager;
-}
-
-MarkerManager::~MarkerManager()
-{
- if (d->map)
- d->map->removeMapObject(m_myLocation);
- delete d->myLocation;
- ...
-}
-
-void MarkerManager::setMap(QGraphicsGeoMap *map)
-{
- ...
- d->map = map;
- d->map->addMapObject(d->myLocation);
- ...
-}
-
-void MarkerManager::setMyLocation(QGeoCoordinate coord)
-{
- d->myLocation->setCoordinate(coord);
-}
- \endcode
-
- To implement searching, we call the QGeoSearchManager::search method, which
- returns a QGeoSearchReply. This reply object emits a signal finished() when
- the search results are available. It can also be constructed already
- finished, and we need to check for this first before connecting the signals.
-
- We make use of the searchManager's version of the \a finished() signal, as
- it gives out the necessary QGeoSearchReply* parameter so that we can have
- one slot to handle both the case where the reply is constructed already
- finished, and the case where the signal fires later.
-
- \code
-MarkerManager::MarkerManager(QGeoSearchManager *searchManager, QObject *parent) :
- ...
-{
- ...
- connect(d->searchManager, SIGNAL(finished(QGeoSearchReply*)),
- this, SLOT(replyFinished(QGeoSearchReply*)));
-}
-
-void MarkerManager::search(QString query)
-{
- QGeoSearchReply *reply = d->searchManager->search(query);
-
- d->forwardReplies.insert(reply);
- if (reply->isFinished()) {
- replyFinished(reply);
- } else {
- connect(reply, SIGNAL(error(QGeoSearchReply::Error,QString)),
- this, SIGNAL(searchError(QGeoSearchReply::Error,QString)));
- }
-}
- \endcode
-
- The QGeoSearchReply yields its results as a list of QGeoPlace instances.
- While these hold quite a bit of information, for now we'll just be using
- them for their coordinates.
-
- \code
-void MarkerManager::replyFinished(QGeoSearchReply *reply)
-{
- if (!d->forwardReplies.contains(reply))
- return;
-
- // generate the markers and add them to the map
- foreach (QGeoPlace place, reply->places()) {
- Marker *m = new Marker(Marker::SearchMarker);
- m->setCoordinate(place.coordinate());
- d->searchMarkers.append(m);
-
- if (d->map) {
- d->map->addMapObject(m);
- // also zoom out until marker is visible
- while (!d->map->viewport().contains(place.coordinate()))
- d->map->setZoomLevel(d->map->zoomLevel()-1);
- }
- }
-
- d->forwardReplies.remove(reply);
- reply->deleteLater();
-
- emit searchFinished();
-}
- \endcode
-
- Next, we add two methods to MapsWidget to keep track of a MarkerManager
- instance associated with its map:
-
- \code
-class MapsWidget : public QWidget
-{
- ...
-public:
- void setMarkerManager(MarkerManager *markerManager);
- MarkerManager *markerManager() const;
-
- ...
-};
-
-class MapsWidgetPrivate
-{
-public:
- MarkerManager *markerManager;
- ...
-};
- \endcode
-
- And then add two small sections of code to connect them together:
-
- \code
-void MapsWidget::initialize(QGeoMappingManager *manager)
-{
- d->map = new GeoMap(manager, this);
- if (d->markerManager)
- d->markerManager->setMap(d->map);
- ...
-}
-
-void MapsWidget::setMarkerManager(MarkerManager *markerManager)
-{
- d->markerManager = markerManager;
- if (d->map)
- d->markerManager->setMap(d->map);
-}
- \endcode
-
- Now we have basic search capability added to our code. But we still
- have no GUI to drive it, and so we'll focus on that in the next section.
-
- \section2 GUI with search dialog
-
- Next we'll build a GUI around our map widget and add a search dialog to
- make use of the code we just wrote. Our finished GUI looks like this:
-
- \image mapsdemo-searchgui.png
-
- We won't cover building the GUI in too much detail (that being the
- subject of other tutorials), but the complete code is in the finished
- MapsDemo example in the QtLocation part of the Qt distribution.
-
- Our GUI consists of a QMainWindow containing our MapsWidget and a QMenuBar.
- On the QMenuBar is an option for zooming to the current "My Location", and
- a menu for performing search operations.
-
- Also part of the GUI is the dialog box displayed when selecting "Search
- for address or name" -- this is a simple QDialog subclass with a QFormLayout
- and a QDialogButtonBox.
-
- In the MainWindow constructor, we simply set up the menubar and MapsWidget
- and other UI details. All initialization of Location-based details are in
- the MainWindow::initialize() slot. For the moment, we will simply assume
- that initialize() is called directly from the constructor (the purpose of
- this decoupling will be explained later).
-
- \code
-void MainWindow::initialize()
-{
- if (serviceProvider)
- delete serviceProvider;
-
- QList<QString> providers = QGeoServiceProvider::availableServiceProviders();
- if (providers.size() < 1) {
- QMessageBox::information(this, tr("Maps Demo"),
- tr("No service providers are available"));
- QCoreApplication::quit();
- return;
- }
-
- serviceProvider = new QGeoServiceProvider(providers[0]);
- if (serviceProvider->error() != QGeoServiceProvider::NoError) {
- QMessageBox::information(this, tr("Maps Demo"),
- tr("Error loading geoservice plugin: %1").arg(providers[0]));
- QCoreApplication::quit();
- return;
- }
-
- mapsWidget->initialize(serviceProvider->mappingManager());
- markerManager = new MarkerManager(serviceProvider->searchManager());
- mapsWidget->setMarkerManager(markerManager);
-
- connect(markerManager, SIGNAL(searchError(QGeoSearchReply::Error,QString)),
- this, SLOT(showErrorMessage(QGeoSearchReply::Error,QString)));
-
- mapsWidget->setMyLocation(QGeoCoordinate(-27.5796, 153.1));
-}
- \endcode
-
- As you can see, this performs more or less the same actions as our old
- code in main() from part 1 of the tutorial did. It fetches the first
- available service provider, then initializes the MapsWidget and
- MarkerManager using the appropriate Manager instances.
-
- Additionally, we've added a setMyLocation() method to MapsWidget which
- simply calls the current MarkerManager's method of the same name, plus
- centreing the view on the marker.
-
- The "Search for address or name" menu item sets off the showSearchDialog()
- slot:
-
- \code
-void MainWindow::showSearchDialog()
-{
- SearchDialog sd;
- if (sd.exec() == QDialog::Accepted) {
- if (markerManager) {
- markerManager->removeSearchMarkers();
- markerManager->search(sd.searchTerms());
- }
- }
-}
- \endcode
-
- Which uses the methods on MarkerManager that we defined previously. So now
- we have a basic searchable mapping application. However, there is one big
- piece of functionality missing for a searchable map: consider if we had
- a provider that allowed us to search for local businesses. We might type
- in a business name in the Search dialog and press OK, and then be presented
- with tens or hundreds of businesses that match the name we typed from all
- around the world. Some of these results might not even be the kind of
- business we were looking for (partial text matches etc).
-
- This can be solved with the addition of two key features: viewing the extra
- details about search results that we're currently throwing away; and adding
- the ability to limit the search area.
-
- \section2 Adding details to search markers
-
- First up, we'll add some additional properties to the Marker class:
-
- \code
-class Marker : public QGeoMapPixmapObject
-{
- ....
-public:
- QString name() const;
- void setName(QString name);
-
- QGeoAddress address() const;
- void setAddress(QGeoAddress addr);
-
- bool moveable() const;
- void setMoveable(bool moveable);
- ...
-};
-
-class MarkerPrivate
-{
-public:
- ...
- QString name;
- bool moveable;
- QGeoAddress address;
-};
- \endcode
-
- And add code to MarkerManager to set them from search results:
-
- \code
-void MarkerManager::replyFinished(QGeoSearchReply *reply)
-{
- ...
- foreach (QGeoPlace place, reply->places()) {
- Marker *m = new Marker(Marker::SearchMarker);
-
- m->setCoordinate(place.coordinate());
- if (place.isLandmark()) {
- QLandmark lm(place);
- m->setName(lm.name());
- } else {
- m->setName(QString("%1, %2").arg(place.address().street())
- .arg(place.address().city()));
- }
- m->setAddress(place.address());
- m->setMoveable(false);
- ...
- \endcode
-
- So now the data is available from the Marker objects. We want to show
- this to the user somehow, though, and the best means of doing this is
- probably a dialog box. We're going to do a dialog box that appears when
- the user clicks a marker, so we'll have to add click detection to
- MapsWidget and GeoMap, first.
-
- We already have methods for handling mouse presses and releases over
- the map widget, so we will modify these. Add two private fields and a
- signal to GeoMap:
-
- \code
-class GeoMap : public QGraphicsGeoMap
-{
- ...
-signals:
- void clicked(Marker *marker);
-
-private:
- ...
- bool markerPressed;
- QGeoMapObject *pressed;
- ...
-};
- \endcode
-
- We set the \c markerPressed flag when the mouse has been pressed over a
- map object, and set \c pressed to the map object in question.
-
- \code
-void GeoMap::mousePressEvent(QGraphicsSceneMouseEvent *event)
-{
- panActive = true;
-
- markerPressed = false;
- QList<QGeoMapObject*> objects = mapObjectsAtScreenPosition(event->pos());
- if (objects.size() > 0) {
- pressed = objects.first();
- markerPressed = true;
- }
-
- event->accept();
-}
-
-void GeoMap::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
-{
- panActive = false;
-
- if (markerPressed) {
- // check if we're still over the object
- QList<QGeoMapObject*> objects = mapObjectsAtScreenPosition(event->pos());
- if (objects.contains(pressed)) {
- Marker *m = dynamic_cast<Marker*>(pressed);
- if (m)
- emit clicked(m);
- }
-
- markerPressed = false;
- }
-
- event->accept();
-}
- \endcode
-
- Finally, we need to pass this clicked() signal up through MapsWidget so
- that we can use it from outside. We do this by adding a signal and
- connecting GeoMap's signal to the signal on MapsWidget with the same
- name.
-
- \code
-class MapsWidget : public QWidget
-{
- ...
-signals:
- void markerClicked(Marker *m);
- ...
-};
-
-void MapsWidget::initialize(QGeoMappingManager *manager)
-{
- ...
- connect(d->map, SIGNAL(clicked(Marker*)),
- this, SIGNAL(markerClicked(Marker*)));
-}
- \endcode
-
- Now that's done, creating a dialog box to display the address information
- is relatively trivial. The MarkerDialog class contains a QLineEdit for the
- name field, a readonly QLabel for the address, and two QDoubleSpinBoxes
- for latitude and longitude.
-
- We connect up the MapsWidget's markerClicked() signal to a slot in
- MainWindow:
-
- \code
-void MainWindow::showMarkerDialog(Marker *marker)
-{
- MarkerDialog md(marker);
- if (md.exec() == QDialog::Accepted) {
- marker->setName(md.markerName());
- QGeoCoordinate c(md.latitude(), md.longitude());
- marker->setCoordinate(c);
- }
-}
- \endcode
-
- And now clicking on markers on the map yields a simple editing dialog box, so
- our first task is complete.
-
- \section2 Limiting search area
-
- The QGeoSearchManager's search() method already comes with support for
- limited search areas -- by setting up a QGeoBoundingArea we can take
- advantage of this functionality.
-
- Firstly, we'll modify the MarkerManager's search() method:
-
- \code
-// declaration
-void search(QString query, qreal radius=-1);
-
-// implementation
-void MarkerManager::search(QString query, qreal radius)
-{
- QGeoSearchReply *reply;
- if (radius > 0) {
- QGeoBoundingCircle boundingCircle(m_myLocation->coordinate(), radius);
- reply = d->searchManager->search(query,
- QGeoSearchManager::SearchAll,
- -1, 0,
- boundingCircle);
- } else {
- reply = d->searchManager->search(query);
- }
-
- if (reply->isFinished()) {
- ...
- \endcode
-
- And now we need to modify the UI to expose this to the user. There are
- a few ways of doing this, but the option we'll take is to expose a
- QComboBox with some preset distances. This is easier to use on
- touch screen devices, especially, where entering numbers often takes
- much more effort from the user than selecting an option.
-
- \code
-// in SearchDialog constructor
-whereCombo = new QComboBox();
-whereCombo->addItem(tr("Nearby (<10km)"), 10000);
-whereCombo->addItem(tr("Within 30 mins drive of me (<25km)"), 25000);
-whereCombo->addItem(tr("Within 100km of me"), 100000);
-whereCombo->addItem(tr("Anywhere in the world"), -1);
-whereCombo->setCurrentIndex(1);
-formLayout->addRow(tr("Where"), whereCombo);
- \endcode
-
- Then to get the radius value to put into search, we simply take the user
- data from the QComboBox, convert it to a qreal and pass it through.
-
- So we now have a searchable map, with clickable markers and the ability
- to limit the search radius. The last feature we'll cover that relates to
- searching is the so-called "reverse geocode" technique.
-
- \section2 Reverse geocode
-
- Currently, if you click the My Location icon on our map application, a
- blank address is displayed. We can add the capability here to turn the
- current coordinates of the marker into an approximate address, and the
- technique is known as "reverse geocode" searching.
-
- To implement this, we'll hook into the coordinateChanged() signal of
- the Marker object:
-
- \code
-MarkerManager::MarkerManager(QGeoSearchManager *searchManager, QObject *parent) :
- QObject(parent),
- d(new MarkerManagerPrivate)
-{
- d->searchManager = searchManager;
- d->myLocation = new Marker(Marker::MyLocationMarker);
- d->myLocation->setName("Me");
-
- ...
-
- // hook the coordinateChanged() signal for reverse geocoding
- connect(d->myLocation, SIGNAL(coordinateChanged(QGeoCoordinate)),
- this, SLOT(myLocationChanged(QGeoCoordinate)));
-}
- \endcode
-
- Then we perform the reverse lookup in the myLocationChanged() slot. This
- looks quite similar to the original search() method, with good reason, as
- the reverse geocode lookup is simply a special kind of search call.
-
- \code
-void MarkerManager::myLocationChanged(QGeoCoordinate location)
-{
- QGeoSearchReply *reply = d->searchManager->reverseGeocode(location);
-
- d->reverseReplies.insert(reply);
- if (reply->isFinished())
- reverseReplyFinished(reply);
-}
-
-void MarkerManager::reverseReplyFinished(QGeoSearchReply *reply)
-{
- if (!d->reverseReplies.contains(reply))
- return;
-
- if (reply->places().size() > 0) {
- QGeoPlace place = reply->places().first();
- d->myLocation->setAddress(place.address());
- }
-
- reply->deleteLater();
-}
- \endcode
-
- However, this isn't going to work very well with a GPS updating myLocation
- on a regular basis and a slow network connection, as the requests
- will pile up and the geocoded coordinates will lag behind the reported
- ones by quite a margin. A simple scheme to solve this relies only
- on two boolean flags:
-
- \code
-class MarkerManagerPrivate
-{
-public:
- ...
- // a reverse geocode request is currently running
- bool revGeocodeRunning;
- // a request is currently running, and my location has changed
- // since it started (ie, the request is stale)
- bool myLocHasMoved;
-};
-
-void MarkerManager::myLocationChanged(QGeoCoordinate location)
-{
- if (d->revGeocodeRunning) {
- d->myLocHasMoved = true;
- } else {
- QGeoSearchReply *reply = d->searchManager->reverseGeocode(location);
- d->reverseReplies.insert(reply);
-
- d->myLocHasMoved = false;
-
- if (reply->isFinished()) {
- d->revGeocodeRunning = false;
- reverseReplyFinished(reply);
- } else {
- d->revGeocodeRunning = true;
- }
- }
-}
-
-void MarkerManager::reverseReplyFinished(QGeoSearchReply *reply)
-{
- if (!d->reverseReplies.contains(reply))
- return;
-
- // set address, as before
-
- d->revGeocodeRunning = false;
- if (d->myLocHasMoved)
- myLocationChanged(d->myLocation->coordinate());
-
- d->reverseReplies.remove(reply);
- reply->deleteLater();
-}
- \endcode
-
- A reverse geocode request is only sent if the previous one has
- finished -- if it hasn't finished, a flag is set so that the location
- will be refreshed at the conclusion of the previous request. This is
- far from a perfect scheme, but in practise it works quite well.
-
- At the end of part 2 now, we have a searchable map with a simple GUI,
- clickable markers, the ability to limit search radius about our location,
- and reverse geocoding to work out the address of where we are. This is
- already quite a bit of useful functionality, but we will continue to
- extend it further.
-
- In part 3, we will add support for using platform positioning methods such
- as GPS, and in part 4 we will add the ability to fetch directions to a given
- destination. Finally, in part 5 we will cover a number of points about
- means for achieving a better user experience on mobile platforms.
-*/
-
-/*!
- \page tutorials-mapsdemo-part3.html
-
- \previouspage Part 2 - Searching for locations
- \contentspage {Maps Demo Tutorial} {Contents}
- \nextpage Part 4 - Stopping for directions
- \startpage Maps Demo Tutorial
-
- \title Part 3 - Listening to satellites
-
- Another useful part of the Location API is the ability to receive updates
- of the user's present geographic location from methods such as GPS or
- network positioning. We're going to add support to our MapsDemo for
- using these methods to update the "my location" marker we've already
- added in parts 1 and 2 of this tutorial.
-
- But first we need an attractive way to present status messages to the user
- while they are busy looking at the map. We're going to do this using an
- animated translucent rectangle at the bottom of the display.
-
- \section2 Animated status bar
-
- First, set up the map to resize automatically:
-
- \code
-class MapsWidget : public QWidget
-{
- ...
-private:
- void resizeEvent(QResizeEvent *event);
- void showEvent(QShowEvent *event);
-};
-
-void MapsWidget::resizeEvent(QResizeEvent *event)
-{
- if (d->view && d->map) {
- d->view->resize(size());
- d->map->resize(size());
- d->view->centerOn(d->map);
- }
-}
-
-void MapsWidget::showEvent(QShowEvent *event)
-{
- if (d->view && d->map) {
- d->view->resize(size());
- d->map->resize(size());
- d->view->centerOn(d->map);
- }
-}
- \endcode
-
- And now we add our new StatusBarItem class:
-
- \code
-class StatusBarItemPrivate;
-class StatusBarItem : public QObject, public QGraphicsRectItem
-{
- Q_OBJECT
- Q_PROPERTY(int offset READ offset WRITE setOffset)
-
-public:
- StatusBarItem();
- ~StatusBarItem();
-
- int offset() const;
- void setRect(qreal x, qreal y, qreal w, qreal h);
-
-public slots:
- void setText(QString text);
-
- void showText(QString text, quint32 timeout=3000);
- void show();
- void hide();
-
- void setOffset(int offset);
-
-private:
- StatusBarItemPrivate *d;
-};
- \endcode
-
- Note that the order of base classes here is very important: QObject
- and then QGraphicsRectItem. Re-ordering the base classes will cause
- the code not to compile, as QGraphicsRectItem does not have a
- meta-object (for more details consult the documentation in Qt).
-
- The \a offset property here is added so that when we come to animating
- our status bar, we can handle the case where the bar is sliding in
- and the window is being resized simultaneously. If we simply animated
- the \a y property of the GraphicsItem instead we would have difficulty
- handling this case.
-
- Now add a pointer to one of these in MapsWidgetPrivate (and matching
- accessor methods):
-
- \code
-class MapsWidgetPrivate
-{
-public:
- ...
- StatusBarItem *statusBarItem;
-};
- \endcode
-
- And we're ready for the implementation. The constructor is not terribly
- exciting, but sets the defaults for everything:
-
- \code
-class StatusBarItemPrivate
-{
-public:
- int offset;
- QGraphicsSimpleTextItem *textItem;
-};
-
-StatusBarItem::StatusBarItem() :
- d(new StatusBarItemPrivate)
-{
- d->offset = 0;
-
- setPen(QPen(QBrush(), 0));
- setBrush(QBrush(QColor(0,0,0,120)));
-
- d->textItem = new QGraphicsSimpleTextItem(this);
- d->textItem->setBrush(QBrush(Qt::white));
-
- setText("");
-}
- \endcode
-
- The \a setText function, however, is more interesting;
-
- \code
-void StatusBarItem::setText(QString text)
-{
- d->textItem->setText(text);
- QRectF rect = d->textItem->boundingRect();
- QPointF delta = this->rect().center() - rect.center();
- d->textItem->setPos(delta.x(), delta.y());
-}
- \endcode
-
- This re-centers the \a textItem inside its parent (the StatusBarItem)
- every time the text changes.
-
- Also, the \a setRect method is used to update the size and position of
- the status bar:
-
- \code
-void StatusBarItem::setRect(qreal x, qreal y, qreal w, qreal h)
-{
- QGraphicsRectItem::setRect(x, y + d->offset, w, h);
- setText(d->textItem->text());
-}
- \endcode
-
- Here we see the use of the \a offset property for the first time. The
- idea is to call \a setRect to specify a rectangle that is below
- the bottom of the visible area in the QGraphicsView. Then \a offset
- is used to bump the status bar up into the visible area when needed.
-
- Whenever we change the offset we should re-calculate our own \a y
- value using the rect and the offset together:
-
- \code
-void StatusBarItem::setOffset(int offset)
-{
- this->setY(this->y() - d->offset + offset);
- d->offset = offset;
-}
- \endcode
-
- And now finally, the animations:
-
- \code
-void StatusBarItem::show()
-{
- QPropertyAnimation *anim = new QPropertyAnimation(this, "offset");
- anim->setStartValue(0);
- anim->setEndValue(-1 * rect().height());
- anim->setDuration(500);
- anim->start(QAbstractAnimation::DeleteWhenStopped);
-}
-
-void StatusBarItem::hide()
-{
- QPropertyAnimation *anim = new QPropertyAnimation(this, "offset");
- anim->setStartValue(d->offset);
- anim->setEndValue(0);
- anim->setDuration(500);
- anim->start(QAbstractAnimation::DeleteWhenStopped);
-}
- \endcode
-
- You can see here that we simply use QPropertyAnimations on the \a offset
- property we just defined. This produces a nice linear slide in and out
- whenever \a show() or \a hide() are called.
-
- Lastly, one convenience method:
-
- \code
-void StatusBarItem::showText(QString text, quint32 timeout)
-{
- setText(text);
- show();
- QTimer::singleShot(timeout, this, SLOT(hide()));
-}
- \endcode
-
- This lets us more easily display a status message when we only want it
- to appear and disappear soon afterwards.
-
- Then we have only to add this into our MapsWidget:
-
- \code
-void MapsWidget::initialize(QGeoMappingManager *manager)
-{
- QGraphicsScene *sc;
- ...
- d->statusBarItem = new StatusBarItem;
- sc->addItem(d->statusBarItem);
-}
-
-void MapsWidget::resizeEvent(QResizeEvent *event)
-{
- if (d->view && d->map) {
- ...
- d->statusBarItem->setRect(0, height(), width(), 20);
- }
-}
-
-// and similarly in MapsWidget::showEvent()
- \endcode
-
- \section2 Getting GPS data
-
- Now we move on to the focus of this section: GPS data and how to get it.
- The QGeoPositionInfoSource class gives a convenient interface to
- receive position updates. We're going to add one to our MainWindow:
-
- \code
-class MainWindow : public QMainWindow
-{
-private:
- QGeoPositionInfoSource *positionSource;
-
-private slots:
- // slot to receive updates
- void updateMyPosition(QGeoPositionInfo info);
-};
- \endcode
-
- And in \a initialize() we'll set it up. We're just using whatever the
- default position source for the platform happens to be, at an update
- interval of 1000ms, which is plenty for a basic maps application. Once
- set up, we call the source's \a startUpdates() method to begin receiving
- position updates.
-
- \code
-void MainWindow::initialize()
-{
- ...
- if (positionSource)
- delete positionSource;
-
- positionSource = QGeoPositionInfoSource::createDefaultSource(this);
-
- if (!positionSource) {
- mapsWidget->statusBar()->showText("Could not open GPS", 5000);
- mapsWidget->setMyLocation(QGeoCoordinate(-27.5796, 153.1));
- } else {
- positionSource->setUpdateInterval(1000);
- connect(positionSource, SIGNAL(positionUpdated(QGeoPositionInfo)),
- this, SLOT(updateMyPosition(QGeoPositionInfo)));
- positionSource->startUpdates();
- mapsWidget->statusBar()->showText("Opening GPS...");
- }
-}
- \endcode
-
- Here we also make use of the StatusBarItem to display a message when
- we are able or unable to open the QGeoPositionInfoSource.
-
- And then in the slot \a updateMyPosition, we use this to set the
- myLocation marker.
-
- \code
-void MainWindow::updateMyPosition(QGeoPositionInfo info)
-{
- if (mapsWidget) {
- mapsWidget->setMyLocation(info.coordinate());
- }
-}
- \endcode
-
- So, running the code as is, we have a moving marker for "My Location"
- that follows our actual GPS or network-sourced location. If you start
- driving your car with this app running however, you'll quickly notice
- the fact that the viewport does not pan to follow you as you leave
- the map area.
-
- We could simply add a call to \a setCenter() on the map object in the
- \a updateMyPosition slot, but in the interests of prettiness, we are
- going to make a nice smoothly animated transition instead.
-
- \section2 Following and animated panning
-
- First, add a new boolean member variable to MainWindow, called
- \a tracking, to keep track of whether the viewport is currently
- following the My Location marker:
-
- \code
-class MainWindow : public QMainWindow
-{
-private:
- bool tracking;
- ...
-};
- \endcode
-
- Our intended design is that initially, the viewport will be in tracking
- mode. It will continue this way until the view is manually panned by
- the user, at which point tracking will stop. Then, if the user clicks
- the "My Location" menu option to re-center the map, we resume tracking
- once again.
-
- So we will need a way to notify the MainWindow that the user has panned
- the view. Add a new signal \a mapPanned() to MapsWidget, and a
- corresponding signal \a panned() to GeoMap, as we did for \a clicked().
-
- \code
-class MapsWidget : public QWidget
-{
-signals:
- void mapPanned();
- ...
-};
-
-class GeoMap : public QGraphicsGeoMap
-{
-signals:
- void panned();
- ...
-};
-
-void MapsWidget::initialize(QGeoMappingManager *manager)
-{
- ...
- connect(geoMap, SIGNAL(panned()),
- this, SIGNAL(mapPanned()));
- ...
-}
- \endcode
-
- And now we simply emit it when a user pan takes place:
-
- \code
-void GeoMap::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
-{
- if (panActive) {
- ...
- emit panned();
- }
- ...
-}
- \endcode
-
- Back up in MainWindow, we create a slot \a disableTracking and hook up the
- new signal to it:
-
- \code
-class MainWindow : public QMainWindow
-{
- ...
-private slots:
- ...
- void disableTracking();
- ...
-};
-
-void MainWindow::initialize()
-{
- ...
- connect(mapsWidget, SIGNAL(mapPanned()),
- this, SLOT(disableTracking()));
- ...
-}
- \endcode
-
- And finally in the slot itself we simply set the flag we created earlier:
-
- \code
-void MainWindow::disableTracking()
-{
- tracking = false;
-}
- \endcode
-
- Next we want animated panning to be available. Add a new method on
- MapsWidget:
-
- \code
-class MapsWidget : public QWidget
-{
-public:
- ...
- void animatedPanTo(QGeoCoordinate center);
- ...
-};
- \endcode
-
- To do animations in Qt, it's always easiest if we can make use of a
- QPropertyAnimation, and to do this you need a Q_PROPERTY to act upon.
- We'll use two animations in parallel, one moving latitude and one moving
- longitude, so we need two Q_PROPERTIES:
-
- \code
-class GeoMap : public QGraphicsGeoMap
-{
- Q_OBJECT
-
- Q_PROPERTY(double centerLatitude READ centerLatitude WRITE setCenterLatitude)
- Q_PROPERTY(double centerLongitude READ centerLongitude WRITE setCenterLongitude)
-
-public:
- ...
- double centerLatitude() const;
- void setCenterLatitude(double lat);
- double centerLongitude() const;
- void setCenterLongitude(double lon);
- ...
-};
- \endcode
-
- These functions simply adjust the corresponding value on \a center() and
- then call \a setCenter() with the new \a QGeoCoordinate.
-
- Now we can implement our \a animatedPanTo() method:
-
- \code
-void MapsWidget::animatedPanTo(QGeoCoordinate center)
-{
- if (!d->map)
- return;
-
- QPropertyAnimation *latAnim = new QPropertyAnimation(d->map, "centerLatitude");
- latAnim->setEndValue(center.latitude());
- latAnim->setDuration(200);
- QPropertyAnimation *lonAnim = new QPropertyAnimation(d->map, "centerLongitude");
- lonAnim->setEndValue(center.longitude());
- lonAnim->setDuration(200);
-
- QParallelAnimationGroup *group = new QParallelAnimationGroup;
- group->addAnimation(latAnim);
- group->addAnimation(lonAnim);
- group->start(QAbstractAnimation::DeleteWhenStopped);
-}
- \endcode
-
- To bring it all together, we make the last few changes in MainWindow:
-
- \code
-void MainWindow::goToMyLocation()
-{
- mapsWidget->animatedPanTo(markerManager->myLocation());
- tracking = true;
-}
-
-void MainWindow::updateMyPosition(QGeoPositionInfo info)
-{
- if (mapsWidget) {
- mapsWidget->setMyLocation(info.coordinate());
- if (tracking)
- mapsWidget->animatedPanTo(info.coordinate());
- }
-}
- \endcode
-
- And now we have the simple location tracking functionality we set out to
- implement.
-*/
-
-/*!
- \page tutorials-mapsdemo-part4.html
-
- \previouspage Part 3 - Listening to satellites
- \contentspage {Maps Demo Tutorial} {Contents}
- \nextpage Part 5 - Tuning for mobile devices
- \startpage Maps Demo Tutorial
-
- \title Part 4 - Stopping for directions
-
- To complete our tour of the Maps API, we're going to add some very basic
- support for finding transport routes across a map. There is much more
- functionality available in the routing and navigation API than we are
- going to use, though some backend plugins may place restrictions on its
- use to develop, for example, voice-aided navigation applications (such
- as the Nokia Ovi maps plugin).
-
- \image mapsdemo-routing.png
-
- We are going to add support for a simple dialog that can be used to search
- for a destination point and display a line on the map giving the route from
- the current GPS "My Location" (which we implemented in part 3) to that
- destination.
-
- First, we implement the dialog along similar lines to the SearchDialog we
- created earlier:
-
- \code
-class NavigateDialog : public QDialog
-{
- Q_OBJECT
-public:
- NavigateDialog(QWidget *parent=0);
- ~NavigateDialog();
-
- QString destinationAddress() const;
- QGeoRouteRequest::TravelModes travelMode() const;
-
-private:
- QLineEdit *addressEdit;
- QComboBox *modeCombo;
-};
- \endcode
-
- Once again we make use of a QFormLayout inside the dialog to align the
- widgets together. We have a QLineEdit for the address of the destination,
- and a QComboBox listing possible travel modes.
-
- In MainWindow, we create a new slot for showing the navigate dialog:
-
- \code
-void MainWindow::showNavigateDialog()
-{
- NavigateDialog nd;
- if (nd.exec() == QDialog::Accepted) {
- if (markerManager) {
- // will fill this out later
- }
- }
-}
- \endcode
-
- And we hook it up to a Menu action:
-
- \code
-MainWindow::MainWindow() :
- ...
-{
- ...
- QMenu *navigateMenu = new QMenu("Directions");
- mbar->addMenu(navigateMenu);
-
- navigateMenu->addAction("From here to address", this, SLOT(showNavigateDialog()));
- ....
-}
- \endcode
-
- Now we need a new class to manage routing. Finding a route to an address
- is a two-stage process: first, a geocode search is performed on the
- address to get a lat/lon coordinate. Then this coordinate is used in
- a route request which finally returns the desired route.
-
- Our new class is called Navigator, and includes private slots to handle
- each of these events:
-
- \code
-class Navigator : public QObject
-{
- Q_OBJECT
-public:
- Navigator(QGeoRoutingManager *routingManager, QGeoSearchManager *searchManager,
- MapsWidget *mapsWidget, const QString &address,
- const QGeoRouteRequest &requestTemplate);
- ~Navigator();
-
- void start();
- QGeoRoute route() const;
-
-signals:
- void finished();
- void searchError(QGeoSearchReply::Error error, QString errorString);
- void routingError(QGeoRouteReply::Error error, QString errorString);
-
-private slots:
- void on_addressSearchFinished();
- void on_routingFinished();
-
-private:
- QString address;
- QGeoRouteRequest request;
-
- QGeoRoutingManager *routingManager;
- QGeoSearchManager *searchManager;
- MapsWidget *mapsWidget;
-
- QGeoSearchReply *addressReply;
- QGeoRouteReply *routeReply;
-
- QGeoMapRouteObject *routeObject;
- Marker *endMarker;
- Marker *startMarker;
-
- QGeoRoute firstRoute;
-};
- \endcode
-
- The intended lifecycle of a Navigator is to be created when the dialog
- is accepted, then \a start() is called to begin the requests. The
- requests will either error out or complete, emitting one of \a finished(),
- \a searchError(), or \a routingError() signals. If the request is
- successful, the Navigator creates the appropriate markers and draws
- the route on the map (using a QGeoMapRouteObject). It then owns these
- map objects and will remove them when deleted.
-
- Now for the Navigator's implementation: first, the \a start() method,
- which begins the process by launching the search request.
-
- A QGeoRouteRequest is specified first and foremost by the points the
- route must pass through (the \a waypoints). In our case we only
- wish two have two waypoints, the user's starting location, and the
- destination. We add the first of these in \a start() and the second
- after the search request returns.
-
- \code
-void Navigator::start()
-{
- QList<QGeoCoordinate> waypoints = request.waypoints();
- waypoints.append(mapsWidget->markerManager()->myLocation());
- request.setWaypoints(waypoints);
-
- startMarker = new Marker(Marker::StartMarker);
- startMarker->setCoordinate(mapsWidget->markerManager()->myLocation());
- startMarker->setName("Start point");
- mapsWidget->map()->addMapObject(startMarker);
-
- addressReply = searchManager->search(address);
- if (addressReply->isFinished()) {
- on_addressSearchFinished();
- } else {
- connect(addressReply, SIGNAL(error(QGeoSearchReply::Error,QString)),
- this, SIGNAL(searchError(QGeoSearchReply::Error,QString)));
- connect(addressReply, SIGNAL(finished()),
- this, SLOT(on_addressSearchFinished()));
- }
-}
- \endcode
-
- After the request finishes, the \a on_addressSearchFinished() slot will
- be invoked, which finishes off the routing request and sends it in a
- similar fashion:
-
- \code
-void Navigator::on_addressSearchFinished()
-{
- if (addressReply->places().size() <= 0) {
- addressReply->deleteLater();
- return;
- }
-
- QGeoPlace place = addressReply->places().at(0);
-
- QList<QGeoCoordinate> waypoints = request.waypoints();
- waypoints.append(place.coordinate());
- request.setWaypoints(waypoints);
-
- routeReply = routingManager->calculateRoute(request);
- if (routeReply->isFinished()) {
- on_routingFinished();
- } else {
- connect(routeReply, SIGNAL(error(QGeoRouteReply::Error,QString)),
- this, SIGNAL(routingError(QGeoRouteReply::Error,QString)));
- connect(routeReply, SIGNAL(finished()),
- this, SLOT(on_routingFinished()));
- }
-
- endMarker = new Marker(Marker::EndMarker);
- endMarker->setCoordinate(place.coordinate());
- endMarker->setAddress(place.address());
- endMarker->setName("Destination");
- mapsWidget->map()->addMapObject(endMarker);
-
- addressReply->deleteLater();
-}
- \endcode
-
- And then finally, when the routing request returns we can create
- the route object on the map and emit \a finished():
-
- \code
-void Navigator::on_routingFinished()
-{
- if (routeReply->routes().size() <= 0) {
- emit routingError(QGeoRouteReply::NoError, "No valid routes returned");
- routeReply->deleteLater();
- return;
- }
-
- QGeoRoute route = routeReply->routes().at(0);
- firstRoute = route;
-
- routeObject = new QGeoMapRouteObject;
- routeObject->setRoute(route);
- routeObject->setPen(QPen(Qt::blue, 2.0));
-
- mapsWidget->map()->addMapObject(routeObject);
-
- emit finished();
- routeReply->deleteLater();
-}
- \endcode
-
- Now in MainWindow we have to create a new Navigator instance after
- the dialog returns. We store the Navigator instance in a member
- variable so that we can delete the last one in order to remove
- its map objects before the new one is constructed:
-
- \code
-class MainWindow : public QMainWindow
-{
-private:
- Navigator *lastNavigator;
- ...
-};
-
-void MainWindow::showNavigateDialog()
-{
- NavigateDialog nd;
- if (nd.exec() == QDialog::Accepted) {
- if (markerManager) {
- QGeoRouteRequest req;
-
- req.setTravelModes(nd.travelMode());
-
- if (lastNavigator)
- lastNavigator->deleteLater();
-
- Navigator *nvg = new Navigator(serviceProvider->routingManager(),
- serviceProvider->searchManager(),
- mapsWidget, nd.destinationAddress(),
- req);
-
- lastNavigator = nvg;
-
- connect(nvg, SIGNAL(searchError(QGeoSearchReply::Error,QString)),
- this, SLOT(showErrorMessage(QGeoSearchReply::Error,QString)));
- connect(nvg, SIGNAL(routingError(QGeoRouteReply::Error,QString)),
- this, SLOT(showErrorMessage(QGeoRouteReply::Error,QString)));
-
- mapsWidget->statusBar()->setText("Routing...");
- mapsWidget->statusBar()->show();
-
- nvg->start();
-
- connect(nvg, SIGNAL(finished()),
- mapsWidget->statusBar(), SLOT(hide()));
- }
- }
-}
- \endcode
-
- And now we have basic support for calculating and displaying
- routes on the map. In addition to this, we could quite easily
- use the QGeoRoute object to show a list of directions and
- overall statistics about the journey. For more information see
- the documentation about QGeoRoute.
-
- In the final part of this tutorial, we will optimise the maps
- demo so far for mobile platforms in order to deploy it to a
- phone.
-*/
-
-/*!
- \page tutorials-mapsdemo-part5.html
-
- \previouspage Part 4 - Stopping for directions
- \contentspage {Maps Demo Tutorial} {Contents}
- \startpage Maps Demo Tutorial
-
- \title Part 5 - Tuning for mobile devices
-
- So far in this tutorial we've been mainly aiming at desktop use
- of the application. If you attempted to build it as is for
- a mobile platform you would quite quickly notice a number of
- issues that prevent it from being really usable, and we will
- now address these one by one.
-
- \section2 Network connection management
-
- Most mobile platforms have multiple network connections, which
- are not connected all the time, and change regularly in
- operation. Qt provides the Bearer API for managing these
- and the events that occur with them.
-
- At present when our application starts it simply assumes that
- a network link is available and that a mapping plugin will
- load. This could quite easily not be the case, so we will use
- the Bearer API to verify the state of the network and ensure
- that it is running before we call \a initialize().
-
- First up, we need a QNetworkConfigurationManager in order to
- get at the default configuration of our environment:
-
- \code
-class MainWindow : public QMainWindow
-{
- ...
-private:
- QNetworkConfigurationManager *netConfigManager;
-};
- \endcode
-
- We create it in the constructor of \a MainWindow. As mentioned
- in the documentation for QNetworkConfigurationManager, we should
- make a call to \a updateConfigurations() before actually making
- use of the instance. So we'll also need a slot to be called
- when this completes (we name this \a openNetworkSession()).
-
- \code
-MainWindow::MainWindow() :
- ...
-{
- ...
- netConfigManager = new QNetworkConfigurationManager;
- connect(netConfigManager, SIGNAL(updateCompleted()),
- this, SLOT(openNetworkSession()));
- netConfigManager->updateConfigurations();
-}
- \endcode
-
- And in the slot itself we use the \a defaultConfiguration() method
- as the parameter to construct a new QNetworkSession to represent
- our default connection to the network.
-
- We first check to see if this session is open, and if so, call
- initialize() right away. Otherwise, we hook up an appropriate
- signal and wait for the network to be available.
-
- \code
-void MainWindow::openNetworkSession()
-{
- session = new QNetworkSession(netConfigManager->defaultConfiguration());
- if (session->isOpen()) {
- initialize();
- } else {
- connect(session, SIGNAL(opened()),
- this, SLOT(initialize()));
- session->open();
- }
-}
- \endcode
-
- So now our \a initialize() method will be called once a network
- connection is available.
-
- \section2 Zoom buttons and panning keys
-
- In our current implementation we depend upon the presence of a mouse
- wheel in order to zoom in and out on the map. This is not terribly
- useful in environments that lack a mouse (ie, anything except a desktop
- or laptop computer). To address this, we will implement a simple pair
- of zoom buttons on the right-hand side of the map display.
-
- We also currently assume that panning the map using a mouse or touch
- screen is possible, which is not the case on many embedded devices.
- To rectify this, we will add support for handling arrow key events
- in \a GeoMap.
-
- First up, our zoom buttons. We're going to use a very similar setup
- to that which we used for the sliding status bar previously, and
- create a new subclass of QGraphicsRectItem:
-
- \code
-class ZoomButtonItemPrivate;
-class ZoomButtonItem : public QGraphicsRectItem
-{
-public:
- explicit ZoomButtonItem(GeoMap *map);
-
- void setRect(qreal x, qreal y, qreal w, qreal h);
-
-private:
- ZoomButtonItemPrivate *d;
-
- bool isTopHalf(const QPointF &point);
- bool isBottomHalf(const QPointF &point);
-
-protected:
- void mousePressEvent(QGraphicsSceneMouseEvent *event);
- void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
-};
- \endcode
-
- Our button is going to simply be a translucent rectangle, with the top
- half containing a "+" symbol, which zooms in when clicked, and the
- bottom half containing a "-" symbol, which zooms out. In the constructor
- we create the two text items:
-
- \code
-class ZoomButtonItemPrivate
-{
-public:
- GeoMap *map;
-
- QGraphicsSimpleTextItem *plusText;
- QGraphicsSimpleTextItem *minusText;
-
- bool pressedOverTopHalf;
- bool pressedOverBottomHalf;
-};
-
-ZoomButtonItem::ZoomButtonItem(GeoMap *map) :
- d(new ZoomButtonItemPrivate)
-{
- d->map = map;
- d->pressedOverBottomHalf = false;
- d->pressedOverTopHalf = false;
-
- setPen(QPen(QBrush(), 0));
- setBrush(QBrush(QColor(0,0,0,150)));
-
- d->plusText = new QGraphicsSimpleTextItem(this);
- d->plusText->setText("+");
- d->plusText->setBrush(QBrush(Qt::white));
-
- d->minusText = new QGraphicsSimpleTextItem(this);
- d->minusText->setText("-");
- d->minusText->setBrush(QBrush(Qt::white));
-}
- \endcode
-
- And in \a setRect() we manage sizing and aligning the text items so
- that they each occupy roughly half the space.
-
- \code
-void ZoomButtonItem::setRect(qreal x, qreal y, qreal w, qreal h)
-{
- QGraphicsRectItem::setRect(x, y, w, h);
-
- QFont f;
- f.setFixedPitch(true);
- f.setPixelSize(h/3.0);
- d->plusText->setFont(f);
- d->minusText->setFont(f);
-
- QRectF plusBound = d->plusText->boundingRect();
- QPointF plusCenter(x+w/2.0, y+h/4.0);
- QPointF plusDelta = plusCenter - plusBound.center();
- d->plusText->setPos(plusDelta);
-
- QRectF minusBound = d->minusText->boundingRect();
- QPointF minusCenter(x+w/2.0, y+3.0*h/4.0);
- QPointF minusDelta = minusCenter - minusBound.center();
- d->minusText->setPos(minusDelta);
-}
- \endcode
-
- Finally, we use the boolean flags in ZoomButtonItemPrivate, above, to
- manage click detection in the \a mousePressEvent and \a mouseReleaseEvent
- functions:
-
- \code
-void ZoomButtonItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
-{
- const QPointF pos = event->pos();
- if (!d->pressedOverTopHalf && !d->pressedOverBottomHalf) {
- if (isTopHalf(pos)) {
- d->pressedOverTopHalf = true;
- } else if (isBottomHalf(pos)) {
- d->pressedOverBottomHalf = true;
- }
- }
- event->accept();
-}
-
-void ZoomButtonItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
-{
- const QPointF pos = event->pos();
- if (isTopHalf(pos) && d->pressedOverTopHalf) {
- d->map->setZoomLevel(d->map->zoomLevel() + 1.0);
- } else if (isBottomHalf(pos) && d->pressedOverBottomHalf) {
- d->map->setZoomLevel(d->map->zoomLevel() - 1.0);
- }
- d->pressedOverBottomHalf = false;
- d->pressedOverTopHalf = false;
- event->accept();
-}
- \endcode
-
- In this way, if the mouse (or finger for touch screens) is pressed
- and then released over the same half of the ZoomButtonItem, we
- perform the zoom action appropriately. We could have simply hooked
- the corresponding events on the children items, \a plusText and
- \a minusText, but as they occupy less space and their size/shape vary
- depending on the default font, users may find it difficult to target
- the active portion of the button (especially in a touch environment).
-
- Adding the new button item to the MapsWidget also happens similarly
- to before:
-
- \code
-void MapsWidget::initialize(QGeoMappingManager *manager)
-{
- ...
- d->zoomButtonItem = new ZoomButtonItem(d->map);
- sc->addItem(d->zoomButtonItem);
-
- resizeEvent(0);
- ...
-}
-
-void MapsWidget::resizeEvent(QResizeEvent *event)
-{
- if (d->view && d->map) {
- ...
- d->zoomButtonItem->setRect(width()-30, height()/2.0 - 35, 25, 70);
- }
-}
- \endcode
-
- And now we can zoom in and out properly on touch devices. Next we'll
- address the need to pan and zoom on devices with neither touch
- nor mouse, which we can do through handling key events.
-
- To do this we override the \a keyPressEvent() method on \a GeoMap:
-
- \code
-void GeoMap::keyPressEvent(QKeyEvent *event)
-{
- QGeoCoordinate center;
- QPropertyAnimation *anim;
- const qreal width = size().width();
- const qreal height = size().height();
-
- switch (event->key()) {
- case Qt::Key_4:
- case Qt::Key_Left:
- center = screenPositionToCoordinate(
- QPointF(width/2 - width/5, height/2));
- anim = new QPropertyAnimation(this, "centerLongitude");
- anim->setEndValue(center.longitude());
- anim->setDuration(200);
- anim->start(QAbstractAnimation::DeleteWhenStopped);
- break;
- case Qt::Key_6:
- case Qt::Key_Right:
- center = screenPositionToCoordinate(
- QPointF(width/2 + width/5, height/2));
- anim = new QPropertyAnimation(this, "centerLongitude");
- anim->setEndValue(center.longitude());
- anim->setDuration(200);
- anim->start(QAbstractAnimation::DeleteWhenStopped);
- break;
- case Qt::Key_2:
- case Qt::Key_Up:
- center = screenPositionToCoordinate(
- QPointF(width/2, height/2 - height/5));
- anim = new QPropertyAnimation(this, "centerLatitude");
- anim->setEndValue(center.latitude());
- anim->setDuration(200);
- anim->start(QAbstractAnimation::DeleteWhenStopped);
- break;
- case Qt::Key_8:
- case Qt::Key_Down:
- center = screenPositionToCoordinate(
- QPointF(width/2, height/2 + height/5));
- anim = new QPropertyAnimation(this, "centerLatitude");
- anim->setEndValue(center.latitude());
- anim->setDuration(200);
- anim->start(QAbstractAnimation::DeleteWhenStopped);
- break;
- case Qt::Key_1:
- if (zoomLevel() > minimumZoomLevel()) {
- setZoomLevel(zoomLevel() - 1);
- }
- break;
- case Qt::Key_3:
- if (zoomLevel() < maximumZoomLevel()) {
- setZoomLevel(zoomLevel() + 1);
- }
- break;
- }
- this->setFocus();
- event->accept();
-}
- \endcode
-
- We allow both the arrow keys (which map to the sides of the D-pad
- on some devices), and the numbers 2, 8, 6 and 4 to pan the map,
- which some users may find more comfortable.
-
- In addition, the 1 and 3 keys allow zooming in and out. This key
- mapping is very similar to that used by the majority of maps
- applications on Symbian, and should be familiar to most users.
-
- \section2 Conclusion
-
- In summary, in this tutorial we have built a simple maps and
- navigation application from scratch using the Qt Location API.
-
- We first built the basic maps widget, then added a UI and search
- capability, followed by basic routing and some tuning for use
- on mobile platforms.
-
- The full code as at the end of the tutorial is available in the
- QtLocation examples, named "mapsdemo".
-*/
diff --git a/src/location/maps/qgeomappingmanager.cpp b/src/location/maps/qgeomappingmanager.cpp
index 3a318fbc..90aff85d 100644
--- a/src/location/maps/qgeomappingmanager.cpp
+++ b/src/location/maps/qgeomappingmanager.cpp
@@ -65,24 +65,6 @@ QT_BEGIN_NAMESPACE
\brief The QGeoMappingManager class provides support for displaying
and interacting with maps.
-
- A QGeoMappingManager instance can create QGeoMapData instances with
- createMapData(). The QGeoMapData instances can be used to contain and
- manage information concerning what a particular QGraphicsGeoMap is viewing.
-
- The functions in this class will typically not be used by clients of this
- API, as the most common uses will only need to obtain a QGeoMappingManager
- instance and associate it with a QGraphicsGeoMap instance:
- \code
- QGeoServiceProvider serviceProvider("nokia");
- QGeoMappingManager *manager = serviceProvider.mappingManager();
- QGraphicsGeoMap *geoMap = new QGraphicsGeoMap(manager);
- \endcode
-
- This could have been simplified by having the plugin return a
- QGraphicsGeoMap instance instead, but this approach allows users to
- subclass QGraphicsGeoMap in order to override the standard event handlers
- and implement custom map behaviours.
*/
/*!
@@ -310,14 +292,6 @@ QList<QGeoMapType> QGeoMappingManager::supportedMapTypes() const
return d_ptr->engine->supportedMapTypes();
}
-///*!
-// Returns a list of the connectivity modes supported by this manager.
-//*/
-//QList<QGraphicsGeoMap::ConnectivityMode> QGeoMappingManager::supportedConnectivityModes() const
-//{
-// return d_ptr->engine->supportedConnectivityModes();
-//}
-
/*!
Returns the length of the edge of the tiles returned by this manager.
diff --git a/src/location/maps/qgeomappingmanager.h b/src/location/maps/qgeomappingmanager.h
index ce9e8c60..fe6f74b3 100644
--- a/src/location/maps/qgeomappingmanager.h
+++ b/src/location/maps/qgeomappingmanager.h
@@ -90,7 +90,6 @@ public:
const QSet<QGeoTileSpec> &tilesRemoved);
QList<QGeoMapType> supportedMapTypes() const;
- // QList<QGraphicsGeoMap::ConnectivityMode> supportedConnectivityModes() const;
int tileSize() const;
diff --git a/src/location/maps/qgeomappingmanagerengine.cpp b/src/location/maps/qgeomappingmanagerengine.cpp
index 91518446..d424c8e2 100644
--- a/src/location/maps/qgeomappingmanagerengine.cpp
+++ b/src/location/maps/qgeomappingmanagerengine.cpp
@@ -58,16 +58,6 @@ QT_BEGIN_NAMESPACE
\brief The QGeoMappingManagerEngine class provides an interface and convenience methods
to implementors of QGeoServiceProvider plugins who want to provide support for displaying
and interacting with maps.
-
- Subclasses of QGeoMappingManagerEngine need to provide an implementations
- of createMapData(). The QGeoMapData instances returned by createMapData()
- can be used to contain and manage information concerning what a particular
- QGraphicsGeoMap is viewing.
-
- Most of the other functions configure the reported capabilities of the engine.
- It is important that these functions are called before createMapData() or any of the
- capability reporting functions are used to prevent incorrect or
- inconsistent behaviour.
*/
/*!
@@ -325,31 +315,6 @@ void QGeoMappingManagerEngine::setSupportedMapTypes(const QList<QGeoMapType> &su
d->supportedMapTypes = supportedMapTypes;
}
-///*!
-// Returns a list of the connectivity modes supported by this engine.
-//*/
-//QList<QGraphicsGeoMap::ConnectivityMode> QGeoMappingManagerEngine::supportedConnectivityModes() const
-//{
-// Q_D(const QGeoMappingManagerEngine);
-// return d->supportedConnectivityModes;
-//}
-
-///*!
-// Sets the list of connectivity modes supported by this engine to \a connectivityModes.
-
-// Subclasses of QGeoMappingManagerEngine should use this function to ensure
-// that supportedConnectivityModes() provides accurate information.
-
-// If createMapData does not specify a connectivity mode the first mode from
-// \a connectivityModes will be used, or QGraphicsGeoMap::NoConnectivity will
-// be used if \a connectivityModes is empty.
-//*/
-//void QGeoMappingManagerEngine::setSupportedConnectivityModes(const QList<QGraphicsGeoMap::ConnectivityMode> &connectivityModes)
-//{
-// Q_D(QGeoMappingManagerEngine);
-// d->supportedConnectivityModes = connectivityModes;
-//}
-
void QGeoMappingManagerEngine::setTileSize(int tileSize)
{
Q_D(QGeoMappingManagerEngine);
diff --git a/src/location/maps/qgeomappingmanagerengine.h b/src/location/maps/qgeomappingmanagerengine.h
index 43cc4928..71cd526a 100644
--- a/src/location/maps/qgeomappingmanagerengine.h
+++ b/src/location/maps/qgeomappingmanagerengine.h
@@ -60,7 +60,6 @@ class QLocale;
class QGeoBoundingBox;
class QGeoCameraCapabilities;
class QGeoCoordinate;
-class QGeoMapData;
class QGeoMappingManagerPrivate;
class QGeoMapRequestOptions;
@@ -83,7 +82,6 @@ public:
int managerVersion() const;
QList<QGeoMapType> supportedMapTypes() const;
-// QList<QGraphicsGeoMap::ConnectivityMode> supportedConnectivityModes() const;
int tileSize() const;
@@ -116,7 +114,6 @@ protected:
QGeoMappingManagerEngine(QGeoMappingManagerEnginePrivate *dd, QObject *parent = 0);
void setSupportedMapTypes(const QList<QGeoMapType> &supportedMapTypes);
-// void setSupportedConnectivityModes(const QList<QGraphicsGeoMap::ConnectivityMode> &connectivityModes);
void setTileSize(int tileSize);
diff --git a/src/location/maps/qgeomappingmanagerengine_p.h b/src/location/maps/qgeomappingmanagerengine_p.h
index 2de9db6f..d3c3f98e 100644
--- a/src/location/maps/qgeomappingmanagerengine_p.h
+++ b/src/location/maps/qgeomappingmanagerengine_p.h
@@ -53,8 +53,6 @@
// We mean it.
//
-//#include "qgraphicsgeomap.h"
-
#include <QSize>
#include <QList>
#include <QMap>
@@ -81,7 +79,6 @@ public:
int managerVersion;
QList<QGeoMapType> supportedMapTypes;
-// QList<QGraphicsGeoMap::ConnectivityMode> supportedConnectivityModes;
int tileSize;
QGeoCameraCapabilities cameraCapabilities_;
QGeoMappingManager::CacheAreas cacheHint;