1 /*
2
3 Support for MapAsia (.tr7) track file format.
4
5 Copyright (C) 2008 Olaf Klein, o.b.klein@gpsbabel.org
6
7 This program 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 2 of the License, or
10 (at your option) any later version.
11
12 This program 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 this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
21 */
22
23 #include <cmath> // for fabs
24 #include <cstring> // for memset
25
26 #include <QtCore/QDate> // for QDate
27 #include <QtCore/QDateTime> // for QDateTime
28 #include <QtCore/QString> // for QString
29 #include <QtCore/QTime> // for QTime
30 #include <QtCore/Qt> // for UTC
31 #include <QtCore/QtGlobal> // for foreach
32
33 #include "defs.h"
34 #include "gbfile.h" // for gbfclose, gbfeof, gbfgetint32, gbfputint32, gbfread, gbfwrite, gbfile, gbfopen_le
35 #include "session.h" // for curr_session
36 #include "src/core/datetime.h" // for DateTime
37
38
39 #define MYNAME "mapasia"
40
41 #define TR7_TRACK_MAGIC 0x223EADB
42
43 #define TR7_S_SIZE 32
44
45 #define TR7_S_YEAR 0
46 #define TR7_S_MONTH 2
47 #define TR7_S_DAY 6
48 #define TR7_S_HOUR 8
49 #define TR7_S_MIN 10
50 #define TR7_S_SEC 12
51 #define TR7_S_LON 16
52 #define TR7_S_LAT 20
53 #define TR7_S_SPEED 24
54 #define TR7_S_COURSE 26
55 #define TR7_S_VALID 28
56 #define TR7_S_FIX 29
57
58 static gbfile* fin, *fout;
59 static const Waypoint* wpt_tmp;
60 static const route_head* trk_tmp;
61 static int course_tmp, speed_tmp;
62
63 static
64 QVector<arglist_t> tr7_args = {
65 };
66
67 /*******************************************************************************
68 * %%% R E A D E R %%% *
69 *******************************************************************************/
70
71 static void
tr7_rd_init(const QString & fname)72 tr7_rd_init(const QString& fname)
73 {
74 fin = gbfopen_le(fname, "rb", MYNAME);
75 }
76
77 static void
tr7_read()78 tr7_read()
79 {
80 route_head* trk = nullptr;
81 Waypoint* prev = nullptr;
82
83 unsigned int magic = gbfgetint32(fin);
84 if (magic != TR7_TRACK_MAGIC) {
85 fatal(MYNAME ": Invalid magic number in header (%X, but %X expected)!\n", magic, TR7_TRACK_MAGIC);
86 }
87
88 while (! gbfeof(fin)) {
89 unsigned char buff[TR7_S_SIZE];
90
91 gbfread(buff, 1, sizeof(buff), fin);
92
93 double lat = (double)le_read32(&buff[TR7_S_LAT]) / 1000000.0;
94 double lon = (double)le_read32(&buff[TR7_S_LON]) / 1000000.0;
95
96 if ((fabs(lat) > 90) || (fabs(lon) > 180)) { /* that really happens */
97 trk = nullptr;
98 continue;
99 }
100
101 QDate date(le_read16(&buff[TR7_S_YEAR]),
102 buff[TR7_S_MONTH],
103 buff[TR7_S_DAY]);
104 QTime time(buff[TR7_S_HOUR],
105 buff[TR7_S_MIN],
106 buff[TR7_S_SEC]);
107 if (!date.isValid() || !time.isValid()) {
108 continue;
109 }
110
111 float speed = KPH_TO_MPS(le_read16(&buff[TR7_S_SPEED]));
112 float course = 360 - le_read16(&buff[TR7_S_COURSE]);
113 if ((speed < 0) || (course > 360) || (course < 0)) {
114 continue;
115 }
116
117 auto* wpt = new Waypoint;
118
119 wpt->latitude = lat;
120 wpt->longitude = lon;
121
122 wpt->SetCreationTime(QDateTime(date, time, Qt::UTC));
123
124 WAYPT_SET(wpt, course, course);
125 WAYPT_SET(wpt, speed, speed);
126
127 #if 0 /* unsure, not validated items */
128 wpt->fix = buff[TR7_S_FIX];
129 if (buff[TR7_S_VALID] != 'A') {
130 delete wpt;
131 continue;
132 }
133 #endif
134 if (waypt_speed(prev, wpt) > 9999.9) { /* filter out some bad trackpoints */
135 delete wpt;
136 continue;
137 }
138
139 if (prev) { /* other track or bad timestamp */
140 if (wpt->creation_time.isValid() && (prev->creation_time.toTime_t() > wpt->creation_time.toTime_t())) {
141 trk = nullptr;
142 } else if (waypt_distance(prev, wpt) > 9999.9) {
143 trk = nullptr;
144 }
145 }
146
147 if (! trk) {
148 trk = new route_head;
149 track_add_head(trk);
150 }
151 track_add_wpt(trk, wpt);
152 prev = wpt;
153 }
154 }
155
156 static void
tr7_check_after_read_head_cb(const route_head * trk)157 tr7_check_after_read_head_cb(const route_head* trk)
158 {
159 trk_tmp = trk;
160 course_tmp = 0;
161 speed_tmp = 0;
162 }
163
164 static void
tr7_check_after_read_wpt_cb(const Waypoint * wpt)165 tr7_check_after_read_wpt_cb(const Waypoint* wpt)
166 {
167 if (wpt->speed != 0) {
168 speed_tmp = 1;
169 }
170 if (wpt->course != 360.0) {
171 course_tmp = 1;
172 }
173 }
174
175 static void
tr7_check_after_read_trailer_cb(const route_head * trk)176 tr7_check_after_read_trailer_cb(const route_head* trk)
177 {
178 foreach (Waypoint* wpt, trk->waypoint_list) {
179 if (speed_tmp == 0) {
180 WAYPT_UNSET(wpt, speed);
181 }
182 if (course_tmp == 0) {
183 WAYPT_UNSET(wpt, course);
184 wpt->course = 0;
185 }
186 }
187 }
188
189 static void
tr7_rd_deinit()190 tr7_rd_deinit()
191 {
192 track_disp_session(curr_session(),
193 tr7_check_after_read_head_cb,
194 tr7_check_after_read_trailer_cb,
195 tr7_check_after_read_wpt_cb);
196 gbfclose(fin);
197 }
198
199 /*******************************************************************************
200 * %%% W R I T E R %%% *
201 *******************************************************************************/
202
203 static void
tr7_disp_track_head_cb(const route_head *)204 tr7_disp_track_head_cb(const route_head*)
205 {
206 wpt_tmp = nullptr;
207 }
208
209 static void
tr7_disp_waypt_cb(const Waypoint * wpt)210 tr7_disp_waypt_cb(const Waypoint* wpt)
211 {
212 unsigned char buff[TR7_S_SIZE];
213 double speed, course;
214
215 memset(buff, 0, sizeof(buff));
216
217 le_write32(&buff[TR7_S_LON], (int)(wpt->longitude * 1000000.0));
218 le_write32(&buff[TR7_S_LAT], (int)(wpt->latitude * 1000000.0));
219
220 if WAYPT_HAS(wpt, course) {
221 course = wpt->course;
222 } else if (wpt_tmp != nullptr) {
223 course = waypt_course(wpt_tmp, wpt);
224 } else {
225 course = -1;
226 }
227 if (course >= 0) {
228 le_write16(&buff[TR7_S_COURSE], (int)(360 - course));
229 }
230
231 QDateTime dt = wpt->GetCreationTime().toUTC();
232 if (dt.isValid()) {
233 QDate d = dt.date();
234
235 le_write16(&buff[TR7_S_YEAR], d.year());
236 buff[TR7_S_MONTH] = d.month();
237 buff[TR7_S_DAY] = d.day();
238
239 QTime t = dt.time();
240 buff[TR7_S_HOUR] = t.hour();
241 buff[TR7_S_MIN] = t.minute();
242 buff[TR7_S_SEC] = t.second();
243
244 if WAYPT_HAS(wpt, speed) {
245 speed = wpt->speed;
246 } else if (wpt_tmp != nullptr) {
247 speed = waypt_speed(wpt_tmp, wpt);
248 } else {
249 speed = -1;
250 }
251 if (speed >= 0) {
252 le_write16(&buff[TR7_S_SPEED], (int)MPS_TO_KPH(speed));
253 }
254 }
255 buff[TR7_S_VALID] = 'A'; /* meaning unknown */
256
257 #if 0 /* not validated */
258 if (wpt->fix != fix_unknown) {
259 buff[TR7_S_FIX] = wpt->fix;
260 }
261 #endif
262 gbfwrite(buff, 1, sizeof(buff), fout);
263
264 wpt_tmp = wpt;
265 }
266
267 static void
tr7_wr_init(const QString & fname)268 tr7_wr_init(const QString& fname)
269 {
270 fout = gbfopen_le(fname, "wb", MYNAME);
271 gbfputint32(TR7_TRACK_MAGIC, fout);
272 }
273
274 static void
tr7_wr_deinit()275 tr7_wr_deinit()
276 {
277 gbfclose(fout);
278 }
279
280 static void
tr7_write()281 tr7_write()
282 {
283 track_disp_all(tr7_disp_track_head_cb, nullptr, tr7_disp_waypt_cb);
284 }
285
286 /**************************************************************************/
287
288 ff_vecs_t mapasia_tr7_vecs = { /* we can read and write tracks */
289 ff_type_file,
290 {
291 ff_cap_none /* waypoints */,
292 (ff_cap)(ff_cap_read | ff_cap_write) /* tracks */,
293 ff_cap_none /* routes */
294 },
295 tr7_rd_init,
296 tr7_wr_init,
297 tr7_rd_deinit,
298 tr7_wr_deinit,
299 tr7_read,
300 tr7_write,
301 nullptr,
302 &tr7_args,
303 CET_CHARSET_UTF8, 1 /* FIXED - CET-REVIEW - */
304 , NULL_POS_OPS,
305 nullptr
306
307 };
308
309 /**************************************************************************/
310