apiKey = config('shj1.apis.google_maps.api_key'); $this->baseUrl = config('shj1.apis.google_maps.base_url'); $this->config = config('shj1.distance.google_maps_config'); } /** * 2点間の距離を計算(Google Maps Distance Matrix API使用) * * @param string $origin 出発地(住所) * @param string $destination 目的地(住所) * @return array ['distance_meters' => int, 'distance_text' => string, 'status' => string] * @throws Exception */ public function calculateDistance(string $origin, string $destination): array { try { $url = "{$this->baseUrl}/distancematrix/json"; $params = [ 'origins' => $origin, 'destinations' => $destination, 'units' => $this->config['units'] ?? 'metric', 'mode' => $this->config['mode'] ?? 'walking', 'language' => $this->config['language'] ?? 'ja', 'region' => $this->config['region'] ?? 'jp', 'key' => $this->apiKey, ]; $response = Http::timeout(30)->get($url, $params); if (!$response->successful()) { throw new Exception("Google Maps API request failed: " . $response->status()); } $data = $response->json(); // APIエラーチェック if ($data['status'] !== 'OK') { throw new Exception("Google Maps API error: " . $data['status']); } $element = $data['rows'][0]['elements'][0]; if ($element['status'] !== 'OK') { throw new Exception("Distance calculation failed: " . $element['status']); } return [ 'distance_meters' => $element['distance']['value'], 'distance_text' => $element['distance']['text'], 'duration_text' => $element['duration']['text'] ?? null, 'status' => 'success' ]; } catch (Exception $e) { Log::error('Google Maps distance calculation error', [ 'origin' => $origin, 'destination' => $destination, 'error' => $e->getMessage() ]); throw $e; } } /** * 住所から座標を取得(Geocoding API使用) * * @param string $address 住所 * @return array ['lat' => float, 'lng' => float] * @throws Exception */ public function geocodeAddress(string $address): array { try { $url = "{$this->baseUrl}/geocode/json"; $params = [ 'address' => $address, 'language' => $this->config['language'] ?? 'ja', 'region' => $this->config['region'] ?? 'jp', 'key' => $this->apiKey, ]; $response = Http::timeout(30)->get($url, $params); if (!$response->successful()) { throw new Exception("Google Geocoding API request failed: " . $response->status()); } $data = $response->json(); if ($data['status'] !== 'OK') { throw new Exception("Google Geocoding API error: " . $data['status']); } $location = $data['results'][0]['geometry']['location']; return [ 'lat' => $location['lat'], 'lng' => $location['lng'] ]; } catch (Exception $e) { Log::error('Google Maps geocoding error', [ 'address' => $address, 'error' => $e->getMessage() ]); throw $e; } } /** * 座標から直線距離を計算(Haversine formula) * Google Maps APIが利用できない場合のフォールバック * * @param float $lat1 緯度1 * @param float $lon1 経度1 * @param float $lat2 緯度2 * @param float $lon2 経度2 * @return int 距離(メートル) */ public function calculateStraightLineDistance(float $lat1, float $lon1, float $lat2, float $lon2): int { $earthRadius = 6371000; // 地球の半径(メートル) $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)); $distance = $earthRadius * $c; return round($distance); } /** * 距離詳細文字列を生成(SHJ-1の要求仕様に準拠) * * @param int $parkId 駐輪場ID * @param int $distanceMeters 距離(メートル) * @param string $method 計算方法 * @return string */ public function generateDistanceDetailString(int $parkId, int $distanceMeters, string $method = 'google_maps'): string { return $parkId . "/" . $parkId . "/二点間距離:" . $distanceMeters . "m (" . $method . ")"; } /** * APIキーが設定されているかチェック * * @return bool */ public function isApiKeyConfigured(): bool { $dummyKey = env('GOOGLE_MAPS_API_KEY') === 'dummy_google_maps_api_key_replace_with_real_one'; return !empty($this->apiKey) && !$dummyKey; } }