155

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?

TylerH
  • 20,799
  • 66
  • 75
  • 101
Gal
  • 23,122
  • 32
  • 97
  • 118
  • 2
    `$.get("https://api.ipdata.co", function (response) { $("#response").html(JSON.stringify(response, null, 4)); }, "jsonp");` – Jonathan Nov 06 '17 at 11:13
  • 1
    See the fiddle https://jsfiddle.net/6wtf0q4g/ – Jonathan Nov 06 '17 at 11:14
  • A quick disclaimer, I built the above service ipdata.co, it's also extremely scalable with 10 global endpoints each able to handle >800M calls daily! – Jonathan Feb 18 '18 at 11:41
  • @SamuelLiew I don't believe the duplicate in this case is valid. This question specifically asks about geolocation, whereas the duplicate asks about locale, a similar but distinct question (as locale does not include lat & long, city, etc.). – Heretic Monkey Nov 24 '20 at 14:30
  • doesn't seem to work anymore @jonathan – Jason Boerner Aug 16 '23 at 03:11

12 Answers12

230

You can use my service, http://ipinfo.io, for this. It will give you the client IP, hostname, geolocation information (city, region, country, area code, zip code etc) and network owner. Here's a simple example that logs the city and country:

$.get("https://ipinfo.io", function(response) {
    console.log(response.city, response.country);
}, "jsonp");

Here's a more detailed JSFiddle example that also prints out the full response information, so you can see all of the available details: http://jsfiddle.net/zK5FN/2/

The location will generally be less accurate than the native geolocation details, but it doesn't require any user permission.

Ben Dowling
  • 17,187
  • 8
  • 87
  • 103
83

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.

