#include #include #include #include #include #include #include "qmapwatchletplugin.h" #include "mapview.h" QTM_USE_NAMESPACE using namespace sowatch; MapView::MapView(QDeclarativeItem *parent) : QDeclarativeItem(parent), _enabled(false), _decolor(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(); } } bool MapView::decolor() const { return _decolor; } void MapView::setDecolor(bool decolor) { _decolor = decolor; } 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()); if (_decolor) { 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 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(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); } else { _mapData->paint(painter, option); } // 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 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: " << error << errorString; if (_searchReply) { _searchReply->deleteLater(); _searchReply = 0; } }