2 * This file is part of jSpeed.
4 * jSpeed is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * jSpeed is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with jSpeed. If not, see <http://www.gnu.org/licenses/>.
19 #include <QtCore/QString>
20 #include <QtCore/QDir>
21 #include <QtCore/QTimer>
22 #include <QtCore/QFile>
23 #include <QtCore/QDebug>
25 #include "poialerts.h"
29 #include "mediaplayer.h"
33 static double const EARTH_MEAN_RADIUS = 6371.0072;
34 static double const PI = 3.14159265;
37 inline static double degToRad(double deg)
39 return deg * PI / 180;
42 inline static double radToDeg(double rad)
44 return rad * 180 / PI;
47 inline static double absValue(double value)
57 PoiAlerts::PoiAlerts(): QObject(0), enabled_(false), currentPoi_(0), loaded_(false)
61 PoiAlerts::~PoiAlerts()
66 PoiAlerts& PoiAlerts::instance()
68 static PoiAlerts instance;
72 bool PoiAlerts::start()
84 connect(&(Odometer::instance()), SIGNAL(dataUpdated()), this, SLOT(onDataUpdated()));
94 disconnect(&(Odometer::instance()), SIGNAL(dataUpdated()), this, SLOT(onDataUpdated()));
98 playedSounds_.clear();
104 bool PoiAlerts::loadConfig()
108 bool enabled = Settings::instance().value("alert_enabled", false).toBool();
117 distance_ = Settings::instance().value("alert_distance", 300).toDouble();
118 onlyOnRoute_ = Settings::instance().value("alert_only_on_route", true).toBool();
125 QString filename = Settings::instance().value("alert_sound", "").toString();
127 if(filename.isEmpty())
129 filename = "alert.wav";
132 QString soundDir = MediaPlayer::getSoundDir();
133 QString localDir = MediaPlayer::getLocalSoundDir();
135 if(!filename.isEmpty() && QFile::exists(soundDir + filename))
137 file_ = soundDir + filename;
139 else if(!filename.isEmpty() && QFile::exists(localDir + filename))
141 file_ = localDir + filename;
164 bool PoiAlerts::loadPois()
166 QString filename = Settings::instance().value("alert_poi_file", "").toString();
168 QString poiFile = getPoiDir() + filename;
170 if(filename.isEmpty() || !QFile::exists(poiFile))
172 error_ = "Poi file doesn't exist";
176 PoiReader* reader = PoiReader::getReader(poiFile);
180 error_ = "Unknown file format: " + poiFile;
184 if(!reader->read(pois_))
186 error_ = reader->error();
193 double PoiAlerts::getCurrentDistance() const
200 return currentDistance_;
203 QString PoiAlerts::getCurrentPoi() const
207 return currentPoi_->name;
213 bool PoiAlerts::poiInView() const
215 return currentPoi_ != 0;
218 QString const& PoiAlerts::error() const
223 void PoiAlerts::onDataUpdated()
225 const Location::Fix* fix = &(Odometer::instance().getLatestFix());
227 if(fix->latitude < 0.01 || fix->longitude < 0.01 ||
228 fix->kmSpeed < 0.01 || isnan(fix->eph))
233 double travelled = Odometer::instance().getTotal();
235 if(absValue(travelled - travelled_) > 0.02)
238 double inRouteMargin = IN_ROUTE_MARGIN + (fix->eph / 1000.0);
242 travelled_ = travelled;
244 for(int i = 0; i < pois_.size(); i++)
246 if((distance = calculateDistance(pois_.at(i).latitude, pois_.at(i).longitude,
247 fix->latitude, fix->longitude)) <= distance_)
249 qDebug() << "Distance: " << distance;
253 double track = absValue(calculateTrack(fix->latitude, fix->longitude,
254 pois_.at(i).latitude, pois_.at(i).longitude) - fix->track);
258 track = 360.0 - track;
261 qDebug() << "Real track: " << track;
265 if(distance < (inRouteMargin * 2.0))
271 trackLimit = 90.0 - radToDeg(acos((inRouteMargin + (distance * 0.16)) / distance));
274 qDebug() << "Tracklimit: " << trackLimit;
276 if(track < trackLimit)
279 currentPoi_ = &pois_[i];
280 currentDistance_ = distance;
281 emit visibilityChanged(true);
287 emit visibilityChanged(false);
294 currentPoi_ = &pois_[i];
295 currentDistance_ = distance;
296 emit visibilityChanged(true);
304 if(!found && currentPoi_)
307 emit visibilityChanged(false);
312 double PoiAlerts::calculateDistance(double latitude1, double longitude1,
313 double latitude2, double longitude2)
315 double dlat = degToRad(latitude1 - latitude2);
316 double dlon = degToRad(longitude1 - longitude2);
317 double y = sin(dlat / 2.0) * sin(dlat / 2.0)
318 + cos(degToRad(latitude2))
319 * cos(degToRad(latitude1))
320 * sin(dlon / 2.0) * sin(dlon / 2.0);
321 double x = 2 * atan2(sqrt(y), sqrt(1 - y));
322 return x * EARTH_MEAN_RADIUS * 1000;
325 double PoiAlerts::calculateTrack(double latitude1, double longitude1,
326 double latitude2, double longitude2)
328 double dlon = degToRad(longitude2 - longitude1);
329 double lat1Rad = degToRad(latitude1);
330 double lat2Rad = degToRad(latitude2);
331 double y = sin(dlon) * cos(lat2Rad);
332 double x = cos(lat1Rad) * sin(lat2Rad) - sin(lat1Rad) * cos(lat2Rad) * cos(dlon);
334 double fraction = modf(radToDeg(atan2(y, x)), &whole);
335 return (int(whole + 360) % 360) + fraction;
338 void PoiAlerts::playSound(int poiIndex)
340 qDebug() << "Almost play sound";
342 if(playedSounds_.indexOf(poiIndex) == -1)
344 playedSounds_.enqueue(poiIndex);
346 qDebug() << "Play sound";
347 MediaPlayer::play(file_);
349 QTimer::singleShot(POI_ALERT_INTERVAL * 1000, this, SLOT(removePlayed()));
354 void PoiAlerts::removePlayed()
356 if(!playedSounds_.isEmpty())
358 int removed = playedSounds_.dequeue();
359 qDebug() << "Removed: " << removed;
363 QString PoiAlerts::getPoiDir()
365 return Settings::getDir() + "pois" + QDir::separator();