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