How to get visitor's location (i.e. country) using geolocation?

Viewed 317512

I'm trying to extend the native geolocation function

if(navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(function(position) {
        var latitude = position.coords.latitude;
        var longitude = position.coords.longitude;
    });
}

so that I can use the visitor's country name (perhaps return an informative array).

So far all I've been able to find are functions that display a google maps interface but none actually gave what I want, except for this library which worked well in this example but for some reason didn't work on my computer. I'm not sure why that went wrong there.

Anyways, do you know how I can simply return an array containing information like country, city, etc. from latitude and longitude values?

15 Answers

You can do this natively wihtout relying on IP services. You can get the user's timezone like this:

Intl.DateTimeFormat().resolvedOptions().timeZone

and then extract the country from that value. Here is a working example on CodePen.

See ipdata.co a service I built that is fast and has reliable performance thanks to having 10 global endpoints each able to handle >10,000 requests per second!

This answer uses a 'test' API Key that is very limited and only meant for testing a few calls. Signup for your own Free API Key and get up to 1500 requests daily for development.

This snippet will return the details of your current ip. To lookup other ip addresses, simply append the ip to the https://api.ipdata.co?api-key=test url eg.

https://api.ipdata.co/1.1.1.1?api-key=test

The API also provides an is_eu field indicating whether the user is in an EU country.

$.get("https://api.ipdata.co?api-key=test", function (response) {
    $("#response").html(JSON.stringify(response, null, 4));
}, "jsonp");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<pre id="response"></pre>

Here's the fiddle; https://jsfiddle.net/ipdata/6wtf0q4g/922/

I also wrote this detailed analysis of 8 of the best IP Geolocation APIs.

I wanted to localize client side pricing for few countries without using any external api, so I used local Date object to fetch the country using new Date()).toString().split('(')[1].split(" ")[0]

    document.write((new Date()).toString().split('(')[1].split(" ")[0])

Basically this small code snippet extracts the first word from Date object. To check for various time zone, you can change the time of your local machine.

In my case, our service only included three countries, so I was able to get the location using the following code.

const countries = ["India", "Australia", "Singapore"]
const countryTimeZoneCodes = {
  "IND": 0,
  "IST": 0,
  "AUS": 1,
  "AES": 1,
  "ACS": 1,
  "AWS": 1,
  "SGT": 2,
  "SIN": 2,
  "SST": 2
} // Probable three characters from timezone part of Date object
let index = 0
try {
  const codeToCheck = (new Date()).toString().split('(')[1].split(" ")[0].toUpperCase().substring(0, 3)
  index = countryTimeZoneCodes[codeToCheck]

} catch (e) {

  document.write(e)
  index = 0
}

document.write(countries[index])

This was just to improve user experience. It's not a full proof solution to detect location. As a fallback for not detecting correctly, I added a dropdown in the menubar for selecting the country.

You can simply import in your app.component.ts or whichever component you want to use

import { HttpClient } from '@angular/common/http';

Then make a simple GET request to http://ip-api.com/json

  getIPAddress() {
    this.http.get("http://ip-api.com/json").subscribe((res: any) => {
      console.log('res ', res);
    })
  }

You will get the following response by using it:

{
    "status": "success",
    "country": "country fullname here",
    "countryCode": "country shortname here",
    "region": "region shortname here",
    "regionName": "region fullname here",
    "city": "city fullname here",
    "zip": "zipcode will be in string",
    "lat": "latitude here will be in integer",
    "lon": "logitude here will be in integer",
    "timezone": "timezone here",
    "isp": "internet service provider name here",
    "org": "internet service provider organization name here",
    "as": "internet service provider name with some code here",
    "query": "ip address here"
}

If you don't want to use an api and only the country is enough for you, you can use topojson and worldatlas.

import { feature } from "https://cdn.skypack.dev/topojson@3.0.2";
import { geoContains, geoCentroid, geoDistance } from "https://cdn.skypack.dev/d3@7.0.0";

