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