QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
qgsquickpositionkit.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsquickpositionkit.cpp
3  --------------------------------------
4  Date : Dec. 2017
5  Copyright : (C) 2017 Peter Petrik
6  Email : zilolv at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include <memory>
17 
18 #include "qgis.h"
19 #include "qgslogger.h"
20 #include "qgsmessagelog.h"
21 
22 #include "qgsquickpositionkit.h"
23 #include "qgsquickutils.h"
25 
27  : QObject( parent )
28 {
29  connect( this,
31  this,
32  &QgsQuickPositionKit::onSimulatePositionLongLatRadChanged );
33 
35 }
36 
37 QGeoPositionInfoSource *QgsQuickPositionKit::gpsSource()
38 {
39  // this should give us "true" position source
40  // on Linux it comes from Geoclue library
41  std::unique_ptr<QGeoPositionInfoSource> source( QGeoPositionInfoSource::createDefaultSource( nullptr ) );
42  if ( ( !source ) || ( source->error() != QGeoPositionInfoSource::NoError ) )
43  {
44  QgsMessageLog::logMessage( QStringLiteral( "%1 (%2)" )
45  .arg( tr( "Unable to create default GPS Position Source" ) )
46  .arg( QString::number( ( long )source->error() ) )
47  , QStringLiteral( "QgsQuick" )
48  , Qgis::Warning );
49  return nullptr;
50  }
51  else
52  {
53  return source.release();
54  }
55 }
56 
57 QGeoPositionInfoSource *QgsQuickPositionKit::simulatedSource( double longitude, double latitude, double radius )
58 {
59  return new QgsQuickSimulatedPositionSource( this, longitude, latitude, radius );
60 }
61 
62 QGeoPositionInfoSource *QgsQuickPositionKit::source() const
63 {
64  return mSource.get();
65 }
66 
67 void QgsQuickPositionKit::useSimulatedLocation( double longitude, double latitude, double radius )
68 {
69  std::unique_ptr<QGeoPositionInfoSource> source( simulatedSource( longitude, latitude, radius ) );
70  mIsSimulated = true;
71  replacePositionSource( source.release() );
72 }
73 
74 void QgsQuickPositionKit::updateScreenPosition()
75 {
76  if ( !mMapSettings )
77  return;
78 
80  if ( screenPosition != mScreenPosition )
81  {
82  mScreenPosition = screenPosition;
83  emit screenPositionChanged();
84  }
85 }
86 
87 void QgsQuickPositionKit::updateScreenAccuracy()
88 {
89  if ( !mMapSettings )
90  return;
91 
92  double screenAccuracy = calculateScreenAccuracy();
93  if ( !qgsDoubleNear( screenAccuracy, mScreenAccuracy ) )
94  {
95  mScreenAccuracy = screenAccuracy;
96  emit screenAccuracyChanged();
97  }
98 }
99 
101 {
102  QGeoPositionInfoSource *source = gpsSource();
103  mIsSimulated = false;
104  replacePositionSource( source );
105 }
106 
107 void QgsQuickPositionKit::replacePositionSource( QGeoPositionInfoSource *source )
108 {
109  if ( mSource.get() == source )
110  return;
111 
112  if ( mSource )
113  {
114  mSource->disconnect();
115  }
116 
117  mSource.reset( source );
118  emit sourceChanged();
119 
120  if ( mSource )
121  {
122  connect( mSource.get(), &QGeoPositionInfoSource::positionUpdated, this, &QgsQuickPositionKit::onPositionUpdated );
123  connect( mSource.get(), &QGeoPositionInfoSource::updateTimeout, this, &QgsQuickPositionKit::onUpdateTimeout );
124 
125  mSource->startUpdates();
126 
127  QgsDebugMsg( QStringLiteral( "Position source changed: %1" ).arg( mSource->sourceName() ) );
128  }
129 }
130 
132 {
133  return mMapSettings;
134 }
135 
136 void QgsQuickPositionKit::updateProjectedPosition()
137 {
138  if ( !mMapSettings )
139  return;
140 
141  QgsPointXY srcPoint = QgsPointXY( mPosition.x(), mPosition.y() );
142  QgsPointXY projectedPositionXY = QgsQuickUtils::transformPoint(
143  positionCRS(),
144  mMapSettings->destinationCrs(),
145  mMapSettings->transformContext(),
146  srcPoint
147  );
148 
149  QgsPoint projectedPosition( projectedPositionXY );
150  projectedPosition.addZValue( mPosition.z() );
151 
152  if ( projectedPosition != mProjectedPosition )
153  {
154  mProjectedPosition = projectedPosition;
156  }
157 }
158 
159 void QgsQuickPositionKit::onPositionUpdated( const QGeoPositionInfo &info )
160 {
161  bool hasPosition = info.coordinate().isValid();
162  if ( hasPosition != mHasPosition )
163  {
164  mHasPosition = hasPosition;
165  emit hasPositionChanged();
166  }
167 
168  // Calculate position
170  info.coordinate().longitude(),
171  info.coordinate().latitude(),
172  info.coordinate().altitude() ); // can be NaN
173 
174  if ( position != mPosition )
175  {
176  mPosition = position;
177  emit positionChanged();
178  }
179  // calculate accuracy
180  double accuracy;
181  if ( info.hasAttribute( QGeoPositionInfo::HorizontalAccuracy ) )
182  accuracy = info.attribute( QGeoPositionInfo::HorizontalAccuracy );
183  else
184  accuracy = -1;
185  if ( !qgsDoubleNear( accuracy, mAccuracy ) )
186  {
187  mAccuracy = accuracy;
188  emit accuracyChanged();
189  }
190 
191  // calculate direction
192  double direction;
193  if ( info.hasAttribute( QGeoPositionInfo::Direction ) )
194  direction = info.attribute( QGeoPositionInfo::Direction );
195  else
196  direction = -1;
197  if ( !qgsDoubleNear( direction, mDirection ) )
198  {
199  mDirection = direction;
200  emit directionChanged();
201  }
202 
203  // recalculate projected/screen variables
204  onMapSettingsUpdated();
205 }
206 
207 void QgsQuickPositionKit::onMapSettingsUpdated()
208 {
209  updateProjectedPosition();
210 
211  updateScreenAccuracy();
212  updateScreenPosition();
213 }
214 
215 void QgsQuickPositionKit::onSimulatePositionLongLatRadChanged( QVector<double> simulatePositionLongLatRad )
216 {
217  if ( simulatePositionLongLatRad.size() > 2 )
218  {
219  double longitude = simulatePositionLongLatRad[0];
220  double latitude = simulatePositionLongLatRad[1];
221  double radius = simulatePositionLongLatRad[2];
222  QgsDebugMsg( QStringLiteral( "Use simulated position around longlat: %1, %2, %3" ).arg( longitude ).arg( latitude ).arg( radius ) );
223  useSimulatedLocation( longitude, latitude, radius );
224  }
225  else if ( mIsSimulated )
226  {
227  QgsDebugMsg( QStringLiteral( "Switching from simulated to GPS location" ) );
228  useGpsLocation();
229  }
230 }
231 
232 double QgsQuickPositionKit::calculateScreenAccuracy()
233 {
234  if ( !mMapSettings )
235  return 2.0;
236 
237  if ( accuracy() > 0 )
238  {
239  double scpm = QgsQuickUtils::screenUnitsToMeters( mMapSettings, 1 );
240  if ( scpm > 0 )
241  return 2 * ( accuracy() / scpm );
242  else
243  return 2.0;
244  }
245  return 2.0;
246 }
247 
248 void QgsQuickPositionKit::onUpdateTimeout()
249 {
250  if ( mHasPosition )
251  {
252  mHasPosition = false;
253  emit hasPositionChanged();
254  }
255 }
256 
258 {
259  return mScreenPosition;
260 }
261 
263 {
264  return mScreenAccuracy;
265 }
266 
268 {
269  return mSimulatePositionLongLatRad;
270 }
271 
272 void QgsQuickPositionKit::setSimulatePositionLongLatRad( const QVector<double> &simulatePositionLongLatRad )
273 {
274  mSimulatePositionLongLatRad = simulatePositionLongLatRad;
276 }
277 
279 {
281 }
282 
284 {
285  return mProjectedPosition;
286 }
287 
289 {
290  return mHasPosition;
291 }
292 
294 {
295  return mPosition;
296 }
297 
299 {
300  return mAccuracy;
301 }
302 
304 {
306 }
307 
309 {
310  return mDirection;
311 }
312 
314 {
315  return mIsSimulated;
316 }
317 
319 {
320  if ( mMapSettings == mapSettings )
321  return;
322 
323  if ( mMapSettings )
324  {
325  mMapSettings->disconnect();
326  }
327 
328  mMapSettings = mapSettings;
329 
330  if ( mMapSettings )
331  {
332  connect( mMapSettings, &QgsQuickMapSettings::extentChanged, this, &QgsQuickPositionKit::onMapSettingsUpdated );
333  connect( mMapSettings, &QgsQuickMapSettings::destinationCrsChanged, this, &QgsQuickPositionKit::onMapSettingsUpdated );
334  connect( mMapSettings, &QgsQuickMapSettings::mapUnitsPerPixelChanged, this, &QgsQuickPositionKit::onMapSettingsUpdated );
335  connect( mMapSettings, &QgsQuickMapSettings::visibleExtentChanged, this, &QgsQuickPositionKit::onMapSettingsUpdated );
336  connect( mMapSettings, &QgsQuickMapSettings::outputSizeChanged, this, &QgsQuickPositionKit::onMapSettingsUpdated );
337  connect( mMapSettings, &QgsQuickMapSettings::outputDpiChanged, this, &QgsQuickPositionKit::onMapSettingsUpdated );
338  }
339 
340  emit mapSettingsChanged();
341 }
@ Warning
Definition: qgis.h:91
This class represents a coordinate reference system (CRS).
static Q_INVOKABLE QgsCoordinateReferenceSystem fromEpsgId(long epsg)
Creates a CRS from a given EPSG ID.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
A class to represent a 2D point.
Definition: qgspointxy.h:44
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:38
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition: qgspoint.cpp:542
Q_GADGET double x
Definition: qgspoint.h:41
double z
Definition: qgspoint.h:43
double y
Definition: qgspoint.h:42
The QgsQuickMapSettings class encapsulates QgsMapSettings class to offer settings of configuration of...
void extentChanged()
Geographical coordinates of the rectangle that should be rendered.
void outputSizeChanged()
The size of the resulting map image.
Q_INVOKABLE QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
void outputDpiChanged()
Output DPI used for conversion between real world units (e.g.
void destinationCrsChanged()
CRS of destination coordinate reference system.
void visibleExtentChanged()
Returns the actual extent derived from requested extent that takes takes output image size into accou...
void mapUnitsPerPixelChanged()
Returns the distance in geographical coordinates that equals to one pixel in the map.
QgsCoordinateReferenceSystem destinationCrs
CRS of destination coordinate reference system.
Q_INVOKABLE QPointF coordinateToScreen(const QgsPoint &point) const
Convert a map coordinate to screen pixel coordinates.
QgsPoint projectedPosition
GPS position in map coords.
bool isSimulated
GPS position and accuracy is simulated (not real from GPS sensor).
double directionChanged() const
GPS direction, bearing in degrees clockwise from north to direction of travel.
void positionChanged()
GPS position in WGS84 coords.
QGeoPositionInfoSource * source
Internal source of GPS location data.
void simulatePositionLongLatRadChanged(QVector< double > simulatePositionLongLatRad)
Uses of GPS and simulated position and sets its parameters.
double screenAccuracyChanged() const
Screen horizontal accuracy, 2 if not available or resolution is too small.
Q_INVOKABLE void useGpsLocation()
Use real GPS source (not simulated)
Q_INVOKABLE void useSimulatedLocation(double longitude, double latitude, double radius)
Use simulated GPS source.
void hasPositionChanged()
hasPosition changed
void mapSettingsChanged()
Associated map settings.
bool hasPosition
GPS position is available (position property is a valid number).
double screenAccuracy
Screen horizontal accuracy, 2 if not available or resolution is too small.
double accuracyChanged() const
GPS horizontal accuracy in accuracyUnits, -1 if not available.
QgsQuickPositionKit(QObject *parent=nullptr)
Creates new position kit.
Q_INVOKABLE QgsCoordinateReferenceSystem positionCRS() const
Coordinate reference system of position - WGS84 (constant)
QPointF screenPosition
GPS position in device coords (pixels).
void sourceChanged()
Emitted when the internal source of GPS location data has been replaced.
void setSimulatePositionLongLatRad(const QVector< double > &simulatePositionLongLatRad)
Uses of GPS and simulated position and sets its parameters.
void setMapSettings(QgsQuickMapSettings *mapSettings)
Associated map settings.
double direction
GPS direction, bearing in degrees clockwise from north to direction of travel.
void projectedPositionChanged()
GPS position in map coords.
QgsQuickMapSettings * mapSettings
Associated map settings.
QVector< double > simulatePositionLongLatRad
Uses of GPS and simulated position and sets its parameters.
QgsPoint position
GPS position in WGS84 coords.
void screenPositionChanged()
GPS position in device coords (pixels).
double accuracy
GPS horizontal accuracy in accuracyUnits, -1 if not available.
QgsUnitTypes::DistanceUnit accuracyUnits() const
GPS horizontal accuracy units - meters (constant)
static Q_INVOKABLE double screenUnitsToMeters(QgsQuickMapSettings *mapSettings, int baseLengthPixels)
Calculates the distance in meter representing baseLengthPixels pixels on the screen based on the curr...
static Q_INVOKABLE QgsPointXY transformPoint(const QgsCoordinateReferenceSystem &srcCrs, const QgsCoordinateReferenceSystem &destCrs, const QgsCoordinateTransformContext &context, const QgsPointXY &srcPoint)
Transforms point between different crs from QML.
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:68
@ DistanceMeters
Meters.
Definition: qgsunittypes.h:69
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:316
#define QgsDebugMsg(str)
Definition: qgslogger.h:38