async function success(position) {
    const topology = await fetch("https://cdn.jsdelivr.net/npm/world-atlas@2/countries-50m.json").then(response => response.json());
    const geojson = feature(topology, topology.objects.countries);
    
    const {
        longitude,
        latitude,
    } = position.coords;
    
    const location = geojson.features
        .filter(d => geoContains(d, [longitude, latitude]))
        .shift();
    
    if (location) {
        document.querySelector('#location').innerHTML = `You are in <u>${location.properties.name}</u>`;
    }
    
    if (!location) {
        const closestCountry = geojson.features
            // You could improve the distance calculation so that you get a more accurate result
            .map(d => ({ ...d, distance: geoDistance(geoCentroid(d), [longitude, latitude]) }))
            .sort((a, b) => a.distance - b.distance)
            .splice(0, 5);
        
        if (closestCountry.length > 0) {
            const possibleLocations = closestCountry.map(d => d.properties.name);
            const suggestLoctions = `${possibleLocations.slice(0, -1).join(', ')} or ${possibleLocations.slice(-1)}`;
            
            document.querySelector('#location').innerHTML = `It's not clear where you are!<section>Looks like you are in ${suggestLoctions}</section>`;
        }
        
        if (closestCountry.length === 0) {
            error();
        }        
    }
}

function error() {
    document.querySelector('#location').innerHTML = 'Sorry, I could not locate you';
};

navigator.geolocation.getCurrentPosition(success, error);

This code takes longitude and latitude and checks if this point is included in one of the geojson's feature (a spatially bounded entity). I created also a working example.

You can get a lot of info about IP via https://apiip.net/ API. They provide an XML response too if needed and it supports IPv6.

Just send the simple GET call:

https://apiip.net/api/check?ip=67.250.186.196&accessKey={your_api_key}

Response:

{
  "ip": "67.250.186.196",
  "continentCode": "NA",
  "continentName": "North America",
  "countryCode": "US",
  "countryName": "United States",
  "countryNameNative": "United States",
  "city": "New York",
  "postalCode": "10001",
  "latitude": 40.8271,
  "longitude": -73.9359,
  "capital": "Washington D.C.",
  "phoneCode": "1",
  "countryFlagEmoj": "",
  "countryFlagEmojUnicode": "U+1F1FA U+1F1F8",
  "isEu": false,
  "languages": {
    "en": {
      "code": "en",
      "name": "English",
      "native": "English"
    }
  },
  "currency": {
    "code": "USD",
    "name": "US Dollar",
    "symbol": "$",
    "number": "840",
    "rates": {
      "EURUSD": 1.11
     }
  },
  "timeZone": {
    "id": "America/New_York",
    "currentTime": "10/26/2021, 2:54:10 PM",
    "code": "EDT",
    "timeZoneName": "EDT",
    "utcOffset": -14400
  },
  "connection": {
    "asn": 12271,
    "isp": "Charter Communications Inc"
  },
  "security": {
    "isPublicProxy": false,
    "isResidentialProxy": false,
    "isTorExitNode": false,
    "network": "67.250.176.0/20"
  }
}

Here's what i have used before: Please modify depending on requirements!

  async function ipInfo(cb, ip) {
    let request;
    if (window.XMLHttpRequest) { // Mozilla, Safari, Chrome...
      request = new XMLHttpRequest();
    } else if (window.ActiveXObject) { // IE
      try {
        request = new ActiveXObject('Msxml2.XMLHTTP');
      } catch (e) {
        try {
          request = new ActiveXObject('Microsoft.XMLHTTP');
        } catch (e) {}
      }
    }
    request.open("GET", "//ipinfo.io/" + ((ip) ? ip : ""), false);
    request.setRequestHeader("Accept", "application/json");
    request.onreadystatechange = function() {
      if (request.readyState == 4 && request.status == 200) {
        cb(request.responseText);
      }
    };
    request.send(null);
  }

  let countryCode;
  try {
    ipInfo(function(resp) {
      countryCode = (resp && JSON.parse(resp).country) ? JSON.parse(resp).country : "US";
    });
  } catch (e) {
    countryCode = "US";
  }
Related