Is that a Speedo in your pocket?

Why yes it is…

I’ve been sitting on this little idea for a while and as a bit of fun I finally got around to putting it together and properly testing it. Basically on an iPhone with geolocation support (3.0+), I have set up a little web app that will get the speed from the GPS and move the speedometer needle according to your current speed in kilometres.

But speed returns null?

When I originally played around with the geolocation API on the iPhone checking the speed attribute always returned null, what I failed to actually do was test this value while actually moving. The speed attribute is capable of getting speeds as low as 2km/h so walking with the phone will start to register speeds.

So we can get the speed let’s use it

if(navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(getSpeed, handleError);
}
function getSpeed(pos) {
    updateStats(pos);
	
    var coordsListener = navigator.geolocation.watchPosition(updateStats, handleError);
}

Here we get the current position and upon success tell it to execute the getSpeed function. Inside that function I call another function which does all the hard work which I’ll explain below, next I call the watchPosition method and pass it the same updateStats function so we can get constant updates from the GPS about our speed and adjust our speedometer needle.

function updateStats(pos) {
    var speed = pos.coords.speed,
         speedVariation = 3.6,
         currentSpeed = Math.round(speed*speedVariation),
         angleVariation = 0.8,
         initSpeedAngle = -105,
         speedAngle = initSpeedAngle + (currentSpeed * angleVariation);
	
    needle.style.webkitTransitionDuration = '500ms';
    needle.style.webkitTransform = "rotate(" + speedAngle + "deg)";
    speedVal.textContent = currentSpeed + "km/h";
}
var speed = pos.coords.speed,
     speedVariation = 3.6,
     currentSpeed = Math.round(speed*speedVariation);

When I get my initial position and my subsequent position updates through the watchPosition method, I access the current speed, in metres, through the coords attribute. To work out the rough speed I times the speed by the speedVariation var which is 3.6 which will give me the speed in km/h, for miles/hour the variation is 2.237. I do this calculation inside the Math.round function so I get a whole rounded number with no decimal places.

var angleVariation = 0.8,
     initSpeedAngle = -105,
     speedAngle = initSpeedAngle + (currentSpeed * angleVariation);

To calculate the corresponding angle to the equivalent speed we are currently getting from the GPS I had to work out the angle variation per kilometre. I knew there was 16° between each 20km/h block. The angle variation then could be worked out to be 0.8° per kilometre, I also needed to have the starting angle of 0km/h and that is exactly -105° from our middle point. With those 2 values plus the currentSpeed I could then work out the angle so the speedometer needle could be correctly rotated to the right spot.

needle.style.webkitTransitionDuration = '500ms';
needle.style.webkitTransform = "rotate(" + speedAngle + "deg)";
speedVal.textContent = currentSpeed + "km/h";

To move the needle when the speed changes and to make that change animate between the two points I use CSS3 transforms and transitions. By setting our -webkit-transition-duration any changes done using the -webkit-transform property will be transitioned smoothly between the current angle and the new angle. The last bit is just to print out the speed so we can compare the needle position to the actual speed being read.

Some CSS3

I’ve added some extra CSS3 to the speedo including animations and box-shadow

#dial #needle
{
    -moz-box-shadow: 1px 1px 3px #ffd712, -1px -1px 3px #feb018;
    -webkit-box-shadow: 1px 1px 3px #ffd712, -1px -1px 3px #feb018;

    -webkit-animation-name: speedorgo;
    -webkit-animation-duration: 2s;
}

To add the slight glow around speedo needle by using a dual box-shadow so it appears on both edges. The -webkit-animation- properties allows me to specify a predefined animation that can be applied to different elements.

@-webkit-keyframes speedorgo {
    0%   { -webkit-transform: rotate(-105deg); }
    20%  { -webkit-transform: rotate(41deg); }
    100% { -webkit-transform: rotate(-105deg); }
}

By using webkits @-webkit-keyframes rule and giving it a unique name which we applied to our speedo needle in the previous CSS using the -webkit-animation-name property we can do some pretty cool animations. Inside this animation rule I set up the animation to rotate any element it’s applied to to go from -105° to 41° then back to -105° when you rotate the phone into landscape mode it’ll trigger the animation and do the animation over a 2 second period.

Pfft! Who needs the internet…

Well technically we initially need the internet but after we load the web app for the first time no connection is required for this to work. By taking advantage of the application cache we can store all our assets on the phone, and since the GPS can still work without a network connection the speedo will still function fine.

A clear sky

In order to pick up a consistent speed reading from the GPS you will need to have the phone with a good clear view of the sky. I’ve found when using it while walking the speed reading will fluctuate quite a lot, going from 0 to the actual speed your doing and back to 0 and back up etc. It becomes more reliable the faster you are moving. I’m sure with some adjustments to the code getting a more consistent speed reading will be possible.

Go forth and hack

We have a fun little tech demo that shows what can with some simple JavaScript and CSS3. I don’t see why this couldn’t work on an Android, Palm Pre or a Blackberry with the new webkit browser, all of these should or should very soon support the features required for the web app to work. If you get this working in any other smart phone please leave a comment.

[link href=”http://cssn.in/ja/024″]