Skip to content

Geolocation Accuracy Enhancement Plan

Executive Summary

Problem Statement

The current geolocation system has a critical flaw in how it determines if tracker devices are within storage or delivery locations. The system uses exact point coordinates from location reports/history records, ignoring the confidence and horizontal_accuracy data that indicates the actual precision of GPS readings.

Current Issues:

  • Point-in-polygon detection using exact coordinates
  • Ignores horizontal_accuracy field (GPS precision in meters)
  • Ignores confidence field (signal quality indicator)
  • Frontend displays only point markers, suggesting exact positioning
  • Users cannot assess location uncertainty

Solution Overview

Implement circle-to-circle intersection detection where:

  • Tracker position becomes a circle with radius = horizontal_accuracy
  • Geofence detection checks if accuracy circle intersects with location geofence
  • Frontend displays both point markers and accuracy circles with purple styling
  • Users can visually assess location precision
  • Confidence values (1, 2, 3 from FindMy) may be used for future enhancements or ignored initially

Current System Analysis

Database Schema

Location Reports Table:

-- location_reports table already contains:
horizontal_accuracy FLOAT NULL  -- GPS accuracy in meters
confidence INTEGER              -- Signal confidence level
location GEOGRAPHY(POINT)       -- Exact coordinates

Location History Table:

-- location_history table already contains:
horizontal_accuracy FLOAT NULL  -- GPS accuracy in meters
confidence INTEGER              -- Signal confidence level
location GEOGRAPHY(POINT)       -- Exact coordinates

Geofence Tables:

-- delivery_locations & storage_locations contain:
geofence_size_meters INTEGER NOT NULL DEFAULT 100  -- Geofence radius
location GEOGRAPHY(POINT)                          -- Center point

Current Geofencing Logic

File: app/crud/location.py

# Current implementation - PROBLEMATIC
def get_nearby(self, db: Session, *, latitude: float, longitude: float, distance_meters: int = 1000):
    point = func.ST_SetSRID(func.ST_MakePoint(longitude, latitude), 4326)
    return db.query(DeliveryLocation).filter(
        func.ST_DWithin(
            DeliveryLocation.location,
            point,
            DeliveryLocation.geofence_size_meters,
        )
    ).all()

Current Frontend Implementation

Files:

  • tracker-frontend/src/features/dashboard/components/TrackerMap.tsx
  • tracker-admin/src/components/common/LocationMap.tsx

Current Colors:

  • Blue: IN_TRANSIT trackers
  • Green: DELIVERED trackers
  • Orange: IN_STORAGE trackers
  • Grey: CREATED trackers
  • Green circles: Delivery location geofences
  • Orange circles: Storage location geofences

Technical Implementation Plan

Phase 1: Backend Geospatial Logic Enhancement

1.1 Create Geospatial Service

File: app/services/geospatial_service.py

from typing import Optional, Tuple
from sqlalchemy import func
from sqlalchemy.orm import Session

class GeospatialService:
    @staticmethod
    def get_effective_accuracy(horizontal_accuracy: Optional[float], confidence: Optional[int]) -> float:
        """
        Calculate effective accuracy radius considering confidence levels.

        Args:
            horizontal_accuracy: GPS reported accuracy in meters
            confidence: FindMy confidence level (1, 2, or 3) - significance TBD

        Returns:
            Effective accuracy radius in meters
        """
        # Default accuracy if none provided
        base_accuracy = horizontal_accuracy or 10.0

        # Note: Confidence values are 1, 2, or 3 from FindMy
        # For now, we'll ignore confidence or apply minimal adjustment
        # TODO: Investigate significance of FindMy confidence values (1=worst, 3=best?)
        if confidence is not None:
            if confidence == 1:
                # Lowest confidence - slight increase in uncertainty
                base_accuracy *= 1.2
            elif confidence == 2:
                # Medium confidence - minimal adjustment
                base_accuracy *= 1.1
            # confidence == 3: Highest confidence - use base accuracy

        # Cap maximum accuracy at reasonable limit
        return min(base_accuracy, 500.0)

    @staticmethod
    def check_tracker_in_geofence(
        tracker_lat: float,
        tracker_lng: float,
        horizontal_accuracy: Optional[float],
        confidence: Optional[int],
        geofence_lat: float,
        geofence_lng: float,
        geofence_radius: float
    ) -> bool:
        """
        Check if tracker accuracy circle intersects with geofence circle.

        Returns True if circles intersect (tracker could be in geofence).
        """
        effective_accuracy = GeospatialService.get_effective_accuracy(
            horizontal_accuracy, confidence
        )

        # Use PostGIS to calculate if circles intersect
        # Distance between centers <= sum of radii means intersection
        return True  # Implementation will use PostGIS ST_DWithin

    @staticmethod
    def get_nearby_locations_with_accuracy(
        db: Session,
        location_model,  # DeliveryLocation or StorageLocation
        tracker_lat: float,
        tracker_lng: float,
        horizontal_accuracy: Optional[float],
        confidence: Optional[int]
    ):
        """
        Get locations where tracker accuracy circle intersects geofence.
        """
        effective_accuracy = GeospatialService.get_effective_accuracy(
            horizontal_accuracy, confidence
        )

        tracker_point = func.ST_SetSRID(
            func.ST_MakePoint(tracker_lng, tracker_lat), 4326
        )

        return db.query(location_model).filter(
            func.ST_DWithin(
                location_model.location,
                tracker_point,
                location_model.geofence_size_meters + effective_accuracy
            )
        ).all()

