Write support for binary lmx

View: New views
1 Messages — Rating Filter:   Alert me  

Write support for binary lmx

by Christian Bühler :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi,
I sat down a while and implemented write support for the binary lmx
format.

I also implicitly fixed some mistakes in the lmx xml format:
- XML files support UTF-8, so no need to convert special characters
- Shortnames are now handled just like in gpx
- There was a missing space in the xsi:schemaLocation
- indentation is now with tabs just like it is generated by my Nokia N82
- added a test to testo for the binary format
- I also added a bit to the lmx documentation

Feel free to fine tune the documentation as I am not a native english
speaker.

Unfortunately, just after I finished this, I realized that my mobile
does not support the binary format at all, so I won't use this in
production. But I hope this is of use for someone else. However, it was
a good practice :-)

Regards,
Christian

[lmx_binary.diff]

diff --git a/lmx.c b/lmx.c
index f40762b..84fd561 100644
--- a/lmx.c
+++ b/lmx.c
@@ -32,11 +32,15 @@
 static gbfile *ofd;
 static waypoint *wpt_tmp;
 char *urllink, *urllinkt;
+static char *binary = NULL;
 
 #define MYNAME "lmx"
 
 static
 arglist_t lmx_args[] = {
+ { "binary", &binary,
+ "Compact binary representation",
+ NULL, ARGTYPE_BOOL, ARG_NOMINMAX },
  ARG_TERMINATOR
 };
 
@@ -57,61 +61,181 @@ lmx_wr_deinit(void)
         gbfclose(ofd);
 }
 
+static char *
+lmx_stag(int tag)
+{
+ switch (tag) {
+ case 0xC5: return "lmx";
+ case 0x46: return "landmarkCollection";
+ case 0x47: return "landmark";
+ case 0x48: return "name";
+ case 0x49: return "description";
+ case 0x4A: return "coordinates";
+ case 0x4B: return "latitude";
+ case 0x4C: return "longitude";
+ case 0x4D: return "altitude";
+ case 0x4E: return "horizontalAccuracy";
+ case 0x4F: return "verticalAccuracy";
+ case 0x50: return "timeStamp";
+ case 0x51: return "coverageRadius";
+ case 0x52: return "category";
+ case 0x53: return "id";
+ case 0x54: return "addressInfo";
+ case 0x55: return "country";
+ case 0x56: return "countryCode";
+ case 0x57: return "state";
+ case 0x58: return "county";
+ case 0x59: return "city";
+ case 0x5A: return "district";
+ case 0x5B: return "postalCode";
+ case 0x5C: return "crossing1";
+ case 0x5D: return "crossing2";
+ case 0x5E: return "street";
+ case 0x5F: return "buildingName";
+ case 0x60: return "buildingFloor";
+ case 0x61: return "buildingZone";
+ case 0x62: return "buildingRoom";
+ case 0x63: return "extension";
+ case 0x64: return "phoneNumber";
+ case 0x65: return "mediaLink";
+ case 0x66: return "mime";
+ case 0x67: return "url";
+ default: return 0;
+ }
+}
+
 static void
-lmx_write_xml(int indent_level, const char *tag, const char *data)
+lmx_indent(int count)
 {
  int i;
- char *tmp_ent = xml_entitize(data);
+ for (i=0; i<count; i++)
+ gbfputc('\t', ofd);
+}
 
