Skip to main content
โšก Calmops

Geolocation API in JavaScript

Geolocation API in JavaScript

Introduction

The Geolocation API allows web applications to access the user’s geographical location. This enables location-based services like maps, local search, and location tracking. Understanding how to use the Geolocation API responsibly is important for building modern web applications.

Geolocation Basics

Checking Support

// Check if Geolocation API is supported
if ('geolocation' in navigator) {
  console.log('Geolocation is supported');
} else {
  console.log('Geolocation is not supported');
}

// Practical example: Feature detection
function isGeolocationSupported() {
  return 'geolocation' in navigator;
}

if (isGeolocationSupported()) {
  // Use geolocation
} else {
  // Fallback or show message
  console.log('Please enable location services');
}

Getting User Location

getCurrentPosition()

// Get current position
navigator.geolocation.getCurrentPosition(
  (position) => {
    const { latitude, longitude, accuracy } = position.coords;
    console.log(`Latitude: ${latitude}`);
    console.log(`Longitude: ${longitude}`);
    console.log(`Accuracy: ${accuracy}m`);
  },
  (error) => {
    console.error('Error getting location:', error.message);
  }
);

// With options
navigator.geolocation.getCurrentPosition(
  (position) => {
    console.log('Location:', position.coords);
  },
  (error) => {
    console.error('Error:', error);
  },
  {
    enableHighAccuracy: true,
    timeout: 5000,
    maximumAge: 0
  }
);

Position Object

// Position object structure
navigator.geolocation.getCurrentPosition((position) => {
  const coords = position.coords;
  
  console.log(coords.latitude); // Latitude in degrees
  console.log(coords.longitude); // Longitude in degrees
  console.log(coords.accuracy); // Accuracy in meters
  console.log(coords.altitude); // Altitude in meters (may be null)
  console.log(coords.altitudeAccuracy); // Altitude accuracy (may be null)
  console.log(coords.heading); // Direction of travel (0-360 degrees)
  console.log(coords.speed); // Speed in m/s (may be null)
  
  console.log(position.timestamp); // Timestamp when position was acquired
});

Watching Position

watchPosition()

// Watch position changes
const watchId = navigator.geolocation.watchPosition(
  (position) => {
    const { latitude, longitude } = position.coords;
    console.log(`Current position: ${latitude}, ${longitude}`);
  },
  (error) => {
    console.error('Error:', error.message);
  },
  {
    enableHighAccuracy: true,
    timeout: 5000,
    maximumAge: 0
  }
);

// Stop watching
navigator.geolocation.clearWatch(watchId);

// Practical example: Track movement
let previousPosition = null;

const watchId = navigator.geolocation.watchPosition((position) => {
  const { latitude, longitude } = position.coords;
  
  if (previousPosition) {
    const distance = calculateDistance(
      previousPosition.latitude,
      previousPosition.longitude,
      latitude,
      longitude
    );
    console.log(`Moved ${distance}m`);
  }
  
  previousPosition = { latitude, longitude };
});

Error Handling

Error Types

// Handle different error types
navigator.geolocation.getCurrentPosition(
  (position) => {
    console.log('Location:', position.coords);
  },
  (error) => {
    switch (error.code) {
      case error.PERMISSION_DENIED:
        console.error('User denied geolocation permission');
        break;
      case error.POSITION_UNAVAILABLE:
        console.error('Position information is unavailable');
        break;
      case error.TIMEOUT:
        console.error('The request to get user location timed out');
        break;
      default:
        console.error('An unknown error occurred');
    }
  }
);

Comprehensive Error Handling

// Robust error handling
function getLocation() {
  return new Promise((resolve, reject) => {
    if (!('geolocation' in navigator)) {
      reject(new Error('Geolocation not supported'));
      return;
    }
    
    navigator.geolocation.getCurrentPosition(
      (position) => {
        resolve(position.coords);
      },
      (error) => {
        let message = 'Unknown error';
        
        if (error.code === error.PERMISSION_DENIED) {
          message = 'Location permission denied';
        } else if (error.code === error.POSITION_UNAVAILABLE) {
          message = 'Location unavailable';
        } else if (error.code === error.TIMEOUT) {
          message = 'Location request timed out';
        }
        
        reject(new Error(message));
      },
      {
        enableHighAccuracy: true,
        timeout: 10000,
        maximumAge: 300000 // 5 minutes
      }
    );
  });
}

