In my last post I wrote about how I attached my TomTom One to my Mac via bluetooth to extract the GPS data. I hooked this all up to Mac stumbler and took a drive around Aberystwyth to see what wireless access points were advertsing themsleves. I used no special aerials, nor did I try and look for access points that were not advertising themselves – if a beacon frame made it to my Mac sitting on the passenger seat, it was counted – otherwise the access point was ignored.

The results of my drive are displayed in this Google map of some Aberystwyth Wireless Access Points. I haven’t put all captured fields of data into this map, because it is just for demonstration purposes. It is also not a complete map of Aberystwyth access points for the same reason. The third disclaimer is that the markers show the locations where I first saw a beacon frame and not the position of the strongest signal from the access point.

But the question is: how do we get the data from Mac stumbler to Google maps?

Mac stumbler saves its data in a “plist” XML format. This is a slightly odd format that looks like this:

<plist version="1.0">
<array>
    <dict>
        <key>channel</key>
        <integer>1</integer>
        <key>comments</key>
        <string></string>
        <key>date</key>
        <date>2006-11-08T17:26:57Z</date>
        <key>latitude</key>
        <string>W 5224.795410</string>
        <key>longitude</key>
        <string>N 404.561493</string>
        <key>mac</key>
        <string>00:12:17:DD:99:0E</string>
…
</dict></array></plist>


and so on.

What would make more sense would be:

<node>
  <channel>1</channel>
  <comments />
  <date>2006-11-10T17:57:11Z</date>
  <latitude>W 5224.771484</latitude>
  <longitude>N 404.355896</longitude>
  <mac>00:12:17:DD:99:0E</mac>
…
</node>

So I wrote an XSL style sheet which would do this translation, and ran the plist file through xalan to apply the stylesheet. The stylesheet is here: 

<?xml version="1.0" ?>
<xsl :stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    </xsl><xsl :template match="array">
        <nodelist>
            <xsl :apply-templates select="dict"/>
        </nodelist>
    </xsl>

    <xsl :template match="dict">
        <node>
            <xsl :apply-templates select="key"/>
        </node>
    </xsl>

    <xsl :template match="key">
        </xsl><xsl :element name="{translate(text(), ' ', '_')}">
            <xsl :value-of select="following-sibling::*"/>
        </xsl>
  </xsl:template>
</xsl:stylesheet>

At this point, I could have just written a google maps page to read my data, but there is a complication. NMEA format presents data in degrees and minutes, whereas google maps and many other applications want to use decimal degrees. I wrote another stylesheet that translates the NMEA data to decimal degrees and then throws out the results in someting that I could copy and paste into an existing google maps page. This is not pretty (and the XML code is not as neat as I would like), but I wanted something running quickly, so here is the code I used:

< ?xml version="1.0" ?>
<xsl :stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl&:output method="html" omit-xml-declaration="no"
     doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
     doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" />

  </xsl><xsl :template match="node">
    </xsl><xsl :variable name="lat1">
      <xsl :value-of select="substring(latitude,3) div 100" />
    </xsl>
    <xsl :variable name="lon1">
      <xsl :value-of select="substring(longitude,3) div 100" />
    </xsl>
    <xsl :variable name="lat2">
      <xsl :value-of select="floor($lat1)" />
    </xsl>
    <xsl :variable name="lon2">
      <xsl :value-of select="floor($lon1)" />
    </xsl>
    <xsl :variable name="lat">
      <xsl :value-of select="(($lat1 - $lat2) div 0.6) + $lat2" />
    </xsl>
    <xsl :variable name="lon">
      <xsl :value-of select="(($lon1 - $lon2) div 0.6) + $lon2" />
    </xsl>

    var marker = createMarker(new GPoint(-<xsl :value-of select="$lon"
         />,  <xsl :value-of select=”$lat”  />), 0<xsl :if test=”wep=’Yes’”
         >1</xsl>,”<xsl :value-of select=”ssid” /><xsl :value-of select=”mac” />“);
    map.addOverlay(marker);
  </xsl:template>

</xsl:stylesheet>

Having run the plist through xalan again, I coped and pasted the results into the web page example above.