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.

998 lines
23KB

  1. /*
  2. Support for "OpenStreetMap" data files (.xml)
  3. Copyright (C) 2008 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. #include "defs.h"
  17. #include "xmlgeneric.h"
  18. #include <QtCore/QHash>
  19. #include <QtCore/QXmlStreamAttributes>
  20. static char* opt_tag, *opt_tagnd, *created_by;
  21. static arglist_t osm_args[] = {
  22. { "tag", &opt_tag, "Write additional way tag key/value pairs", NULL, ARGTYPE_STRING, ARG_NOMINMAX },
  23. { "tagnd", &opt_tagnd, "Write additional node tag key/value pairs", NULL, ARGTYPE_STRING, ARG_NOMINMAX },
  24. { "created_by", &created_by, "Use this value as custom created_by value","GPSBabel", ARGTYPE_STRING, ARG_NOMINMAX },
  25. ARG_TERMINATOR
  26. };
  27. #define MYNAME "osm"
  28. static QHash<QString, const Waypoint*> waypoints;
  29. static QHash<QString, int> keys;
  30. static QHash<QString, const struct osm_icon_mapping_s*> values;
  31. static QHash<QString, const struct osm_icon_mapping_s*> icons;
  32. static gbfile* fout;
  33. static int node_id;
  34. static int skip_rte;
  35. static route_head* rte;
  36. static Waypoint* wpt;
  37. static xg_callback osm_node, osm_node_tag, osm_node_end;
  38. static xg_callback osm_way, osm_way_nd, osm_way_tag, osm_way_center, osm_way_end;
  39. static
  40. xg_tag_mapping osm_map[] = {
  41. { osm_node, cb_start, "/osm/node" },
  42. { osm_node_tag, cb_start, "/osm/node/tag" },
  43. { osm_node_end, cb_end, "/osm/node" },
  44. { osm_way, cb_start, "/osm/way" },
  45. { osm_way_nd, cb_start, "/osm/way/nd" },
  46. { osm_way_tag, cb_start, "/osm/way/tag" },
  47. { osm_way_center, cb_start, "/osm/way/center" },
  48. { osm_way_end, cb_end, "/osm/way" },
  49. { NULL, (xg_cb_type)0, NULL }
  50. };
  51. static const char* osm_features[] = {
  52. "- dummy -", /* 0 */
  53. "aeroway", /* 1 */
  54. "amenity", /* 2 */
  55. "building", /* 3 */
  56. "cycleway", /* 4 */
  57. "railway", /* 5 */
  58. "highway", /* 6 */
  59. "historic", /* 7 */
  60. "landuse", /* 8 */
  61. "leisure", /* 9 */
  62. "man_made", /* 10 */
  63. "military", /* 11 */
  64. "natural", /* 12 */
  65. "place", /* 13 */
  66. "power", /* 14 */
  67. "shop", /* 15 */
  68. "sport", /* 16 */
  69. "tourism", /* 17 */
  70. "waterway", /* 18 */
  71. "aerialway", /* 19 */
  72. NULL
  73. };
  74. typedef struct osm_icon_mapping_s {
  75. const int key;
  76. const char* value;
  77. const char* icon;
  78. } osm_icon_mapping_t;
  79. /* based on <http://wiki.openstreetmap.org/index.php/Map_Features> */
  80. static osm_icon_mapping_t osm_icon_mappings[] = {
  81. /* cycleway ...*/
  82. /* highway ...*/
  83. // { 6, "mini_roundabout", "?" },
  84. // { 6, "stop", "?" },
  85. // { 6, "traffic_signals", "?" },
  86. // { 6, "crossing", "?" },
  87. // { 6, "gate", "?" },
  88. // { 6, "stile", "?" },
  89. // { 6, "cattle_grid", "?" },
  90. // { 6, "toll_booth", "?" },
  91. // { 6, "incline", "?" },
  92. // { 6, "incline_steep", "?" },
  93. // { 6, "viaduct", "?" },
  94. // { 6, "motorway_junction", "?" },
  95. // { 6, "services", "?" },
  96. // { 6, "ford", "?" },
  97. // { 6, "bus_stop", "?" },
  98. // { 6, "turning_circle", "?" },
  99. // { 6, "User Defined", "?" },
  100. /* waterway ... */
  101. { 18, "dock", "Dock" },
  102. // { 18, "lock_gate", "?" },
  103. // { 18, "turning_point", "?" },
  104. // { 18, "aqueduct", "?" },
  105. // { 18, "boatyard", "?" },
  106. // { 18, "water_point", "?" },
  107. // { 18, "waste_disposal", "?" },
  108. // { 18, "mooring", "?" },
  109. // { 18, "weir", "?" },
  110. // { 18, "User Defined", "?" },
  111. /* railway ... */
  112. // { 5, "station", "?" },
  113. // { 5, "halt", "?" },
  114. // { 5, "tram_stop", "?" },
  115. // { 5, "viaduct", "?" },
  116. { 5, "crossing", "Crossing" },
  117. // { 5, "level_crossing", "?" },
  118. // { 5, "subway_entrance", "?" },
  119. // { 5, "turntable", "?" },
  120. // { 5, "User Defined", "?" },
  121. /* aeroway ... */
  122. { 1, "aerodrome", "Airport" },
  123. { 1, "terminal", "Airport" },
  124. { 1, "helipad", "Heliport" },
  125. // { 1, "User Defined", "?" },
  126. /* aerialway ... */
  127. // { 19, "User Defined", "?" },
  128. /* power ... */
  129. // { 14, "tower", "?" },
  130. // { 14, "sub_station", "?" },
  131. // { 14, "generator", "?" },
  132. /* man_made ... */
  133. // { 10, "works", "?" },
  134. // { 10, "beacon", "?" },
  135. // { 10, "survey_point", "?" },
  136. // { 10, "power_wind", "?" },
  137. // { 10, "power_hydro", "?" },
  138. // { 10, "power_fossil", "?" },
  139. // { 10, "power_nuclear", "?" },
  140. // { 10, "tower", "?" },
  141. // { 10, "water_tower", "?" },
  142. // { 10, "gasometer", "?" },
  143. // { 10, "reservoir_covered", "?" },
  144. // { 10, "lighthouse", "?" },
  145. // { 10, "windmill", "?" },
  146. // { 10, "wastewater_plant", "?" },
  147. // { 10, "crane", "?" },
  148. // { 10, "User Defined", "?" },
  149. /* building ... */
  150. { 3, "yes", "Building" },
  151. // { 3, "User Defined", "?" },
  152. /* leisure ... */
  153. // { 9, "sports_centre", "?" },
  154. { 9, "golf_course", "Golf Course" },
  155. { 9, "stadium", "Stadium" },
  156. // { 9, "track", "?" },
  157. // { 9, "pitch", "?" },
  158. // { 9, "water_park", "?" },
  159. { 9, "marina", "Marina" },
  160. // { 9, "slipway", "?" },
  161. { 9, "fishing", "Fishing Area" },
  162. // { 9, "nature_reserve", "?" },
  163. { 9, "park", "Park" },
  164. // { 9, "playground", "?" },
  165. // { 9, "garden", "?" },
  166. // { 9, "common", "?" },
  167. // { 9, "User Defined", "?" },
  168. /* amenity ... */
  169. { 2, "pub", "Bar" },
  170. // { 2, "biergarten", "?" },
  171. { 2, "nightclub", "Bar" },
  172. // { 2, "cafe", "?" },
  173. { 2, "restaurant", "Restaurant" },
  174. { 2, "fast_food", "Fast Food" },
  175. { 2, "parking", "Parking Area" },
  176. // { 2, "bicycle_parking", "?" },
  177. // { 2, "bicycle_rental", "?" },
  178. { 2, "car_rental", "Car Rental" },
  179. // { 2, "car_sharing", "?" },
  180. // { 2, "taxi", "?" },
  181. { 2, "fuel", "Gas Station" },
  182. { 2, "telephone", "Telephone" },
  183. { 2, "toilets", "Restroom" },
  184. // { 2, "recycling", "?" },
  185. // { 2, "public_building", "?" },
  186. { 2, "townhall", "City Hall" },
  187. // { 2, "place_of_worship", "?" },
  188. // { 2, "grave_yard", "?" },
  189. { 2, "post_office", "Post Office" },
  190. // { 2, "post_box", "?" },
  191. { 2, "school", "School" },
  192. // { 2, "university", "?" },
  193. // { 2, "college", "?" },
  194. { 2, "pharmacy", "Pharmacy" },
  195. { 2, "hospital", "Medical Facility" },
  196. // { 2, "library", "?" },
  197. { 2, "police", "Police Station" },
  198. // { 2, "fire_station", "?" },
  199. // { 2, "bus_station", "?" },
  200. // { 2, "theatre", "?" },
  201. // { 2, "cinema", "?" },
  202. // { 2, "arts_centre", "?" },
  203. // { 2, "courthouse", "?" },
  204. // { 2, "prison", "?" },
  205. { 2, "bank", "Bank" },
  206. // { 2, "bureau_de_change", "?" },
  207. // { 2, "atm", "?" },
  208. // { 2, "fountain", "?" },
  209. // { 2, "User Defined", "?" },
  210. /* shop ... */
  211. // { 15, "supermarket", "?" },
  212. { 15, "convenience", "Convenience Store" },
  213. // { 15, "butcher", "?" },
  214. // { 15, "bicycle", "?" },
  215. // { 15, "doityourself", "?" },
  216. // { 15, "dry_cleaning", "?" },
  217. // { 15, "laundry", "?" },
  218. // { 15, "outdoor", "?" },
  219. // { 15, "kiosk", "?" },
  220. // { 15, "User Defined", "?" },
  221. /* tourism ... */
  222. { 17, "information", "Information" },
  223. { 17, "hotel", "Hotel" },
  224. { 17, "motel", "Lodging" },
  225. { 17, "guest_house", "Lodging" },
  226. { 17, "hostel", "Lodging" },
  227. { 17, "camp_site", "Campground" },
  228. { 17, "caravan_site", "RV Park" },
  229. { 17, "picnic_site", "Picnic Area" },
  230. { 17, "viewpoint", "Scenic Area" },
  231. // { 17, "theme_park", "?" },
  232. // { 17, "attraction", "?" },
  233. { 17, "zoo", "Zoo" },
  234. // { 17, "artwork", "?" },
  235. { 17, "museum", "Museum" },
  236. // { 17, "User Defined", "?" },
  237. /* historic ... */
  238. // { 7, "castle", "?" },
  239. // { 7, "monument", "?" },
  240. // { 7, "memorial", "?" },
  241. // { 7, "archaeological_site", "?" },
  242. // { 7, "ruins", "?" },
  243. // { 7, "battlefield", "?" },
  244. // { 7, "User Defined", "?" },
  245. /* landuse ... */
  246. // { 8, "farm", "?" },
  247. // { 8, "quarry", "?" },
  248. // { 8, "landfill", "?" },
  249. // { 8, "basin", "?" },
  250. // { 8, "reservoir", "?" },
  251. { 8, "forest", "Forest" },
  252. // { 8, "allotments", "?" },
  253. // { 8, "residential", "?" },
  254. // { 8, "retail", "?" },
  255. // { 8, "commercial", "?" },
  256. // { 8, "industrial", "?" },
  257. // { 8, "brownfield", "?" },
  258. // { 8, "greenfield", "?" },
  259. // { 8, "railway", "?" },
  260. // { 8, "construction", "?" },
  261. { 8, "military", "Military" },
  262. { 8, "cemetery", "Cemetery" },
  263. // { 8, "village_green", "?" },
  264. // { 8, "recreation_ground", "?" },
  265. // { 8, "User Defined", "?" },
  266. /* military ... */
  267. // { 11, "airfield", "?" },
  268. // { 11, "bunker", "?" },
  269. // { 11, "barracks", "?" },
  270. // { 11, "danger_area", "?" },
  271. // { 11, "range", "?" },
  272. // { 11, "naval_base", "?" },
  273. // { 11, "User Defined", "?" },
  274. /* natural ... */
  275. // { 12, "spring", "?" },
  276. // { 12, "peak", "?" },
  277. // { 12, "glacier", "?" },
  278. // { 12, "volcano", "?" },
  279. // { 12, "cliff", "?" },
  280. // { 12, "scree", "?" },
  281. // { 12, "scrub", "?" },
  282. // { 12, "fell", "?" },
  283. // { 12, "heath", "?" },
  284. // { 12, "wood", "?" },
  285. // { 12, "marsh", "?" },
  286. // { 12, "water", "?" },
  287. // { 12, "coastline", "?" },
  288. // { 12, "mud", "?" },
  289. { 12, "beach", "Beach" },
  290. // { 12, "bay", "?" },
  291. // { 12, "land", "?" },
  292. // { 12, "cave_entrance", "?" },
  293. // { 12, "User Defined", "?" },
  294. /* sport ... */
  295. // { 16, "10pin", "?" },
  296. // { 16, "athletics", "?" },
  297. // { 16, "australian_football", "?" },
  298. // { 16, "baseball", "?" },
  299. // { 16, "basketball", "?" },
  300. // { 16, "boules", "?" },
  301. // { 16, "bowls", "?" },
  302. // { 16, "climbing", "?" },
  303. // { 16, "cricket", "?" },
  304. // { 16, "cricket_nets", "?" },
  305. // { 16, "croquet", "?" },
  306. // { 16, "cycling", "?" },
  307. // { 16, "dog_racing", "?" },
  308. // { 16, "equestrian", "?" },
  309. // { 16, "football", "?" },
  310. // { 16, "golf", "?" },
  311. // { 16, "gymnastics", "?" },
  312. // { 16, "hockey", "?" },
  313. // { 16, "horse_racing", "?" },
  314. // { 16, "motor", "?" },
  315. // { 16, "multi", "?" },
  316. // { 16, "pelota", "?" },
  317. // { 16, "racquet", "?" },
  318. // { 16, "rugby", "?" },
  319. // { 16, "skating", "?" },
  320. // { 16, "skateboard", "?" },
  321. // { 16, "soccer", "?" },
  322. { 16, "swimming", "Swimming Area" },
  323. { 16, "skiing", "Skiing Area" },
  324. // { 16, "table_tennis", "?" },
  325. // { 16, "tennis", "?" },
  326. // { 16, "orienteering", "?" },
  327. // { 16, "User Defined", "?" },
  328. /* place ... */
  329. // { 13, "continent", "?" },
  330. // { 13, "country", "?" },
  331. // { 13, "state", "?" },
  332. // { 13, "region", "?" },
  333. // { 13, "county", "?" },
  334. { 13, "city", "City (Large)" },
  335. { 13, "town", "City (Medium)" },
  336. { 13, "village", "City (Small)" },
  337. // { 13, "hamlet", "?" },
  338. // { 13, "suburb", "?" },
  339. // { 13, "locality", "?" },
  340. // { 13, "island", "?" },
  341. // { 13, "User Defined", "?" },
  342. { -1, NULL, NULL }
  343. };
  344. /*******************************************************************************/
  345. /* READER */
  346. /*-----------------------------------------------------------------------------*/
  347. static void
  348. osm_features_init(void)
  349. {
  350. int i;
  351. /* the first of osm_features is a place holder */
  352. for (i = 1; osm_features[i]; i++) {
  353. keys.insert(QString::fromUtf8(osm_features[i]), i);
  354. }
  355. for (i = 0; osm_icon_mappings[i].value; i++) {
  356. char buff[128];
  357. buff[0] = osm_icon_mappings[i].key;
  358. strncpy(&buff[1], osm_icon_mappings[i].value, sizeof(buff) - 1);
  359. values.insert(QString::fromUtf8(buff), &osm_icon_mappings[i]);
  360. }
  361. }
  362. static char
  363. osm_feature_ikey(const QString& key)
  364. {
  365. return keys.value(key, -1);
  366. }
  367. static QString
  368. osm_feature_symbol(const int ikey, const char* value)
  369. {
  370. char buff[128];
  371. QString key;
  372. buff[0] = ikey;
  373. strncpy(&buff[1], value, sizeof(buff) - 1);
  374. key = QString::fromUtf8(buff);
  375. QString result;
  376. if (values.contains(key)) {
  377. result = values.value(key)->icon;
  378. } else {
  379. result = QString("%1:%2").arg(osm_features[ikey]).arg(value);
  380. }
  381. return result;
  382. }
  383. static char*
  384. osm_strip_html(const char* str)
  385. {
  386. utf_string utf;
  387. utf.is_html = 1;
  388. utf.utfstring = (char*)str;
  389. return strip_html(&utf); // util.cc
  390. }
  391. static QString
  392. osm_strip_html(const QString& str)
  393. {
  394. char* r = osm_strip_html(CSTR(str));
  395. QString rv(r);
  396. xfree(r);
  397. return rv;
  398. }
  399. static void
  400. osm_node_end(xg_string args, const QXmlStreamAttributes*)
  401. {
  402. if (wpt) {
  403. if (wpt->wpt_flags.fmt_use) {
  404. waypt_add(wpt);
  405. } else {
  406. delete wpt;
  407. }
  408. wpt = NULL;
  409. }
  410. }
  411. static void
  412. osm_node(xg_string args, const QXmlStreamAttributes* attrv)
  413. {
  414. wpt = new Waypoint;
  415. if (attrv->hasAttribute("id")) {
  416. QString atstr = attrv->value("id").toString();
  417. wpt->description = "osm-id " + atstr;
  418. if (waypoints.contains(atstr)) {
  419. warning(MYNAME ": Duplicate osm-id %s!\n", qPrintable(atstr));
  420. } else {
  421. waypoints.insert(atstr, wpt);
  422. wpt->wpt_flags.fmt_use = 1;
  423. }
  424. }
  425. // if (attrv->hasAttribute("user")) ; // ignored
  426. if (attrv->hasAttribute("lat")) {
  427. wpt->latitude = attrv->value("lat").toString().toDouble();
  428. }
  429. if (attrv->hasAttribute("lon")) {
  430. wpt->longitude = attrv->value("lon").toString().toDouble();
  431. }
  432. if (attrv->hasAttribute("timestamp")) {
  433. QString ts = attrv->value("timestamp").toString();
  434. wpt->creation_time = xml_parse_time(ts);
  435. }
  436. }
  437. static void
  438. osm_node_tag(xg_string args, const QXmlStreamAttributes* attrv)
  439. {
  440. QString key, value;
  441. QString str;
  442. signed char ikey;
  443. if (attrv->hasAttribute("k")) {
  444. key = attrv->value("k").toString();
  445. }
  446. if (attrv->hasAttribute("v")) {
  447. value = attrv->value("v").toString();
  448. }
  449. str = osm_strip_html(value);
  450. if (key == QLatin1String("name")) {
  451. if (wpt->shortname.isEmpty()) {
  452. wpt->shortname = str;
  453. }
  454. } else if (key == QLatin1String("name:en")) {
  455. wpt->shortname = str;
  456. } else if ((ikey = osm_feature_ikey(key)) >= 0) {
  457. wpt->icon_descr = osm_feature_symbol(ikey, CSTR(value));
  458. } else if (key == QLatin1String("note")) {
  459. if (wpt->notes.isEmpty()) {
  460. wpt->notes = str;
  461. } else {
  462. wpt->notes += "; ";
  463. wpt->notes += str;
  464. }
  465. } else if (key == QLatin1String("gps:hdop")) {
  466. wpt->hdop = str.toDouble();
  467. } else if (key == QLatin1String("gps:vdop")) {
  468. wpt->vdop = str.toDouble();
  469. } else if (key == QLatin1String("gps:pdop")) {
  470. wpt->pdop = str.toDouble();
  471. } else if (key == QLatin1String("gps:sat")) {
  472. wpt->sat = str.toDouble();
  473. } else if (key == QLatin1String("gps:fix")) {
  474. if (str == QLatin1String("2d")) {
  475. wpt->fix = fix_2d;
  476. } else if (str == QLatin1String("3d")) {
  477. wpt->fix = fix_3d;
  478. } else if (str == QLatin1String("dgps")) {
  479. wpt->fix = fix_dgps;
  480. } else if (str == QLatin1String("pps")) {
  481. wpt->fix = fix_pps;
  482. } else if (str == QLatin1String("none")) {
  483. wpt->fix = fix_none;
  484. }
  485. }
  486. }
  487. static void
  488. osm_way(xg_string args, const QXmlStreamAttributes* attrv)
  489. {
  490. rte = route_head_alloc();
  491. // create a wpt to represent the route center if it has a center tag
  492. wpt = new Waypoint;
  493. if (attrv->hasAttribute("id")) {
  494. rte->rte_desc = "osm-id " + attrv->value("id").toString();
  495. }
  496. }
  497. static void
  498. osm_way_nd(xg_string args, const QXmlStreamAttributes* attrv)
  499. {
  500. if (attrv->hasAttribute("ref")) {
  501. QString atstr = attrv->value("ref").toString();
  502. Waypoint* tmp;
  503. const Waypoint* ctmp;
  504. if (waypoints.contains(atstr)) {
  505. ctmp = waypoints.value(atstr);
  506. tmp = new Waypoint(*ctmp);
  507. route_add_wpt(rte, tmp);
  508. } else {
  509. warning(MYNAME ": Way reference id \"%s\" wasn't listed under nodes!\n", qPrintable(atstr));
  510. }
  511. }
  512. }
  513. static void
  514. osm_way_tag(xg_string args, const QXmlStreamAttributes* attrv)
  515. {
  516. QString key, value;
  517. QString str;
  518. signed char ikey;
  519. if (attrv->hasAttribute("k")) {
  520. key = attrv->value("k").toString();
  521. }
  522. if (attrv->hasAttribute("v")) {
  523. value = attrv->value("v").toString();
  524. }
  525. str = osm_strip_html(value);
  526. if (key == QLatin1String("name")) {
  527. if (rte->rte_name.isEmpty()) {
  528. rte->rte_name = str;
  529. wpt->shortname = str;
  530. }
  531. } else if (key == QLatin1String("name:en")) {
  532. rte->rte_name = str;
  533. wpt->shortname = str;
  534. // The remaining cases only apply to the center node
  535. } else if ((ikey = osm_feature_ikey(key)) >= 0) {
  536. wpt->icon_descr = osm_feature_symbol(ikey, CSTR(value));
  537. } else if (key == "note") {
  538. if (wpt->notes.isEmpty()) {
  539. wpt->notes = str;
  540. } else {
  541. wpt->notes += "; ";
  542. wpt->notes += str;
  543. }
  544. }
  545. }
  546. static void
  547. osm_way_center(xg_string args, const QXmlStreamAttributes* attrv)
  548. {
  549. wpt->wpt_flags.fmt_use = 1;
  550. if (attrv->hasAttribute("lat")) {
  551. wpt->latitude = attrv->value("lat").toString().toDouble();
  552. }
  553. if (attrv->hasAttribute("lon")) {
  554. wpt->longitude = attrv->value("lon").toString().toDouble();
  555. }
  556. }
  557. static void
  558. osm_way_end(xg_string args, const QXmlStreamAttributes*)
  559. {
  560. if (rte) {
  561. route_add_head(rte);
  562. rte = NULL;
  563. }
  564. if (wpt) {
  565. if (wpt->wpt_flags.fmt_use) {
  566. waypt_add(wpt);
  567. } else {
  568. delete(wpt);
  569. wpt = NULL;
  570. }
  571. }
  572. }
  573. static void
  574. osm_rd_init(const QString& fname)
  575. {
  576. wpt = NULL;
  577. rte = NULL;
  578. waypoints.clear();
  579. if (keys.isEmpty()) {
  580. osm_features_init();
  581. }
  582. xml_init(fname, osm_map, NULL);
  583. }
  584. static void
  585. osm_read(void)
  586. {
  587. xml_read();
  588. }
  589. static void
  590. osm_rd_deinit(void)
  591. {
  592. xml_deinit();
  593. waypoints.clear();
  594. }
  595. /*******************************************************************************/
  596. /* WRITER */
  597. /*-----------------------------------------------------------------------------*/
  598. static void
  599. osm_init_icons(void)
  600. {
  601. int i;
  602. if (!icons.isEmpty()) {
  603. return;
  604. }
  605. for (i = 0; osm_icon_mappings[i].value; i++) {
  606. icons.insert(QString::fromUtf8(osm_icon_mappings[i].icon),
  607. &osm_icon_mappings[i]);
  608. }
  609. }
  610. static void
  611. osm_write_tag(const QString& key, const QString& value)
  612. {
  613. if (!value.isEmpty()) {
  614. char* str = xml_entitize(CSTR(value));
  615. gbfprintf(fout, " <tag k='%s' v='%s'/>\n", CSTR(key), str);
  616. xfree(str);
  617. }
  618. }
  619. static void
  620. osm_disp_feature(const Waypoint* wpt)
  621. {
  622. const osm_icon_mapping_t* map;
  623. if (icons.contains(wpt->icon_descr)) {
  624. map = icons.value(wpt->icon_descr);
  625. osm_write_tag(osm_features[map->key], map->value);
  626. }
  627. }
  628. static void
  629. osm_write_opt_tag(const char* atag)
  630. {
  631. char* tag, *cin, *ce;
  632. if (!atag) {
  633. return;
  634. }
  635. tag = cin = xstrdup(atag);
  636. ce = cin + strlen(cin);
  637. while (cin < ce) {
  638. char* sc, *dp;
  639. if ((sc = strchr(cin, ';'))) {
  640. *sc = '\0';
  641. }
  642. if ((dp = strchr(cin, ':'))) {
  643. *dp++ = '\0';
  644. osm_write_tag(cin, dp);
  645. }
  646. cin += strlen(cin) + 1;
  647. }
  648. xfree(tag);
  649. }
  650. static void
  651. osm_release_ids(const Waypoint* wpt)
  652. {
  653. if (wpt && wpt->extra_data) {
  654. Waypoint* tmp = (Waypoint*)wpt;
  655. xfree(tmp->extra_data);
  656. tmp->extra_data = NULL;
  657. }
  658. }
  659. static QString
  660. osm_name_from_wpt(const Waypoint* wpt)
  661. {
  662. QString name = QString("%1\01%2\01%3")
  663. .arg(wpt->shortname)
  664. .arg(wpt->latitude)
  665. .arg(wpt->longitude);
  666. return name;
  667. }
  668. static void
  669. osm_waypt_disp(const Waypoint* wpt)
  670. {
  671. QString name = osm_name_from_wpt(wpt);
  672. if (waypoints.contains(name)) {
  673. return;
  674. }
  675. waypoints.insert(name, wpt);
  676. int* id;
  677. id = (int*) xmalloc(sizeof(*id));
  678. *id = --node_id;
  679. ((Waypoint*)(wpt))->extra_data = id;
  680. gbfprintf(fout, " <node id='%d' visible='true' lat='%0.7f' lon='%0.7f'", *id, wpt->latitude, wpt->longitude);
  681. if (wpt->creation_time.isValid()) {
  682. QString time_string = wpt->CreationTimeXML();
  683. gbfprintf(fout, " timestamp='%s'", qPrintable(time_string));
  684. }
  685. gbfprintf(fout, ">\n");
  686. if (wpt->hdop) {
  687. gbfprintf(fout, " <tag k='gps:hdop' v='%f' />\n", wpt->hdop);
  688. }
  689. if (wpt->vdop) {
  690. gbfprintf(fout, " <tag k='gps:vdop' v='%f' />\n", wpt->vdop);
  691. }
  692. if (wpt->pdop) {
  693. gbfprintf(fout, " <tag k='gps:pdop' v='%f' />\n", wpt->pdop);
  694. }
  695. if (wpt->sat > 0) {
  696. gbfprintf(fout, " <tag k='gps:sat' v='%d' />\n", wpt->sat);
  697. }
  698. switch (wpt->fix) {
  699. case fix_2d:
  700. gbfprintf(fout, " <tag k='gps:fix' v='2d' />\n");
  701. break;
  702. case fix_3d:
  703. gbfprintf(fout, " <tag k='gps:fix' v='3d' />\n");
  704. break;
  705. case fix_dgps:
  706. gbfprintf(fout, " <tag k='gps:fix' v='dgps' />\n");
  707. break;
  708. case fix_pps:
  709. gbfprintf(fout, " <tag k='gps:fix' v='pps' />\n");
  710. break;
  711. case fix_none:
  712. gbfprintf(fout, " <tag k='gps:fix' v='none' />\n");
  713. break;
  714. case fix_unknown:
  715. default:
  716. break;
  717. }
  718. if (strlen(created_by) !=0) {
  719. gbfprintf(fout, " <tag k='created_by' v='%s",created_by);
  720. if (gpsbabel_time != 0)
  721. if (strcmp("GPSBabel",created_by)==0) {
  722. gbfprintf(fout, "-%s", gpsbabel_version);
  723. }
  724. gbfprintf(fout, "'/>\n");
  725. }
  726. osm_write_tag("name", wpt->shortname);
  727. osm_write_tag("note", (wpt->notes.isEmpty()) ? wpt->description : wpt->notes);
  728. if (!wpt->icon_descr.isNull()) {
  729. osm_disp_feature(wpt);
  730. }
  731. osm_write_opt_tag(opt_tagnd);
  732. gbfprintf(fout, " </node>\n");
  733. }
  734. static void
  735. osm_rte_disp_head(const route_head* rte)
  736. {
  737. skip_rte = (rte->rte_waypt_ct <= 0);
  738. if (skip_rte) {
  739. return;
  740. }
  741. gbfprintf(fout, " <way id='%d' visible='true'>\n", --node_id);
  742. }
  743. static void
  744. osm_rtept_disp(const Waypoint* wpt_ref)
  745. {
  746. QString name = osm_name_from_wpt(wpt_ref);
  747. const Waypoint* wpt;
  748. if (skip_rte) {
  749. return;
  750. }
  751. if (waypoints.contains(name)) {
  752. wpt = waypoints.value(name);
  753. int* id = (int*) wpt->extra_data;
  754. gbfprintf(fout, " <nd ref='%d'/>\n", *id);
  755. }
  756. }
  757. static void
  758. osm_rte_disp_trail(const route_head* rte)
  759. {
  760. if (skip_rte) {
  761. return;
  762. }
  763. if (strlen(created_by) !=0) {
  764. gbfprintf(fout, " <tag k='created_by' v='%s",created_by);
  765. if (gpsbabel_time != 0)
  766. if (strcmp("GPSBabel",created_by)==0) {
  767. gbfprintf(fout, "-%s", gpsbabel_version);
  768. }
  769. gbfprintf(fout, "'/>\n");
  770. }
  771. osm_write_tag("name", rte->rte_name);
  772. osm_write_tag("note", rte->rte_desc);
  773. if (opt_tag && (case_ignore_strncmp(opt_tag, "tagnd", 5) != 0)) {
  774. osm_write_opt_tag(opt_tag);
  775. }
  776. gbfprintf(fout, " </way>\n");
  777. }
  778. /*-----------------------------------------------------------------------------*/
  779. static void
  780. osm_wr_init(const QString& fname)
  781. {
  782. fout = gbfopen(fname, "w", MYNAME);
  783. osm_init_icons();
  784. waypoints.clear();
  785. node_id = 0;
  786. }
  787. static void
  788. osm_write(void)
  789. {
  790. gbfprintf(fout, "<?xml version='1.0' encoding='UTF-8'?>\n");
  791. gbfprintf(fout, "<osm version='0.6' generator='GPSBabel");
  792. if (gpsbabel_time != 0) {
  793. gbfprintf(fout, "-%s", gpsbabel_version);
  794. }
  795. gbfprintf(fout, "'>\n");
  796. waypt_disp_all(osm_waypt_disp);
  797. route_disp_all(NULL, NULL, osm_waypt_disp);
  798. track_disp_all(NULL, NULL, osm_waypt_disp);
  799. route_disp_all(osm_rte_disp_head, osm_rte_disp_trail, osm_rtept_disp);
  800. track_disp_all(osm_rte_disp_head, osm_rte_disp_trail, osm_rtept_disp);
  801. gbfprintf(fout, "</osm>\n");
  802. }
  803. static void
  804. osm_wr_deinit(void)
  805. {
  806. gbfclose(fout);
  807. waypt_disp_all(osm_release_ids);
  808. route_disp_all(NULL, NULL, osm_release_ids);
  809. track_disp_all(NULL, NULL, osm_release_ids);
  810. waypoints.clear();
  811. }
  812. static void
  813. osm_exit(void)
  814. {
  815. keys.clear();
  816. values.clear();
  817. icons.clear();
  818. }
  819. /*-----------------------------------------------------------------------------*/
  820. ff_vecs_t osm_vecs = {
  821. ff_type_file,
  822. {
  823. (ff_cap)(ff_cap_read | ff_cap_write) /* waypoints */,
  824. ff_cap_write /* tracks */,
  825. (ff_cap)(ff_cap_read | ff_cap_write) /* routes */,
  826. },
  827. osm_rd_init,
  828. osm_wr_init,
  829. osm_rd_deinit,
  830. osm_wr_deinit,
  831. osm_read,
  832. osm_write,
  833. osm_exit,
  834. osm_args,
  835. CET_CHARSET_UTF8, 0
  836. };