// Usage
getLocation()
  .then(coords => console.log('Location:', coords))
  .catch(error => console.error(error.message));

Practical Examples

Example 1: Display Location on Map

// Display user location on map
function displayLocationOnMap() {
  navigator.geolocation.getCurrentPosition(
    (position) => {
      const { latitude, longitude } = position.coords;
      
      // Create map (using Leaflet or similar)
      const map = L.map('map').setView([latitude, longitude], 13);
      
      L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
      
      // Add marker for user location
      L.marker([latitude, longitude])
        .addTo(map)
        .bindPopup('Your location')
        .openPopup();
    },
    (error) => {
      console.error('Error getting location:', error);
    }
  );
}

Example 2: Distance Calculator

// Calculate distance between two points
function calculateDistance(lat1, lon1, lat2, lon2) {
  const R = 6371; // Earth's radius in km
  const dLat = (lat2 - lat1) * Math.PI / 180;
  const dLon = (lon2 - lon1) * Math.PI / 180;
  
  const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
            Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
            Math.sin(dLon / 2) * Math.sin(dLon / 2);
  
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  return R * c; // Distance in km
}

// Find nearby locations
function findNearbyLocations(locations, maxDistance = 5) {
  return new Promise((resolve, reject) => {
    navigator.geolocation.getCurrentPosition(
      (position) => {
        const { latitude, longitude } = position.coords;
        
        const nearby = locations.filter(location => {
          const distance = calculateDistance(
            latitude,
            longitude,
            location.latitude,
            location.longitude
          );
          return distance <= maxDistance;
        });
        
        resolve(nearby);
      },
      reject
    );
  });
}

// Usage
const locations = [
  { name: 'Coffee Shop', latitude: 40.7128, longitude: -74.0060 },
  { name: 'Restaurant', latitude: 40.7580, longitude: -73.9855 }
];

findNearbyLocations(locations, 10)
  .then(nearby => console.log('Nearby:', nearby))
  .catch(error => console.error(error));

Example 3: Location Tracking

// Track user movement
class LocationTracker {
  constructor(options = {}) {
    this.watchId = null;
    this.positions = [];
    this.options = {
      enableHighAccuracy: true,
      timeout: 5000,
      maximumAge: 0,
      ...options
    };
  }
  
  start() {
    if (!('geolocation' in navigator)) {
      console.error('Geolocation not supported');
      return;
    }
    
    this.watchId = navigator.geolocation.watchPosition(
      (position) => {
        this.positions.push({
          coords: position.coords,
          timestamp: position.timestamp
        });
        
        this.onPositionUpdate(position);
      },
      (error) => {
        this.onError(error);
      },
      this.options
    );
  }
  
  stop() {
    if (this.watchId !== null) {
      navigator.geolocation.clearWatch(this.watchId);
      this.watchId = null;
    }
  }
  
  getPositions() {
    return this.positions;
  }
  
  getTotalDistance() {
    let total = 0;
    
    for (let i = 1; i < this.positions.length; i++) {
      const prev = this.positions[i - 1].coords;
      const curr = this.positions[i].coords;
      
      total += calculateDistance(
        prev.latitude,
        prev.longitude,
        curr.latitude,
        curr.longitude
      );
    }
    
    return total;
  }
  
  onPositionUpdate(position) {
    // Override in subclass
  }
  
  onError(error) {
    console.error('Tracking error:', error);
  }
}

// Usage
const tracker = new LocationTracker();
tracker.start();

// Stop after 5 minutes
setTimeout(() => {
  tracker.stop();
  console.log('Total distance:', tracker.getTotalDistance(), 'km');
}, 5 * 60 * 1000);

Example 4: Geofencing

