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