- for (i = 0; i < indent_level; i++) {
- gbfputs("  ", ofd);
+static void
+lmx_start_tag(int tag, int indent)
+{
+ if (binary)
+ gbfputc(tag, ofd);
+ else {
+ lmx_indent(indent);
+ gbfprintf(ofd, "<lm:%s>", lmx_stag(tag));
  }
+}
 
- gbfprintf(ofd, "<%s>%s</%s>\n", tag, tmp_ent, tag);
+static void
+lmx_end_tag(int tag, int indent)
+{
+ if (binary)
+ gbfputc(0x01, ofd);
+ else {
+ lmx_indent(indent);
+ gbfprintf(ofd, "</lm:%s>\n", lmx_stag(tag));
+ }
+}
 
- xfree(tmp_ent);
+static void
+lmx_write_xml(int tag, const char *data, int indent)
+{
+ lmx_start_tag(tag, indent);
+
+ if (binary) {
+ gbfputc(0x03, ofd); // inline string follows
+ gbfputcstr(data, ofd);
+ }
+ else {
+ char *tmp_ent = xml_entitize(data);
+ gbfputs(tmp_ent, ofd);
+ xfree(tmp_ent);
+ }
+
+ lmx_end_tag(tag, 0);
 }
 
 static void
 lmx_print(const waypoint *wpt)
 {
- gbfprintf(ofd, "    <lm:landmark>\n");
- if (wpt->shortname) {
- lmx_write_xml(4, "lm:name", global_opts.synthesize_shortnames ? wpt->description : wpt->shortname);
+ const char *oname;
+ char *odesc;
+ char tbuf[100];
+
+ /*
+ * Desparation time, try very hard to get a good shortname
+ */
+ odesc = wpt->notes;
+ if (!odesc) {
+ odesc = wpt->description;
+ }
+ if (!odesc) {
+ odesc = wpt->shortname;
+ }
+
+ oname = global_opts.synthesize_shortnames ? odesc : wpt->shortname;
+
+ lmx_start_tag(0x47, 2); // landmark
+ if (!binary) gbfputc('\n', ofd);
+ if (oname) {
+ lmx_write_xml(0x48, oname, 3); // name
  }
  if (wpt->description) {
- lmx_write_xml(4, "lm:description", wpt->description);
+ lmx_write_xml(0x49, wpt->description, 3); // description
  }
- gbfprintf(ofd, "        <lm:coordinates>\n");
- gbfprintf(ofd, "          <lm:latitude>%f</lm:latitude>\n", wpt->latitude);
- gbfprintf(ofd, "          <lm:longitude>%f</lm:longitude>\n",wpt->longitude);
+ lmx_start_tag(0x4A, 3); // coordinates
+ if (!binary) gbfputc('\n', ofd);
+
+ sprintf(tbuf, "%f", wpt->latitude);
+ lmx_write_xml(0x4B, tbuf, 4); // latitude
+
+ sprintf(tbuf, "%f", wpt->longitude);
+ lmx_write_xml(0x4C, tbuf, 4); // longitude
+
  if (wpt->altitude && (wpt->altitude != unknown_alt)) {
-   gbfprintf(ofd, "          <lm:altitude>%f</lm:altitude>\n",wpt->altitude);
+ sprintf(tbuf, "%f", wpt->altitude);
+ lmx_write_xml(0x4D, tbuf, 4); // altitude
  }
- gbfprintf(ofd, "        </lm:coordinates>\n");
+ lmx_end_tag(0x4A, 3); // coordinates
 
  if (wpt->url && wpt->url[0]) {
- gbfprintf(ofd, "        <lm:mediaLink>\n");
+ lmx_start_tag(0x65, 3); // mediaLink
+ if (!binary) gbfputc('\n', ofd);
  if (wpt->url_link_text)
- lmx_write_xml(5,"lm:name", wpt->url_link_text);
- lmx_write_xml(5, "lm:url", wpt->url);
- gbfprintf(ofd, "        </lm:mediaLink>\n");
+ lmx_write_xml(0x48, wpt->url_link_text, 4); // name
+ lmx_write_xml(0x67, wpt->url, 4); // url
+ lmx_end_tag(0x65, 3); // mediaLink
  }
 
- gbfprintf(ofd, "    </lm:landmark>\n");
+ lmx_end_tag(0x47, 2); // landmark
 }
 
 
 static void
 lmx_write(void)
 {
- gbfprintf(ofd, "<?xml version=\"1.0\" ?>\n");
- 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\">");
+ if (binary) {
+ gbfputc(0x03, ofd); // WBXML version 1.3
+ gbfputuint16(0x04A4, ofd); // "-//NOKIA//DTD LANDMARKS 1.0//EN"
+ gbfputc(106, ofd); // Charset=UTF-8
+ gbfputc(0x00, ofd); // empty string table
+ gbfputc(0xC5, ofd); // lmx
+ gbfputc(0x05, ofd); // xmlns=http://www.nokia.com/schemas/location/landmarks/
+ gbfputc(0x85, ofd); // 1/0/
+ gbfputc(0x06, ofd); // xmlns:xsi=
+ gbfputc(0x86, ofd); // http://www.w3.org/2001/XMLSchema-instance
+ gbfputc(0x07, ofd); // xsi:schemaLocation=http://www.nokia.com/schemas/location/landmarks/
+ gbfputc(0x85, ofd); // 1/0/
+ gbfputc(0x87, ofd); // whitespace
+ gbfputc(0x88, ofd); // lmx.xsd
+ gbfputc(0x01, ofd); // END lmx attributes
+ } else {
+ gbfprintf(ofd, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+ 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");
+ }
 
- gbfprintf(ofd, "  <lm:landmarkCollection>\n");
+ lmx_start_tag(0x46, 1); // landmarkCollection
+ if (!binary) gbfputc('\n', ofd);
  waypt_disp_all(lmx_print);
- gbfprintf(ofd, "  </lm:landmarkCollection>\n");
- gbfprintf(ofd, "</lm:lmx>\n");
+ lmx_end_tag(0x46, 1); // landmarkCollection
+ lmx_end_tag(0xC5, 0); // lmx
 }
 
 /*
@@ -242,5 +366,5 @@ ff_vecs_t lmx_vecs = {
         lmx_write,
         NULL,
         lmx_args,
- CET_CHARSET_ASCII, 0 /* CET-REVIEW */
+ CET_CHARSET_UTF8, 0 /* CET-REVIEW */
 };
diff --git a/reference/binary.lmx b/reference/binary.lmx
new file mode 100644
index 0000000..cceb86e
Binary files /dev/null and b/reference/binary.lmx differ
diff --git a/reference/nokia.lmx b/reference/nokia.lmx
index 1807707..01331e8 100644
--- a/reference/nokia.lmx
+++ b/reference/nokia.lmx
@@ -1,112 +1,113 @@
-<?xml version="1.0" ?>
-<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">  <lm:landmarkCollection>
-    <lm:landmark>
-        <lm:name>GCEBB</lm:name>
-        <lm:description>Mountain Bike Heaven by susy1313</lm:description>
-        <lm:coordinates>
-          <lm:latitude>35.972033</lm:latitude>
-          <lm:longitude>-87.134700</lm:longitude>
-        </lm:coordinates>
-        <lm:mediaLink>
-          <lm:name>Cache Details</lm:name>
-          <lm:url>http://www.geocaching.com/seek/cache_details.asp?ID=3771</lm:url>
-        </lm:mediaLink>
-    </lm:landmark>
-    <lm:landmark>
-        <lm:name>GC1A37</lm:name>
-        <lm:description>The Troll by a182pilot & Family</lm:description>
-        <lm:coordinates>
-          <lm:latitude>36.090683</lm:latitude>
-          <lm:longitude>-86.679550</lm:longitude>
-        </lm:coordinates>
-        <lm:mediaLink>
-          <lm:name>Cache Details</lm:name>
-          <lm:url>http://www.geocaching.com/seek/cache_details.asp?ID=6711</lm:url>
-        </lm:mediaLink>
-    </lm:landmark>
-    <lm:landmark>
-        <lm:name>GC1C2B</lm:name>
-        <lm:description>Dive Bomber by JoGPS & family</lm:description>
-        <lm:coordinates>
-          <lm:latitude>35.996267</lm:latitude>
-          <lm:longitude>-86.620117</lm:longitude>
-        </lm:coordinates>
-        <lm:mediaLink>
-          <lm:name>Cache Details</lm:name>
-          <lm:url>http://www.geocaching.com/seek/cache_details.asp?ID=7211</lm:url>
-        </lm:mediaLink>
-    </lm:landmark>
-    <lm:landmark>
-        <lm:name>GC25A9</lm:name>
-        <lm:description>FOSTER by JoGPS & Family</lm:description>
-        <lm:coordinates>
-          <lm:latitude>36.038483</lm:latitude>
-          <lm:longitude>-86.648617</lm:longitude>
-        </lm:coordinates>
-        <lm:mediaLink>
-          <lm:name>Cache Details</lm:name>
-          <lm:url>http://www.geocaching.com/seek/cache_details.asp?ID=9641</lm:url>
-        </lm:mediaLink>
-    </lm:landmark>
-    <lm:landmark>
-        <lm:name>GC2723</lm:name>
-        <lm:description>Logan Lighthouse by JoGps & Family</lm:description>
-        <lm:coordinates>
-          <lm:latitude>36.112183</lm:latitude>
-          <lm:longitude>-86.741767</lm:longitude>
-        </lm:coordinates>
-        <lm:mediaLink>
-          <lm:name>Cache Details</lm:name>
-          <lm:url>http://www.geocaching.com/seek/cache_details.asp?ID=10019</lm:url>
-        </lm:mediaLink>
-    </lm:landmark>
-    <lm:landmark>
-        <lm:name>GC2B71</lm:name>
-        <lm:description>Ganier Cache by Susy1313</lm:description>
-        <lm:coordinates>
-          <lm:latitude>36.064083</lm:latitude>
-          <lm:longitude>-86.790517</lm:longitude>
-        </lm:coordinates>
-        <lm:mediaLink>
-          <lm:name>Cache Details</lm:name>
-          <lm:url>http://www.geocaching.com/seek/cache_details.asp?ID=11121</lm:url>
-        </lm:mediaLink>
-    </lm:landmark>
-    <lm:landmark>
-        <lm:name>GC309F</lm:name>
-        <lm:description>Shy's Hill by FireFighterEng33</lm:description>
-        <lm:coordinates>
-          <lm:latitude>36.087767</lm:latitude>
-          <lm:longitude>-86.809733</lm:longitude>
-        </lm:coordinates>
-        <lm:mediaLink>
-          <lm:name>Cache Details</lm:name>
-          <lm:url>http://www.geocaching.com/seek/cache_details.asp?ID=12447</lm:url>
-        </lm:mediaLink>
-    </lm:landmark>
-    <lm:landmark>
-        <lm:name>GC317A</lm:name>
-        <lm:description>GittyUp by JoGPS / Warner Parks</lm:description>
-        <lm:coordinates>
-          <lm:latitude>36.057500</lm:latitude>
-          <lm:longitude>-86.892000</lm:longitude>
-        </lm:coordinates>
-        <lm:mediaLink>
-          <lm:name>Cache Details</lm:name>
-          <lm:url>http://www.geocaching.com/seek/cache_details.asp?ID=12666</lm:url>
-        </lm:mediaLink>
-    </lm:landmark>
-    <lm:landmark>
-        <lm:name>GC317D</lm:name>
-        <lm:description>Inlighting by JoGPS / Warner Parks</lm:description>
-        <lm:coordinates>
-          <lm:latitude>36.082800</lm:latitude>
-          <lm:longitude>-86.867283</lm:longitude>
-        </lm:coordinates>
-        <lm:mediaLink>
-          <lm:name>Cache Details</lm:name>
-          <lm:url>http://www.geocaching.com/seek/cache_details.asp?ID=12669</lm:url>
-        </lm:mediaLink>
-    </lm:landmark>
-  </lm:landmarkCollection>
+<?xml version="1.0" encoding="UTF-8"?>
+<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">
+ <lm:landmarkCollection>
+ <lm:landmark>
+ <lm:name>GCEBB</lm:name>
+ <lm:description>Mountain Bike Heaven by susy1313</lm:description>
+ <lm:coordinates>
+ <lm:latitude>35.972033</lm:latitude>
+ <lm:longitude>-87.134700</lm:longitude>
+ </lm:coordinates>
+ <lm:mediaLink>
+ <lm:name>Cache Details</lm:name>
+ <lm:url>http://www.geocaching.com/seek/cache_details.asp?ID=3771</lm:url>
+ </lm:mediaLink>
+ </lm:landmark>
+ <lm:landmark>
+ <lm:name>GC1A37</lm:name>
+ <lm:description>The Troll by a182pilot & Family</lm:description>
+ <lm:coordinates>
+ <lm:latitude>36.090683</lm:latitude>
+ <lm:longitude>-86.679550</lm:longitude>
+ </lm:coordinates>
+ <lm:mediaLink>
+ <lm:name>Cache Details</lm:name>
+ <lm:url>http://www.geocaching.com/seek/cache_details.asp?ID=6711</lm:url>
+ </lm:mediaLink>
+ </lm:landmark>
+ <lm:landmark>
+ <lm:name>GC1C2B</lm:name>
+ <lm:description>Dive Bomber by JoGPS & family</lm:description>
+ <lm:coordinates>
+ <lm:latitude>35.996267</lm:latitude>
+ <lm:longitude>-86.620117</lm:longitude>
+ </lm:coordinates>
+ <lm:mediaLink>
+ <lm:name>Cache Details</lm:name>
+ <lm:url>http://www.geocaching.com/seek/cache_details.asp?ID=7211</lm:url>
+ </lm:mediaLink>
+ </lm:landmark>
+ <lm:landmark>
+ <lm:name>GC25A9</lm:name>
+ <lm:description>FOSTER by JoGPS & Family</lm:description>
+ <lm:coordinates>
+ <lm:latitude>36.038483</lm:latitude>
+ <lm:longitude>-86.648617</lm:longitude>
+ </lm:coordinates>
+ <lm:mediaLink>
+ <lm:name>Cache Details</lm:name>
+ <lm:url>http://www.geocaching.com/seek/cache_details.asp?ID=9641</lm:url>
+ </lm:mediaLink>
+ </lm:landmark>
+ <lm:landmark>
+ <lm:name>GC2723</lm:name>
+ <lm:description>Logan Lighthouse by JoGps & Family</lm:description>
+ <lm:coordinates>
+ <lm:latitude>36.112183</lm:latitude>
+ <lm:longitude>-86.741767</lm:longitude>
+ </lm:coordinates>
+ <lm:mediaLink>
+ <lm:name>Cache Details</lm:name>
+ <lm:url>http://www.geocaching.com/seek/cache_details.asp?ID=10019</lm:url>
+ </lm:mediaLink>
+ </lm:landmark>
+ <lm:landmark>
+ <lm:name>GC2B71</lm:name>
+ <lm:description>Ganier Cache by Susy1313</lm:description>
+ <lm:coordinates>
+ <lm:latitude>36.064083</lm:latitude>
+ <lm:longitude>-86.790517</lm:longitude>
+ </lm:coordinates>
+ <lm:mediaLink>
+ <lm:name>Cache Details</lm:name>
+ <lm:url>http://www.geocaching.com/seek/cache_details.asp?ID=11121</lm:url>
+ </lm:mediaLink>
+ </lm:landmark>
+ <lm:landmark>
+ <lm:name>GC309F</lm:name>
+ <lm:description>Shy's Hill by FireFighterEng33</lm:description>
+ <lm:coordinates>
+ <lm:latitude>36.087767</lm:latitude>
+ <lm:longitude>-86.809733</lm:longitude>
+ </lm:coordinates>
+ <lm:mediaLink>
+ <lm:name>Cache Details</lm:name>
+ <lm:url>http://www.geocaching.com/seek/cache_details.asp?ID=12447</lm:url>
+ </lm:mediaLink>
+ </lm:landmark>
+ <lm:landmark>
+ <lm:name>GC317A</lm:name>
+ <lm:description>GittyUp by JoGPS / Warner Parks</lm:description>
+ <lm:coordinates>
+ <lm:latitude>36.057500</lm:latitude>
+ <lm:longitude>-86.892000</lm:longitude>
+ </lm:coordinates>
+ <lm:mediaLink>
+ <lm:name>Cache Details</lm:name>
+ <lm:url>http://www.geocaching.com/seek/cache_details.asp?ID=12666</lm:url>
+ </lm:mediaLink>
+ </lm:landmark>
+ <lm:landmark>
+ <lm:name>GC317D</lm:name>
+ <lm:description>Inlighting by JoGPS / Warner Parks</lm:description>
+ <lm:coordinates>
+ <lm:latitude>36.082800</lm:latitude>
+ <lm:longitude>-86.867283</lm:longitude>
+ </lm:coordinates>
+ <lm:mediaLink>
+ <lm:name>Cache Details</lm:name>
+ <lm:url>http://www.geocaching.com/seek/cache_details.asp?ID=12669</lm:url>
+ </lm:mediaLink>
+ </lm:landmark>
+ </lm:landmarkCollection>
 </lm:lmx>
diff --git a/testo b/testo
index 2134fb8..4edd485 100755
--- a/testo
+++ b/testo
@@ -1298,6 +1298,8 @@ compare ${REFERENCE}/umsonstdraussen.gpx ${TMPDIR}/umsonstdraussen.gpx
 #
 gpsbabel -i lmx -f ${REFERENCE}/nokia.lmx -o lmx -F ${TMPDIR}/nokia.lmx
 compare ${REFERENCE}/nokia.lmx ${TMPDIR}/nokia.lmx
+gpsbabel -i lmx -f ${REFERENCE}/nokia.lmx -o lmx,binary -F ${TMPDIR}/binary.lmx
+bincompare ${REFERENCE}/binary.lmx ${TMPDIR}/binary.lmx
 
 #
 # Swiss Map (.xol) tests
diff --git a/xmldoc/formats/lmx.xml b/xmldoc/formats/lmx.xml
index 8f512a8..f668be4 100644
--- a/xmldoc/formats/lmx.xml
+++ b/xmldoc/formats/lmx.xml
@@ -1,6 +1,14 @@
 <para>
-This format supports Nokia Landmark Exchange (LMX) files used by several
-Nokia phones.   GPSBabel supports only the traditional XML format and
-not the compressed binary format.  
+This format supports
+<ulink url="http://sw.nokia.com/id/9001c8de-c19e-41a0-87d3-5be4297e4d4c/S60_Platform_Landmarks_Exchange_Specification_v1_0_en.pdf">
+Nokia Landmark Exchange (LMX) files</ulink> used by several Nokia phones.
+GPSBabel supports the traditional XML format for reading and writing. The
+compressed binary format (WBXML) can be written, but most current Nokia phones
+do not support it (confirmed with N82 and N95).
+</para>
+<para>
+With this format, landmarks can be imported into the landmark store of the
+mobile phone. This landmark store is then used to display them on a map with
+several applications. The most common ones are the pre-installed Ovi Maps (or
+its predecessor Nokia Maps) and Google Maps Mobile.
 </para>
-
diff --git a/xmldoc/formats/options/lmx-binary.xml b/xmldoc/formats/options/lmx-binary.xml
new file mode 100644
index 0000000..dbf05fb
--- /dev/null
+++ b/xmldoc/formats/options/lmx-binary.xml
@@ -0,0 +1,9 @@
+<para>
+This option specifies if you want to write the compressed binary format (WBXML)
+instead of the XML format. However, most current Nokia phones do only support
+the XML format (confirmed with N82 and N95).
+</para>
+<para>
+This option has no effect when used for input, only reading the traditional XML
+format is supported.
+</para>



------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july
_______________________________________________
Gpsbabel-code mailing list  http://www.gpsbabel.org
Gpsbabel-code@...
https://lists.sourceforge.net/lists/listinfo/gpsbabel-code

binary.lmx (1K) Download Attachment