185 lines
5.7 KiB
PHP
185 lines
5.7 KiB
PHP
<?php
|
||
|
||
namespace App\Services;
|
||
|
||
use Exception;
|
||
use Illuminate\Support\Facades\Http;
|
||
use Illuminate\Support\Facades\Log;
|
||
|
||
/**
|
||
* Google Maps API サービス
|
||
* SHJ-1本人確認自動処理で距離計算に使用
|
||
*/
|
||
class GoogleMapsService
|
||
{
|
||
protected $apiKey;
|
||
protected $baseUrl;
|
||
protected $config;
|
||
|
||
public function __construct()
|
||
{
|
||
$this->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;
|
||
}
|
||
}
|