Finding Paradise With Mapbox, Turf.js, and MEDUZA
In support of MEDUZA’s hit single “Paradise”
To kick off this year’s client work, I had the opportunity to work with Republic Records and the Italian production trio MEDUZA to help promote their new hit single “Paradise.” The single speaks about the distance separating us and a person or place we wish to get to. Sound familiar?
Oh, My, My, My…
Just a thousand miles between me and paradise
We kicked around a bunch of ideas like figuring out places of interest which were exactly 1,000 miles from you or using AI to attempt to guess your paradise based on prompts. However, I couldn’t shake the simple thought of helping users calculate the distance between them and a location they deemed paradise. I was thinking that this concept allowed for the most amount of personalization and fun. To some people, paradise might be Fiji but for others, it might simply be the bagel shop around the corner.
How far are you from paradise? Head over to the app to find out and read on to learn how the main components were developed.
Locating User
Allowing a user to share their GPS location via the browser requires use of the HTML Geolocation API. If all goes well, the user’s coordinates will come through in the position
payload. However, it is very important to check when GPS fails or the user simply does not want to share their location. The Geolocation API does a pretty good job of letting you know why the call may have been rejected in the error
callback.
navigator.geolocation.getCurrentPosition(position => {
// position.coords
}, error => {
// something went wrong
}, {
timeout: 10000
})
As more browsers and operating systems evolve their privacy settings to protect users from unknowingly giving away their personal details, such as location, it’s important to prepare your experience accordingly. For our app, I added a 10 second timeout, at which point the app switches to the GeoIP service Maxmind provides. This will give us an estimated location for the user based on their IP address. It’s not perfect but works well enough for these purposes and is certainly better than no experience at all.
Finding Paradise
The next step is allowing the user to search for their paradise. The simplest user experience used to search for a location is an input field with a list of results that get populated as the user types. You’ve probably used a similar interface on Google Maps or Apple Maps. This technique is known as forward geocoding, which takes a query, such as Maui, HI
, and turns it into a pair of coordinates. We can use the excellent Mapbox Geocoding API to do exactly this by simply passing the query to the API endpoint.
https://api.mapbox.com/geocoding/v5/mapbox.places/QUERY.json
Let’s break down the overall UX as a simple Vue.js component. First, the HTML should include an input and a dynamic list of found places.
<template>
<div>
<input type="text" v-model="query" placeholder="Paradise is..."> <div id="places">
<div class="place" v-for="p in places" v-bind:key="p.id">
{{ place.name }}
</div>
</div>
</div>
</template>
In the <script>
, we’ll setup a watcher which listens for changes to the query
variable and calls the search()
method. In order to prevent the API from being called after each key press (and running up API calls,) we’ll use the throttle method from Lodash to only call the search()
method every 1.5 seconds. Results are then passed to the places
array and subsequently to the view.
<script>
import throttle from 'lodash.throttle'export default{
data() {
return {
query: '',
places: []
}
},
watch: {
query(newVal, oldVal) {
this.search()
}
},
methods: {
search: throttle(async function() {
let response = await fetch(`https://api.mapbox.com/geocoding/v5/mapbox.places/${this.query}.json?access_token=ACCESS_TOKEN`) let data = await response.json() this.places = data.features
}, 1500)
}
}
Within each place result contains the coordinates you need to calculate the distance between the user and their paradise.
Calculating Distance
Now that you have the both the coordinates for the user and their paradise, you can calculate the distance between them. For this, I use the geospatial library Turf.js. Turf has a distance()
function, among many others, which can be used to calculate the distance between two points using the Haversine formula. You can even choose the unit of measurement, which in our case is “miles,” as sung in the lyrics.
let miles = distance(user, paradise, {
units: 'miles'
})
There’s lots of other interesting elements worth exploring on this project. I’ve made two CodePen.IO pens available which explain how the Oh, My, My, My animation and interactive Gold Foil gradient were developed. In addition, I would suggest checking out the Mapbox Static Maps product which was used to construct the downloadable share image.
Thanks
Thanks to Monica Seetharam and Republic Records for bringing me in on this project and kicking around a ton of ideas before we landed on this one. Special thanks to Club Class Management and to MEDUZA for wanting to build something fun for their fans.
Stream Paradise by MEDUZA featuring Dermot Kennedy today.