Skillversitas

Geolocation API

The Geolocation API allows a website to get the user's geographic location (latitude, longitude) with the user's permission. It is very useful for features such as campus attendance, finding the nearest location, or tracking.

Code Example

Basics — getCurrentPosition

// Request the user's location
navigator.geolocation.getCurrentPosition(
  function (position) {
    console.log('Latitude:', position.coords.latitude);
    console.log('Longitude:', position.coords.longitude);
    console.log('Akurasi:', position.coords.accuracy, 'meter');
    console.log('Timestamp:', new Date(position.timestamp));
  },
  function (error) {
    console.error('Error:', error.message);
    switch (error.code) {
      case error.PERMISSION_DENIED:
        console.error('Excused location ditolak');
        break;
      case error.POSITION_UNAVAILABLE:
        console.error('Location unavailable');
        break;
      case error.TIMEOUT:
        console.error('Request timeout');
        break;
    }
  },
  {
    enableHighAccuracy: true,
    timeout: 10000,
    maximumAge: 300000
  }
);

watchPosition — Monitor Location Changes

// Monitor the location continuously
const watchId = navigator.geolocation.watchPosition(
  function (position) {
    console.log('New position:');
    console.log('Lat:', position.coords.latitude);
    console.log('Lng:', position.coords.longitude);
    updateMap(position.coords.latitude, position.coords.longitude);
  },
  function (error) {
    console.error('Error tracking:', error);
  },
  {
    enableHighAccuracy: true,
    timeout: 5000,
    maximumAge: 10000
  }
);

// Stop the watch when it is no longer needed
// For example when the user leaves the tracking page
navigator.geolocation.clearWatch(watchId);

Code Explanation

PositionOptions

{
  enableHighAccuracy: true,  // GPS (more accurate but slower)
  timeout: 10000,             // maximum wait time (ms)
  maximumAge: 300000          // use a cached location for up to 5 minutes
}

Position Object

position.coords.latitude    // latitude (-90 to 90)
position.coords.longitude   // longitude (-180 to 180)
position.coords.accuracy    // accuracy in meters
position.coords.altitude    // altitude in meters (if available)
position.coords.speed       // speed in m/s (if available)
position.coords.heading      // direction in degrees (if available)
position.timestamp          // time the location was obtained (ms)

Error Handling

function getLocation() {
  return new Promise((resolve, reject) => {
    if (!navigator.geolocation) {
      reject(new Error('Geolocation is not supported by this browser'));
      return;
    }

    navigator.geolocation.getCurrentPosition(
      position => resolve(position.coords),
      error => {
        switch (error.code) {
          case error.PERMISSION_DENIED:
            reject(new Error('Location permission denied. Enable it in your browser settings.'));
            break;
          case error.POSITION_UNAVAILABLE:
            reject(new Error('Location unavailable. Make sure GPS is enabled.'));
            break;
          case error.TIMEOUT:
            reject(new Error('Request location timeout. Try again.'));
            break;
          default:
            reject(new Error('An error occurred while getting location.'));
        }
      },
      { enableHighAccuracy: true, timeout: 10000, maximumAge: 300000 }
    );
  });
}

Real-World Examples

Campus Attendance with Geolocation

class CampusAttendance {
  constructor(campusLat, campusLng, radiusMeter = 100) {
    this.campusLat = campusLat;
    this.campusLng = campusLng;
    this.radius = radiusMeter;
  }

  // Calculate the distance between two points (Haversine formula)
  calculateDistance(lat1, lng1, lat2, lng2) {
    const R = 6371e3; // Earth's radius in meters
    const phi1 = (lat1 * Math.PI) / 180;
    const phi2 = (lat2 * Math.PI) / 180;
    const deltaPhi = ((lat2 - lat1) * Math.PI) / 180;
    const deltaLambda = ((lng2 - lng1) * Math.PI) / 180;

    const a =
      Math.sin(deltaPhi / 2) * Math.sin(deltaPhi / 2) +
      Math.cos(phi1) * Math.cos(phi2) *
      Math.sin(deltaLambda / 2) * Math.sin(deltaLambda / 2);
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

    return R * c; // distance in meters
  }

  // Check whether the user is inside the campus area
  async checkLocation() {
    const coords = await getLocation();
    const distance = this.calculateDistance(
      coords.latitude, coords.longitude,
      this.campusLat, this.campusLng
    );

    return {
      inArea: distance <= this.radius,
      distance: Math.round(distance),
      latitude: coords.latitude,
      longitude: coords.longitude
    };
  }

