QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgsgpslogger.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsgpslogger.cpp
3 -------------------
4 begin : November 2022
5 copyright : (C) 2022 by Nyall Dawson
6 email : nyall dot dawson 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 "qgsgpslogger.h"
17
18#include <memory>
19
20#include "gmath.h"
21#include "qgsgeometry.h"
22#include "qgsgpsconnection.h"
23#include "qgslinestring.h"
24#include "qgspolygon.h"
27#include "qgssettingstree.h"
28
29#include <QTimeZone>
30#include <QTimer>
31
32#include "moc_qgsgpslogger.cpp"
33
36const QgsSettingsEntryString *QgsGpsLogger::settingsTimestampTimeZone = new QgsSettingsEntryString( QStringLiteral( "timestampTimeZone" ), QgsSettingsTree::sTreeGps, QString() );
37const QgsSettingsEntryInteger *QgsGpsLogger::settingsTimeStampFormat = new QgsSettingsEntryInteger( QStringLiteral( "timeStampFormat" ), QgsSettingsTree::sTreeGps, Qt::LocalTime );
40
42
43const QgsSettingsEntryBool *QgsGpsLogger::settingsGpsStoreAttributeInMValues = new QgsSettingsEntryBool( QStringLiteral( "store-attribute-in-m-values" ), QgsSettingsTree::sTreeGps, false, QStringLiteral( "Whether GPS attributes should be stored in geometry m values" ) ) SIP_SKIP;
44
46 : QObject( parent )
47 , mWgs84CRS( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) )
48{
50
51 mLastNmeaPosition.lat = nmea_degree2radian( 0.0 );
52 mLastNmeaPosition.lon = nmea_degree2radian( 0.0 );
53
54 mAcquisitionTimer = std::make_unique<QTimer>( this );
55 mAcquisitionTimer->setSingleShot( true );
56
58
59 connect( mAcquisitionTimer.get(), &QTimer::timeout,
60 this, &QgsGpsLogger::switchAcquisition );
61}
62
67
69{
70 return mConnection;
71}
72
74{
75 if ( mConnection )
76 {
77 disconnect( mConnection, &QgsGpsConnection::stateChanged, this, &QgsGpsLogger::gpsStateChanged );
78 }
79
80 mConnection = connection;
81
82 if ( mConnection )
83 {
84 connect( mConnection, &QgsGpsConnection::stateChanged, this, &QgsGpsLogger::gpsStateChanged );
85 }
86}
87
88void QgsGpsLogger::setEllipsoid( const QString &ellipsoid )
89{
90 mDistanceCalculator.setEllipsoid( ellipsoid );
92}
93
95{
96 mTransformContext = context;
97 mDistanceCalculator.setSourceCrs( mWgs84CRS, mTransformContext );
99}
100
102{
103 return mTransformContext;
104}
105
107{
108 return mDistanceCalculator;
109}
110
111QVector<QgsPoint> QgsGpsLogger::currentTrack() const
112{
113 return mCaptureListWgs84;
114}
115
117{
118 const Qgis::GeometryType geometryType = QgsWkbTypes::geometryType( type );
119 const QVector< QgsPoint > captureListWgs84 = currentTrack();
120 if ( geometryType == Qgis::GeometryType::Line && captureListWgs84.size() < 2 )
121 {
122 error = tr( "Creating a line feature requires a track with at least two vertices." );
123 return QgsGeometry();
124 }
125 else if ( geometryType == Qgis::GeometryType::Polygon && captureListWgs84.size() < 3 )
126 {
127 error = tr( "Creating a polygon feature requires a track with at least three vertices." );
128 return QgsGeometry();
129 }
130
131 const bool is3D = QgsWkbTypes::hasZ( type );
132 const bool isMeasure = QgsWkbTypes::hasM( type );
133 switch ( geometryType )
134 {
136 {
137 const QgsPointXY pointXYWgs84 = lastPosition();
138
139 QgsGeometry g;
140 if ( is3D )
141 g = QgsGeometry( new QgsPoint( pointXYWgs84.x(), pointXYWgs84.y(), lastElevation() ) );
142 else
143 g = QgsGeometry::fromPointXY( pointXYWgs84 );
144
145 if ( isMeasure )
146 g.get()->addMValue( lastMValue() );
147
148 if ( QgsWkbTypes::isMultiType( type ) )
150
151 return g;
152 }
153
156 {
157 QgsGeometry g;
158
159 auto ringWgs84 = std::make_unique<QgsLineString>( captureListWgs84 );
160 if ( !is3D )
161 ringWgs84->dropZValue();
162 if ( !isMeasure )
163 ringWgs84->dropMValue();
164
165 if ( geometryType == Qgis::GeometryType::Line )
166 {
167 g = QgsGeometry( ringWgs84.release() );
168 if ( QgsWkbTypes::isMultiType( type ) )
170 }
171 else if ( geometryType == Qgis::GeometryType::Polygon )
172 {
173 ringWgs84->close();
174 auto polygon = std::make_unique<QgsPolygon>();
175 polygon->setExteriorRing( ringWgs84.release() );
176
177 g = QgsGeometry( polygon.release() );
178
179 if ( QgsWkbTypes::isMultiType( type ) )
181 }
182 return g;
183 }
184
187 break;
188 }
189 return QgsGeometry();
190}
191
193{
194 return mLastGpsPositionWgs84;
195}
196
198{
199 return mLastElevation;
200}
201
203{
204 return mLastMValue;
205}
206
208{
210
211 const bool trackWasEmpty = mCaptureListWgs84.isEmpty();
212 mCaptureListWgs84.clear();
214 mTrackStartTime = QDateTime();
215
216 if ( !trackWasEmpty )
217 emit trackIsEmptyChanged( true );
218
219 emit trackReset();
220}
221
223{
224 int acquisitionInterval = 0;
226 {
227 acquisitionInterval = static_cast< int >( QgsGpsConnection::settingGpsAcquisitionInterval->value() );
228 mDistanceThreshold = QgsGpsConnection::settingGpsDistanceThreshold->value();
229 mApplyLeapSettings = QgsGpsConnection::settingGpsApplyLeapSecondsCorrection->value();
230 mLeapSeconds = static_cast< int >( QgsGpsConnection::settingGpsLeapSeconds->value() );
233 mOffsetFromUtc = static_cast< int >( QgsGpsConnection::settingsGpsTimeStampOffsetFromUtc->value() );
234
235 mStoreAttributeInMValues = settingsGpsStoreAttributeInMValues->value();
236 mMValueComponent = settingsGpsMValueComponent->value();
237 }
238 else
239 {
240 acquisitionInterval = QgsGpsLogger::settingsAcquisitionInterval->value();
241 mDistanceThreshold = QgsGpsLogger::settingsDistanceThreshold->value();
242 mApplyLeapSettings = QgsGpsLogger::settingsApplyLeapSeconds->value();
243 mLeapSeconds = QgsGpsLogger::settingsLeapSecondsCorrection->value();
244
245 switch ( QgsGpsLogger::settingsTimeStampFormat->value() )
246 {
247 case 0:
248 mTimeStampSpec = Qt::TimeSpec::LocalTime;
249 break;
250
251 case 1:
252 mTimeStampSpec = Qt::TimeSpec::UTC;
253 break;
254
255 case 2:
256 mTimeStampSpec = Qt::TimeSpec::TimeZone;
257 break;
258 }
259 mTimeZone = QgsGpsLogger::settingsTimestampTimeZone->value();
260 }
261
262 mAcquisitionInterval = acquisitionInterval * 1000;
263 if ( mAcquisitionTimer->isActive() )
264 mAcquisitionTimer->stop();
265 mAcquisitionEnabled = true;
266
267 switchAcquisition();
268}
269
271{
272 QVector<QgsPointXY> points;
273 QgsGeometry::convertPointList( mCaptureListWgs84, points );
274 try
275 {
276 return mDistanceCalculator.measureLine( points );
277 }
278 catch ( QgsCsException & )
279 {
280 // TODO report errors to user
281 QgsDebugError( QStringLiteral( "An error occurred while calculating length" ) );
282 return 0;
283 }
284}
285
287{
288 if ( mCaptureListWgs84.empty() )
289 return 0;
290
291 try
292 {
293 return mDistanceCalculator.measureLine( { QgsPointXY( mCaptureListWgs84.constFirst() ), QgsPointXY( mCaptureListWgs84.constLast() )} );
294 }
295 catch ( QgsCsException & )
296 {
297 // TODO report errors to user
298 QgsDebugError( QStringLiteral( "An error occurred while calculating length" ) );
299 return 0;
300 }
301}
302
304{
305 if ( !mConnection )
306 return QVariant();
307
308 switch ( component )
309 {
323 return mConnection->currentGPSInformation().componentValue( component );
324
326 return lastTimestamp();
327
329 return totalTrackLength();
331 return trackDistanceFromStart();
333 return trackStartTime();
335 return lastTimestamp();
336
338 try
339 {
340 return mPreviousTrackPoint.isEmpty() ? QVariant() : distanceArea().measureLine( mPreviousTrackPoint, lastPosition() );
341 }
342 catch ( QgsCsException & )
343 {
344 // TODO report errors to user
345 QgsDebugError( QStringLiteral( "An error occurred while calculating length" ) );
346 return 0;
347 }
348
350 return mPreviousTrackPointTime.isValid() ? static_cast< double >( mPreviousTrackPointTime.msecsTo( lastTimestamp() ) ) / 1000 : QVariant();
351 }
353}
354
355void QgsGpsLogger::switchAcquisition()
356{
357 if ( mAcquisitionInterval > 0 )
358 {
359 if ( mAcquisitionEnabled )
360 mAcquisitionTimer->start( mAcquisitionInterval );
361 else
362 //wait only acquisitionInterval/10 for new valid data
363 mAcquisitionTimer->start( mAcquisitionInterval / 10 );
364 // anyway switch to enabled / disabled acquisition
365 mAcquisitionEnabled = !mAcquisitionEnabled;
366 }
367}
368
369void QgsGpsLogger::gpsStateChanged( const QgsGpsInformation &info )
370{
372 return;
373
374 const bool validFlag = info.isValid();
375 QgsPointXY newLocationWgs84;
376 nmeaPOS newNmeaPosition;
377 double newAlt = 0.0;
378 if ( validFlag )
379 {
380 newLocationWgs84 = QgsPointXY( info.longitude, info.latitude );
381 newNmeaPosition.lat = nmea_degree2radian( info.latitude );
382 newNmeaPosition.lon = nmea_degree2radian( info.longitude );
383 newAlt = info.elevation;
384
385 if ( info.utcDateTime.isValid() )
386 {
387 mLastTime = info.utcDateTime;
388 }
389
390 switch ( mMValueComponent )
391 {
404 {
405 const QVariant value = info.componentValue( mMValueComponent );
406 mLastMValue = value.isValid() ? info.componentValue( mMValueComponent ).toDouble() : std::numeric_limits< double >::quiet_NaN();
407 break;
408 }
409
411 mLastMValue = static_cast< double >( info.utcDateTime.toMSecsSinceEpoch() );
412 break;
413
421 // not possible
422 break;
423 }
424 }
425 else
426 {
427 newLocationWgs84 = mLastGpsPositionWgs84;
428 newNmeaPosition = mLastNmeaPosition;
429 newAlt = mLastElevation;
430 }
431 if ( !mAcquisitionEnabled || ( nmea_distance( &newNmeaPosition, &mLastNmeaPosition ) < mDistanceThreshold ) )
432 {
433 // do not update position if update is disabled by timer or distance is under threshold
434 newLocationWgs84 = mLastGpsPositionWgs84;
435
436 }
437 if ( validFlag && mAcquisitionEnabled )
438 {
439 // position updated by valid data, reset timer
440 switchAcquisition();
441 }
442
443 // Avoid adding track vertices when we haven't moved
444 if ( mLastGpsPositionWgs84 != newLocationWgs84 )
445 {
446 mLastGpsPositionWgs84 = newLocationWgs84;
447 mLastNmeaPosition = newNmeaPosition;
448 mLastElevation = newAlt;
449
450 if ( mAutomaticallyAddTrackVertices )
451 {
453 }
454 }
455
456 emit stateChanged( info );
457
458 mPreviousTrackPointTime = lastTimestamp();
459 mPreviousTrackPoint = mLastGpsPositionWgs84;
460}
461
463{
464 QgsPoint pointWgs84 = QgsPoint( mLastGpsPositionWgs84.x(), mLastGpsPositionWgs84.y(), mLastElevation );
465
466 if ( mStoreAttributeInMValues )
467 {
468 pointWgs84.addMValue( mLastMValue );
469 }
470
471 const bool trackWasEmpty = mCaptureListWgs84.empty();
472 mCaptureListWgs84.push_back( pointWgs84 );
473
474 emit trackVertexAdded( pointWgs84 );
475
476 if ( trackWasEmpty )
477 {
478 mTrackStartTime = lastTimestamp();
479 emit trackIsEmptyChanged( false );
480 }
481}
482
484{
485 return mAutomaticallyAddTrackVertices;
486}
487
489{
490 mAutomaticallyAddTrackVertices = enabled;
491}
492
494{
495 if ( !mLastTime.isValid() )
496 return QDateTime();
497
498 QDateTime time = mLastTime;
499
500 // Time from GPS is UTC time
501 time.setTimeSpec( Qt::UTC );
502 // Apply leap seconds correction
503 if ( mApplyLeapSettings && mLeapSeconds != 0 )
504 {
505 time = time.addSecs( mLeapSeconds );
506 }
507 // Desired format
508 if ( mTimeStampSpec != Qt::TimeSpec::OffsetFromUTC )
509 time = time.toTimeSpec( mTimeStampSpec );
510
511 if ( mTimeStampSpec == Qt::TimeSpec::TimeZone )
512 {
513#if QT_FEATURE_timezone > 0
514 // Get timezone from the combo
515 const QTimeZone destTz( mTimeZone.toUtf8() );
516 if ( destTz.isValid() )
517 {
518 time = time.toTimeZone( destTz );
519 }
520#else
521 QgsDebugError( QStringLiteral( "Qt is built without timezone support, cannot convert GPS timestamps to specified timezone." ) );
522#endif
523 }
524 else if ( mTimeStampSpec == Qt::TimeSpec::LocalTime )
525 {
526 time = time.toLocalTime();
527 }
528 else if ( mTimeStampSpec == Qt::TimeSpec::OffsetFromUTC )
529 {
530 time = time.toOffsetFromUtc( mOffsetFromUtc );
531 }
532 else if ( mTimeStampSpec == Qt::TimeSpec::UTC )
533 {
534 // Do nothing: we are already in UTC
535 }
536
537 return time;
538}
539
541{
542 return mTrackStartTime;
543}
GpsInformationComponent
GPS information component.
Definition qgis.h:1956
@ TrackStartTime
Timestamp at start of current track (available from QgsGpsLogger class only).
Definition qgis.h:1971
@ GroundSpeed
Ground speed.
Definition qgis.h:1959
@ TrackTimeSinceLastPoint
Time since last recorded location (available from QgsGpsLogger class only).
Definition qgis.h:1974
@ Pdop
Dilution of precision.
Definition qgis.h:1963
@ TrackEndTime
Timestamp at end (current point) of current track (available from QgsGpsLogger class only).
Definition qgis.h:1972
@ Altitude
Altitude/elevation above or below the mean sea level.
Definition qgis.h:1958
@ TrackDistanceFromStart
Direct distance from first vertex in current GPS track to last vertex (available from QgsGpsLogger cl...
Definition qgis.h:1962
@ TotalTrackLength
Total distance of current GPS track (available from QgsGpsLogger class only).
Definition qgis.h:1961
@ Hdop
Horizontal dilution of precision.
Definition qgis.h:1964
@ EllipsoidAltitude
Altitude/elevation above or below the WGS-84 Earth ellipsoid.
Definition qgis.h:1976
@ Bearing
Bearing measured in degrees clockwise from true north to the direction of travel.
Definition qgis.h:1960
@ Vdop
Vertical dilution of precision.
Definition qgis.h:1965
@ GeoidalSeparation
Geoidal separation, the difference between the WGS-84 Earth ellipsoid and mean-sea-level (geoid),...
Definition qgis.h:1975
@ VerticalAccuracy
Vertical accuracy in meters.
Definition qgis.h:1967
@ Location
2D location (latitude/longitude), as a QgsPointXY value
Definition qgis.h:1957
@ TrackDistanceSinceLastPoint
Distance since last recorded location (available from QgsGpsLogger class only).
Definition qgis.h:1973
@ HorizontalAccuracy
Horizontal accuracy in meters.
Definition qgis.h:1966
@ SatellitesUsed
Count of satellites used in obtaining the fix.
Definition qgis.h:1969
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:358
@ Point
Points.
Definition qgis.h:359
@ Line
Lines.
Definition qgis.h:360
@ Polygon
Polygons.
Definition qgis.h:361
@ Unknown
Unknown types.
Definition qgis.h:362
@ Null
No geometry.
Definition qgis.h:363
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:277
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
Represents a coordinate reference system (CRS).
Contains information about the context in which a coordinate transform is executed.
Custom exception class for Coordinate Reference System related exceptions.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
double measureLine(const QVector< QgsPointXY > &points) const
Measures the length of a line with multiple segments.
A geometry is the spatial representation of a feature.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
bool convertToMultiType()
Converts single type geometry into multitype geometry e.g.
static void convertPointList(const QVector< QgsPointXY > &input, QgsPointSequence &output)
Upgrades a point list from QgsPointXY to QgsPoint.
Abstract base class for connections to a GPS device.
static const QgsSettingsEntryEnumFlag< Qt::TimeSpec > * settingsGpsTimeStampSpecification
Settings entry time specification for GPS time stamps.
static const QgsSettingsEntryString * settingsGpsTimeStampTimeZone
Settings entry GPS time stamp time zone.
static const QgsSettingsEntryBool * settingGpsApplyLeapSecondsCorrection
Settings entry GPS apply leap seconds correction.
static const QgsSettingsEntryInteger * settingGpsLeapSeconds
Settings entry GPS leap seconds correction amount (in seconds).
static const QgsSettingsEntryInteger * settingsGpsTimeStampOffsetFromUtc
Settings entry GPS time offset from UTC in seconds.
static const QgsSettingsEntryInteger * settingGpsAcquisitionInterval
Settings entry GPS track point acquisition interval.
static const QgsSettingsEntryDouble * settingGpsDistanceThreshold
Settings entry GPS track point distance threshold.
void stateChanged(const QgsGpsInformation &info)
Emitted whenever the GPS state is changed.
Encapsulates information relating to a GPS position fix.
bool isValid() const
Returns whether the connection information is valid.
double latitude
Latitude in decimal degrees, using the WGS84 datum.
double longitude
Longitude in decimal degrees, using the WGS84 datum.
QDateTime utcDateTime
The date and time at which this position was reported, in UTC time.
double elevation
Altitude (in meters) above or below the mean sea level.
QVariant componentValue(Qgis::GpsInformationComponent component) const
Returns the value of the corresponding GPS information component.
Base class for objects which log incoming GPS data.
static const QgsSettingsEntryBool * settingsGpsStoreAttributeInMValues
Settings entry for whether storing GPS attributes as geometry M values should be enabled.
virtual void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the coordinate transform context to be used when transforming GPS coordinates.
~QgsGpsLogger() override
void stateChanged(const QgsGpsInformation &info)
Emitted whenever the associated GPS device state is changed.
static const QgsSettingsEntryEnumFlag< Qgis::GpsInformationComponent > * settingsGpsMValueComponent
Settings entry dictating which GPS attribute should be stored in geometry M values.
double lastElevation() const
Returns the last recorded elevation the device.
static const QgsSettingsEntryInteger * settingsTimeStampFormat
QgsGpsLogger(QgsGpsConnection *connection, QObject *parent=nullptr)
Constructor for QgsGpsLogger with the specified parent object.
QgsCoordinateReferenceSystem mWgs84CRS
WGS84 coordinate reference system.
static const QgsSettingsEntryInteger * settingsLeapSecondsCorrection
int mBlockGpsStateChanged
Used to pause logging of incoming GPS messages.
static const QgsSettingsEntryBool * settingsApplyLeapSeconds
QgsGeometry currentGeometry(Qgis::WkbType type, QString &error) const
Returns the current logged GPS positions as a geometry of the specified type.
const QgsDistanceArea & distanceArea() const
Returns the distance area calculator which should be used for calculating distances associated with t...
QVector< QgsPoint > currentTrack() const
Returns the recorded points in the current track.
bool automaticallyAddTrackVertices() const
Returns true if track vertices will be automatically added whenever the GPS position is changed.
void trackIsEmptyChanged(bool isEmpty)
Emitted whenever the current track changes from being empty to non-empty or vice versa.
QDateTime trackStartTime() const
Returns the timestamp at which the current track was started.
static const QgsSettingsEntryDouble * settingsDistanceThreshold
void updateGpsSettings()
Should be called whenever the QGIS GPS settings are changed.
void trackReset()
Emitted whenever the current track is reset.
void resetTrack()
Resets the current track, discarding all recorded points.
QDateTime lastTimestamp() const
Returns the last recorded timestamp from the device.
void setAutomaticallyAddTrackVertices(bool enabled)
Sets whether track vertices will be automatically added whenever the GPS position is changed.
void addTrackVertex()
Adds a track vertex at the current GPS location.
double lastMValue() const
Returns the last recorded value corresponding to the QgsGpsLogger::settingsGpsMValueComponent setting...
double trackDistanceFromStart() const
Returns the direct length from the first vertex in the track to the last (in meters).
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context to be used when transforming GPS coordinates.
void setConnection(QgsGpsConnection *connection)
Sets the associated GPS connection.
void distanceAreaChanged()
Emitted whenever the distance area used to calculate track distances is changed.
QgsGpsConnection * connection()
Returns the associated GPS connection.
static const QgsSettingsEntryString * settingsTimestampTimeZone
double totalTrackLength() const
Returns the total length of the current digitized track (in meters).
void setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid which will be used for calculating distances in the log.
static const QgsSettingsEntryInteger * settingsAcquisitionInterval
QVariant componentValue(Qgis::GpsInformationComponent component) const
Returns the value of the corresponding GPS information component.
void trackVertexAdded(const QgsPoint &vertex)
Emitted whenever a new vertex is added to the track.
QgsPointXY lastPosition() const
Returns the last recorded position of the device.
Represents a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
Definition qgspoint.cpp:572
A boolean settings entry.
A double settings entry.
A template class for enum and flag settings entry.
An integer settings entry.
A string settings entry.
Holds the tree structure for the settings in QGIS core.
static QgsSettingsTreeNode * sTreeGps
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Q_INVOKABLE bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi type.
#define BUILTIN_UNREACHABLE
Definition qgis.h:7208
#define SIP_SKIP
Definition qgis_sip.h:134
#define QgsDebugError(str)
Definition qgslogger.h:57