1.2 Update CRUD Location Methods

File: app/crud/location.py

# Add import
from app.services.geospatial_service import GeospatialService

class CRUDDeliveryLocation(CRUDBase[DeliveryLocation, DeliveryLocationCreate, DeliveryLocationUpdate]):
    def get_nearby_with_accuracy(
        self,
        db: Session,
        *,
        latitude: float,
        longitude: float,
        horizontal_accuracy: Optional[float] = None,
        confidence: Optional[int] = None
    ) -> List[DeliveryLocation]:
        """Get delivery locations considering tracker accuracy."""
        return GeospatialService.get_nearby_locations_with_accuracy(
            db, DeliveryLocation, latitude, longitude, horizontal_accuracy, confidence
        )

    # Keep existing get_nearby for backward compatibility
    def get_nearby(self, db: Session, *, latitude: float, longitude: float, distance_meters: int = 1000):
        """Legacy method - use get_nearby_with_accuracy for new code."""
        return self.get_nearby_with_accuracy(db, latitude=latitude, longitude=longitude)

# Similar updates for CRUDStorageLocation

1.3 Update API Endpoints

Files: app/api/routes/delivery_locations.py, app/api/routes/storage_locations.py

Add new endpoints that accept accuracy parameters:

@router.get("/nearby-with-accuracy/")
def get_nearby_delivery_locations_with_accuracy(
    latitude: float,
    longitude: float,
    horizontal_accuracy: Optional[float] = None,
    confidence: Optional[int] = None,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """Get delivery locations considering GPS accuracy."""
    locations = delivery_location.get_nearby_with_accuracy(
        db,
        latitude=latitude,
        longitude=longitude,
        horizontal_accuracy=horizontal_accuracy,
        confidence=confidence
    )
    return [
        DeliveryLocationResponse(
            id=loc.id,
            name=loc.name,
            coordinates=Coordinates(
                latitude=loc.get_coordinates(db)["latitude"],
                longitude=loc.get_coordinates(db)["longitude"]
            ),
            geofence_size_meters=loc.geofence_size_meters,
            production_run_id=loc.production_run_id
        )
        for loc in locations
    ]

Phase 2: Frontend Accuracy Visualization

2.1 Enhanced Tracker Markers

Color Scheme for Accuracy Circles:

  • Purple (#8B5CF6) - Distinct from existing colors, user preference
  • Semi-transparent (opacity: 0.15)
  • Dashed border for differentiation from geofence circles

2.2 Update TrackerMap Component

File: tracker-frontend/src/features/dashboard/components/TrackerMap.tsx

// Add Circle import
import { Circle } from 'react-leaflet';

// Add accuracy circle rendering function
const renderAccuracyCircles = (trackers: any[]) => {
  return trackers
    .filter(tracker => tracker.horizontal_accuracy && tracker.lat && tracker.lng)
    .map(tracker => (
      <Circle
        key={`accuracy-${tracker.id}`}
        center={[tracker.lat, tracker.lng]}
        radius={tracker.horizontal_accuracy}
        pathOptions={{
          color: '#8B5CF6',
          fillColor: '#8B5CF6',
          fillOpacity: 0.15,
          weight: 2,
          opacity: 0.6,
          dashArray: '5, 5'
        }}
      />
    ));
};

// Update MarkerClusterGroup component to include accuracy circles
function MarkerClusterGroup({ trackers, isDarkMode }) {
  // ... existing marker logic ...

  return (
    <>
      {/* Existing marker cluster logic */}
      {/* Add accuracy circles */}
      {renderAccuracyCircles(trackers)}
    </>
  );
}

2.3 Update Legend

Add accuracy circle explanation to map legend:

{/* Add to existing legend */}
<div className="flex items-center">
  <div
    className="w-3 h-3 border-2 border-purple-500 rounded-full mr-2"
    style={{
      backgroundColor: "rgba(139, 92, 246, 0.15)",
      borderStyle: "dashed"
    }}
  ></div>
  <span className="text-xs text-gray-700 dark:text-gray-300">
    GPS Accuracy
  </span>
</div>

2.4 Update Admin Panel Maps

File: tracker-admin/src/components/common/LocationMap.tsx

Similar updates to include accuracy circles for nearby trackers.

2.5 Update Tracker History Modal

File: tracker-frontend/src/features/dashboard/components/TrackerHistoryModal.tsx

Add accuracy circles to historical location display.

Phase 3: API Response Updates

3.1 Include Accuracy Data in Responses

Files: app/schemas/tracker.py, app/api/routes/trackers/location_history.py

# Update LocationHistoryResponse schema
class LocationHistoryResponse(BaseModel):
    id: int
    timestamp: datetime
    coordinates: Coordinates
    nearest_city: Optional[str] = None
    horizontal_accuracy: Optional[float] = None  # Add this
    confidence: Optional[int] = None             # Add this

    class Config:
        from_attributes = True

3.2 Update Tracker Detail Endpoints

Ensure all location-related endpoints return accuracy data:

# In tracker detail responses
{
  "latest_location": {
    "coordinates": {"latitude": 51.5074, "longitude": -0.1278},
    "horizontal_accuracy": 15.5,
    "confidence": 3,  # FindMy confidence level (1, 2, or 3)
    "timestamp": "2025-01-07T12:00:00Z"
  }
}

Phase 4: Database Optimizations

4.1 Handle Null Accuracy Values

Migration Script: alembic/versions/xxx_set_default_accuracy.py

def upgrade():
    # Set reasonable defaults for null horizontal_accuracy
    op.execute("""
        UPDATE location_reports
        SET horizontal_accuracy = 10.0
        WHERE horizontal_accuracy IS NULL
    """)

    op.execute("""
        UPDATE location_history
        SET horizontal_accuracy = 10.0
        WHERE horizontal_accuracy IS NULL
    """)

def downgrade():
    # Revert changes if needed
    pass

4.2 Add Computed Indexes (Optional)

-- Index for geospatial queries with accuracy
CREATE INDEX idx_location_reports_accuracy_geom
ON location_reports USING GIST (location, horizontal_accuracy);

CREATE INDEX idx_location_history_accuracy_geom
ON location_history USING GIST (location, horizontal_accuracy);

Phase 5: Testing Strategy

5.1 Unit Tests

File: tests/services/test_geospatial_service.py

import pytest
from app.services.geospatial_service import GeospatialService

class TestGeospatialService:
    def test_get_effective_accuracy_default(self):
        """Test default accuracy when none provided."""
        accuracy = GeospatialService.get_effective_accuracy(None, None)
        assert accuracy == 10.0

    def test_get_effective_accuracy_low_confidence(self):
        """Test accuracy adjustment for low confidence (FindMy confidence = 1)."""
        accuracy = GeospatialService.get_effective_accuracy(10.0, 1)
        assert accuracy == 12.0  # 1.2x multiplier for lowest confidence

    def test_get_effective_accuracy_medium_confidence(self):
        """Test accuracy adjustment for medium confidence (FindMy confidence = 2)."""
        accuracy = GeospatialService.get_effective_accuracy(10.0, 2)
        assert accuracy == 11.0  # 1.1x multiplier for medium confidence

    def test_get_effective_accuracy_high_confidence(self):
        """Test accuracy with high confidence (FindMy confidence = 3)."""
        accuracy = GeospatialService.get_effective_accuracy(10.0, 3)
        assert accuracy == 10.0  # No adjustment for highest confidence

    def test_accuracy_cap(self):
        """Test maximum accuracy cap."""
        accuracy = GeospatialService.get_effective_accuracy(1000.0, 1)
        assert accuracy == 500.0  # Capped at maximum even with low confidence

5.2 Integration Tests

File: tests/api/routes/test_delivery_locations_accuracy.py

def test_get_nearby_with_accuracy(client, db_session, sample_delivery_location):
    """Test accuracy-aware geofence detection."""
    # Test case where point is outside geofence but accuracy circle intersects
    response = client.get(
        "/api/v1/delivery-locations/nearby-with-accuracy/",
        params={
            "latitude": 51.5074,  # Just outside geofence
            "longitude": -0.1278,
            "horizontal_accuracy": 50.0,  # Large enough to intersect
            "confidence": 2  # FindMy confidence level (1, 2, or 3)
        }
    )
    assert response.status_code == 200
    assert len(response.json()) > 0  # Should find location due to accuracy

5.3 Frontend Tests

File: tracker-frontend/src/features/dashboard/components/__tests__/TrackerMap.test.tsx

describe('TrackerMap Accuracy Circles', () => {
  it('renders accuracy circles for trackers with horizontal_accuracy', () => {
    const trackersWithAccuracy = [
      {
        id: 1,
        lat: 51.5074,
        lng: -0.1278,
        horizontal_accuracy: 25.5,
        status: 'IN_TRANSIT'
      }
    ];

    render(<TrackerMap trackers={trackersWithAccuracy} />);

    // Check for accuracy circle
    expect(screen.getByTestId('accuracy-circle-1')).toBeInTheDocument();
  });
});

Deployment Considerations

Migration Strategy

  1. Phase 1: Deploy backend changes with feature flag
  2. Phase 2: Deploy frontend changes (backward compatible)
  3. Phase 3: Enable accuracy-aware geofencing gradually
  4. Phase 4: Monitor performance and adjust

Rollback Plan

  • Keep existing get_nearby() methods for backward compatibility
  • Feature flag to switch between old/new geofencing logic
  • Database changes are additive (no data loss)

Performance Monitoring

  • Monitor geospatial query performance
  • Track accuracy circle rendering performance
  • Monitor API response times

Configuration

Environment Variables:

# Feature flags
ENABLE_ACCURACY_GEOFENCING=true
DEFAULT_GPS_ACCURACY=10.0
MAX_GPS_ACCURACY=500.0

# Performance tuning
GEOFENCE_QUERY_TIMEOUT=5000
ACCURACY_CIRCLE_MAX_ZOOM=15

Success Metrics

Technical Metrics

  • Geofencing accuracy improvement (measured against known test cases)
  • API response time impact (should be < 20% increase)
  • Frontend rendering performance (accuracy circles)

User Experience Metrics

  • Reduced false positive/negative geofence detections
  • User understanding of location uncertainty
  • Support ticket reduction related to "incorrect" location detection

Future Enhancements

Phase 6: Advanced Features (Future)

  1. Confidence-based styling: Different circle styles based on confidence levels
  2. Historical accuracy trends: Show accuracy improvement over time
  3. Accuracy-based alerts: Warn when accuracy is too low for reliable geofencing
  4. Machine learning: Predict accuracy based on environmental factors

Phase 7: Analytics (Future)

  1. Accuracy statistics: Dashboard showing GPS accuracy trends
  2. Geofence effectiveness: Metrics on geofence detection reliability
  3. Location quality scoring: Overall location data quality metrics

Implementation Timeline

Week 1-2: Phase 1 (Backend geospatial service) Week 3: Phase 2 (Frontend accuracy circles) Week 4: Phase 3 (API updates and testing) Week 5: Phase 4 (Database optimizations) Week 6: Phase 5 (Comprehensive testing and deployment)

Conclusion

This enhancement addresses a fundamental flaw in the current geolocation system by properly accounting for GPS accuracy limitations. The solution provides:

  1. More accurate geofencing through circle-to-circle intersection
  2. Better user understanding through accuracy visualization
  3. Backward compatibility with existing systems
  4. Scalable architecture for future enhancements

The implementation is designed to be deployed incrementally with minimal risk and maximum benefit to location-based functionality.