react-native-background-geolocation icon indicating copy to clipboard operation
react-native-background-geolocation copied to clipboard

[Help Wanted]: Inaccurate paths from coords.

Open Saad-Bashar opened this issue 6 months ago • 3 comments

Required Reading

  • [x] Confirmed

Plugin Version

"react-native-background-fetch": "^4.2.8", "react-native-background-geolocation": "^4.18.6",

Mobile operating-system(s)

  • [x] iOS
  • [x] Android

Device Manufacturer(s) and Model(s)

iPhone 14, Samsung S Series

Device operating-systems(s)

iOS 17, Android 14/15

React Native / Expo version

RN - 0.74.5, Expo - 50

What do you require assistance about?

First of all, thanks for your hard work to maintain this amazing plugin. We recently have purchased the license to use this module in our app.

Our Use Case We want to track user's path when they are in a shift and calculate mileage from the path. We are relying on the coords from this module and drawing polylines to show the path on the map.

Issue I have done several test drives to check the paths. Most of the cases it works fine but we also frequently see weird paths. For example, check out the following image.

Image

As in the image sometimes the poly lines just cuts straight through map (doesn't follow the street)

Some other examples of wrong paths from the coordinates,

Image

Question

  1. Why is this happening? Is it due to GPS / Device issues or something on our end?
  2. Do I need to change some configs? We are mostly concerned to track users' path when they are driving to calculate mileage.

[Optional] Plugin Code and/or Config

// Relevant code
// MAIN setup and tracking 
  useEffect(() => {
    let locationSubscription: any;
    BackgroundGeolocation.ready({
      desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_HIGH,
      distanceFilter: 20,
      stopOnTerminate: true,
      locationAuthorizationRequest: 'Always',
      locationAuthorizationAlert: {
        titleWhenNotEnabled: 'Location Services Required',
        titleWhenOff: 'Location Services Required',
        instructions:
          "To calculate your trip mileage automatically, we need access to your location — even when the app is in the background.\n\nWe only use your location while you are on a trip. You'll always have full control, and your data stays private",
        cancelButton: 'Cancel',
        settingsButton: 'Settings',
      },
      backgroundPermissionRationale: {
        title: 'Location Services Required',
        message:
          "To calculate your trip mileage automatically, we need access to your location — even when the app is in the background.\n\nWe only use your location while you are on a trip. You'll always have full control, and your data stays private",
        positiveAction: 'Change to Allow all the time',
      },
    }).then(() => {
      setIsBackgroundLocationEnabled(true);
      // Location listener
      locationSubscription = BackgroundGeolocation.onLocation(loc => {
        if (loc.coords.accuracy && loc.coords.accuracy > 50) {
          return;
        }

        if (loc.coords?.latitude && loc.coords?.longitude) {
          const coords = {
            latitude: loc.coords.latitude,
            longitude: loc.coords.longitude,
            timestamp: loc.timestamp,
            accuracy: loc.coords.accuracy,
          };

          setTrackedCoords(prev => {
            if (prev.length === 0 || prev[prev.length - 1].length === 0) {
              const newCoords = [...prev, [coords]];
              if (shiftId) {
                storage.set(
                  `trip_coords_${shiftId}`,
                  JSON.stringify(newCoords),
                );
              }
              return newCoords;
            }

            const newCoords = [...prev];
            const currentSegment = newCoords[newCoords.length - 1];
            newCoords[newCoords.length - 1] = [...currentSegment, coords];

            if (shiftId) {
              storage.set(`trip_coords_${shiftId}`, JSON.stringify(newCoords));
            }

            return newCoords;
          });
        }
      });
    });

    return () => {
      locationSubscription?.remove();
    };
  }, [shiftId, isTracking, isPaused]);

// Start, pause, resume, stop functions
  const handleStartTrip = useCallback(
    async (shouldClearCoords = true) => {
      if (!isBackgroundLocationEnabled) {
        Alert.alert(
          'Location Services Required',
          'Please enable location services in the settings to start tracking.',
        );
        return;
      }

      try {
        await BackgroundGeolocation.start();
        setIsTracking(true);
        setIsPaused(false);

        // Only clear coordinates if explicitly starting a new trip
        if (shouldClearCoords) {
          setTrackedCoords([]);
          if (shiftId) {
            storage.delete(`trip_coords_${shiftId}`);
          }
          setCalculatedDistance(0);
        }
      } catch (error) {
        console.error('Error starting trip:', error);
      }
    },
    [shiftId, isBackgroundLocationEnabled],
  );

  const handlePauseTrip = async () => {
    try {
      await BackgroundGeolocation.stop();
      setIsPaused(true);
    } catch (error) {
      console.error('Error pausing trip:', error);
    }
  };

  const handleResumeTrip = async () => {
    if (!isBackgroundLocationEnabled) {
      Alert.alert(
        'Location Services Required',
        'Please enable location services in the settings to resume tracking.',
      );
      return;
    }

    try {
      await BackgroundGeolocation.stop();

      // Add a new empty segment to the coordinates array
      setTrackedCoords(prev => {
        const newCoords = [...prev, []];
        return newCoords;
      });

      // Start fresh tracking
      await BackgroundGeolocation.start();
      setIsPaused(false);
    } catch (error) {
      console.error('Error resuming trip:', error);
    }
  };

  const handleStopTrip = async () => {
    try {
      await BackgroundGeolocation.stop();
      setIsTracking(false);
      setIsPaused(false);

      // Only update parent component when trip stops
      onDistanceUpdate(calculatedDistance);

      if (shiftId) {
        storage.set(
          `trip_tracking_state_${shiftId}`,
          JSON.stringify({
            isTracking: false,
            isPaused: false,
          }),
        );
      }
    } catch (error) {
      console.error('Error stopping trip:', error);
    }
  };

// MAP VIEW
 <MapView
        ref={mapRef}
        style={styles.map}
        initialRegion={region}
        showsUserLocation={isActiveTracking}
        showsMyLocationButton={true}
        followsUserLocation={isActiveTracking}>
        {coordinates.map((segment, segmentIndex) => {
          // Only render polyline if segment has at least 2 points
          if (segment.length < 2) {
            return null;
          }

          const startPoint = segment[0];
          const endPoint = segment[segment.length - 1];

          return (
            <React.Fragment key={`segment-${segmentIndex}`}>
              {/* Polyline for the route */}
              <Polyline
                coordinates={segment}
                strokeColor={colors.primary[500]}
                strokeWidth={4}
              />

              {/* Start marker */}
              <Marker
                flat={true}
                pinColor={colors.primary[500]}
                coordinate={startPoint}
              />

              {/* End marker */}
              <Marker
                flat={true}
                pinColor={colors.primary[500]}
                coordinate={endPoint}
              />
            </React.Fragment>
          );
        })}
      </MapView>

[Optional] Relevant log output


Saad-Bashar avatar May 21 '25 05:05 Saad-Bashar

There’s a wide discrepancy of performance between different Android models.

See https://dontkillmyapp.com

christocracy avatar May 21 '25 12:05 christocracy

Thank you for your response. Yes, I have seen the link from other issues as well in the repo.

Keeping this limitation in mind, can you please have a quick glance at the relevant code that I posted and if possible suggest us better ways to track? We believe we can play with some config props to make the tracking better. We are just concerned to track our users for the following,

  • They are driving.
  • The app is foreground & background.
  • No need to track if the app is terminated.

Thank you again! 🙏

Saad-Bashar avatar May 22 '25 00:05 Saad-Bashar

There is nothing unusual about your Config.

christocracy avatar May 22 '25 00:05 christocracy

This issue is stale because it has been open for 30 days with no activity.

github-actions[bot] avatar Jun 21 '25 02:06 github-actions[bot]

This issue was closed because it has been inactive for 14 days since being marked as stale.

github-actions[bot] avatar Jul 06 '25 02:07 github-actions[bot]