summaryrefslogtreecommitdiff
path: root/qmapwatchlet/mapview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'qmapwatchlet/mapview.cpp')
-rw-r--r--qmapwatchlet/mapview.cpp273
1 files changed, 273 insertions, 0 deletions
diff --git a/qmapwatchlet/mapview.cpp b/qmapwatchlet/mapview.cpp
new file mode 100644
index 0000000..5972e07
--- /dev/null
+++ b/qmapwatchlet/mapview.cpp
@@ -0,0 +1,273 @@
+#include <QtCore/QDebug>
+#include <QtGui/QMainWindow>
+#include <QtGui/QLabel>
+#include <QtLocation/QGeoMapData>
+#include <QtLocation/QGraphicsGeoMap>
+#include <QtLocation/QGeoSearchManager>
+#include "qmapwatchletplugin.h"
+#include "mapview.h"
+
+QTM_USE_NAMESPACE
+using namespace sowatch;
+
+MapView::MapView(QDeclarativeItem *parent) :
+ QDeclarativeItem(parent),
+ _enabled(false),
+ _arrow(SOWATCH_QML_DIR "/qmapwatchlet/arrow.png"),
+ _mapData(0),
+ _posSource(QGeoPositionInfoSource::createDefaultSource(this)),
+ _searchArea(0), _searchReply(0)
+{
+ QGeoServiceProvider *provider = QMapWatchletPlugin::geoServiceProvider();
+ if (!provider) {
+ qWarning() << "No geo service provider for map watchlet!";
+ }
+
+ QGeoMappingManager *manager = provider->mappingManager();
+ _mapData = manager->createMapData();
+
+ if (_mapData) {
+ _mapData->init();
+ _mapData->setMapType(QGraphicsGeoMap::StreetMap);
+
+ _mapData->setZoomLevel(12);
+
+ connect(_mapData, SIGNAL(zoomLevelChanged(qreal)), SIGNAL(zoomLevelChanged()));
+ connect(_mapData, SIGNAL(updateMapDisplay(QRectF)), SLOT(handleMapChanged(QRectF)));
+ } else {
+ qWarning() << "No mapdata!";
+ }
+
+ if (_posSource) {
+ connect(_posSource, SIGNAL(positionUpdated(QGeoPositionInfo)),
+ SLOT(handlePositionUpdate(QGeoPositionInfo)));
+ _posSource->lastKnownPosition();
+ } else {
+ qWarning() << "No position source for moving map!";
+ }
+
+ setFlag(QGraphicsItem::ItemHasNoContents, false);
+}
+
+MapView::~MapView()
+{
+ delete _mapData;
+ delete _searchReply;
+ delete _searchArea;
+}
+
+bool MapView::updateEnabled() const
+{
+ return _enabled;
+}
+
+void MapView::setUpdateEnabled(bool enabled)
+{
+ if (_posSource && _enabled != enabled) {
+ if (enabled) {
+ qDebug() << "Start position updates";
+ _posSource->startUpdates();
+ } else {
+ qDebug() << "Stop position updates";
+ _posSource->stopUpdates();
+ }
+ _enabled = enabled;
+
+ emit updateEnabledChanged();
+ }
+
+}
+
+int MapView::updateInterval() const
+{
+ if (_posSource) {
+ return _posSource->updateInterval();
+ } else {
+ return 0;
+ }
+}
+
+void MapView::setUpdateInterval(int msec)
+{
+ if (_posSource) {
+ _posSource->setUpdateInterval(msec);
+ emit updateIntervalChanged();
+ }
+}
+
+qreal MapView::zoomLevel() const
+{
+ if (_mapData) {
+ return _mapData->zoomLevel();
+ } else {
+ return -1.0;
+ }
+}
+
+void MapView::setZoomLevel(qreal level)
+{
+ if (_mapData) {
+ _mapData->setZoomLevel(level);
+ qDebug() << "new zoom level" << level;
+ }
+}
+
+QString MapView::currentLocationName() const
+{
+ return _posName;
+}
+
+void MapView::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(widget);
+ if (_mapData) {
+ // Render to an image first
+ const QSize size(_mapData->windowSize().toSize());
+ QImage image(size, QImage::Format_RGB32);
+ QImage pixmap(size, QImage::Format_MonoLSB);
+
+ const int w = image.width(), h = image.height();
+ const int npixels = w * h;
+ QScopedArrayPointer<qreal> greys(new qreal[npixels]);
+
+ {
+ QPainter p(&image);
+ _mapData->paint(&p, option);
+ }
+
+ // Convert to a bitmap using some ad-hoc ugly algorithm...
+ qreal sum = 0;
+ for (int y = 0; y < h; y++) {
+ QRgb *l = reinterpret_cast<QRgb*>(image.scanLine(y));
+ for (int x = 0; x < w; x++) {
+ const int r = qRed(l[x]), g = qGreen(l[x]), b = qBlue(l[x]);
+ const qreal grey = r * 0.299f + g * 0.587f + b * 0.114f;
+
+ greys[y * w + x] = grey;
+
+ sum += grey;
+ }
+ }
+
+ const qreal avg = sum / npixels;
+ const qreal thr = avg * 0.9;
+
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ // TODO: Optimize
+ pixmap.setPixel(x, y, greys[y * w + x] >= thr ? Qt::color1 : Qt::color0);
+ }
+ }
+
+ // And render into the watch
+ painter->drawImage(0, 0, pixmap);
+
+ // Now render the arrow indicator
+ const int centerX = size.width() / 2, centerY = size.height() / 2;
+ painter->save();
+ painter->translate(centerX, centerY);
+ if (_pos.hasAttribute(QGeoPositionInfo::Direction)) {
+ painter->rotate(_pos.attribute(QGeoPositionInfo::Direction));
+ }
+ painter->drawImage(-_arrow.width() / 2, -_arrow.height() / 2, _arrow);
+ painter->restore();
+ }
+}
+
+void MapView::updateCurrentLocationName()
+{
+ if (_searchReply) {
+ qDebug() << "Search already in progress";
+ return;
+ }
+ QGeoServiceProvider *provider = QMapWatchletPlugin::geoServiceProvider();
+ if (!provider) {
+ qWarning() << "No geo service provider for map watchlet!";
+ }
+
+ // Lifetime of 'bounds' in call to reverseGeocode() is not specified anywhere.
+ // So we keep it "forever" until the next call to reverseGeocode().
+ // which is ... now.
+ delete _searchArea;
+
+ // Create the new bounds object.
+ if (_mapData) {
+ _searchArea = new QGeoBoundingBox(_mapData->viewport());
+ } else {
+ _searchArea = 0;
+ }
+
+ _posName.clear();
+
+ qDebug() << "Start request of current location";
+
+ QGeoSearchManager *manager = provider->searchManager();
+ _searchReply = manager->reverseGeocode(_pos.coordinate(), _searchArea);
+ connect(_searchReply, SIGNAL(finished()),
+ SLOT(handleCurrentLocationNameSearchFinished()));
+ connect(_searchReply, SIGNAL(error(QGeoSearchReply::Error,QString)),
+ SLOT(handleCurrentLocationNameSearchError(QGeoSearchReply::Error,QString)));
+}
+
+void MapView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ Q_UNUSED(oldGeometry);
+ if (_mapData) {
+ _mapData->setWindowSize(newGeometry.size());
+ }
+}
+
+void MapView::handlePositionUpdate(const QGeoPositionInfo& info)
+{
+ _pos = info;
+ if (_mapData) {
+ _mapData->setCenter(info.coordinate());
+ }
+}
+
+void MapView::handleMapChanged(const QRectF &rect)
+{
+ update(rect);
+}
+
+void MapView::handleCurrentLocationNameSearchFinished()
+{
+ if (_searchReply) {
+ if (_searchReply->error() == QGeoSearchReply::NoError) {
+ QList<QGeoPlace> places = _searchReply->places();
+ qDebug() << "Current location name search got " << places.size() << " results";
+ foreach (const QGeoPlace& place, places) {
+ QGeoAddress address = place.address();
+ qDebug() << " " << address.street() << " - " << address.district() << " - " << address.city();
+ }
+ if (!places.isEmpty()) {
+ QGeoAddress address = places.first().address();
+ if (!address.street().isEmpty()) {
+ _posName = address.street();
+ } else if (!address.district().isEmpty()) {
+ _posName = address.district();
+ } else if (!address.city().isEmpty()) {
+ _posName = address.city();
+ } else {
+ _posName.clear();
+ }
+ qDebug() << "Current location name search finished:" << _posName;
+ emit currentLocationNameChanged();
+ }
+ } else {
+ qDebug() << "Current location name search finished with error:"
+ << _searchReply->error();
+ }
+ _searchReply->deleteLater();
+ _searchReply = 0;
+ }
+}
+
+void MapView::handleCurrentLocationNameSearchError(QGeoSearchReply::Error error, const QString &errorString)
+{
+ qWarning() << "Current location name search error: " << errorString;
+ if (_searchReply) {
+ _searchReply->deleteLater();
+ _searchReply = 0;
+ }
+}