Google Maps API Integration in React: Achieving Stable User Location and Device Orientation

75 views Asked by At

I'm working on a Google Maps API integration in React to display a user's dynamic location, orientation, and tilt based on their mobile or tablet device. However, I'm encountering an issue: even when the device is still, there are minor fluctuations in position (latitude/longitude) or tilt/orientation values.

I've managed to achieve the basic functionality, but the behavior differs from what I've observed in the official Google Maps app. I'd like to fine-tune my implementation to match the smooth and stable behavior of Google Maps. Could someone guide me on what might be causing these fluctuations or offer tips on how to improve my code?

Thanks in advance!

Code:-

import React, { useEffect, useRef, useState } from 'react';

let watchPositionId = null;
let isLocationPermission = false;
let map = null;
let userMarker = null; 
let prevRotationVal = 0;


const GoogleMap = () => {
  const [longLat, setLongLat] = useState({ longitude: 0, latitude: 0 });
  const [orientation, setOrientation] = useState(0);
  const [autoAdjust, setAutoAdjust] = useState(true); // New state for auto adjustment
  const mapRef = useRef(null);

  useEffect(() => {
    const initMap = () => {
      if(!map){
        map = new window.google.maps.Map(mapRef.current, {
          center: {
            lat: longLat?.latitude,
            lng: longLat?.longitude,
          },
          zoom: 30,
          heading: 320,
          tilt: 47.5,
          mapId: "90f87356969d889c",
          mapTypeId: "satellite"
        });
      }else{

        // Remove previous user marker if it exists
        if (userMarker) {
          userMarker.setMap(null);
        }


        map.setCenter({
          lat: longLat.latitude,
          lng: longLat.longitude,
        });
      }

      const adjustMap = function (mode, amount) {
        switch (mode) {
          case "tilt":
            map.setTilt(map.getTilt() + amount);
            break;
          case "rotate":
            map.setHeading(map.getHeading() + amount);
            break;
          default:
            break;
        }
      };

      userMarker = new window.google.maps.Marker({
        position: {
          lat: longLat.latitude,
          lng: longLat.longitude,
        },
        map: map,
        title: 'My Location',
      });
    

      const handleOrientation = (event) => {
        const alpha = event.alpha;
        const beta = event.beta;
        const gamma = event.gamma;

        if (autoAdjust) {
          const direction = calculateHeading(alpha, beta, gamma);
          setOrientation(direction);

          console.info("calling now", {gamma, direction});
          // Automatically adjust the map based on the new orientation
          adjustMap("tilt", beta * 0.05); // Adjust the factor as needed
         // const rotationVal = gamma * 0.02; // Adjust the factor as needed
          const rotationVal = parseInt(direction * 0.1); // Adjust the factor as needed
     
          if(prevRotationVal !== rotationVal ){
              const fineTunedRotationValue = rotationVal * 20;
              prevRotationVal = fineTunedRotationValue;
              adjustMap("rotate", fineTunedRotationValue);
          }
        }
      };

      window.addEventListener('deviceorientation', handleOrientation, true);

    };

    if (window.google && mapRef.current) {
      if (isLocationPermission) {
        initMap();
      }
    } else {
      console.error("Google Maps API not loaded or mapRef not available");
    }

    return () => window.removeEventListener('deviceorientation', () => {});
  }, [isLocationPermission, autoAdjust, longLat]);

  useEffect(() => {
    if (window) {
      userDeviceOrientatationHandler(setOrientation);
      userLocationHandler(setLongLat);
    }

    return () => navigator?.geolocation?.clearWatch(watchPositionId);
  }, []);

  return (
    <>
      <div ref={mapRef} style={{ width: '100%', height: '100vh' }} />
    </>
  );
};

export default GoogleMap;


function calculateHeading(alpha, beta, gamma) {
  // Convert alpha (compass heading) to radians
  const compassHeadingRadians = alpha * (Math.PI / 180);

  // Convert beta and gamma to radians
  const betaRadians = beta * (Math.PI / 180);
  const gammaRadians = gamma * (Math.PI / 180);

  // Combine values to derive a more accurate heading
  
  const heading = compassHeadingRadians + betaRadians + gammaRadians;

  return heading * gamma;
}

function handleOrientation(event, setOrientation) {
  /**
   * gamma is the data of angle in degrees the device is tilted left-to-right, where right is positive.
   * alpha is the data of the direction the device is facing according to the compass.
   * beta is the data of the angle in degrees the device is tilted front-to-back, where front is positive.
  **/

  const alpha = event.alpha; // Z-Axis
  const beta  = event.beta;  // Y-Axis
  const gamma = event.gamma; // X-Axis 

  if(alpha && beta){
    const direction = calculateHeading(alpha, beta, gamma);
 
    setOrientation(direction);
  }
}

function userDeviceOrientatationHandler(setOrientation){
  if (window.DeviceOrientationEvent) {
    window.addEventListener('deviceorientation', (event) => handleOrientation(event, setOrientation), true);
  }else{
    alert("Device Orientation is not supported on your device or browser. Sorry.");
  }
}

function userCurrentPositionSuccessHandler(event, setLongLat){
  isLocationPermission = true;
  setLongLat({ longitude: event?.coords?.longitude, latitude: event?.coords?.latitude });
}

function userCurrentPositionErrorHandler(error){
  isLocationPermission = false;
  switch (error.code) {
    case error.PERMISSION_DENIED:
        alert("The request denied for geolocation.");
        break;
    case error.POSITION_UNAVAILABLE:
        alert("Location information is unavailable.");
        break;
    case error.TIMEOUT:
        alert("The request to get location timed out.");
        break;
    case error.UNKNOWN_ERROR:
        alert("An unknown error occurred.");
        break;
    default:
      alert("Something went wrong.");
      break;
  }
}

function userLocationHandler(setLongLat){
  watchPositionId = navigator?.geolocation?.watchPosition((event) => userCurrentPositionSuccessHandler(event, setLongLat), userCurrentPositionErrorHandler)
}
0

There are 0 answers