Is that a Speedo in your pocket?

Mar 13
 
 
 
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.

Short URL: http://cssn.in/ja/024

 
 
 

Post filed under: css, html5, javascript.

Skip to comment form.

Comments

  1. Sweet! Great idea man. :) Maybe this could be tweaked to be a cool universal pre-loader as well?

    John Pencola, March 13th, 2010
  2. Thanks John. You can do anything you like with it, grab the source files and have a crack at doing a pre-loader.

    The Css Ninja, March 13th, 2010
  3. I don’t have an iPhone with my (sadly) but a friend tried this script and told me it doesn’t even ask for GPS access. The needle goes up and then goes to zero in a second.

    He says it isn’t working. Something is wrong with my friend’s iphone? works for every iphone?

    Javier, March 28th, 2010
  4. Now that I try it on my ipod touch, the needle does go to 180 and then to zero, and stays there. I ran all over the office like an idiot (I run like idiots do, kinda like a chicken) and the needle didn’t went up again.

    And one more thing: if I open the example with FF3.x it does ask me to share my location, while on my ipod touch (and as far as my friend told me, on his iphone) doesn’t. It doesn’t ask to share the location.

    If I set my ipodtouch location services to OFF it does tell me to activate it on the settings, but that’s it. If I have it ON, it doesn’t ask me to share any location or record anything, which is crazy.

    Javier, March 28th, 2010
  5. Dude, new info:

    If I leave it alone for a while it tells me the following in a message box:
    http://labs.thecssninja.com Something has gone wrong in getting your location.

    I suspect that is because I have an iPod Touch instead of an iPhone. But now, why my friend with the iPhone can’t see this with his Location services on?

    And what is worst: it never ask him or me to share my location.

    Just trying to test it! XD

    Javier, March 28th, 2010
  6. @Javier –

    Now that I try it on my ipod touch, the needle does go to 180 and then to zero, and stays there.

    That’s just a CSS3 animation that runs when you first rotate it to landscape. It’s not getting that from the GPS.

    I ran all over the office like an idiot (I run like idiots do, kinda like a chicken) and the needle didn’t went up again.

    Lol, for this to get a good reading you need to: have an iPhone 3G at least needs a GPS (iPod won’t work) and be outside with a clear view of the sky. I can get it to read speeds while I’m walking outside but it fluctuates between my actual speed and zero. To get consistant speed readings run it while you’re in a car or train.

    Get your friend with his iPhone to try my other GPS demo and see if that works. Both of these demo also prompt for access to my location in Firefox 3.6

    If I leave it alone for a while it tells me the following in a message box: http://labs.thecssninja.com Something has gone wrong in getting your location.

    That alert message is attached to the geolocation error event so obviously something has gone wrong.

    Get your friend with the iPhone to try my other GPS demo. All my GPS demo’s should work in Firefox 3.6 it’ll prompt you for access to your location.

    The Css Ninja, March 28th, 2010
  7. My man!
    Just got a word from my friend on UK. He told me it didn’t worked while he was at his home, but once he got outside and drove his car, the speedo start moving and it actually works!

    So it only works if you ride a car / bus / train, which makes it perfect for what I wanted!

    I love you! come close, let me man-kiss you!

    Javier, March 31st, 2010
  8. After upgrading my 3GS to iOS 4.0 I can’t seem to get a reading on the speedo. I answered yes to all questions about allowing access to GPS when the WebApp i started from my Home Screen.

    I’ll try your other GPS demo.

    /Mogens

    Mogens Beltoft, June 18th, 2010
  9. Hmmm.. your other GPS demo places me at Apple HQ, Infinity Loop, Cupertino.

    /Mogens

    Mogens Beltoft, June 18th, 2010
  10. @Mogens B – Hmm I’ve been away on holidays for the past 2 months so I have to upgrade my phone will check them out when I do.

    Thanks for the heads up. If you figure out the issue in the mean time let me know.

    The Css Ninja, July 5th, 2010
  11. Actually – I guess I have solved it. I left a note from your contacts page, but the essense is seems to help to add options to the navigation calls:

    var options = {
      timeout: 10000,
      maximumAge: 20000,
      enableHighAccuracy: true
    };
     
    coordsListener = navigator.geolocation.watchPosition(TCNPS.updateStats, TCNPS.handleError, options);

    and

    navigator.geolocation.getCurrentPosition(TCNPS.getSpeed, TCNPS.handleError, options);

    /Mogens

    Mogens Beltoft, July 9th, 2010

Trackbacks

  1. [...] iPhone Speedometer Kilometers/hr using geolocation API [...]

    Attack Of Deliciously Colorful Code That Caused Me To Explode | Tweeaks Design, March 31st, 2010

Leave a comment