Diego Fortes
  • 8,830
  • 3
  • 32
  • 42
  • 4
    That's a really interesting way to do it. I feel like there must be something wrong with it, but I can't think of a reason why. My first thought was time zones that span countries, but you seem to have that covered by including the linked time zones. This would, in theory, still get the right country even if the user is using VPN. The only downside is having to update your list of time zones when they change. – Gabriel Luci Feb 19 '22 at 00:46
  • Thanks! Yeah, I'm unsure how reliable this is in a production environment, but it seems to be good enough for most cases. I confess I haven't tested this solution in larger projects, therefore I would recommend caution when doing so. I reckon that it would be possible to implement some logic to handle timezone nuances as well. – Diego Fortes Feb 19 '22 at 08:57
  • 2
    I tried it in the UK and worked! – Sorush Mar 17 '22 at 17:59
  • 2
    It seems like you can test it by changing the timezone on your computer. This works on my Macbook anyway. – JB Wilson Apr 26 '22 at 19:35
  • 1
    This is truly nice and everything, but what about Server-Side Rendring? I won't work there. Like, let's say I have a website with different languages, and I want to send back an html with the right language, based on the user's location. I don't want to have to do 2 trips for that. – Tal Kohavy Jul 03 '22 at 11:24
  • @TalKohavy well, you'll need the client's information either way to get their location, so it wouldn't be difficult to develop some solution for Node.js using this. – Diego Fortes Jul 03 '22 at 13:16
  • 1
    @DiegoFortes Not neccesarily my friend. Consider the case I mentioned once again, by typing in the webite's url and hitting Enter, the client provides nothing but an ip + cookie. The cookie is sent from the second time onwards (considering i'm using cookies in my website), while the ip is provided from the first time onwards. What you are suggesting is CSR, while I want SSR. – Tal Kohavy Jul 04 '22 at 17:18
  • I was checking this question for just research and I find this the most impressive answer! – Ilyas karim Aug 05 '22 at 08:36
  • 1
    I tested this code on Windows + Chrome/Edge/Firefox. It works everywhere! – naXa stands with Ukraine Aug 12 '22 at 15:28
  • 1
    really nice solution, but no sure about delivering of extra 2000 lines of js code to the browser, maybe the better option is to use some extrenal service – Nikita Aug 31 '22 at 09:45
  • 1
    @Nikita naturally it's just one of the many options out there, it is not the axiom of getting users' locations. Developers may improve or disregard it as they seem fit. It's crucial to bear in mind that even with the 2000+ lines of code it's still faster than an API call, 100% reliable and it doesn't cost anything. Furthermore the 2000+ lines is simply a JSON object, that is very unlikely to have any meaningful impact on the website's performance. – Diego Fortes Aug 31 '22 at 16:40
  • A good alternative to IP geolocation services. Would not hurt to have some fallback, if you decide to take this code to production. – College Code Oct 01 '22 at 04:44
  • Who maintains the database? Is there a central authority that can be relied on to update the database? – Billy Mitchell Nov 16 '22 at 11:32
  • @BillyMitchell more information about the `Intl` object can be [found here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl). – Diego Fortes Nov 16 '22 at 16:06
  • 1
    Wow... Worked Perfect...This answer deserves lots of upvotes..... :) – Smack Alpha Dec 28 '22 at 14:43
  • 1
    I'm not in Singapore. – boctulus Apr 27 '23 at 05:09
  • 4
    @boctulus as aforementioned, this is not a flawless solution. It grabs your device's timezone and translates to what country is most likely that you're in. Also if you setup your device's timezone to `UTC`, it will return `null`. If you require precision, you ought to use a third party IP service – Diego Fortes Apr 27 '23 at 08:48
  • @DiegoFortes I get it. Well US and Mexico and Canada overlaps a lot in TimeZones. I think this solution is useless my friend. – boctulus Apr 29 '23 at 10:01
  • 1
    @BillyMitchell Found databases containing the mappings here: https://www.iana.org/time-zones download "Data only", then look into `zone.tab` and `iso3166.tab`. – Nearoo Aug 03 '23 at 12:18
  • 1
    Same solution but using moment-timezone ! Without using geoloc or IP lookup APIs (are free for dev use only) Using `Intl.DateTimeFormat().resolvedOptions().timeZone` and file extracted from `moment-timezone` Link to demo (https://stackblitz.com/edit/js-azokeh?file=index.js), you can download a file. – abdelgrib Aug 18 '23 at 07:41
79

You don't need to locate the user if you only need their country. You can look their IP address up in any IP-to-location service (like maxmind, ipregistry or ip2location). This will be accurate most of the time.

Here is a client-side example with Ipregistry (disclaimer, I am working for):

fetch('https://api.ipregistry.co/?key=tryout')
    .then(function (response) {
        return response.json();
    })
    .then(function (payload) {
        console.log(payload.location.country.name + ', ' + payload.location.city);
    });

If you really need to get their location, you can get their lat/lng with that method, then query Google's or Yahoo's reverse geocoding service.

Laurent
  • 14,122
  • 13
  • 57
  • 89
Galen
  • 29,976
  • 9
  • 71
  • 89
  • @juanpastas you don't. you just ping a service, and service will know. or you write service yourself, but that's out of pure javascript scope. – tishma Nov 07 '14 at 22:56
  • 1
    but to ping a service, that would be in server side? do you know a way to do that from client? – sites Nov 08 '14 at 00:14
  • 3
    What if I access the internet via a proxy in a different country? – Liam Oct 11 '18 at 16:01
  • 3
    `ipregistry.co` is more generous than other alternatives. it has a free tier of 100k API calls, registration is simple and doesn't require entering credit card. – Maksim Shamihulau Jan 17 '21 at 18:30
  • 1
    Nice recommendation, I tried ipregistry and the info is pretty straightforward. – Mihail Minkov Feb 19 '21 at 02:13
  • Excellent answer – Grant Dec 01 '21 at 15:50
  • Be careful with such calls. The service may read information from your request, which means you should better check if this is compatible with your data protection rules. – Pateta Jan 25 '22 at 02:01
38

You can use your IP address to get your 'country', 'city', 'isp' etc...
Just use one of the web-services that provide you with a simple api like http://ip-api.com which provide you a JSON service at http://ip-api.com/json. Simple send a Ajax (or Xhr) request and then parse the JSON to get whatever data you need.

var requestUrl = "http://ip-api.com/json";

$.ajax({
  url: requestUrl,
  type: 'GET',
  success: function(json)
  {
    console.log("My country is: " + json.country);
  },
  error: function(err)
  {
    console.log("Request failed, error= " + err);
  }
});
Gil Epshtain
  • 8,670
  • 7
  • 63
  • 89
  • 11
    Since `ip-api.com` do not support https request, most browser will reject the request if called from within an https content/page. Plus, as per their website, > You are free to use ip-api.com for non-commercial use. We do not allow commercial use without prior approval. – pdeschen Oct 28 '17 at 21:19
  • 1
    `ip-api` is just one example of different services that provide IP-Location service. The question doesn't ask about `https` protocols. If you do use `https` web site I'm sure you could implement the same logic, just with a different IP-Location service. – Gil Epshtain Oct 29 '17 at 13:10
15

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.

Jonathan
  • 10,792
  • 5
  • 65
  • 85
  • 2
    That is false. The service is free up to 45,000 requests a month. Smallest plan is $10 and the error mentioned is returned to prevent abuse. Note that it is also accompanied by an email to warn you you've exhausted all your requests. – Jonathan Mar 29 '20 at 11:08
  • My comment is not false – tfa Apr 01 '20 at 18:16
  • 1
    Your comment is inherently false and misleading. – Jonathan May 01 '20 at 15:55
13

A very easy to use service is provided by ws.geonames.org. Here's an example URL:

http://ws.geonames.org/countryCode?lat=43.7534932&lng=28.5743187&type=JSON

And here's some (jQuery) code which I've added to your code:

if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(function(position) {
        $.getJSON('http://ws.geonames.org/countryCode', {
            lat: position.coords.latitude,
            lng: position.coords.longitude,
            type: 'JSON'
        }, function(result) {
            alert('Country: ' + result.countryName + '\n' + 'Code: ' + result.countryCode);
        });
    });
}​

