Use Case: Geographical Location Tracking (LAT/LON)

Overview

This use case describes how the Plings system can capture and store geographical coordinates (latitude/longitude) for objects during creation, enabling location-based discovery and “near me” functionality for outdoor, mobile, or uncontained objects.

User Story

As a user, I want to store the geographical location where objects are created or located so that I can discover objects near my current location or search for items in specific geographical areas.

Business Value

  • Spatial Discovery: Find objects based on geographical proximity (“tools near me”)
  • Location Context: Understand where objects were created or last seen
  • Outdoor Object Management: Track items in large outdoor spaces (construction sites, parks, farms)
  • Mobile Asset Tracking: Monitor equipment that moves between geographical locations
  • Emergency Response: Quickly locate critical equipment during emergencies

Current System Integration

Existing Foundation

The Plings system already has some geographical components:

  • ScanEvent Location: ScanEvent nodes track lat/lon of tag scans
  • Spatial System: Comprehensive relative positioning (IN, ON, LEFT_OF, etc.)
  • Object Creation Flow: Established CreateObjectModal workflow
  • Mobile Architecture: Mobile-responsive object creation interface

Integration with Spatial Relationships

Complementary Systems: Geographical coordinates complement rather than replace spatial relationships:

  • Relative Positioning: Objects maintain IN, ON, NEXT_TO relationships for local spatial context
  • Absolute Positioning: Objects gain latitude, longitude for global geographical context
  • Hierarchy Integration: Containers can have geographical coordinates that child objects inherit
  • Multi-Scale Navigation: Users can navigate from global map view to detailed spatial relationships

Workflow Steps

Step 1: Location Permission Request

  • Trigger: User opens CreateObjectModal
  • Browser API: Request navigator.geolocation.getCurrentPosition()
  • User Experience:
    📍 Location Access
    Plings would like to store the location where this object is created.
    [Allow] [Deny] [Ask Each Time]
    
  • Privacy: Clear explanation of location data usage

Step 2: Location Detection and Display

Success Path:

navigator.geolocation.getCurrentPosition((position) => {
  const { latitude, longitude, accuracy } = position.coords;
  // Display: "📍 Current Location: 59.3293°N, 18.0686°E (±10m)"
});

User Interface:

📍 Location: Stockholm, Sweden
   📍 Coordinates: 59.3293°N, 18.0686°E
   🎯 Accuracy: ±10 meters
   [📍 Use Current] [🌍 Select on Map] [❌ No Location]

Step 3: Location Options and Overrides

Location Source Options:

  1. Current GPS: Use device’s current location
  2. Manual Entry: User enters coordinates or address
  3. Map Selection: Interactive map picker for precise placement
  4. Inherited Location: Use parent container’s location for indoor objects
  5. No Location: Skip geographical tracking for indoor/contained objects

Override Scenarios:

  • Creating Historic Records: Object was located elsewhere previously
  • Indoor Objects: Skip geo-location for items with clear spatial containers
  • Privacy Sensitive: User chooses not to share location for certain objects

Step 4: Location Storage and Validation

Data Validation:

interface GeographicalLocation {
  latitude: number;    // -90 to +90
  longitude: number;   // -180 to +180
  accuracy?: number;   // meters
  altitude?: number;   // meters above sea level
  capturedAt: string;  // ISO 8601 timestamp
  source: 'gps' | 'manual' | 'map' | 'inherited' | 'estimated';
  address?: string;    // Reverse-geocoded human-readable address
}

Storage Integration:

  • Neo4j ObjectInstance: Add geographical properties to nodes
  • Supabase Metadata: Store location metadata for querying and indexing
  • Privacy Controls: Respect user location sharing preferences

Step 5: Location-Based Discovery

“Near Me” Functionality:

// Find objects within radius of current location
const nearbyObjects = await findObjectsNear({
  latitude: userLocation.lat,
  longitude: userLocation.lon,
  radiusKm: 5,
  objectTypes: ['tools', 'equipment'],
  organizationId: currentOrg.id
});

Distance Calculation:

// Haversine formula for distance between coordinates
function calculateDistance(lat1, lon1, lat2, lon2) {
  const R = 6371; // Earth's radius in kilometers
  const dLat = toRadians(lat2 - lat1);
  const dLon = toRadians(lon2 - lon1);
  const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
    Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) *
    Math.sin(dLon/2) * Math.sin(dLon/2);
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
  return R * c; // Distance in kilometers
}

Use Cases and Scenarios

Use Case 1: Construction Site Equipment

Scenario: Construction project with equipment spread across large outdoor area

  • Creation: Bulldozer created with GPS coordinates when delivered to site
  • Discovery: Foreman searches “heavy equipment near Building A coordinates”
  • Tracking: Equipment location updated when moved to different site areas
  • Reporting: Generate location-based equipment utilization reports

Use Case 2: Outdoor Event Management

Scenario: Festival organizer tracking assets across event grounds

  • Setup Phase: Speakers, lighting, generators placed and GPS-tagged
  • Event Phase: Staff can find nearest backup equipment by location
  • Breakdown: Recovery teams locate all equipment for removal
  • Analysis: Heat maps showing equipment density and usage patterns

Use Case 3: Agricultural Equipment

Scenario: Farm with equipment distributed across multiple fields

  • Seasonal Storage: Tractors, implements stored in different field locations
  • Work Planning: Find implements near specific field coordinates
  • Maintenance: Locate equipment for scheduled service visits
  • Weather Response: Quickly secure equipment before storms

Use Case 4: Emergency Response

Scenario: Emergency services managing equipment across jurisdiction

  • Resource Deployment: Find nearest medical equipment to incident location
  • Mutual Aid: Share equipment locations with neighboring departments
  • Inventory Management: Track equipment across multiple stations/locations
  • Response Optimization: Analyze equipment placement effectiveness

Use Case 5: Found and Lost Object Management

Scenario: Community members documenting found, lost, or misplaced objects

  • Found Items: User finds abandoned bicycle and logs its location for potential owner recovery
  • Lost Object Reporting: User reports missing laptop with last known location
  • Public Objects: Document location of public resources (benches, water fountains, charging stations)
  • Recovery Networks: Enable community-based object recovery efforts

Use Case 6: Environmental and Conservation Tracking

Scenario: Environmental monitoring and waste management

  • Litter Documentation: Volunteers log trash locations for cleanup coordination
  • Recycling Points: Map recycling centers and waste collection points
  • Natural Resource Monitoring: Track environmental objects and their conditions
  • Conservation Efforts: Document wildlife equipment, trail markers, research instruments

Use Case 7: Commercial and Public Space Objects

Scenario: Tracking objects in commercial or public environments

  • Store Visits: Document interesting products or services encountered while shopping
  • Public Infrastructure: Map public amenities, facilities, and services
  • Business Resources: Track equipment or tools available at different locations
  • Shared Resources: Community tool libraries, equipment sharing programs

Use Case 8: Tool Discovery and Sharing Economy

Scenario: Finding tools and equipment available for purchase, rent, or borrowing based on proximity

  • Immediate Need: “I need a drill right now” - discover drills available within walking distance
  • Multi-Source Discovery: Single search shows tools from:
    • Retail Stores: Tools for purchase at nearby hardware stores
    • Rental Services: Professional tool rental shops in the area
    • Peer-to-Peer: Neighbors willing to lend tools through sharing platforms
    • Community Resources: Tool libraries and maker spaces
  • Availability Filtering: Filter by immediate availability, price range, rental terms
  • Distance-Based Decisions: Compare options based on proximity vs. cost trade-offs

Example User Journey:

  1. User searches: “power drill near me”
  2. System returns sorted by distance:
    • “John’s DeWalt Drill - 200m away - Available to borrow”
    • “Home Depot - 800m away - $89 purchase”
    • “Tool Library - 1.2km away - Free with membership”
    • “Rent-A-Tool - 2.5km away - $15/day rental”
  3. User can filter by:
    • Acquisition type (buy/rent/borrow)
    • Maximum distance willing to travel
    • Price range or free options only
    • User ratings and tool condition
  4. Integration with object status system:
    • Shows real-time availability
    • Indicates if tool is currently in use
    • Estimated return time for borrowed items

Technical Implementation Strategy

Frontend Location Capture

Enhanced CreateObjectModal:

const LocationSection = () => {
  const [location, setLocation] = useState<GeographicalLocation | null>(null);
  const [locationStatus, setLocationStatus] = useState<'detecting' | 'found' | 'denied' | 'manual'>('detecting');
  
  const requestLocation = () => {
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(
        (position) => {
          setLocation({
            latitude: position.coords.latitude,
            longitude: position.coords.longitude,
            accuracy: position.coords.accuracy,
            capturedAt: new Date().toISOString(),
            source: 'gps'
          });
          setLocationStatus('found');
        },
        (error) => {
          console.error('Location access denied:', error);
          setLocationStatus('denied');
        }
      );
    }
  };

  return (
    <div className="location-section">
      {locationStatus === 'detecting' && (
        <div className="location-detecting">
          <GpsIcon className="animate-pulse" />
          <span>Detecting location...</span>
        </div>
      )}
      
      {locationStatus === 'found' && location && (
        <div className="location-found">
          <MapPinIcon />
          <span>{formatCoordinates(location.latitude, location.longitude)}</span>
          <Badge variant="secondary">±{location.accuracy}m</Badge>
          <Button variant="outline" onClick={() => setLocationStatus('manual')}>
            Change Location
          </Button>
        </div>
      )}
      
      {locationStatus === 'denied' && (
        <div className="location-denied">
          <MapPinOffIcon />
          <span>Location access denied</span>
          <Button onClick={() => setLocationStatus('manual')}>
            Enter Manually
          </Button>
        </div>
      )}
      
      {locationStatus === 'manual' && (
        <ManualLocationEntry 
          onLocationSet={setLocation}
          onCancel={() => setLocationStatus('denied')}
        />
      )}
    </div>
  );
};

Backend Database Extensions

Architecture Alignment: Following Plings database architecture rules where PostgreSQL stores metadata/audit trails and Neo4j stores graph data used for queries.

Neo4j ObjectInstance Enhancement:

// Enhanced ObjectInstance node with geographical properties
CREATE (obj:ObjectInstance {
  id: $objectId,
  name: $name,
  description: $description,
  statuses: $statuses,
  owner: $owner,
  
  // Geographical properties
  latitude: $latitude,
  longitude: $longitude,
  location_accuracy: $accuracy,
  location_captured_at: $capturedAt,
  location_source: $locationSource,
  location_address: $address
})

PostgreSQL Location Events Table (for audit trail):

-- Create location_events table for location history and audit
CREATE TABLE location_events (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  object_instance_id UUID REFERENCES object_instances(id) NOT NULL,
  event_type TEXT NOT NULL CHECK (event_type IN ('created', 'updated', 'manual_override', 'inherited')),
  latitude DECIMAL(10, 8) NOT NULL,
  longitude DECIMAL(11, 8) NOT NULL,
  accuracy INTEGER, -- meters
  altitude INTEGER, -- meters above sea level
  source TEXT NOT NULL CHECK (source IN ('gps', 'manual', 'map', 'inherited', 'estimated')),
  address TEXT,
  captured_by UUID REFERENCES profiles(id) NOT NULL,
  captured_at TIMESTAMPTZ DEFAULT now(),
  device_info JSONB, -- optional device/browser info
  
  -- Indexes for performance
  INDEX idx_location_events_object (object_instance_id),
  INDEX idx_location_events_captured_at (captured_at DESC)
);

-- RLS policy for location_events
CREATE POLICY location_events_access ON location_events
FOR ALL USING (
  object_instance_id IN (
    SELECT id FROM object_instances
    -- Inherits RLS from object_instances
  )
);

Note: The primary location data lives in Neo4j for graph-based proximity queries, while PostgreSQL maintains the audit trail and location history. This follows the existing pattern used by ScanEvent nodes which store lat/lon in Neo4j.

GraphQL Schema Extensions

New Types and Operations:

# Location Types
type GeographicalLocation {
  latitude: Float!
  longitude: Float!
  accuracy: Int
  altitude: Int
  capturedAt: String!
  source: LocationSource!
  address: String
}

enum LocationSource {
  GPS
  MANUAL
  MAP
  INHERITED
  ESTIMATED
}