  // Check in
  async attendance() {
    try {
      const location = await this.checkLocation();

      if (!location.inArea) {
        throw new Error(
          `You are ${location.distance}m from the university. ` +
          `Must be within a ${this.radius}m radius.`
        );
      }

      // Send the attendance data to the server
      const response = await fetch('/api/attendance', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          latitude: location.latitude,
          longitude: location.longitude,
          timestamp: new Date().toISOString(),
          radius: this.radius
        })
      });

      if (!response.ok) throw new Error('Failed to save attendance');

      return { success: true, message: 'Attendance successful!' };
    } catch (error) {
      return { success: false, message: error.message };
    }
  }
}

// Usage
// Campus coordinates: New York (-6.8912, 107.6108), radius 100m
const attendance = new CampusAttendance(-6.8912, 107.6108, 100);

document.querySelector('#btn-attendance').addEventListener('click', async function () {
  this.disabled = true;
  this.textContent = 'Memprocess...';

  const result = await attendance.attendance();

  if (result.success) {
    showNotification(result.message, 'success');
  } else {
    showNotification(result.message, 'error');
  }

  this.disabled = false;
  this.textContent = 'Check In Now';
});

Real-Time Location Tracking

class LocationTracker {
  constructor() {
    this.watchId = null;
    this.isTracking = false;
    this.locations = [];
  }

  start(callback) {
    if (this.isTracking) return;

    this.watchId = navigator.geolocation.watchPosition(
      position => {
        const data = {
          latitude: position.coords.latitude,
          longitude: position.coords.longitude,
          accuracy: position.coords.accuracy,
          timestamp: new Date(position.timestamp)
        };

        this.locations.push(data);
        callback(data);
      },
      error => {
        console.error('Tracking error:', error);
        this.stop();
      },
      { enableHighAccuracy: true, maximumAge: 5000 }
    );

    this.isTracking = true;
  }

  stop() {
    if (this.watchId !== null) {
      navigator.geolocation.clearWatch(this.watchId);
      this.watchId = null;
    }
    this.isTracking = false;
    return this.locations;
  }

  getHistory() {
    return [...this.locations];
  }

  clearHistory() {
    this.locations = [];
  }
}

// Usage
const tracker = new LocationTracker();

document.querySelector('#btn-start-tracking').addEventListener('click', function () {
  tracker.start(data => {
    updateMap(data.latitude, data.longitude);
    addLogEntry(data);
  });
  this.textContent = 'Tracking active...';
});

document.querySelector('#btn-stop-tracking').addEventListener('click', function () {
  const locations = tracker.stop();
  console.log('Locations recorded:', locations.length, 'points');
  saveRoute(locations);
});

Finding the Nearest Location from Coordinates

const campusLocation = [
  { name: 'Building A (Rektorat)', lat: -6.8912, lng: 107.6108 },
  { name: 'Building B (Teknik)', lat: -6.8915, lng: 107.6112 },
  { name: 'Library', lat: -6.8910, lng: 107.6105 },
  { name: 'Kantin', lat: -6.8918, lng: 107.6115 }
];

async function findNearestLocation() {
  try {
    const coords = await getLocation();

    const withDistance = campusLocation.map(loc => ({
      ...loc,
      distance: calculateDistance(coords.latitude, coords.longitude, loc.lat, loc.lng)
    }));

    withDistance.sort((a, b) => a.distance - b.distance);

    console.log('Nearest location:', withDistance[0].name);
    console.log('Distance:', Math.round(withDistance[0].distance), 'meter');

    return withDistance[0];
  } catch (error) {
    console.error('Failed to get location:', error);
  }
}

Tips and Notes

// 1. Always check whether geolocation is supported
if ('geolocation' in navigator) {
  // Browser supports it
} else {
  console.error('Browser does not support geolocation');
}

// 2. HTTPS is required for geolocation in production
// HTTP only works on localhost for development

// 3. Request permission explicitly
// The browser will show the permission dialog automatically
// Don't call getCurrentPosition too often

// 4. handleHighAccuracy: true uses GPS (more accurate, but)
// it consumes more battery and is slower

// 5. Clean up watchPosition when the component is destroyed
window.addEventListener('beforeunload', function () {
  if (tracker && tracker.isTracking) {
    tracker.stop();
  }
});

Conclusion

The Geolocation API allows a website to get the user's location with permission. Use getCurrentPosition to get the location once, and watchPosition for real-time tracking. Always handle errors (permission denied, timeout), validate distance for features like campus attendance, and clean up watchPosition when it is no longer needed. Make sure to use HTTPS in production.