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 const Location::Fix* fix = &(Odometer::instance().getLatestFix());
202 return calculateDistance(currentPoi_->latitude, currentPoi_->longitude,
203 fix->latitude, fix->longitude);
206 QString PoiAlerts::getCurrentPoi() const
210 return currentPoi_->name;
216 bool PoiAlerts::poiInView() const
218 return currentPoi_ != 0;
221 QString const& PoiAlerts::error() const
226 void PoiAlerts::onDataUpdated()
228 const Location::Fix* fix = &(Odometer::instance().getLatestFix());
230 if(fix->latitude < 0.01 || fix->longitude < 0.01 ||
231 fix->kmSpeed < 0.01 || isnan(fix->eph))
236 double travelled = Odometer::instance().getTotal();
238 if(absValue(travelled - travelled_) > 0.015)
241 double inRouteMargin = IN_ROUTE_MARGIN + (fix->eph / 1000.0);
245 travelled_ = travelled;
247 for(int i = 0; i < pois_.size(); i++)
249 if((distance = calculateDistance(pois_.at(i).latitude, pois_.at(i).longitude,
250 fix->latitude, fix->longitude)) <= distance_)
252 qDebug() << "Distance: " << distance;
256 double track = absValue(calculateTrack(fix->latitude, fix->longitude,
257 pois_.at(i).latitude, pois_.at(i).longitude) - fix->track);
261 track = 360.0 - track;
264 qDebug() << "Real track: " << track;
268 if(distance < (inRouteMargin * 2.0))
274 trackLimit = 90.0 - radToDeg(acos((inRouteMargin + (distance * 0.17)) / distance));
277 qDebug() << "Tracklimit: " << trackLimit;
279 if(track < trackLimit)
282 currentPoi_ = &pois_[i];
283 emit visibilityChanged(true);
289 emit visibilityChanged(false);
296 currentPoi_ = &pois_[i];
297 emit visibilityChanged(true);
305 if(!found && currentPoi_)
308 emit visibilityChanged(false);
313 double PoiAlerts::calculateDistance(double latitude1, double longitude1,
314 double latitude2, double longitude2)
316 double dlat = degToRad(latitude1 - latitude2);
317 double dlon = degToRad(longitude1 - longitude2);
318 double y = sin(dlat / 2.0) * sin(dlat / 2.0)
319 + cos(degToRad(latitude2))
320 * cos(degToRad(latitude1))
321 * sin(dlon / 2.0) * sin(dlon / 2.0);
322 double x = 2 * atan2(sqrt(y), sqrt(1 - y));
323 return x * EARTH_MEAN_RADIUS * 1000;
326 double PoiAlerts::calculateTrack(double latitude1, double longitude1,
327 double latitude2, double longitude2)
329 double dlon = degToRad(longitude2 - longitude1);
330 double lat1Rad = degToRad(latitude1);
331 double lat2Rad = degToRad(latitude2);
332 double y = sin(dlon) * cos(lat2Rad);
333 double x = cos(lat1Rad) * sin(lat2Rad) - sin(lat1Rad) * cos(lat2Rad) * cos(dlon);
335 double fraction = modf(radToDeg(atan2(y, x)), &whole);
336 return (int(whole + 360) % 360) + fraction;
339 void PoiAlerts::playSound(int poiIndex)
341 qDebug() << "Almost play sound";
343 if(playedSounds_.indexOf(poiIndex) == -1)
345 playedSounds_.enqueue(poiIndex);
347 qDebug() << "Play sound";
348 MediaPlayer::play(file_);
350 QTimer::singleShot(POI_ALERT_INTERVAL * 1000, this, SLOT(removePlayed()));
355 void PoiAlerts::removePlayed()
357 if(!playedSounds_.isEmpty())
359 int removed = playedSounds_.dequeue();
360 qDebug() << "Removed: " << removed;
364 QString PoiAlerts::getPoiDir()
366 return Settings::getDir() + "pois" + QDir::separator();