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