Using Geolocation with Vue.js

Using Geolocation with Vue.js

I decided to spend my lazy Sunday morning working on a quick Vue.js post. Geolocation is one of the older and simpler APIs you can use with your web browser so this article won't necessarily be that exciting, but I thought a quick demo of the API with Vue, and a few variations, could be useful to folks. As a reminder, web pages that use Geolocation must be run on either localhost or an https server. This is a security precaution and... let's be honest - there is zero reason to be using a non-secure server in 2019.

Example One

For the first example, let's build a simple Vue application that will:

  • Automatically try to get your location
  • Display a "loading" type message while this is happening
  • And properly support error conditions.

First we'll build the front end:

<div id="app" v-cloak>
  
  <div v-if="errorStr">
    Sorry, but the following error
    occurred: {{errorStr}}
  </div>
  
  <div v-if="gettingLocation">
    <i>Getting your location...</i>
  </div>
  
  <div v-if="location">
    Your location data is {{ location.coords.latitude }}, {{ location.coords.longitude}}
  </div>
  
</div>

I've got three divs here. The first handles displaying an error. The second is the loading message. And the final div displays our location. Now let's look at the code.

const app = new Vue({
  el:'#app',
  data:{
    location:null,
    gettingLocation: false,
    errorStr:null
  },
  created() {
    //do we support geolocation
    if(!("geolocation" in navigator)) {
      this.errorStr = 'Geolocation is not available.';
      return;
    }

    this.gettingLocation = true;
    // get position
    navigator.geolocation.getCurrentPosition(pos => {
      this.gettingLocation = false;
      this.location = pos;
    }, err => {
      this.gettingLocation = false;
      this.errorStr = err.message;
    })
  }
})

I'm using the created method to start requesting location as soon as the application is ready. I do a quick check to see if the API is supported. After that, I simply use the API. It's all rather simple, but even this code could be improved. You'll notice that my front end is addressing the result as location.coords.latitude. If I know for a fact that I only need latitude and longitude, I could copy those values out. My front end code could then look something like this:

Your location data is {{ latitude }}, {{ longitude}}

That's a bit better in my opinion as the layout code is simpler and not directly tied to knowing that the Geolocation API was used. You can play with this example here:

See the Pen Geolocation 1 by Raymond Camden (@cfjedimaster) on CodePen.

Example Two

In my next example, I'm going to switch the code so that it doesn't request your location until the user actually needs it. In this case I'm going to use a simple button to kick off that process. Here's the HTML:

<div id="app" v-cloak>

  <p>
    Let us locate you for better results...
    <button @click="locateMe">Get location</button>
  </p>
  
  <div v-if="errorStr">
    Sorry, but the following error
    occurred: {{errorStr}}
  </div>
  
  <div v-if="gettingLocation">
    <i>Getting your location...</i>
  </div>
  
  <div v-if="location">
    Your location data is {{ location.coords.latitude }}, {{ location.coords.longitude}}
  </div>
  
</div>

Most of the layout above is the same with the exception of the paragraph and button on top. For the code, I decided to abstract things a bit. The locateMe method referenced by the button will be simpler as I've migrated out the Geolocation stuff. Let's take a look.

const app = new Vue({
  el:'#app',
  data:{
    location:null,
    gettingLocation: false,
    errorStr:null
  },
  methods: {
    async getLocation() {
      
      return new Promise((resolve, reject) => {

        if(!("geolocation" in navigator)) {
          reject(new Error('Geolocation is not available.'));
        }

        navigator.geolocation.getCurrentPosition(pos => {
          resolve(pos);
        }, err => {
          reject(err);
        });

      });
    },
    async locateMe() {

      this.gettingLocation = true;
      try {
        this.gettingLocation = false;
        this.location = await this.getLocation();
      } catch(e) {
        this.gettingLocation = false;
        this.errorStr = e.message;
      }
      
    }
  }
})

If you focus on locateMe, you can see it is much simpler. I use async and await to call getLocation. My method handles things like the loading screen and errors, and the result, but the actual mechanism of the location request is now abstracted away. getLocation makes use of a Promise to properly work with async and await, but outside of that it's mostly the same as before.

You can test this version here:

See the Pen Geolocation 2 by Raymond Camden (@cfjedimaster) on CodePen.

Option Three

For one last example, let's do something fun with the location. Most people can't translate a longitude and latitude into something useful. It would be cooler if we could use reverse geocoding (which is the process of attempting to map a latitude/longitude to a place with a name) to display the user's location in a friendlier name. For this example I'm going to be making use of the Geocoding API by HERE. Disclaimer - I started working for HERE last week so I'm talking about my employers products. This API (and many more) have a free tier so you can play with them all you want!

