retry(0) // wir handeln retry selbst ->get('https://maps.googleapis.com/maps/api/directions/json', [ 'origin' => $origin, 'destination' => $destination, 'mode' => $mode, 'language' => 'de', 'region' => 'at', 'key' => $key, ]); if (! $response->successful()) { $lastErr = 'HTTP '.$response->status(); Log::warning('Directions API HTTP-Fehler', ['attempt' => $attempt, 'status' => $response->status()]); if ($attempt < 2) { usleep(400_000); continue; } return null; } $data = $response->json(); $status = $data['status'] ?? null; // ZERO_RESULTS oder UNKNOWN_ERROR sind oft transient bei ÖPNV - retry if (in_array($status, ['ZERO_RESULTS', 'UNKNOWN_ERROR', 'OVER_QUERY_LIMIT'], true) && $attempt < 2) { Log::info('Directions transient '.$status.', retry'); usleep(500_000); continue; } break; } catch (\Throwable $e) { $lastErr = $e->getMessage(); Log::warning('Directions exception', ['attempt' => $attempt, 'e' => $lastErr]); if ($attempt < 2) { usleep(400_000); continue; } return null; } } try { if ($status !== 'OK' || empty($data['routes'])) { Log::warning('Directions: keine Route nach '.($attempt ?? 1).' Versuchen', [ 'status' => $status, 'msg' => $data['error_message'] ?? $lastErr, 'origin' => $origin, 'destination' => $destination, ]); return null; } $route = $data['routes'][0]; $leg = $route['legs'][0]; return [ 'distance_text' => $leg['distance']['text'] ?? '', 'distance_m' => $leg['distance']['value'] ?? 0, 'duration_text' => $leg['duration']['text'] ?? '', 'duration_s' => $leg['duration']['value'] ?? 0, 'start_address' => $leg['start_address'] ?? '', 'end_address' => $leg['end_address'] ?? '', 'start_location' => $leg['start_location'] ?? null, 'end_location' => $leg['end_location'] ?? null, 'bounds' => $route['bounds'] ?? null, 'geojson' => $this->decodePolyline($route['overview_polyline']['points'] ?? ''), 'mode' => $mode, 'steps' => $this->normalizeSteps($leg['steps'] ?? []), ]; } catch (\Throwable $e) { Log::warning('RouteService exception', ['e' => $e->getMessage()]); return null; } } /** * Google-Steps → schlankeres Format für die Sidebar mit Transit-Details. */ protected function normalizeSteps(array $steps): array { $out = []; foreach ($steps as $s) { $mode = $s['travel_mode'] ?? 'WALKING'; $instruction = strip_tags(str_replace( ['