baseUrl = 'https://api.opentripmap.com/0.1/en/places'; $this->apiKey = config('services.opentripmap.key', env('OPENTRIPMAP_API_KEY')); $this->cacheTtl = config('services.opentripmap.cache_ttl', 7) * 24 * 60; // Convert days to minutes } /** * Get attractions near coordinates * * @param float $lat * @param float $lon * @param int $radius in meters (default 5000m = 5km) * @param int $limit * @return array */ public function getAttractions(float $lat, float $lon, int $radius = 5000, int $limit = 50): array { $cacheKey = "opentripmap:attractions:{$lat}:{$lon}:{$radius}:{$limit}"; return Cache::remember($cacheKey, $this->cacheTtl, function () use ($lat, $lon, $radius, $limit) { try { $response = Http::timeout(15) ->get("{$this->baseUrl}/radius", [ 'apikey' => $this->apiKey, 'radius' => $radius, 'lon' => $lon, 'lat' => $lat, 'limit' => $limit, 'format' => 'json', ]); if ($response->successful()) { $features = $response->json('features', []); $attractions = []; foreach ($features as $feature) { $properties = $feature['properties'] ?? []; $geometry = $feature['geometry'] ?? []; $coordinates = $geometry['coordinates'] ?? []; if (!empty($properties['name'])) { $attractions[] = [ 'name' => $properties['name'], 'source_id' => $properties['xid'] ?? null, 'latitude' => $coordinates[1] ?? null, 'longitude' => $coordinates[0] ?? null, 'category' => $this->mapCategory($properties['kinds'] ?? ''), 'rating' => $properties['rate'] ?? null, ]; } } return $attractions; } Log::warning("OpenTripMap API request failed", [ 'status' => $response->status(), 'lat' => $lat, 'lon' => $lon, ]); return []; } catch (\Exception $e) { Log::error("OpenTripMap API error", [ 'error' => $e->getMessage(), 'lat' => $lat, 'lon' => $lon, ]); return []; } }); } /** * Get detailed information about an attraction * * @param string $xid OpenTripMap attraction ID * @return array|null */ public function getAttractionDetails(string $xid): ?array { $cacheKey = "opentripmap:details:{$xid}"; return Cache::remember($cacheKey, $this->cacheTtl, function () use ($xid) { try { $response = Http::timeout(10) ->get("{$this->baseUrl}/xid/{$xid}", [ 'apikey' => $this->apiKey, ]); if ($response->successful()) { $data = $response->json(); return [ 'name' => $data['name'] ?? null, 'description' => $data['wikipedia_extracts']['text'] ?? $data['info']['descr'] ?? null, 'image_url' => $data['preview']['source'] ?? $data['image'] ?? null, 'wikipedia_url' => $data['wikipedia'] ?? null, 'rating' => $data['rate'] ?? null, ]; } return null; } catch (\Exception $e) { Log::error("OpenTripMap details error for xid: {$xid}", [ 'error' => $e->getMessage(), ]); return null; } }); } /** * Map OpenTripMap categories to our simplified categories * * @param string $kinds * @return string */ private function mapCategory(string $kinds): string { $kinds = strtolower($kinds); if (str_contains($kinds, 'museums')) { return 'museum'; } elseif (str_contains($kinds, 'theatres_and_entertainments') || str_contains($kinds, 'cinemas')) { return 'entertainment'; } elseif (str_contains($kinds, 'cultural') || str_contains($kinds, 'interesting_places')) { return 'cultural'; } elseif (str_contains($kinds, 'architecture') || str_contains($kinds, 'historic')) { return 'historic'; } elseif (str_contains($kinds, 'natural') || str_contains($kinds, 'nature_reserves')) { return 'nature'; } elseif (str_contains($kinds, 'sport')) { return 'sport'; } elseif (str_contains($kinds, 'food') || str_contains($kinds, 'restaurants')) { return 'restaurant'; } return 'other'; } /** * Clear cache for specific coordinates * * @param float $lat * @param float $lon * @return void */ public function clearCache(float $lat, float $lon): void { // Clear all related cache keys Cache::forget("opentripmap:attractions:{$lat}:{$lon}:5000:50"); } }