# Enhanced Object Creation
input CreateObjectInput {
  name: String!
  description: String
  ownerOrgId: ID!
  statuses: [String!]
  photos: [Upload!]
  location: GeographicalLocationInput  # NEW
}

input GeographicalLocationInput {
  latitude: Float!
  longitude: Float!
  accuracy: Int
  altitude: Int
  source: LocationSource!
  address: String
}

# Location-based Queries
type Query {
  objectsNear(
    latitude: Float!
    longitude: Float!
    radiusKm: Float!
    filters: ObjectFilters
  ): [ObjectInstance!]!
  
  objectsInBounds(
    northEast: LatLngInput!
    southWest: LatLngInput!
    filters: ObjectFilters
  ): [ObjectInstance!]!
  
  # Tool Discovery Query - NEW
  findToolsNear(
    latitude: Float!
    longitude: Float!
    radiusKm: Float!
    toolQuery: String!
    acquisitionTypes: [AcquisitionType!]
    maxPrice: Float
    availableOnly: Boolean = true
  ): [ToolAvailabilityResult!]!
}

input LatLngInput {
  latitude: Float!
  longitude: Float!
}

# Tool Discovery Types - NEW
type ToolAvailabilityResult {
  object: ObjectInstance!
  distance: Float!              # Distance in meters
  acquisitionOptions: [AcquisitionOption!]!
  availability: ToolAvailability!
  userRating: Float
  estimatedTravelTime: Int      # Minutes walking/driving
}

type AcquisitionOption {
  type: AcquisitionType!
  price: Float
  currency: String
  terms: String                 # "per day", "per hour", "one-time", etc.
  provider: AcquisitionProvider!
  contactInfo: ContactInfo
  immediateAvailability: Boolean!
}

type AcquisitionProvider {
  id: ID!
  name: String!
  type: ProviderType!
  rating: Float
  trustScore: Int
  responseTime: String          # "Usually responds within 1 hour"
}

type ToolAvailability {
  status: AvailabilityStatus!
  availableUntil: String        # ISO datetime
  nextAvailable: String         # ISO datetime if currently unavailable
  currentUser: String           # Who has it now if borrowed
  estimatedReturnTime: String   # When expected back
}

enum AcquisitionType {
  BUY
  RENT
  BORROW
  FREE_USE
}

enum ProviderType {
  PRIVATE_OWNER
  RETAIL_STORE
  RENTAL_SERVICE
  COMMUNITY_LIBRARY
  MAKER_SPACE
  SHARING_PLATFORM
}

enum AvailabilityStatus {
  AVAILABLE
  IN_USE
  RESERVED
  MAINTENANCE
  UNAVAILABLE
}

type ContactInfo {
  method: ContactMethod!
  value: String!
  preferredHours: String
}

enum ContactMethod {
  PHONE
  EMAIL
  IN_APP_MESSAGE
  WALK_IN
  WEBSITE
}

# Enhanced Object with Location
type ObjectInstance {
  id: ID!
  name: String!
  description: String
  location: GeographicalLocation  # NEW
  spatialParent: SpatialRef
  spatialHierarchy: [SpatialRef!]!
  # Tool sharing extensions
  acquisitionOptions: [AcquisitionOption!]! # NEW
  availability: ToolAvailability              # NEW
  # ... existing fields
}

Location-Based Search Implementation

Backend Resolver for Proximity Search:

async def resolve_objects_near(self, info, latitude: float, longitude: float, radius_km: float, filters=None):
    """
    Find objects within specified radius of given coordinates
    Uses Neo4j for spatial queries since location data lives in the graph
    """
    # Neo4j Cypher query for proximity search using Haversine formula
    query = """
    MATCH (obj:ObjectInstance)
    WHERE obj.latitude IS NOT NULL AND obj.longitude IS NOT NULL
    WITH obj, 
         point.distance(
             point({latitude: $latitude, longitude: $longitude}),
             point({latitude: obj.latitude, longitude: obj.longitude})
         ) AS distance
    WHERE distance <= $radius_meters
    RETURN obj.id AS id,
           obj.name AS name,
           obj.latitude AS latitude,
           obj.longitude AS longitude,
           distance
    ORDER BY distance
    LIMIT 100
    """
    
    radius_meters = radius_km * 1000
    
    # Execute Neo4j query
    async with info.context["neo4j_driver"].session() as session:
        result = await session.run(
            query, 
            latitude=latitude,
            longitude=longitude,
            radius_meters=radius_meters
        )
        neo4j_objects = [dict(record) async for record in result]
    
    # Enrich with PostgreSQL metadata if needed
    object_ids = [obj['id'] for obj in neo4j_objects]
    if object_ids:
        pg_query = """
        SELECT id, name, description, owner_organization_id, main_image_url
        FROM object_instances
        WHERE id = ANY($1)
        """
        pg_objects = await db.fetch(pg_query, object_ids)
        
        # Merge Neo4j location data with PostgreSQL metadata
        return merge_location_with_metadata(neo4j_objects, pg_objects)
    
    return []

