VUE

useGeoLocation Composable

Reactive geolocation tracking with watch position and error handling

Vue3ComposablesGeolocationMapsReactive

Code

import { ref, onUnmounted, type Ref } from 'vue';

interface GeoLocation {
  latitude: Ref<number | null>;
  longitude: Ref<number | null>;
  accuracy: Ref<number | null>;
  altitude: Ref<number | null>;
  altitudeAccuracy: Ref<number | null>;
  heading: Ref<number | null>;
  speed: Ref<number | null>;
  timestamp: Ref<number | null>;
  isLoading: Ref<boolean>;
  error: Ref<GeolocationPositionError | null>;
  isWatching: Ref<boolean>;
}

export function useGeoLocation(
  options: PositionOptions = { enableHighAccuracy: false, timeout: 10000, maximumAge: 30000 }
): GeoLocation {
  const latitude = ref<number | null>(null);
  const longitude = ref<number | null>(null);
  const accuracy = ref<number | null>(null);
  const altitude = ref<number | null>(null);
  const altitudeAccuracy = ref<number | null>(null);
  const heading = ref<number | null>(null);
  const speed = ref<number | null>(null);
  const timestamp = ref<number | null>(null);
  const isLoading = ref(false);
  const error = ref<GeolocationPositionError | null>(null);
  const isWatching = ref(false);
  let watchId: number | null = null;

  // Update position data
  const updatePosition = (pos: GeolocationPosition) => {
    const coords = pos.coords;
    latitude.value = coords.latitude;
    longitude.value = coords.longitude;
    accuracy.value = coords.accuracy;
    altitude.value = coords.altitude;
    altitudeAccuracy.value = coords.altitudeAccuracy;
    heading.value = coords.heading;
    speed.value = coords.speed;
    timestamp.value = pos.timestamp;
    isLoading.value = false;
    error.value = null;
  };

  // Handle position error
  const handleError = (err: GeolocationPositionError) => {
    error.value = err;
    isLoading.value = false;
    isWatching.value = false;
  };

  // Get current position once
  const getCurrentPosition = __TOKEN_36__ () => {
    __TOKEN_37__ (!navigator.geolocation) {
      error.value = new Error('Geolocation is not supported by your browser') as GeolocationPositionError;
      return;
    }

    isLoading.value = true;
    try {
      const pos = await new Promise<GeolocationPosition>((resolve, reject) => {
        navigator.geolocation.getCurrentPosition(resolve, reject, options);
      });
      updatePosition(pos);
    } __TOKEN_44__ (err) {
      handleError(err as GeolocationPositionError);
    }
  };

  // Watch position changes
  const watchPosition = () => {
    __TOKEN_46__ (!navigator.geolocation || isWatching.value) return;

    isWatching.value = true;
    isLoading.value = true;
    watchId = navigator.geolocation.watchPosition(
      updatePosition,
      handleError,
      options
    );
  };

  // Clear watch
  const clearWatch = () => {
    __TOKEN_49__ (watchId !== null && navigator.geolocation) {
      navigator.geolocation.clearWatch(watchId);
      watchId = null;
    }
    isWatching.value = false;
  };

  // Toggle watch position
  const toggleWatch = () => {
    __TOKEN_51__ (isWatching.value) {
      clearWatch();
    } else {
      watchPosition();
    }
  };

  // Cleanup
  onUnmounted(() => {
    clearWatch();
  });

  return {
    latitude,
    longitude,
    accuracy,
    altitude,
    altitudeAccuracy,
    heading,
    speed,
    timestamp,
    isLoading,
    error,
    isWatching,
    getCurrentPosition,
    watchPosition,
    clearWatch,
    toggleWatch
  };
}

// Usage example
// const { latitude, longitude, getCurrentPosition } = useGeoLocation();
// getCurrentPosition().then(() => {
//   console.log('Location:', latitude.value, longitude.value);
// });