Retro HUD Web App

Map apps have gotten boring. I thought it would be fun to inject some 90's video game chaos into the field

07 October 2021

What is it?

I've built a different kind of navigation app. Whereas you might typically have a map with a little blue dot and a line directing you to your destination, that's radical enough for me. I want something a little more exciting, a little more _interesting_ (even if it's possibly not the most effective means of getting to where your going).

So, I've made a progressive web app that acts as a heads up display in my car windscreen that points me to the next waypoint on the way to my destination as determined by Amazon Location Services.

How does it work (in brief)?

Your phone is filled with a bunch of cool sensors that you can access through a web browser. With a GPS and a compass you know where you are, and where you're going. Add in a spinkle of mathematics and JavaScript and you can figure out which direction and how far you should be going relative to where you are now!

Simply enter where you want to go, and place your phone on the dashboard and the big, friendly (dare I say cool?) green arrow will tell you which way you should be going. Neat, huh?

Why would you do this?

The Rules of being a Londoner

This year, I did something no Londoner should ever do: I bought a car!

You see, I spent 15 months in a two-bed flat in Brixton during the (ongoing) 2020 - 2021 COVID-19 pandemic. That wasn't fun, but after things started to ease up a little I finally managed to get home to my family and declared that never again would I be stuck in one place away from everyone that I love for so long.

So I bought a car: sporty, sleek, majestic - It's none of those, I bought a 2014 Toyota Yaris; The perfect car for a man who doesn't want to worry about his car.

A picture of my gorgeous 2014 Toyota Yaris

Look at her! Isn't she a B.E.A.youty?

So, what's it like having a car?

I haven't owned a car in over a decade, and aside from the odd rental I've barely driven at all in the interceding years, but do you know what I've remembered since getting this 2 ton box of joy? I **LOVE** driving. Aside from the neverending, joyless pursuit of finding an empty parking space in London, I love being able to get about in it.

And, now I think back on it, that's probably come from all of the driving games I played as a kid. From Sega Rally Championship on the Sega Saturn (yes, I had a Saturn) to Mario Kart, I've always loved kicking back and going full throttle around a racetrack, but one game sticks out in my mind: Crazy Taxi.

A screenshot of the 90's game 'Crazy Taxi'

Real life doesn't compare to Crazy Taxi though. Where in Crazy Taxi your goal is to speed around a city picking up people passengers and defying every rule in the Highway Code to get them to their destination as quickly as possible with the help of a big, spinning, green arrow - London is filled with low-traffic networks and 20 MPH zones.

And this got me to thinking... how can I make my life a little bit more like Crazy Taxi (without breaking any driving regulations).

Living that Crazy Taxi Lifestyle

Well, I have a big old piece of glass sitting in front of me in my car whenever I drive it, and you may have noticed I have something of a penchant for reflecting images off of them [1] [2] to create 3D effects.

What if, instead of using my normal navigation application to get me from A to B, I could have the big ol' Crazy Taxi arrow pointing to my next turn?

Well, with a mobile phone, a progressive web app and Amazon Location Services you can do exactly that!

A screenshot of the demo application I've made: 'Pathfinder'

How does it work (in detail)?

Here's the kind of architecture you can build to make this sort of thing.

An architecture diagram showing the parts of the 'pathfinder app'

A webserver hosts and serves both the front-end application code, and an API which a user can use to search for a destination and plot a route which makes requests to the Amazon Location Services.

The Front-End

Smart phones have a plethora of sensors and radios in them, and web browsers have a bunch of APIs that let you access them from a standard web page. No need for a clunky ol' native app taking up space on your local storage.

With a Progressive Web App (PWA) you can create an application that lives on the home screen of your mobile device (except iOS, but it's not our fault that's stuck in 2010), can go full-screen, and work offline if needs be.

Of all of these features and technologies, the two we really need access to are the compass and the GPS. The compass can be accessed in any supported browser by binding an event listener for `deviceorientationabsolute` to the `window` object like so:

window.addEventListener('deviceorientationabsolute', function(e){
                                     // Degrees offset from north.
    const compassHeading = e.alpha;  // 0 means we're facing north.
                                     // 180 means we're facing south.
}, false);

The `absolute` part of `deviceorientationabsolute` is important. When using the Device Orientation API, the heading that will typically be returned is the current heading relative to where you were facing when you first request the information.

This is super handy if you're building a virtual/augmented reality application, but we're a navigation app, we need a fixed point to orientate ourselves in a given location so that we can calculate which direction we should be heading in next.

`deviceorientationabsolute` returns the heading relative to magnetic north which is perfect for our needs.

Getting the current GPS coordinates of our device is much the same as how we access the compass information, except that we call a method with a callback on the `navigator.geolocation` object, like so:

function success(e){

    const latitude = e.coords.latitude;   // Our current latitude
    const longitude = e.coords.longitude; // Our current longitude

}

function error(err){
    console.error('watchPosition err:', err);
}

navigator.geolocation.watchPosition(success, error, { enableHighAccuracy: true, maximumAge: 0, timeout: 10000 } );

With those two APIs we have everything we need to track our progress as we follow a route.

The Back-end

Set a course, Captain!

The back-end in this instance is a little Node.js server that routes requests for location finding and routing through to Amazon Location Services.

In order to plot a route, you first need to know where you are (which we've dealt with on the front-end) and where we're going, which the Location Services can help us with. To find a place that we want to go to, we can use the `searchPlaceIndexForText` class in the AWS SDK. This will take our input and return a list of possible destinations you want to go to - like a postcode, or a store name, for example.

const searchPlaceIndexForTextParams = {
    IndexName : process.env.PLACE_INDEX_NAME,
    Text : location,
    BiasPosition : [, ]
};

return client.searchPlaceIndexForText(searchPlaceIndexForTextParams).promise()
    .catch(err => {
        debug('searchForALocation err:', err);
        throw err;
    })
;

Once you know where you're going, you just need to figure out how to get there! and that's where the `calculateRoute` class comes in. You can use it like so:

const calculateRouteParams = {
    CalculatorName : process.env.ROUTE_CALCULATOR_NAME,
    TravelMode : "Car",
    DeparturePosition : start, // An array with your current position [, ]
    DestinationPosition : end // The same, but for your intended destination as derived from `searchPlaceIndexForText` results
};

return client.calculateRoute(calculateRouteParams).promise()
    .catch(err => {
        debug('getARouteBetweenTwoPoints err:', err);
        throw err;
    })
;

Once we have that we can send over the information to the front-end app where it'll start doing the calculations about where we are, and where we should be going.

You can get the application code and experiment for yourself from GitHub.