User Interface Design

Location Display Components

1. Location Capture Interface

┌─────────────────────────────────────┐
│ 📍 Object Location                  │
│                                     │
│ ○ Use current location (📡 GPS)     │
│ ○ Enter coordinates manually        │
│ ○ Select on map                     │
│ ○ Skip location (indoor object)     │
│                                     │
│ [📍 Detect Location]               │
└─────────────────────────────────────┘

2. Location Status Display

┌─────────────────────────────────────┐
│ 📍 Location Detected                │
│                                     │
│ Coordinates: 59.3293°N, 18.0686°E   │
│ Address: Stockholm, Sweden          │
│ Accuracy: ±10 meters                │
│ Source: GPS                         │
│                                     │
│ [🗺️ View on Map] [✏️ Edit] [❌ Remove] │
└─────────────────────────────────────┘

3. Tool Discovery Widget

┌─────────────────────────────────────┐
│ 🔧 Find Tools Near You              │
│                                     │
│ 🔍 [Search for tools...       ] 📍  │
│                                     │
│ Recent: drill, ladder, saw          │
│ [🎯 200m] [💰 <$20] [⚡ Available]   │
└─────────────────────────────────────┘

4. Tool Search Results

┌─────────────────────────────────────┐
│ 🔧 "power drill" near you           │
│                                     │
│ 📍 John's DeWalt Drill • 200m       │
│ 🆓 Borrow • ⭐ 4.9 • Available now │
│ [💬 Message John] [📍 Directions]   │
│                                     │
│ 📍 Home Depot • 800m               │
│ 💰 $89 Purchase • ⭐ 4.2 • Open    │
│ [🌐 Visit Store] [📍 Directions]   │
│                                     │
│ 📍 Tool Library • 1.2km            │
│ 🆓 Free w/ membership • ⭐ 4.7      │
│ [ℹ️ Learn More] [📍 Directions]    │
│                                     │
│ [Show 4 more results...]           │
└─────────────────────────────────────┘

5. Nearby Objects Widget

┌─────────────────────────────────────┐
│ 🗺️ Objects Near You                 │
│                                     │
│ 📍 Drill Set (50m away)             │
│ 📍 Safety Equipment (120m away)     │
│ 📍 Generator (300m away)            │
│                                     │
│ [View All on Map]                   │
└─────────────────────────────────────┘

4. Map Integration Component

const ObjectLocationMap = ({ objects, userLocation }) => {
  return (
    <div className="object-map">
      <MapContainer center={[userLocation.lat, userLocation.lng]} zoom={13}>
        <TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
        
        {/* User location marker */}
        <Marker position={[userLocation.lat, userLocation.lng]}>
          <Popup>Your current location</Popup>
        </Marker>
        
        {/* Object markers */}
        {objects.map(object => (
          <Marker key={object.id} position={[object.latitude, object.longitude]}>
            <Popup>
              <div>
                <h3>{object.name}</h3>
                <p>Distance: {calculateDistance(userLocation, object)}m</p>
                <Button onClick={() => viewObject(object.id)}>View Details</Button>
              </div>
            </Popup>
          </Marker>
        ))}
      </MapContainer>
    </div>
  );
};

Privacy and Security Considerations

Location Privacy Controls

User Preferences:

interface LocationPrivacySettings {
  enableLocationTracking: boolean;
  requireConfirmationPerObject: boolean;
  shareLocationWithOrganization: boolean;
  retainLocationHistory: boolean;
  precisionLevel: 'precise' | 'approximate' | 'city' | 'region';
}

