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