Recently I worked on this geolocation-based app that fetches and maps real-time availability of London’s Santander Bikes with Navigator, Google Maps API, Transport For London API. All in vanilla JavaScript.
💻 You can see mine working here💻
I thought it would be good to share a step-by-step guide on how to build it. This could be easily replicated for your own city, providing the local authorities or the service you want to use have an API. A similar take would be fetching local shops or restaurants from the Google Places API, and getting reviews or any other data.
First of all, we want to breakdown our plan into small tasks, so here’s what we need to do:
Steps
1. Get the users location from the browser
2. Render a Google map and create a request to Google Maps using the users location
3. Create a request to Travel For London (TfL) for bike information
4. Parse the Tfl API responses and add the data to the map
5. Retrieve specific info and create and InfoWindow
6. Add bikepoint info and image elements to the InfoWindow
7. Add custom markers
1. Get the user's location from the browser
In order for this app to be more relevant to a Londoner, we want to retrieve the user's location and show the bikes available nearby.
"Right, but how on earth do we do that?", you ask. 🧐
We will use the Navigator object for this. The Navigator object lives under the hood in your browser, and it allows scripts to query state and to register themselves to carry on some activities.
There's a lot of properties in the Navigator object, which you can read more about here, but for this purpose we want to use Geolocation.
The
Navigator.geolocation
read-only property returns a Geolocation object that gives Web content access to the location of the device. This allows a Web site or app to offer customised results based on the user's location.
Once we call navigator.geolocation
, it will prompt the user to allow for their location to be shared. If you're on Google Chrome you will see this alert:
In Firefox, for example, you will likely have to change the geolocation sharing permissions in the settings.
This will be done on pageload so we can handle the success case (as in, getting the user's current position).
We take in createMap
as parameter. This will be a function that we'll create in Step 2.
If sharing location was not allowed, we will get an error on the console.
2. Render a Google map and create a request to Google Maps
For the next step, we want to render a Google map on our page which we'll wrap in a function.
You will have to sign up with Google Maps Platform and get an API key. Here's how you get started.
Once you have your API key, you'll have to include it in your Maps JavaScript API request as per the documentation. Include the Google script
tag in your html file.
Then, we create a request to Google Maps using the user's location. The position.coords
are retrieved from navigator.geolocation.getCurrentPosition()
from Step 1.
map
is just an id given to a <div>
so we can use it as a selector. We want to center the map based in the current location, so we use center: { lat: latitude, lng: longitude }
which store our position.coords
. We also want to tweak the zoom
property so it's more zoomed in than the default value.
Here's more on geolocation using the Google Maps API, if you would like to try a different approach.
Within the createMap
function we also want to invoke the getBikeData()
, which we'll create in the next step.
Destructuring objects
const { latitude, longitude } = position.coords;
If you've never came across this weird looking syntax this is called destructuring. This was a feature added to ES6 and you can read more about it here.
Destructuring on objects lets you bind variables to different properties of an object. You specify the property being bound, followed by the variable you are binding its value to.
3. Create request to Transport for London API for bike information.
Now we'll write our getBikeData()
function, where we'll create a fetch request to Transport for London API for real-time bike data.
We don't need an API key to fetch data from the TfL API, so we simply use the endpoint provided to retrieve bikepoint data. We'll receive the data in JSON.
Once the fetch response is successful, we want to add our bike markers. We'll create a function for that shortly.
There's several types of data you can get from TfL, which you can access here.
4. Parse TfL API response and add bike locations to map
We want to parse our TfL API response where we take in bikepoints as our function's parameter.
We'll have quite a few bikepoints to parse so we'll run a forEach loop on each of them. We want to store the bikeLocation
as a variable, which will store each bikepoint's latitude and longitude.
Then, for each of them, we want to create a Google marker, so we'll use one of the Google's functions for that. Relevant documentation on markers here. The Google marker is in fact an object which has position
, map
, and icon
as properties. Position will take an object with the bikepoint latitude and longitude. Map will refer to the map we've previously created. And lastly, icon refers to the marker image, which will come to on our last step.
Within our addBikeMarkers
function, we also invoke createBikeInfo()
with bikepoint and marker as parameters. You guessed it, we'll jump onto that now.
This is how your app should look like at this point, with gorgeous markers for each Santander bike point. Gorgeous!
5. Retrieve specific TfL info and create an information window for when a user selects a bike point
It's nice to have all the bikepoints mapped, but we need to provide more info to the user. What happens when you click on a marker? We want to display a window with all info such as number of bikes available, number of spaces available and number of docks in each bikepoint.
We create a function that retrieves and stores this data so we can then display it in a user-friendly format. This window will be prompted when each marker is clicked on.
You'll see in the JSON data, that the Place
object has an addtionalProperties
array which contains the nbBikes
, nbDocks
and nbSpaces
. This actaully gives us real-time values, so we can display an accurate status of all bikepoints in London.
If you notice that there's a mismatch in these numbers (i.e. nbDocks - (nbBikes + nbSpaces) != 0
), this indicates broken docks.
In order to find the exact property we want (nbBikes, nbDocks, nbSpaces), we use the find()
array method so it can find the exact key within that object.
I'm logging a few bits here to confirm that the data that is being pulled is correct.
Finally, we add an event listener to each marker
. Please note, that this is in fact using a Google method that replaces addEventListener
. In here, we use addListener
instead to interact with the Google marker. More on events on the Google Maps API documentation here.
We call the createInfoWindowForMarker()
function (that we will create in the next step) to where we will pass in marker
, bikepoint
and bikeInfo
as arguments.
6. Add bikepoint info and image elements to the InfoWindow
Almost there! In this function we will want to create a InfoWindow to display our bikepoint data.
InfoWindow is a type of overlay provided by Google Maps. It displays content (usually text or images) in a popup window above the map, at a given location. You can read more on InfoWindows here
First, we declare the infoWindow
variable outside the function so it remains in global scope, which we'll assign with a new.google.maps.InfoWindow inside our function. This Google Maps function will take in an object literal which specifies the parameters for displaying the info window.
The one we will use is content
, which is the equivalent to an innerHtml
/innerText
in JavaScript.
In here, we want to pass in html to the DOM, creating a div that holds an image and the data we fetched from the TfL API (the bikepoint name, number of bikes available, number of spaces available, number of docks and also the bike availability percentage). We calculate this percentage in a separate variable above.
You'll see that the first line in our code block here tells an existing open infoWindow to close. We only want to show one infoWindow at a time, so this guarantees that if there is already an infoWindow open this will be closed, so a new one can be displayed.
infoWindow.open(map, marker);
map.setCenter(marker.getPosition());
Lastly, we want to open a new infoWindow which will display the real-time bikepoint info. We pass in the map
and marker
as arguments so it's linked to the marker trigged by the user click. We also want to center the infoWindow in our map. Again, we use setCenter
which is a built-in method that we can use with the Google Maps map, which then takes getPosition()
chained to marker
. getPosition()
is a Google Maps method associated to marker
. If you'd like to learn more on Google Maps map and marker methods, go here.
Et voila! What a beaut.
7. Add custom markers
This is entirely optional as we already have a fully-functioning geolocation based app that tells us the availability of public-use bikes in London. 🤩
For our final step, we just want to give it a little personal touch and add our own custom marker images.
Remember in Step 4 when we created our marker? One of the properties the marker object takes is icon
. The google.maps.Icon
interface is a structure that represents a Marker icon image. There's quite a few properties associated to icon
, you can learn more on that here.
For our InfoWindow we will use url
(pulling an image file of your choice), scaledSize
(instantly scaling the original image to the desired size), and origin
and anchor
(which will position the marker image in correspondence to the location of the marker on the map).
There we have it, a geolocation-driven app using Navigator, Google Maps API and fetching data from the Transport for London bikes API.
-
This is my first ever technical blogpost so would love to know if you found this helpful in any of your projects using the Google Maps API! Your constructive feedback is appreciated.
Top comments (6)
Cool project, compliments.
By the way:
"This could be easily replicated for your own city, providing the local authorities or the service you want to use have an API"
Of course no API for my city 😕
By the way a possible improvement can be an "get directions" button to reach the bike.
Cheers Alan! Appreciate your feedback. What a shame you haven't got a similar API to work with. Although you could try something similar with Google Places, mapping local businesses. 🙂
Good idea re the improvement!
Thanks good idea (I preferred the idea of bikes but better than nothing)
Now I'm trying to create something about hiking trails
Awesome, looks great app. Thanks
Thanks Waleed!
Awesome, looks great app. Thanks for sharing.