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.

lmx.cc 8.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. /*
  2. Access Nokia Landmark Exchange files.
  3. Copyright (C) 2007 Robert Lipe, robertlipe+source@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. * Nokia's Landmark Exchange (LMX) format is a straight-forward XML
  18. * format. Though they do support a compact binary representation,
  19. * we don't implement that at this time in GPSBabel.
  20. */
  21. #include "defs.h"
  22. #include "xmlgeneric.h"
  23. #include <stdio.h>
  24. #include <QtCore/QXmlStreamAttributes>
  25. static gbfile* ofd;
  26. static Waypoint* wpt_tmp;
  27. QString urllink;
  28. QString urllinkt;
  29. static char* binary = NULL;
  30. #define MYNAME "lmx"
  31. static
  32. arglist_t lmx_args[] = {
  33. {
  34. "binary", &binary,
  35. "Compact binary representation",
  36. NULL, ARGTYPE_BOOL, ARG_NOMINMAX
  37. },
  38. ARG_TERMINATOR
  39. };
  40. /*
  41. * Writer
  42. */
  43. static void
  44. lmx_wr_init(const QString& fname)
  45. {
  46. ofd = gbfopen(fname, "w", MYNAME);
  47. }
  48. static void
  49. lmx_wr_deinit(void)
  50. {
  51. gbfclose(ofd);
  52. }
  53. static const char*
  54. lmx_stag(int tag)
  55. {
  56. switch (tag) {
  57. case 0xC5:
  58. return "lmx";
  59. case 0x46:
  60. return "landmarkCollection";
  61. case 0x47:
  62. return "landmark";
  63. case 0x48:
  64. return "name";
  65. case 0x49:
  66. return "description";
  67. case 0x4A:
  68. return "coordinates";
  69. case 0x4B:
  70. return "latitude";
  71. case 0x4C:
  72. return "longitude";
  73. case 0x4D:
  74. return "altitude";
  75. case 0x4E:
  76. return "horizontalAccuracy";
  77. case 0x4F:
  78. return "verticalAccuracy";
  79. case 0x50:
  80. return "timeStamp";
  81. case 0x51:
  82. return "coverageRadius";
  83. case 0x52:
  84. return "category";
  85. case 0x53:
  86. return "id";
  87. case 0x54:
  88. return "addressInfo";
  89. case 0x55:
  90. return "country";
  91. case 0x56:
  92. return "countryCode";
  93. case 0x57:
  94. return "state";
  95. case 0x58:
  96. return "county";
  97. case 0x59:
  98. return "city";
  99. case 0x5A:
  100. return "district";
  101. case 0x5B:
  102. return "postalCode";
  103. case 0x5C:
  104. return "crossing1";
  105. case 0x5D:
  106. return "crossing2";
  107. case 0x5E:
  108. return "street";
  109. case 0x5F:
  110. return "buildingName";
  111. case 0x60:
  112. return "buildingFloor";
  113. case 0x61:
  114. return "buildingZone";
  115. case 0x62:
  116. return "buildingRoom";
  117. case 0x63:
  118. return "extension";
  119. case 0x64:
  120. return "phoneNumber";
  121. case 0x65:
  122. return "mediaLink";
  123. case 0x66:
  124. return "mime";
  125. case 0x67:
  126. return "url";
  127. default:
  128. return 0;
  129. }
  130. }
  131. static void
  132. lmx_indent(int count)
  133. {
  134. int i;
  135. for (i=0; i<count; i++) {
  136. gbfputc('\t', ofd);
  137. }
  138. }
  139. static void
  140. lmx_start_tag(int tag, int indent)
  141. {
  142. if (binary) {
  143. gbfputc(tag, ofd);
  144. } else {
  145. lmx_indent(indent);
  146. gbfprintf(ofd, "<lm:%s>", lmx_stag(tag));
  147. }
  148. }
  149. static void
  150. lmx_end_tag(int tag, int indent)
  151. {
  152. if (binary) {
  153. gbfputc(0x01, ofd);
  154. } else {
  155. lmx_indent(indent);
  156. gbfprintf(ofd, "</lm:%s>\n", lmx_stag(tag));
  157. }
  158. }
  159. static void
  160. lmx_write_xml(int tag, const QString& data, int indent)
  161. {
  162. lmx_start_tag(tag, indent);
  163. if (binary) {
  164. gbfputc(0x03, ofd); // inline string follows
  165. gbfputcstr(data, ofd);
  166. } else {
  167. char* tmp_ent = xml_entitize(CSTR(data));
  168. gbfputs(tmp_ent, ofd);
  169. xfree(tmp_ent);
  170. }
  171. lmx_end_tag(tag, 0);
  172. }
  173. static void
  174. lmx_print(const Waypoint* wpt)
  175. {
  176. QString oname;
  177. QString odesc;
  178. /*
  179. * Desparation time, try very hard to get a good shortname
  180. */
  181. odesc = wpt->notes;
  182. if (odesc.isEmpty()) {
  183. odesc = wpt->description;
  184. }
  185. if (odesc.isEmpty()) {
  186. odesc = wpt->shortname;
  187. }
  188. oname = global_opts.synthesize_shortnames ? odesc : wpt->shortname;
  189. lmx_start_tag(0x47, 2); // landmark
  190. if (!binary) {
  191. gbfputc('\n', ofd);
  192. }
  193. if (!oname.isEmpty()) {
  194. lmx_write_xml(0x48, oname, 3); // name
  195. }
  196. if (!wpt->description.isEmpty()) {
  197. lmx_write_xml(0x49, wpt->description, 3); // description
  198. }
  199. lmx_start_tag(0x4A, 3); // coordinates
  200. if (!binary) {
  201. gbfputc('\n', ofd);
  202. }
  203. char tbuf[100];
  204. sprintf(tbuf, "%f", wpt->latitude);
  205. lmx_write_xml(0x4B, tbuf, 4); // latitude
  206. sprintf(tbuf, "%f", wpt->longitude);
  207. lmx_write_xml(0x4C, tbuf, 4); // longitude
  208. if (wpt->altitude && (wpt->altitude != unknown_alt)) {
  209. sprintf(tbuf, "%f", wpt->altitude);
  210. lmx_write_xml(0x4D, tbuf, 4); // altitude
  211. }
  212. lmx_end_tag(0x4A, 3); // coordinates
  213. if (wpt->HasUrlLink()) {
  214. lmx_start_tag(0x65, 3); // mediaLink
  215. if (!binary) {
  216. gbfputc('\n', ofd);
  217. }
  218. UrlLink link = wpt->GetUrlLink();
  219. if (!link.url_link_text_.isEmpty()) {
  220. lmx_write_xml(0x48, link.url_link_text_, 4); // name
  221. }
  222. lmx_write_xml(0x67, link.url_, 4); // url
  223. lmx_end_tag(0x65, 3); // mediaLink
  224. }
  225. lmx_end_tag(0x47, 2); // landmark
  226. }
  227. static void
  228. lmx_write(void)
  229. {
  230. if (binary) {
  231. gbfputc(0x03, ofd); // WBXML version 1.3
  232. gbfputuint16(0x04A4, ofd); // "-//NOKIA//DTD LANDMARKS 1.0//EN"
  233. gbfputc(106, ofd); // Charset=UTF-8
  234. gbfputc(0x00, ofd); // empty string table
  235. gbfputc(0xC5, ofd); // lmx
  236. gbfputc(0x05, ofd); // xmlns=http://www.nokia.com/schemas/location/landmarks/
  237. gbfputc(0x85, ofd); // 1/0/
  238. gbfputc(0x06, ofd); // xmlns:xsi=
  239. gbfputc(0x86, ofd); // http://www.w3.org/2001/XMLSchema-instance
  240. gbfputc(0x07, ofd); // xsi:schemaLocation=http://www.nokia.com/schemas/location/landmarks/
  241. gbfputc(0x85, ofd); // 1/0/
  242. gbfputc(0x87, ofd); // whitespace
  243. gbfputc(0x88, ofd); // lmx.xsd
  244. gbfputc(0x01, ofd); // END lmx attributes
  245. } else {
  246. gbfprintf(ofd, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
  247. gbfprintf(ofd, "<lm:lmx xmlns:lm=\"http://www.nokia.com/schemas/location/landmarks/1/0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.nokia.com/schemas/location/landmarks/1/0/ lmx.xsd\">\n");
  248. }
  249. lmx_start_tag(0x46, 1); // landmarkCollection
  250. if (!binary) {
  251. gbfputc('\n', ofd);
  252. }
  253. waypt_disp_all(lmx_print);
  254. lmx_end_tag(0x46, 1); // landmarkCollection
  255. lmx_end_tag(0xC5, 0); // lmx
  256. }
  257. /*
  258. * Reader
  259. */
  260. static xg_callback lmx_lm_start, lmx_lm_end;
  261. static xg_callback lmx_lm_name,lmx_lm_desc;
  262. static xg_callback lmx_lm_lat, lmx_lm_lon, lmx_lm_alt;
  263. static xg_callback lmx_lm_mlink_s, lmx_lm_mlink_e;
  264. static xg_callback lmx_lm_link, lmx_lm_linkt;
  265. static xg_tag_mapping gl_map[] = {
  266. #define LM "/lm:lmx/lm:landmarkCollection/lm:landmark"
  267. { lmx_lm_start, cb_start, LM },
  268. { lmx_lm_end, cb_end, LM },
  269. { lmx_lm_name, cb_cdata, LM "/lm:name" },
  270. { lmx_lm_desc, cb_cdata, LM "/lm:description" },
  271. { lmx_lm_lat, cb_cdata, LM "/lm:coordinates/lm:latitude" },
  272. { lmx_lm_lon, cb_cdata, LM "/lm:coordinates/lm:longitude" },
  273. { lmx_lm_alt, cb_cdata, LM "/lm:coordinates/lm:altitude" },
  274. { lmx_lm_mlink_s, cb_start, LM "/lm:mediaLink" },
  275. { lmx_lm_link, cb_cdata, LM "/lm:mediaLink/lm:url" },
  276. { lmx_lm_linkt, cb_cdata, LM "/lm:mediaLink/lm:name" },
  277. { lmx_lm_mlink_e, cb_end, LM "/lm:mediaLink" },
  278. { NULL, (xg_cb_type)0, NULL}
  279. };
  280. static void
  281. lmx_rd_init(const QString& fname)
  282. {
  283. xml_init(fname, gl_map, NULL);
  284. }
  285. static void
  286. lmx_read(void)
  287. {
  288. xml_read();
  289. }
  290. static void
  291. lmx_rd_deinit(void)
  292. {
  293. xml_deinit();
  294. }
  295. static void
  296. lmx_lm_start(xg_string args, const QXmlStreamAttributes*)
  297. {
  298. wpt_tmp = new Waypoint;
  299. }
  300. static void
  301. lmx_lm_end(xg_string args, const QXmlStreamAttributes*)
  302. {
  303. waypt_add(wpt_tmp);
  304. }
  305. static void
  306. lmx_lm_lat(xg_string args, const QXmlStreamAttributes*)
  307. {
  308. wpt_tmp->latitude = args.toDouble();
  309. }
  310. static void
  311. lmx_lm_lon(xg_string args, const QXmlStreamAttributes*)
  312. {
  313. wpt_tmp->longitude = args.toDouble();
  314. }
  315. static void
  316. lmx_lm_alt(xg_string args, const QXmlStreamAttributes*)
  317. {
  318. wpt_tmp->altitude = args.toDouble();
  319. }
  320. static void
  321. lmx_lm_name(xg_string args, const QXmlStreamAttributes*)
  322. {
  323. wpt_tmp->shortname = args;
  324. }
  325. static void
  326. lmx_lm_desc(xg_string args, const QXmlStreamAttributes*)
  327. {
  328. wpt_tmp->description = args;
  329. }
  330. static void
  331. lmx_lm_mlink_s(xg_string args, const QXmlStreamAttributes*)
  332. {
  333. urllink = urllinkt = QString();
  334. }
  335. static void
  336. lmx_lm_link(xg_string args, const QXmlStreamAttributes*)
  337. {
  338. urllink = args;
  339. }
  340. static void
  341. lmx_lm_linkt(xg_string args, const QXmlStreamAttributes*)
  342. {
  343. urllinkt = args;
  344. }
  345. static void
  346. lmx_lm_mlink_e(xg_string args, const QXmlStreamAttributes*)
  347. {
  348. waypt_add_url(wpt_tmp, urllink, urllinkt);
  349. }
  350. ff_vecs_t lmx_vecs = {
  351. ff_type_file,
  352. {
  353. (ff_cap)(ff_cap_read | ff_cap_write), /* waypoints */
  354. ff_cap_none, /* tracks */
  355. ff_cap_none /* routes */
  356. },
  357. lmx_rd_init,
  358. lmx_wr_init,
  359. lmx_rd_deinit,
  360. lmx_wr_deinit,
  361. lmx_read,
  362. lmx_write,
  363. NULL,
  364. lmx_args,
  365. CET_CHARSET_UTF8, 0 /* CET-REVIEW */
  366. };