'datetime', 'fix_time' => 'datetime', 'server_time' => 'datetime', 'outdated' => 'boolean', 'valid' => 'boolean', 'latitude' => 'decimal:8', 'longitude' => 'decimal:8', 'altitude' => 'decimal:2', 'speed' => 'decimal:2', 'course' => 'decimal:2', 'accuracy' => 'decimal:2', 'attributes' => 'array', ]; /** * Get the device that owns this position */ public function device(): BelongsTo { return $this->belongsTo(Device::class); } /** * Scope for recent positions */ public function scopeRecent($query, $hours = 24) { return $query->where('device_time', '>=', now()->subHours($hours)); } /** * Scope for valid positions */ public function scopeValid($query) { return $query->where('valid', true); } /** * Get formatted speed with unit */ public function getFormattedSpeed(): string { $speedKmh = $this->speed * 1.852; // Convert knots to km/h return round($speedKmh, 1) . ' km/h'; } /** * Get formatted coordinates */ public function getCoordinates(): string { return number_format($this->latitude, 6) . ', ' . number_format($this->longitude, 6); } /** * Check if position is in a specific geofence */ public function isInGeofence(Geofence $geofence): bool { // Simple point-in-polygon check for circular geofences if ($geofence->type === 'circle') { $distance = $this->calculateDistance( $this->latitude, $this->longitude, $geofence->latitude, $geofence->longitude ); return $distance <= $geofence->radius; } // For polygon geofences, you would implement a point-in-polygon algorithm return false; } /** * Calculate distance between two points in meters */ private function calculateDistance($lat1, $lon1, $lat2, $lon2): float { $earthRadius = 6371000; // Earth's radius in meters $dLat = deg2rad($lat2 - $lat1); $dLon = deg2rad($lon2 - $lon1); $a = sin($dLat / 2) * sin($dLat / 2) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLon / 2) * sin($dLon / 2); $c = 2 * atan2(sqrt($a), sqrt(1 - $a)); return $earthRadius * $c; } }