QML Geohelper Plugin
hamishwillee
(Talk | contribs) m (Text replace - "<code cpp>" to "<code cpp-qt>") |
|||
| (22 intermediate revisions by 4 users not shown) | |||
| Line 1: | Line 1: | ||
| − | [[Category:Qt Mobility]][[Category:Qt Quick]] | + | [[Category:Qt]][[Category:Qt Mobility]][[Category:Qt Quick]][[Category:Symbian]] |
| − | + | {{ArticleMetaData <!-- v1.2 --> | |
| − | + | |sourcecode= <!-- Link to example source code (e.g. [[Media:The Code Example ZIP.zip]]) --> | |
| − | {{ | + | |installfile= <!-- Link to installation file (e.g. [[Media:The Installation File.sis]]) --> |
| − | | | + | |devices= N8, E7, C7 |
| − | | | + | |sdk= <!-- SDK(s) built and tested against (e.g. [http://linktosdkdownload/ Nokia Qt SDK 1.1]) --> |
| − | |devices=... | + | |platform= Symbian 3 |
| − | | | + | |devicecompatability= <!-- Compatible devices (e.g.: All* (must have GPS) ) --> |
| − | | | + | |dependencies= <!-- Any other/external dependencies e.g.: Google Maps Api v1.0 --> |
| − | | | + | |signing= <!-- Empty or one of Self-Signed, DevCert, Manufacturer --> |
| − | |keywords=... | + | |capabilities= <!-- Capabilities required by the article/code example (e.g. Location, NetworkServices. --> |
| + | |keywords= QML, Geocoding, Location, Maps, Map objects, JSON stringify | ||
| + | |language= <!-- Language category code for non-English topics - e.g. Lang-Chinese --> | ||
| + | |translated-by= <!-- [[User:XXXX]] --> | ||
| + | |translated-from-title= <!-- Title only --> | ||
| + | |translated-from-id= <!-- Id of translated revision --> | ||
| + | |review-by= <!-- After re-review: [[User:username]] --> | ||
| + | |review-timestamp= <!-- After re-review: YYYYMMDD --> | ||
| + | |update-by= <!-- After significant update: [[User:username]]--> | ||
| + | |update-timestamp= <!-- After significant update: YYYYMMDD --> | ||
| + | |creationdate= 29.04.2011 | ||
| + | |author= [[User:Jahartik]] | ||
| + | <!-- The following are not in current metadata --> | ||
| + | |subcategory= Location | ||
}} | }} | ||
| Line 15: | Line 28: | ||
| − | |||
==Overview== | ==Overview== | ||
| − | Geohelper | + | {{Abstract|Geohelper plug-in to expose Qt mobility location geoservice APIs to QML environment}} |
| + | |||
| + | Geohelper source code includes also workaround to manage several map objects: Map object “copy” to construct the map's objects list again. This is because the objects list does not have a method to remove a single object, see method {{Icode|void GeoHelper::removeFromMap(QString id)}} | ||
This snippet can be self-signed. | This snippet can be self-signed. | ||
| Line 25: | Line 39: | ||
==Preconditions== | ==Preconditions== | ||
| − | Qt 4.7.1 or newer and Qt mobility 1.1.0 or newer installed | + | [http://qt.nokia.com/downloads Qt SDK ]4.7.1 or newer and [http://qt.nokia.com/products/qt-addons/mobility Qt mobility ]1.1.0 or newer installed |
| + | |||
| − | ==Pro file | + | ==Pro file == |
The following capabilities and libraries are required: | The following capabilities and libraries are required: | ||
| + | <code cpp-qt> | ||
CAPABILITY: | CAPABILITY: | ||
symbian:TARGET.CAPABILITY = NetworkServices Location ReadUserData WriteUserData | symbian:TARGET.CAPABILITY = NetworkServices Location ReadUserData WriteUserData | ||
| + | CONFIG += mobility | ||
| + | MOBILITY = location | ||
| + | </code> | ||
==Header file== | ==Header file== | ||
| − | geohelper.h applying this macro to definitions of member functions to allow them to be invoked via the meta-object system | + | '''geohelper.h''' applying this macro to definitions of member functions to allow them to be invoked via the''' meta-object system''' |
| − | <code> | + | <code cpp-qt> |
#ifndef GEOHELPER_H | #ifndef GEOHELPER_H | ||
| Line 131: | Line 150: | ||
==Source file== | ==Source file== | ||
| − | + | geohelper source code | |
| + | |||
| + | <code cpp-qt> | ||
| + | #include "geohelper.h" | ||
| + | |||
| + | #include <QScriptEngine> | ||
| + | #include <QScriptValue> | ||
| + | #include <QScriptValueIterator> | ||
| + | |||
| + | #include <QDeclarativeListReference> | ||
| + | #include <QGeoMapRouteObject> | ||
| + | |||
| + | |||
| + | GeoHelper::GeoHelper(QObject *parent) : | ||
| + | QObject(parent) | ||
| + | { | ||
| + | provider = new QGeoServiceProvider("nokia"); | ||
| + | mappingManager = provider->mappingManager(); | ||
| + | searchManager = provider->searchManager(); | ||
| + | routingManager = provider->routingManager(); | ||
| + | mapitem = NULL; | ||
| + | |||
| + | QObject::connect(searchManager, SIGNAL(error(QGeoSearchReply *, QGeoSearchReply::Error, QString)), this, SLOT(searchErrorSlot(QGeoSearchReply *, QGeoSearchReply::Error, QString))); | ||
| + | QObject::connect(searchManager, SIGNAL(finished(QGeoSearchReply*)), this, SLOT(searchFinishedSlot(QGeoSearchReply*))); | ||
| + | |||
| + | QObject::connect(routingManager, SIGNAL(error(QGeoRouteReply*, QGeoRouteReply::Error, QString)), this, SLOT(routingErrorSlot(QGeoRouteReply*, QGeoRouteReply::Error, QString))); | ||
| + | QObject::connect(routingManager, SIGNAL(finished(QGeoRouteReply*)), this, SLOT(routingFinishedSlot(QGeoRouteReply*))); | ||
| + | } | ||
| + | |||
| + | GeoHelper::~GeoHelper() | ||
| + | { | ||
| + | clearMap(); | ||
| + | |||
| + | if (provider) | ||
| + | { | ||
| + | delete provider; | ||
| + | provider = NULL; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | |||
| + | void GeoHelper::searchErrorSlot(QGeoSearchReply *reply, QGeoSearchReply::Error error, QString errorString) | ||
| + | { | ||
| + | emit searchError(errorString); | ||
| + | } | ||
| + | |||
| + | void GeoHelper::searchFinishedSlot(QGeoSearchReply *reply) | ||
| + | { | ||
| + | if (reply->error() == QGeoSearchReply::NoError) | ||
| + | { | ||
| + | QScriptEngine scriptEngine; | ||
| + | QScriptValue replyObject = scriptEngine.newArray(); | ||
| + | |||
| + | QList<QGeoPlace> places = reply->places(); | ||
| + | for (int i = 0; i < places.count(); i++) | ||
| + | { | ||
| + | QScriptValue placeObject = scriptEngine.newObject(); | ||
| + | |||
| + | QScriptValue coordinateObject = scriptEngine.newObject(); | ||
| + | QGeoCoordinate coordinate = places[i].coordinate(); | ||
| + | coordinateObject.setProperty("latitude", QScriptValue(coordinate.latitude())); | ||
| + | coordinateObject.setProperty("longitude", QScriptValue(coordinate.longitude())); | ||
| + | placeObject.setProperty("coordinate", coordinateObject); | ||
| + | |||
| + | QScriptValue addressObject = scriptEngine.newObject(); | ||
| + | QGeoAddress address = places[i].address(); | ||
| + | |||
| + | if (!address.isEmpty()) | ||
| + | { | ||
| + | addressObject.setProperty("country", address.country()); | ||
| + | addressObject.setProperty("countryCode", address.countryCode()); | ||
| + | addressObject.setProperty("state", address.state()); | ||
| + | addressObject.setProperty("county", address.county()); | ||
| + | addressObject.setProperty("city", address.city()); | ||
| + | addressObject.setProperty("district", address.district()); | ||
| + | addressObject.setProperty("street", address.street()); | ||
| + | addressObject.setProperty("postcode", address.postcode()); | ||
| + | |||
| + | } | ||
| + | |||
| + | placeObject.setProperty("address", addressObject); | ||
| + | replyObject.setProperty(i, placeObject); | ||
| + | } | ||
| + | |||
| + | |||
| + | QScriptValue fun = scriptEngine.evaluate("(function(a) { return JSON.stringify(a); })"); | ||
| + | QScriptValueList args; | ||
| + | args << replyObject; | ||
| + | QScriptValue result = fun.call(QScriptValue(), args); | ||
| + | |||
| + | emit searchReply(result.toString()); | ||
| + | } | ||
| + | |||
| + | |||
| + | } | ||
| + | |||
| + | void GeoHelper::routingErrorSlot(QGeoRouteReply *reply, QGeoRouteReply::Error error, QString errorString) | ||
| + | { | ||
| + | emit routingError(errorString); | ||
| + | } | ||
| + | |||
| + | |||
| + | void GeoHelper::routingFinishedSlot(QGeoRouteReply * reply) | ||
| + | { | ||
| + | |||
| + | if (reply->error() == QGeoRouteReply::NoError) | ||
| + | { | ||
| + | QScriptEngine scriptEngine; | ||
| + | QScriptValue replyObject = scriptEngine.newArray(); | ||
| + | |||
| + | QList<QGeoCoordinate> waypoints = reply->request().waypoints(); | ||
| + | double lat1 = 0; | ||
| + | double lon1 = 0; | ||
| + | double lat2 = 0; | ||
| + | double lon2 = 0; | ||
| + | |||
| + | if (waypoints.count() > 0) | ||
| + | { | ||
| + | /* | ||
| + | QString msg = QString("lat %1, lon %2 => lat %3, lon %4"). | ||
| + | arg(waypoints.at(0).latitude()).arg(waypoints.at(0).longitude()). | ||
| + | arg(waypoints.at((waypoints.count()-1)).latitude()).arg(waypoints.at((waypoints.count()-1)).longitude()); | ||
| + | emit routingError(msg); | ||
| + | */ | ||
| + | lat1 = waypoints.at(0).latitude(); | ||
| + | lon1 = waypoints.at(0).longitude(); | ||
| + | lat2 = waypoints.at((waypoints.count()-1)).latitude(); | ||
| + | lon2 = waypoints.at((waypoints.count()-1)).longitude(); | ||
| + | |||
| + | } | ||
| + | |||
| + | |||
| + | for (int i = 0; i < reply->routes().size(); ++i) | ||
| + | { | ||
| + | QScriptValue routeObject = scriptEngine.newObject(); | ||
| + | QGeoRoute route = reply->routes().at(i); | ||
| + | |||
| + | routeObject.setProperty("distance", QScriptValue(route.distance())); | ||
| + | routeObject.setProperty("travelTime", QScriptValue(route.travelTime())); | ||
| + | routeObject.setProperty("lat1", QScriptValue(lat1)); | ||
| + | routeObject.setProperty("lon1", QScriptValue(lon1)); | ||
| + | routeObject.setProperty("lat2", QScriptValue(lat2)); | ||
| + | routeObject.setProperty("lon2", QScriptValue(lon2)); | ||
| + | |||
| + | |||
| + | QScriptValue pathObject = scriptEngine.newArray(); | ||
| + | QList<QGeoCoordinate> path = route.path(); | ||
| + | for (int p = 0; p < path.length(); p++) | ||
| + | { | ||
| + | QScriptValue coordinateObject = scriptEngine.newObject(); | ||
| + | coordinateObject.setProperty("latitude", QScriptValue(path[p].latitude())); | ||
| + | coordinateObject.setProperty("longitude", QScriptValue(path[p].longitude())); | ||
| + | pathObject.setProperty(p, coordinateObject); | ||
| + | |||
| + | } | ||
| + | |||
| + | routeObject.setProperty("path", pathObject); | ||
| + | |||
| + | replyObject.setProperty(i, routeObject); | ||
| + | |||
| + | } | ||
| + | |||
| + | QScriptValue fun = scriptEngine.evaluate("(function(a) { return JSON.stringify(a); })"); | ||
| + | QScriptValueList args; | ||
| + | args << replyObject; | ||
| + | QScriptValue result = fun.call(QScriptValue(), args); | ||
| + | |||
| + | emit routingReply(result.toString()); | ||
| + | |||
| + | } | ||
| + | } | ||
| + | |||
| + | // ------------- Q_INVOKABLE METHODS ------- | ||
| + | |||
| + | void GeoHelper::removeFromMap(QString id) | ||
| + | { | ||
| + | if (mapobjects.contains(id)) | ||
| + | { | ||
| + | |||
| + | QGeoMapObject *obj = mapobjects.take(id); | ||
| + | if (obj != NULL) | ||
| + | { | ||
| + | delete obj; | ||
| + | obj = NULL; | ||
| + | } | ||
| + | |||
| + | // Now we have to construct the map's objects list again | ||
| + | // this is because the objects list does not have a method | ||
| + | // to remove a single object. | ||
| + | for (int i = 0; i < listRef.count(); i++) | ||
| + | listRef.at(i)->deleteLater(); | ||
| + | |||
| + | listRef.clear(); | ||
| + | |||
| + | QStringList keys = mapobjects.keys(); | ||
| + | foreach (QString id, keys) | ||
| + | { | ||
| + | emit debugMsg("lisaa uudestaan " + id); | ||
| + | QGeoMapObject *obj = mapobjects.value(id); | ||
| + | if (obj != NULL) | ||
| + | { | ||
| + | if (obj->type() == QGeoMapObject::PolylineType) | ||
| + | { | ||
| + | QGeoMapPolylineObject *newobj = new QGeoMapPolylineObject; | ||
| + | newobj->setPath(((QGeoMapPolylineObject *)obj)->path()); | ||
| + | newobj->setPen(QPen(QBrush(Qt::blue), 4)); | ||
| + | newobj->setObjectName(obj->objectName()); | ||
| + | |||
| + | listRef.append(newobj); | ||
| + | } | ||
| + | else if (obj->type() == QGeoMapObject::PixmapType) | ||
| + | { | ||
| + | QGeoMapPixmapObject *newobj = new QGeoMapPixmapObject; | ||
| + | newobj->setCoordinate(((QGeoMapPixmapObject *)obj)->coordinate()); | ||
| + | newobj->setPixmap(((QGeoMapPixmapObject *)obj)->pixmap()); | ||
| + | newobj->setOffset(QPoint(-10,-34)); | ||
| + | newobj->setObjectName(obj->objectName()); | ||
| + | |||
| + | listRef.append(newobj); | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | void GeoHelper::clearMap() | ||
| + | { | ||
| + | |||
| + | QStringList keys = mapobjects.keys(); | ||
| + | foreach (QString id, keys) | ||
| + | { | ||
| + | QGeoMapObject *obj = mapobjects.take(id); | ||
| + | if (obj != NULL) | ||
| + | { | ||
| + | delete obj; | ||
| + | obj = NULL; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | mapobjects.clear(); | ||
| + | |||
| + | for (int i = 0; i < listRef.count(); i++) | ||
| + | listRef.at(i)->deleteLater(); | ||
| + | |||
| + | listRef.clear(); | ||
| + | } | ||
| + | |||
| + | void GeoHelper::drawPolyline(QString id, QString coordinateArr) | ||
| + | { | ||
| + | /* | ||
| + | [ | ||
| + | {"latitude":61.4735985,"longitude":23.7550697}, | ||
| + | {"latitude":61.4735985,"longitude":23.7550697} | ||
| + | ] | ||
| + | |||
| + | */ | ||
| + | |||
| + | |||
| + | if (mapitem != NULL) | ||
| + | { | ||
| + | removeFromMap(id); | ||
| + | |||
| + | QScriptValue sc; | ||
| + | QScriptEngine engine; | ||
| + | sc = engine.evaluate("(" + QString(coordinateArr) + ")"); | ||
| + | |||
| + | if (sc.isArray()) | ||
| + | { | ||
| + | |||
| + | QScriptValueIterator it(sc); | ||
| + | QList<QGeoCoordinate> coordinates; | ||
| + | |||
| + | while (it.hasNext()) | ||
| + | { | ||
| + | it.next(); | ||
| + | if (it.value().property("latitude").toString() != "" && it.value().property("longitude").toString() != "") | ||
| + | { | ||
| + | coordinates << QGeoCoordinate(it.value().property("latitude").toNumber(),it.value().property("longitude").toNumber()); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | QGeoMapPolylineObject *obj = new QGeoMapPolylineObject; | ||
| + | obj->setPath(coordinates); | ||
| + | obj->setPen(QPen(QBrush(Qt::blue), 4)); | ||
| + | obj->setObjectName(id); | ||
| + | |||
| + | listRef.append(obj); | ||
| + | |||
| + | // keep a copy to construct the map objects list again | ||
| + | QGeoMapPolylineObject *copyobj = new QGeoMapPolylineObject; | ||
| + | copyobj->setPath(coordinates); | ||
| + | copyobj->setPen(QPen(QBrush(Qt::blue), 4)); | ||
| + | copyobj->setObjectName(id); | ||
| + | mapobjects.insert(id,copyobj); | ||
| + | |||
| + | |||
| + | |||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | void GeoHelper::drawImage(QString id, double latitude, double longitude, QString imagepath, int xOffset, int yOffset) | ||
| + | { | ||
| + | |||
| + | if (mapitem != NULL) | ||
| + | { | ||
| + | |||
| + | //emit debugMsg(QString("offset: %1, %2").arg(xOffset).arg(yOffset)); | ||
| + | |||
| + | removeFromMap(id); | ||
| + | QGeoMapPixmapObject *obj = new QGeoMapPixmapObject; | ||
| + | obj->setCoordinate(QGeoCoordinate(latitude,longitude)); | ||
| + | obj->setPixmap(imagepath); | ||
| + | obj->setOffset(QPoint(xOffset,yOffset)); | ||
| + | //obj->setOffset(QPoint(-10,-34)); | ||
| + | obj->setObjectName(id); | ||
| + | |||
| + | listRef.append(obj); | ||
| + | |||
| + | // keep a copy to construct the map objects list again | ||
| + | QGeoMapPixmapObject *copyobj = new QGeoMapPixmapObject; | ||
| + | copyobj->setCoordinate(QGeoCoordinate(latitude,longitude)); | ||
| + | copyobj->setPixmap(imagepath); | ||
| + | copyobj->setOffset(QPoint(xOffset,yOffset)); | ||
| + | //copyobj->setOffset(QPoint(-10,-34)); | ||
| + | copyobj->setObjectName(id); | ||
| + | mapobjects.insert(id,copyobj); | ||
| + | |||
| + | |||
| + | } | ||
| + | |||
| + | } | ||
| + | |||
| + | |||
| + | |||
| + | QBrush usebrush(Qt::darkRed); | ||
| + | QPen usepen(usebrush,1); | ||
| + | QFont usefont("Terminal", 12); | ||
| + | |||
| + | |||
| + | |||
| + | void GeoHelper::drawText(QString id, double latitude, double longitude, QString text) | ||
| + | { | ||
| + | |||
| + | if (mapitem != NULL) | ||
| + | { | ||
| + | |||
| + | //emit debugMsg(QString("offset: %1, %2").arg(xOffset).arg(yOffset)); | ||
| + | |||
| + | removeFromMap(id); | ||
| + | QGeoMapTextObject *obj = new QGeoMapTextObject; | ||
| + | obj->setCoordinate(QGeoCoordinate(latitude,longitude)); | ||
| + | obj->setText(text); | ||
| + | obj->setFont(usefont); | ||
| + | obj->setOffset(QPoint(20,-10)); | ||
| + | obj->setAlignment(Qt::AlignLeft | Qt::AlignBottom); | ||
| + | obj->setBrush(usebrush); | ||
| + | obj->setPen(usepen); | ||
| + | obj->setObjectName(id); | ||
| + | |||
| + | listRef.append(obj); | ||
| + | |||
| + | // keep a copy to construct the map objects list again | ||
| + | QGeoMapTextObject *copyobj = new QGeoMapTextObject; | ||
| + | copyobj->setCoordinate(QGeoCoordinate(latitude,longitude)); | ||
| + | copyobj->setText(text); | ||
| + | copyobj->setFont(usefont); | ||
| + | copyobj->setOffset(QPoint(20,-10)); | ||
| + | copyobj->setAlignment(Qt::AlignLeft | Qt::AlignBottom); | ||
| + | copyobj->setBrush(usebrush); | ||
| + | copyobj->setPen(usepen); | ||
| + | copyobj->setObjectName(id); | ||
| + | mapobjects.insert(id,copyobj); | ||
| + | |||
| + | } | ||
| + | |||
| + | } | ||
| + | |||
| + | void GeoHelper::findObjectsInCoordinates(double latitude, double longitude) | ||
| + | { | ||
| + | QGeoCoordinate coord(latitude, longitude); | ||
| + | |||
| + | for (int i = 0; i < listRef.count(); i++) | ||
| + | { | ||
| + | if ( ((QGeoMapObject *)listRef.at(i))->contains(coord)) | ||
| + | { | ||
| + | emit geomapobjectSelected(listRef.at(i)->objectName(), true); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | } | ||
| + | |||
| + | void GeoHelper::findRoute(double fromLatitude, double fromLongitude, double toLatitude, double toLongitude) | ||
| + | { | ||
| + | QGeoRouteRequest *geoRouteRequest = new QGeoRouteRequest(QGeoCoordinate(fromLatitude, fromLongitude), QGeoCoordinate(toLatitude, toLongitude)); | ||
| + | routingManager->calculateRoute(*geoRouteRequest); | ||
| + | } | ||
| + | |||
| + | void GeoHelper::findAddress(double latitude, double longitude) | ||
| + | { | ||
| + | QGeoCoordinate location(latitude,longitude); | ||
| + | searchManager->reverseGeocode(location); | ||
| + | } | ||
| + | |||
| + | void GeoHelper::findCoordinates(QString street, QString city, QString country) | ||
| + | { | ||
| + | /* | ||
| + | NOTE! This geocode method is not capable to handle the number of the street address. | ||
| + | But the use of the plain search alternative seems to work. | ||
| + | QGeoAddress address; | ||
| + | address.setStreet(street); | ||
| + | address.setCity(city); | ||
| + | address.setCountry(country); | ||
| + | searchManager->geocode(address); | ||
| + | */ | ||
| + | QString str = QString("%1,%2,%3").arg(street).arg(city).arg(country); | ||
| + | searchManager->search(str, QGeoSearchManager::SearchGeocode); | ||
| + | } | ||
| − | + | </code> | |
| − | + | To register the C++ type in the QML system with the name GeoHelper include following code in your main.cpp file | |
| + | <code cpp-qt> | ||
| + | qmlRegisterType<GeoHelper>("GeoHelper",1,0,"GeoHelper"); | ||
| + | </code> | ||
| − | + | Import geohelper plugin in QML | |
| + | <code cpp-qt> | ||
| + | import GeoHelper 1.0 | ||
| + | </code> | ||
| − | + | Example use QML | |
| + | <code cpp-qt> | ||
| + | GeoHelper { | ||
| + | id: geohelper… | ||
| + | ---- | ||
| + | Geohelper developer APIs | ||
| + | Constructor and destructor | ||
| + | GeoHelper(QObject *parent = 0); | ||
| + | ~GeoHelper(); | ||
| − | + | Public slots: | |
| + | void findRoute(double fromLatitude, double fromLongitude, double toLatitude, double toLongitude); | ||
| + | void findAddress(double latitude, double longitude); | ||
| + | void findCoordinates(QString street, QString city, QString country = QString("FINLAND")); | ||
| + | void clearMap(); | ||
| + | void removeFromMap(QString id); | ||
| + | void drawPolyline(QString id, QString coordinateArr); | ||
| + | void drawImage(QString id, double latitude, double longitude, QString imagepath, int xOffset, int yOffset); | ||
| + | void drawText(QString id, double latitude, double longitude, QString text); | ||
| + | void findObjectsInCoordinates(double latitude, double longitude); | ||
| − | + | Public signals | |
| + | void searchError(const QString &error); | ||
| + | void routingError(const QString &error); | ||
| + | void searchReply(const QString &reply); | ||
| + | void routingReply(const QString &reply); | ||
| + | void geomapobjectSelected(QString id, bool selected); | ||
| + | void debugMsg(const QString &reply); | ||
| − | + | </code> | |
Latest revision as of 04:18, 11 October 2012
Article Metadata
Tested with
Compatibility
Article
Contents |
Overview
Geohelper plug-in to expose Qt mobility location geoservice APIs to QML environment
Geohelper source code includes also workaround to manage several map objects: Map object “copy” to construct the map's objects list again. This is because the objects list does not have a method to remove a single object, see method void GeoHelper::removeFromMap(QString id)
This snippet can be self-signed.
Preconditions
Qt SDK 4.7.1 or newer and Qt mobility 1.1.0 or newer installed
Pro file
The following capabilities and libraries are required:
CAPABILITY:
symbian:TARGET.CAPABILITY = NetworkServices Location ReadUserData WriteUserData
CONFIG += mobility
MOBILITY = location
Header file
geohelper.h applying this macro to definitions of member functions to allow them to be invoked via the meta-object system
#ifndef GEOHELPER_H
#define GEOHELPER_H
#include <QObject>
#include <QMap>
#include <QGeoServiceProvider>
#include <QGeoMappingManager>
#include <QGeoSearchManager>
#include <QGeoRoutingManager>
#include <QDeclarativeEngine>
#include <QGeoRouteReply>
#include <QGeoRouteRequest>
#include <QGeoCoordinate>
#include <QDeclarativeItem>
#include <QGeoMapPolylineObject>
#include <QGeoMapPixmapObject>
#include <QGeoMapTextObject>
QTM_USE_NAMESPACE
class GeoHelper : public QObject
{
Q_OBJECT
Q_PROPERTY(QDeclarativeItem* map READ map WRITE setMap)
public:
explicit GeoHelper(QObject *parent = 0);
~GeoHelper();
QDeclarativeItem *map() const {return mapitem; }
void setMap(QDeclarativeItem *map) { mapitem = map; listRef = QDeclarativeListReference(mapitem, "objects");};
Q_INVOKABLE void findRoute(double fromLatitude, double fromLongitude, double toLatitude, double toLongitude);
Q_INVOKABLE void findAddress(double latitude, double longitude);
Q_INVOKABLE void findCoordinates(QString street, QString city, QString country = QString("FINLAND"));
Q_INVOKABLE void clearMap();
Q_INVOKABLE void removeFromMap(QString id);
Q_INVOKABLE void drawPolyline(QString id, QString coordinateArr);
Q_INVOKABLE void drawImage(QString id, double latitude, double longitude, QString imagepath, int xOffset, int yOffset);
Q_INVOKABLE void drawText(QString id, double latitude, double longitude, QString text);
Q_INVOKABLE void findObjectsInCoordinates(double latitude, double longitude);
signals:
void searchError(const QString &error);
void routingError(const QString &error);
void searchReply(const QString &reply);
void routingReply(const QString &reply);
void geomapobjectSelected(QString id, bool selected);
void debugMsg(const QString &reply);
private slots:
void searchErrorSlot(QGeoSearchReply *reply, QGeoSearchReply::Error error, QString errorString = QString());
void searchFinishedSlot(QGeoSearchReply *reply);
void routingErrorSlot(QGeoRouteReply *reply, QGeoRouteReply::Error error, QString errorString);
void routingFinishedSlot(QGeoRouteReply * reply);
private:
QGeoServiceProvider* provider;
QGeoMappingManager* mappingManager;
QGeoSearchManager* searchManager;
QGeoRoutingManager* routingManager;
QDeclarativeContext* context;
QDeclarativeItem* mapitem;
QMap<QString, QGeoMapObject *> mapobjects;
QDeclarativeListReference listRef;
};
#endif // GEOHELPER_H
Source file
geohelper source code
#include "geohelper.h"
#include <QScriptEngine>
#include <QScriptValue>
#include <QScriptValueIterator>
#include <QDeclarativeListReference>
#include <QGeoMapRouteObject>
GeoHelper::GeoHelper(QObject *parent) :
QObject(parent)
{
provider = new QGeoServiceProvider("nokia");
mappingManager = provider->mappingManager();
searchManager = provider->searchManager();
routingManager = provider->routingManager();
mapitem = NULL;
QObject::connect(searchManager, SIGNAL(error(QGeoSearchReply *, QGeoSearchReply::Error, QString)), this, SLOT(searchErrorSlot(QGeoSearchReply *, QGeoSearchReply::Error, QString)));
QObject::connect(searchManager, SIGNAL(finished(QGeoSearchReply*)), this, SLOT(searchFinishedSlot(QGeoSearchReply*)));
QObject::connect(routingManager, SIGNAL(error(QGeoRouteReply*, QGeoRouteReply::Error, QString)), this, SLOT(routingErrorSlot(QGeoRouteReply*, QGeoRouteReply::Error, QString)));
QObject::connect(routingManager, SIGNAL(finished(QGeoRouteReply*)), this, SLOT(routingFinishedSlot(QGeoRouteReply*)));
}
GeoHelper::~GeoHelper()
{
clearMap();
if (provider)
{
delete provider;
provider = NULL;
}
}
void GeoHelper::searchErrorSlot(QGeoSearchReply *reply, QGeoSearchReply::Error error, QString errorString)
{
emit searchError(errorString);
}
void GeoHelper::searchFinishedSlot(QGeoSearchReply *reply)
{
if (reply->error() == QGeoSearchReply::NoError)
{
QScriptEngine scriptEngine;
QScriptValue replyObject = scriptEngine.newArray();
QList<QGeoPlace> places = reply->places();
for (int i = 0; i < places.count(); i++)
{
QScriptValue placeObject = scriptEngine.newObject();
QScriptValue coordinateObject = scriptEngine.newObject();
QGeoCoordinate coordinate = places[i].coordinate();
coordinateObject.setProperty("latitude", QScriptValue(coordinate.latitude()));
coordinateObject.setProperty("longitude", QScriptValue(coordinate.longitude()));
placeObject.setProperty("coordinate", coordinateObject);
QScriptValue addressObject = scriptEngine.newObject();
QGeoAddress address = places[i].address();
if (!address.isEmpty())
{
addressObject.setProperty("country", address.country());
addressObject.setProperty("countryCode", address.countryCode());
addressObject.setProperty("state", address.state());
addressObject.setProperty("county", address.county());
addressObject.setProperty("city", address.city());
addressObject.setProperty("district", address.district());
addressObject.setProperty("street", address.street());
addressObject.setProperty("postcode", address.postcode());
}
placeObject.setProperty("address", addressObject);
replyObject.setProperty(i, placeObject);
}
QScriptValue fun = scriptEngine.evaluate("(function(a) { return JSON.stringify(a); })");
QScriptValueList args;
args << replyObject;
QScriptValue result = fun.call(QScriptValue(), args);
emit searchReply(result.toString());
}
}
void GeoHelper::routingErrorSlot(QGeoRouteReply *reply, QGeoRouteReply::Error error, QString errorString)
{
emit routingError(errorString);
}
void GeoHelper::routingFinishedSlot(QGeoRouteReply * reply)
{
if (reply->error() == QGeoRouteReply::NoError)
{
QScriptEngine scriptEngine;
QScriptValue replyObject = scriptEngine.newArray();
QList<QGeoCoordinate> waypoints = reply->request().waypoints();
double lat1 = 0;
double lon1 = 0;
double lat2 = 0;
double lon2 = 0;
if (waypoints.count() > 0)
{
/*
QString msg = QString("lat %1, lon %2 => lat %3, lon %4").
arg(waypoints.at(0).latitude()).arg(waypoints.at(0).longitude()).
arg(waypoints.at((waypoints.count()-1)).latitude()).arg(waypoints.at((waypoints.count()-1)).longitude());
emit routingError(msg);
*/
lat1 = waypoints.at(0).latitude();
lon1 = waypoints.at(0).longitude();
lat2 = waypoints.at((waypoints.count()-1)).latitude();
lon2 = waypoints.at((waypoints.count()-1)).longitude();
}
for (int i = 0; i < reply->routes().size(); ++i)
{
QScriptValue routeObject = scriptEngine.newObject();
QGeoRoute route = reply->routes().at(i);
routeObject.setProperty("distance", QScriptValue(route.distance()));
routeObject.setProperty("travelTime", QScriptValue(route.travelTime()));
routeObject.setProperty("lat1", QScriptValue(lat1));
routeObject.setProperty("lon1", QScriptValue(lon1));
routeObject.setProperty("lat2", QScriptValue(lat2));
routeObject.setProperty("lon2", QScriptValue(lon2));
QScriptValue pathObject = scriptEngine.newArray();
QList<QGeoCoordinate> path = route.path();
for (int p = 0; p < path.length(); p++)
{
QScriptValue coordinateObject = scriptEngine.newObject();
coordinateObject.setProperty("latitude", QScriptValue(path[p].latitude()));
coordinateObject.setProperty("longitude", QScriptValue(path[p].longitude()));
pathObject.setProperty(p, coordinateObject);
}
routeObject.setProperty("path", pathObject);
replyObject.setProperty(i, routeObject);
}
QScriptValue fun = scriptEngine.evaluate("(function(a) { return JSON.stringify(a); })");
QScriptValueList args;
args << replyObject;
QScriptValue result = fun.call(QScriptValue(), args);
emit routingReply(result.toString());
}
}
// ------------- Q_INVOKABLE METHODS -------
void GeoHelper::removeFromMap(QString id)
{
if (mapobjects.contains(id))
{
QGeoMapObject *obj = mapobjects.take(id);
if (obj != NULL)
{
delete obj;
obj = NULL;
}
// Now we have to construct the map's objects list again
// this is because the objects list does not have a method
// to remove a single object.
for (int i = 0; i < listRef.count(); i++)
listRef.at(i)->deleteLater();
listRef.clear();
QStringList keys = mapobjects.keys();
foreach (QString id, keys)
{
emit debugMsg("lisaa uudestaan " + id);
QGeoMapObject *obj = mapobjects.value(id);
if (obj != NULL)
{
if (obj->type() == QGeoMapObject::PolylineType)
{
QGeoMapPolylineObject *newobj = new QGeoMapPolylineObject;
newobj->setPath(((QGeoMapPolylineObject *)obj)->path());
newobj->setPen(QPen(QBrush(Qt::blue), 4));
newobj->setObjectName(obj->objectName());
listRef.append(newobj);
}
else if (obj->type() == QGeoMapObject::PixmapType)
{
QGeoMapPixmapObject *newobj = new QGeoMapPixmapObject;
newobj->setCoordinate(((QGeoMapPixmapObject *)obj)->coordinate());
newobj->setPixmap(((QGeoMapPixmapObject *)obj)->pixmap());
newobj->setOffset(QPoint(-10,-34));
newobj->setObjectName(obj->objectName());
listRef.append(newobj);
}
}
}
}
}
void GeoHelper::clearMap()
{
QStringList keys = mapobjects.keys();
foreach (QString id, keys)
{
QGeoMapObject *obj = mapobjects.take(id);
if (obj != NULL)
{
delete obj;
obj = NULL;
}
}
mapobjects.clear();
for (int i = 0; i < listRef.count(); i++)
listRef.at(i)->deleteLater();
listRef.clear();
}
void GeoHelper::drawPolyline(QString id, QString coordinateArr)
{
/*
[
{"latitude":61.4735985,"longitude":23.7550697},
{"latitude":61.4735985,"longitude":23.7550697}
]
*/
if (mapitem != NULL)
{
removeFromMap(id);
QScriptValue sc;
QScriptEngine engine;
sc = engine.evaluate("(" + QString(coordinateArr) + ")");
if (sc.isArray())
{
QScriptValueIterator it(sc);
QList<QGeoCoordinate> coordinates;
while (it.hasNext())
{
it.next();
if (it.value().property("latitude").toString() != "" && it.value().property("longitude").toString() != "")
{
coordinates << QGeoCoordinate(it.value().property("latitude").toNumber(),it.value().property("longitude").toNumber());
}
}
QGeoMapPolylineObject *obj = new QGeoMapPolylineObject;
obj->setPath(coordinates);
obj->setPen(QPen(QBrush(Qt::blue), 4));
obj->setObjectName(id);
listRef.append(obj);
// keep a copy to construct the map objects list again
QGeoMapPolylineObject *copyobj = new QGeoMapPolylineObject;
copyobj->setPath(coordinates);
copyobj->setPen(QPen(QBrush(Qt::blue), 4));
copyobj->setObjectName(id);
mapobjects.insert(id,copyobj);
}
}
}
void GeoHelper::drawImage(QString id, double latitude, double longitude, QString imagepath, int xOffset, int yOffset)
{
if (mapitem != NULL)
{
//emit debugMsg(QString("offset: %1, %2").arg(xOffset).arg(yOffset));
removeFromMap(id);
QGeoMapPixmapObject *obj = new QGeoMapPixmapObject;
obj->setCoordinate(QGeoCoordinate(latitude,longitude));
obj->setPixmap(imagepath);
obj->setOffset(QPoint(xOffset,yOffset));
//obj->setOffset(QPoint(-10,-34));
obj->setObjectName(id);
listRef.append(obj);
// keep a copy to construct the map objects list again
QGeoMapPixmapObject *copyobj = new QGeoMapPixmapObject;
copyobj->setCoordinate(QGeoCoordinate(latitude,longitude));
copyobj->setPixmap(imagepath);
copyobj->setOffset(QPoint(xOffset,yOffset));
//copyobj->setOffset(QPoint(-10,-34));
copyobj->setObjectName(id);
mapobjects.insert(id,copyobj);
}
}
QBrush usebrush(Qt::darkRed);
QPen usepen(usebrush,1);
QFont usefont("Terminal", 12);
void GeoHelper::drawText(QString id, double latitude, double longitude, QString text)
{
if (mapitem != NULL)
{
//emit debugMsg(QString("offset: %1, %2").arg(xOffset).arg(yOffset));
removeFromMap(id);
QGeoMapTextObject *obj = new QGeoMapTextObject;
obj->setCoordinate(QGeoCoordinate(latitude,longitude));
obj->setText(text);
obj->setFont(usefont);
obj->setOffset(QPoint(20,-10));
obj->setAlignment(Qt::AlignLeft | Qt::AlignBottom);
obj->setBrush(usebrush);
obj->setPen(usepen);
obj->setObjectName(id);
listRef.append(obj);
// keep a copy to construct the map objects list again
QGeoMapTextObject *copyobj = new QGeoMapTextObject;
copyobj->setCoordinate(QGeoCoordinate(latitude,longitude));
copyobj->setText(text);
copyobj->setFont(usefont);
copyobj->setOffset(QPoint(20,-10));
copyobj->setAlignment(Qt::AlignLeft | Qt::AlignBottom);
copyobj->setBrush(usebrush);
copyobj->setPen(usepen);
copyobj->setObjectName(id);
mapobjects.insert(id,copyobj);
}
}
void GeoHelper::findObjectsInCoordinates(double latitude, double longitude)
{
QGeoCoordinate coord(latitude, longitude);
for (int i = 0; i < listRef.count(); i++)
{
if ( ((QGeoMapObject *)listRef.at(i))->contains(coord))
{
emit geomapobjectSelected(listRef.at(i)->objectName(), true);
}
}
}
void GeoHelper::findRoute(double fromLatitude, double fromLongitude, double toLatitude, double toLongitude)
{
QGeoRouteRequest *geoRouteRequest = new QGeoRouteRequest(QGeoCoordinate(fromLatitude, fromLongitude), QGeoCoordinate(toLatitude, toLongitude));
routingManager->calculateRoute(*geoRouteRequest);
}
void GeoHelper::findAddress(double latitude, double longitude)
{
QGeoCoordinate location(latitude,longitude);
searchManager->reverseGeocode(location);
}
void GeoHelper::findCoordinates(QString street, QString city, QString country)
{
/*
NOTE! This geocode method is not capable to handle the number of the street address.
But the use of the plain search alternative seems to work.
QGeoAddress address;
address.setStreet(street);
address.setCity(city);
address.setCountry(country);
searchManager->geocode(address);
*/
QString str = QString("%1,%2,%3").arg(street).arg(city).arg(country);
searchManager->search(str, QGeoSearchManager::SearchGeocode);
}
To register the C++ type in the QML system with the name GeoHelper include following code in your main.cpp file
qmlRegisterType<GeoHelper>("GeoHelper",1,0,"GeoHelper");
Import geohelper plugin in QML
import GeoHelper 1.0Example use QML
GeoHelper {
id: geohelper…
----
Geohelper developer APIs
Constructor and destructor
GeoHelper(QObject *parent = 0);
~GeoHelper();
Public slots:
void findRoute(double fromLatitude, double fromLongitude, double toLatitude, double toLongitude);
void findAddress(double latitude, double longitude);
void findCoordinates(QString street, QString city, QString country = QString("FINLAND"));
void clearMap();
void removeFromMap(QString id);
void drawPolyline(QString id, QString coordinateArr);
void drawImage(QString id, double latitude, double longitude, QString imagepath, int xOffset, int yOffset);
void drawText(QString id, double latitude, double longitude, QString text);
void findObjectsInCoordinates(double latitude, double longitude);
Public signals
void searchError(const QString &error);
void routingError(const QString &error);
void searchReply(const QString &reply);
void routingReply(const QString &reply);
void geomapobjectSelected(QString id, bool selected);
void debugMsg(const QString &reply);

