Since my first year at university, I’ve been looking for an app that would allow me to enter a start and end location on a map, and for it to return directions between those two points, with all the drinking establishments along the route.
Over the years, I’ve come across many websites and apps that attempt to do this, however, most don’t hit the nail on the head, and others make it far too complicated. Some even fail to utilise the API’s of a service such as Google Map, Foursquare or even Facebook, and, as a result, did not have anywhere near enough results.
After giving up on my search, I recently became in need for such a service and I found a new app that came close to solving the problem, but alas, it was only available in London. So for my Cookies Lab week, I decided I would attempt to create an app to solve the problem.
Enter Bar Voyage
The clever name was suggested by Gemma Stephen. This proof of concept version of Bar Voyage allows you to enter two locations (Start and End) then show Google Maps with directions between the two points and drops pints on the locations.
Easy Enough!
We will start by breaking down my large problem in to smaller manageable chunks.
- Have two input fields (Start and End), maybe use Google Place Autocomplete API
- Send the two locations to Google Map Directions API to get a path / route
- Find a way to return all the “Bars” along that route
Using Google Map / Places API it was easy and straight forward to init a map.
First we call the Map API with a few libraries that we’ll use later on.
https://maps.googleapis.com/maps/api/js?key=[API-KEY]&libraries=places,visualization
<div id="map"></div>
#map {
width: 100%;
height: 100%;
top: 0;
left: 0;
position: absolute;
}
// You get access to the 'google' object when you add the google map api
var mapOptions = {
center: new google.maps.LatLng(0, 0),
mapTypeId: google.maps.MapTypeId.ROADMAP,
zoom: 2,
disableDefaultUI: true
};
map = new google.maps.Map(document.getElementById("map"), mapOptions);
Simply by following the Google Map guides I was then able to integrate the direction service.
Routeboxer
At this point, we have two input fields that use the Google Place autocomplete API to get the start and end points and directions between the two location when you search. The first major blocker was that we couldn’t find anything on Google API docs about being able to let you search along a route, which is the cornerstone of the app.
A reliable solution to this problem is Routeboxer. Simply put, Routeboxer will take our direction Polyline
object and it will return a set of boundaries that will cover the entire map.
For example:
The more bounding boxes it has, the more API calls it will make, but you will get more relevant results along a route. The good thing about Routeboxer is that we can reduce the bounding boxes so only results within a few metres are shown. We can also increase the bounding boxes so that it performs less requests, but the trade off being that we risk getting results that will cause you to deviate from our route.
Integrating Routeboxer in to Bar Voyage was simple:
// Direction service for route boxer
var directionService = new google.maps.DirectionsService();
var routeboxer = new RouteBoxer();
var distance = 0.01; // km
directionService.route(request, function(result, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsRenderer.setDirections(result);
// Box around the overview path of the first route
var path = result.routes[0].overview_path;
bounds = routeBoxer.box(path, distance);
searchBounds(bounds);
} else {
alert("Directions query failed: " + status);
}
});
/*
* The search function is wrap around a for loop with a setTimeout so that the request are throttled to an avg of 10 a second that is the max request you can make on google
*/
function searchBounds(bound) {
for (var i = 0; i < bound; i++) {
(function(i) {
setTimeout(function() {
// Perform search on the bound and save the result
performSearch(bound[i]);
//If the last box
if ((bound.length - 1) === i) {
addAllMarkers(bound);
}
}, 400 * i);
}(i));
}
}
function performSearch(bound) {
var request = {
bounds: bound,
keyword: 'bars'
};
currentBound = bound;
service.radarSearch(request, callback);
}
// Call back function from the radar search
function callback(results, status) {
if (status !== google.maps.places.PlacesServiceStatus.OK) {
console.error(status);
return;
}
for (var i = 0, result; result = results[i]; i++) {
// Go through each result from the search and if the place exist already in our list of places then done push it in to the array
if (!placeExists(result.id)) {
allPlaces.push(result);
}
}
}
The radar search returned results that were some times up to 3 times outside the radius of our search, so upon getting the result of a place, we check the geo location to make sure it is within one of our boundaries.
bound.contains(new google.maps.LatLng(allPlaces[j].geometry.location.lat(), allPlaces[j].geometry.location.lng()))
Examples
Some places around bristol
Next Steps
As it stands, Bar voyage solves our problem, but improvements to the UX and design are to be made in order for the experience to be smoother and pain-free. A few places might be missing from Google Maps that may be available on other services such as Foursquare or Facebook. RouteBoxer has a bound using geolocation, so we should be able to search on other APIs producing more results and filtering out irrelevant results such as “Fish bar”.
Bar Voyage will be available soon on the App and Play store.