You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

bcr.cc 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. /*
  2. Support for Motorrad Routenplaner (Map&Guide) .bcr files.
  3. Copyright (C) 2005-2007 Olaf Klein, o.b.klein@gpsbabel.org
  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 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
  15. */
  16. /*
  17. 2006/01/22: reader simplified with inifile library
  18. 2007/01/30: new option prefer_shortnames
  19. don't check global_opts.objective
  20. 2007/04&14: new handling of DESCRIPTION lines
  21. */
  22. #include "defs.h"
  23. #include "csv_util.h"
  24. #include "garmin_tables.h"
  25. #include "cet_util.h"
  26. #include "inifile.h"
  27. #include <math.h>
  28. #include <stdlib.h>
  29. #include <stdio.h>
  30. #define MYNAME "bcr"
  31. #undef BCR_DEBUG
  32. #define R_EARTH 6371000 /* radius of our big blue ball */
  33. #define BCR_DEF_ICON "Standort"
  34. #define BCR_DEF_MPS_ICON "Waypoint"
  35. #define BCR_UNKNOWN /*(double) */ 999999999
  36. /*
  37. 6371014 would be a better value when converting to f.e. to mapsoure,
  38. but this seems to be used by Map&Guide when exporting to XML.
  39. */
  40. static gbfile* fout;
  41. static int curr_rte_num, target_rte_num;
  42. static double radius;
  43. static inifile_t* ini;
  44. /* placeholders for options */
  45. static char* rtenum_opt;
  46. static char* rtename_opt;
  47. static char* radius_opt;
  48. static char* prefer_shortnames_opt;
  49. static
  50. arglist_t bcr_args[] = {
  51. {
  52. "index", &rtenum_opt, "Index of route to write (if more than one in source)",
  53. NULL, ARGTYPE_INT, "1", NULL
  54. },
  55. {
  56. "name", &rtename_opt, "New name for the route",
  57. NULL, ARGTYPE_STRING, ARG_NOMINMAX
  58. },
  59. {
  60. "radius", &radius_opt, "Radius of our big earth (default 6371000 meters)", "6371000",
  61. ARGTYPE_FLOAT, ARG_NOMINMAX
  62. },
  63. {
  64. "prefer_shortnames", &prefer_shortnames_opt, "Use shortname instead of description",
  65. NULL, ARGTYPE_BOOL, ARG_NOMINMAX
  66. },
  67. ARG_TERMINATOR
  68. };
  69. typedef struct {
  70. const char* bcr_name;
  71. const char* mps_name;
  72. const char* symbol_DE;
  73. int warned;
  74. } bcr_icon_mapping_t;
  75. static
  76. bcr_icon_mapping_t bcr_icon_mapping[] = {
  77. { BCR_DEF_ICON, BCR_DEF_MPS_ICON, BCR_DEF_ICON },
  78. { "", BCR_DEF_MPS_ICON, "Eigene Adressen" },
  79. { "AdrMon alpen", "Summit", "Pass-Strassen" },
  80. { "AdrMon bauern", NULL, "Bauern- und Biohoefe" },
  81. { "AdrMon cmpngs", "Campground", "Campingplaetzte" },
  82. { "AdrMon p_aeu", "Scenic Area", "Sehenswertes" },
  83. { "AdrMon p_beu", "Gas Station", "Tanken" },
  84. { "AdrMon p_deu", "Parking Area", "Parken" },
  85. { "AdrMon p_feu", "Restaurant", "Gastro" },
  86. { "AdrMon p_geu", "Museum", "Freizeit" },
  87. { "AdrMon p_heu", "Gas Station", "Tankstellen" },
  88. { "AdrMon p_keu", NULL, "Faehrverbindungen" },
  89. { "AdrMon p_leu", NULL, "Grenzuebergaenge" },
  90. { "AdrMon p_teu", NULL, "Wein- und Sektgueter" },
  91. { "AdrMon RUINEN", "Ghost Town", "Burgen und Schloesser" },
  92. { "AdrMon NFHAUS", "Residence", "Naturfreundehaeuser" },
  93. { "AdrMon racing", "Bike Trail", "Rennstrecken" },
  94. { "AdrMon TNKRST", "Bar", "Tankraststaetten" },
  95. { "AdrMon tpclub", "Contact, Biker", "Motorrad-Clubs" },
  96. { "AdrMon tpequ", NULL, "Motorrad-Equipment" },
  97. { "AdrMon tphot", "Hotel", "Motorrad-Hotels" },
  98. { "AdrMon tpmh", NULL, "Motorradhaendler" },
  99. { "AdrMon tpss", "Restricted Area", "Sperrungen" },
  100. { "AdrMon tpsw", "Scenic Area", "Sehenswertes" },
  101. { "AdrMon tptref", NULL, "Treffpunkte" },
  102. { "AdrMon VORTE", "Information", "Ortsinformationen" },
  103. { "AdrMon WEBCAM", NULL, "WebCam-Standorte" },
  104. { "AdrMon youthh", NULL, "Jugendherbergen" },
  105. { "Town", "City (Small)", "Orte" },
  106. { NULL, NULL, NULL, 0 }
  107. };
  108. static void
  109. bcr_handle_icon_str(const char* str, Waypoint* wpt)
  110. {
  111. bcr_icon_mapping_t* m;
  112. wpt->icon_descr = BCR_DEF_MPS_ICON;
  113. for (m = bcr_icon_mapping; (m->bcr_name); m++) {
  114. if (case_ignore_strcmp(str, m->bcr_name) == 0) {
  115. int nr;
  116. if (m->symbol_DE == NULL) {
  117. if (! m->warned) {
  118. m->warned = 1;
  119. warning(MYNAME ": Unknown icon \"%s\" found. Please report.\n", str);
  120. }
  121. return;
  122. }
  123. wpt->description = m->symbol_DE;
  124. if (m->mps_name != NULL) {
  125. nr = gt_find_icon_number_from_desc(m->mps_name, MAPSOURCE);
  126. wpt->icon_descr = gt_find_desc_from_icon_number(nr, MAPSOURCE);
  127. }
  128. return;
  129. }
  130. }
  131. }
  132. static const char*
  133. get_bcr_icon_from_icon_descr(const QString& icon_descr)
  134. {
  135. const char* result = BCR_DEF_ICON;
  136. if (!icon_descr.isNull()) {
  137. bcr_icon_mapping_t* m;
  138. for (m = bcr_icon_mapping; (m->bcr_name); m++) {
  139. if (! m->mps_name) {
  140. continue;
  141. }
  142. if (icon_descr.compare(m->mps_name, Qt::CaseInsensitive) == 0) {
  143. result = m->bcr_name;
  144. break;
  145. }
  146. }
  147. }
  148. return result;
  149. }
  150. static void
  151. bcr_init_radius(void)
  152. {
  153. if (radius_opt != NULL) { /* preinitialize the earth radius */
  154. radius = atof(radius_opt);
  155. if (radius <= 0) {
  156. fatal(MYNAME ": Sorry, the radius should be greater than zero!\n");
  157. }
  158. } else {
  159. radius = (double)R_EARTH;
  160. }
  161. if (global_opts.verbose_status > 0) {
  162. printf(MYNAME ": We calculate with radius %f meters.\n", radius);
  163. }
  164. }
  165. static void
  166. bcr_rd_init(const QString& fname)
  167. {
  168. ini = inifile_init(qPrintable(fname), MYNAME);
  169. if (ini->unicode) {
  170. cet_convert_init(CET_CHARSET_UTF8, 1);
  171. }
  172. bcr_init_radius();
  173. }
  174. static void
  175. bcr_rd_deinit(void)
  176. {
  177. inifile_done(ini);
  178. }
  179. /* ------------------------------------------------------------*/
  180. static void
  181. bcr_create_waypts_from_route(route_head* route)
  182. {
  183. Waypoint* wpt;
  184. queue* elem, *tmp;
  185. QUEUE_FOR_EACH(&route->waypoint_list, elem, tmp) {
  186. wpt = new Waypoint(*(Waypoint*) elem);
  187. waypt_add(wpt);
  188. }
  189. }
  190. static void
  191. bcr_wgs84_to_mercator(const double lat, const double lon, int* north, int* east)
  192. {
  193. double N, E;
  194. N = log(tan(lat * M_PI / 360 + M_PI / 4)) * radius;
  195. E = lon * radius * M_PI / (double)180;
  196. if (lat > 0) {
  197. N += 0.500000000001; /* we go from double to integer */
  198. } else {
  199. N -= 0.500000000001; /* it's time to round a little bit */
  200. }
  201. if (lon > 0) {
  202. E += 0.500000000001;
  203. } else {
  204. E -= 0.500000000001;
  205. }
  206. *north = N;
  207. *east = E;
  208. }
  209. void
  210. bcr_mercator_to_wgs84(const int north, const int east, double* lat, double* lon)
  211. {
  212. *lat = 2 * (atan(exp(north / radius)) - M_PI / 4) / M_PI * (double)180;
  213. *lon = (double)east * (double)180 / (radius * M_PI);
  214. }
  215. /* ------------------------------------------------------------- */
  216. static void
  217. bcr_data_read(void)
  218. {
  219. int index;
  220. char* str;
  221. route_head* route;
  222. route = route_head_alloc();
  223. if ((str = inifile_readstr(ini, "client", "routename"))) {
  224. route->rte_name = str;
  225. }
  226. route_add_head(route);
  227. for (index = 1; index > 0; index ++) {
  228. char station[32];
  229. char* str;
  230. int mlat, mlon; /* mercator data */
  231. Waypoint* wpt;
  232. snprintf(station, sizeof(station), "STATION%d", index);
  233. if (NULL == (str = inifile_readstr(ini, "coordinates", station))) {
  234. break;
  235. }
  236. if (2 != sscanf(str, "%d,%d", &mlon, &mlat)) {
  237. fatal(MYNAME ": structure error at %s (Coordinates)!\n", station);
  238. }
  239. wpt = new Waypoint;
  240. wpt->shortname = station;
  241. bcr_mercator_to_wgs84(mlat, mlon, &wpt->latitude, &wpt->longitude);
  242. if (NULL != (str = inifile_readstr(ini, "client", station))) {
  243. char* cx;
  244. cx = strchr(str, ',');
  245. if (cx == NULL) {
  246. fatal(MYNAME ": structure error at %s (Client)!\n", station);
  247. }
  248. *cx++ = '\0';
  249. bcr_handle_icon_str(str, wpt);
  250. }
  251. if (NULL != (str = inifile_readstr(ini, "description", station))) {
  252. char* c;
  253. c = strchr(str, ',');
  254. if (c != NULL) {
  255. *c = '\0';
  256. }
  257. if (*str) {
  258. wpt->notes = str;
  259. }
  260. if ((str = c)) {
  261. str++;
  262. c = strchr(str, ',');
  263. if (c != NULL) {
  264. *c = '\0';
  265. }
  266. if (*str) {
  267. wpt->shortname = str;
  268. }
  269. }
  270. }
  271. route_add_wpt(route, wpt);
  272. }
  273. /* remove empty route */
  274. if (route->rte_waypt_ct == 0) {
  275. route_del_head(route);
  276. } else {
  277. bcr_create_waypts_from_route(route);
  278. }
  279. }
  280. /* %%% bcr write support %%% ----------------------------------- */
  281. static void
  282. bcr_wr_init(const QString& fname)
  283. {
  284. fout = gbfopen(fname, "wb", MYNAME);
  285. bcr_init_radius();
  286. }
  287. static void
  288. bcr_wr_deinit(void)
  289. {
  290. gbfclose(fout);
  291. }
  292. static void
  293. bcr_route_trailer(const route_head* rte)
  294. {
  295. }
  296. static void
  297. bcr_write_wpt(const Waypoint* wpt)
  298. {
  299. }
  300. void bcr_write_line(gbfile* fout, const QString& key, int* index, const QString& value)
  301. {
  302. if (value.isEmpty()) { /* this is mostly used in the world of windows */
  303. /* so we respectfully add a CR/LF on each line */
  304. gbfprintf(fout, "%s\r\n", CSTR(key));
  305. } else {
  306. char* tmp;
  307. tmp = (value != NULL) ? xstrdup(value) : xstrdup("");
  308. if (index != NULL) {
  309. gbfprintf(fout, "%s%d=%s\r\n", CSTR(key), *index, tmp);
  310. } else {
  311. gbfprintf(fout, "%s=%s\r\n", CSTR(key), tmp);
  312. }
  313. xfree(tmp);
  314. }
  315. }
  316. static void
  317. bcr_route_header(const route_head* route)
  318. {
  319. queue* elem, *tmp;
  320. Waypoint* wpt;
  321. QString sout;
  322. int i, north, east, nmin, nmax, emin, emax;
  323. curr_rte_num++;
  324. if (curr_rte_num != target_rte_num) {
  325. return;
  326. }
  327. bcr_write_line(fout, "[CLIENT]", NULL, NULL); /* client section */
  328. bcr_write_line(fout, "REQUEST", NULL, "TRUE");
  329. sout = route->rte_name;
  330. if (rtename_opt != 0) {
  331. sout = rtename_opt;
  332. }
  333. if (sout != NULL) {
  334. bcr_write_line(fout, "ROUTENAME", NULL, sout);
  335. } else {
  336. bcr_write_line(fout, "ROUTENAME", NULL, "Route");
  337. }
  338. bcr_write_line(fout, "DESCRIPTIONLINES", NULL, "0");
  339. i = 0;
  340. QUEUE_FOR_EACH(&route->waypoint_list, elem, tmp) {
  341. const char* icon;
  342. Waypoint* wpt = (Waypoint*) elem;
  343. i++;
  344. icon = get_bcr_icon_from_icon_descr(wpt->icon_descr);
  345. sout = QString("%1,%2").arg(icon).arg(BCR_UNKNOWN,10);
  346. bcr_write_line(fout, "STATION", &i, sout);
  347. }
  348. bcr_write_line(fout, "[COORDINATES]", NULL, NULL); /* coords section */
  349. nmin = emin = (1<<30);
  350. emax = nmax = -nmin;
  351. i = 0;
  352. QUEUE_FOR_EACH(&route->waypoint_list, elem, tmp) {
  353. i++;
  354. wpt = (Waypoint*) elem;
  355. bcr_wgs84_to_mercator(wpt->latitude, wpt->longitude, &north, &east);
  356. if (north > nmax) {
  357. nmax = north;
  358. }
  359. if (east > emax) {
  360. emax = east;
  361. }
  362. if (north < nmin) {
  363. nmin = north;
  364. }
  365. if (east < emin) {
  366. emin = east;
  367. }
  368. sout = QString::number(east) + "," + QString::number(north);
  369. bcr_write_line(fout, "STATION", &i, sout);
  370. }
  371. bcr_write_line(fout, "[DESCRIPTION]", NULL, NULL); /* descr. section */
  372. i = 0;
  373. QUEUE_FOR_EACH(&route->waypoint_list, elem, tmp) {
  374. QString s1, s2;
  375. i++;
  376. wpt = (Waypoint*) elem;
  377. s1 = wpt->notes;
  378. if (s1.isEmpty()) {
  379. s1 = wpt->description;
  380. }
  381. if (prefer_shortnames_opt || (s1.isEmpty())) {
  382. s2 = s1;
  383. s1 = wpt->shortname;
  384. } else {
  385. s2 = wpt->shortname;
  386. }
  387. if (s1.isEmpty()) {
  388. s1 = QString();
  389. } else {
  390. s1 = csv_stringclean(s1, ",\t\r\n");
  391. }
  392. if (s2.isEmpty()) {
  393. s2 = QString();
  394. } else {
  395. s2 = csv_stringclean(s2, ",\t\r\n");
  396. }
  397. if (sout.isEmpty()) {
  398. sout = QString("%1,%2,@,0").arg(s1).arg(s1);
  399. } else {
  400. sout = QString("%1,%2,@,0").arg(s1).arg(s2);
  401. }
  402. bcr_write_line(fout, "STATION", &i, sout);
  403. }
  404. bcr_write_line(fout, "[ROUTE]", NULL, NULL); /* route section */
  405. sout = QString::number(emin) + "," +
  406. QString::number(nmax) + "," +
  407. QString::number(emax) + "," +
  408. QString::number(nmin);
  409. bcr_write_line(fout, "ROUTERECT", NULL, sout);
  410. }
  411. static void
  412. bcr_data_write(void)
  413. {
  414. target_rte_num = 1;
  415. if (rtenum_opt != NULL) {
  416. target_rte_num = atoi(rtenum_opt);
  417. if (((unsigned)target_rte_num > route_count()) || (target_rte_num < 1))
  418. fatal(MYNAME ": invalid route number %d (1..%d))!\n",
  419. target_rte_num, route_count());
  420. }
  421. curr_rte_num = 0;
  422. route_disp_all(bcr_route_header, bcr_route_trailer, bcr_write_wpt);
  423. }
  424. ff_vecs_t bcr_vecs = {
  425. ff_type_file,
  426. { ff_cap_none, ff_cap_none, (ff_cap)(ff_cap_read | ff_cap_write)},
  427. bcr_rd_init,
  428. bcr_wr_init,
  429. bcr_rd_deinit,
  430. bcr_wr_deinit,
  431. bcr_data_read,
  432. bcr_data_write,
  433. NULL,
  434. bcr_args,
  435. CET_CHARSET_MS_ANSI, 0 /* CET-REVIEW */
  436. };