1 /*
2  *    Copyright 2013 Thomas Schöps
3  *    Copyright 2016, 2018 Kai Pastor
4  *
5  *    This file is part of OpenOrienteering.
6  *
7  *    OpenOrienteering is free software: you can redistribute it and/or modify
8  *    it under the terms of the GNU General Public License as published by
9  *    the Free Software Foundation, either version 3 of the License, or
10  *    (at your option) any later version.
11  *
12  *    OpenOrienteering is distributed in the hope that it will be useful,
13  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *    GNU General Public License for more details.
16  *
17  *    You should have received a copy of the GNU General Public License
18  *    along with OpenOrienteering.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 
22 #ifndef OPENORIENTEERING_GPS_DISPLAY_H
23 #define OPENORIENTEERING_GPS_DISPLAY_H
24 
25 #include <QtGlobal>
26 #include <QObject>
27 #include <QString>
28 
29 #include "core/map_coord.h"
30 
31 class QGeoPositionInfo;
32 class QGeoPositionInfoSource;
33 class QPainter;
34 class QTimerEvent;
35 
36 namespace OpenOrienteering {
37 
38 class Georeferencing;
39 class MapWidget;
40 
41 
42 /**
43  * Retrieves the GPS position and displays a marker at this position on a MapWidget.
44  *
45  * \todo Use qreal instead of float (in all sensor code) for consistency with Qt.
46  */
47 class GPSDisplay : public QObject
48 {
49 Q_OBJECT
50 public:
51 	/// Creates a GPS display for the given map widget and georeferencing.
52 	GPSDisplay(MapWidget* widget, const Georeferencing& georeferencing, QObject* parent = nullptr);
53 	/// Destructor, removes the GPS display from the map widget.
54 	~GPSDisplay() override;
55 
56 	/**
57 	 * Checks if GPS is enabled, and may guide the user to the device settings.
58 	 *
59 	 * If GPS is not enabled in the device settings, it asks the user whether he
60 	 * wishes to open the device's location settings dialog.
61 	 * (At the moment, this is implemented for Android only.)
62 	 *
63 	 * Returns true if GPS is enabled, but also when the settings dialog remains
64 	 * open when returning from this function and the final setting is unknown.
65 	 */
66 	bool checkGPSEnabled();
67 
68 	/// Starts regular position updates. This will issue redraws of the map widget.
69 	void startUpdates();
70 	/// Stops regular position updates.
71 	void stopUpdates();
72 
73 	/// Sets GPS marker visibility (true by default)
74 	void setVisible(bool visible);
75 	/// Returns GPS marker visibility
isVisible()76 	bool isVisible() const { return visible; }
77 
78 	/// Sets whether distance rings are drawn
79 	void enableDistanceRings(bool enable);
80 	/// Sets whether the current heading from the Compass is used to draw a heading indicator.
81 	void enableHeadingIndicator(bool enable);
82 
83 	/// This is called from the MapWidget drawing code to draw the GPS position marker.
84 	void paint(QPainter* painter);
85 
86 	/// Returns if a valid position was received since the last call to startUpdates().
hasValidPosition()87 	bool hasValidPosition() const { return has_valid_position; }
88 	/// Returns the latest received GPS coord. Check hasValidPosition() beforehand!
getLatestGPSCoord()89 	const MapCoordF& getLatestGPSCoord() const { return latest_gps_coord; }
90 	/// Returns the accuracy of the latest received GPS coord, or -1 if unknown. Check hasValidPosition() beforehand!
getLatestGPSCoordAccuracy()91 	float getLatestGPSCoordAccuracy() const { return latest_gps_coord_accuracy; }
92 
93 	/// Starts quick blinking for one or more seconds.
94 	void startBlinking(int seconds);
95 
96 	/// Stops blinking.
97 	void stopBlinking();
98 
99 	/// Returns true while blinking is active.
isBlinking()100 	bool isBlinking() const { return blink_count > 0; }
101 
102 protected:
103 	/// Handles blinking.
104 	void timerEvent(QTimerEvent* e) override;
105 
106 signals:
107 	/// Is emitted whenever a new position update happens.
108 	/// If the accuracy is unknown, -1 will be given.
109 	void mapPositionUpdated(const OpenOrienteering::MapCoordF& coord, float accuracy);
110 
111 	/// Like mapPositionUpdated(), but gives the values as
112 	/// latitude / longitude in degrees and also gives altitude
113 	/// (meters above sea level; -9999 is unknown)
114 	void latLonUpdated(double latitude, double longitude, double altitude, float accuracy);
115 
116 	/// Is emitted when updates are interrupted after previously being active,
117 	/// due to loss of satellite reception or another error such as the user
118 	/// turning off the GPS receiver.
119 	void positionUpdatesInterrupted();
120 
121 private slots:
122     void positionUpdated(const QGeoPositionInfo& info);
123 	void error();
124 	void updateTimeout();
125 	void debugPositionUpdate();
126 
127 private:
128 	MapCoordF calcLatestGPSCoord(bool& ok);
129 	void updateMapWidget();
130 
131 	/**
132 	 * A lightweight utility for sinusoidal pulsating opacity.
133 	 *
134 	 * This class depends on another object's QObject::timerEvent() override
135 	 * to advance the pulsation state and eventually stop the activity.
136 	 *
137 	 * \see QBasicTimer
138 	 */
139 	class PulsatingOpacity
140 	{
141 	public:
142 		/// Returns true when the pulsation is active.
isActive()143 		bool isActive() const { return bool(timer_id); }
144 		/// Starts a timer on the given object.
145 		void start(QObject& object);
146 		/// Stops the timer on the given object.
147 		void stop(QObject& object);
148 		/// Returns the ID of the active timer.
timerId()149 		int timerId() const { return timer_id; }
150 
151 		/// Advances the pulsation state.
152 		bool advance();
153 		/// Returns the current opacity.
154 		qreal current() const;
155 
156 	private:
157 		int timer_id = 0;
158 		quint8 index = 0;
159 	};
160 
161 	MapWidget* widget;
162 	const Georeferencing& georeferencing;
163 	QGeoPositionInfoSource* source = nullptr;
164 	MapCoordF latest_gps_coord;
165 	float latest_gps_coord_accuracy = 0;
166 	PulsatingOpacity pulsating_opacity;
167 	int blink_count = 0;
168 	bool tracking_lost             = false;
169 	bool has_valid_position        = false;
170 	bool gps_updated               = false;
171 	bool visible                   = false;
172 	bool distance_rings_enabled    = false;
173 	bool heading_indicator_enabled = false;
174 };
175 
176 
177 }  // namespace OpenOrienteering
178 
179 #endif
180