Monday, December 22, 2014

Updated SPOT API example

Several years ago I posted some example code to exercise the SPOT tracker API. SPOT has since both updated their data format and added a JSON feed.  I think both changes are for the better; certainly JSON is easier to deal with than XML and is far more efficient to process.  So, I've updated my code to speak JSON.  Note that in this code I'm pulling the JSON in from a file that I created by downloading the JSON using curl; you should be able to pull it in directly from SPOT by putting together a URL following the instructions on their API page (I did it using a local file because SPOT, frankly, is a bit parsimonious about server traffic).  Nearly all of the change was to the function extract_gps_data().

Note that the data are simply a JSON-ized version of the data shown at the SPOT API page.

I've put the code up on Github, as well.  The caveats from the previous API post still apply - don't write a tracker that runs in the browser, don't write it in Javascript, don't hit the servers (either SPOT or Google Maps) more often than absolutely necessary, etc.  Be sensitive to privacy issues, and what you're revealing when you write a tracker that's publicly available. 

Let me know if you identify errors in the code or if you have questions!  Most of all, have some fun with this.



<!DOCTYPE html>
<html>
<head>

 <title>My Wee Tracker</title>
 <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
 <style type="text/css">
  html { height: 100% }
  body { height: 100%; margin: 0; padding: 0 }
  #map_canvas { height: 100% }
 </style>
 <script type="text/javascript" 
  src="http://maps.googleapis.com/maps/api/js?key=AIzaSyBFoJjPtS9vWXIENOa-egd0XFFnnQbfTIk&sensor=false&libraries=geometry">
 </script>

<script type="text/javascript">
//<![CDATA[


// convert text from the tracker data to a JSON object and
// pull out deeply-nested data elements

function extract_gps_data(trackerdata)  {
    var points = new Array();

    var track_data = JSON.parse(trackerdata);
    var messages = track_data['response']['feedMessageResponse']['messages']['message'];

    for (i = 0 ; i < track_data['response']['feedMessageResponse']['count'] ; i++)  {
        var timestamp = messages[i]['dateTime'];
        var latitude = messages[i]['latitude'];
        var longitude = messages[i]['longitude'];
        var point_holder = new point(timestamp, latitude, longitude);
        points.push(point_holder);
    }
    return points;
}


// "point" is an object we use to hold the data we'll be putting on the map

function point(timestamp, latitude, longitude)  {
    this.timestamp = timestamp;
    this.latitude = latitude;
    this.longitude = longitude;
}

function get_track(url)  {
    var request = new XMLHttpRequest();
    request.open("GET", url, false);
    request.send();
    return request.response;
}

function makeinfobox(pointnum, thispoint, theotherpoint)  {
    var latlnga, latlngb; 
    var distance;
    var infoboxtext;
    var timestamp;
    
    timestamp = new Date(thispoint.timestamp); // we convert it from ISO format to something more readable
    infoboxtext = String(timestamp);
    if (pointnum > 0 && theotherpoint)  {  // no point calculating distance on the point
        latlnga = new google.maps.LatLng(thispoint.latitude, thispoint.longitude);
        latlngb = new google.maps.LatLng(theotherpoint.latitude, theotherpoint.longitude);
        distance = google.maps.geometry.spherical.computeDistanceBetween(latlnga, latlngb) / 1610; // convert to miles
        infoboxtext = infoboxtext + "<br />" + distance.toFixed(2) + " miles";
    } 
    return infoboxtext; 
}

function initialize()  {
    var points;
    url = "spot_track.json";
    trackline = new Array();

    trackerdata = get_track(url);
    points = extract_gps_data(trackerdata);

    var spot = new google.maps.LatLng(points[0].latitude, points[0].longitude);
    var my_options = {
        center: spot,
        zoom: 12,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    var map = new google.maps.Map(document.getElementById("map_canvas"), my_options);
    for ( i = 0 ; i < points.length ; i++ )  {
        var contentstring = "Point " + i; 
        var spot = new google.maps.LatLng(points[i].latitude, points[i].longitude);
  // here we create the text that is displayed when we click on a marker
        var windowtext = makeinfobox(i, points[i], points[i+1]);  
        var marker = new google.maps.Marker( {
            position: spot, 
            map: map,
            title: points[i].timestamp,
            html: windowtext
        } );
  // instantiate the infowindow
  
        var infowindow = new google.maps.InfoWindow( {
        } );

  // when you click on a marker, pop up an info window
        google.maps.event.addListener(marker, 'click', function() {
            infowindow.setContent(this.html);
            infowindow.open(map, this);
        });

  // set up the array from which we'll draw a line connecting the readings
        trackline.push(spot);
    }  
 
 // here's where we actually draw the path 
    var trackpath = new google.maps.Polyline( {
        path: trackline,
        strokeColor: "#FF00FF",
        strokeWeight: 3
    } );
    trackpath.setMap(map);
}

//]]>

</script>
</head>

<body onload="initialize()">

<div id="map_canvas" style="width:100%; height:100%"></div>

</body>
</html>

Saturday, December 20, 2014

Catching up with the SPOT API

Over the holiday I'm planning on putting out an update of the sample code that uses the SPOT API, both to work with the new format and to work with the JSON representation rather than the XML.  In the meantime, here's a sample JSON element representing a single SPOT tracker message:


{
'    @clientUnixTime': '0',
    'batteryState': u'GOOD',
    'dateTime': '2014-12-19T07:34:35+0000',
    'hidden': 0,
    'id': 349242506,
    'latitude': 64.82709,
    'longitude': -148.99679,
    'messageType': 'TRACK',
    'messengerId': '0-8283550',
    'messengerName': 'Melinda\'s tracker',
    'modelId': 'SPOT2',
    'showCustomMsg': 'Y',
    'unixTime': 1418974475
}

The (minimal) SPOT api documentation is available online here.  More later!