a56ec27063906640273d9b77ba74b7d26bb0e6bb
[situare] / src / qmlui / geomap.cpp
1 #include "geomap.h"
2 #include <QDebug>
3 #include <QGeoMapPixmapObject>
4 #include <QGeoMapPolylineObject>
5 #include "../user/friendmodel.h"
6 #include "../coordinates/geocoordinate.h"
7 #include "../routing/routemodel.h"
8 #include <QGraphicsSceneMouseEvent>
9 #include "../ui/avatarimage.h"
10
11 using namespace QtMobility;
12
13 class GeoMapPrivate
14 {
15 public:
16     GeoMapPrivate()
17         : friendModel(0),
18           friendFilterModel(0),
19           routeModel(0),
20           gpsLocationMapObject(0),
21           routeMapObject(0)
22     {}
23
24     /**
25       * Filter geometry points.
26       * TODO: redo :)
27       */
28     void filterCoordinatePoints(QList<QGeoCoordinate>* geometryPoints)
29     {
30         // Rough guess for maximum point count for smooth use
31         // Produces max 500+n items to result array (rounding error in division)...
32         static const int MAX_POINT_COUNT = 500;
33
34         if (geometryPoints->count() > MAX_POINT_COUNT) {
35             const int skip = geometryPoints->count() / MAX_POINT_COUNT;
36
37             QList<QGeoCoordinate> filtered;
38             for (int i = 0; i < geometryPoints->count(); i += skip)
39                 filtered << geometryPoints->at(i);
40             filtered << geometryPoints->last();
41             *geometryPoints = filtered;
42         }
43     }
44
45     FriendModel* friendModel;
46     FilteredFriendModel* friendFilterModel;
47
48     RouteModel* routeModel;
49
50     QGeoMapPixmapObject* gpsLocationMapObject;
51     QGeoMapPolylineObject* routeMapObject;
52
53     QList<QGeoMapPixmapObject*> friendMapObjects;
54 };
55
56 GeoMap::GeoMap(QGraphicsItem* parent)
57     : QGraphicsGeoMap(parent),
58       d_ptr(new GeoMapPrivate),
59       source(0),
60       prevGpsLocation(0,0),
61       initialized(false)
62 {
63     qDebug() << __PRETTY_FUNCTION__;
64     connect(this, SIGNAL(panned(QPoint)), this, SLOT(panned()));
65
66     setCenter(prevGpsLocation);
67
68     goToGpsLocation();
69
70     setObjectName("geoMap");
71 }
72
73 GeoMap::~GeoMap()
74 {
75     qDebug() << __PRETTY_FUNCTION__;
76 }
77
78 qreal GeoMap::angleToGpsLocation() const
79 {
80     const qreal result = prevGpsLocation.isValid() ? center().azimuthTo(prevGpsLocation) : 0.0;
81     qDebug() << "QmlMap::angleToGpsLocation: " << result;
82     return result;
83 }
84
85 qreal GeoMap::distanceToGpsLocation() const
86 {
87     const qreal result = prevGpsLocation.isValid() ? center().distanceTo(prevGpsLocation) : 0.0;
88     qDebug() << "QmlMap::distanceToGpsLocation: " << result << " meters";
89     return result;
90 }
91
92 QString GeoMap::distanceToGpsLocationText() const
93 {
94     const qreal distance = distanceToGpsLocation();
95     // copy/paste from: IndicatorButtonPanel::updateValues
96     const int MAX_TO_METERS = 999.5;
97     const int MAX_TO_KM_WITH_DESIMAL = 99950;
98     const int MIN_TO_METERS = 10;
99     const int M_TO_KM = 1000;                   ///< Meters to kilometers conversion
100     const QString UNIT_KILOMETER = " km";
101     const QString UNIT_METER = " m";
102
103     QString distanceText;
104     if (distance < MIN_TO_METERS) {
105         distanceText.setNum(10);
106         distanceText.prepend("< ");
107         distanceText.append(UNIT_METER);
108     } else if (distance < MAX_TO_METERS) {
109         distanceText.setNum(distance, 'f', 0);
110         distanceText.append(UNIT_METER);
111     } else if (distance < MAX_TO_KM_WITH_DESIMAL) {
112         distanceText.setNum(distance / M_TO_KM, 'f', 1);
113         distanceText.append(UNIT_KILOMETER);
114     } else {
115         distanceText.setNum(distance / M_TO_KM, 'f', 0);
116         distanceText.append(UNIT_KILOMETER);
117     }
118     return distanceText;
119 }
120
121 double GeoMap::centerLatitude() const {
122     return center().latitude();
123 }
124
125 void GeoMap::setCenterLatitude(const double latitude) {
126     QGeoCoordinate newCenter = center();
127     newCenter.setLatitude(latitude);
128     setCenter(newCenter);
129     emit positionChanged();
130 }
131
132 double GeoMap::centerLongitude() const
133 {
134     return center().longitude();
135 }
136
137 void GeoMap::setCenterLongitude(const double longitude) {
138     QGeoCoordinate newCenter = center();
139     newCenter.setLongitude(longitude);
140     setCenter(newCenter);
141     emit positionChanged();
142
143 }
144
145 double GeoMap::gpsLocationLatitude() const
146 {
147     return prevGpsLocation.latitude();
148 }
149
150 double GeoMap::gpsLocationLongitude() const
151 {
152     return prevGpsLocation.longitude();
153 }
154
155 void GeoMap::setFriendModels(FriendModel* friendModel, FilteredFriendModel* friendFilterModel)
156 {
157     Q_D(GeoMap);
158     Q_ASSERT(friendModel && friendFilterModel);
159     if (friendModel && friendFilterModel) {
160         d->friendModel = friendModel;
161         d->friendFilterModel = friendFilterModel;
162         connect(d->friendModel, SIGNAL(modelReset()), this, SLOT(onFriendModelReset()));
163     }
164 }
165
166 void GeoMap::setRouteModel(RouteModel* routeModel)
167 {
168     Q_D(GeoMap);
169     Q_ASSERT(routeModel);
170     d->routeModel = routeModel;
171     connect(d->routeModel, SIGNAL(modelReset()), this, SLOT(onRouteModelReset()));
172 }
173
174 void GeoMap::panned()
175 {
176     emit positionChanged();
177 }
178
179 void GeoMap::onFriendModelReset()
180 {
181     Q_D(GeoMap);
182     Q_ASSERT(d->friendModel);
183
184     foreach(QGeoMapPixmapObject* mapObject, d->friendMapObjects) {
185         removeMapObject(mapObject);
186         delete mapObject;
187     }
188     d->friendMapObjects.clear();
189
190     for (int i=0; i < d->friendModel->rowCount(); ++i) {
191         const QModelIndex index = d->friendModel->index(i);
192         QPixmap pixmap = d->friendModel->data(index, FriendModel::ImageRole).value<QPixmap>();
193         GeoCoordinate coordinate = d->friendModel->data(index, FriendModel::CoordinateRole).value<GeoCoordinate>();
194
195         if (pixmap.isNull())
196             pixmap = QPixmap(":/res/images/user_info.png");
197
198         Q_ASSERT(!pixmap.isNull());
199
200         pixmap = AvatarImage::create(pixmap, AvatarImage::Small);
201
202         if (!pixmap.isNull() && coordinate.isValid()) {
203             QPoint offset(-pixmap.width()/2, -pixmap.height()/2);
204             QGeoMapPixmapObject* object = new QGeoMapPixmapObject(QGeoCoordinate(coordinate.latitude(), coordinate.longitude()),
205                                                                   offset,
206                                                                   pixmap,
207                                                                   0);
208             object->setProperty("user", d->friendModel->data(index, FriendModel::UserRole));
209             addMapObject(object);
210             d->friendMapObjects << object;
211         }
212     }
213 }
214
215 void GeoMap::onRouteModelReset()
216 {
217     qDebug() << __PRETTY_FUNCTION__;
218     Q_D(GeoMap);
219     Q_ASSERT(d->routeModel);
220
221     const QList<GeoCoordinate>& geometryPoints = d->routeModel->geometryPoints();
222
223     QList<QGeoCoordinate> path;
224     foreach(GeoCoordinate c, geometryPoints)
225         path << QGeoCoordinate(c.latitude(), c.longitude());
226
227     if (!d->routeMapObject) {
228         d->routeMapObject = new QGeoMapPolylineObject();
229     }
230     else {
231         removeMapObject(d->routeMapObject);
232     }
233     if (!path.isEmpty()) {
234         qDebug() << "PATH BEFORE: " << path.count();
235         d->filterCoordinatePoints(&path);
236         qDebug() << "PATH AFTER: " << path.count();
237
238         d->routeMapObject->setPath(path);
239         QPen pen(Qt::red);
240         pen.setWidth(5);
241         d->routeMapObject->setPen(pen);
242         addMapObject(d->routeMapObject);
243     }
244 }
245
246 void GeoMap::goToGpsLocation()
247 {
248     qDebug() << "QmlMap::goToGpsLocation";
249     if (!source)
250         source = QGeoPositionInfoSource::createDefaultSource(this);
251
252     if (source) {
253         source->requestUpdate();
254         connect(source, SIGNAL(positionUpdated(QGeoPositionInfo)), this, SLOT(positionUpdated(QGeoPositionInfo)));
255         source->setUpdateInterval(10000);
256         source->startUpdates();
257     }
258 }
259
260 void GeoMap::onClicked(qreal mouseX, qreal mouseY)
261 {
262     Q_D(GeoMap);
263     QList<QGeoMapObject*> mapObjects = mapObjectsAtScreenPosition(QPointF(mouseX, mouseY));
264     QList<User*> filter;
265     foreach(QGeoMapObject* mapObject, mapObjects) {
266         User* user = static_cast<User*>(mapObject->property("user").value<void*>());
267         if (user)
268             filter << user;
269     }
270     // TODO: reset filter only if it contains at least one entry
271     //if (!filter.isEmpty())
272         d->friendFilterModel->setFilter(filter);
273 }
274
275 void GeoMap::zoomIn()
276 {
277     setZoomLevel(zoomLevel() + 1.0);
278 }
279
280 void GeoMap::zoomOut()
281 {
282     if (zoomLevel() > 1.0)
283         setZoomLevel(zoomLevel() - 1.0);
284 }
285
286
287 void GeoMap::positionUpdated(const QGeoPositionInfo& positionInfo)
288 {
289     qDebug() << "QmlMap::positionUpdated: " << positionInfo.coordinate();
290     prevGpsLocation = positionInfo.coordinate();
291     Q_D(GeoMap);
292
293     if (!d->gpsLocationMapObject) {
294         QPixmap image(":/res/gps_position.png");
295         Q_ASSERT(!image.isNull());
296         d->gpsLocationMapObject = new QGeoMapPixmapObject(prevGpsLocation, QPoint(-image.width()/2, -image.height()/2), image);
297         addMapObject(d->gpsLocationMapObject);
298         setCenter(prevGpsLocation);
299         emit positionChanged();
300     }
301     else {
302         d->gpsLocationMapObject->setCoordinate(prevGpsLocation);
303     }
304     emit gpsLocationChanged();
305 }