// Simple geofencing
class Geofence {
  constructor(latitude, longitude, radiusKm) {
    this.latitude = latitude;
    this.longitude = longitude;
    this.radiusKm = radiusKm;
    this.isInside = false;
  }
  
  check(userLat, userLon) {
    const distance = calculateDistance(
      this.latitude,
      this.longitude,
      userLat,
      userLon
    );
    
    const wasInside = this.isInside;
    this.isInside = distance <= this.radiusKm;
    
    if (!wasInside && this.isInside) {
      this.onEnter();
    } else if (wasInside && !this.isInside) {
      this.onExit();
    }
  }
  
  onEnter() {
    console.log('Entered geofence');
  }
  
  onExit() {
    console.log('Exited geofence');
  }
}

// Usage
const geofence = new Geofence(40.7128, -74.0060, 1); // 1km radius

navigator.geolocation.watchPosition((position) => {
  const { latitude, longitude } = position.coords;
  geofence.check(latitude, longitude);
});

Privacy and Permissions

Requesting Permission

// Permission is requested automatically on first use
// User sees a permission prompt

// Check permission status (if available)
if ('permissions' in navigator) {
  navigator.permissions.query({ name: 'geolocation' })
    .then(permission => {
      console.log('Permission status:', permission.state);
      // 'granted', 'denied', or 'prompt'
    });
}

// Handle permission changes
if ('permissions' in navigator) {
  navigator.permissions.query({ name: 'geolocation' })
    .then(permission => {
      permission.addEventListener('change', () => {
        console.log('Permission changed:', permission.state);
      });
    });
}

Best Practices

// Best practices for geolocation
function useGeolocationResponsibly() {
  // 1. Check support
  if (!('geolocation' in navigator)) {
    console.log('Geolocation not available');
    return;
  }
  
  // 2. Explain why you need location
  console.log('We need your location to find nearby stores');
  
  // 3. Use appropriate options
  const options = {
    enableHighAccuracy: false, // Use false unless necessary
    timeout: 10000,
    maximumAge: 300000 // Cache for 5 minutes
  };
  
  // 4. Handle errors gracefully
  navigator.geolocation.getCurrentPosition(
    (position) => {
      // Use location
    },
    (error) => {
      // Provide fallback
      console.log('Using default location');
    },
    options
  );
  
  // 5. Allow users to opt-out
  // Provide a way to disable location tracking
}

Common Mistakes to Avoid

Mistake 1: Not Checking Support

// โŒ Wrong - Assumes geolocation exists
navigator.geolocation.getCurrentPosition(...);

// โœ… Correct - Check first
if ('geolocation' in navigator) {
  navigator.geolocation.getCurrentPosition(...);
}

Mistake 2: Ignoring Errors

// โŒ Wrong - No error handling
navigator.geolocation.getCurrentPosition((position) => {
  console.log(position.coords);
});

// โœ… Correct - Handle errors
navigator.geolocation.getCurrentPosition(
  (position) => {
    console.log(position.coords);
  },
  (error) => {
    console.error('Error:', error.message);
  }
);

Mistake 3: Using High Accuracy Unnecessarily

// โŒ Wrong - High accuracy drains battery
const options = { enableHighAccuracy: true };

// โœ… Correct - Use only when needed
const options = { enableHighAccuracy: false };

Mistake 4: Not Cleaning Up Watches

// โŒ Wrong - Watch continues indefinitely
navigator.geolocation.watchPosition(...);

// โœ… Correct - Clean up when done
const watchId = navigator.geolocation.watchPosition(...);
// Later...
navigator.geolocation.clearWatch(watchId);

Summary

Geolocation API enables location-based features:

  • Check support before using
  • Use getCurrentPosition() for one-time location
  • Use watchPosition() for continuous tracking
  • Handle errors appropriately
  • Respect user privacy
  • Use appropriate accuracy settings
  • Clean up watches when done
  • Provide fallbacks for unsupported browsers
  • Explain why you need location
  • Allow users to opt-out

Next Steps

Continue your learning journey:

Comments