Geolocation API
What is the Geolocation API?
The Geolocation API allows you to access the user’s geographic location (with their permission). Perfect for maps, location-based services, and local recommendations.
if ('geolocation' in navigator) {
navigator.geolocation.getCurrentPosition((position) => {
console.log('Latitude:', position.coords.latitude);
console.log('Longitude:', position.coords.longitude);
});
}
Important: Requires HTTPS (except localhost) and user permission.
Checking for Support
function supportsGeolocation() {
return 'geolocation' in navigator;
}
if (supportsGeolocation()) {
console.log('Geolocation is supported');
} else {
console.log('Geolocation is not supported');
// Show fallback UI
}
Getting Current Position
Basic Usage
function getLocation() {
if ('geolocation' in navigator) {
navigator.geolocation.getCurrentPosition(
// Success callback
(position) => {
const lat = position.coords.latitude;
const lon = position.coords.longitude;
const accuracy = position.coords.accuracy;
console.log(`Location: ${lat}, ${lon}`);
console.log(`Accuracy: ${accuracy} meters`);
},
// Error callback
(error) => {
console.error('Error getting location:', error.message);
}
);
} else {
console.error('Geolocation not supported');
}
}
getLocation();
Position Object
navigator.geolocation.getCurrentPosition((position) => {
const coords = position.coords;
console.log('Latitude:', coords.latitude); // Decimal degrees
console.log('Longitude:', coords.longitude); // Decimal degrees
console.log('Accuracy:', coords.accuracy); // Meters
console.log('Altitude:', coords.altitude); // Meters (may be null)
console.log('Altitude Accuracy:', coords.altitudeAccuracy); // Meters (may be null)
console.log('Heading:', coords.heading); // Degrees (may be null)
console.log('Speed:', coords.speed); // Meters/second (may be null)
console.log('Timestamp:', position.timestamp); // When position was acquired
});
Handling Errors
function handleLocationError(error) {
switch (error.code) {
case error.PERMISSION_DENIED:
console.error('User denied geolocation permission');
showMessage('Please enable location access');
break;
case error.POSITION_UNAVAILABLE:
console.error('Location information unavailable');
showMessage('Unable to determine your location');
break;
case error.TIMEOUT:
console.error('Location request timed out');
showMessage('Location request took too long');
break;
default:
console.error('Unknown error:', error.message);
showMessage('An error occurred');
}
}
navigator.geolocation.getCurrentPosition(
successCallback,
handleLocationError
);
Position Options
Customize how location is obtained:
const options = {
enableHighAccuracy: true, // Request best possible accuracy
timeout: 10000, // Wait max 10 seconds
maximumAge: 0 // Don't use cached position
};
navigator.geolocation.getCurrentPosition(
(position) => {
console.log('Got position:', position.coords);
},
(error) => {
console.error('Error:', error);
},
options
);
Option Details
// High accuracy (uses GPS, slower, more battery)
const highAccuracy = {
enableHighAccuracy: true,
timeout: 15000,
maximumAge: 0
};
// Fast, less accurate (uses WiFi/IP, faster, less battery)
const lowAccuracy = {
enableHighAccuracy: false,
timeout: 5000,
maximumAge: 30000 // Accept position up to 30 seconds old
};
// Use appropriate option based on needs
navigator.geolocation.getCurrentPosition(success, error, highAccuracy);
Watching Position (Live Tracking)
Track user movement in real-time:
let watchId;
function startTracking() {
if ('geolocation' in navigator) {
watchId = navigator.geolocation.watchPosition(
(position) => {
const lat = position.coords.latitude;
const lon = position.coords.longitude;
const speed = position.coords.speed;
console.log(`Moving: ${lat}, ${lon}`);
if (speed) {
console.log(`Speed: ${speed} m/s`);
}
updateMapMarker(lat, lon);
},
(error) => {
console.error('Tracking error:', error.message);
},
{
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
}
);
console.log('Started tracking with ID:', watchId);
}
}
function stopTracking() {
if (watchId) {
navigator.geolocation.clearWatch(watchId);
console.log('Stopped tracking');
watchId = null;
}
}
// Start tracking
startTracking();
// Stop tracking when done
// stopTracking();
Practical Examples
1. Show User Location on Map
function showUserOnMap() {
navigator.geolocation.getCurrentPosition(
(position) => {
const lat = position.coords.latitude;
const lon = position.coords.longitude;
// Using Google Maps
const mapUrl = `https://www.google.com/maps?q=${lat},${lon}`;
window.open(mapUrl, '_blank');
// Or display on embedded map
initMap(lat, lon);
},
(error) => {
alert('Could not get your location: ' + error.message);
}
);
}
function initMap(lat, lon) {
// Example with Google Maps JavaScript API
const map = new google.maps.Map(document.getElementById('map'), {
center: { lat, lng: lon },
zoom: 15
});
new google.maps.Marker({
position: { lat, lng: lon },
map: map,
title: 'You are here'
});
}
2. Calculate Distance to Target
function degreesToRadians(degrees) {
return degrees * (Math.PI / 180);
}
function calculateDistance(lat1, lon1, lat2, lon2) {
const earthRadiusKm = 6371;
const dLat = degreesToRadians(lat2 - lat1);
const dLon = degreesToRadians(lon2 - lon1);
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(degreesToRadians(lat1)) * Math.cos(degreesToRadians(lat2)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return earthRadiusKm * c;
}
function getDistanceToStore() {
const storeLocation = { lat: 40.7128, lon: -74.0060 }; // New York
navigator.geolocation.getCurrentPosition((position) => {
const userLat = position.coords.latitude;
const userLon = position.coords.longitude;
const distance = calculateDistance(
userLat, userLon,
storeLocation.lat, storeLocation.lon
);
console.log(`Store is ${distance.toFixed(2)} km away`);
});
}
3. Find Nearest Locations
async function findNearestStores() {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(
(position) => {
const userLat = position.coords.latitude;
const userLon = position.coords.longitude;
const stores = [
{ name: 'Store A', lat: 40.7128, lon: -74.0060 },
{ name: 'Store B', lat: 40.7589, lon: -73.9851 },
{ name: 'Store C', lat: 40.7306, lon: -73.9352 }
];
// Calculate distance to each store
const storesWithDistance = stores.map(store => ({
...store,
distance: calculateDistance(userLat, userLon, store.lat, store.lon)
}));
// Sort by distance
storesWithDistance.sort((a, b) => a.distance - b.distance);
resolve(storesWithDistance);
},
reject
);
});
}
// Usage
const nearestStores = await findNearestStores();
console.log('Nearest store:', nearestStores[0].name);
4. Location-Based Recommendations
class LocationService {
constructor() {
this.currentPosition = null;
}
async getCurrentLocation() {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(
(position) => {
this.currentPosition = {
lat: position.coords.latitude,
lon: position.coords.longitude,
accuracy: position.coords.accuracy
};
resolve(this.currentPosition);
},
reject,
{
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 300000 // 5 minutes
}
);
});
}
async getNearbyPlaces(radius = 5) {
const location = await this.getCurrentLocation();
// Call external API (e.g., Google Places API)
const response = await fetch(
`https://api.example.com/places?lat=${location.lat}&lon=${location.lon}&radius=${radius}`
);
return await response.json();
}
isWithinRadius(targetLat, targetLon, radiusKm) {
if (!this.currentPosition) {
throw new Error('Current position not available');
}
const distance = calculateDistance(
this.currentPosition.lat,
this.currentPosition.lon,
targetLat,
targetLon
);
return distance <= radiusKm;
}
}
// Usage
const locationService = new LocationService();
const places = await locationService.getNearbyPlaces(10); // 10km radius
5. Geofencing (Track Entry/Exit)
class Geofence {
constructor(centerLat, centerLon, radiusKm) {
this.center = { lat: centerLat, lon: centerLon };
this.radius = radiusKm;
this.watchId = null;
this.inside = false;
}
start(onEnter, onExit) {
this.watchId = navigator.geolocation.watchPosition(
(position) => {
const lat = position.coords.latitude;
const lon = position.coords.longitude;
const distance = calculateDistance(
lat, lon,
this.center.lat, this.center.lon
);
const currentlyInside = distance <= this.radius;
// Detect crossing boundary
if (currentlyInside && !this.inside) {
onEnter();
this.inside = true;
} else if (!currentlyInside && this.inside) {
onExit();
this.inside = false;
}
},
(error) => {
console.error('Geofence error:', error);
},
{
enableHighAccuracy: true,
maximumAge: 0
}
);
}
stop() {
if (this.watchId) {
navigator.geolocation.clearWatch(this.watchId);
this.watchId = null;
}
}
}
// Usage
const storeFence = new Geofence(40.7128, -74.0060, 0.5); // 500m radius
storeFence.start(
() => console.log('Welcome to our store!'),
() => console.log('Thanks for visiting!')
);
Permission Handling
Request Permission
async function requestLocationPermission() {
try {
const result = await navigator.permissions.query({ name: 'geolocation' });
console.log('Permission state:', result.state);
// 'granted', 'denied', or 'prompt'
// Listen for permission changes
result.addEventListener('change', () => {
console.log('Permission changed to:', result.state);
});
return result.state;
} catch (error) {
console.error('Permission API not supported');
return 'unknown';
}
}
// Usage
const permission = await requestLocationPermission();
if (permission === 'granted') {
getLocation();
} else if (permission === 'denied') {
showPermissionInstructions();
}
User-Friendly Permission Flow
async function requestLocationAccess() {
// Check if already granted
const state = await requestLocationPermission();
if (state === 'granted') {
return getLocation();
}
if (state === 'denied') {
showMessage('Location access denied. Please enable in browser settings.');
return;
}
// Show why we need permission
showModal({
title: 'Enable Location',
message: 'We use your location to find nearby stores and show personalized recommendations.',
onAccept: () => {
getLocation();
}
});
}
Performance Considerations
// Cache position to avoid repeated API calls
class LocationCache {
constructor(maxAgeMs = 300000) { // 5 minutes default
this.maxAge = maxAgeMs;
this.cachedPosition = null;
this.cachedTime = null;
}
async getPosition(options = {}) {
const now = Date.now();
// Return cached if still valid
if (this.cachedPosition && this.cachedTime &&
(now - this.cachedTime) < this.maxAge) {
return this.cachedPosition;
}
// Get fresh position
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(
(position) => {
this.cachedPosition = position;
this.cachedTime = now;
resolve(position);
},
reject,
options
);
});
}
clearCache() {
this.cachedPosition = null;
this.cachedTime = null;
}
}
// Usage
const locationCache = new LocationCache(300000); // 5 min cache
const position1 = await locationCache.getPosition(); // API call
const position2 = await locationCache.getPosition(); // From cache
Best Practices
✅ DO:
// Always check for support
if ('geolocation' in navigator) {
// Use geolocation
}
// Provide error handling
navigator.geolocation.getCurrentPosition(success, handleError);
// Explain why you need location
showMessage('We need your location to show nearby stores');
// Use appropriate accuracy
const options = {
enableHighAccuracy: false // Use true only if really needed
};
// Clear watch when done
navigator.geolocation.clearWatch(watchId);
// Cache results when appropriate
const cachedLocation = new LocationCache();
❌ DON’T:
// Don't use without HTTPS (except localhost)
// http://example.com ❌
// Don't forget error handling
navigator.geolocation.getCurrentPosition(success); // ❌ No error handler
// Don't request location without explanation
getCurrentPosition(); // ❌ User doesn't know why
// Don't use high accuracy unnecessarily
enableHighAccuracy: true // ❌ Drains battery if not needed
// Don't forget to stop watching
watchPosition(); // ❌ Never stopped, drains battery
// Don't store location without consent
localStorage.setItem('location', coords); // ❌ Privacy issue
Summary
Get Current Position:
navigator.geolocation.getCurrentPosition(
(position) => {
console.log(position.coords.latitude, position.coords.longitude);
},
(error) => {
console.error(error.message);
},
{ enableHighAccuracy: true, timeout: 10000 }
);
Watch Position:
const watchId = navigator.geolocation.watchPosition(callback);
navigator.geolocation.clearWatch(watchId); // Stop watching
Key Properties:
position.coords.latitude- Decimal degreesposition.coords.longitude- Decimal degreesposition.coords.accuracy- Accuracy in metersposition.coords.speed- Speed in m/s (may be null)
Error Codes:
PERMISSION_DENIED(1) - User denied accessPOSITION_UNAVAILABLE(2) - Can’t determine locationTIMEOUT(3) - Request took too long
Requirements:
- HTTPS (or localhost)
- User permission
- Hardware with GPS/location capability
Next Article: Notifications API