1 /**********************************************************************************************
2     Copyright (C) 2015-2016 Christian Eichler <code@christian-eichler.de>
3 
4     This program 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.
8 
9     This program 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.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 
17 **********************************************************************************************/
18 
19 
20 #include "gis/trk/CKnownExtension.h"
21 #include "units/IUnit.h"
22 #include <QStringBuilder>
23 
24 const QString CKnownExtension::internalSlope = "ql:slope";
25 const QString CKnownExtension::internalSpeedDist = "ql:speeddist";
26 const QString CKnownExtension::internalSpeedTime = "ql:speedtime";
27 const QString CKnownExtension::internalEle = "ql:ele";
28 const QString CKnownExtension::internalProgress = "ql:progress";
29 const QString CKnownExtension::internalTerrainSlope = "ql:terrainslope";
30 
31 QHash<QString, CKnownExtension> CKnownExtension::knownExtensions;
32 QSet<QString> CKnownExtension::registeredNS;
33 
34 static const int NOORDER = std::numeric_limits<int>::max();
35 
getExtensionValueFunc(const QString ext)36 static fTrkPtGetVal getExtensionValueFunc(const QString ext)
37 {
38     return [ext](const CTrackData::trkpt_t& p)
39            {
40                bool ok;
41                qreal val = p.extensions.value(ext).toReal(&ok);
42                return ok ? val : NOFLOAT;
43            };
44 }
45 
registerNS(const QString & ns)46 bool CKnownExtension::registerNS(const QString& ns)
47 {
48     if(!registeredNS.contains(ns))
49     {
50         registeredNS.insert(ns);
51         return true;
52     }
53 
54     return false;
55 }
56 
57 
initGarminTPXv1(const IUnit & units,const QString & ns)58 void CKnownExtension::initGarminTPXv1(const IUnit& units, const QString& ns)
59 {
60     if(!registerNS(ns))
61     {
62         return;
63     }
64 
65     // support for the Garmin TrackPointExtension v1
66     //  https://www8.garmin.com/xmlschemas/TrackPointExtensionv1.xsd
67     knownExtensions.insert(ns % ":TrackPointExtension|" % ns % ":atemp",
68                            { tr("Air Temp.", "extShortName"), tr("Air Temperature", "extLongName"), 0, -100., 100., 1., "°C", "://icons/32x32/CSrcATemp.png", true, false,
69                              getExtensionValueFunc(ns % ":TrackPointExtension|" % ns % ":atemp")});
70 
71     knownExtensions.insert(ns % ":TrackPointExtension|" % ns % ":wtemp",
72                            { tr("Water Temp.", "extShortName"), tr("Water Temperature", "extLongName"), 1, -100., 100., 1., "°C", "://icons/32x32/CSrcWTemp.png", true, false,
73                              getExtensionValueFunc(ns % ":TrackPointExtension|" % ns % ":wtemp")});
74 
75     knownExtensions.insert(ns % ":TrackPointExtension|" % ns % ":depth",
76                            { tr("Depth", "extShortName"), tr("Depth", "extLongName"), 2, 0., 12000., units.elevationFactor, units.elevationUnit, "://icons/32x32/CSrcDepth.png", true, false,
77                              getExtensionValueFunc(ns % ":TrackPointExtension|" % ns % ":depth")});
78 
79     knownExtensions.insert(ns % ":TrackPointExtension|" % ns % ":hr",
80                            { tr("Heart R.", "extShortName"), tr("Heart Rate", "extLongName"), 3, 0., 300., 1., "bpm", "://icons/32x32/CSrcHR.png", true, false,
81                              getExtensionValueFunc(ns % ":TrackPointExtension|" % ns % ":hr")});
82 
83     knownExtensions.insert(ns % ":TrackPointExtension|" % ns % ":cad",
84                            { tr("Cadence", "extShortName"), tr("Cadence", "extLongName"), 4, 0., 500., 1., "rpm", "://icons/32x32/CSrcCAD.png", true, false,
85                              getExtensionValueFunc(ns % ":TrackPointExtension|" % ns % ":cad")});
86 
87     knownExtensions.insert(ns % ":TrackPointExtension|" % ns % ":power",
88                            { tr("Power", "extShortName"), tr("Power", "extLongName"), 5, 0., 1500., 1., "Watt", "://icons/32x32/CSrcPower.png", true, false,
89                              getExtensionValueFunc(ns % ":TrackPointExtension|" % ns % ":power")});
90 }
91 
initMioTPX(const IUnit & units)92 void CKnownExtension::initMioTPX(const IUnit& units)
93 {
94     // support for extensions used by MIO Cyclo ver. 4.2 (who needs xml namespaces?!)
95     knownExtensions.insert("heartrate",
96                            { tr("Heart R.", "extShortName"), tr("Heart Rate", "extLongName"), NOORDER, 0., 300., 1., "bpm", "://icons/32x32/CSrcHR.png", true, false,
97                              getExtensionValueFunc("heartrate")});
98 
99     knownExtensions.insert("cadence",
100                            { tr("Cadence", "extShortName"), tr("Cadence", "extLongName"), NOORDER, 0., 500., 1., "rpm", "://icons/32x32/CSrcCAD.png", true, false,
101                              getExtensionValueFunc("cadence")});
102 
103     knownExtensions.insert("speed",
104                            { tr("Speed", "extShortName"), tr("Speed", "extLongName"), NOORDER, 0., 600., units.speedFactor, units.speedUnit, "://icons/32x32/CSrcSpeed.png", true, false,
105                              getExtensionValueFunc("speed")});
106 
107     knownExtensions.insert("acceleration",
108                            { tr("Accel.", "extShortName"), tr("Acceleration", "extLongName"), NOORDER, std::numeric_limits<qreal>::lowest(), std::numeric_limits<qreal>::max(), units.baseFactor, units.baseUnit + "/s²", "://icons/32x32/CSrcAccel.png", true, false,
109                              getExtensionValueFunc("acceleration")});
110 
111     knownExtensions.insert("course",
112                            { tr("Course", "extShortName"), tr("Course", "extLongName"), NOORDER, -3.2, 3.2, 1., "rad", "://icons/32x32/CSrcCourse.png", true, false,
113                              getExtensionValueFunc("course")});
114 }
115 
initClueTrustTPXv1(const IUnit & units,const QString & ns)116 void CKnownExtension::initClueTrustTPXv1(const IUnit& units, const QString& ns)
117 {
118     knownExtensions.insert(ns % ":cadence",
119                            { tr("Cadence", "extShortName"), tr("Cadence", "extLongName"), 0, 0., 500., 1., "rpm", "://icons/32x32/CSrcCAD.png", true, false,
120                              getExtensionValueFunc(ns % ":cadence")});
121 
122     knownExtensions.insert(ns % ":temp",
123                            { tr("Temp.", "extShortName"), tr("Temperature", "extLongName"), 1, -100., 100., 1., "°C", "://icons/32x32/CSrcATemp.png", true, false,
124                              getExtensionValueFunc(ns % ":temp")});
125 
126     knownExtensions.insert(ns % ":distance",
127                            { tr("Dist.", "extShortName"), tr("Distance", "extLongName"), 2, 0., +100000000., units.baseFactor, units.baseUnit, "://icons/32x32/CSrcDistance.png", true, false,
128                              getExtensionValueFunc(ns % ":distance") });
129 
130     knownExtensions.insert(ns % ":altitude",
131                            { tr("Ele.", "extShortName"), tr("Elevation", "extLongName"), 3, -1000., +10000., units.elevationFactor, units.elevationUnit, "://icons/32x32/CSrcElevation.png", true, false,
132                              getExtensionValueFunc(ns % ":altitude") });
133 
134     knownExtensions.insert(ns % ":energy",
135                            { tr("Energy", "extShortName"), tr("Energy", "extLongName"), 4, 0., 10000., 1., "kcal/min", "://icons/32x32/CSrcEnergy.png", true, false,
136                              getExtensionValueFunc(ns % ":energy") });
137 
138 
139     knownExtensions.insert(ns % ":seaLevelPressure",
140                            { tr("Sea Lev. Pres.", "extShortName"), tr("Sea Level Pressure", "extLongName"), 5, 0., 1500., 1., "hPa", "://icons/32x32/CSrcSeaLevelPressure.png", true, false,
141                              getExtensionValueFunc(ns % ":seaLevelPressure") });
142 
143     knownExtensions.insert(ns % ":speed",
144                            { tr("Speed", "extShortName"), tr("Speed", "extLongName"), 6, 0., 600., units.speedFactor, units.speedUnit, "://icons/32x32/CSrcSpeed.png", true, false,
145                              getExtensionValueFunc(ns % ":speed")});
146 
147     knownExtensions.insert(ns % ":verticalSpeed",
148                            { tr("v. Speed", "extShortName"), tr("Vertical Speed", "extLongName"), 7, 0., 50., units.speedFactor, units.speedUnit, "://icons/32x32/CSrcVertSpeed.png", true, false,
149                              getExtensionValueFunc(ns % ":verticalSpeed")});
150 }
151 
init(const IUnit & units)152 void CKnownExtension::init(const IUnit& units)
153 {
154     knownExtensions =
155     {
156         {internalSlope,
157          { tr("Slope", "extShortName"), tr("Slope*"), -1, -90., 90., 1., (IUnit::getSlopeMode() == IUnit::eSlopePercent) ? "%" : "°",
158            "://icons/32x32/CSrcSlope.png", true, true,
159            [](const CTrackData::trkpt_t& p) { return (IUnit::getSlopeMode() == IUnit::eSlopePercent) ? p.slope2 : p.slope1; }}
160         },
161 
162         {internalSpeedDist,
163          { tr("Speed", "extShortName"), tr("Speed over Distance*", "extLongName"), -1, 0., 600., units.speedFactor, units.speedUnit, "://icons/32x32/CSrcSpeed.png", true, true,
164            [](const CTrackData::trkpt_t& p) { return p.speed; }}
165         },
166 
167         {internalSpeedTime,
168          { tr("Speed", "extShortName"), tr("Speed over Time*", "extLongName"), -1, 0., NOFLOAT, units.speedFactor, units.speedUnit, "://icons/32x32/CSrcSpeed.png", true, true,
169            [](const CTrackData::trkpt_t& p) { return p.speed; }}
170         },
171 
172         {internalEle,
173          { tr("Ele.", "extShortName"), tr("Elevation*", "extLongName"), -1, 0., 100000., units.elevationFactor, units.elevationUnit, "://icons/32x32/CSrcElevation.png", true, true,
174            [](const CTrackData::trkpt_t& p) { return (NOINT == p.ele) ? NOFLOAT : p.ele; }}
175         },
176 
177         {internalProgress,
178          { tr("Progress", "extShortName"), tr("Progress*", "extLongName"), -1, 0., NOFLOAT, units.baseFactor, units.baseUnit, "://icons/32x32/Progress.png", true, true,
179            [](const CTrackData::trkpt_t& p) { return p.distance; }}
180         },
181 
182         {internalTerrainSlope,
183          { tr("Terr. Slope", "extShortName"), tr("Terrain Slope*", "extLongName"), -1, 0, 90., 1., "°", "://icons/32x32/CSrcSlope.png", true, false,
184            getExtensionValueFunc(internalTerrainSlope)}
185         }
186     };
187 
188     initGarminTPXv1(units, "gpxtpx");
189     initGarminTPXv1(units, "tp1");
190 
191     initMioTPX(units);
192     initClueTrustTPXv1(units, "gpxdata");
193 }
194 
get(const QString & key)195 const CKnownExtension CKnownExtension::get(const QString& key)
196 {
197     CKnownExtension def("", "", NOORDER, -100000., 100000., 1., "", "://icons/32x32/CSrcUnknown.png", false, true,
198                         getExtensionValueFunc(key)
199                         );
200     return knownExtensions.value(key, def);
201 }
202 
isKnown(const QString & key)203 bool CKnownExtension::isKnown(const QString& key)
204 {
205     return knownExtensions.contains(key);
206 }
207 
getName(const QString & altName) const208 QString CKnownExtension::getName(const QString& altName) const
209 {
210     bool hasNoName = nameShortText.isEmpty();
211     QString name = hasNoName ? altName : nameShortText;
212 
213     if(derivedQMS && !hasNoName)
214     {
215         name += "*";
216     }
217 
218     return name;
219 }
220 
toString(qreal value,bool withName,const QString & key) const221 QString CKnownExtension::toString(qreal value, bool withName, const QString& key) const
222 {
223     QString str;
224     if(key == CKnownExtension::internalProgress)
225     {
226         return str;
227     }
228     else if(key.contains("speed"))
229     {
230         QString v, u;
231         IUnit::self().meter2speed(value, v, u);
232         str = v + u;
233     }
234     else if(key == CKnownExtension::internalEle)
235     {
236         QString v, u;
237         IUnit::self().meter2elevation(value, v, u);
238         str = v + u;
239     }
240     else if(key == CKnownExtension::internalSlope)
241     {
242         QString v, u;
243         IUnit::self().slope2string(value, v, u);
244         str = v + u;
245     }
246     else
247     {
248         str = QString("%1%2").arg(value * factor).arg(unit);
249     }
250 
251     if(withName)
252     {
253         str = getName(key) + " " + str;
254     }
255 
256     return str;
257 }
258