Organization-Level Controls:

  • Admin Settings: Organization admins can require or disable location tracking
  • Data Retention: Configurable retention periods for location data
  • Access Controls: Who can view object locations within organization
  • Export Restrictions: Control over location data in exports and reports

Security Implementation

Data Protection:

  • HTTPS Only: All location data transmitted over encrypted connections
  • Database Encryption: Location coordinates encrypted at rest
  • Access Logging: Audit trail of location data access
  • Geographic Restrictions: Optional geofencing for sensitive locations

Privacy by Design:

  • Opt-in Only: Location tracking disabled by default
  • Clear Consent: Explicit user consent for location collection
  • Granular Control: Per-object location sharing decisions
  • Data Minimization: Only collect necessary location precision

Performance Optimization

Location Query Performance

Database Optimization:

  • Neo4j Spatial Indexes: Point indexes on latitude/longitude properties for fast proximity queries
  • Graph Traversal: Combine location queries with spatial relationships (IN, ON, etc.)
  • Query Caching: Cache frequent location-based searches at API level
  • Batched Updates: Group location updates when modifying multiple objects
  • Selective Loading: Only fetch location properties when explicitly requested

Frontend Optimization:

  • Location Caching: Cache user location to avoid repeated GPS requests
  • Debounced Queries: Prevent excessive API calls during map interaction
  • Progressive Loading: Load nearby objects incrementally as user explores
  • Offline Support: Cache location data for offline object creation

Mobile Performance

GPS Optimization:

  • Timeout Management: Fallback to last known location if GPS slow
  • Battery Conservation: Minimize GPS usage through smart caching
  • Network Efficiency: Compress location data in API requests
  • Background Processing: Location detection while user fills other fields

Analytics and Insights

Location-Based Analytics

Usage Metrics:

  • Location Adoption Rate: Percentage of objects with geographical data
  • Search Patterns: Most common location-based search queries
  • Discovery Effectiveness: Success rate of “near me” searches
  • Geographic Distribution: Heat maps of object density

Business Intelligence:

  • Asset Utilization: Equipment usage patterns by geographical area
  • Efficiency Analysis: Travel time savings from location-based discovery
  • Coverage Assessment: Identification of under-served geographical areas
  • Demand Forecasting: Predict equipment needs by location and time

Success Metrics

User Experience Metrics

  • Location Capture Rate: % of objects created with geographical coordinates
  • Search Success Rate: % of location-based searches that find relevant objects
  • Time to Discovery: Average time to locate objects using geographical search
  • User Satisfaction: Feedback on location-based features

Technical Performance Metrics

  • GPS Accuracy: Average accuracy of captured location coordinates
  • Query Performance: Response time for proximity-based object searches
  • Battery Impact: GPS usage impact on mobile device battery life
  • Data Quality: Completeness and accuracy of geographical metadata

Business Impact Metrics

  • Operational Efficiency: Reduction in time spent physically searching for objects
  • Asset Recovery: Improved success rate in locating misplaced equipment
  • Cost Savings: Reduced travel time and fuel costs for asset retrieval
  • Decision Making: Better resource allocation based on geographical insights

Future Enhancements

Advanced Location Features

  • Movement Tracking: Historical location trail for mobile objects
  • Geofencing: Alerts when objects move outside designated areas
  • Route Optimization: Optimal paths for collecting multiple objects
  • Location-Based Automation: Automatic status changes based on geographical triggers

Integration Expansions

  • IoT Integration: Real-time location updates from GPS-enabled devices
  • Augmented Reality: AR-guided navigation to object locations
  • Weather Integration: Weather conditions at object locations
  • Traffic Integration: Real-time travel time estimates to object locations

Current System References

Integration Points

  • Spatial System: Complements existing relative positioning with absolute coordinates
  • Object Creation: Extends CreateObjectModal with location capture
  • Search System: Adds geographical dimension to object discovery
  • Mobile Experience: Leverages mobile GPS capabilities for location-aware workflows

Use Case Created: Mån 7 Jul 2025 11:04:28 CEST - Geographical Location (LAT/LON) tracking for location-based object discovery and “near me” functionality