diff options
Diffstat (limited to 'qmapwatchlet/mapview.cpp')
-rw-r--r-- | qmapwatchlet/mapview.cpp | 273 |
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; + } +} |