Try it on jsfiddle.net ...

hippietrail
  • 15,848
  • 18
  • 99
  • 158
8

A free and easy to use service is provided at Webtechriser (click here to read the article) (called wipmania). This one is a JSONP service and requires plain javascript coding with HTML. It can also be used in JQuery. I modified the code a bit to change the output format and this is what I've used and found to be working: (it's the code of my HTML page)

<html>
    <body>
        <p id="loc"></p>


        <script type="text/javascript">
            var a = document.getElementById("loc");

               function jsonpCallback(data) { 
             a.innerHTML = "Latitude: " + data.latitude + 
                              "<br/>Longitude: " + data.longitude + 
                              "<br/>Country: " + data.address.country; 
              }
        </script>
        <script src="http://api.wipmania.com/jsonp?callback=jsonpCallback"
                     type="text/javascript"></script>


    </body>
</html>

PLEASE NOTE: This service gets the location of the visitor without prompting the visitor to choose whether to share their location, unlike the HTML 5 geolocation API (the code that you've written). Therefore, privacy is compromised. So, you should make judicial use of this service.

  • I see `0` for **Latitude** and **Longitude** and `Unknown` for **Country** over here on stack overflow but it worked when I used it in a HTML webpage. –  Jun 19 '16 at 06:39
  • I've just used this on a very old site of mine and I'm please to say it works: http://e-artlab.com/about-you/ - although not on all platforms or browsers (on my partner's Win/Edge and Win/Chrome access is blocked, although my Mac/Chrome is fine)? – Dave Everitt Aug 19 '17 at 11:21
  • Insecure connection as using HTTP. – Ramis Jan 05 '21 at 02:23
6

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.

ak100
  • 245
  • 2
  • 3
4

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"
}
iftikharyk
  • 870
  • 5
  • 10
3

For developers looking for a full-featured geolocation utility, you can have a look at geolocator.js (I'm the author).

Example below will first try HTML5 Geolocation API to obtain the exact coordinates. If fails or rejected, it will fallback to Geo-IP look-up. Once it gets the coordinates, it will reverse-geocode the coordinates into an address.

var options = {
    enableHighAccuracy: true,
    timeout: 6000,
    maximumAge: 0,
    desiredAccuracy: 30,
    fallbackToIP: true, // if HTML5 geolocation fails or rejected
    addressLookup: true, // get detailed address information
    timezone: true, 
    map: "my-map" // this will even create a map for you
};

geolocator.locate(options, function (err, location) {
    console.log(err || location);
});

It supports geo-location (via HTML5 or IP lookups), geocoding, address look-ups (reverse geocoding), distance & durations, timezone information and more...

Onur Yıldırım
  • 32,327
  • 12
  • 84
  • 98
  • works like a charm! However, you need to enable multiple APIs on google to make it work. Had to enable **Google Maps Time Zone API** and **Google Maps Geocoding API**. Adding it to documentation could save user some more time :). Thanks! – khawarizmi Feb 16 '17 at 10:53
  • Geolocator has a detailed documentation here: http://onury.github.io/geolocator/?api=geolocator Thanks. – Onur Yıldırım Feb 16 '17 at 12:31
2

You can use ip-api.io to get visitor's location. It supports IPv6.

As a bonus it allows to check whether ip address is a tor node, public proxy or spammer.

JavaScript Code:

function getIPDetails() {
    var ipAddress = document.getElementById("txtIP").value;

    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function () {
        if (this.readyState == 4 && this.status == 200) {
            console.log(JSON.parse(xhttp.responseText));
        }
    };
    xhttp.open("GET", "http://ip-api.io/json/" + ipAddress, true);
    xhttp.send();
}

<input type="text" id="txtIP" placeholder="Enter the ip address" />
<button onclick="getIPDetails()">Get IP Details</button>

jQuery Code:

$(document).ready(function () {
        $('#btnGetIpDetail').click(function () {
            if ($('#txtIP').val() == '') {
                alert('IP address is reqired');
                return false;
            }
            $.getJSON("http://ip-api.io/json/" + $('#txtIP').val(),
                 function (result) {
                     alert('Country Name: ' + result.country_name)
                     console.log(result);
                 });
        });
    });

<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<div>
    <input type="text" id="txtIP" />
    <button id="btnGetIpDetail">Get Location of IP</button>
</div>
Vindhyachal Kumar
  • 1,713
  • 2
  • 23
  • 27
1

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/[email protected]";
import { geoContains, geoCentroid, geoDistance } from "https://cdn.skypack.dev/[email protected]";

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.

Niekes
  • 400
  • 1
  • 4
  • 11