The API is rather extensive (you can see the docs here) but I'll focus on the simplest example. To begin, I created a new JavaScript project in my HERE account. This gave me an API key I could then use in my code. I added two HERE JavaScript libraries and then this bit of initialization code:

const platform = new H.service.Platform({
  'apikey': 'iEnZe8bO68AnNVZEdPpq7hl9UFqiPxTSPjQkLfR3Qcg'
});
const geocoder = platform.getGeocodingService();

Note that you can specify a domain whitelist for your API keys which will make the code above perfectly safe for your public web pages. Once you've configured your geocoder, to do a reverse geocode you can simply do this (pseudo-code):

let reverseGeocodingParameters = {
    prox: 'Latiude,Longitude', // not literaly that, but the real values
    mode: 'retrieveAddresses',
    maxresults: 1
};

geocoder.reverseGeocode(
    reverseGeocodingParameters,
    res => {
        // work with results
    },
    e => reject(e) 
);

Here's the updated JavaScript for getLocation:

async getLocation() {
    
    return new Promise((resolve, reject) => {

    if(!("geolocation" in navigator)) {
        reject(new Error('Geolocation is not available.'));
    }

    navigator.geolocation.getCurrentPosition(pos => {
        let reverseGeocodingParameters = {
            prox: `${pos.coords.latitude},${pos.coords.longitude}`,
            mode: 'retrieveAddresses',
            maxresults: 1
        };

        geocoder.reverseGeocode(
        reverseGeocodingParameters,
        res => {
            let results = res.Response.View;
            if(results.length === 0) {
                resolve('No match.')
            } else {
                resolve(results[0].Result[0].Location);
            }
        },
        e => reject(e) 
        );
    }, err => {
        reject(err);
    });

    });
},

For the most part this is just a simple update to the previous example, but do note that when I leave the function, I "dig down" into the Geocoder result to simplify things a bit: resolve(results[0].Result[0].Location);

The HTML now uses this:

<div v-if="location">
    Your location data is {{ location.Address.Label }}
</div>

If you remember what I said about Option One, I kind of don't like my HTML having too much knowledge about the data so a nicer solution would probably just store Address.Label to location. You can run this here:

See the Pen Geolocation 3 by Raymond Camden (@cfjedimaster) on CodePen.

As always, let me know what you think and ask any questions in the comments below. There's also multiple options for Vue components to simply Geolocation for you. One is vue-browser-geolocation.

Header photo by Paula May on Unsplash

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Armish Sonkar posted on 12/5/2019 at 9:31 AM

Thanks a lot for introducing "HERE", hope it would give us street names in Indian cities :)

Comment 2 (In reply to #1) by Raymond Camden posted on 12/5/2019 at 2:31 PM

I believe our data is good in India but I haven't tested. Please give it a shot and let me know!

Comment 3 by Armish Sonkar posted on 12/6/2019 at 10:30 AM

Hi Raymond, I am observing this error while calling the api from a Vue Cli dev server (https call), the url seems to be taking app_id and app_code equal to null but I believe we only need apikey, am i right ?

Sorry, but the following error occurred: [timeout] http://reverse.geocoder.api... request failed

Comment 4 (In reply to #3) by Armish Sonkar posted on 12/6/2019 at 11:19 AM

It worked after generating APP_ID and APP_CODE

Comment 5 by Leandro Landim posted on 12/25/2019 at 12:21 AM

Hey, I'm using the first option but in my main.js I have the following code




new Vue({


router,


store,


render: h => h(App),


}).$mount('#app');


My question is how a configure the geolocation with since in the main.js file?

Comment 6 (In reply to #5) by Raymond Camden posted on 12/26/2019 at 2:23 PM

It sounds like you are using the Vue CLI to build an application, and in that case you would use Geolocation in one of your components, not the main.js file.

Comment 7 by Roark McColgan posted on 3/2/2020 at 8:00 AM

Hey Raymond, thanks for the great post.
Looking at HERE api, is there a way to calculate the distance between 2 GPS coordinates "as the crow flies" rather than a route by driving or walking?

Thanks!

Comment 8 (In reply to #7) by Raymond Camden posted on 3/2/2020 at 3:09 PM

Nope, but that's "simple" math, and by simple math, I mean not simple at all, but it's been done before. If you google for it, you can find JS implementations. Here's one example: https://www.geodatasource.c...

Comment 9 (In reply to #8) by Roark McColgan posted on 3/3/2020 at 2:11 PM

Now that is awesome! thank you!

Comment 10 by rowild posted on 1/24/2021 at 9:28 PM

Nice tutorial! And also HERE seems to be a fine service! - But since this is a Vue tutorial, you really should explain how you add those HERE scripts and not just leave the user with a "find it on your own in the docu" IMO. Currently, if you don't write the script on CodePen, you get a "H is undefined" error...