diff options
author | Liang Qi <liang.qi@qt.io> | 2018-01-26 10:02:03 +0100 |
---|---|---|
committer | Liang Qi <liang.qi@qt.io> | 2018-01-26 10:18:14 +0100 |
commit | 518633da419d6cfaf603773b9e8c5840e783bc4a (patch) | |
tree | 326ffd3f5497be64e38fa420af016867ada00538 | |
parent | 9283a290040fd55f7eb34580b99f771d4facdeb4 (diff) | |
parent | 68b5ee9e7064f66fd7e7bd69b4b51229dbe8cc3c (diff) | |
download | qtlocation-518633da419d6cfaf603773b9e8c5840e783bc4a.tar.gz |
Merge remote-tracking branch 'origin/5.9' into 5.10
Conflicts:
tests/auto/qgeotiledmap/tst_qgeotiledmap.cpp
tests/plugins/declarativetestplugin/testhelper.h
Change-Id: Ie218ab1dc68642a6922e05e5688c20b90440b72e
28 files changed, 599 insertions, 249 deletions
diff --git a/dist/changes-5.9.4 b/dist/changes-5.9.4 new file mode 100644 index 00000000..63931298 --- /dev/null +++ b/dist/changes-5.9.4 @@ -0,0 +1,31 @@ +Qt 5.9.4 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.9.0. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +http://doc.qt.io/qt-5/index.html + +The Qt version 5.9 series is binary compatible with the 5.8.x series. +Applications compiled for 5.8 will continue to run with 5.9. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Qt 5.9.4 Changes * +**************************************************************************** + +QtLocation +---------- + + - [QTBUG-63013] Upgraded Mapbox GL to fix SQLite query binding. + - [QTBUG-58589] Added plugin dependencies to the mapviewer example. + - [QTBUG-64632] Upgraded Mapbox GL to fix remove GLES limitations on Windows. + This upgrade also fixed building Mapbox GL with MinGW. + - [QTBUG-57147] Fixed license headers. diff --git a/src/3rdparty/earcut/earcut.hpp b/src/3rdparty/earcut/earcut.hpp index 287be028..ba3fb17a 100644 --- a/src/3rdparty/earcut/earcut.hpp +++ b/src/3rdparty/earcut/earcut.hpp @@ -9,7 +9,7 @@ #include <memory> #include <vector> -namespace mapbox { +namespace qt_mapbox { namespace util { @@ -771,7 +771,7 @@ void Earcut<N>::removeNode(Node* p) { template <typename N = uint32_t, typename Polygon> std::vector<N> earcut(const Polygon& poly) { - mapbox::detail::Earcut<N> earcut; + qt_mapbox::detail::Earcut<N> earcut; earcut(poly); return earcut.indices; } diff --git a/src/3rdparty/mapbox-gl-native b/src/3rdparty/mapbox-gl-native -Subproject 8c1be4ec01ef46bf453856531ebf53b48ce3dbe +Subproject 572822c8ca15be190b43afbf7f91d132e988bf2 diff --git a/src/imports/positioning/qdeclarativepositionsource.cpp b/src/imports/positioning/qdeclarativepositionsource.cpp index 05232d51..73d99fe5 100644 --- a/src/imports/positioning/qdeclarativepositionsource.cpp +++ b/src/imports/positioning/qdeclarativepositionsource.cpp @@ -297,6 +297,7 @@ void QDeclarativePositionSource::setNmeaSource(const QUrl &nmeaSource) qDebug() << "QDeclarativePositionSource NMEA File was found: " << localFileName; #endif m_positionSource = new QNmeaPositionInfoSource(QNmeaPositionInfoSource::SimulationMode); + (qobject_cast<QNmeaPositionInfoSource *>(m_positionSource))->setUserEquivalentRangeError(2.5); // it is internally multiplied by 2 in qlocationutils_readGga (qobject_cast<QNmeaPositionInfoSource *>(m_positionSource))->setDevice(m_nmeaFile); connect(m_positionSource, SIGNAL(positionUpdated(QGeoPositionInfo)), this, SLOT(positionUpdateReceived(QGeoPositionInfo))); diff --git a/src/location/declarativemaps/qdeclarativegeocodemodel.cpp b/src/location/declarativemaps/qdeclarativegeocodemodel.cpp index 3e2a1aea..e5d0356f 100644 --- a/src/location/declarativemaps/qdeclarativegeocodemodel.cpp +++ b/src/location/declarativemaps/qdeclarativegeocodemodel.cpp @@ -382,12 +382,13 @@ void QDeclarativeGeocodeModel::geocodeFinished(QGeoCodeReply *reply) { if (reply != reply_ || reply->error() != QGeoCodeReply::NoError) return; + + reply->deleteLater(); + reply_ = 0; int oldCount = declarativeLocations_.count(); setLocations(reply->locations()); setError(NoError, QString()); setStatus(QDeclarativeGeocodeModel::Ready); - reply->deleteLater(); - reply_ = 0; emit locationsChanged(); if (oldCount != declarativeLocations_.count()) emit countChanged(); @@ -402,7 +403,9 @@ void QDeclarativeGeocodeModel::geocodeError(QGeoCodeReply *reply, { if (reply != reply_) return; - Q_UNUSED(error); + + reply->deleteLater(); + reply_ = 0; int oldCount = declarativeLocations_.count(); if (oldCount > 0) { // Reset the model @@ -412,8 +415,6 @@ void QDeclarativeGeocodeModel::geocodeError(QGeoCodeReply *reply, } setError(static_cast<QDeclarativeGeocodeModel::GeocodeError>(error), errorString); setStatus(QDeclarativeGeocodeModel::Error); - reply->deleteLater(); - reply_ = 0; } /*! diff --git a/src/location/declarativemaps/qdeclarativegeomap.cpp b/src/location/declarativemaps/qdeclarativegeomap.cpp index 34885f54..b9f62fad 100644 --- a/src/location/declarativemaps/qdeclarativegeomap.cpp +++ b/src/location/declarativemaps/qdeclarativegeomap.cpp @@ -51,6 +51,7 @@ #include <QtQuick/QSGRectangleNode> #include <QtQuick/private/qquickwindow_p.h> #include <QtQml/qqmlinfo.h> +#include <QtQuick/private/qquickitem_p.h> #include <cmath> #ifndef M_PI @@ -187,8 +188,6 @@ QDeclarativeGeoMap::QDeclarativeGeoMap(QQuickItem *parent) setFlags(QQuickItem::ItemHasContents | QQuickItem::ItemClipsChildrenToShape); setFiltersChildMouseEvents(true); - connect(this, SIGNAL(childrenChanged()), this, SLOT(onMapChildrenChanged()), Qt::QueuedConnection); - m_activeMapType = new QDeclarativeGeoMapType(QGeoMapType(QGeoMapType::NoMap, tr("No Map"), tr("No Map"), @@ -254,77 +253,13 @@ QDeclarativeGeoMap::~QDeclarativeGeoMap() } m_mapItemGroups.clear(); - delete m_copyrights.data(); + if (m_copyrights.data()) + delete m_copyrights.data(); m_copyrights.clear(); delete m_map; } -/*! - \internal -*/ -void QDeclarativeGeoMap::onMapChildrenChanged() -{ - if (!m_componentCompleted || !m_map) - return; - - int maxChildZ = 0; - QObjectList kids = children(); - bool foundCopyrights = false; - - for (int i = 0; i < kids.size(); ++i) { - QDeclarativeGeoMapCopyrightNotice *copyrights = qobject_cast<QDeclarativeGeoMapCopyrightNotice *>(kids.at(i)); - if (copyrights) { - foundCopyrights = true; - } else { - QDeclarativeGeoMapItemBase *mapItem = qobject_cast<QDeclarativeGeoMapItemBase *>(kids.at(i)); - if (mapItem) { - if (mapItem->z() > maxChildZ) - maxChildZ = mapItem->z(); - } - } - } - - QDeclarativeGeoMapCopyrightNotice *copyrights = m_copyrights.data(); - // if copyrights object not found within the map's children - if (!foundCopyrights) { - // if copyrights object was deleted! - if (!copyrights) { - // create a new one and set its parent, re-assign it to the weak pointer, then connect the copyrights-change signal - m_copyrights = new QDeclarativeGeoMapCopyrightNotice(this); - m_copyrights->onCopyrightsStyleSheetChanged(m_map->copyrightsStyleSheet()); - - copyrights = m_copyrights.data(); - - connect(m_map, SIGNAL(copyrightsChanged(QImage)), - copyrights, SLOT(copyrightsChanged(QImage))); - connect(m_map, SIGNAL(copyrightsChanged(QImage)), - this, SIGNAL(copyrightsChanged(QImage))); - - connect(m_map, SIGNAL(copyrightsChanged(QString)), - copyrights, SLOT(copyrightsChanged(QString))); - connect(m_map, SIGNAL(copyrightsChanged(QString)), - this, SIGNAL(copyrightsChanged(QString))); - - connect(m_map, SIGNAL(copyrightsStyleSheetChanged(QString)), - copyrights, SLOT(onCopyrightsStyleSheetChanged(QString))); - - connect(copyrights, SIGNAL(linkActivated(QString)), - this, SIGNAL(copyrightLinkActivated(QString))); - - // set visibility of copyright notice - copyrights->setCopyrightsVisible(m_copyrightsVisible); - - } else { - // just re-set its parent. - copyrights->setParent(this); - } - } - - // put the copyrights notice object at the highest z order - copyrights->setCopyrightsZ(maxChildZ + 1); -} - static QDeclarativeGeoMapType *findMapType(const QList<QDeclarativeGeoMapType *> &types, const QGeoMapType &type) { for (int i = 0; i < types.size(); ++i) @@ -438,7 +373,7 @@ void QDeclarativeGeoMap::initialize() emit mapReadyChanged(true); - if (m_copyrights) + if (m_copyrights) // To not update during initialize() update(); } @@ -735,7 +670,6 @@ void QDeclarativeGeoMap::onCameraCapabilitiesChanged(const QGeoCameraCapabilitie } } - /*! \internal this function will only be ever called once @@ -747,6 +681,12 @@ void QDeclarativeGeoMap::mappingManagerInitialized() if (!m_map) return; + /* COPY NOTICE SETUP */ + m_copyrights = new QDeclarativeGeoMapCopyrightNotice(this); + m_copyrights->setCopyrightsZ(m_maxChildZ + 1); + m_copyrights->setCopyrightsVisible(m_copyrightsVisible); + m_copyrights->setMapSource(this); + m_gestureArea->setMap(m_map); QList<QGeoMapType> types = m_mappingManager->supportedMapTypes(); @@ -795,40 +735,26 @@ void QDeclarativeGeoMap::mappingManagerInitialized() QOverload<const QImage &>::of(&QGeoMap::copyrightsChanged), [©rightImage](const QImage ©){ copyrightImage = copy; }); m_map->setViewportSize(QSize(width(), height())); - initialize(); + initialize(); // This emits the caught signals above QObject::disconnect(copyrightStringCatcherConnection); QObject::disconnect(copyrightImageCatcherConnection); } - m_copyrights = new QDeclarativeGeoMapCopyrightNotice(this); - m_copyrights->onCopyrightsStyleSheetChanged(m_map->copyrightsStyleSheet()); - connect(m_map, SIGNAL(copyrightsChanged(QImage)), - m_copyrights.data(), SLOT(copyrightsChanged(QImage))); + /* COPYRIGHT SIGNALS REWIRING */ connect(m_map, SIGNAL(copyrightsChanged(QImage)), this, SIGNAL(copyrightsChanged(QImage))); - - connect(m_map, SIGNAL(copyrightsChanged(QString)), - m_copyrights.data(), SLOT(copyrightsChanged(QString))); connect(m_map, SIGNAL(copyrightsChanged(QString)), this, SIGNAL(copyrightsChanged(QString))); - if (!copyrightString.isEmpty()) emit m_map->copyrightsChanged(copyrightString); else if (!copyrightImage.isNull()) emit m_map->copyrightsChanged(copyrightImage); - connect(m_map, SIGNAL(copyrightsStyleSheetChanged(QString)), - m_copyrights.data(), SLOT(onCopyrightsStyleSheetChanged(QString))); - connect(m_copyrights.data(), SIGNAL(linkActivated(QString)), - this, SIGNAL(copyrightLinkActivated(QString))); connect(m_map, &QGeoMap::sgNodeChanged, this, &QQuickItem::update); connect(m_map, &QGeoMap::cameraCapabilitiesChanged, this, &QDeclarativeGeoMap::onCameraCapabilitiesChanged); - // set visibility of copyright notice - m_copyrights->setCopyrightsVisible(m_copyrightsVisible); - // This prefetches a buffer around the map m_map->prefetchData(); @@ -1152,7 +1078,7 @@ void QDeclarativeGeoMap::setMinimumFieldOfView(qreal minimumFieldOfView, bool us } /*! - \qmlproperty bool QtLocation::Map::minimumFieldOfView + \qmlproperty real QtLocation::Map::minimumFieldOfView This property holds the minimum valid field of view for the map, in degrees. @@ -1189,7 +1115,7 @@ void QDeclarativeGeoMap::setMaximumFieldOfView(qreal maximumFieldOfView, bool us } /*! - \qmlproperty bool QtLocation::Map::maximumFieldOfView + \qmlproperty real QtLocation::Map::maximumFieldOfView This property holds the maximum valid field of view for the map, in degrees. @@ -1207,7 +1133,7 @@ qreal QDeclarativeGeoMap::maximumFieldOfView() const } /*! - \qmlproperty bool QtLocation::Map::minimumTilt + \qmlproperty real QtLocation::Map::minimumTilt This property holds the minimum valid tilt for the map, in degrees. @@ -1244,7 +1170,7 @@ void QDeclarativeGeoMap::setMaximumTilt(qreal maximumTilt, bool userSet) } /*! - \qmlproperty bool QtLocation::Map::maximumTilt + \qmlproperty real QtLocation::Map::maximumTilt This property holds the maximum valid tilt for the map, in degrees. @@ -1412,7 +1338,7 @@ QColor QDeclarativeGeoMap::color() const } /*! - \qmlproperty color QtLocation::Map::mapReady + \qmlproperty bool QtLocation::Map::mapReady This property holds whether the map has been successfully initialized and is ready to be used. Some methods, such as \l fromCoordinate and \l toCoordinate, will not work before the map is ready. @@ -1647,6 +1573,27 @@ QGeoServiceProvider::Error QDeclarativeGeoMap::error() const return m_error; } +void QDeclarativeGeoMap::itemChange(ItemChange change, const ItemChangeData &value) +{ + if (change == ItemChildAddedChange) { + QQuickItem *child = value.item; + QQuickItem *mapItem = qobject_cast<QDeclarativeGeoMapItemBase *>(child); + if (!mapItem) + mapItem = qobject_cast<QDeclarativeGeoMapItemGroup *>(child); + + if (mapItem) { + qreal z = mapItem->z(); + if (z > m_maxChildZ) { // Ignore children removal + m_maxChildZ = z; + // put the copyrights notice object at the highest z order + if (m_copyrights) + m_copyrights->setCopyrightsZ(m_maxChildZ + 1); + } + } + } + QQuickItem::itemChange(change, value); +} + /*! \internal */ @@ -1679,6 +1626,32 @@ bool QDeclarativeGeoMap::isInteractive() return (m_gestureArea->enabled() && m_gestureArea->acceptedGestures()) || m_gestureArea->isActive(); } +void QDeclarativeGeoMap::attachCopyrightNotice(bool initialVisibility) +{ + if (initialVisibility) { + ++m_copyNoticesVisible; + if (m_map) + m_map->setCopyrightVisible(m_copyNoticesVisible > 0); + } +} + +void QDeclarativeGeoMap::detachCopyrightNotice(bool currentVisibility) +{ + if (currentVisibility) { + --m_copyNoticesVisible; + if (m_map) + m_map->setCopyrightVisible(m_copyNoticesVisible > 0); + } +} + +void QDeclarativeGeoMap::onAttachedCopyrightNoticeVisibilityChanged() +{ + QDeclarativeGeoMapCopyrightNotice *copy = static_cast<QDeclarativeGeoMapCopyrightNotice *>(sender()); + m_copyNoticesVisible += ( int(copy->copyrightsVisible()) * 2 - 1); + if (m_map) + m_map->setCopyrightVisible(m_copyNoticesVisible > 0); +} + /*! \internal */ @@ -2129,10 +2102,10 @@ void QDeclarativeGeoMap::fitViewportToMapItemsRefine(bool refine, bool onlyVisib if (m_mapItems.size() == 0) return; - double minX = 0; - double maxX = 0; - double minY = 0; - double maxY = 0; + double minX = qInf(); + double maxX = -qInf(); + double minY = qInf(); + double maxY = -qInf(); double topLeftX = 0; double topLeftY = 0; double bottomRightX = 0; @@ -2149,13 +2122,11 @@ void QDeclarativeGeoMap::fitViewportToMapItemsRefine(bool refine, bool onlyVisib continue; // skip quick items in the first pass and refine the fit later - if (refine) { - QDeclarativeGeoMapQuickItem *quickItem = - qobject_cast<QDeclarativeGeoMapQuickItem*>(item); - if (quickItem) { + QDeclarativeGeoMapQuickItem *quickItem = + qobject_cast<QDeclarativeGeoMapQuickItem*>(item); + if (refine && quickItem) { haveQuickItem = true; continue; - } } // Force map items to update immediately. Needed to ensure correct item size and positions // when recursively calling this function. @@ -2163,25 +2134,35 @@ void QDeclarativeGeoMap::fitViewportToMapItemsRefine(bool refine, bool onlyVisib // in relation to // a) fitViewportToMapItems // b) presence of MouseArea + // + // This is also legacy code. It must be updated to not operate on screen sizes. if (item->isPolishScheduled()) item->updatePolish(); - topLeftX = item->position().x(); - topLeftY = item->position().y(); - bottomRightX = topLeftX + item->width(); - bottomRightY = topLeftY + item->height(); + if (quickItem && quickItem->matrix_ && !quickItem->matrix_->m_matrix.isIdentity()) { + // TODO: recalculate the center/zoom level so that the item becomes projectable again + if (quickItem->zoomLevel() == 0.0) // the item is unprojectable, should be skipped. + continue; - if (itemCount == 0) { - minX = topLeftX; - maxX = bottomRightX; - minY = topLeftY; - maxY = bottomRightY; + QRectF brect = item->boundingRect(); + brect = quickItem->matrix_->m_matrix.mapRect(brect); + QPointF transformedPosition = quickItem->matrix_->m_matrix * item->position(); + topLeftX = transformedPosition.x(); + topLeftY = transformedPosition.y(); + bottomRightX = topLeftX + brect.width(); + bottomRightY = topLeftY + brect.height(); } else { - minX = qMin(minX, topLeftX); - maxX = qMax(maxX, bottomRightX); - minY = qMin(minY, topLeftY); - maxY = qMax(maxY, bottomRightY); + topLeftX = item->position().x(); + topLeftY = item->position().y(); + bottomRightX = topLeftX + item->width(); + bottomRightY = topLeftY + item->height(); } + + minX = qMin(minX, topLeftX); + maxX = qMax(maxX, bottomRightX); + minY = qMin(minY, topLeftY); + maxY = qMax(maxY, bottomRightY); + ++itemCount; } diff --git a/src/location/declarativemaps/qdeclarativegeomap_p.h b/src/location/declarativemaps/qdeclarativegeomap_p.h index 5c568d8f..e552affe 100644 --- a/src/location/declarativemaps/qdeclarativegeomap_p.h +++ b/src/location/declarativemaps/qdeclarativegeomap_p.h @@ -61,6 +61,7 @@ #include <QtGui/QColor> #include <QtPositioning/qgeorectangle.h> #include <QtLocation/private/qgeomap_p.h> +#include <QtQuick/private/qquickitemchangelistener_p.h> QT_BEGIN_NAMESPACE @@ -189,6 +190,9 @@ public: QString errorString() const; QGeoServiceProvider::Error error() const; + // From QQuickItem + void itemChange(ItemChange, const ItemChangeData &) override; + Q_SIGNALS: void pluginChanged(QDeclarativeGeoServiceProvider *plugin); void zoomLevelChanged(qreal zoomLevel); @@ -239,9 +243,9 @@ protected: private Q_SLOTS: void mappingManagerInitialized(); void pluginReady(); - void onMapChildrenChanged(); void onSupportedMapTypesChanged(); void onCameraCapabilitiesChanged(const QGeoCameraCapabilities &oldCameraCapabilities); + void onAttachedCopyrightNoticeVisibilityChanged(); private: void setupMapView(QDeclarativeGeoMapItemView *view); @@ -250,6 +254,8 @@ private: void fitViewportToMapItemsRefine(bool refine, bool onlyVisible); void fitViewportToGeoShape(); bool isInteractive(); + void attachCopyrightNotice(bool initialVisibility); + void detachCopyrightNotice(bool currentVisibility); private: QDeclarativeGeoServiceProvider *m_plugin; @@ -287,6 +293,10 @@ private: qreal m_userMinimumFieldOfView; qreal m_userMaximumFieldOfView; + int m_copyNoticesVisible = 0; + qreal m_maxChildZ = 0; + + friend class QDeclarativeGeoMapItem; friend class QDeclarativeGeoMapItemView; friend class QQuickGeoMapGestureArea; diff --git a/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice.cpp b/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice.cpp index 1a8489eb..2a7fd829 100644 --- a/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice.cpp +++ b/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice.cpp @@ -43,9 +43,17 @@ #include <QtQuick/private/qquickanchors_p.h> #include <QtQuick/private/qquickanchors_p_p.h> #include <QtLocation/private/qdeclarativegeomap_p.h> +#include <QtQuick/private/qquickpainteditem_p.h> QT_BEGIN_NAMESPACE +class QDeclarativeGeoMapCopyrightNoticePrivate: public QQuickPaintedItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeGeoMapCopyrightNotice) +public: + virtual void setVisible(bool visible); +}; + /*! \qmltype MapCopyrightNotice \instantiates QDeclarativeGeoMapCopyrightNotice @@ -99,6 +107,7 @@ QDeclarativeGeoMapCopyrightNotice::QDeclarativeGeoMapCopyrightNotice(QQuickItem QDeclarativeGeoMapCopyrightNotice::~QDeclarativeGeoMapCopyrightNotice() { + setMapSource(nullptr); } void QDeclarativeGeoMapCopyrightNotice::anchorToBottomLeft() @@ -112,35 +121,41 @@ void QDeclarativeGeoMapCopyrightNotice::anchorToBottomLeft() } } -void QDeclarativeGeoMapCopyrightNotice::setMapSource(QDeclarativeGeoMap *mapSource) +void QDeclarativeGeoMapCopyrightNotice::setMapSource(QDeclarativeGeoMap *map) { - if (m_mapSource == mapSource) + if (m_mapSource == map) return; if (m_mapSource) { // disconnect this object from current map source + m_mapSource->detachCopyrightNotice(copyrightsVisible()); m_mapSource->disconnect(this); m_mapSource->m_map->disconnect(this); - m_copyrightsHtml->clear(); + if (m_copyrightsHtml) + m_copyrightsHtml->clear(); m_copyrightsImage = QImage(); m_mapSource = Q_NULLPTR; } - if (mapSource) { - m_mapSource = mapSource; + if (map) { + m_mapSource = map; + m_mapSource->attachCopyrightNotice(copyrightsVisible()); + connect(this, &QDeclarativeGeoMapCopyrightNotice::copyrightsVisibleChanged, + mapSource(), &QDeclarativeGeoMap::onAttachedCopyrightNoticeVisibilityChanged); + // First update the copyright. Only Image will do here, no need to store HTML right away. - if (mapSource->m_copyrights && !mapSource->m_copyrights->m_copyrightsImage.isNull()) - m_copyrightsImage = mapSource->m_copyrights->m_copyrightsImage; + if (m_mapSource->m_copyrights && !m_mapSource->m_copyrights->m_copyrightsImage.isNull()) + m_copyrightsImage = m_mapSource->m_copyrights->m_copyrightsImage; - connect(m_mapSource, SIGNAL(copyrightsChanged(QImage)), + connect(mapSource(), SIGNAL(copyrightsChanged(QImage)), this, SLOT(copyrightsChanged(QImage))); - connect(m_mapSource, SIGNAL(copyrightsChanged(QString)), + connect(mapSource(), SIGNAL(copyrightsChanged(QString)), this, SLOT(copyrightsChanged(QString))); if (m_mapSource->m_map) connectMap(); else - connect(m_mapSource, &QDeclarativeGeoMap::mapReadyChanged, this, &QDeclarativeGeoMapCopyrightNotice::connectMap); + connect(mapSource(), &QDeclarativeGeoMap::mapReadyChanged, this, &QDeclarativeGeoMapCopyrightNotice::connectMap); } } @@ -149,7 +164,7 @@ void QDeclarativeGeoMapCopyrightNotice::connectMap() connect(m_mapSource->m_map, SIGNAL(copyrightsStyleSheetChanged(QString)), this, SLOT(onCopyrightsStyleSheetChanged(QString))); connect(this, SIGNAL(linkActivated(QString)), - m_mapSource, SIGNAL(copyrightLinkActivated(QString))); + mapSource(), SIGNAL(copyrightLinkActivated(QString))); onCopyrightsStyleSheetChanged(m_mapSource->m_map->copyrightsStyleSheet()); @@ -159,7 +174,7 @@ void QDeclarativeGeoMapCopyrightNotice::connectMap() QDeclarativeGeoMap *QDeclarativeGeoMapCopyrightNotice::mapSource() { - return m_mapSource; + return m_mapSource.data(); } QString QDeclarativeGeoMapCopyrightNotice::styleSheet() const @@ -253,20 +268,36 @@ void QDeclarativeGeoMapCopyrightNotice::createCopyright() m_copyrightsHtml->setDocumentMargin(0); } +void QDeclarativeGeoMapCopyrightNoticePrivate::setVisible(bool visible) +{ + Q_Q(QDeclarativeGeoMapCopyrightNotice); + q->m_copyrightsVisible = visible; + QQuickItemPrivate::setVisible(visible); +} + /*! \internal */ void QDeclarativeGeoMapCopyrightNotice::setCopyrightsVisible(bool visible) { + Q_D(QDeclarativeGeoMapCopyrightNotice); + if (visible == m_copyrightsVisible) + return; + m_copyrightsVisible = visible; + d->QQuickItemPrivate::setVisible(!m_copyrightsImage.isNull() && visible); + emit copyrightsVisibleChanged(); +} - setVisible(!m_copyrightsImage.isNull() && visible); +bool QDeclarativeGeoMapCopyrightNotice::copyrightsVisible() const +{ + return m_copyrightsVisible; } /*! \internal */ -void QDeclarativeGeoMapCopyrightNotice::setCopyrightsZ(int copyrightsZ) +void QDeclarativeGeoMapCopyrightNotice::setCopyrightsZ(qreal copyrightsZ) { setZ(copyrightsZ); update(); @@ -277,6 +308,7 @@ void QDeclarativeGeoMapCopyrightNotice::setCopyrightsZ(int copyrightsZ) */ void QDeclarativeGeoMapCopyrightNotice::copyrightsChanged(const QImage ©rightsImage) { + Q_D(QDeclarativeGeoMapCopyrightNotice); delete m_copyrightsHtml; m_copyrightsHtml = 0; @@ -286,20 +318,19 @@ void QDeclarativeGeoMapCopyrightNotice::copyrightsChanged(const QImage ©righ setKeepMouseGrab(false); setAcceptedMouseButtons(Qt::NoButton); - setVisible(m_copyrightsVisible); + d->QQuickItemPrivate::setVisible(m_copyrightsVisible && !m_copyrightsImage.isNull()); update(); } void QDeclarativeGeoMapCopyrightNotice::copyrightsChanged(const QString ©rightsHtml) { + Q_D(QDeclarativeGeoMapCopyrightNotice); if (copyrightsHtml.isEmpty()) { - setVisible(false); + d->QQuickItemPrivate::setVisible(false); return; - } else if (!m_copyrightsVisible) { - setVisible(false); - } else { - setVisible(true); + } else { + d->QQuickItemPrivate::setVisible(m_copyrightsVisible); } // Divfy, so we can style the background. The extra <span> is a diff --git a/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice_p.h b/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice_p.h index 0d7f7a20..73ce48f1 100644 --- a/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice_p.h +++ b/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice_p.h @@ -52,13 +52,14 @@ #include <QtLocation/private/qlocationglobal_p.h> #include <QtGui/QImage> +#include <QPointer> #include <QtQuick/QQuickPaintedItem> QT_BEGIN_NAMESPACE class QTextDocument; class QDeclarativeGeoMap; - +class QDeclarativeGeoMapCopyrightNoticePrivate; class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMapCopyrightNotice : public QQuickPaintedItem { Q_OBJECT @@ -69,9 +70,10 @@ public: QDeclarativeGeoMapCopyrightNotice(QQuickItem *parent = Q_NULLPTR); ~QDeclarativeGeoMapCopyrightNotice(); - void setCopyrightsZ(int copyrightsZ); + void setCopyrightsZ(qreal copyrightsZ); void setCopyrightsVisible(bool visible); + bool copyrightsVisible() const; void anchorToBottomLeft(); void setMapSource(QDeclarativeGeoMap *mapSource); @@ -90,6 +92,7 @@ signals: void mapSourceChanged(); void backgroundColorChanged(const QColor &color); void styleSheetChanged(const QString &styleSheet); + void copyrightsVisibleChanged(); protected: void paint(QPainter *painter) Q_DECL_OVERRIDE; @@ -106,10 +109,13 @@ private: QImage m_copyrightsImage; QString m_activeAnchor; bool m_copyrightsVisible; - QDeclarativeGeoMap *m_mapSource; + QPointer<QDeclarativeGeoMap> m_mapSource; QColor m_backgroundColor; QString m_styleSheet; bool m_userDefinedStyleSheet; + + Q_DISABLE_COPY(QDeclarativeGeoMapCopyrightNotice) + Q_DECLARE_PRIVATE(QDeclarativeGeoMapCopyrightNotice) }; QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeomapitembase.cpp b/src/location/declarativemaps/qdeclarativegeomapitembase.cpp index 729825fd..bb503cd3 100644 --- a/src/location/declarativemaps/qdeclarativegeomapitembase.cpp +++ b/src/location/declarativemaps/qdeclarativegeomapitembase.cpp @@ -214,9 +214,14 @@ bool QDeclarativeGeoMapItemBase::childMouseEventFilter(QQuickItem *item, QEvent { Q_UNUSED(item) if (event->type() == QEvent::MouseButtonPress && !contains(static_cast<QMouseEvent*>(event)->pos())) { - // This is an evil hack: in case of items that are not rectangles, we never accept the event. - // Instead the events are now delivered to QDeclarativeGeoMapItemBase which doesn't to anything with them. - // The map below it still works since it filters events and steals the events at some point. + // In case of items that are not rectangles, this filter is used to test if the event has landed + // inside the actual item shape. + // If so, the method returns true, meaning that it prevents the event delivery to child "*item" (for example, + // a mouse area that is on top of this map item). + // However, this method sets "accepted" to false, so that the event can still be passed further up, + // specifically to the parent Map, that is a sort of flickable. + // Otherwise, if the event is not contained within the map item, the method returns false, meaning the event + // is delivered to the child *item (like the mouse area associated). event->setAccepted(false); return true; } diff --git a/src/location/declarativemaps/qdeclarativegeomapquickitem_p.h b/src/location/declarativemaps/qdeclarativegeomapquickitem_p.h index 2035a997..f6b2c7b1 100644 --- a/src/location/declarativemaps/qdeclarativegeomapquickitem_p.h +++ b/src/location/declarativemaps/qdeclarativegeomapquickitem_p.h @@ -126,6 +126,8 @@ private: bool mapAndSourceItemSet_; bool updatingGeometry_; QMapQuickItemMatrix4x4 *matrix_; + + friend class QDeclarativeGeoMap; }; QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp b/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp index 7460a376..6b58a061 100644 --- a/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp +++ b/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp @@ -304,7 +304,7 @@ void QGeoMapPolygonGeometry::updateScreenPoints(const QGeoMap &map) screenIndices_.clear(); for (const auto &p : poly) screenVertices_ << QPointF(p[0], p[1]); - std::vector<N> indices = mapbox::earcut<N>(polygon); + std::vector<N> indices = qt_mapbox::earcut<N>(polygon); for (const auto &i: indices) screenIndices_ << quint32(i); } diff --git a/src/location/maps/qgeomap.cpp b/src/location/maps/qgeomap.cpp index 0c6ce0a7..46a88003 100644 --- a/src/location/maps/qgeomap.cpp +++ b/src/location/maps/qgeomap.cpp @@ -230,6 +230,15 @@ QString QGeoMap::copyrightsStyleSheet() const return QStringLiteral("#copyright-root { background: rgba(255, 255, 255, 128) }"); } +void QGeoMap::setCopyrightVisible(bool visible) +{ + Q_D(QGeoMap); + if (d->m_copyrightVisible == visible) + return; + + d->m_copyrightVisible = visible; +} + QGeoMapPrivate::QGeoMapPrivate(QGeoMappingManagerEngine *engine, QGeoProjection *geoProjection) : QObjectPrivate(), m_geoProjection(geoProjection), @@ -287,4 +296,14 @@ void QGeoMapPrivate::removeMapItem(QDeclarativeGeoMapItemBase *item) Q_UNUSED(item) } +void QGeoMapPrivate::setCopyrightVisible(bool visible) +{ + m_copyrightVisible = visible; +} + +bool QGeoMapPrivate::copyrightVisible() const +{ + return m_copyrightVisible; +} + QT_END_NAMESPACE diff --git a/src/location/maps/qgeomap_p.h b/src/location/maps/qgeomap_p.h index bb7ade55..f465be1d 100644 --- a/src/location/maps/qgeomap_p.h +++ b/src/location/maps/qgeomap_p.h @@ -124,6 +124,8 @@ public: virtual QString copyrightsStyleSheet() const; + virtual void setCopyrightVisible(bool visible); + protected: QGeoMap(QGeoMapPrivate &dd, QObject *parent = 0); void setCameraData(const QGeoCameraData &cameraData); diff --git a/src/location/maps/qgeomap_p_p.h b/src/location/maps/qgeomap_p_p.h index ec498484..2c366610 100644 --- a/src/location/maps/qgeomap_p_p.h +++ b/src/location/maps/qgeomap_p_p.h @@ -90,6 +90,9 @@ protected: virtual void changeCameraData(const QGeoCameraData &oldCameraData) = 0; // called by QGeoMap::setCameraData() virtual void changeActiveMapType(const QGeoMapType mapType) = 0; // called by QGeoMap::setActiveMapType() + virtual void setCopyrightVisible(bool visible); + virtual bool copyrightVisible() const; + protected: QSize m_viewportSize; QGeoProjection *m_geoProjection; @@ -99,6 +102,7 @@ protected: QList<QGeoMapParameter *> m_mapParameters; QList<QDeclarativeGeoMapItemBase *> m_mapItems; QGeoCameraCapabilities m_cameraCapabilities; + bool m_copyrightVisible = true; }; QT_END_NAMESPACE diff --git a/src/location/maps/qgeorouteparserosrmv5.cpp b/src/location/maps/qgeorouteparserosrmv5.cpp index 75daefda..6c1f7c83 100644 --- a/src/location/maps/qgeorouteparserosrmv5.cpp +++ b/src/location/maps/qgeorouteparserosrmv5.cpp @@ -953,7 +953,7 @@ QUrl QGeoRouteParserOsrmV5Private::requestUrl(const QGeoRouteRequest &request, c foreach (const QGeoCoordinate &c, request.waypoints()) { if (notFirst) routingUrl.append(QLatin1Char(';')); - routingUrl.append(QString::number(c.longitude())).append(QLatin1Char(',')).append(QString::number(c.latitude())); + routingUrl.append(QString::number(c.longitude(), 'f', 7)).append(QLatin1Char(',')).append(QString::number(c.latitude(), 'f', 7)); ++notFirst; } diff --git a/src/location/maps/qgeotiledmap.cpp b/src/location/maps/qgeotiledmap.cpp index 0eeb189d..815a8b99 100644 --- a/src/location/maps/qgeotiledmap.cpp +++ b/src/location/maps/qgeotiledmap.cpp @@ -143,6 +143,17 @@ void QGeoTiledMap::clearData() d->m_mapScene->clearTexturedTiles(); } +void QGeoTiledMap::setCopyrightVisible(bool visible) +{ + Q_D(QGeoTiledMap); + if (visible == d->m_copyrightVisible) + return; + + QGeoMap::setCopyrightVisible(visible); + if (visible) + evaluateCopyrights(d->m_mapScene->visibleTiles()); +} + void QGeoTiledMap::clearScene(int mapId) { Q_D(QGeoTiledMap); @@ -319,7 +330,7 @@ void QGeoTiledMapPrivate::updateScene() bool newTilesIntroduced = !m_mapScene->visibleTiles().contains(tiles); m_mapScene->setVisibleTiles(tiles); - if (newTilesIntroduced) + if (newTilesIntroduced && m_copyrightVisible) q->evaluateCopyrights(tiles); // don't request tiles that are already built and textured @@ -381,7 +392,8 @@ void QGeoTiledMapPrivate::changeViewportSize(const QSize& size) m_cache->setMinTextureUsage(newSize); } - q->evaluateCopyrights(m_visibleTiles->createTiles()); + if (m_copyrightVisible) + q->evaluateCopyrights(m_mapScene->visibleTiles()); updateScene(); } diff --git a/src/location/maps/qgeotiledmap_p.h b/src/location/maps/qgeotiledmap_p.h index b709cb57..89dd1285 100644 --- a/src/location/maps/qgeotiledmap_p.h +++ b/src/location/maps/qgeotiledmap_p.h @@ -87,6 +87,8 @@ public: void prefetchData() Q_DECL_OVERRIDE; void clearData() Q_DECL_OVERRIDE; + void setCopyrightVisible(bool visible) override; + public Q_SLOTS: virtual void clearScene(int mapId); diff --git a/src/plugins/position/corelocation/corelocation.pro b/src/plugins/position/corelocation/corelocation.pro index 947a1d0c..85a5aaed 100644 --- a/src/plugins/position/corelocation/corelocation.pro +++ b/src/plugins/position/corelocation/corelocation.pro @@ -1,6 +1,6 @@ TARGET = qtposition_cl -QT = core positioning +QT = core core-private positioning OBJECTIVE_SOURCES += \ qgeopositioninfosource_cl.mm \ diff --git a/src/plugins/position/corelocation/qgeopositioninfosource_cl.mm b/src/plugins/position/corelocation/qgeopositioninfosource_cl.mm index 3fac056e..94c5b807 100644 --- a/src/plugins/position/corelocation/qgeopositioninfosource_cl.mm +++ b/src/plugins/position/corelocation/qgeopositioninfosource_cl.mm @@ -40,6 +40,7 @@ #include <QTimerEvent> #include <QDebug> #include <QtCore/qglobal.h> +#include <QtCore/private/qglobal_p.h> #include "qgeopositioninfosource_cl_p.h" @@ -141,15 +142,17 @@ bool QGeoPositionInfoSourceCL::enableLocationManager() if (!m_locationManager) { m_locationManager = [[CLLocationManager alloc] init]; -#ifdef Q_OS_IOS - NSDictionary<NSString *, id> *infoDict = [[NSBundle mainBundle] infoDictionary]; - if (id value = [infoDict objectForKey:@"UIBackgroundModes"]) { - if ([value isKindOfClass:[NSArray class]]) { - NSArray *modes = static_cast<NSArray *>(value); - for (id mode in modes) { - if ([@"location" isEqualToString:mode]) { - m_locationManager.allowsBackgroundLocationUpdates = YES; - break; +#if defined(Q_OS_IOS) || defined(Q_OS_WATCHOS) + if (__builtin_available(watchOS 4.0, *)) { + NSDictionary<NSString *, id> *infoDict = [[NSBundle mainBundle] infoDictionary]; + if (id value = [infoDict objectForKey:@"UIBackgroundModes"]) { + if ([value isKindOfClass:[NSArray class]]) { + NSArray *modes = static_cast<NSArray *>(value); + for (id mode in modes) { + if ([@"location" isEqualToString:mode]) { + m_locationManager.allowsBackgroundLocationUpdates = YES; + break; + } } } } @@ -162,10 +165,12 @@ bool QGeoPositionInfoSourceCL::enableLocationManager() // These two methods are new in iOS 8. They require NSLocationAlwaysUsageDescription // and NSLocationWhenInUseUsageDescription to be set in Info.plist to work (methods are // noop if there are no such entries in plist). - if ([m_locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) - [m_locationManager performSelector:@selector(requestAlwaysAuthorization)]; - if ([m_locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) - [m_locationManager performSelector:@selector(requestWhenInUseAuthorization)]; +#ifndef Q_OS_MACOS +#ifndef Q_OS_TVOS + [m_locationManager requestAlwaysAuthorization]; +#endif + [m_locationManager requestWhenInUseAuthorization]; +#endif } return (m_locationManager != 0); diff --git a/src/positioning/doc/src/qtpositioning.qdoc b/src/positioning/doc/src/qtpositioning.qdoc index 81de8bec..2a966b19 100644 --- a/src/positioning/doc/src/qtpositioning.qdoc +++ b/src/positioning/doc/src/qtpositioning.qdoc @@ -66,7 +66,9 @@ Currently the API is supported on \l {Qt for Android}{Android}, \l {Qt for iOS}{ \l {Qt for Linux/X11}{Linux} (using \l {http://www.freedesktop.org/wiki/Software/GeoClue}{GeoClue version 0.12.99}), \l {Qt for Windows}{Windows} (with GPS receivers exposed as a serial port providing NMEA sentences), -and \l {Qt for WinRT}{WinRT} (using Windows.Devices.Geolocation). +and \l {Qt for WinRT}{WinRT} (using Windows.Devices.Geolocation). Note that the \l {Qt for WinRT}{WinRT} +implementation can also be used in Win32 Desktop uses cases if the underlying platform is Windows 10 +or later. \section1 Overview diff --git a/src/positioning/positioning.pro b/src/positioning/positioning.pro index d02237be..4bca41e0 100644 --- a/src/positioning/positioning.pro +++ b/src/positioning/positioning.pro @@ -60,6 +60,7 @@ PRIVATE_HEADERS += \ qlocationdata_simulator_p.h \ qdoublematrix4x4_p.h \ qgeopath_p.h \ + qgeopositioninfo_p.h \ qclipperutils_p.h SOURCES += \ diff --git a/src/positioning/qgeopositioninfo.cpp b/src/positioning/qgeopositioninfo.cpp index 84b7fa16..71e363d1 100644 --- a/src/positioning/qgeopositioninfo.cpp +++ b/src/positioning/qgeopositioninfo.cpp @@ -37,7 +37,7 @@ ** ****************************************************************************/ #include "qgeopositioninfo.h" - +#include "qgeopositioninfo_p.h" #include <QHash> #include <QDebug> #include <QDataStream> @@ -47,14 +47,6 @@ QT_BEGIN_NAMESPACE -class QGeoPositionInfoPrivate -{ -public: - QDateTime timestamp; - QGeoCoordinate coord; - QHash<QGeoPositionInfo::Attribute, qreal> doubleAttribs; -}; - /*! \class QGeoPositionInfo \inmodule QtPositioning @@ -106,9 +98,12 @@ QGeoPositionInfo::QGeoPositionInfo(const QGeoCoordinate &coordinate, const QDate Creates a QGeoPositionInfo with the values of \a other. */ QGeoPositionInfo::QGeoPositionInfo(const QGeoPositionInfo &other) - : d(new QGeoPositionInfoPrivate) + : d(other.d->clone()) +{ +} + +QGeoPositionInfo::QGeoPositionInfo(QGeoPositionInfoPrivate &dd) : d(&dd) { - operator=(other); } /*! @@ -127,9 +122,12 @@ QGeoPositionInfo &QGeoPositionInfo::operator=(const QGeoPositionInfo & other) if (this == &other) return *this; - d->timestamp = other.d->timestamp; - d->coord = other.d->coord; - d->doubleAttribs = other.d->doubleAttribs; + delete d; + d = other.d->clone(); + +// d->timestamp = other.d->timestamp; +// d->coord = other.d->coord; +// d->doubleAttribs = other.d->doubleAttribs; return *this; } @@ -140,9 +138,7 @@ QGeoPositionInfo &QGeoPositionInfo::operator=(const QGeoPositionInfo & other) */ bool QGeoPositionInfo::operator==(const QGeoPositionInfo &other) const { - return d->timestamp == other.d->timestamp - && d->coord == other.d->coord - && d->doubleAttribs == other.d->doubleAttribs; + return *d == *other.d; } /*! @@ -356,4 +352,27 @@ QDataStream &operator>>(QDataStream &stream, QGeoPositionInfo &info) } #endif +QGeoPositionInfoPrivate::~QGeoPositionInfoPrivate() +{ + +} + +QGeoPositionInfoPrivate *QGeoPositionInfoPrivate::clone() const +{ + return new QGeoPositionInfoPrivate(*this); +} + +bool QGeoPositionInfoPrivate::operator==(const QGeoPositionInfoPrivate &other) const +{ + return timestamp == other.timestamp + && coord == other.coord + && doubleAttribs == other.doubleAttribs; +} + +QGeoPositionInfoPrivate *QGeoPositionInfoPrivate::getPimpl(const QGeoPositionInfo &info) +{ + return info.d; +} + QT_END_NAMESPACE + diff --git a/src/positioning/qgeopositioninfo.h b/src/positioning/qgeopositioninfo.h index 9f43fe8e..77d44970 100644 --- a/src/positioning/qgeopositioninfo.h +++ b/src/positioning/qgeopositioninfo.h @@ -64,6 +64,7 @@ public: QGeoPositionInfo(); QGeoPositionInfo(const QGeoCoordinate &coordinate, const QDateTime &updateTime); QGeoPositionInfo(const QGeoPositionInfo &other); + QGeoPositionInfo(QGeoPositionInfoPrivate &dd); ~QGeoPositionInfo(); QGeoPositionInfo &operator=(const QGeoPositionInfo &other); @@ -95,6 +96,7 @@ private: friend Q_POSITIONING_EXPORT QDataStream &operator>>(QDataStream &stream, QGeoPositionInfo &info); #endif QGeoPositionInfoPrivate *d; + friend class QGeoPositionInfoPrivate; }; #ifndef QT_NO_DEBUG_STREAM diff --git a/src/positioning/qgeopositioninfo_p.h b/src/positioning/qgeopositioninfo_p.h new file mode 100644 index 00000000..cc4a9f3d --- /dev/null +++ b/src/positioning/qgeopositioninfo_p.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QGEOPOSITIONINFO_P_H +#define QGEOPOSITIONINFO_P_H + +#include <QtPositioning/private/qpositioningglobal_p.h> +#include "qgeopositioninfo.h" +#include <QHash> +#include <QDateTime> +#include <QtPositioning/qgeocoordinate.h> + +QT_BEGIN_NAMESPACE + +class Q_POSITIONING_PRIVATE_EXPORT QGeoPositionInfoPrivate +{ +public: + virtual ~QGeoPositionInfoPrivate(); + virtual QGeoPositionInfoPrivate *clone() const; + + virtual bool operator==(const QGeoPositionInfoPrivate &other) const; + + QDateTime timestamp; + QGeoCoordinate coord; + QHash<QGeoPositionInfo::Attribute, qreal> doubleAttribs; + + static QGeoPositionInfoPrivate *getPimpl(const QGeoPositionInfo &info); +}; + +QT_END_NAMESPACE + +#endif // QGEOPOSITIONINFO_P_H diff --git a/src/positioning/qnmeapositioninfosource.cpp b/src/positioning/qnmeapositioninfosource.cpp index 75fa9645..282f30b4 100644 --- a/src/positioning/qnmeapositioninfosource.cpp +++ b/src/positioning/qnmeapositioninfosource.cpp @@ -39,17 +39,93 @@ ** ****************************************************************************/ #include "qnmeapositioninfosource_p.h" +#include "qgeopositioninfo_p.h" #include "qlocationutils_p.h" #include <QIODevice> #include <QBasicTimer> #include <QTimerEvent> #include <QTimer> +#include <array> +#include <QDebug> #include <QtCore/QtNumeric> QT_BEGIN_NAMESPACE +#define USE_NMEA_PIMPL 0 + +#if USE_NMEA_PIMPL +class QGeoPositionInfoPrivateNmea : public QGeoPositionInfoPrivate +{ +public: + virtual ~QGeoPositionInfoPrivateNmea(); + virtual QGeoPositionInfoPrivate *clone() const; + + QList<QByteArray> nmeaSentences; +}; + + +QGeoPositionInfoPrivateNmea::~QGeoPositionInfoPrivateNmea() +{ + +} + +QGeoPositionInfoPrivate *QGeoPositionInfoPrivateNmea::clone() const +{ + return new QGeoPositionInfoPrivateNmea(*this); +} +#else +typedef QGeoPositionInfoPrivate QGeoPositionInfoPrivateNmea; +#endif + +static void mergePositions(QGeoPositionInfo &dst, const QGeoPositionInfo &src, QByteArray nmeaSentence) +{ +#if USE_NMEA_PIMPL + QGeoPositionInfoPrivateNmea *dstPimpl = static_cast<QGeoPositionInfoPrivateNmea *>(QGeoPositionInfoPrivate::getPimpl(dst)); + dstPimpl->nmeaSentences.append(nmeaSentence); +#else + Q_UNUSED(nmeaSentence) +#endif + + QGeoCoordinate c = dst.coordinate(); + if (!qIsNaN(src.coordinate().latitude())) + c.setLatitude(src.coordinate().latitude()); + if (!qIsNaN(src.coordinate().longitude())) + c.setLongitude(src.coordinate().longitude()); + if (!qIsNaN(src.coordinate().altitude())) + c.setAltitude(src.coordinate().altitude()); + dst.setCoordinate(c); + + if (!dst.timestamp().date().isValid() && src.timestamp().isValid()) // time was supposed to be set/the same already. Date can be overwritten. + dst.setTimestamp(src.timestamp()); + + static Q_DECL_CONSTEXPR std::array<QGeoPositionInfo::Attribute, 6> attrs { + { QGeoPositionInfo::GroundSpeed + ,QGeoPositionInfo::HorizontalAccuracy + ,QGeoPositionInfo::VerticalAccuracy + ,QGeoPositionInfo::Direction + ,QGeoPositionInfo::VerticalSpeed + ,QGeoPositionInfo::MagneticVariation} }; + for (const auto a: attrs) { + if (src.hasAttribute(a)) + dst.setAttribute(a, src.attribute(a)); + } + + +} + +static qint64 msecsTo(const QDateTime &from, const QDateTime &to) +{ + if (!from.time().isValid() || !to.time().isValid()) + return 0; + + if (!from.date().isValid() || !to.date().isValid()) // use only time + return from.time().msecsTo(to.time()); + + return from.msecsTo(to); +} + QNmeaRealTimeReader::QNmeaRealTimeReader(QNmeaPositionInfoSourcePrivate *sourcePrivate) : QNmeaReader(sourcePrivate) { @@ -107,25 +183,111 @@ void QNmeaSimulatedReader::readAvailableData() } } -bool QNmeaSimulatedReader::setFirstDateTime() +static int processSentence(QGeoPositionInfo &info, + QByteArray &m_nextLine, + QNmeaPositionInfoSourcePrivate *m_proxy, + QQueue<QPendingGeoPositionInfo> &m_pendingUpdates, + bool &hasFix) { - // find the first update with valid date and time - QGeoPositionInfo update; - bool hasFix = false; - while (m_proxy->m_device->bytesAvailable() > 0) { - char buf[1024]; - qint64 size = m_proxy->m_device->readLine(buf, sizeof(buf)); + int timeToNextUpdate = -1; + QDateTime prevTs; + if (m_pendingUpdates.size() > 0) + prevTs = m_pendingUpdates.head().info.timestamp(); + + // find the next update with a valid time (as long as the time is valid, + // we can calculate when the update should be emitted) + while (m_nextLine.size() || (m_proxy->m_device && m_proxy->m_device->bytesAvailable() > 0)) { + char static_buf[1024]; + char *buf = static_buf; + QByteArray nextLine; + qint64 size = 0; + if (m_nextLine.size()) { + // Read something in the previous call, but TS was later. + size = m_nextLine.size(); + nextLine = m_nextLine; + m_nextLine.clear(); + buf = nextLine.data(); + } else { + size = m_proxy->m_device->readLine(buf, sizeof(static_buf)); + } + if (size <= 0) continue; - bool ok = m_proxy->parsePosInfoFromNmeaData(buf, size, &update, &hasFix); - if (ok && update.timestamp().isValid()) { - QPendingGeoPositionInfo pending; - pending.info = update; - pending.hasFix = hasFix; - m_pendingUpdates.enqueue(pending); - return true; + + const QTime infoTime = info.timestamp().time(); // if info has been set, time must be valid. + const QDate infoDate = info.timestamp().date(); // this one might not be valid, as some sentences do not contain it + + /* + Packets containing time information are GGA, RMC, ZDA, GLL: + + GGA : GPS fix data - only time + GLL : geographic latitude and longitude - only time + RMC : recommended minimum FPOS/transit data - date/time + ZDA : only timestamp - date/time + + QLocationUtils is currently also capable of parsing VTG and GSA sentences: + + VTG: containing Track made good and ground speed + GSA: overall satellite data + + Since these sentences contain no timestamp, their content will be merged with the content + from any prior sentence that had timestamp info, if any is available. + */ + + QGeoPositionInfoPrivateNmea *pimpl = new QGeoPositionInfoPrivateNmea; + QGeoPositionInfo pos(*pimpl); + if (m_proxy->parsePosInfoFromNmeaData(buf, size, &pos, &hasFix)) { + // Date may or may not be valid, as some packets do not have date. + // If date isn't valid, match is performed on time only. + // Hence, make sure that packet blocks are generated with + // the sentences containing the full timestamp (e.g., GPRMC) *first* ! + if (infoTime.isValid()) { + if (pos.timestamp().time().isValid()) { + if (infoTime != pos.timestamp().time() || infoDate != pos.timestamp().date()) { + // Effectively read data for different update, so copy buf into m_nextLine + m_nextLine = QByteArray(buf, size); + break; + } else { + // timestamps match -- merge into info + mergePositions(info, pos, QByteArray(buf, size)); + } + } else { + // no timestamp available -- merge into info + mergePositions(info, pos, QByteArray(buf, size)); + } + } else { + // there was no info with valid TS. Overwrite with whatever is parsed. +#if USE_NMEA_PIMPL + pimpl->nmeaSentences.append(QByteArray(buf, size)); +#endif + info = pos; + } + + if (prevTs.time().isValid()) { + timeToNextUpdate = msecsTo(prevTs, info.timestamp()); + if (timeToNextUpdate < 0) // Somehow parsing expired packets, reset info + info = QGeoPositionInfo(*new QGeoPositionInfoPrivateNmea); + } } } + + return timeToNextUpdate; +} + +bool QNmeaSimulatedReader::setFirstDateTime() +{ + // find the first update with valid date and time + QGeoPositionInfo info(*new QGeoPositionInfoPrivateNmea); + bool hasFix = false; + processSentence(info, m_nextLine, m_proxy, m_pendingUpdates, hasFix); + + if (info.timestamp().time().isValid()) { // NMEA may have sentences with only time and no date. These would generate invalid positions + QPendingGeoPositionInfo pending; + pending.info = info; + pending.hasFix = hasFix; + m_pendingUpdates.enqueue(pending); + return true; + } return false; } @@ -149,37 +311,10 @@ void QNmeaSimulatedReader::timerEvent(QTimerEvent *event) void QNmeaSimulatedReader::processNextSentence() { - QGeoPositionInfo info; + QGeoPositionInfo info(*new QGeoPositionInfoPrivateNmea); bool hasFix = false; - int timeToNextUpdate = -1; - QTime prevTime; - if (m_pendingUpdates.size() > 0) - prevTime = m_pendingUpdates.head().info.timestamp().time(); - - // find the next update with a valid time (as long as the time is valid, - // we can calculate when the update should be emitted) - while (m_proxy->m_device && m_proxy->m_device->bytesAvailable() > 0) { - char buf[1024]; - qint64 size = m_proxy->m_device->readLine(buf, sizeof(buf)); - if (size <= 0) - continue; - if (m_proxy->parsePosInfoFromNmeaData(buf, size, &info, &hasFix)) { - QTime time = info.timestamp().time(); - if (time.isValid()) { - if (!prevTime.isValid()) { - timeToNextUpdate = 0; - break; - } - timeToNextUpdate = prevTime.msecsTo(time); - if (timeToNextUpdate >= 0) - break; - } else { - timeToNextUpdate = 0; - break; - } - } - } + int timeToNextUpdate = processSentence(info, m_nextLine, m_proxy, m_pendingUpdates, hasFix); if (timeToNextUpdate < 0) return; diff --git a/src/positioning/qnmeapositioninfosource_p.h b/src/positioning/qnmeapositioninfosource_p.h index 056de90d..6efb5648 100644 --- a/src/positioning/qnmeapositioninfosource_p.h +++ b/src/positioning/qnmeapositioninfosource_p.h @@ -168,6 +168,7 @@ private: void processNextSentence(); QQueue<QPendingGeoPositionInfo> m_pendingUpdates; + QByteArray m_nextLine; int m_currTimerId; bool m_hasValidDateTime; }; diff --git a/tests/auto/qnmeapositioninfosource/tst_qnmeapositioninfosource.cpp b/tests/auto/qnmeapositioninfosource/tst_qnmeapositioninfosource.cpp index 8305dc1d..c77e465d 100644 --- a/tests/auto/qnmeapositioninfosource/tst_qnmeapositioninfosource.cpp +++ b/tests/auto/qnmeapositioninfosource/tst_qnmeapositioninfosource.cpp @@ -212,7 +212,7 @@ void tst_QNmeaPositionInfoSource::beginWithBufferedData_data() QTest::newRow("requestUpdate(), 1 update in buffer") << dateTimes << RequestUpdatesMethod; for (int i=1; i<3; i++) - dateTimes << dateTimes[0].addDays(i); + dateTimes << dateTimes[0].addMSecs(i * 100); QTest::newRow("startUpdates(), multiple updates in buffer") << dateTimes << StartUpdatesMethod; QTest::newRow("requestUpdate(), multiple updates in buffer") << dateTimes << RequestUpdatesMethod; } @@ -356,8 +356,10 @@ void tst_QNmeaPositionInfoSource::startUpdates_waitForValidDateTime() QNmeaPositionInfoSourceProxy *proxy = static_cast<QNmeaPositionInfoSourceProxy*>(factory.createProxy(&source)); QSignalSpy spy(proxy->source(), SIGNAL(positionUpdated(QGeoPositionInfo))); + QObject::connect(proxy->source(), &QNmeaPositionInfoSource::positionUpdated, [](const QGeoPositionInfo &info) { + qDebug() << info.timestamp(); + }); proxy->source()->startUpdates(); - proxy->feedBytes(bytes); QTRY_COMPARE(spy.count(), dateTimes.count()); @@ -373,7 +375,7 @@ void tst_QNmeaPositionInfoSource::startUpdates_waitForValidDateTime() if (pInfo.hasAttribute(QGeoPositionInfo::HorizontalAccuracy)) QVERIFY(qFuzzyCompare(pInfo.attribute(QGeoPositionInfo::HorizontalAccuracy), 35.7)); - // Generate GSA sentences have hard coded VDOP of 4.0, which corrisponds to a vertical + // Generated GSA sentences have hard coded VDOP of 4.0, which corrisponds to a vertical // accuracy of 40.8, for the user equivalent range error of 5.1 set above. QCOMPARE(pInfo.hasAttribute(QGeoPositionInfo::VerticalAccuracy), expectVerticalAccuracy[i]); @@ -393,45 +395,54 @@ void tst_QNmeaPositionInfoSource::startUpdates_waitForValidDateTime_data() QByteArray bytes; // should only receive RMC sentence and the GGA sentence *after* it - bytes += QLocationTestUtils::createGgaSentence(dt.addSecs(1).time()).toLatin1(); - bytes += QLocationTestUtils::createRmcSentence(dt.addSecs(2)).toLatin1(); - bytes += QLocationTestUtils::createGgaSentence(dt.addSecs(3).time()).toLatin1(); + bytes += QLocationTestUtils::createGgaSentence(dt.addMSecs(100).time()).toLatin1(); + bytes += QLocationTestUtils::createRmcSentence(dt.addMSecs(200)).toLatin1(); + bytes += QLocationTestUtils::createGgaSentence(dt.addMSecs(300).time()).toLatin1(); QTest::newRow("Feed GGA,RMC,GGA; expect RMC, second GGA only") - << bytes << (QList<QDateTime>() << dt.addSecs(2) << dt.addSecs(3)) - << (QList<bool>() << true << true) + << bytes << (QList<QDateTime>() << dt.addMSecs(200) << dt.addMSecs(300)) + << (QList<bool>() << true << true) // accuracies are currently cached and injected in QGeoPositionInfos that do not have it << (QList<bool>() << false << false); // should not receive ZDA (has no coordinates) but should get the GGA // sentence after it since it got the date/time from ZDA bytes.clear(); - bytes += QLocationTestUtils::createGgaSentence(dt.addSecs(1).time()).toLatin1(); - bytes += QLocationTestUtils::createZdaSentence(dt.addSecs(2)).toLatin1(); - bytes += QLocationTestUtils::createGgaSentence(dt.addSecs(3).time()).toLatin1(); + bytes += QLocationTestUtils::createGgaSentence(dt.addMSecs(100).time()).toLatin1(); + bytes += QLocationTestUtils::createZdaSentence(dt.addMSecs(200)).toLatin1(); + bytes += QLocationTestUtils::createGgaSentence(dt.addMSecs(300).time()).toLatin1(); QTest::newRow("Feed GGA,ZDA,GGA; expect second GGA only") - << bytes << (QList<QDateTime>() << dt.addSecs(3)) + << bytes << (QList<QDateTime>() << dt.addMSecs(300)) << (QList<bool>() << true) << (QList<bool>() << false); // Feed ZDA,GGA,GSA,GGA; expect vertical accuracy from second GGA. bytes.clear(); - bytes += QLocationTestUtils::createZdaSentence(dt.addSecs(1)).toLatin1(); - bytes += QLocationTestUtils::createGgaSentence(dt.addSecs(2).time()).toLatin1(); + bytes += QLocationTestUtils::createZdaSentence(dt.addMSecs(100)).toLatin1(); + bytes += QLocationTestUtils::createGgaSentence(dt.addMSecs(200).time()).toLatin1(); bytes += QLocationTestUtils::createGsaSentence().toLatin1(); - bytes += QLocationTestUtils::createGgaSentence(dt.addSecs(3).time()).toLatin1(); - QTest::newRow("Feed ZDA,GGA,GSA,GGA; expect vertical accuracy from second GGA") - << bytes << (QList<QDateTime>() << dt.addSecs(2) << dt.addSecs(3)) - << (QList<bool>() << true << true) - << (QList<bool>() << false << true); + bytes += QLocationTestUtils::createGgaSentence(dt.addMSecs(300).time()).toLatin1(); + if (m_mode == QNmeaPositionInfoSource::SimulationMode) { + QTest::newRow("Feed ZDA,GGA,GSA,GGA; expect vertical accuracy from second GGA") + << bytes << (QList<QDateTime>() << dt.addMSecs(200) << dt.addMSecs(300)) + << (QList<bool>() << true << true) + << (QList<bool>() << true << true); // First GGA gets VDOP from GSA bundled into previous, as it has no timestamp, second GGA gets the cached value. + } else { + // FixMe: remove else block once NMEA realtime mode supports timestamp-based combination of nmea sentences + QTest::newRow("Feed ZDA,GGA,GSA,GGA; expect vertical accuracy from second GGA") + << bytes << (QList<QDateTime>() << dt.addMSecs(200) << dt.addMSecs(300)) + << (QList<bool>() << true << true) + << (QList<bool>() << false << true); + + } if (m_mode == QNmeaPositionInfoSource::SimulationMode) { // In sim m_mode, should ignore sentence with a date/time before the known date/time // (in real time m_mode, everything is passed on regardless) bytes.clear(); - bytes += QLocationTestUtils::createRmcSentence(dt.addSecs(1)).toLatin1(); - bytes += QLocationTestUtils::createRmcSentence(dt.addSecs(-2)).toLatin1(); - bytes += QLocationTestUtils::createRmcSentence(dt.addSecs(2)).toLatin1(); + bytes += QLocationTestUtils::createRmcSentence(dt.addMSecs(100)).toLatin1(); + bytes += QLocationTestUtils::createRmcSentence(dt.addMSecs(-200)).toLatin1(); + bytes += QLocationTestUtils::createRmcSentence(dt.addMSecs(200)).toLatin1(); QTest::newRow("Feed good RMC, RMC with bad date/time, good RMC; expect first and third RMC only") - << bytes << (QList<QDateTime>() << dt.addSecs(1) << dt.addSecs(2)) + << bytes << (QList<QDateTime>() << dt.addMSecs(100) << dt.addMSecs(200)) << (QList<bool>() << false << false) << (QList<bool>() << false << false); } |