How to dynamically add map objects to QML MAP element? Or how to create customised Map widget?
-
@
// map_widget.cpp
#include <QDebug>
#include <QGeoServiceProvider>
#include <QGeoMappingManager>
#include <QGeoCoordinate>
#include <QGeoMapCircleObject>
#include <QGeoMapPixmapObject>
#include <QGeoMapGroupObject>
#include <QGraphicsSceneMouseEvent>
#include <QGeoBoundingBox>
#include <QtCore>#include "map_widget.h"
// A widget for QML, therefore we need the parameter-less constructor.
MapWidget::MapWidget() :
QGraphicsGeoMap(createManager())
{
setCenter(QGeoCoordinate(51.05, 13.73));
setZoomLevel(17);
}MapWidget::~MapWidget()
{
}// This method is a bit a heck. We actually call it before the object
// is completely created. We need to do this, because our parent class
// needs a QGeoMappingManager passed to its constructor.
QGeoMappingManager* MapWidget::createManager()
{
qDebug() << "INFO: Creating mapping manager";
// We have to access this as static member, because we can't get anything
// inside this object from the outside. The reason is, that this method
// is called by the constructor and that we cannot add values to the
// constructor, because QML always needs a parameter-less constructor.
QGeoServiceProvider *serviceProvider = new QGeoServiceProvider("osm", params);
QGeoMappingManager *mappingManager = serviceProvider->mappingManager();
if (mappingManager == 0) {
qDebug() << "WARN: Could not load 'osm' plugin. Falling back to 'nokia' plugin.";
serviceProvider = new QGeoServiceProvider("nokia");
mappingManager = serviceProvider->mappingManager();
}return mappingManager;
}
void MapWidget::addPoi(PoiData *poi, bool active)
{
QGeoCoordinate coord(poi->getLat(), poi->getLon());QPixmap pixmap; if (active) { pixmap = QPixmap(":qml/Common/img/poi_active.png"); } else { pixmap = QPixmap(":qml/Common/img/poi_inactive.png"); } QGeoMapPixmapObject *pixMapObject = new QGeoMapPixmapObject(coord, QPoint(-26,-65), pixmap); pixMapObject->setProperty("uuid", poi->getUuid()); pixMapObject->setObjectName("poiMarker"); QGeoMapGroupObject *poiMarker = new QGeoMapGroupObject(); poiMarker->setProperty("uuid", poi->getUuid()); poiMarker->addChildObject(pixMapObject); if (active) { QGeoMapCircleObject *circle = new QGeoMapCircleObject(coord, poi->getRadius()); circle->setPen(QPen((poiColor))); circle->setBrush(QBrush(poiColor)); circle->setZValue(-1); poiMarker->addChildObject(circle); } addMapObject(poiMarker);
}
void MapWidget::removePoi(PoiData *poi)
{
for (int i = 0; i < poiMarkers.length(); ++i) {
QGeoMapObject *marker = poiMarkers[i];
if (marker->property("uuid").toString() == poi->getUuid()) {
removeMapObject(marker);
return;
}
}
}void MapWidget::clearPois()
{
for (int i = 0; i < poiMarkers.length(); ++i) {
QGeoMapObject *obj = poiMarkers[i];
removeMapObject(obj);
}
}/*
- Remember the position. We use this in mouseReleaseEvent
/
void MapWidget::mousePressEvent(QGraphicsSceneMouseEvent event)
{
lastPos = event->pos();
}
/*
-
If the mouse was moved by a maximum of 30px between press and release
-
we look at the press coordinates and if there is a POI we emit a signal
-
if there are several POIs, we only emit the signal for the first one.
/
void MapWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent event)
{
QPointF newPos = event->pos();
QPointF diff = lastPos - newPos;if (qAbs(diff.x()) > 30 || qAbs(diff.y()) > 30) {
return;
}QList<QGeoMapObject*> objects = mapObjectsAtScreenPosition(lastPos);
if (objects.length() > 0) {
for (int i = 0; i < objects.length(); i++) {
QGeoMapObject *obj = objects[i];
if (obj->objectName() == "poiMarker") {
QString uuid = obj->property("uuid").toString();
emit poiClicked(uuid);
}
}
}
}
void MapWidget::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{
// Round to int
QPoint lastPos = event->lastPos().toPoint();
QPoint pos = event->pos().toPoint();int dx = lastPos.x() - pos.x(); int dy = lastPos.y() - pos.y(); pan(dx, dy); setFollowPosition(false);
}
@ - Remember the position. We use this in mouseReleaseEvent
-
Hi Conny,
Thank you very much!
Your approach does work! I think for developers who need to use more complicated features than what qml location plugin offers, probably a custimised map widget which extends QGraphicsGeoMap is the best option.
And it is also possible to develope own map widget, which subclasses QGraphicsWidget, by using QGeoMapData and QGeoMappingManager.
Thanks again!
Regards,
Juan -
Hi Jano,
if you're new with QML you should start by using the default map item. It has also much improved in QtM 1.2. Have a look here:
http://doc.qt.nokia.com/qtmobility-1.2/declarative-location-mapviewer-mapviewer-qml.html -
-
Hi Conny,
I resolve issue. I'm gettin mouse events in simulator aswell.
Do you maybe know if is possible to add mapObject to the map so that onclick event can be handled?
Somehow extend QGeoMapObject?Unfortunatelly method mapObjectsAtScreenPosition(lastPos) does not work on devices. I'm retreiving empty objects lists.
I have latest Qt SDK 1.1.2 and usign Qt Mobility 1.1
Many Thanks for your help
-
Yes you're right. Actually I've reported that bug. Unfortunately I don't know a way to get the mouse events on a devices with QtM < 1.2. I'm also waiting that QtM 1.2 will finally be available for devices.
On Maemo5 I've tested it with QtM 1.2 and it's actually working. On Symbian and for Ovi, we still have to wait :(
-
Nice! I have also decided to give qml a try and of course are running into the same issues. I have started to make a minimal demo app from conny's example and would like to setup a wiki page or similar as i think we should document this a little bit, so others can re-use this.
-
Ok, "here":http://wiki.meego.com/QML/QGraphicsGeoMap is the wiki page. Please contribute your findings and let's try to make this into a small complete guide for this. I e.g. found that the map doesn't scale when the window size changes.