src/Service/MeteomaticsWeatherService.php line 1819

Open in your IDE?
  1. <?php
  2. namespace App\Service;
  3. use PDO;
  4. use DateTime;
  5. use GuzzleHttp;
  6. use GuzzleHttp\Client;
  7. use GuzzleHttp\Exception;
  8. use App\Lib\ExcelGenerator;
  9. use App\Service\RedisCache;
  10. use Pimcore\Model\DataObject\Report;
  11. use GuzzleHttp\Exception\RequestException;
  12. use Symfony\Component\HttpFoundation\Response;
  13. class MeteomaticsWeatherService
  14. {
  15. private $apiBaseUrl = MATEOMATICS_API_URL;
  16. private $username;
  17. private $password;
  18. private $redisCache;
  19. public function __construct(RedisCache $redisCache)
  20. {
  21. $this->username = MATEOMATICS_API_USERNAME;
  22. $this->password = MATEOMATICS_API_PASSWORD;
  23. $this->redisCache = $redisCache;
  24. }
  25. /**
  26. * Query Meteomatics API for time series data and return the parsed response
  27. *
  28. * @param DateTime $startDate The start date of the time series data
  29. * @param DateTime $endDate The end date of the time series data
  30. * @param string $resolution The time resolution of the data (e.g. PT1H for hourly data)
  31. * @param float $lat The latitude of the location to query data for
  32. * @param float $lon The longitude of the location to query data for
  33. * @param int $hour The number of hours ahead to forecast (e.g. 1 for one hour ahead)
  34. * @param string $format The format to request the data in (e.g. json)
  35. * @return array The parsed response data
  36. */
  37. public function timeSeriesQueryMeteocache(DateTime $startDate, DateTime $endDate, $resolution, $lat, $lon, $hour, $format)
  38. {
  39. try {
  40. $startDateStr = $startDate->format(DateTime::ISO8601);
  41. $endDateStr = $endDate->format(DateTime::ISO8601);
  42. $parameters = [
  43. 'wind_speed_10m:kmh',
  44. 'wind_dir_10m:d',
  45. 't_2m:C',
  46. 'precip_3h:mm',
  47. 'weather_symbol_' . $hour . 'h:idx',
  48. 'precip_type:idx',
  49. 'sunrise:sql'
  50. ];
  51. $parametersStr = implode(',', $parameters);
  52. // Create unique Redis key
  53. $keyParams = [
  54. $startDate->format(DateTime::ISO8601),
  55. $endDate->format(DateTime::ISO8601),
  56. $resolution,
  57. $lat,
  58. $lon,
  59. $hour,
  60. $format
  61. ];
  62. $redisKey = hash('sha256', implode('_', $keyParams));
  63. // Try to get the weather forecast data from Redis cache
  64. $data = $this->redisCache->get($redisKey);
  65. if (!$data) {
  66. $url = "{$this->apiBaseUrl}/{$startDateStr}--{$endDateStr}:{$resolution}/{$parametersStr}/{$lat},{$lon}/" . $format;
  67. //echo $url;exit;
  68. $client = new Client(['verify' => false]);
  69. $response = $client->request('GET', $url, [
  70. 'auth' => [$this->username, $this->password],
  71. 'connect_timeout' => 2,
  72. 'headers' => [
  73. 'User-Agent' => 'Meteomatics PHP connector (Guzzle)'
  74. ]
  75. ]);
  76. $statusCode = $response->getStatusCode();
  77. $data = json_decode($response->getBody(), true);
  78. if ($statusCode != 200) {
  79. return $this->createErrorResponse($statusCode);
  80. }
  81. $parsedData = array();
  82. if (isset($data['data']) && $format == "json") {
  83. foreach ($data['data'] as $item) {
  84. $parameter = $item["parameter"];
  85. $coordinates = $item["coordinates"];
  86. $lat = $coordinates[0]["lat"];
  87. $lon = $coordinates[0]["lon"];
  88. $dates = $coordinates[0]["dates"];
  89. $groupedDates = array();
  90. foreach ($dates as $date) {
  91. $dateTime = new DateTime($date["date"]);
  92. $dateString = $dateTime->format("Y-m-d");
  93. if (!array_key_exists($dateString, $groupedDates)) {
  94. $groupedDates[$dateString] = array();
  95. }
  96. $groupedDates[$dateString][] = $date["value"];
  97. }
  98. $parsedData[$parameter] = array("coordinates" => array("lat" => $lat, "lon" => $lon), "dates" => $groupedDates);
  99. }
  100. } else {
  101. $parsedData = $data;
  102. }
  103. if ($parsedData) {
  104. $this->redisCache->set($redisKey, $parsedData, 86400);
  105. }
  106. } else {
  107. $parsedData = $data;
  108. }
  109. return $parsedData;
  110. } catch (Exception\RequestException $e) {
  111. return throw new \Exception($e->getMessage());
  112. } catch (\Exception $e) {
  113. return throw new \Exception($e->getMessage());
  114. }
  115. }
  116. public function createErrorResponse(int $http_code): Response
  117. {
  118. switch ($http_code) {
  119. case 100:
  120. $text = 'Continue';
  121. break;
  122. case 101:
  123. $text = 'Switching Protocols';
  124. break;
  125. case 200:
  126. $text = 'OK';
  127. break;
  128. case 201:
  129. $text = 'Created';
  130. break;
  131. case 202:
  132. $text = 'Accepted';
  133. break;
  134. case 203:
  135. $text = 'Non-Authoritative Information';
  136. break;
  137. case 204:
  138. $text = 'No Content';
  139. break;
  140. case 205:
  141. $text = 'Reset Content';
  142. break;
  143. case 206:
  144. $text = 'Partial Content';
  145. break;
  146. case 300:
  147. $text = 'Multiple Choices';
  148. break;
  149. case 301:
  150. $text = 'Moved Permanently';
  151. break;
  152. case 302:
  153. $text = 'Moved Temporarily';
  154. break;
  155. case 303:
  156. $text = 'See Other';
  157. break;
  158. case 304:
  159. $text = 'Not Modified';
  160. break;
  161. case 305:
  162. $text = 'Use Proxy';
  163. break;
  164. case 400:
  165. $text = 'Bad Request';
  166. break;
  167. case 401:
  168. $text = 'Unauthorized';
  169. break;
  170. case 402:
  171. $text = 'Payment Required';
  172. break;
  173. case 403:
  174. $text = 'Forbidden';
  175. break;
  176. case 404:
  177. $text = 'Not Found';
  178. break;
  179. case 405:
  180. $text = 'Method Not Allowed';
  181. break;
  182. case 406:
  183. $text = 'Not Acceptable';
  184. break;
  185. case 407:
  186. $text = 'Proxy Authentication Required';
  187. break;
  188. case 408:
  189. $text = 'Request Time-out';
  190. break;
  191. case 409:
  192. $text = 'Conflict';
  193. break;
  194. case 410:
  195. $text = 'Gone';
  196. break;
  197. case 411:
  198. $text = 'Length Required';
  199. break;
  200. case 412:
  201. $text = 'Precondition Failed';
  202. break;
  203. case 413:
  204. $text = 'Request Entities Too Large';
  205. break;
  206. case 414:
  207. $text = 'Request-URI Too Large';
  208. break;
  209. case 415:
  210. $text = 'Unsupported Media Type';
  211. break;
  212. case 500:
  213. $text = 'Internal Server Error';
  214. break;
  215. case 501:
  216. $text = 'Not Implemented';
  217. break;
  218. case 502:
  219. $text = 'Bad Gateway';
  220. break;
  221. case 503:
  222. $text = 'Service Unavailable';
  223. break;
  224. case 504:
  225. $text = 'Gateway Time-out';
  226. break;
  227. case 505:
  228. $text = 'HTTP Version not supported';
  229. break;
  230. default:
  231. $text = 'Unknown http status code';
  232. break;
  233. }
  234. $response = new Response();
  235. $response->setContent($text);
  236. $response->setStatusCode($http_code);
  237. $response->headers->set('Content-Type', 'text/plain');
  238. return $response;
  239. }
  240. /**
  241. * Fetches the weather forecast data for a given latitude, longitude, and for selected data range
  242. *
  243. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  244. * @param int $days The number of days for which forecast is required
  245. * @return array|null The weather forecast data in JSON format, or null if there was an error
  246. */
  247. public function getTempratureByParams(array $coordinates, string $startDate, string $endDate, string $parametersStr, int $hour = 1)
  248. {
  249. try {
  250. if (count($coordinates) > 1) {
  251. // Validate the input parameters
  252. foreach ($coordinates as $coordinate) {
  253. if (count($coordinate) < 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  254. throw new \InvalidArgumentException('Invalid coordinates');
  255. }
  256. }
  257. if (empty($startDate) || empty($endDate)) {
  258. throw new \InvalidArgumentException('Invalid dates');
  259. }
  260. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  261. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  262. // Create a Redis key for the cache
  263. $cacheKey = sprintf('custom_noti_%s_%s_%s', md5(implode('_', array_map(function ($coordinate) {
  264. return implode('_', $coordinate);
  265. }, $coordinates))), ($startDate . '-' . $endDate), $parametersStr . $hour);
  266. // Try to get the data from Redis cache
  267. $cachedData = $this->redisCache->get($cacheKey);
  268. if ($cachedData !== null) {
  269. // Return the cached data if available
  270. return $cachedData;
  271. }
  272. // If cache is empty, get the data from the API
  273. $client = new Client(['verify' => false]);
  274. // $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  275. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  276. // p_r($coordinateString); // Debug
  277. $url = sprintf(
  278. '%s/%s--%s:PT%sH/%s/%s/json',
  279. $this->apiBaseUrl,
  280. $startDate,
  281. $endDate,
  282. $hour,
  283. $parametersStr,
  284. $coordinateString
  285. );
  286. } else {
  287. //p_R($coordinates);
  288. if ($coordinates) {
  289. $latitude = $coordinates[0][0];
  290. $longitude = $coordinates[0][1];
  291. }
  292. // Validate the input parameters
  293. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  294. throw new \InvalidArgumentException('Invalid latitude');
  295. }
  296. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  297. throw new \InvalidArgumentException('Invalid longitude');
  298. }
  299. if (empty($startDate) || empty($endDate)) {
  300. throw new \InvalidArgumentException('Invalid dates');
  301. }
  302. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  303. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  304. // Create a Redis key for the cache
  305. $cacheKey = sprintf('custom_noti_%s_%s_%s', md5(implode('_', array_map(function ($coordinate) {
  306. return implode('_', $coordinate);
  307. }, $coordinates))), ($startDate . '-' . $endDate), $parametersStr . $hour);
  308. // Try to get the data from Redis cache
  309. $cachedData = $this->redisCache->get($cacheKey);
  310. if ($cachedData !== null) {
  311. // Return the cached data if available
  312. //return $cachedData;
  313. }
  314. // If cache is empty, get the data from the API
  315. $client = new Client(['verify' => false]);
  316. $url = sprintf(
  317. '%s/%s--%s:PT%sH/%s/%s,%s/json',
  318. $this->apiBaseUrl,
  319. $startDate,
  320. $endDate,
  321. $hour,
  322. $parametersStr,
  323. $latitude,
  324. $longitude
  325. );
  326. //p_r($url);
  327. }
  328. $response = $client->request('GET', $url, [
  329. 'auth' => [$this->username, $this->password],
  330. ]);
  331. $statusCode = $response->getStatusCode();
  332. $data = json_decode($response->getBody(), true);
  333. // Set the data to Redis cache
  334. $this->redisCache->set($cacheKey, $data);
  335. return $data;
  336. } catch (GuzzleHttp\Exception\RequestException $e) {
  337. return throw new \Exception($e->getMessage());
  338. } catch (\Exception $e) {
  339. return throw new \Exception($e->getMessage());
  340. }
  341. }
  342. /**
  343. * Fetches the weather forecast data for a given latitude, longitude, and for selected data range
  344. *
  345. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  346. * @param int $days The number of days for which forecast is required
  347. * @return array|null The weather forecast data in JSON format, or null if there was an error
  348. */
  349. public function getForecastData(array $coordinates, string $startDate, string $endDate, int $hours, string $model = "ksancm-wrf-48", $translator)
  350. {
  351. try {
  352. // Set timezone to Saudi (UTC+3)
  353. $timezone = new \DateTimeZone('Asia/Riyadh');
  354. if (count($coordinates) > 1) {
  355. // Validate the input parameters
  356. foreach ($coordinates as $coordinate) {
  357. if (count($coordinate) < 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  358. // throw new \InvalidArgumentException('Invalid coordinates');
  359. return ["success" => false, "message" => $translator->trans("invalid_coordinates")];
  360. }
  361. }
  362. if (empty($startDate) || empty($endDate)) {
  363. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  364. // throw new \InvalidArgumentException('Invalid dates');
  365. }
  366. if ($hours < 1 and $hours > 24) {
  367. throw new \InvalidArgumentException('Invalid hour');
  368. }
  369. // $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  370. // $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  371. // // Adjust the date range based on the hours parameter
  372. // if ($hours == 24) {
  373. // $startDateNew = date('Y-m-d\TH:i:s.v\Z', strtotime($startDate . ' +1 day'));
  374. // $endDateNew = date('Y-m-d\TH:i:s.v\Z', strtotime($endDate . ' +1 day'));
  375. // } elseif ($hours == 1) {
  376. // $startDateNew = date('Y-m-d\TH:i:s.v\Z', strtotime($startDate . ' +1 hour'));
  377. // $endDateNew = date('Y-m-d\TH:i:s.v\Z', strtotime($endDate . ' +1 hour'));
  378. // }
  379. // Convert dates to Saudi timezone
  380. $startDateObj = new \DateTime($startDate, $timezone);
  381. $endDateObj = new \DateTime($endDate, $timezone);
  382. // Subtract 3 hours from each date
  383. $startDateObj->modify('-3 hours');
  384. $endDateObj->modify('-3 hours');
  385. $startDate = $startDateObj->format('Y-m-d\TH:i:s.v\Z');
  386. $endDate = $endDateObj->format('Y-m-d\TH:i:s.v\Z');
  387. // Adjust date range based on the hours parameter
  388. $startDateNew = clone $startDateObj;
  389. $endDateNew = clone $endDateObj;
  390. if ($hours == 24) {
  391. $startDateNew->modify('+1 day');
  392. $endDateNew->modify('+1 day');
  393. } elseif ($hours == 1) {
  394. $startDateNew->modify('+1 hour');
  395. $endDateNew->modify('+1 hour');
  396. }
  397. $startDateNew = $startDateNew->format('Y-m-d\TH:i:s.v\Z');
  398. $endDateNew = $endDateNew->format('Y-m-d\TH:i:s.v\Z');
  399. // Create a Redis key for the cache
  400. $cacheKey = sprintf('daily_forecast_%s_%s', implode('_', array_map(function ($coordinate) {
  401. return implode('_', $coordinate);
  402. }, $coordinates)), (($startDate) . '-' . ($endDate)) . $model . $hours);
  403. // Try to get the data from Redis cache
  404. $cachedData = $this->redisCache->get($cacheKey);
  405. if ($cachedData !== null) {
  406. // Return the cached data if available
  407. return $cachedData;
  408. }
  409. // If cache is empty, get the data from the API
  410. $client = new Client(['verify' => false]);
  411. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  412. $url1 = sprintf(
  413. '%s/%s--%s:PT%sH/t_max_2m_%sh:C,t_min_2m_%sh:C,t_apparent_min_%sh:C,t_apparent_max_%sh:C/%s/json?model=' . $model,
  414. $this->apiBaseUrl,
  415. $startDateNew,
  416. $endDateNew,
  417. $hours,
  418. $hours,
  419. $hours,
  420. $hours,
  421. $hours,
  422. $coordinateString
  423. );
  424. $url2 = sprintf(
  425. '%s/%s--%s:PT%sH/wind_speed_mean_10m_%sh:kmh,wind_dir_mean_10m_%sh:d,prob_precip_%sh:p,precip_%sh:mm,relative_humidity_mean_2m_%sh:p,effective_cloud_cover_mean_%sh:octas,dew_point_mean_2m_%sh:C,wind_gusts_10m_%sh:kmh/%s/json?model=' . $model,
  426. $this->apiBaseUrl,
  427. $startDateNew,
  428. $endDateNew,
  429. $hours,
  430. $hours,
  431. $hours,
  432. $hours,
  433. $hours,
  434. $hours,
  435. $hours,
  436. $hours,
  437. $hours,
  438. $coordinateString
  439. );
  440. $url3 = sprintf(
  441. '%s/%s--%s:PT%sH/t_2m:C,visibility:km/%s/json?model=mix',
  442. $this->apiBaseUrl,
  443. $startDate,
  444. $endDate,
  445. $hours,
  446. $coordinateString
  447. );
  448. $url4 = sprintf(
  449. '%s/%s--%s:PT10M/t_2m:C/%s/json?model=' . $model,
  450. $this->apiBaseUrl,
  451. $startDate,
  452. $endDate,
  453. $coordinateString
  454. );
  455. $url5 = sprintf(
  456. '%s/%s--%s:PT3H/precip_3h:mm,prob_precip_3h:p/%s/json?model=' . $model,
  457. $this->apiBaseUrl,
  458. $startDate,
  459. $endDate,
  460. $coordinateString
  461. );
  462. $url6 = sprintf(
  463. '%s/%s--%s:PT6H/precip_6h:mm,prob_precip_6h:p/%s/json?model=' . $model,
  464. $this->apiBaseUrl,
  465. $startDate,
  466. $endDate,
  467. $coordinateString
  468. );
  469. } else {
  470. if ($coordinates) {
  471. $latitude = $coordinates[0][0];
  472. $longitude = $coordinates[0][1];
  473. }
  474. // Validate the input parameters
  475. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  476. // throw new \InvalidArgumentException('Invalid latitude');
  477. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  478. }
  479. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  480. // throw new \InvalidArgumentException('Invalid longitude');
  481. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  482. }
  483. if (empty($startDate) || empty($endDate)) {
  484. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  485. }
  486. if ($hours < 1 and $hours > 24) {
  487. throw new \InvalidArgumentException('Invalid hour');
  488. }
  489. // $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  490. // $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  491. // // Adjust the date range based on the hours parameter
  492. // if ($hours == 24) {
  493. // $startDateNew = date('Y-m-d\TH:i:s.v\Z', strtotime($startDate . ' +1 day'));
  494. // $endDateNew = date('Y-m-d\TH:i:s.v\Z', strtotime($endDate . ' +1 day'));
  495. // } elseif ($hours == 1) {
  496. // $startDateNew = date('Y-m-d\TH:i:s.v\Z', strtotime($startDate . ' +1 hour'));
  497. // $endDateNew = date('Y-m-d\TH:i:s.v\Z', strtotime($endDate . ' +1 hour'));
  498. // }
  499. // Convert dates to Saudi timezone
  500. $startDateObj = new \DateTime($startDate, $timezone);
  501. $endDateObj = new \DateTime($endDate, $timezone);
  502. // Subtract 3 hours from each date
  503. $startDateObj->modify('-3 hours');
  504. $endDateObj->modify('-3 hours');
  505. $startDate = $startDateObj->format('Y-m-d\TH:i:s.v\Z');
  506. $endDate = $endDateObj->format('Y-m-d\TH:i:s.v\Z');
  507. // Adjust date range based on the hours parameter
  508. $startDateNew = clone $startDateObj;
  509. $endDateNew = clone $endDateObj;
  510. if ($hours == 24) {
  511. $startDateNew->modify('+1 day');
  512. $endDateNew->modify('+1 day');
  513. } elseif ($hours == 1) {
  514. $startDateNew->modify('+1 hour');
  515. $endDateNew->modify('+1 hour');
  516. }
  517. $startDateNew = $startDateNew->format('Y-m-d\TH:i:s.v\Z');
  518. $endDateNew = $endDateNew->format('Y-m-d\TH:i:s.v\Z');
  519. // Create a Redis key for the cache
  520. $cacheKey = sprintf('daily_forecast_%s_%s', implode('_', array_map(function ($coordinate) {
  521. return implode('_', $coordinate);
  522. }, $coordinates)), ($startDate . '-' . $endDate) . $model . $hours);
  523. // Try to get the data from Redis cache
  524. $cachedData = $this->redisCache->get($cacheKey);
  525. if ($cachedData !== null) {
  526. // Return the cached data if available
  527. return $cachedData;
  528. }
  529. // If cache is empty, get the data from the API
  530. $client = new Client(['verify' => false]);
  531. $url1 = sprintf(
  532. '%s/%s--%s:PT%sH/t_max_2m_%sh:C,t_min_2m_%sh:C,t_apparent_min_%sh:C,t_apparent_max_%sh:C/%s,%s/json?model=' . $model,
  533. $this->apiBaseUrl,
  534. $startDateNew,
  535. $endDateNew,
  536. $hours,
  537. $hours,
  538. $hours,
  539. $hours,
  540. $hours,
  541. $latitude,
  542. $longitude
  543. );
  544. $url2 = sprintf(
  545. '%s/%s--%s:PT%sH/wind_speed_mean_10m_%sh:kmh,wind_dir_mean_10m_%sh:d,prob_precip_%sh:p,precip_%sh:mm,relative_humidity_mean_2m_%sh:p,effective_cloud_cover_mean_%sh:octas,dew_point_mean_2m_%sh:C,wind_gusts_10m_%sh:kmh/%s,%s/json?model=' . $model,
  546. $this->apiBaseUrl,
  547. $startDateNew,
  548. $endDateNew,
  549. $hours,
  550. $hours,
  551. $hours,
  552. $hours,
  553. $hours,
  554. $hours,
  555. $hours,
  556. $hours,
  557. $hours,
  558. $latitude,
  559. $longitude
  560. );
  561. $url3 = sprintf(
  562. '%s/%s--%s:PT%sH/t_2m:C,visibility:km/%s,%s/json?model=mix',
  563. $this->apiBaseUrl,
  564. $startDate,
  565. $endDate,
  566. $hours,
  567. $latitude,
  568. $longitude
  569. );
  570. $url4 = sprintf(
  571. '%s/%s--%s:PT10M/t_2m:C/%s,%s/json?model=' . $model,
  572. $this->apiBaseUrl,
  573. $startDate,
  574. $endDate,
  575. $latitude,
  576. $longitude
  577. );
  578. $url5 = sprintf(
  579. '%s/%s--%s:PT3H/precip_3h:mm,prob_precip_3h:p/%s,%s/json?model=' . $model,
  580. $this->apiBaseUrl,
  581. $startDate,
  582. $endDate,
  583. $latitude,
  584. $longitude
  585. );
  586. $url6 = sprintf(
  587. '%s/%s--%s:PT6H/precip_6h:mm,prob_precip_6h:p/%s,%s/json?model=' . $model,
  588. $this->apiBaseUrl,
  589. $startDate,
  590. $endDate,
  591. $latitude,
  592. $longitude
  593. );
  594. }
  595. $response = $client->request('GET', $url1, [
  596. 'auth' => [$this->username, $this->password],
  597. ]);
  598. $statusCode = $response->getStatusCode();
  599. $data1 = json_decode($response->getBody(), true);
  600. // Adjust the dates in the response data
  601. $this->adjustResponseDates($data1['data'], $hours);
  602. $response = $client->request('GET', $url2, [
  603. 'auth' => [$this->username, $this->password],
  604. ]);
  605. $statusCode = $response->getStatusCode();
  606. $data2 = json_decode($response->getBody(), true);
  607. $this->adjustResponseDates($data2['data'], $hours);
  608. $response = $client->request('GET', $url3, [
  609. 'auth' => [$this->username, $this->password],
  610. ]);
  611. $statusCode = $response->getStatusCode();
  612. $data3 = json_decode($response->getBody(), true);
  613. $response = $client->request('GET', $url4, [
  614. 'auth' => [$this->username, $this->password],
  615. ]);
  616. $statusCode = $response->getStatusCode();
  617. $data4 = json_decode($response->getBody(), true);
  618. $response = $client->request('GET', $url5, [
  619. 'auth' => [$this->username, $this->password],
  620. ]);
  621. $statusCode = $response->getStatusCode();
  622. $data5 = json_decode($response->getBody(), true);
  623. $response = $client->request('GET', $url6, [
  624. 'auth' => [$this->username, $this->password],
  625. ]);
  626. $statusCode = $response->getStatusCode();
  627. $data6 = json_decode($response->getBody(), true);
  628. $dataFinal = $data1;
  629. $dataFinal['data'] = array_merge($data6['data'], $data5['data'], $data4['data'], $data1['data'], $data2['data'], $data3['data']);
  630. $weatherSymbols = $this->getWeatherSymbols($coordinates, $startDate, $endDate, 'PT' . $hours . 'H', $hours . 'h', true);
  631. if (isset($weatherSymbols['data'][0]['parameter'])) {
  632. $dataFinal['symbols'] = $weatherSymbols['data'][0];
  633. }
  634. // Set the data to Redis cache
  635. $this->redisCache->set($cacheKey, $dataFinal);
  636. return $dataFinal;
  637. } catch (GuzzleHttp\Exception\RequestException $e) {
  638. return throw new \Exception($e->getMessage());
  639. } catch (\Exception $e) {
  640. return throw new \Exception($e->getMessage());
  641. }
  642. }
  643. /**
  644. * Fetches the hourly weather forecast data for a given latitude, longitude, and hour
  645. *
  646. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  647. * @param string $hourly The hour for which forecast is required in 24-hour format
  648. * @return array The hourly weather forecast data in JSON format
  649. */
  650. public function getHourlyForecastData(array $coordinates, string $startDate, string $endDate, int $hour, string $model, $translator)
  651. {
  652. try {
  653. if (count($coordinates) > 1) {
  654. // Validate the input parameters
  655. foreach ($coordinates as $coordinate) {
  656. if (count($coordinate) < 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  657. return ["success" => false, "message" => $translator->trans("invalid_coordinates")];
  658. }
  659. }
  660. if (empty($startDate) || empty($endDate)) {
  661. // throw new \InvalidArgumentException('Invalid dates');
  662. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  663. }
  664. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  665. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  666. // Create a Redis key for the cache
  667. $cacheKey = sprintf('hourly_forecast_%s_%s_%s', implode('_', array_map(function ($coordinate) {
  668. return implode('_', $coordinate);
  669. }, $coordinates)), (($startDate) . '-' . ($endDate)) . $model, $hour);
  670. // Try to get the data from Redis cache
  671. $cachedData = $this->redisCache->get($cacheKey);
  672. if ($cachedData !== null) {
  673. // Return the cached data if available
  674. return $cachedData;
  675. }
  676. // If cache is empty, get the data from the API
  677. $client = new Client(['verify' => false]);
  678. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  679. $url1 = sprintf(
  680. '%s/%s--%s:PT%sH/t_max_2m_1h:C,t_min_2m_1h:C,t_apparent_min_1h:C,t_apparent_max_1h:C/%+%/json?model=' . $model,
  681. $this->apiBaseUrl,
  682. $startDate,
  683. $endDate,
  684. $hour,
  685. $coordinateString,
  686. $coordinateString
  687. );
  688. $url2 = sprintf(
  689. '%s/%s--%s:PT%sH/wind_speed_mean_10m_1h:kmh,wind_dir_mean_10m_1h:d,prob_precip_1h:p,precip_1h:mm,relative_humidity_mean_2m_1h:p,visibility:km,effective_cloud_cover_mean_1h:octas,dew_point_mean_2m_1h:C,wind_gusts_10m_1h:kmh/%+%/json?model=' . $model,
  690. $this->apiBaseUrl,
  691. $startDate,
  692. $endDate,
  693. $hour,
  694. $coordinateString,
  695. $coordinateString
  696. );
  697. } else {
  698. if ($coordinates) {
  699. $latitude = $coordinates[0][0];
  700. $longitude = $coordinates[0][1];
  701. }
  702. // Validate the input parameters
  703. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  704. // throw new \InvalidArgumentException('Invalid latitude');
  705. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  706. }
  707. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  708. // throw new \InvalidArgumentException('Invalid longitude');
  709. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  710. }
  711. if (empty($startDate) || empty($endDate)) {
  712. // throw new \InvalidArgumentException('Invalid dates');
  713. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  714. }
  715. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  716. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  717. // Create a Redis key for the cache
  718. $cacheKey = sprintf('hourly_forecast_%s_%s_%s', implode('_', array_map(function ($coordinate) {
  719. return implode('_', $coordinate);
  720. }, $coordinates)), ($startDate . '-' . $endDate) . $model, $hour);
  721. // Try to get the data from Redis cache
  722. $cachedData = $this->redisCache->get($cacheKey);
  723. if ($cachedData !== null) {
  724. // Return the cached data if available
  725. return $cachedData;
  726. }
  727. // If cache is empty, get the data from the API
  728. $client = new Client(['verify' => false]);
  729. $url1 = sprintf(
  730. '%s/%s--%s:PT%sH/t_max_2m_1h:C,t_min_2m_1h:C,t_apparent_min_1h:C,t_apparent_max_1h:C/%s,%s/json?model=' . $model,
  731. $this->apiBaseUrl,
  732. $startDate,
  733. $endDate,
  734. $hour,
  735. $latitude,
  736. $longitude
  737. );
  738. $url2 = sprintf(
  739. '%s/%s--%s:PT%sH/wind_speed_mean_10m_1h:kmh,wind_dir_mean_10m_1h:d,prob_precip_1h:p,precip_1h:mm,relative_humidity_mean_2m_1h:p,visibility:km,effective_cloud_cover_mean_1h:octas,dew_point_mean_2m_1h:C,wind_gusts_10m_1h:kmh/%s,%s/json?model=' . $model,
  740. $this->apiBaseUrl,
  741. $startDate,
  742. $endDate,
  743. $hour,
  744. $latitude,
  745. $longitude
  746. );
  747. }
  748. $response = $client->request('GET', $url1, [
  749. 'auth' => [$this->username, $this->password],
  750. ]);
  751. $statusCode = $response->getStatusCode();
  752. $data1 = json_decode($response->getBody(), true);
  753. $response = $client->request('GET', $url2, [
  754. 'auth' => [$this->username, $this->password],
  755. ]);
  756. $statusCode = $response->getStatusCode();
  757. $data2 = json_decode($response->getBody(), true);
  758. $data3 = $data1;
  759. $data3['data'] = array_merge($data1['data'], $data2['data']);
  760. $weatherSymbols = $this->getWeatherSymbols($coordinates, $startDate, $endDate, 'PT1H', '1h');
  761. if (isset($weatherSymbols['data'][0]['parameter'])) {
  762. $data3['symbols'] = $weatherSymbols['data'][0];
  763. }
  764. // Set the data to Redis cache
  765. $this->redisCache->set($cacheKey, $data3);
  766. return $data3;
  767. } catch (GuzzleHttp\Exception\RequestException $e) {
  768. return throw new \Exception($e->getMessage());
  769. } catch (\Exception $e) {
  770. return throw new \Exception($e->getMessage());
  771. }
  772. }
  773. /**
  774. * Fetches the hourly weather forecast data for a given latitude, longitude, and hour
  775. *
  776. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  777. * @param string $hourly The hour for which forecast is required in 24-hour format
  778. * @return array The hourly weather forecast data in JSON format
  779. */
  780. public function getForecastDataHistoryHourly(array $coordinates, string $startDate, string $endDate, $translator, int $hour = 1)
  781. {
  782. try {
  783. if (count($coordinates) > 1) {
  784. // Validate the input parameters
  785. foreach ($coordinates as $coordinate) {
  786. if (count($coordinate) < 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  787. // throw new \InvalidArgumentException('Invalid coordinates');
  788. return ["success" => false, "message" => $translator->trans("invalid_coordinates")];
  789. }
  790. }
  791. if (empty($startDate) || empty($endDate)) {
  792. // throw new \InvalidArgumentException('Invalid dates');
  793. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  794. }
  795. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  796. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  797. // Create a Redis key for the cache
  798. $cacheKey = sprintf('historical_hourly_forecast_%s_%s_%s', implode('_', array_map(function ($coordinate) {
  799. return implode('_', $coordinate);
  800. }, $coordinates)), (($startDate) . '-' . ($endDate)), $hour);
  801. // Try to get the data from Redis cache
  802. $cachedData = $this->redisCache->get($cacheKey);
  803. if ($cachedData !== null) {
  804. // Return the cached data if available
  805. return $cachedData;
  806. }
  807. // If cache is empty, get the data from the API
  808. $client = new Client(['verify' => false]);
  809. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  810. $url1 = sprintf(
  811. '%s/%s--%s:PT%sH/sfc_pressure:hPa,msl_pressure:hPa,dew_point_1000hPa:C,relative_humidity_2m:p,t_max_2m_1h:C,t_min_2m_1h:C,wind_speed_10m:kmh,wind_dir_10m:d,precip_1h:mm/%s+%s/json',
  812. $this->apiBaseUrl,
  813. $startDate,
  814. $endDate,
  815. $hour,
  816. $coordinateString,
  817. $coordinateString
  818. );
  819. $url2 = sprintf(
  820. '%s/%s--%s:PT%sH/effective_cloud_cover:octas,dew_point_2m:C,wind_speed_mean_10m_1h:ms/%s+%s/json',
  821. $this->apiBaseUrl,
  822. $startDate,
  823. $endDate,
  824. $hour,
  825. $coordinateString,
  826. $coordinateString
  827. );
  828. } else {
  829. if ($coordinates) {
  830. $latitude = $coordinates[0][0];
  831. $longitude = $coordinates[0][1];
  832. }
  833. // Validate the input parameters
  834. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  835. // throw new \InvalidArgumentException('Invalid latitude');
  836. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  837. }
  838. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  839. // throw new \InvalidArgumentException('Invalid longitude');
  840. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  841. }
  842. if (empty($startDate) || empty($endDate)) {
  843. // throw new \InvalidArgumentException('Invalid dates');
  844. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  845. }
  846. // 2023-04-06T00:00:00.000Z--2023-04-07T00:00:00.000Z
  847. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  848. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  849. // Create a Redis key for the cache
  850. $cacheKey = sprintf('historical_hourly_forecast_%s_%s_%s', implode('_', array_map(function ($coordinate) {
  851. return implode('_', $coordinate);
  852. }, $coordinates)), (($startDate) . '-' . ($endDate)), $hour);
  853. // Try to get the data from Redis cache
  854. $cachedData = $this->redisCache->get($cacheKey);
  855. if ($cachedData !== null) {
  856. // Return the cached data if available
  857. return $cachedData;
  858. }
  859. // If cache is empty, get the data from the API
  860. $client = new Client(['verify' => false]);
  861. $url1 = sprintf(
  862. '%s/%s--%s:PT%sH/sfc_pressure:hPa,msl_pressure:hPa,dew_point_1000hPa:C,relative_humidity_2m:p,t_max_2m_1h:C,t_min_2m_1h:C,wind_speed_10m:kmh,wind_dir_10m:d,precip_1h:mm/%s,%s/json',
  863. $this->apiBaseUrl,
  864. $startDate,
  865. $endDate,
  866. $hour,
  867. $latitude,
  868. $longitude
  869. );
  870. $url2 = sprintf(
  871. '%s/%s--%s:PT%sH/sfc_pressure:hPa,msl_pressure:hPa,dew_point_1000hPa:C,relative_humidity_2m:p,t_max_2m_1h:C,t_min_2m_1h:C,wind_speed_10m:kmh,wind_dir_10m:d,precip_1h:mm/%s,%s/json',
  872. $this->apiBaseUrl,
  873. $startDate,
  874. $endDate,
  875. $hour,
  876. $latitude,
  877. $longitude
  878. );
  879. //echo $url;exit;
  880. }
  881. $response = $client->request('GET', $url1, [
  882. 'auth' => [$this->username, $this->password],
  883. ]);
  884. $statusCode = $response->getStatusCode();
  885. $data1 = json_decode($response->getBody(), true);
  886. $response = $client->request('GET', $url2, [
  887. 'auth' => [$this->username, $this->password],
  888. ]);
  889. $statusCode = $response->getStatusCode();
  890. $data2 = json_decode($response->getBody(), true);
  891. array_push($data1['data'], $data2['data']);
  892. $weatherSymbols = $this->getWeatherSymbols($coordinates, $startDate, $endDate, 'PT1H', $hour . 'h');
  893. if (isset($weatherSymbols['data'][0]['parameter'])) {
  894. $data1['symbols'] = $weatherSymbols['data'][0];
  895. }
  896. // Set the data to Redis cache
  897. $this->redisCache->set($cacheKey, $data1);
  898. return $data1;
  899. } catch (GuzzleHttp\Exception\RequestException $e) {
  900. //p_r($e->getMessage());exit;
  901. return throw new \Exception($e->getMessage());
  902. } catch (\Exception $e) {
  903. //p_r($e->getMessage());exit;
  904. return throw new \Exception($e->getMessage());
  905. }
  906. }
  907. /**
  908. * Compare weather based on mateomatics models
  909. *
  910. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  911. * @param string $hourly The hour for which forecast is required in 24-hour format
  912. * @return array The hourly weather forecast data in JSON format
  913. */
  914. public function getCompareParameters(array $coordinates, string $startDate, string $endDate, string $model, string $tempParam, string $timeDuration = "P1H", $translator)
  915. {
  916. try {
  917. if (count($coordinates) > 1) {
  918. // Validate the input parameters
  919. foreach ($coordinates as $coordinate) {
  920. if (count($coordinate) < 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  921. // throw new \InvalidArgumentException('Invalid coordinates');
  922. return ["success" => false, "message" => $translator->trans("invalid_coordinates")];
  923. }
  924. }
  925. if (empty($startDate) || empty($endDate)) {
  926. // throw new \InvalidArgumentException('Invalid dates');
  927. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  928. }
  929. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  930. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  931. // Create a Redis key for the cache
  932. $cacheKey = sprintf('compare_model_%s_%s_' . $model . '_' . $tempParam, implode('_', array_map(function ($coordinate) {
  933. return implode('_', $coordinate);
  934. }, $coordinates)), (($startDate) . '-' . ($endDate)) . $timeDuration);
  935. // Try to get the data from Redis cache
  936. $cachedData = $this->redisCache->get($cacheKey);
  937. if ($cachedData !== null) {
  938. // Return the cached data if available
  939. return $cachedData;
  940. }
  941. // If cache is empty, get the data from the API
  942. $client = new Client(['verify' => false]);
  943. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  944. $url = sprintf(
  945. '%s/%s--%s:%s/%s/%+%/json?model=' . $model,
  946. $this->apiBaseUrl,
  947. $startDate,
  948. $endDate,
  949. $timeDuration,
  950. $tempParam,
  951. $coordinateString,
  952. $coordinateString
  953. );
  954. } else {
  955. if ($coordinates) {
  956. $latitude = $coordinates[0][0];
  957. $longitude = $coordinates[0][1];
  958. }
  959. // Validate the input parameters
  960. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  961. // throw new \InvalidArgumentException('Invalid latitude');
  962. return ["success" => true, "message" => $translator->trans("invalid_latitude")];
  963. }
  964. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  965. // throw new \InvalidArgumentException('Invalid longitude');
  966. return ["success" => true, "message" => $translator->trans("invalid_longitude")];
  967. }
  968. if (empty($startDate) || empty($endDate)) {
  969. // throw new \InvalidArgumentException('Invalid dates');
  970. return ["success" => true, "message" => $translator->trans("invalid_dates")];
  971. }
  972. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  973. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  974. // Create a Redis key for the cache
  975. $cacheKey = sprintf('compare_model_%s_%s_' . $model . '_' . $tempParam, implode('_', array_map(function ($coordinate) {
  976. return implode('_', $coordinate);
  977. }, $coordinates)), (($startDate) . '-' . ($endDate)) . $timeDuration);
  978. // Try to get the data from Redis cache
  979. $cachedData = $this->redisCache->get($cacheKey);
  980. if ($cachedData !== null) {
  981. // Return the cached data if available
  982. return $cachedData;
  983. }
  984. // If cache is empty, get the data from the API
  985. $client = new Client(['verify' => false]);
  986. $url = sprintf(
  987. '%s/%s--%s:%s/%s/%s,%s/json?model=' . $model,
  988. $this->apiBaseUrl,
  989. $startDate,
  990. $endDate,
  991. $timeDuration,
  992. $tempParam,
  993. $latitude,
  994. $longitude
  995. );
  996. }
  997. $response = $client->request('GET', $url, [
  998. 'auth' => [$this->username, $this->password],
  999. ]);
  1000. $statusCode = $response->getStatusCode();
  1001. $data = json_decode($response->getBody(), true);
  1002. // Set the data to Redis cache
  1003. $this->redisCache->set($cacheKey, $data);
  1004. return $data;
  1005. } catch (GuzzleHttp\Exception\RequestException $e) {
  1006. return throw new \Exception($e->getMessage());
  1007. } catch (\Exception $e) {
  1008. return throw new \Exception($e->getMessage());
  1009. }
  1010. }
  1011. /**
  1012. * Get forecast data in daily dashboard display for multiple locations
  1013. *
  1014. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  1015. * @param int $days The number of days for which forecast is required
  1016. * @return array The model forecast data
  1017. */
  1018. public function getDashboardDailyForecast(array $coordinates, string $startDate, string $endDate)
  1019. {
  1020. try {
  1021. if (count($coordinates) > 1) {
  1022. // Validate the input parameters
  1023. foreach ($coordinates as $coordinate) {
  1024. if (count($coordinate) < 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  1025. throw new \InvalidArgumentException('Invalid coordinates');
  1026. }
  1027. }
  1028. if (empty($startDate) || empty($endDate)) {
  1029. throw new \InvalidArgumentException('Invalid dates');
  1030. }
  1031. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  1032. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  1033. // Create a Redis key for the cache
  1034. $cacheKey = sprintf('dashboard_%s_%s', implode('_', array_map(function ($coordinate) {
  1035. return implode('_', $coordinate);
  1036. }, $coordinates)), (($startDate) . '-' . ($endDate)));
  1037. // Try to get the data from Redis cache
  1038. $cachedData = $this->redisCache->get($cacheKey);
  1039. if ($cachedData !== null) {
  1040. // Return the cached data if available
  1041. return $cachedData;
  1042. }
  1043. // If cache is empty, get the data from the API
  1044. $client = new Client(['verify' => false]);
  1045. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  1046. $url = sprintf(
  1047. '%s/%s--%s:P%sD/t_max_2m_24h:C,t_apparent:C,wind_speed_10m:kmh,wind_dir_mean_10m_24h:d,prob_precip_24h:p,precip_24h:mm,sunrise:sql,visibility:km/%+%/json',
  1048. $this->apiBaseUrl,
  1049. $endDate,
  1050. $startDate,
  1051. 1,
  1052. $coordinateString,
  1053. $coordinateString
  1054. );
  1055. } else {
  1056. if ($coordinates) {
  1057. $latitude = $coordinates[0][0];
  1058. $longitude = $coordinates[0][1];
  1059. }
  1060. // Validate the input parameters
  1061. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  1062. throw new \InvalidArgumentException('Invalid latitude');
  1063. }
  1064. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  1065. throw new \InvalidArgumentException('Invalid longitude');
  1066. }
  1067. if (empty($startDate) || empty($endDate)) {
  1068. throw new \InvalidArgumentException('Invalid dates');
  1069. }
  1070. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  1071. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  1072. // Create a Redis key for the cache
  1073. $cacheKey = sprintf('dashboard_%s_%s', implode('_', array_map(function ($coordinate) {
  1074. return implode('_', $coordinate);
  1075. }, $coordinates)), (($startDate) . '-' . ($endDate)));
  1076. // Try to get the data from Redis cache
  1077. $cachedData = $this->redisCache->get($cacheKey);
  1078. if ($cachedData !== null) {
  1079. // Return the cached data if available
  1080. return $cachedData;
  1081. }
  1082. // If cache is empty, get the data from the API
  1083. $client = new Client(['verify' => false]);
  1084. $url = sprintf(
  1085. '%s/%s--%s:P%sD/t_max_2m_24h:C,t_apparent:C,wind_speed_10m:kmh,wind_dir_mean_10m_24h:d,prob_precip_24h:p,precip_24h:mm,sunrise:sql,visibility:km/%s,%s/json',
  1086. $this->apiBaseUrl,
  1087. $endDate,
  1088. $startDate,
  1089. 1,
  1090. $latitude,
  1091. $longitude
  1092. );
  1093. }
  1094. $response = $client->request('GET', $url, [
  1095. 'auth' => [$this->username, $this->password],
  1096. ]);
  1097. $statusCode = $response->getStatusCode();
  1098. $data = json_decode($response->getBody(), true);
  1099. // Set the data to Redis cache
  1100. $this->redisCache->set($cacheKey, $data);
  1101. return $data;
  1102. } catch (GuzzleHttp\Exception\RequestException $e) {
  1103. return throw new \Exception($e->getMessage());
  1104. } catch (\Exception $e) {
  1105. return throw new \Exception($e->getMessage());
  1106. }
  1107. }
  1108. /**
  1109. * Get forecast data for the provided coordinates
  1110. *
  1111. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  1112. * @param string $timestamp The timestamp for which forecast is required in the format "YYYY-MM-DDTHHZ"
  1113. * @param string $duration The duration for which forecast is required in ISO 8601 format "P1D" for 1 day, "PT1H" for 1 hour
  1114. * @param string $parameters The weather parameters to fetch separated by comma, e.g. "t_2m:C,sfc_pressure:hPa,wind_speed_10m:ms"
  1115. * @param string $aggregations The aggregations to apply to the fetched parameters separated by comma, e.g. "mean,sum"
  1116. * @param string $format The format in which to receive the data, either "json" or "xml"
  1117. *
  1118. * @return array The forecast data for the provided coordinates
  1119. */
  1120. public function getForecastForCoordinates(
  1121. array $coordinates,
  1122. string $timestamp,
  1123. string $duration,
  1124. string $parameters,
  1125. string $aggregations,
  1126. string $format
  1127. ) {
  1128. try {
  1129. // Validate the input parameters
  1130. foreach ($coordinates as $coordinate) {
  1131. if (!is_array($coordinate) || count($coordinate) !== 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  1132. throw new \InvalidArgumentException('Invalid coordinates');
  1133. }
  1134. }
  1135. if (!preg_match('/^\d{4}-\d{2}-\d{2}T\d{2}Z$/', $timestamp)) {
  1136. throw new \InvalidArgumentException('Invalid timestamp');
  1137. }
  1138. if (!in_array($duration, ['PT1H', 'P1D'])) {
  1139. throw new \InvalidArgumentException('Invalid duration');
  1140. }
  1141. if (!is_string($parameters) || empty($parameters)) {
  1142. throw new \InvalidArgumentException('Invalid parameters');
  1143. }
  1144. if (!is_string($aggregations) || empty($aggregations)) {
  1145. throw new \InvalidArgumentException('Invalid aggregations');
  1146. }
  1147. if (!in_array($format, ['json', 'xml'])) {
  1148. throw new \InvalidArgumentException('Invalid format');
  1149. }
  1150. // Convert the coordinates array into a string
  1151. $coordinatesString = implode('_', array_map(function ($coordinate) {
  1152. return implode(',', $coordinate);
  1153. }, $coordinates));
  1154. // Build the URL for the API call
  1155. $url = sprintf(
  1156. '%s/%s/%s/%s/%s/%s/%s.%s',
  1157. $this->apiBaseUrl,
  1158. $timestamp,
  1159. $duration,
  1160. $parameters,
  1161. $coordinatesString,
  1162. $aggregations,
  1163. $format
  1164. );
  1165. // Make the API call
  1166. $client = new Client(['verify' => false]);
  1167. $response = $client->request('GET', $url, [
  1168. 'auth' => [$this->username, $this->password],
  1169. ]);
  1170. $statusCode = $response->getStatusCode();
  1171. $data = json_decode($response->getBody(), true);
  1172. return $data;
  1173. } catch (GuzzleHttp\Exception\RequestException $e) {
  1174. return throw new \Exception($e->getMessage());
  1175. } catch (\Exception $e) {
  1176. return throw new \Exception($e->getMessage());
  1177. }
  1178. }
  1179. /**
  1180. * Get weather symbols
  1181. *
  1182. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  1183. * @param int $days The number of days for which forecast is required
  1184. * @return array The model forecast data
  1185. */
  1186. public function getWeatherSymbols(array $coordinates, string $startDate, string $endDate, string $weatherFirstParam = 'PT1H', string $weatherSecondParam = '1h', $adjustDate = false)
  1187. {
  1188. try {
  1189. if ($coordinates) {
  1190. $latitude = $coordinates[0][0];
  1191. $longitude = $coordinates[0][1];
  1192. }
  1193. // Validate the input parameters
  1194. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  1195. throw new \InvalidArgumentException('Invalid latitude');
  1196. }
  1197. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  1198. throw new \InvalidArgumentException('Invalid longitude');
  1199. }
  1200. if (empty($startDate) || empty($endDate)) {
  1201. throw new \InvalidArgumentException('Invalid dates');
  1202. }
  1203. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  1204. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  1205. if ($adjustDate) {
  1206. // Adjust the date range based on the hours parameter
  1207. if ($weatherSecondParam == '24h') {
  1208. $startDate = date('Y-m-d\TH:i:s.v\Z', strtotime($startDate . ' +1 day'));
  1209. $endDate = date('Y-m-d\TH:i:s.v\Z', strtotime($endDate . ' +1 day'));
  1210. } elseif ($weatherSecondParam == '1h') {
  1211. $startDate = date('Y-m-d\TH:i:s.v\Z', strtotime($startDate . ' +1 hour'));
  1212. $endDate = date('Y-m-d\TH:i:s.v\Z', strtotime($endDate . ' +1 hour'));
  1213. } elseif ($weatherSecondParam == '12h') {
  1214. $startDate = date('Y-m-d\TH:i:s.v\Z', strtotime($startDate . ' +12 hour'));
  1215. $endDate = date('Y-m-d\TH:i:s.v\Z', strtotime($endDate . ' +12 hour'));
  1216. }
  1217. }
  1218. // Create a Redis key for the cache
  1219. $cacheKey = sprintf('get_symbols_%s_%s', implode('_', array_map(function ($coordinate) {
  1220. return implode('_', $coordinate);
  1221. }, $coordinates)), (($startDate) . '-' . ($endDate)) . $weatherFirstParam);
  1222. // Try to get the data from Redis cache
  1223. $cachedData = $this->redisCache->get($cacheKey);
  1224. if ($cachedData !== null) {
  1225. // Return the cached data if available
  1226. return $cachedData;
  1227. }
  1228. // If cache is empty, get the data from the API
  1229. $client = new Client(['verify' => false]);
  1230. $url = sprintf(
  1231. '%s/%s--%s:%s/weather_symbol_%s:idx/%s,%s/json',
  1232. $this->apiBaseUrl,
  1233. $startDate,
  1234. $endDate,
  1235. $weatherFirstParam,
  1236. $weatherSecondParam,
  1237. $latitude,
  1238. $longitude
  1239. );
  1240. $response = $client->request('GET', $url, [
  1241. 'auth' => [$this->username, $this->password],
  1242. ]);
  1243. $statusCode = $response->getStatusCode();
  1244. $data = json_decode($response->getBody(), true);
  1245. if ($adjustDate) {
  1246. // Define your adjustment ('-1 day' or '-1 hour' or '-12 hour')
  1247. if ($weatherSecondParam == '24h') {
  1248. $timeAdjustment = 24;
  1249. } elseif ($weatherSecondParam == '1h') {
  1250. $timeAdjustment = 1;
  1251. } elseif ($weatherSecondParam == '12h') {
  1252. $timeAdjustment = 12;
  1253. }
  1254. // Adjust the dates in the response data
  1255. $this->adjustResponseDates($data['data'], $timeAdjustment);
  1256. }
  1257. // Set the data to Redis cache
  1258. $this->redisCache->set($cacheKey, $data);
  1259. return $data;
  1260. } catch (GuzzleHttp\Exception\RequestException $e) {
  1261. return throw new \Exception($e->getMessage());
  1262. } catch (\Exception $e) {
  1263. return throw new \Exception($e->getMessage());
  1264. }
  1265. }
  1266. /**
  1267. * Weather Map
  1268. *
  1269. * @param string $version The version of the API (e.g., '1.3.0')
  1270. * @param string $request The type of request (e.g., 'GetMap')
  1271. * @param string $layers The layers to include in the map
  1272. * @param string $crs The coordinate reference system (e.g., 'EPSG:3857')
  1273. * @param string $bBox The bounding box in the format 'minX,minY,maxX,maxY'
  1274. * @param string $format The format of the map image (e.g., 'image/png')
  1275. * @param int $width The width of the map image
  1276. * @param int $height The height of the map image
  1277. * @param bool $tiled Whether to use tiled rendering (default: true)
  1278. * @return array The model forecast data
  1279. * @throws \InvalidArgumentException If any parameter has an invalid data type
  1280. */
  1281. public function getWeatherMap(
  1282. string $version,
  1283. string $request,
  1284. string $layers,
  1285. string $crs,
  1286. string $bBox,
  1287. string $format,
  1288. int $width,
  1289. int $height,
  1290. bool $tiled = true
  1291. ) {
  1292. // Validate data types of input parameters
  1293. if (!is_string($version)) {
  1294. throw new \InvalidArgumentException('Invalid data type for $version. Expected string.');
  1295. }
  1296. if (!is_string($request)) {
  1297. throw new \InvalidArgumentException('Invalid data type for $request. Expected string.');
  1298. }
  1299. if (!is_string($layers)) {
  1300. throw new \InvalidArgumentException('Invalid data type for $layers. Expected string.');
  1301. }
  1302. if (!is_string($crs)) {
  1303. throw new \InvalidArgumentException('Invalid data type for $crs. Expected string.');
  1304. }
  1305. if (!is_string($bBox)) {
  1306. throw new \InvalidArgumentException('Invalid data type for $bBox. Expected string.');
  1307. }
  1308. if (!is_string($format)) {
  1309. throw new \InvalidArgumentException('Invalid data type for $format. Expected string.');
  1310. }
  1311. if (!is_int($width)) {
  1312. throw new \InvalidArgumentException('Invalid data type for $width. Expected int.');
  1313. }
  1314. if (!is_int($height)) {
  1315. throw new \InvalidArgumentException('Invalid data type for $height. Expected int.');
  1316. }
  1317. if (!is_bool($tiled)) {
  1318. throw new \InvalidArgumentException('Invalid data type for $tiled. Expected bool.');
  1319. }
  1320. try {
  1321. // If cache is empty, get the data from the API
  1322. $client = new Client(['verify' => false]);
  1323. $url = sprintf(
  1324. '%s/wms?VERSION=%s&REQUEST=%s&LAYERS=%s&CRS=%s&BBOX=%s&FORMAT=%s&WIDTH=%s&HEIGHT=%s&TILED=%s',
  1325. $this->apiBaseUrl,
  1326. $version,
  1327. $request,
  1328. $layers,
  1329. $crs,
  1330. $bBox,
  1331. $format,
  1332. $width,
  1333. $height,
  1334. $tiled
  1335. );
  1336. $response = $client->request('GET', $url, [
  1337. 'auth' => [$this->username, $this->password],
  1338. ]);
  1339. return $response->getBody();
  1340. } catch (GuzzleHttp\Exception\RequestException $e) {
  1341. return throw new \Exception($e->getMessage());
  1342. } catch (\Exception $e) {
  1343. return throw new \Exception($e->getMessage());
  1344. }
  1345. }
  1346. /**
  1347. * Weather Warnings
  1348. *
  1349. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  1350. * @param string $startDate The start date for the forecast
  1351. * @param string $endDate The end date for the forecast
  1352. * @param string $duration The duration for the forecast (default: '24')
  1353. * @param string $warningParam The type of weather warning parameter (default: 'frost_warning')
  1354. * @param string $format The format of the forecast data (default: 'json')
  1355. * @return array The model forecast data
  1356. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  1357. */
  1358. public function getWeatherWarnings(array $coordinates, string $startDate, string $endDate, string $duration = '24', string $warningParam = 'frost_warning', string $format = "json", $translator)
  1359. {
  1360. try {
  1361. // Validate the input parameters
  1362. if (empty($coordinates)) {
  1363. // throw new \InvalidArgumentException('Invalid coordinates');
  1364. return ["success" => false, "message" => $translator->trans("invalid_coordinates")];
  1365. }
  1366. if (!is_array($coordinates) || count($coordinates) < 1) {
  1367. // throw new \InvalidArgumentException('Coordinates should be a non-empty array');
  1368. return ["success" => false, "message" => $translator->trans("coordinates_should_be_a_non_empty_array")];
  1369. }
  1370. $latitude = $coordinates[0][0] ?? null;
  1371. $longitude = $coordinates[0][1] ?? null;
  1372. if (!is_numeric($latitude) || $latitude < -90 || $latitude > 90) {
  1373. // throw new \InvalidArgumentException('Invalid latitude');
  1374. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  1375. }
  1376. if (!is_numeric($longitude) || $longitude < -180 || $longitude > 180) {
  1377. // throw new \InvalidArgumentException('Invalid longitude');
  1378. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  1379. }
  1380. if (empty($startDate) || empty($endDate)) {
  1381. // throw new \InvalidArgumentException('Invalid dates');
  1382. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  1383. }
  1384. if (!is_numeric($duration)) {
  1385. // throw new \InvalidArgumentException('Duration should be numeric');
  1386. return ["success" => false, "message" => $translator->trans("duration_should_be _numeric")];
  1387. }
  1388. $weatherWarning = new \Pimcore\Model\DataObject\MMWarningConfig\Listing();
  1389. $weatherWarning->setCondition("WarningKey = ? AND (hourEnd >= ? AND hourStart <= ?)", [$warningParam, $duration, $duration]);
  1390. $weatherWarning = $weatherWarning->load();
  1391. if (!$weatherWarning) {
  1392. throw new \Exception('Weather configuration is missing for key.' . $warningParam);
  1393. }
  1394. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  1395. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  1396. // Create a Redis key for the cache
  1397. $cacheKey = sprintf('weather_warning_%s_%s_%s', $warningParam . $format, implode('_', array_map(function ($coordinate) {
  1398. return implode('_', $coordinate);
  1399. }, $coordinates)), (($startDate) . '-' . ($endDate)));
  1400. // Try to get the data from Redis cache
  1401. $cachedData = $this->redisCache->get($cacheKey);
  1402. if ($cachedData !== null) {
  1403. // Return the cached data if available
  1404. return $cachedData;
  1405. }
  1406. // If cache is empty, get the data from the API
  1407. $client = new Client(['verify' => false]);
  1408. $url = sprintf(
  1409. '%s/%s--%s:PT%sH//%s_%sh:idx/%s,%s/json',
  1410. $this->apiBaseUrl,
  1411. $startDate,
  1412. $endDate,
  1413. $duration,
  1414. $warningParam,
  1415. $duration,
  1416. $latitude,
  1417. $longitude
  1418. );
  1419. $response = $client->request('GET', $url, [
  1420. 'auth' => [$this->username, $this->password],
  1421. ]);
  1422. $statusCode = $response->getStatusCode();
  1423. $data = json_decode($response->getBody(), true);
  1424. $modifiedParams = [];
  1425. if (isset($data['data'][0]['coordinates'][0]['dates'])) {
  1426. foreach ($data['data'][0]['coordinates'][0]['dates'] as $weather) {
  1427. $data = \App\Lib\Utility::getWeatherTypeDescription($weatherWarning[0]->getParams(), $weather);
  1428. $modifiedParams[] = $data;
  1429. }
  1430. }
  1431. $data['data'] = $modifiedParams;
  1432. // Set the data to Redis cache
  1433. $this->redisCache->set($cacheKey, $data);
  1434. return $data;
  1435. } catch (GuzzleHttp\Exception\RequestException $e) {
  1436. return throw new \Exception($e->getMessage());
  1437. } catch (\Exception $e) {
  1438. return throw new \Exception($e->getMessage());
  1439. }
  1440. }
  1441. /**
  1442. * Weather Warnings
  1443. *
  1444. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  1445. * @param string $startDate The start date for the forecast
  1446. * @param string $endDate The end date for the forecast
  1447. * @param string $duration The duration for the forecast (default: '24')
  1448. * @param string $warningParam The type of weather warning parameter (default: 'frost_warning')
  1449. * @param string $format The format of the forecast data (default: 'json')
  1450. * @return array The model forecast data
  1451. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  1452. */
  1453. public function getPrecipitationType(array $coordinates, string $startDate, string $endDate, string $duration = '24', string $format = "json", $translator)
  1454. {
  1455. try {
  1456. // Validate the input parameters
  1457. if (empty($coordinates)) {
  1458. // throw new \InvalidArgumentException('Invalid coordinates');
  1459. return ["success" => false, "message" => $translator->trans("invalid_coordinates")];
  1460. }
  1461. if (!is_array($coordinates) || count($coordinates) < 1) {
  1462. // throw new \InvalidArgumentException('Coordinates should be a non-empty array');
  1463. return ["success" => false, "message" => $translator->trans("coordinates_should_be_a_non_empty_array")];
  1464. }
  1465. $latitude = $coordinates[0][0] ?? null;
  1466. $longitude = $coordinates[0][1] ?? null;
  1467. if (!is_numeric($latitude) || $latitude < -90 || $latitude > 90) {
  1468. // throw new \InvalidArgumentException('Invalid latitude');
  1469. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  1470. }
  1471. if (!is_numeric($longitude) || $longitude < -180 || $longitude > 180) {
  1472. // throw new \InvalidArgumentException('Invalid longitude');
  1473. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  1474. }
  1475. if (empty($startDate) || empty($endDate)) {
  1476. // throw new \InvalidArgumentException('Invalid dates');
  1477. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  1478. }
  1479. if (!is_numeric($duration)) {
  1480. // throw new \InvalidArgumentException('Duration should be numeric');
  1481. return ["success" => false, "message" => $translator->trans("duration_should_be_numeric")];
  1482. }
  1483. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  1484. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  1485. // Create a Redis key for the cache
  1486. $cacheKey = sprintf('weather_precitipitation_%s_%s', implode('_', array_map(function ($coordinate) {
  1487. return implode('_', $coordinate);
  1488. }, $coordinates)), (($startDate) . '-' . ($endDate)));
  1489. // Try to get the data from Redis cache
  1490. $cachedData = $this->redisCache->get($cacheKey);
  1491. if ($cachedData !== null) {
  1492. // Return the cached data if available
  1493. // return $cachedData;
  1494. }
  1495. // If cache is empty, get the data from the API
  1496. $client = new Client(['verify' => false]);
  1497. $url = sprintf(
  1498. '%s/%s--%s:PT%sM/precip_type:idx/%s,%s/json',
  1499. $this->apiBaseUrl,
  1500. $startDate,
  1501. $endDate,
  1502. $duration,
  1503. $duration,
  1504. $latitude,
  1505. $longitude
  1506. );
  1507. $response = $client->request('GET', $url, [
  1508. 'auth' => [$this->username, $this->password],
  1509. ]);
  1510. $statusCode = $response->getStatusCode();
  1511. $data = json_decode($response->getBody(), true);
  1512. $modifiedParams = [];
  1513. if (isset($data['data'][0]['coordinates'][0]['dates'])) {
  1514. foreach ($data['data'][0]['coordinates'][0]['dates'] as $prepData) {
  1515. $data = \App\Lib\Utility::getPrecipitationTypeDescription($prepData);
  1516. $modifiedParams[] = $data;
  1517. }
  1518. } else {
  1519. throw new \Exception("Data not available");
  1520. }
  1521. $data['data'][0]['coordinates'][0]['dates'] = $modifiedParams;
  1522. // Set the data to Redis cache
  1523. $this->redisCache->set($cacheKey, $data);
  1524. return $data;
  1525. } catch (GuzzleHttp\Exception\RequestException $e) {
  1526. return throw new \Exception($e->getMessage());
  1527. } catch (\Exception $e) {
  1528. return throw new \Exception($e->getMessage());
  1529. }
  1530. }
  1531. /**
  1532. * Weather Warnings
  1533. *
  1534. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  1535. * @param string $startDate The start date for the forecast
  1536. * @param string $endDate The end date for the forecast
  1537. * @param string $duration The duration for the forecast (default: '24')
  1538. * @param string $intervalType type of interval (default: 'min')
  1539. * @param string $format The format of the forecast data (default: 'json')
  1540. * @return array The model forecast data
  1541. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  1542. */
  1543. public function getHailIndex(array $coordinates, string $startDate, string $endDate, string $duration = '24', string $intervalType = "min", string $format = "json", $translator)
  1544. {
  1545. try {
  1546. // Validate the input parameters
  1547. if (count($coordinates) > 1) {
  1548. // Validate the input parameters
  1549. foreach ($coordinates as $coordinate) {
  1550. if (count($coordinate) < 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  1551. throw new \InvalidArgumentException('Invalid coordinates');
  1552. }
  1553. }
  1554. if (empty($startDate) || empty($endDate)) {
  1555. // throw new \InvalidArgumentException('Invalid dates');
  1556. return ["success" => false, "message" => $translator->trans("duration_dates")];
  1557. }
  1558. if (!is_numeric($duration)) {
  1559. // throw new \InvalidArgumentException('Duration should be numeric');
  1560. return ["success" => false, "message" => $translator->trans("duration_should_be_numeric")];
  1561. }
  1562. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  1563. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  1564. // Create a Redis key for the cache
  1565. $cacheKey = sprintf('weather_hail_%s_%s', implode('_', array_map(function ($coordinate) {
  1566. return implode('_', $coordinate);
  1567. }, $coordinates)), (($startDate) . '-' . ($endDate) . $duration . $intervalType));
  1568. // Try to get the data from Redis cache
  1569. $cachedData = $this->redisCache->get($cacheKey);
  1570. if ($cachedData !== null) {
  1571. // Return the cached data if available
  1572. return $cachedData;
  1573. }
  1574. // If cache is empty, get the data from the API
  1575. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  1576. //10min, 20min, 30min, 1h, 3h, 6h, 12h, 24h
  1577. // If cache is empty, get the data from the API
  1578. if ($intervalType == "min") {
  1579. $url = sprintf(
  1580. '%s/%s--%s:PT%sM/hail_%smin:cm/%s/json',
  1581. $this->apiBaseUrl,
  1582. $startDate,
  1583. $endDate,
  1584. $duration,
  1585. $duration,
  1586. $coordinateString
  1587. );
  1588. } else if ($intervalType == "hour") {
  1589. $url = sprintf(
  1590. '%s/%s--%s:PT%sH/hail_%sh:cm/%s/json',
  1591. $this->apiBaseUrl,
  1592. $startDate,
  1593. $endDate,
  1594. $duration,
  1595. $duration,
  1596. $coordinateString
  1597. );
  1598. }
  1599. $client = new Client(['verify' => false]);
  1600. $response = $client->request('GET', $url, [
  1601. 'auth' => [$this->username, $this->password],
  1602. ]);
  1603. $statusCode = $response->getStatusCode();
  1604. $data = json_decode($response->getBody(), true);
  1605. // Set the data to Redis cache
  1606. $this->redisCache->set($cacheKey, $data);
  1607. return $data;
  1608. } else {
  1609. $latitude = $coordinates[0][0] ?? null;
  1610. $longitude = $coordinates[0][1] ?? null;
  1611. if (!is_numeric($latitude) || $latitude < -90 || $latitude > 90) {
  1612. // throw new \InvalidArgumentException('Invalid latitude');
  1613. return ["success" => false, "message" => $translator->trans("duration_latitude")];
  1614. }
  1615. if (!is_numeric($longitude) || $longitude < -180 || $longitude > 180) {
  1616. // throw new \InvalidArgumentException('Invalid longitude');
  1617. return ["success" => false, "message" => $translator->trans("duration_longitude")];
  1618. }
  1619. if (empty($startDate) || empty($endDate)) {
  1620. // throw new \InvalidArgumentException('Invalid dates');
  1621. return ["success" => false, "message" => $translator->trans("duration_dates")];
  1622. }
  1623. if (!is_numeric($duration)) {
  1624. // throw new \InvalidArgumentException('Duration should be numeric');
  1625. return ["success" => false, "message" => $translator->trans("duration_should_be_numeric")];
  1626. }
  1627. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  1628. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  1629. // Create a Redis key for the cache
  1630. $cacheKey = sprintf('weather_hail_%s_%s', implode('_', array_map(function ($coordinate) {
  1631. return implode('_', $coordinate);
  1632. }, $coordinates)), (($startDate) . '-' . ($endDate) . $duration . $intervalType));
  1633. // Try to get the data from Redis cache
  1634. $cachedData = $this->redisCache->get($cacheKey);
  1635. if ($cachedData !== null) {
  1636. // Return the cached data if available
  1637. return $cachedData;
  1638. }
  1639. //10min, 20min, 30min, 1h, 3h, 6h, 12h, 24h
  1640. // If cache is empty, get the data from the API
  1641. if ($intervalType == "min") {
  1642. $url = sprintf(
  1643. '%s/%s--%s:PT%sM/hail_%smin:cm/%s,%s/json',
  1644. $this->apiBaseUrl,
  1645. $startDate,
  1646. $endDate,
  1647. $duration,
  1648. $duration,
  1649. $latitude,
  1650. $longitude
  1651. );
  1652. } else if ($intervalType == "hour") {
  1653. $url = sprintf(
  1654. '%s/%s--%s:PT%sH/hail_%sh:cm/%s,%s/json',
  1655. $this->apiBaseUrl,
  1656. $startDate,
  1657. $endDate,
  1658. $duration,
  1659. $duration,
  1660. $latitude,
  1661. $longitude
  1662. );
  1663. }
  1664. $client = new Client(['verify' => false]);
  1665. $response = $client->request('GET', $url, [
  1666. 'auth' => [$this->username, $this->password],
  1667. ]);
  1668. $statusCode = $response->getStatusCode();
  1669. $data = json_decode($response->getBody(), true);
  1670. // Set the data to Redis cache
  1671. $this->redisCache->set($cacheKey, $data);
  1672. return $data;
  1673. }
  1674. } catch (GuzzleHttp\Exception\RequestException $e) {
  1675. return throw new \Exception($e->getMessage());
  1676. } catch (\Exception $e) {
  1677. return throw new \Exception($e->getMessage());
  1678. }
  1679. }
  1680. /**
  1681. * Temprature
  1682. *
  1683. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  1684. * @param string $startDate The start date for the forecast
  1685. * @param string $endDate The end date for the forecast
  1686. * @param string $duration The duration for the forecast (default: '24')
  1687. * @param string $intervalType type of interval (default: 'hour')
  1688. * @param string $format The format of the forecast data (default: 'json')
  1689. * @return array The model forecast data
  1690. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  1691. */
  1692. //https://api.meteomatics.com/2023-07-11T00:00:00Z--2023-07-14T00:00:00Z:PT1H/t_2m:C/48.8566,2.3522/json
  1693. public function getTemprature(array $coordinates, string $startDate, string $endDate, string $duration = '24', string $intervalType = "hour", string $format = "json", $translator)
  1694. {
  1695. try {
  1696. // Validate the input parameters
  1697. if (empty($coordinates)) {
  1698. // throw new \InvalidArgumentException('Invalid coordinates');
  1699. return ["success" => false, "message" => $translator->trans("invalid_coordinate")];
  1700. }
  1701. if (!is_array($coordinates) || count($coordinates) < 1) {
  1702. // throw new \InvalidArgumentException('Coordinates should be a non-empty array');
  1703. return ["success" => false, "message" => $translator->trans("coordinates_should_be_a_non_empty_array")];
  1704. }
  1705. $latitude = $coordinates[0][0] ?? null;
  1706. $longitude = $coordinates[0][1] ?? null;
  1707. if (!is_numeric($latitude) || $latitude < -90 || $latitude > 90) {
  1708. // throw new \InvalidArgumentException('Invalid latitude');
  1709. return ["success" => false, "message" => $translator->trans("invalid_coordinate")];
  1710. }
  1711. if (!is_numeric($longitude) || $longitude < -180 || $longitude > 180) {
  1712. // throw new \InvalidArgumentException('Invalid longitude');
  1713. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  1714. }
  1715. if (empty($startDate) || empty($endDate)) {
  1716. // throw new \InvalidArgumentException('Invalid dates');
  1717. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  1718. }
  1719. if (!is_numeric($duration)) {
  1720. // throw new \InvalidArgumentException('Duration should be numeric');
  1721. return ["success" => false, "message" => $translator->trans("duration_should_be_numeric")];
  1722. }
  1723. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  1724. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  1725. // Create a Redis key for the cache
  1726. $cacheKey = sprintf('temprature_%s_%s', implode('_', array_map(function ($coordinate) {
  1727. return implode('_', $coordinate);
  1728. }, $coordinates)), (($startDate) . '-' . ($endDate) . $duration . $intervalType));
  1729. // Try to get the data from Redis cache
  1730. $cachedData = $this->redisCache->get($cacheKey);
  1731. if ($cachedData !== null) {
  1732. // Return the cached data if available
  1733. return $cachedData;
  1734. }
  1735. // If cache is empty, get the data from the API
  1736. if ($intervalType == "hour") {
  1737. $url = sprintf(
  1738. '%s/%s--%s:PT%sM/t_2m:C/%s,%s/json',
  1739. $this->apiBaseUrl,
  1740. $startDate,
  1741. $endDate,
  1742. $duration,
  1743. $latitude,
  1744. $longitude
  1745. );
  1746. } else if ($intervalType == "day") {
  1747. $url = sprintf(
  1748. '%s/%s--%s:P%sD/t_2m:C/%s,%s/json',
  1749. $this->apiBaseUrl,
  1750. $startDate,
  1751. $endDate,
  1752. $duration,
  1753. $latitude,
  1754. $longitude
  1755. );
  1756. }
  1757. $client = new Client(['verify' => false]);
  1758. $response = $client->request('GET', $url, [
  1759. 'auth' => [$this->username, $this->password],
  1760. ]);
  1761. $statusCode = $response->getStatusCode();
  1762. $data = json_decode($response->getBody(), true);
  1763. // Set the data to Redis cache
  1764. $this->redisCache->set($cacheKey, $data);
  1765. return $data;
  1766. } catch (GuzzleHttp\Exception\RequestException $e) {
  1767. return throw new \Exception($e->getMessage());
  1768. } catch (\Exception $e) {
  1769. return throw new \Exception($e->getMessage());
  1770. }
  1771. }
  1772. /**
  1773. * Precipitation Probability
  1774. *
  1775. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  1776. * @param string $startDate The start date for the forecast
  1777. * @param string $endDate The end date for the forecast
  1778. * @param string $duration The duration for the forecast (default: '24')
  1779. * @param string $intervalType type of interval (default: 'hour')
  1780. * @param string $format The format of the forecast data (default: 'json')
  1781. * @return array The model forecast data
  1782. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  1783. */
  1784. //https://api.meteomatics.com/2023-07-11T00:00:00Z--2023-07-14T00:00:00Z:PT1H/t_2m:C/48.8566,2.3522/json
  1785. public function getPrecipitationProbability(array $coordinates, string $startDate, string $endDate, string $duration = '24', string $format = "json", $translator)
  1786. {
  1787. try {
  1788. // Validate the input parameters
  1789. if (empty($coordinates)) {
  1790. return ["success" => false, "message" => $translator->trans("invalid_coordinate")];
  1791. }
  1792. if (!is_array($coordinates) || count($coordinates) < 1) {
  1793. return ["success" => false, "message" => $translator->trans("coordinates_should_be_a_non_empty_array")];
  1794. }
  1795. $latitude = $coordinates[0][0] ?? null;
  1796. $longitude = $coordinates[0][1] ?? null;
  1797. if (!is_numeric($latitude) || $latitude < -90 || $latitude > 90) {
  1798. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  1799. }
  1800. if (!is_numeric($longitude) || $longitude < -180 || $longitude > 180) {
  1801. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  1802. }
  1803. if (empty($startDate) || empty($endDate)) {
  1804. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  1805. }
  1806. if (!is_numeric($duration)) {
  1807. // throw new \InvalidArgumentException('Duration should be numeric');
  1808. return ["success" => false, "message" => $translator->trans("duration_should_be_numeric")];
  1809. }
  1810. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  1811. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  1812. // Create a Redis key for the cache
  1813. $cacheKey = sprintf('precep_prob_%s_%s', implode('_', array_map(function ($coordinate) {
  1814. return implode('_', $coordinate);
  1815. }, $coordinates)), (($startDate) . '-' . ($endDate) . $duration));
  1816. // Try to get the data from Redis cache
  1817. $cachedData = $this->redisCache->get($cacheKey);
  1818. if ($cachedData !== null) {
  1819. // Return the cached data if available
  1820. return $cachedData;
  1821. }
  1822. // If cache is empty, get the data from the API
  1823. $url = sprintf(
  1824. '%s/%s--%s:PT%sH/prob_precip_%sh:p/%s,%s/json',
  1825. $this->apiBaseUrl,
  1826. $startDate,
  1827. $endDate,
  1828. $duration,
  1829. $duration,
  1830. $latitude,
  1831. $longitude
  1832. );
  1833. $client = new Client(['verify' => false]);
  1834. $response = $client->request('GET', $url, [
  1835. 'auth' => [$this->username, $this->password],
  1836. ]);
  1837. $statusCode = $response->getStatusCode();
  1838. $data = json_decode($response->getBody(), true);
  1839. // Set the data to Redis cache
  1840. $this->redisCache->set($cacheKey, $data);
  1841. return $data;
  1842. } catch (GuzzleHttp\Exception\RequestException $e) {
  1843. return throw new \Exception($e->getMessage());
  1844. } catch (\Exception $e) {
  1845. return throw new \Exception($e->getMessage());
  1846. }
  1847. }
  1848. /**
  1849. * Visibility
  1850. *
  1851. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  1852. * @param string $startDate The start date for the forecast
  1853. * @param string $endDate The end date for the forecast
  1854. * @param string $duration The duration for the forecast (default: '24')
  1855. * @param string $intervalType type of interval (default: 'hour')
  1856. * @param string $format The format of the forecast data (default: 'json')
  1857. * @return array The model forecast data
  1858. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  1859. */
  1860. public function getVisibility(array $coordinates, string $startDate, string $endDate, string $duration = '24', $unit = 'km', string $format = "json", $translator)
  1861. {
  1862. try {
  1863. // Validate the input parameters
  1864. if (empty($coordinates)) {
  1865. // throw new \InvalidArgumentException('Invalid coordinates');
  1866. return ["success" => false, "message" => $translator->trans("invalid_coordinates")];
  1867. }
  1868. if (!is_array($coordinates) || count($coordinates) < 1) {
  1869. // throw new \InvalidArgumentException('Coordinates should be a non-empty array');
  1870. return ["success" => false, "message" => $translator->trans("coordinates_should_be_a_non_empty_array")];
  1871. }
  1872. $latitude = $coordinates[0][0] ?? null;
  1873. $longitude = $coordinates[0][1] ?? null;
  1874. if (!is_numeric($latitude) || $latitude < -90 || $latitude > 90) {
  1875. // throw new \InvalidArgumentException('Invalid latitude');
  1876. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  1877. }
  1878. if (!is_numeric($longitude) || $longitude < -180 || $longitude > 180) {
  1879. // throw new \InvalidArgumentException('Invalid longitude');
  1880. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  1881. }
  1882. if (empty($startDate) || empty($endDate)) {
  1883. // throw new \InvalidArgumentException('Invalid dates');
  1884. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  1885. }
  1886. if (!is_numeric($duration)) {
  1887. // throw new \InvalidArgumentException('Duration should be numeric');
  1888. return ["success" => false, "message" => $translator->trans("duration_should_be_numeric")];
  1889. }
  1890. if (!in_array($unit, ['m', 'km', 'ft', 'nmi'])) {
  1891. // throw new \InvalidArgumentException('Invalid unit');
  1892. return ["success" => false, "message" => $translator->trans("invalid_unit")];
  1893. }
  1894. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  1895. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  1896. // Create a Redis key for the cache
  1897. $cacheKey = sprintf('visibility_%s_%s', implode('_', array_map(function ($coordinate) {
  1898. return implode('_', $coordinate);
  1899. }, $coordinates)), (($startDate) . '-' . ($endDate) . $duration));
  1900. // Try to get the data from Redis cache
  1901. $cachedData = $this->redisCache->get($cacheKey);
  1902. if ($cachedData !== null) {
  1903. // Return the cached data if available
  1904. return $cachedData;
  1905. }
  1906. // If cache is empty, get the data from the API
  1907. $url = sprintf(
  1908. '%s/%s--%s:PT%sH/visibility:%s/%s,%s/json',
  1909. $this->apiBaseUrl,
  1910. $startDate,
  1911. $endDate,
  1912. $duration,
  1913. $unit,
  1914. $latitude,
  1915. $longitude
  1916. );
  1917. $client = new Client(['verify' => false]);
  1918. $response = $client->request('GET', $url, [
  1919. 'auth' => [$this->username, $this->password],
  1920. ]);
  1921. $statusCode = $response->getStatusCode();
  1922. $data = json_decode($response->getBody(), true);
  1923. // Set the data to Redis cache
  1924. $this->redisCache->set($cacheKey, $data);
  1925. return $data;
  1926. } catch (GuzzleHttp\Exception\RequestException $e) {
  1927. return throw new \Exception($e->getMessage());
  1928. } catch (\Exception $e) {
  1929. return throw new \Exception($e->getMessage());
  1930. }
  1931. }
  1932. /**
  1933. * Wind Direction
  1934. *
  1935. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  1936. * @param string $startDate The start date for the forecast
  1937. * @param string $endDate The end date for the forecast
  1938. * @param string $duration The duration for the forecast (default: '24')
  1939. * @param string $intervalType type of interval (default: 'hour')
  1940. * @param string $format The format of the forecast data (default: 'json')
  1941. * @return array The model forecast data
  1942. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  1943. */
  1944. //https://api.meteomatics.com/2023-07-11T00:00:00ZP2D:PT3H/wind_dir_10m:d,wind_dir_700hPa:d/47.412164,9.340652/csv
  1945. public function getWindDirection(array $coordinates, string $startDate, string $endDate, string $duration = '24', $height = '10m', $level = '700hPa', string $format = "json", $translator)
  1946. {
  1947. try {
  1948. // Validate the input parameters
  1949. if (empty($coordinates)) {
  1950. return ["success" => false, "message" => $translator->trans("invalid_coordinates")];
  1951. }
  1952. if (!is_array($coordinates) || count($coordinates) < 1) {
  1953. return ["success" => false, "message" => $translator->trans("coordinates_should_be_a_non_empty_array")];
  1954. }
  1955. $latitude = $coordinates[0][0] ?? null;
  1956. $longitude = $coordinates[0][1] ?? null;
  1957. if (!is_numeric($latitude) || $latitude < -90 || $latitude > 90) {
  1958. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  1959. }
  1960. if (!is_numeric($longitude) || $longitude < -180 || $longitude > 180) {
  1961. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  1962. }
  1963. if (empty($startDate) || empty($endDate)) {
  1964. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  1965. }
  1966. if (!is_numeric($duration)) {
  1967. return ["success" => false, "message" => $translator->trans("duration_should_be_numeric")];
  1968. }
  1969. if (!in_array($level, ['1000hPa', '950hPa', '925hPa', '900hPa', '850hPa', '800hPa', '700hPa', '500hPa', '300hPa', '250hPa', '200hPa', '150hPa', '100hPa', '70hPa', '50hPa', '10hPa'])) {
  1970. return ["success" => false, "message" => $translator->trans("invalid_level")];
  1971. }
  1972. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  1973. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  1974. // Create a Redis key for the cache
  1975. $cacheKey = sprintf('wind_direction_%s_%s', implode('_', array_map(function ($coordinate) {
  1976. return implode('_', $coordinate);
  1977. }, $coordinates)), (($startDate) . '-' . ($endDate) . $duration . $level));
  1978. // Try to get the data from Redis cache
  1979. $cachedData = $this->redisCache->get($cacheKey);
  1980. if ($cachedData !== null) {
  1981. // Return the cached data if available
  1982. return $cachedData;
  1983. }
  1984. // If cache is empty, get the data from the API
  1985. $url = sprintf(
  1986. '%s/%s--%s:PT%sH/wind_dir_%s:d,wind_dir_%s:d/%s,%s/json',
  1987. $this->apiBaseUrl,
  1988. $startDate,
  1989. $endDate,
  1990. $duration,
  1991. $height,
  1992. $level,
  1993. $latitude,
  1994. $longitude
  1995. );
  1996. $client = new Client(['verify' => false]);
  1997. $response = $client->request('GET', $url, [
  1998. 'auth' => [$this->username, $this->password],
  1999. ]);
  2000. $statusCode = $response->getStatusCode();
  2001. $data = json_decode($response->getBody(), true);
  2002. // Set the data to Redis cache
  2003. $this->redisCache->set($cacheKey, $data);
  2004. return $data;
  2005. } catch (GuzzleHttp\Exception\RequestException $e) {
  2006. return throw new \Exception($e->getMessage());
  2007. } catch (\Exception $e) {
  2008. return throw new \Exception($e->getMessage());
  2009. }
  2010. }
  2011. /**
  2012. * Wind Speed
  2013. *
  2014. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  2015. * @param string $startDate The start date for the forecast
  2016. * @param string $endDate The end date for the forecast
  2017. * @param string $duration The duration for the forecast (default: '24')
  2018. * @param string $intervalType type of interval (default: 'hour')
  2019. * @param string $format The format of the forecast data (default: 'json')
  2020. * @return array The model forecast data
  2021. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  2022. */
  2023. public function getWindSpeed(array $coordinates, string $startDate, string $endDate, string $duration = '24', $unit = 'ft', $level = '700hPa', string $format = "json", $translator)
  2024. {
  2025. try {
  2026. // Validate the input parameters
  2027. if (empty($coordinates)) {
  2028. return ["success" => false, "message" => $translator->trans("invalid_coordinates")];
  2029. }
  2030. if (!is_array($coordinates) || count($coordinates) < 1) {
  2031. return ["success" => false, "message" => $translator->trans("coordinates_should_be_a_non_empty_array")];
  2032. }
  2033. $latitude = $coordinates[0][0] ?? null;
  2034. $longitude = $coordinates[0][1] ?? null;
  2035. if (!is_numeric($latitude) || $latitude < -90 || $latitude > 90) {
  2036. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  2037. }
  2038. if (!is_numeric($longitude) || $longitude < -180 || $longitude > 180) {
  2039. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  2040. }
  2041. if (empty($startDate) || empty($endDate)) {
  2042. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  2043. }
  2044. if (!is_numeric($duration)) {
  2045. return ["success" => false, "message" => $translator->trans("duration_should_be_numeric")];
  2046. }
  2047. if (!in_array($level, ['1000hPa', '950hPa', '925hPa', '900hPa', '850hPa', '800hPa', '700hPa', '500hPa', '300hPa', '250hPa', '200hPa', '150hPa', '100hPa', '70hPa', '50hPa', '10hPa'])) {
  2048. return ["success" => false, "message" => $translator->trans("invalid_level")];
  2049. }
  2050. if (!in_array($unit, ['ms', 'kmh', 'mph', 'kn', 'bft'])) {
  2051. return ["success" => false, "message" => $translator->trans("invalid_unit")];
  2052. }
  2053. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  2054. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  2055. // Create a Redis key for the cache
  2056. $cacheKey = sprintf('wind_speed_%s_%s', implode('_', array_map(function ($coordinate) {
  2057. return implode('_', $coordinate);
  2058. }, $coordinates)), (($startDate) . '-' . ($endDate) . $duration . $unit . $level));
  2059. // Try to get the data from Redis cache
  2060. $cachedData = $this->redisCache->get($cacheKey);
  2061. if ($cachedData !== null) {
  2062. // Return the cached data if available
  2063. return $cachedData;
  2064. }
  2065. // If cache is empty, get the data from the API
  2066. $url = sprintf(
  2067. '%s/%s--%s:PT%sH/wind_speed_%s:%s/%s,%s/json',
  2068. $this->apiBaseUrl,
  2069. $startDate,
  2070. $endDate,
  2071. $duration,
  2072. $level,
  2073. $unit,
  2074. $latitude,
  2075. $longitude
  2076. );
  2077. $client = new Client(['verify' => false]);
  2078. $response = $client->request('GET', $url, [
  2079. 'auth' => [$this->username, $this->password],
  2080. ]);
  2081. $statusCode = $response->getStatusCode();
  2082. $data = json_decode($response->getBody(), true);
  2083. // Set the data to Redis cache
  2084. $this->redisCache->set($cacheKey, $data);
  2085. return $data;
  2086. } catch (GuzzleHttp\Exception\RequestException $e) {
  2087. return throw new \Exception($e->getMessage());
  2088. } catch (\Exception $e) {
  2089. return throw new \Exception($e->getMessage());
  2090. }
  2091. }
  2092. /**
  2093. * Slippery Road
  2094. *
  2095. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  2096. * @param string $startDate The start date for the forecast
  2097. * @param string $endDate The end date for the forecast
  2098. * @param string $duration The duration for the forecast (default: '24')
  2099. * @param string $intervalType type of interval (default: 'hour')
  2100. * @param string $format The format of the forecast data (default: 'json')
  2101. * @return array The model forecast data
  2102. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  2103. */
  2104. public function getSlipperyRoad(array $coordinates, string $startDate, string $endDate, string $duration = '24', string $format = "json", $translator)
  2105. {
  2106. try {
  2107. // Validate the input parameters
  2108. if (empty($coordinates)) {
  2109. return ["success" => false, "message" => $translator->trans("invalid_coordinates")];
  2110. }
  2111. if (!is_array($coordinates) || count($coordinates) < 1) {
  2112. return ["success" => false, "message" => $translator->trans("coordinates_should_be_a_non_empty_array")];
  2113. }
  2114. $latitude = $coordinates[0][0] ?? null;
  2115. $longitude = $coordinates[0][1] ?? null;
  2116. if (!is_numeric($latitude) || $latitude < -90 || $latitude > 90) {
  2117. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  2118. }
  2119. if (!is_numeric($longitude) || $longitude < -180 || $longitude > 180) {
  2120. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  2121. }
  2122. if (empty($startDate) || empty($endDate)) {
  2123. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  2124. }
  2125. if (!is_numeric($duration)) {
  2126. return ["success" => false, "message" => $translator->trans("duration_should_be_numeric")];
  2127. }
  2128. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  2129. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  2130. // Create a Redis key for the cache
  2131. $cacheKey = sprintf('slipary_road_%s_%s', implode('_', array_map(function ($coordinate) {
  2132. return implode('_', $coordinate);
  2133. }, $coordinates)), (($startDate) . '-' . ($endDate) . $duration));
  2134. // Try to get the data from Redis cache
  2135. $cachedData = $this->redisCache->get($cacheKey);
  2136. if ($cachedData !== null) {
  2137. // Return the cached data if available
  2138. return $cachedData;
  2139. }
  2140. // If cache is empty, get the data from the API
  2141. $url = sprintf(
  2142. '%s/%s--%s:PT%sH/prob_slippery_road_%sh:p,is_slippery_road_%sh:idx/%s,%s/json',
  2143. $this->apiBaseUrl,
  2144. $startDate,
  2145. $endDate,
  2146. $duration,
  2147. $duration,
  2148. $duration,
  2149. $latitude,
  2150. $longitude
  2151. );
  2152. $client = new Client(['verify' => false]);
  2153. $response = $client->request('GET', $url, [
  2154. 'auth' => [$this->username, $this->password],
  2155. ]);
  2156. $statusCode = $response->getStatusCode();
  2157. $data = json_decode($response->getBody(), true);
  2158. // Set the data to Redis cache
  2159. $this->redisCache->set($cacheKey, $data);
  2160. return $data;
  2161. } catch (GuzzleHttp\Exception\RequestException $e) {
  2162. return throw new \Exception($e->getMessage());
  2163. } catch (\Exception $e) {
  2164. return throw new \Exception($e->getMessage());
  2165. }
  2166. }
  2167. /**
  2168. * Solar Radiation
  2169. *
  2170. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  2171. * @param string $startDate The start date for the forecast
  2172. * @param string $endDate The end date for the forecast
  2173. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  2174. */
  2175. public function getSolarRadiation(array $coordinates, string $startDate, string $endDate, $translator, string $format = 'png',)
  2176. {
  2177. try {
  2178. // Validate the input parameters
  2179. if (empty($coordinates)) {
  2180. return ["success" => false, "message" => $translator->trans("invalid_coordinates")];
  2181. }
  2182. if (!is_array($coordinates) || count($coordinates) < 1) {
  2183. return ["success" => false, "message" => $translator->trans("coordinates_should_be_a_non_empty_array")];
  2184. }
  2185. $latitude1 = $coordinates[0][0] ?? null;
  2186. $longitude1 = $coordinates[0][1] ?? null;
  2187. $latitude2 = $coordinates[1][0] ?? null;
  2188. $longitude2 = $coordinates[1][1] ?? null;
  2189. if (!is_numeric($latitude1) || $latitude1 < -90 || $latitude1 > 90) {
  2190. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  2191. }
  2192. if (!is_numeric($longitude1) || $longitude1 < -180 || $longitude1 > 180) {
  2193. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  2194. }
  2195. if (!is_numeric($latitude2) || $latitude2 < -90 || $latitude2 > 90) {
  2196. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  2197. }
  2198. if (
  2199. !is_numeric($longitude2) || $longitude2 < -180 || $longitude2 > 180
  2200. ) {
  2201. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  2202. }
  2203. if (empty($startDate) || empty($endDate)) {
  2204. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  2205. }
  2206. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  2207. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  2208. $url = sprintf(
  2209. '%s/%s/global_rad:W/%s,%s_%s,%s:600x400/%s',
  2210. $this->apiBaseUrl,
  2211. $startDate,
  2212. $latitude1,
  2213. $longitude1,
  2214. $latitude2,
  2215. $longitude2,
  2216. $format
  2217. );
  2218. // echo $url;
  2219. // exit;
  2220. $client = new Client(['verify' => false]);
  2221. $response = $client->request('GET', $url, [
  2222. 'auth' => [$this->username, $this->password],
  2223. ]);
  2224. $statusCode = $response->getStatusCode();
  2225. return $response->getBody();
  2226. } catch (GuzzleHttp\Exception\RequestException $e) {
  2227. return throw new \Exception($e->getMessage());
  2228. } catch (\Exception $e) {
  2229. return throw new \Exception($e->getMessage());
  2230. }
  2231. }
  2232. /**
  2233. * Humidity
  2234. *
  2235. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  2236. * @param string $startDate The start date for the forecast
  2237. * @param string $endDate The end date for the forecast
  2238. * @param string $duration The duration for the forecast (default: '24')
  2239. * @param string $intervalType type of interval (default: 'hour')
  2240. * @param string $format The format of the forecast data (default: 'json')
  2241. * @return array The model forecast data
  2242. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  2243. */
  2244. public function getHumidity(array $coordinates, string $startDate, string $endDate, string $duration = '24', $unit = 'ft', $level = '700hPa', string $format = "json", $translator)
  2245. {
  2246. try {
  2247. // Validate the input parameters
  2248. if (empty($coordinates)) {
  2249. return ["success" => false, "message" => $translator->trans("invalid_coordinates")];
  2250. }
  2251. if (!is_array($coordinates) || count($coordinates) < 1) {
  2252. return ["success" => false, "message" => $translator->trans("coordinates_should_be_a_non_empty_array")];
  2253. }
  2254. $latitude = $coordinates[0][0] ?? null;
  2255. $longitude = $coordinates[0][1] ?? null;
  2256. if (!is_numeric($latitude) || $latitude < -90 || $latitude > 90) {
  2257. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  2258. }
  2259. if (!is_numeric($longitude) || $longitude < -180 || $longitude > 180) {
  2260. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  2261. }
  2262. if (empty($startDate) || empty($endDate)) {
  2263. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  2264. }
  2265. if (!is_numeric($duration)) {
  2266. return ["success" => false, "message" => $translator->trans("duration_should_be_numeric")];
  2267. }
  2268. if (!in_array($level, ['1000hPa', '950hPa', '925hPa', '900hPa', '850hPa', '800hPa', '700hPa', '500hPa', '300hPa', '250hPa', '200hPa', '150hPa', '100hPa', '70hPa', '50hPa', '10hPa'])) {
  2269. return ["success" => false, "message" => $translator->trans("invalid_level")];
  2270. }
  2271. if (!in_array($unit, ['p'])) {
  2272. return ["success" => false, "message" => $translator->trans("invalid_unit")];
  2273. }
  2274. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  2275. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  2276. // Create a Redis key for the cache
  2277. $cacheKey = sprintf('humidity_%s_%s', implode('_', array_map(function ($coordinate) {
  2278. return implode('_', $coordinate);
  2279. }, $coordinates)), (($startDate) . '-' . ($endDate) . $duration . $unit . $level));
  2280. // Try to get the data from Redis cache
  2281. $cachedData = $this->redisCache->get($cacheKey);
  2282. if ($cachedData !== null) {
  2283. // Return the cached data if available
  2284. return $cachedData;
  2285. }
  2286. // If cache is empty, get the data from the API
  2287. $url = sprintf(
  2288. '%s/%s--%s:PT%sH/relative_humidity_%s:%s/%s,%s/json',
  2289. $this->apiBaseUrl,
  2290. $startDate,
  2291. $endDate,
  2292. $duration,
  2293. $level,
  2294. $unit,
  2295. $latitude,
  2296. $longitude
  2297. );
  2298. $client = new Client(['verify' => false]);
  2299. $response = $client->request('GET', $url, [
  2300. 'auth' => [$this->username, $this->password],
  2301. ]);
  2302. $statusCode = $response->getStatusCode();
  2303. $data = json_decode($response->getBody(), true);
  2304. // Set the data to Redis cache
  2305. $this->redisCache->set($cacheKey, $data);
  2306. return $data;
  2307. } catch (GuzzleHttp\Exception\RequestException $e) {
  2308. return throw new \Exception($e->getMessage());
  2309. } catch (\Exception $e) {
  2310. return throw new \Exception($e->getMessage());
  2311. }
  2312. }
  2313. /**
  2314. * Fog
  2315. *
  2316. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  2317. * @param string $startDate The start date for the forecast
  2318. * @param string $endDate The start date for the forecast
  2319. * @param string $interval Hours gap between search results
  2320. * @param string $intervalType type of interval (default: 'hour')
  2321. * @param string $format The format of the forecast data (default: 'json')
  2322. * @return array The model forecast data
  2323. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  2324. */
  2325. public function getFog(array $coordinates, string $startDate, string $endDate, string $interval = '1', $unit = 'km', $translator, string $format = "json")
  2326. {
  2327. try {
  2328. if (count($coordinates) > 1) {
  2329. // Validate the input parameters
  2330. foreach ($coordinates as $coordinate) {
  2331. if (count($coordinate) < 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  2332. throw new \InvalidArgumentException('Invalid coordinates');
  2333. }
  2334. }
  2335. if (empty($startDate)) {
  2336. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  2337. }
  2338. if (empty($endDate)) {
  2339. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  2340. }
  2341. if (!is_numeric($interval)) {
  2342. return ["success" => false, "message" => $translator->trans("interval_should_be_numeric")];
  2343. }
  2344. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  2345. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  2346. // Create a Redis key for the cache
  2347. $cacheKey = sprintf('visibility:%s,is_fog_%sh:idx', implode('_', array_map(function ($coordinate) {
  2348. return implode('_', $coordinate);
  2349. }, $coordinates)), (($startDate) . '-' . $endDate . $interval . $unit));
  2350. // Try to get the data from Redis cache
  2351. $cachedData = $this->redisCache->get($cacheKey);
  2352. if ($cachedData !== null) {
  2353. // Return the cached data if available
  2354. return $cachedData;
  2355. }
  2356. // If cache is empty, get the data from the API
  2357. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  2358. // If cache is empty, get the data from the API
  2359. $url = sprintf(
  2360. '%s/%s--%s:PT%sH/visibility:%s,is_fog_%sh:idx/%s/json',
  2361. $this->apiBaseUrl,
  2362. $startDate,
  2363. $endDate,
  2364. $interval,
  2365. $unit,
  2366. $interval,
  2367. $coordinateString
  2368. );
  2369. $client = new Client(['verify' => false]);
  2370. $response = $client->request('GET', $url, [
  2371. 'auth' => [$this->username, $this->password],
  2372. ]);
  2373. $statusCode = $response->getStatusCode();
  2374. $data = json_decode($response->getBody(), true);
  2375. // Set the data to Redis cache
  2376. $this->redisCache->set($cacheKey, $data);
  2377. return $data;
  2378. } else {
  2379. // Validate the input parameters
  2380. if (empty($coordinates)) {
  2381. return ["success" => false, "message" => $translator->trans("invalid_coordinates")];
  2382. }
  2383. if (!is_array($coordinates) || count($coordinates) < 1) {
  2384. return ["success" => false, "message" => $translator->trans("coordinates_should_be_a_non_empty_array")];
  2385. }
  2386. $latitude = $coordinates[0][0] ?? null;
  2387. $longitude = $coordinates[0][1] ?? null;
  2388. if (!is_numeric($latitude) || $latitude < -90 || $latitude > 90) {
  2389. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  2390. }
  2391. if (!is_numeric($longitude) || $longitude < -180 || $longitude > 180) {
  2392. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  2393. }
  2394. if (empty($startDate)) {
  2395. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  2396. }
  2397. if (empty($endDate)) {
  2398. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  2399. }
  2400. if (!is_numeric($interval)) {
  2401. return ["success" => false, "message" => $translator->trans("interval_should_be_numeric")];
  2402. }
  2403. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  2404. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  2405. // Create a Redis key for the cache
  2406. $cacheKey = sprintf('visibility:%s,is_fog_%sh:idx', implode('_', array_map(function ($coordinate) {
  2407. return implode('_', $coordinate);
  2408. }, $coordinates)), (($startDate) . '-' . $endDate . $interval . $unit));
  2409. // Try to get the data from Redis cache
  2410. $cachedData = $this->redisCache->get($cacheKey);
  2411. if ($cachedData !== null) {
  2412. // Return the cached data if available
  2413. return $cachedData;
  2414. }
  2415. // If cache is empty, get the data from the API
  2416. $url = sprintf(
  2417. '%s/%s--%s:PT%sH/visibility:%s,is_fog_%sh:idx/%s,%s/json',
  2418. $this->apiBaseUrl,
  2419. $startDate,
  2420. $endDate,
  2421. $interval,
  2422. $unit,
  2423. $interval,
  2424. $latitude,
  2425. $longitude
  2426. );
  2427. $client = new Client(['verify' => false]);
  2428. $response = $client->request('GET', $url, [
  2429. 'auth' => [$this->username, $this->password],
  2430. ]);
  2431. $statusCode = $response->getStatusCode();
  2432. $data = json_decode($response->getBody(), true);
  2433. // Set the data to Redis cache
  2434. $this->redisCache->set($cacheKey, $data);
  2435. return $data;
  2436. }
  2437. } catch (GuzzleHttp\Exception\RequestException $e) {
  2438. return throw new \Exception($e->getMessage());
  2439. } catch (\Exception $e) {
  2440. return throw new \Exception($e->getMessage());
  2441. }
  2442. }
  2443. /**
  2444. * Icing Potential
  2445. *
  2446. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  2447. * @param string $startDate The start date for the forecast
  2448. * @param string $interval gap between duration
  2449. * @param string $duration The duration for the forecast (default: '24')
  2450. * @param string $intervalType type of interval (default: 'hour')
  2451. * @param string $format The format of the forecast data (default: 'json')
  2452. * @return array The model forecast data
  2453. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  2454. */
  2455. public function getIcingPotential(array $coordinates, string $startDate, string $duration = '24', $interval = '1', $level = '300hPa', $translator, string $format = "json")
  2456. {
  2457. try {
  2458. // Validate the input parameters
  2459. if (empty($coordinates)) {
  2460. return ["success" => false, "message" => $translator->trans("invalid_coordinates")];
  2461. }
  2462. if (!is_array($coordinates) || count($coordinates) < 1) {
  2463. return ["success" => false, "message" => $translator->trans("coordinates_should_be_a_non_empty_array")];
  2464. }
  2465. $latitude = $coordinates[0][0] ?? null;
  2466. $longitude = $coordinates[0][1] ?? null;
  2467. if (!is_numeric($latitude) || $latitude < -90 || $latitude > 90) {
  2468. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  2469. }
  2470. if (!is_numeric($longitude) || $longitude < -180 || $longitude > 180) {
  2471. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  2472. }
  2473. if (empty($startDate)) {
  2474. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  2475. }
  2476. if (!is_numeric($duration)) {
  2477. return ["success" => false, "message" => $translator->trans("duration_should_be_numeric")];
  2478. }
  2479. if (!is_numeric($interval)) {
  2480. return ["success" => false, "message" => $translator->trans("interval_should_be_numeric")];
  2481. }
  2482. if (!in_array($level, ['1000hPa', '975hPa', '950hPa', '925hPa', '900hPa', '875hPa', '850hPa', '825hPa', '800hPa', '775hPa', '750hPa', '700hPa', '650hPa', '600hPa', '550hPa', '500hPa', '450hPa', '400hPa', '350hPa', '300hPa'])) {
  2483. return ["success" => false, "message" => $translator->trans("invalid_level")];
  2484. }
  2485. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  2486. // Create a Redis key for the cache
  2487. $cacheKey = sprintf('icing_potential_%s_%s', implode('_', array_map(function ($coordinate) {
  2488. return implode('_', $coordinate);
  2489. }, $coordinates)), (($startDate) . $duration . $interval . $level));
  2490. // Try to get the data from Redis cache
  2491. $cachedData = $this->redisCache->get($cacheKey);
  2492. if ($cachedData !== null) {
  2493. // Return the cached data if available
  2494. return $cachedData;
  2495. }
  2496. // If cache is empty, get the data from the API
  2497. //https://api.meteomatics.com/2023-07-22T00:00:00ZP5D:PT1H/icing_potential_300hPa:idx,icing_potential_500hPa:idx,icing_potential_800hPa:idx/47.457625,8.555272/html
  2498. $url = sprintf(
  2499. '%s/%sP%sD:PT%sH/icing_potential_%s:idx/%s,%s/json',
  2500. $this->apiBaseUrl,
  2501. $startDate,
  2502. $duration,
  2503. $interval,
  2504. $level,
  2505. $latitude,
  2506. $longitude
  2507. );
  2508. $client = new Client(['verify' => false]);
  2509. $response = $client->request('GET', $url, [
  2510. 'auth' => [$this->username, $this->password],
  2511. ]);
  2512. $statusCode = $response->getStatusCode();
  2513. $data = json_decode($response->getBody(), true);
  2514. // Set the data to Redis cache
  2515. $this->redisCache->set($cacheKey, $data);
  2516. return $data;
  2517. } catch (GuzzleHttp\Exception\RequestException $e) {
  2518. return throw new \Exception($e->getMessage());
  2519. } catch (\Exception $e) {
  2520. return throw new \Exception($e->getMessage());
  2521. }
  2522. }
  2523. /**
  2524. * Wind Gust
  2525. *
  2526. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  2527. * @param string $startDate The start date for the forecast
  2528. * @param string $interval Gap between duration
  2529. * @param string $duration The duration for the forecast (default: '24')
  2530. * @param string $intervalType type of interval (default: 'hour')
  2531. * @param string $format The format of the forecast data (default: 'json')
  2532. * @return array The model forecast data
  2533. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  2534. */
  2535. //https://api.meteomatics.com/2023-07-11T00:00:00ZP2D:PT3H/wind_dir_10m:d,wind_dir_700hPa:d/47.412164,9.340652/csv
  2536. public function getWindGust(array $coordinates, string $startDate, string $duration = '24', string $interval = '1', $height = '100', $translator, string $format = "json")
  2537. {
  2538. try {
  2539. // Validate the input parameters
  2540. if (empty($coordinates)) {
  2541. return ["success" => false, "message" => $translator->trans("invalid_coordinates")];
  2542. }
  2543. if (!is_array($coordinates) || count($coordinates) < 1) {
  2544. return ["success" => false, "message" => $translator->trans("coordinates_should_be_a_non_empty_array")];
  2545. }
  2546. $latitude = $coordinates[0][0] ?? null;
  2547. $longitude = $coordinates[0][1] ?? null;
  2548. if (!is_numeric($latitude) || $latitude < -90 || $latitude > 90) {
  2549. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  2550. }
  2551. if (!is_numeric($longitude) || $longitude < -180 || $longitude > 180) {
  2552. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  2553. }
  2554. if (empty($startDate)) {
  2555. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  2556. }
  2557. if (!is_numeric($duration)) {
  2558. return ["success" => false, "message" => $translator->trans("duration_should_be_numeric")];
  2559. }
  2560. if (!in_array($duration, ['1', '3', '6', '12', '24'])) {
  2561. return ["success" => false, "message" => $translator->trans("invalid_duration")];
  2562. }
  2563. if (!is_numeric($interval)) {
  2564. return ["success" => false, "message" => $translator->trans("interval_should_be_numeric")];
  2565. }
  2566. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  2567. // Create a Redis key for the cache
  2568. $cacheKey = sprintf('wind_gusts_%s_%s', implode('_', array_map(function ($coordinate) {
  2569. return implode('_', $coordinate);
  2570. }, $coordinates)), (($startDate) . $duration . $interval));
  2571. // Try to get the data from Redis cache
  2572. $cachedData = $this->redisCache->get($cacheKey);
  2573. if ($cachedData !== null) {
  2574. // Return the cached data if available
  2575. return $cachedData;
  2576. }
  2577. // If cache is empty, get the data from the API
  2578. // https://api.meteomatics.com/2023-07-22T00:00:00ZP4D:PT3H/wind_speed_mean_100m_3h:ms,wind_gusts_100m_3h:ms/47.412164,9.340652/html
  2579. $url = sprintf(
  2580. '%s/%sP%sD:PT%sH/wind_gusts_%s_%sh:ms/%s,%s/json',
  2581. $this->apiBaseUrl,
  2582. $startDate,
  2583. $duration,
  2584. $interval,
  2585. $height,
  2586. $interval,
  2587. $latitude,
  2588. $longitude
  2589. );
  2590. $client = new Client(['verify' => false]);
  2591. $response = $client->request('GET', $url, [
  2592. 'auth' => [$this->username, $this->password],
  2593. ]);
  2594. $statusCode = $response->getStatusCode();
  2595. $data = json_decode($response->getBody(), true);
  2596. // Set the data to Redis cache
  2597. $this->redisCache->set($cacheKey, $data);
  2598. return $data;
  2599. } catch (GuzzleHttp\Exception\RequestException $e) {
  2600. return throw new \Exception($e->getMessage());
  2601. } catch (\Exception $e) {
  2602. return throw new \Exception($e->getMessage());
  2603. }
  2604. }
  2605. /**
  2606. * Lightning Strikes
  2607. *
  2608. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  2609. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  2610. */
  2611. public function getLightningStrikes(array $coordinates)
  2612. {
  2613. try {
  2614. // Validate the input parameters
  2615. if (empty($coordinates)) {
  2616. throw new \InvalidArgumentException('Invalid coordinates');
  2617. }
  2618. // Create a Redis key for the cache
  2619. $cacheKey = sprintf('lighting_strikes_%s', implode('_', array_map(function ($coordinate) {
  2620. return $coordinate;
  2621. }, $coordinates)));
  2622. // Try to get the data from Redis cache
  2623. $cachedData = $this->redisCache->get($cacheKey);
  2624. if ($cachedData !== null) {
  2625. // Return the cached data if available
  2626. // return $cachedData;
  2627. }
  2628. // If cache is empty, get the data from the API
  2629. // https://api.meteomatics.com/wfs?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=lightnings&BBOX=5.77,45.74,10.65,47.89
  2630. $url = sprintf(
  2631. '%s/wfs?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=lightnings&BBOX=%s',
  2632. $this->apiBaseUrl,
  2633. implode(",", $coordinates)
  2634. );
  2635. $client = new Client(['verify' => false]);
  2636. $response = $client->request('GET', $url, [
  2637. 'auth' => [$this->username, $this->password],
  2638. ]);
  2639. $statusCode = $response->getStatusCode();
  2640. $data = $response->getBody();
  2641. // Set the data to Redis cache
  2642. $this->redisCache->set($cacheKey, $data);
  2643. return $data;
  2644. } catch (GuzzleHttp\Exception\RequestException $e) {
  2645. return throw new \Exception($e->getMessage());
  2646. } catch (\Exception $e) {
  2647. return throw new \Exception($e->getMessage());
  2648. }
  2649. }
  2650. /**
  2651. * Fetches Turbulence data from the API or cache
  2652. *
  2653. * @param string $datetime The date and time in the format "YYYY-MM-DDTHH:MM:SSZ"
  2654. * @return mixed The Turbulence data if available, or an error response
  2655. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  2656. */
  2657. public function getTurbulenceData(string $datetime)
  2658. {
  2659. try {
  2660. // Validate the input parameter
  2661. if (empty($datetime)) {
  2662. throw new \InvalidArgumentException('Invalid datetime');
  2663. }
  2664. $datetime = date('Y-m-d\TH:i:s.v\Z', (strtotime($datetime)));
  2665. // Create a Redis key for the cache
  2666. $cacheKey = sprintf('turbulence_%s', $datetime);
  2667. // Try to get the data from Redis cache
  2668. $cachedData = $this->redisCache->get($cacheKey);
  2669. if ($cachedData !== null) {
  2670. // Return the cached data if available
  2671. //return $cachedData;
  2672. }
  2673. $client = new Client(['verify' => false]);
  2674. // If cache is empty, get the data from the API
  2675. $url = sprintf(
  2676. '%s/%s/turbulence_cape:m23s/global:1,1/html_map',
  2677. $this->apiBaseUrl,
  2678. $datetime
  2679. );
  2680. // Make an HTTP request to the API
  2681. $response = $client->request('GET', $url, [
  2682. 'auth' => [$this->username, $this->password],
  2683. ]);
  2684. $statusCode = $response->getStatusCode();
  2685. $data = json_decode($response->getBody(), true);
  2686. // Set the data to Redis cache
  2687. $this->redisCache->set($cacheKey, $data);
  2688. return $data;
  2689. } catch (GuzzleHttp\Exception\RequestException $e) {
  2690. // Handle Guzzle HTTP request exceptions and create and return an error response
  2691. return throw new \Exception($e->getMessage());
  2692. } catch (\Exception $e) {
  2693. // Handle other exceptions and create and return an error response
  2694. return throw new \Exception($e->getMessage());
  2695. }
  2696. }
  2697. /**
  2698. * Fetches SIGMET data from the API or cache
  2699. *
  2700. * @param string $datetime The date and time in the format "YYYY-MM-DDTHH:MM:SSZ"
  2701. * @return mixed The SIGMET data if available, or an error response
  2702. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  2703. */
  2704. public function getSigmetData(string $datetime, $translator)
  2705. {
  2706. try {
  2707. // Validate the input parameter
  2708. if (empty($datetime)) {
  2709. return ["success" => false, "message" => $translator->trans("invalid_datetime")];
  2710. }
  2711. $datetime = date('Y-m-d\TH:i:s.v\Z', (strtotime($datetime)));
  2712. // Create a Redis key for the cache
  2713. $cacheKey = sprintf('sigmet_%s', $datetime);
  2714. // Try to get the data from Redis cache
  2715. $cachedData = $this->redisCache->get($cacheKey);
  2716. if ($cachedData !== null) {
  2717. // Return the cached data if available
  2718. return $cachedData;
  2719. }
  2720. // If cache is empty, get the data from the API
  2721. $url = sprintf(
  2722. '%s/mvt/aviation_reports/sigmet:0/style.json?datetime=%s',
  2723. $this->apiBaseUrl,
  2724. $datetime
  2725. );
  2726. $client = new Client(['verify' => false]);
  2727. // Make an HTTP request to the API
  2728. $response = $client->request('GET', $url, [
  2729. 'auth' => [$this->username, $this->password],
  2730. ]);
  2731. $statusCode = $response->getStatusCode();
  2732. $data = json_decode($response->getBody(), true);
  2733. // Set the data to Redis cache
  2734. $this->redisCache->set($cacheKey, $data);
  2735. return $data;
  2736. } catch (GuzzleHttp\Exception\RequestException $e) {
  2737. // Handle Guzzle HTTP request exceptions and create and return an error response
  2738. return throw new \Exception($e->getMessage());
  2739. } catch (\Exception $e) {
  2740. // Handle other exceptions and create and return an error response
  2741. return throw new \Exception($e->getMessage());
  2742. }
  2743. }
  2744. /**
  2745. * Fetches Geopotential Height data from the API or cache
  2746. *
  2747. * @param array $coordinates The coordinates value
  2748. * @param string $startDate The start date and time in the format "YYYY-MM-DDTHH:MM:SSZ"
  2749. * @param string $endDate The end date and time in the format "YYYY-MM-DDTHH:MM:SSZ"
  2750. * @param float $level The level value
  2751. * @param float $interval The interval value
  2752. * @return mixed The Geopotential Height data if available, or an error response
  2753. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  2754. */
  2755. public function getGeopotentialHeightData(array $coordinates, string $startDate, string $endDate, string $level, string $interval, $translator)
  2756. {
  2757. try {
  2758. if ($coordinates) {
  2759. $latitude = $coordinates[0][0];
  2760. $longitude = $coordinates[0][1];
  2761. }
  2762. // Validate the input parameters
  2763. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  2764. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  2765. }
  2766. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  2767. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  2768. }
  2769. if (empty($startDate) || empty($endDate)) {
  2770. return ["success" => false, "message" => $translator->trans("invalid_dates")];;
  2771. }
  2772. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  2773. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  2774. if (!is_numeric($interval)) {
  2775. return ["success" => false, "message" => $translator->trans("interval_should_be_numeric")];
  2776. }
  2777. if (!in_array($level, ['1000hPa', '950hPa', '925hPa', '900hPa', '850hPa', '800hPa', '700hPa', '500hPa', '300hPa', '250hPa', '200hPa', '150hPa', '100hPa', '70hPa', '50hPa', '10hPa'])) {
  2778. return ["success" => false, "message" => $translator->trans("invalid_level")];
  2779. }
  2780. // Create a Redis key for the cache
  2781. $cacheKey = sprintf('geopotential_height_%s_%s_%.6f_%.6f', $startDate, $endDate, $latitude, $longitude);
  2782. // Try to get the data from Redis cache
  2783. $cachedData = $this->redisCache->get($cacheKey);
  2784. if ($cachedData !== null) {
  2785. // Return the cached data if available
  2786. return $cachedData;
  2787. }
  2788. // If cache is empty, get the data from the API
  2789. //https://api.meteomatics.com/2023-07-23T00:00:00Z--2023-07-26T00:00:00Z:PT1H/geopotential_height_500hPa:m/46.5468,7.9826/html
  2790. $url = sprintf(
  2791. '%s/%s--%s:PT%sH/geopotential_height_%s:m/%s,%s/json',
  2792. $this->apiBaseUrl,
  2793. $startDate,
  2794. $endDate,
  2795. $interval,
  2796. $level,
  2797. $latitude,
  2798. $longitude
  2799. );
  2800. $client = new Client(['verify' => false]);
  2801. // Make an HTTP request to the API
  2802. $response = $client->request('GET', $url, [
  2803. 'auth' => [$this->username, $this->password],
  2804. ]);
  2805. $statusCode = $response->getStatusCode();
  2806. $data = json_decode($response->getBody(), true);
  2807. // Set the data to Redis cache
  2808. $this->redisCache->set($cacheKey, $data);
  2809. return $data;
  2810. } catch (GuzzleHttp\Exception\RequestException $e) {
  2811. // Handle Guzzle HTTP request exceptions and create and return an error response
  2812. return throw new \Exception($e->getMessage());
  2813. } catch (\Exception $e) {
  2814. // Handle other exceptions and create and return an error response
  2815. return throw new \Exception($e->getMessage());
  2816. }
  2817. }
  2818. /**
  2819. * Pressure at Higher Altitudes data from the API or cache
  2820. *
  2821. * @param array $coordinates The coordinates value
  2822. * @param string $startDate The start date and time in the format "YYYY-MM-DDTHH:MM:SSZ"
  2823. * @param string $endDate The end date and time in the format "YYYY-MM-DDTHH:MM:SSZ"
  2824. * @param float $level The level value
  2825. * @param float $interval The interval value
  2826. * @return mixed The Geopotential Height data if available, or an error response
  2827. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  2828. */
  2829. public function getPressureData(array $coordinates, string $startDate, string $endDate, string $level, string $interval)
  2830. {
  2831. try {
  2832. if ($coordinates) {
  2833. $latitude = $coordinates[0][0];
  2834. $longitude = $coordinates[0][1];
  2835. }
  2836. // Validate the input parameters
  2837. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  2838. throw new \InvalidArgumentException('Invalid latitude');
  2839. }
  2840. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  2841. throw new \InvalidArgumentException('Invalid longitude');
  2842. }
  2843. if (empty($startDate) || empty($endDate)) {
  2844. throw new \InvalidArgumentException('Invalid dates');
  2845. }
  2846. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  2847. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  2848. if (!is_numeric($interval)) {
  2849. throw new \InvalidArgumentException('Interval should be numeric');
  2850. }
  2851. if ($level < 1 && $level > 20000) {
  2852. throw new \InvalidArgumentException('Invalid level it should be between 1 to 20000');
  2853. }
  2854. // Create a Redis key for the cache
  2855. $cacheKey = sprintf('pressure_%s_%s_%.6f_%.6f', $startDate, $endDate, $latitude, $longitude);
  2856. // Try to get the data from Redis cache
  2857. $cachedData = $this->redisCache->get($cacheKey);
  2858. if ($cachedData !== null) {
  2859. // Return the cached data if available
  2860. return $cachedData;
  2861. }
  2862. // If cache is empty, get the data from the API
  2863. ///pressure_1000m:hPa/-16.489689,-68.119293/html
  2864. $url = sprintf(
  2865. '%s/%s--%s:PT%sH/pressure_%sm:hPa/%s,%s/json',
  2866. $this->apiBaseUrl,
  2867. $startDate,
  2868. $endDate,
  2869. $interval,
  2870. $level,
  2871. $latitude,
  2872. $longitude
  2873. );
  2874. $client = new Client(['verify' => false]);
  2875. // Make an HTTP request to the API
  2876. $response = $client->request('GET', $url, [
  2877. 'auth' => [$this->username, $this->password],
  2878. ]);
  2879. $statusCode = $response->getStatusCode();
  2880. $data = json_decode($response->getBody(), true);
  2881. // Set the data to Redis cache
  2882. $this->redisCache->set($cacheKey, $data);
  2883. return $data;
  2884. } catch (GuzzleHttp\Exception\RequestException $e) {
  2885. // Handle Guzzle HTTP request exceptions and create and return an error response
  2886. return throw new \Exception($e->getMessage());
  2887. } catch (\Exception $e) {
  2888. // Handle other exceptions and create and return an error response
  2889. return throw new \Exception($e->getMessage());
  2890. }
  2891. }
  2892. /**
  2893. * Cloud Cover data from the API or cache
  2894. *
  2895. * @param array $coordinates The coordinates value
  2896. * @param string $startDate The start date and time in the format "YYYY-MM-DDTHH:MM:SSZ"
  2897. * @param string $endDate The end date and time in the format "YYYY-MM-DDTHH:MM:SSZ"
  2898. * @param float $level The level value
  2899. * @param float $interval The interval value
  2900. * @return mixed The Geopotential Height data if available, or an error response
  2901. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  2902. */
  2903. public function getCloudCover(array $coordinates, string $startDate, string $endDate, $translator)
  2904. {
  2905. try {
  2906. if ($coordinates) {
  2907. $latitude = $coordinates[0][0];
  2908. $longitude = $coordinates[0][1];
  2909. }
  2910. // Validate the input parameters
  2911. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  2912. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  2913. }
  2914. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  2915. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  2916. }
  2917. if (empty($startDate) || empty($endDate)) {
  2918. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  2919. }
  2920. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  2921. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  2922. // Create a Redis key for the cache
  2923. $cacheKey = sprintf('cloud_cover_%s_%s_%.6f_%.6f', $startDate, $endDate, $latitude, $longitude);
  2924. // Try to get the data from Redis cache
  2925. $cachedData = $this->redisCache->get($cacheKey);
  2926. if ($cachedData !== null) {
  2927. // Return the cached data if available
  2928. return $cachedData;
  2929. }
  2930. // If cache is empty, get the data from the API
  2931. $url = sprintf(
  2932. '%s/%s/effective_cloud_cover:octas/%s,%s/json',
  2933. $this->apiBaseUrl,
  2934. $startDate,
  2935. $latitude,
  2936. $longitude
  2937. );
  2938. $client = new Client(['verify' => false]);
  2939. // Make an HTTP request to the API
  2940. $response = $client->request('GET', $url, [
  2941. 'auth' => [$this->username, $this->password],
  2942. ]);
  2943. $statusCode = $response->getStatusCode();
  2944. $data = json_decode($response->getBody(), true);
  2945. // Set the data to Redis cache
  2946. $this->redisCache->set($cacheKey, $data);
  2947. return $data;
  2948. } catch (GuzzleHttp\Exception\RequestException $e) {
  2949. // Handle Guzzle HTTP request exceptions and create and return an error response
  2950. return throw new \Exception($e->getMessage());
  2951. } catch (\Exception $e) {
  2952. // Handle other exceptions and create and return an error response
  2953. return throw new \Exception($e->getMessage());
  2954. }
  2955. }
  2956. /**
  2957. * Wind Speed vertical components data from the API or cache
  2958. *
  2959. * @param array $coordinates The coordinates value
  2960. * @param string $startDate The start date and time in the format "YYYY-MM-DDTHH:MM:SSZ"
  2961. * @param string $endDate The end date and time in the format "YYYY-MM-DDTHH:MM:SSZ"
  2962. * @param int $level The day value
  2963. * @param string $level The level value
  2964. * @param string $unit The level value
  2965. * @param intervalInMinutes $interval The interval value
  2966. * @return mixed The Geopotential Height data if available, or an error response
  2967. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  2968. */
  2969. public function getWindSpeedVerticalComponents(array $coordinates, string $startDate, int $day, int $intervalInMinutes, string $level, string $unit, $translator)
  2970. {
  2971. try {
  2972. if ($coordinates) {
  2973. $latitude = $coordinates[0][0];
  2974. $longitude = $coordinates[0][1];
  2975. }
  2976. // Validate the input parameters
  2977. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  2978. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  2979. }
  2980. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  2981. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  2982. }
  2983. if (empty($startDate)) {
  2984. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  2985. }
  2986. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  2987. if (!is_numeric($day)) {
  2988. return ["success" => false, "message" => $translator->trans("day_should_be_numeric")];
  2989. }
  2990. if (!is_numeric($intervalInMinutes)) {
  2991. return ["success" => false, "message" => $translator->trans("interval_in_minute_should_be_numeric")];
  2992. }
  2993. if (!in_array($level, ['1000hPa', '950hPa', '925hPa', '900hPa', '850hPa', '800hPa', '700hPa', '500hPa', '300hPa', '250hPa', '200hPa', '150hPa', '100hPa', '70hPa'])) {
  2994. return ["success" => false, "message" => $translator->trans("invalid_level")];
  2995. }
  2996. // Create a Redis key for the cache
  2997. $cacheKey = sprintf('wind_speed_vertical_%s_%s_%s_%s_%.6f_%.6f', $startDate, $day, $level, $unit, $latitude, $longitude);
  2998. // Try to get the data from Redis cache
  2999. $cachedData = $this->redisCache->get($cacheKey);
  3000. if ($cachedData !== null) {
  3001. // Return the cached data if available
  3002. return $cachedData;
  3003. }
  3004. // If cache is empty, get the data from the API
  3005. $url = sprintf(
  3006. '%s/%sP%sD:PT%sM/wind_speed_w_%s:%s/%s,%s/json',
  3007. $this->apiBaseUrl,
  3008. $startDate,
  3009. $day,
  3010. $intervalInMinutes,
  3011. $level,
  3012. $unit,
  3013. $latitude,
  3014. $longitude
  3015. );
  3016. $client = new Client(['verify' => false]);
  3017. // Make an HTTP request to the API
  3018. $response = $client->request('GET', $url, [
  3019. 'auth' => [$this->username, $this->password],
  3020. ]);
  3021. $data = json_decode($response->getBody(), true);
  3022. // Set the data to Redis cache
  3023. $this->redisCache->set($cacheKey, $data);
  3024. return $data;
  3025. } catch (GuzzleHttp\Exception\RequestException $e) {
  3026. // Handle Guzzle HTTP request exceptions and create and return an error response
  3027. return throw new \Exception($e->getMessage());
  3028. } catch (\Exception $e) {
  3029. // Handle other exceptions and create and return an error response
  3030. return throw new \Exception($e->getMessage());
  3031. }
  3032. }
  3033. /**
  3034. * Wind Power data from the API or cache
  3035. * @param array $coordinates The coordinates value
  3036. * @param string $startDate The start date and time in the format "YYYY-MM-DDTHH:MM:SSZ"
  3037. * @param string $endDate The end date and time in the format "YYYY-MM-DDTHH:MM:SSZ"
  3038. * @param int $intervalInMinutes The interval value
  3039. * @param int $height The height value
  3040. * @param float $unit The interval value
  3041. * @return mixed The Geopotential Height data if available, or an error response
  3042. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  3043. */
  3044. public function getWindPower(array $coordinates, string $startDate, string $endDate, int $height, int $intervalInMinutes, string $unit, string $turbine_id, $translator)
  3045. {
  3046. try {
  3047. if ($coordinates) {
  3048. $latitude = $coordinates[0][0];
  3049. $longitude = $coordinates[0][1];
  3050. }
  3051. // Validate the input parameters
  3052. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  3053. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  3054. }
  3055. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  3056. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  3057. }
  3058. if (empty($startDate)) {
  3059. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  3060. }
  3061. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  3062. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  3063. if (!is_numeric($intervalInMinutes)) {
  3064. return ["success" => false, "message" => $translator->trans("interval_should_be_numeric")];
  3065. }
  3066. if (!is_numeric($height)) {
  3067. return ["success" => false, "message" => $translator->trans("height_should_be_numeric")];
  3068. }
  3069. // Create a Redis key for the cache
  3070. $cacheKey = sprintf('wind_power_%s_%s_%s_%s_%s', $startDate, $endDate, $height, $unit, $intervalInMinutes);
  3071. // Try to get the data from Redis cache
  3072. $cachedData = $this->redisCache->get($cacheKey);
  3073. if ($cachedData !== null) {
  3074. // Return the cached data if available
  3075. return $cachedData;
  3076. }
  3077. // If cache is empty, get the data from the API
  3078. $url = sprintf(
  3079. '%s/%s--%s:PT%sM/wind_power_turbine_%s_hub_height_%sm:%s,wind_power_turbine_siemens_swt_2_3_93_2300_hub_height_%sm:%s,wind_power_turbine_enercon_e66_2000_hub_height_%sm:%s/%s,%s/json',
  3080. $this->apiBaseUrl,
  3081. $startDate,
  3082. $endDate,
  3083. $intervalInMinutes,
  3084. $turbine_id,
  3085. $height,
  3086. $unit,
  3087. $height,
  3088. $unit,
  3089. $height,
  3090. $unit,
  3091. $latitude,
  3092. $longitude
  3093. );
  3094. $client = new Client(['verify' => false]);
  3095. // Make an HTTP request to the API
  3096. $response = $client->request('GET', $url, [
  3097. 'auth' => [$this->username, $this->password],
  3098. ]);
  3099. $data = json_decode($response->getBody(), true);
  3100. // Set the data to Redis cache
  3101. $this->redisCache->set($cacheKey, $data);
  3102. return $data;
  3103. } catch (GuzzleHttp\Exception\RequestException $e) {
  3104. // Handle Guzzle HTTP request exceptions and create and return an error response
  3105. return throw new \Exception($e->getMessage());
  3106. } catch (\Exception $e) {
  3107. // Handle other exceptions and create and return an error response
  3108. return throw new \Exception($e->getMessage());
  3109. }
  3110. }
  3111. /**
  3112. * Radiation data from the API or cache
  3113. * @param array $coordinates The coordinates value
  3114. * @param string $startDate The start date and time in the format "YYYY-MM-DDTHH:MM:SSZ"
  3115. * @param int $intervalInHours The interval value
  3116. * @param int $day The day value
  3117. * @param string $typeOfRadiation should be one of these "clear_sky_rad","diffuse_rad","direct_rad","global_rad"
  3118. * @return mixed The Geopotential Height data if available, or an error response
  3119. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  3120. */
  3121. public function getRadiation(array $coordinates, string $startDate, int $day, int $intervalInHours, string $typeOfRadiation, $translator)
  3122. {
  3123. try {
  3124. if ($coordinates) {
  3125. $latitude = $coordinates[0][0];
  3126. $longitude = $coordinates[0][1];
  3127. }
  3128. // Validate the input parameters
  3129. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  3130. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  3131. }
  3132. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  3133. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  3134. }
  3135. if (empty($startDate)) {
  3136. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  3137. }
  3138. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  3139. if (!is_numeric($intervalInHours)) {
  3140. return ["success" => false, "message" => $translator->trans("interval_in_hour_should_be_numeric")];
  3141. }
  3142. if (!is_numeric($day)) {
  3143. return ["success" => false, "message" => $translator->trans("day_should_be_numeric")];
  3144. }
  3145. if (!in_array($typeOfRadiation, ["clear_sky_rad", "diffuse_rad", "direct_rad", "global_rad"])) {
  3146. // throw new \InvalidArgumentException('Radiation type should be one of these "clear_sky_rad","diffuse_rad","direct_rad","global_rad"');
  3147. return ["success" => false, "message" => $translator->trans("Radiation_should_be_type:'clear_sky_rad','diffuse_rad','direct_rad','global_rad'")];
  3148. }
  3149. // Create a Redis key for the cache
  3150. $cacheKey = sprintf('radiation_%s_%s_%s_%s_%s_%s', $startDate, $day, $intervalInHours, $typeOfRadiation, $latitude, $longitude);
  3151. // Try to get the data from Redis cache
  3152. $cachedData = $this->redisCache->get($cacheKey);
  3153. if ($cachedData !== null) {
  3154. // Return the cached data if available
  3155. return $cachedData;
  3156. }
  3157. // If cache is empty, get the data from the API
  3158. $url = sprintf(
  3159. '%s/%sP%sD:PT%sH/%s:W/%s,%s/json',
  3160. $this->apiBaseUrl,
  3161. $startDate,
  3162. $day,
  3163. $intervalInHours,
  3164. $typeOfRadiation,
  3165. $latitude,
  3166. $longitude
  3167. );
  3168. $client = new Client(['verify' => false]);
  3169. // Make an HTTP request to the API
  3170. $response = $client->request('GET', $url, [
  3171. 'auth' => [$this->username, $this->password],
  3172. ]);
  3173. $data = json_decode($response->getBody(), true);
  3174. // Set the data to Redis cache
  3175. $this->redisCache->set($cacheKey, $data);
  3176. return $data;
  3177. } catch (GuzzleHttp\Exception\RequestException $e) {
  3178. // Handle Guzzle HTTP request exceptions and create and return an error response
  3179. return throw new \Exception($e->getMessage());
  3180. } catch (\Exception $e) {
  3181. // Handle other exceptions and create and return an error response
  3182. return throw new \Exception($e->getMessage());
  3183. }
  3184. }
  3185. /**
  3186. * Solar Power data from the API or cache
  3187. * @param array $coordinates The coordinates value
  3188. * @param string $startDate The start date and time in the format "YYYY-MM-DDTHH:MM:SSZ"
  3189. * @param string $endDate The start date and time in the format "YYYY-MM-DDTHH:MM:SSZ"
  3190. * @param int $intervalInHours The interval value
  3191. * @param int $hour The hour value
  3192. * @param string $unit The unit value
  3193. * @param string $specification would be like this "installed_capacity_<capacity>"
  3194. * @return mixed The Geopotential Height data if available, or an error response
  3195. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  3196. */
  3197. public function getSolarPower(array $coordinates, string $startDate, string $endDate, int $intervalInHours, string $hour, string $unit, string $specification, $translator)
  3198. {
  3199. try {
  3200. if ($coordinates) {
  3201. $latitude = $coordinates[0][0];
  3202. $longitude = $coordinates[0][1];
  3203. }
  3204. // Validate the input parameters
  3205. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  3206. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  3207. }
  3208. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  3209. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  3210. }
  3211. if (empty($startDate)) {
  3212. return ["success" => false, "message" => $translator->trans("invalid_start_date")];
  3213. }
  3214. if (empty($endDate)) {
  3215. return ["success" => false, "message" => $translator->trans("invalid_end_date")];
  3216. }
  3217. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  3218. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  3219. if (!is_numeric($intervalInHours)) {
  3220. return ["success" => false, "message" => $translator->trans("interval_should_be_numeric")];;
  3221. }
  3222. if (!is_numeric($hour)) {
  3223. return ["success" => false, "message" => $translator->trans("hours_should_be_numeric")];
  3224. }
  3225. // Create a Redis key for the cache
  3226. $cacheKey = sprintf('solar_power_%s_%s_%s_%s_%s_%s_%s', $specification, $startDate, $endDate, $unit, $hour, $latitude, $longitude);
  3227. // Try to get the data from Redis cache
  3228. $cachedData = $this->redisCache->get($cacheKey);
  3229. if ($cachedData !== null) {
  3230. // Return the cached data if available
  3231. return $cachedData;
  3232. }
  3233. // If cache is empty, get the data from the API
  3234. $url = sprintf(
  3235. '%s/%s--%s:PT%sH/solar_power_%s:%s/%s,%s/json',
  3236. $this->apiBaseUrl,
  3237. $startDate,
  3238. $endDate,
  3239. $hour,
  3240. $specification,
  3241. $unit,
  3242. $latitude,
  3243. $longitude
  3244. );
  3245. // p_r($url);
  3246. // exit;
  3247. $client = new Client(['verify' => false]);
  3248. // Make an HTTP request to the API
  3249. $response = $client->request('GET', $url, [
  3250. 'auth' => [$this->username, $this->password],
  3251. ]);
  3252. $data = json_decode($response->getBody(), true);
  3253. // Set the data to Redis cache
  3254. $this->redisCache->set($cacheKey, $data);
  3255. return $data;
  3256. } catch (GuzzleHttp\Exception\RequestException $e) {
  3257. // Handle Guzzle HTTP request exceptions and create and return an error response
  3258. return throw new \Exception($e->getMessage());
  3259. } catch (\Exception $e) {
  3260. // Handle other exceptions and create and return an error response
  3261. return throw new \Exception($e->getMessage());
  3262. }
  3263. }
  3264. /**
  3265. * Rainfall
  3266. *
  3267. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  3268. * @param string $startDate The start date for the forecast
  3269. * @param string $endDate The end date for the forecast
  3270. * @param string $duration The duration for the forecast (default: '24')
  3271. * @param string $intervalType type of interval (default: 'hour')
  3272. * @param string $format The format of the forecast data (default: 'json')
  3273. * @return array The model forecast data
  3274. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  3275. */
  3276. public function getRainfall(array $coordinates, string $startDate, string $endDate, string $duration = '24', $translator, string $format = "json")
  3277. {
  3278. try {
  3279. // Validate the input parameters
  3280. if (empty($coordinates)) {
  3281. return ["success" => false, "message" => $translator->trans("invalid_coordinates")];
  3282. }
  3283. if (!is_array($coordinates) || count($coordinates) < 1) {
  3284. return ["success" => false, "message" => $translator->trans("coordinates_should_be_a_non_empty_array")];
  3285. }
  3286. $latitude = $coordinates[0][0] ?? null;
  3287. $longitude = $coordinates[0][1] ?? null;
  3288. if (!is_numeric($latitude) || $latitude < -90 || $latitude > 90) {
  3289. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  3290. }
  3291. if (!is_numeric($longitude) || $longitude < -180 || $longitude > 180) {
  3292. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  3293. }
  3294. if (empty($startDate) || empty($endDate)) {
  3295. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  3296. }
  3297. if (!is_numeric($duration)) {
  3298. return ["success" => false, "message" => $translator->trans("duration_should_be_numeric")];
  3299. }
  3300. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  3301. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  3302. // Create a Redis key for the cache
  3303. $cacheKey = sprintf('rainfall_%s_%s', implode('_', array_map(function ($coordinate) {
  3304. return implode('_', $coordinate);
  3305. }, $coordinates)), (($startDate) . '-' . ($endDate) . $duration));
  3306. // Try to get the data from Redis cache
  3307. $cachedData = $this->redisCache->get($cacheKey);
  3308. if ($cachedData !== null) {
  3309. // Return the cached data if available
  3310. return $cachedData;
  3311. }
  3312. // If cache is empty, get the data from the API
  3313. $url = sprintf(
  3314. '%s/%s--%s:PT%sM/is_rain_%smin:idx/%s,%s/json',
  3315. $this->apiBaseUrl,
  3316. $startDate,
  3317. $endDate,
  3318. $duration,
  3319. $duration,
  3320. $latitude,
  3321. $longitude
  3322. );
  3323. $client = new Client(['verify' => false]);
  3324. $response = $client->request('GET', $url, [
  3325. 'auth' => [$this->username, $this->password],
  3326. ]);
  3327. $statusCode = $response->getStatusCode();
  3328. $data = json_decode($response->getBody(), true);
  3329. // Set the data to Redis cache
  3330. $this->redisCache->set($cacheKey, $data);
  3331. return $data;
  3332. } catch (GuzzleHttp\Exception\RequestException $e) {
  3333. return throw new \Exception($e->getMessage());
  3334. } catch (\Exception $e) {
  3335. return throw new \Exception($e->getMessage());
  3336. }
  3337. }
  3338. /**
  3339. * Dew Point
  3340. *
  3341. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  3342. * @param string $startDate The start date for the forecast
  3343. * @param string $endDate The end date for the forecast
  3344. * @param string $duration The duration for the forecast (default: '24')
  3345. * @param string $intervalType type of interval (default: 'hour')
  3346. * @param string $format The format of the forecast data (default: 'json')
  3347. * @return array The model forecast data
  3348. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  3349. */
  3350. public function getDewPoint(array $coordinates, string $startDate, string $endDate, string $duration = '24', $translator, string $format = "json")
  3351. {
  3352. try {
  3353. // Validate the input parameters
  3354. if (empty($coordinates)) {
  3355. return ["success" => false, "message" => $translator->trans("invalid_coordinates")];
  3356. }
  3357. if (!is_array($coordinates) || count($coordinates) < 1) {
  3358. return ["success" => false, "message" => $translator->trans("coordinates_should_be_a_non_empty_array")];
  3359. }
  3360. $latitude = $coordinates[0][0] ?? null;
  3361. $longitude = $coordinates[0][1] ?? null;
  3362. if (!is_numeric($latitude) || $latitude < -90 || $latitude > 90) {
  3363. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  3364. }
  3365. if (!is_numeric($longitude) || $longitude < -180 || $longitude > 180) {
  3366. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  3367. }
  3368. if (empty($startDate) || empty($endDate)) {
  3369. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  3370. }
  3371. if (!is_numeric($duration)) {
  3372. return ["success" => false, "message" => $translator->trans("duration_should_be_numeric")];
  3373. }
  3374. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  3375. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  3376. // Create a Redis key for the cache
  3377. $cacheKey = sprintf('dew_point_%s_%s', implode('_', array_map(function ($coordinate) {
  3378. return implode('_', $coordinate);
  3379. }, $coordinates)), (($startDate) . '-' . ($endDate) . $duration));
  3380. // Try to get the data from Redis cache
  3381. $cachedData = $this->redisCache->get($cacheKey);
  3382. if ($cachedData !== null) {
  3383. // Return the cached data if available
  3384. return $cachedData;
  3385. }
  3386. // If cache is empty, get the data from the API
  3387. $url = sprintf(
  3388. '%s/%s--%s:PT%sH/dew_point_2m:C/%s,%s/json',
  3389. $this->apiBaseUrl,
  3390. $startDate,
  3391. $endDate,
  3392. $duration,
  3393. $latitude,
  3394. $longitude
  3395. );
  3396. $client = new Client(['verify' => false]);
  3397. $response = $client->request('GET', $url, [
  3398. 'auth' => [$this->username, $this->password],
  3399. ]);
  3400. $statusCode = $response->getStatusCode();
  3401. $data = json_decode($response->getBody(), true);
  3402. // Set the data to Redis cache
  3403. $this->redisCache->set($cacheKey, $data);
  3404. return $data;
  3405. } catch (GuzzleHttp\Exception\RequestException $e) {
  3406. return throw new \Exception($e->getMessage());
  3407. } catch (\Exception $e) {
  3408. return throw new \Exception($e->getMessage());
  3409. }
  3410. }
  3411. /**
  3412. * Wms
  3413. *
  3414. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  3415. */
  3416. public function getWms($params)
  3417. {
  3418. try {
  3419. // If cache is empty, get the data from the API
  3420. $url = sprintf(
  3421. '%s/wms?%s',
  3422. $this->apiBaseUrl,
  3423. $params['param']
  3424. );
  3425. $client = new Client(['verify' => false]);
  3426. $response = $client->request('GET', $url, [
  3427. 'auth' => [$this->username, $this->password],
  3428. ]);
  3429. return $response->getBody();
  3430. } catch (GuzzleHttp\Exception\RequestException $e) {
  3431. return throw new \Exception($e->getMessage());
  3432. } catch (\Exception $e) {
  3433. return throw new \Exception($e->getMessage());
  3434. }
  3435. }
  3436. /**
  3437. * Wind
  3438. *
  3439. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  3440. * @param string $startDate The start date for the forecast
  3441. * @param string $endDate The end date for the forecast
  3442. * @param string $duration The duration for the forecast (default: '24')
  3443. * @param string $intervalType type of interval (default: 'hour')
  3444. * @param string $format The format of the forecast data (default: 'json')
  3445. * @return array The model forecast data
  3446. * @throws \InvalidArgumentException If any of the parameters are invalid or missing
  3447. */
  3448. public function getWind(array $coordinates, string $startDate, string $endDate, string $duration = '24', $translator, string $format = "json")
  3449. {
  3450. try {
  3451. // Validate the input parameters
  3452. if (empty($coordinates)) {
  3453. return ["success" => false, "message" => $translator->trans("invalid_coordinates")];
  3454. }
  3455. if (!is_array($coordinates) || count($coordinates) < 1) {
  3456. return ["success" => false, "message" => $translator->trans("coordinates_should_be_a_non_empty_array")];
  3457. }
  3458. $latitude = $coordinates[0][0] ?? null;
  3459. $longitude = $coordinates[0][1] ?? null;
  3460. if (!is_numeric($latitude) || $latitude < -90 || $latitude > 90) {
  3461. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  3462. }
  3463. if (!is_numeric($longitude) || $longitude < -180 || $longitude > 180) {
  3464. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  3465. }
  3466. if (empty($startDate) || empty($endDate)) {
  3467. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  3468. }
  3469. if (!is_numeric($duration)) {
  3470. return ["success" => false, "message" => $translator->trans("duration_should_be_numeric")];
  3471. }
  3472. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  3473. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  3474. // Create a Redis key for the cache
  3475. $cacheKey = sprintf('wind_%s_%s', implode('_', array_map(function ($coordinate) {
  3476. return implode('_', $coordinate);
  3477. }, $coordinates)), (($startDate) . '-' . ($endDate) . $duration));
  3478. // Try to get the data from Redis cache
  3479. $cachedData = $this->redisCache->get($cacheKey);
  3480. if ($cachedData !== null) {
  3481. // Return the cached data if available
  3482. return $cachedData;
  3483. }
  3484. // If cache is empty, get the data from the API
  3485. $url = sprintf(
  3486. '%s/%s--%s:PT%sH/wind_speed_u_10m:ms,wind_speed_v_10m:ms/%s,%s/json',
  3487. $this->apiBaseUrl,
  3488. $startDate,
  3489. $endDate,
  3490. $duration,
  3491. $latitude,
  3492. $longitude
  3493. );
  3494. $client = new Client(['verify' => false]);
  3495. $response = $client->request('GET', $url, [
  3496. 'auth' => [$this->username, $this->password],
  3497. ]);
  3498. $statusCode = $response->getStatusCode();
  3499. $data = json_decode($response->getBody(), true);
  3500. // Set the data to Redis cache
  3501. $this->redisCache->set($cacheKey, $data);
  3502. return $data;
  3503. } catch (GuzzleHttp\Exception\RequestException $e) {
  3504. return throw new \Exception($e->getMessage());
  3505. } catch (\Exception $e) {
  3506. return throw new \Exception($e->getMessage());
  3507. }
  3508. }
  3509. /**
  3510. * Wind by location
  3511. *
  3512. * @param string $time The type of data time (e.g., '2023-09-01')
  3513. * @param string $location The latitude and longitude of location
  3514. * @param string $format return type of json
  3515. * @throws \InvalidArgumentException If any parameter has an invalid data type
  3516. */
  3517. public function getWindByLocation(
  3518. string $time,
  3519. string $location,
  3520. string $format = 'json'
  3521. ) {
  3522. if (!is_string($time)) {
  3523. throw new \InvalidArgumentException('Invalid data type for $time. Expected string.');
  3524. }
  3525. if (!is_string($location)) {
  3526. throw new \InvalidArgumentException('Invalid data type for $location. Expected string.');
  3527. }
  3528. if (!is_string($format)) {
  3529. throw new \InvalidArgumentException('Invalid data type for $format. Expected string.');
  3530. }
  3531. $cacheKey = sprintf('wind_location_%s_%s', $time, $location);
  3532. // Try to get the data from Redis cache
  3533. $cachedData = $this->redisCache->get($cacheKey);
  3534. if ($cachedData !== null) {
  3535. // Return the cached data if available
  3536. return $cachedData;
  3537. }
  3538. try {
  3539. // If cache is empty, get the data from the API
  3540. $client = new Client(['verify' => false]);
  3541. $url = sprintf(
  3542. '%s/%s/wind_speed_u_10m:ms,wind_speed_v_10m:ms/%s/%s',
  3543. $this->apiBaseUrl,
  3544. $time,
  3545. $location,
  3546. $format
  3547. );
  3548. $response = $client->request('GET', $url, [
  3549. 'auth' => [$this->username, $this->password],
  3550. ]);
  3551. $data = json_decode($response->getBody(), true);
  3552. // Set the data to Redis cache
  3553. $this->redisCache->set($cacheKey, $data);
  3554. return $data;
  3555. } catch (GuzzleHttp\Exception\RequestException $e) {
  3556. return throw new \Exception($e->getMessage());
  3557. } catch (\Exception $e) {
  3558. return throw new \Exception($e->getMessage());
  3559. }
  3560. }
  3561. /**
  3562. * Water Temperature
  3563. *
  3564. * @param string $query The type of data request (e.g., 't_sea_sfc:ms')
  3565. * @param string $format return type of json
  3566. * @param string $date The type of data date (e.g., '2023-09-01')
  3567. * @param array $coordinates The latitude and longitude of location in array
  3568. * @param string $dimensions type of data (e.g., '500x300')
  3569. * @param string $modal type of data (e.g., 'noaa-hycom', 'ecmwf-cmems', 'noaa-hycom')
  3570. * @throws \InvalidArgumentException If any parameter has an invalid data type
  3571. */
  3572. public function getWaterTempereture(
  3573. string $query,
  3574. array $coordinates,
  3575. string $dimensions,
  3576. string $format,
  3577. string $date,
  3578. string $modal
  3579. ) {
  3580. try {
  3581. if (empty($query)) {
  3582. throw new \InvalidArgumentException('Invalid query');
  3583. }
  3584. if (empty($date)) {
  3585. throw new \InvalidArgumentException('Invalid date');
  3586. }
  3587. $date = date('Y-m-d\TH:i:s\Z', (strtotime($date)));
  3588. foreach ($coordinates as $coordinate) {
  3589. if (count($coordinate) < 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  3590. throw new \InvalidArgumentException('Invalid coordinates');
  3591. }
  3592. }
  3593. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  3594. $cacheKey = \App\Lib\Utility::generateKey($query, $coordinates, $dimensions, $date, $format, $modal);
  3595. // Try to get the data from Redis cache
  3596. $cachedData = $this->redisCache->get($cacheKey);
  3597. if ($cachedData !== null) {
  3598. // Return the cached data if available
  3599. return $cachedData;
  3600. }
  3601. // If cache is empty, get the data from the API
  3602. $client = new Client(['verify' => false]);
  3603. $url = sprintf(
  3604. '%s/%s/%s/%s:%s/%s?%s',
  3605. $this->apiBaseUrl,
  3606. $date,
  3607. $query,
  3608. $coordinateString,
  3609. $dimensions,
  3610. $format,
  3611. $modal
  3612. );
  3613. $response = $client->request('GET', $url, [
  3614. 'auth' => [$this->username, $this->password],
  3615. ]);
  3616. if ($format == "json") {
  3617. $data = json_decode($response->getBody(), true);
  3618. } else {
  3619. $data = $response->getBody();
  3620. }
  3621. // Set the data to Redis cache
  3622. $this->redisCache->set($cacheKey, $data);
  3623. return $data;
  3624. } catch (GuzzleHttp\Exception\RequestException $e) {
  3625. return throw new \Exception($e->getMessage());
  3626. } catch (\Exception $e) {
  3627. return throw new \Exception($e->getMessage());
  3628. }
  3629. }
  3630. /**
  3631. * Ocean Current
  3632. *
  3633. * @param string $query The type of data request (e.g., 'ocean_current_speed_2m:ms')
  3634. * @param string $format return type of json
  3635. * @param string $date The type of data date (e.g., '2023-09-01')
  3636. * @param array $coordinates The latitude and longitude of location in array
  3637. * @param string $dimensions type of data (e.g., '500x300')
  3638. * @throws \InvalidArgumentException If any parameter has an invalid data type
  3639. */
  3640. public function getOceanCurrent(
  3641. string $query,
  3642. array $coordinates,
  3643. string $dimensions,
  3644. string $format,
  3645. string $date
  3646. ) {
  3647. try {
  3648. if (empty($query)) {
  3649. throw new \InvalidArgumentException('Invalid query');
  3650. }
  3651. if (empty($date)) {
  3652. throw new \InvalidArgumentException('Invalid dates');
  3653. }
  3654. $date = date('Y-m-d\TH:i:s\Z', (strtotime($date)));
  3655. foreach ($coordinates as $coordinate) {
  3656. if (count($coordinate) < 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  3657. throw new \InvalidArgumentException('Invalid coordinates');
  3658. }
  3659. }
  3660. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  3661. $cacheKey = \App\Lib\Utility::generateKey($query, $coordinates, $dimensions, $date, $format);
  3662. // Try to get the data from Redis cache
  3663. $cachedData = $this->redisCache->get($cacheKey);
  3664. if ($cachedData !== null) {
  3665. // Return the cached data if available
  3666. return $cachedData;
  3667. }
  3668. // If cache is empty, get the data from the API
  3669. $client = new Client(['verify' => false]);
  3670. $url = sprintf(
  3671. '%s/%s/%s/%s:%s/%s',
  3672. $this->apiBaseUrl,
  3673. $date,
  3674. $query,
  3675. $coordinateString,
  3676. $dimensions,
  3677. $format
  3678. );
  3679. $response = $client->request('GET', $url, [
  3680. 'auth' => [$this->username, $this->password],
  3681. ]);
  3682. if ($format == "json") {
  3683. $data = json_decode($response->getBody(), true);
  3684. } else {
  3685. $data = $response->getBody();
  3686. }
  3687. // Set the data to Redis cache
  3688. $this->redisCache->set($cacheKey, $data);
  3689. return $data;
  3690. } catch (GuzzleHttp\Exception\RequestException $e) {
  3691. return throw new \Exception($e->getMessage());
  3692. } catch (\Exception $e) {
  3693. return throw new \Exception($e->getMessage());
  3694. }
  3695. }
  3696. /**
  3697. * Oceanic Tides
  3698. *
  3699. * @param string $query The type of data request (e.g., 'tidal_amplitude:cm')
  3700. * @param string $format return type of json
  3701. * @param string $date The type of data date (e.g., '2023-09-01')
  3702. * @param array $latitude The latitude of location
  3703. * @param array $longitude The longitude of location
  3704. * @param string $dimensions type of data (e.g., '500x300')
  3705. * @throws \InvalidArgumentException If any parameter has an invalid data type
  3706. */
  3707. public function getOceanicTides(
  3708. string $query,
  3709. string $latitude,
  3710. string $longitude,
  3711. string $date,
  3712. string $format,
  3713. string $resolution,
  3714. string $model
  3715. ) {
  3716. try {
  3717. if (empty($query)) {
  3718. throw new \InvalidArgumentException('Invalid query');
  3719. }
  3720. if (empty($date)) {
  3721. throw new \InvalidArgumentException('Invalid dates');
  3722. }
  3723. if (!is_numeric($latitude) || $latitude < -90 || $latitude > 90) {
  3724. throw new \InvalidArgumentException('Invalid latitude');
  3725. }
  3726. if (!is_numeric($longitude) || $longitude < -180 || $longitude > 180) {
  3727. throw new \InvalidArgumentException('Invalid longitude');
  3728. }
  3729. $date = date('Y-m-d\TH:i:s\Z', (strtotime($date)));
  3730. $cacheKey = \App\Lib\Utility::generateKey($query, $latitude, $longitude, $date, $format, $resolution, $model);
  3731. // Try to get the data from Redis cache
  3732. $cachedData = $this->redisCache->get($cacheKey);
  3733. if ($cachedData !== null) {
  3734. // Return the cached data if available
  3735. return $cachedData;
  3736. }
  3737. // If cache is empty, get the data from the API
  3738. $client = new Client(['verify' => false]);
  3739. $url = sprintf(
  3740. '%s/%s%s/%s/%s,%s/%s?%s',
  3741. $this->apiBaseUrl,
  3742. $date,
  3743. $resolution,
  3744. $query,
  3745. $latitude,
  3746. $longitude,
  3747. $format,
  3748. $model
  3749. );
  3750. $response = $client->request('GET', $url, [
  3751. 'auth' => [$this->username, $this->password],
  3752. ]);
  3753. if ($format == "json") {
  3754. $data = json_decode($response->getBody(), true);
  3755. } else {
  3756. $data = $response->getBody();
  3757. }
  3758. // Set the data to Redis cache
  3759. $this->redisCache->set($cacheKey, $data);
  3760. return $data;
  3761. } catch (GuzzleHttp\Exception\RequestException $e) {
  3762. return throw new \Exception($e->getMessage());
  3763. } catch (\Exception $e) {
  3764. return throw new \Exception($e->getMessage());
  3765. }
  3766. }
  3767. /**
  3768. * Drought Index
  3769. *
  3770. * @param string $query The type of data request (e.g., 'drought_index:idx/Asia')
  3771. * @param string $format return type of json or html_map
  3772. * @param string $startDate The type of data date (e.g., '2023-09-01')
  3773. * @param string $longitude The longitude of location
  3774. * @param string $latitude The latitude of location
  3775. * @throws \InvalidArgumentException If any parameter has an invalid data type
  3776. */
  3777. public function getDroughtIndex(
  3778. string $query,
  3779. string $latitude,
  3780. string $longitude,
  3781. string $date,
  3782. string $format,
  3783. ) {
  3784. try {
  3785. if (empty($query)) {
  3786. throw new \InvalidArgumentException('Invalid query');
  3787. }
  3788. if (empty($date)) {
  3789. throw new \InvalidArgumentException('Invalid dates');
  3790. }
  3791. if (!is_numeric($latitude) || $latitude < -90 || $latitude > 90) {
  3792. throw new \InvalidArgumentException('Invalid latitude');
  3793. }
  3794. if (!is_numeric($longitude) || $longitude < -180 || $longitude > 180) {
  3795. throw new \InvalidArgumentException('Invalid longitude');
  3796. }
  3797. $date = date('Y-m-d\TH:i:s\Z', (strtotime($date)));
  3798. $cacheKey = \App\Lib\Utility::generateKey($query, $latitude, $longitude, $date, $format);
  3799. // Try to get the data from Redis cache
  3800. $cachedData = $this->redisCache->get($cacheKey);
  3801. if ($cachedData !== null) {
  3802. // Return the cached data if available
  3803. return $cachedData;
  3804. }
  3805. // If cache is empty, get the data from the API
  3806. $client = new Client(['verify' => false]);
  3807. $url = sprintf(
  3808. '%s/%s/%s:%s,%s/%s',
  3809. $this->apiBaseUrl,
  3810. $date,
  3811. $query,
  3812. $latitude,
  3813. $longitude,
  3814. $format
  3815. );
  3816. $response = $client->request('GET', $url, [
  3817. 'auth' => [$this->username, $this->password],
  3818. ]);
  3819. if ($format == "json") {
  3820. $data = json_decode($response->getBody(), true);
  3821. } else {
  3822. $data = $response->getBody();
  3823. }
  3824. // Set the data to Redis cache
  3825. $this->redisCache->set($cacheKey, $data);
  3826. return $data;
  3827. } catch (GuzzleHttp\Exception\RequestException $e) {
  3828. return throw new \Exception($e->getMessage());
  3829. } catch (\Exception $e) {
  3830. return throw new \Exception($e->getMessage());
  3831. }
  3832. }
  3833. /**
  3834. * Fetches the hourly weather forecast data for a given latitude, longitude, and hour
  3835. *
  3836. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  3837. * @param string $hourly The hour for which forecast is required in 24-hour format
  3838. * @return array The hourly weather forecast data in JSON format
  3839. */
  3840. public function getHourlyForecastWithoutSymbols(array $coordinates, string $startDate, string $endDate, int $hour, string $model)
  3841. {
  3842. try {
  3843. if (count($coordinates) > 1) {
  3844. // Validate the input parameters
  3845. foreach ($coordinates as $coordinate) {
  3846. if (count($coordinate) < 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  3847. throw new \InvalidArgumentException('Invalid coordinates');
  3848. }
  3849. }
  3850. if (empty($startDate) || empty($endDate)) {
  3851. throw new \InvalidArgumentException('Invalid dates');
  3852. }
  3853. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  3854. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  3855. // Create a Redis key for the cache
  3856. $cacheKey = sprintf('cn_hourly_forecast_%s_%s_%s', implode('_', array_map(function ($coordinate) {
  3857. return implode('_', $coordinate);
  3858. }, $coordinates)), (($startDate) . '-' . ($endDate)) . $model, $hour);
  3859. // Try to get the data from Redis cache
  3860. $cachedData = $this->redisCache->get($cacheKey);
  3861. if ($cachedData !== null) {
  3862. // Return the cached data if available
  3863. return $cachedData;
  3864. }
  3865. // If cache is empty, get the data from the API
  3866. $client = new Client(['verify' => false]);
  3867. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  3868. $url1 = sprintf(
  3869. '%s/%s--%s:PT%sH/t_2m:C,wind_speed_10m:kmh,wind_dir_mean_100m_2h:d,prob_precip_2h:p,precip_2h:mm/%+%/json?model=' . $model,
  3870. $this->apiBaseUrl,
  3871. $startDate,
  3872. $endDate,
  3873. $hour,
  3874. $coordinateString,
  3875. $coordinateString
  3876. );
  3877. } else {
  3878. if ($coordinates) {
  3879. $latitude = $coordinates[0][0];
  3880. $longitude = $coordinates[0][1];
  3881. }
  3882. // Validate the input parameters
  3883. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  3884. throw new \InvalidArgumentException('Invalid latitude');
  3885. }
  3886. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  3887. throw new \InvalidArgumentException('Invalid longitude');
  3888. }
  3889. if (empty($startDate) || empty($endDate)) {
  3890. throw new \InvalidArgumentException('Invalid dates');
  3891. }
  3892. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  3893. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  3894. // Create a Redis key for the cache
  3895. $cacheKey = sprintf('cn_hourly_forecast_%s_%s_%s', implode('_', array_map(function ($coordinate) {
  3896. return implode('_', $coordinate);
  3897. }, $coordinates)), ($startDate . '-' . $endDate) . $model, $hour);
  3898. // Try to get the data from Redis cache
  3899. $cachedData = $this->redisCache->get($cacheKey);
  3900. if ($cachedData !== null) {
  3901. // Return the cached data if available
  3902. return $cachedData;
  3903. }
  3904. // If cache is empty, get the data from the API
  3905. $client = new Client(['verify' => false]);
  3906. $url1 = sprintf(
  3907. '%s/%s--%s:PT%sH/t_2m:C,wind_speed_10m:kmh,wind_dir_mean_100m_2h:d,prob_precip_2h:p,precip_2h:mm/%s,%s/json?model=' . $model,
  3908. $this->apiBaseUrl,
  3909. $startDate,
  3910. $endDate,
  3911. $hour,
  3912. $latitude,
  3913. $longitude
  3914. );
  3915. }
  3916. $response = $client->request('GET', $url1, [
  3917. 'auth' => [$this->username, $this->password],
  3918. ]);
  3919. $statusCode = $response->getStatusCode();
  3920. $data1 = json_decode($response->getBody(), true);
  3921. // Set the data to Redis cache
  3922. $this->redisCache->set($cacheKey, $data1);
  3923. return $data1;
  3924. } catch (GuzzleHttp\Exception\RequestException $e) {
  3925. return throw new \Exception($e->getMessage());
  3926. } catch (\Exception $e) {
  3927. return throw new \Exception($e->getMessage());
  3928. }
  3929. }
  3930. /**
  3931. * Fetches the weather forecast data for a given latitude, longitude, and for selected data range
  3932. *
  3933. * @param array $coordinates An array of coordinates in the format [[lat1, long1], [lat2, long2], ...]
  3934. * @param int $days The number of days for which forecast is required
  3935. * @return array|null The weather forecast data in JSON format, or null if there was an error
  3936. */
  3937. public function getTempratureByParamsHourly(
  3938. array $coordinates,
  3939. string $startDate,
  3940. string $endDate,
  3941. string $parametersStr,
  3942. int $hour = 2,
  3943. string $model,
  3944. $timezone
  3945. ) {
  3946. try {
  3947. // Convert start and end dates to the specified timezone
  3948. $startDateObj = new \DateTime($startDate, $timezone);
  3949. $endDateObj = new \DateTime($endDate, $timezone);
  3950. // Format dates to ISO 8601 format for the API request
  3951. $startDate = $startDateObj->format('Y-m-d\TH:i:s.v\Z');
  3952. $endDate = $endDateObj->format('Y-m-d\TH:i:s.v\Z');
  3953. if (count($coordinates) > 1) {
  3954. // Validate the input parameters
  3955. foreach ($coordinates as $coordinate) {
  3956. if (count($coordinate) < 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  3957. throw new \InvalidArgumentException('Invalid coordinates');
  3958. }
  3959. }
  3960. // Create a Redis key for the cache
  3961. $cacheKey = sprintf('hourly_custom_noti_%s_%s_%s_%s', md5(implode('_', array_map(function ($coordinate) {
  3962. return implode('_', $coordinate);
  3963. }, $coordinates))), ($startDate . '-' . $endDate), $parametersStr, $model);
  3964. // Try to get the data from Redis cache
  3965. $cachedData = $this->redisCache->get($cacheKey);
  3966. if ($cachedData !== null) {
  3967. return $cachedData;
  3968. }
  3969. // Build the API URL
  3970. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  3971. $url = sprintf(
  3972. '%s/%s--%s:PT%sH/%s/%s+%s/json?source=%s',
  3973. $this->apiBaseUrl,
  3974. $startDate,
  3975. $endDate,
  3976. $hour,
  3977. $parametersStr,
  3978. $coordinateString,
  3979. $coordinateString,
  3980. $model
  3981. );
  3982. } else {
  3983. // Handle single coordinate case
  3984. if ($coordinates) {
  3985. $latitude = $coordinates[0][0];
  3986. $longitude = $coordinates[0][1];
  3987. }
  3988. if (empty($latitude) || empty($longitude)) {
  3989. throw new \InvalidArgumentException('Invalid coordinates');
  3990. }
  3991. // Create a Redis key for the cache
  3992. $cacheKey = sprintf('hourly_custom_noti_%s_%s_%s_%s', md5(implode('_', array_map(function ($coordinate) {
  3993. return implode('_', $coordinate);
  3994. }, $coordinates))), ($startDate . '-' . $endDate), $parametersStr, $model);
  3995. // Try to get the data from Redis cache
  3996. $cachedData = $this->redisCache->get($cacheKey);
  3997. if ($cachedData !== null) {
  3998. return $cachedData;
  3999. }
  4000. // Build the API URL for a single coordinate
  4001. $url = sprintf(
  4002. '%s/%s--%s:PT%sH/%s/%s,%s/json?source=%s',
  4003. $this->apiBaseUrl,
  4004. $startDate,
  4005. $endDate,
  4006. $hour,
  4007. $parametersStr,
  4008. $latitude,
  4009. $longitude,
  4010. $model
  4011. );
  4012. }
  4013. // Make the API request
  4014. $client = new Client(['verify' => false]);
  4015. $response = $client->request('GET', $url, [
  4016. 'auth' => [$this->username, $this->password],
  4017. ]);
  4018. $data = json_decode($response->getBody(), true);
  4019. // Cache the data in Redis
  4020. $this->redisCache->set($cacheKey, $data);
  4021. return $data;
  4022. } catch (GuzzleHttp\Exception\RequestException $e) {
  4023. throw new \Exception($e->getMessage());
  4024. } catch (\Exception $e) {
  4025. throw new \Exception($e->getMessage());
  4026. }
  4027. }
  4028. /**
  4029. * Tidal Amplitude
  4030. *
  4031. * @param string $hourly The hour for which forecast is required in 24-hour format
  4032. * @param string $unit The type of data request unit (e.g., 'cm')
  4033. * @param string $format return type of json
  4034. * @param string $startDate The type of data date (e.g., '2023-09-01')
  4035. * @param string $endDate The type of data date (e.g., '2023-09-01')
  4036. * @param array $coordinates The latitude and longitude of location
  4037. * @param string $model type of data Api return (e.g., 'mm-tides')
  4038. * @throws \InvalidArgumentException If any parameter has an invalid data type
  4039. */
  4040. public function getTidalAmplitudes(int $hour, $startDate, $endDate, array $coordinates, $unit, $model, $format)
  4041. {
  4042. try {
  4043. if (count($coordinates) > 1) {
  4044. // Validate the input parameters
  4045. foreach ($coordinates as $coordinate) {
  4046. if (count($coordinate) < 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  4047. throw new \InvalidArgumentException('Invalid coordinates');
  4048. }
  4049. }
  4050. if (empty($startDate) || empty($endDate)) {
  4051. throw new \InvalidArgumentException('Invalid dates');
  4052. }
  4053. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  4054. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  4055. $cacheKey = \App\Lib\Utility::generateKey($hour, $startDate, $endDate, $coordinates, $unit, $model, $format);
  4056. // Try to get the data from Redis cache
  4057. $cachedData = $this->redisCache->get($cacheKey);
  4058. if ($cachedData !== null) {
  4059. // Return the cached data if available
  4060. return $cachedData;
  4061. }
  4062. // If cache is empty, get the data from the API
  4063. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  4064. $client = new Client(['verify' => false]);
  4065. $url = sprintf(
  4066. '%s/%s--%s:PT%sH/tidal_amplitude:%s/%s/%s?model=%s',
  4067. $this->apiBaseUrl,
  4068. $startDate,
  4069. $endDate,
  4070. $hour,
  4071. $unit,
  4072. $coordinateString,
  4073. $format,
  4074. $model
  4075. );
  4076. $response = $client->request('GET', $url, [
  4077. 'auth' => [$this->username, $this->password],
  4078. ]);
  4079. $statusCode = $response->getStatusCode();
  4080. $data = json_decode($response->getBody(), true);
  4081. if ($statusCode != 200) {
  4082. return $this->createErrorResponse($statusCode);
  4083. }
  4084. // Set the data to Redis cache
  4085. $this->redisCache->set($cacheKey, $data);
  4086. return $data;
  4087. } else {
  4088. if ($coordinates) {
  4089. $latitude = $coordinates[0][0];
  4090. $longitude = $coordinates[0][1];
  4091. }
  4092. // Validate the input parameters
  4093. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  4094. throw new \InvalidArgumentException('Invalid latitude');
  4095. }
  4096. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  4097. throw new \InvalidArgumentException('Invalid longitude');
  4098. }
  4099. if (empty($startDate)) {
  4100. throw new \InvalidArgumentException('Invalid startDate');
  4101. }
  4102. if (empty($endDate)) {
  4103. throw new \InvalidArgumentException('Invalid endDate');
  4104. }
  4105. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  4106. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  4107. // Create a Redis key for the cache
  4108. $cacheKey = \App\Lib\Utility::generateKey($hour, $startDate, $endDate, $unit, $latitude, $longitude, $model, $format);
  4109. // Try to get the data from Redis cache
  4110. $cachedData = $this->redisCache->get($cacheKey);
  4111. if ($cachedData !== null) {
  4112. // Return the cached data if available
  4113. return $cachedData;
  4114. }
  4115. // If cache is empty, get the data from the API
  4116. $client = new Client(['verify' => false]);
  4117. $url = sprintf(
  4118. '%s/%s--%s:PT%sH/tidal_amplitude:%s/%s,%s/%s?model=%s',
  4119. $this->apiBaseUrl,
  4120. $startDate,
  4121. $endDate,
  4122. $hour,
  4123. $unit,
  4124. $latitude,
  4125. $longitude,
  4126. $format,
  4127. $model
  4128. );
  4129. $response = $client->request('GET', $url, [
  4130. 'auth' => [$this->username, $this->password],
  4131. ]);
  4132. $statusCode = $response->getStatusCode();
  4133. $data = json_decode($response->getBody(), true);
  4134. if ($statusCode != 200) {
  4135. return $this->createErrorResponse($statusCode);
  4136. }
  4137. // Set the data to Redis cache
  4138. $this->redisCache->set($cacheKey, $data);
  4139. return $data;
  4140. }
  4141. } catch (GuzzleHttp\Exception\RequestException $e) {
  4142. return throw new \Exception($e->getMessage());
  4143. } catch (\Exception $e) {
  4144. return throw new \Exception($e->getMessage());
  4145. }
  4146. }
  4147. /**
  4148. * High and Low Tide Times
  4149. *
  4150. * @param string $hourly The hour for which forecast is required in 24-hour format
  4151. * @param string $format return type of json
  4152. * @param string $startDate The type of data date (e.g., '2023-09-01')
  4153. * @param string $endDate The type of data date (e.g., '2023-09-01')
  4154. * @param array $coordinates The latitude and longitude of location
  4155. * @param string $model type of data Api return (e.g., 'mm-tides')
  4156. * @throws \InvalidArgumentException If any parameter has an invalid data type
  4157. */
  4158. public function getHighLowTideTimes(int $hour, $startDate, $endDate, array $coordinates, $model, $format)
  4159. {
  4160. try {
  4161. if (count($coordinates) > 1) {
  4162. // Validate the input parameters
  4163. foreach ($coordinates as $coordinate) {
  4164. if (count($coordinate) < 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  4165. throw new \InvalidArgumentException('Invalid coordinates');
  4166. }
  4167. }
  4168. if (empty($startDate) || empty($endDate)) {
  4169. throw new \InvalidArgumentException('Invalid dates');
  4170. }
  4171. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  4172. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  4173. $cacheKey = \App\Lib\Utility::generateKey($hour, $startDate, $endDate, $coordinates, $model, $format);
  4174. // Try to get the data from Redis cache
  4175. $cachedData = $this->redisCache->get($cacheKey);
  4176. if ($cachedData !== null) {
  4177. // Return the cached data if available
  4178. return $cachedData;
  4179. }
  4180. // If cache is empty, get the data from the API
  4181. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  4182. $client = new Client(['verify' => false]);
  4183. $url = sprintf(
  4184. '%s/%s--%s:PT%sH/first_high_tide:sql,second_high_tide:sql,first_low_tide:sql,second_low_tide:sql/%s/%s?model=%s',
  4185. $this->apiBaseUrl,
  4186. $startDate,
  4187. $endDate,
  4188. $hour,
  4189. $coordinateString,
  4190. $format,
  4191. $model
  4192. );
  4193. $response = $client->request('GET', $url, [
  4194. 'auth' => [$this->username, $this->password],
  4195. ]);
  4196. $statusCode = $response->getStatusCode();
  4197. $data = json_decode($response->getBody(), true);
  4198. if ($statusCode != 200) {
  4199. return $this->createErrorResponse($statusCode);
  4200. }
  4201. // Set the data to Redis cache
  4202. $this->redisCache->set($cacheKey, $data);
  4203. return $data;
  4204. } else {
  4205. if ($coordinates) {
  4206. $latitude = $coordinates[0][0];
  4207. $longitude = $coordinates[0][1];
  4208. }
  4209. // Validate the input parameters
  4210. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  4211. throw new \InvalidArgumentException('Invalid latitude');
  4212. }
  4213. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  4214. throw new \InvalidArgumentException('Invalid longitude');
  4215. }
  4216. if (empty($startDate)) {
  4217. throw new \InvalidArgumentException('Invalid startDate');
  4218. }
  4219. if (empty($endDate)) {
  4220. throw new \InvalidArgumentException('Invalid endDate');
  4221. }
  4222. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  4223. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  4224. // Create a Redis key for the cache
  4225. $cacheKey = \App\Lib\Utility::generateKey($hour, $startDate, $endDate, $latitude, $longitude, $model, $format);
  4226. // Try to get the data from Redis cache
  4227. $cachedData = $this->redisCache->get($cacheKey);
  4228. if ($cachedData !== null) {
  4229. // Return the cached data if available
  4230. return $cachedData;
  4231. }
  4232. // If cache is empty, get the data from the API
  4233. $client = new Client(['verify' => false]);
  4234. $url = sprintf(
  4235. '%s/%s--%s:PT%sH/first_high_tide:sql,second_high_tide:sql,first_low_tide:sql,second_low_tide:sql/%s,%s/%s?model=%s',
  4236. $this->apiBaseUrl,
  4237. $startDate,
  4238. $endDate,
  4239. $hour,
  4240. $latitude,
  4241. $longitude,
  4242. $format,
  4243. $model
  4244. );
  4245. $response = $client->request('GET', $url, [
  4246. 'auth' => [$this->username, $this->password],
  4247. ]);
  4248. $statusCode = $response->getStatusCode();
  4249. $data = json_decode($response->getBody(), true);
  4250. if ($statusCode != 200) {
  4251. return $this->createErrorResponse($statusCode);
  4252. }
  4253. // Set the data to Redis cache
  4254. $this->redisCache->set($cacheKey, $data);
  4255. return $data;
  4256. }
  4257. } catch (GuzzleHttp\Exception\RequestException $e) {
  4258. return throw new \Exception($e->getMessage());
  4259. } catch (\Exception $e) {
  4260. return throw new \Exception($e->getMessage());
  4261. }
  4262. }
  4263. /**
  4264. * Significant Wave Height
  4265. *
  4266. * @param string $hourly The hour for which forecast is required in 24-hour format
  4267. * @param string $format return type of json
  4268. * @param string $startDate The type of data date (e.g., '2023-09-01')
  4269. * @param string $endDate The type of data date (e.g., '2023-09-01')
  4270. * @param array $coordinates The latitude and longitude of location
  4271. * @throws \InvalidArgumentException If any parameter has an invalid data type
  4272. */
  4273. public function getSignificantWaveHeight(int $hour, $startDate, $endDate, array $coordinates, $format)
  4274. {
  4275. try {
  4276. if (count($coordinates) > 1) {
  4277. // Validate the input parameters
  4278. foreach ($coordinates as $coordinate) {
  4279. if (count($coordinate) < 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  4280. throw new \InvalidArgumentException('Invalid coordinates');
  4281. }
  4282. }
  4283. if (empty($startDate) || empty($endDate)) {
  4284. throw new \InvalidArgumentException('Invalid dates');
  4285. }
  4286. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  4287. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  4288. $cacheKey = \App\Lib\Utility::generateKey($hour, $startDate, $endDate, $coordinate, $format);
  4289. // Try to get the data from Redis cache
  4290. $cachedData = $this->redisCache->get($cacheKey);
  4291. if ($cachedData !== null) {
  4292. // Return the cached data if available
  4293. return $cachedData;
  4294. }
  4295. // If cache is empty, get the data from the API
  4296. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  4297. $client = new Client(['verify' => false]);
  4298. $url = sprintf(
  4299. '%s/%s--%s:PT%sH/significant_height_wind_waves:m/%s/%s',
  4300. $this->apiBaseUrl,
  4301. $startDate,
  4302. $endDate,
  4303. $hour,
  4304. $coordinateString,
  4305. $format
  4306. );
  4307. $response = $client->request('GET', $url, [
  4308. 'auth' => [$this->username, $this->password],
  4309. ]);
  4310. $statusCode = $response->getStatusCode();
  4311. $data = json_decode($response->getBody(), true);
  4312. if ($statusCode != 200) {
  4313. return $this->createErrorResponse($statusCode);
  4314. }
  4315. // Set the data to Redis cache
  4316. $this->redisCache->set($cacheKey, $data);
  4317. return $data;
  4318. } else {
  4319. if ($coordinates) {
  4320. $latitude = $coordinates[0][0];
  4321. $longitude = $coordinates[0][1];
  4322. }
  4323. // Validate the input parameters
  4324. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  4325. throw new \InvalidArgumentException('Invalid latitude');
  4326. }
  4327. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  4328. throw new \InvalidArgumentException('Invalid longitude');
  4329. }
  4330. if (empty($startDate)) {
  4331. throw new \InvalidArgumentException('Invalid startDate');
  4332. }
  4333. if (empty($endDate)) {
  4334. throw new \InvalidArgumentException('Invalid endDate');
  4335. }
  4336. $startDate = date('Y-m-d\TH\Z', (strtotime($startDate)));
  4337. $endDate = date('Y-m-d\TH\Z', (strtotime($endDate)));
  4338. // Create a Redis key for the cache
  4339. $cacheKey = \App\Lib\Utility::generateKey($hour, $startDate, $endDate, $latitude, $longitude, $format);
  4340. // Try to get the data from Redis cache
  4341. $cachedData = $this->redisCache->get($cacheKey);
  4342. if ($cachedData !== null) {
  4343. // Return the cached data if available
  4344. return $cachedData;
  4345. }
  4346. // If cache is empty, get the data from the API
  4347. $client = new Client(['verify' => false]);
  4348. $url = sprintf(
  4349. '%s/%s--%s:PT%sH/significant_height_wind_waves:m/%s,%s/%s',
  4350. $this->apiBaseUrl,
  4351. $startDate,
  4352. $endDate,
  4353. $hour,
  4354. $latitude,
  4355. $longitude,
  4356. $format
  4357. );
  4358. $response = $client->request('GET', $url, [
  4359. 'auth' => [$this->username, $this->password],
  4360. ]);
  4361. $statusCode = $response->getStatusCode();
  4362. $data = json_decode($response->getBody(), true);
  4363. if ($statusCode != 200) {
  4364. return $this->createErrorResponse($statusCode);
  4365. }
  4366. // Set the data to Redis cache
  4367. $this->redisCache->set($cacheKey, $data);
  4368. return $data;
  4369. }
  4370. } catch (GuzzleHttp\Exception\RequestException $e) {
  4371. return throw new \Exception($e->getMessage());
  4372. } catch (\Exception $e) {
  4373. return throw new \Exception($e->getMessage());
  4374. }
  4375. }
  4376. /**
  4377. * Surge Amplitude
  4378. *
  4379. * @param string $hourly The hour for which forecast is required in 24-hour format
  4380. * @param string $format return type of json
  4381. * @param string $startDate The type of data date (e.g., '2023-09-01')
  4382. * @param string $endDate The type of data date (e.g., '2023-09-01')
  4383. * @param string $unit The type of data unit (e.g., 'cm')
  4384. * @param array $coordinates The latitude and longitude of location
  4385. * @param string $model type of data Api return (e.g., 'mix')
  4386. * @throws \InvalidArgumentException If any parameter has an invalid data type
  4387. */
  4388. public function getSurgeAmplitude(int $hour, $startDate, $endDate, array $coordinates, $unit, $model, $format)
  4389. {
  4390. try {
  4391. if (count($coordinates) > 1) {
  4392. // Validate the input parameters
  4393. foreach ($coordinates as $coordinate) {
  4394. if (count($coordinate) < 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  4395. throw new \InvalidArgumentException('Invalid coordinates');
  4396. }
  4397. }
  4398. if (empty($startDate) || empty($endDate)) {
  4399. throw new \InvalidArgumentException('Invalid dates');
  4400. }
  4401. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  4402. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  4403. $cacheKey = \App\Lib\Utility::generateKey($hour, $startDate, $endDate, $coordinate, $unit, $model, $format);
  4404. // Try to get the data from Redis cache
  4405. $cachedData = $this->redisCache->get($cacheKey);
  4406. if ($cachedData !== null) {
  4407. // Return the cached data if available
  4408. return $cachedData;
  4409. }
  4410. // If cache is empty, get the data from the API
  4411. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  4412. $client = new Client(['verify' => false]);
  4413. $url = sprintf(
  4414. '%s/%s--%s:PT%sH/surge_amplitude:%s/%s/%s?model=%s',
  4415. $this->apiBaseUrl,
  4416. $startDate,
  4417. $endDate,
  4418. $hour,
  4419. $unit,
  4420. $coordinateString,
  4421. $format,
  4422. $model
  4423. );
  4424. $response = $client->request('GET', $url, [
  4425. 'auth' => [$this->username, $this->password],
  4426. ]);
  4427. $statusCode = $response->getStatusCode();
  4428. $data = json_decode($response->getBody(), true);
  4429. if ($statusCode != 200) {
  4430. return $this->createErrorResponse($statusCode);
  4431. }
  4432. // Set the data to Redis cache
  4433. $this->redisCache->set($cacheKey, $data);
  4434. return $data;
  4435. } else {
  4436. if ($coordinates) {
  4437. $latitude = $coordinates[0][0];
  4438. $longitude = $coordinates[0][1];
  4439. }
  4440. // Validate the input parameters
  4441. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  4442. throw new \InvalidArgumentException('Invalid latitude');
  4443. }
  4444. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  4445. throw new \InvalidArgumentException('Invalid longitude');
  4446. }
  4447. if (empty($startDate)) {
  4448. throw new \InvalidArgumentException('Invalid startDate');
  4449. }
  4450. if (empty($endDate)) {
  4451. throw new \InvalidArgumentException('Invalid endDate');
  4452. }
  4453. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  4454. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  4455. // Create a Redis key for the cache
  4456. $cacheKey = \App\Lib\Utility::generateKey($hour, $startDate, $endDate, $latitude, $longitude, $unit, $model, $format);
  4457. // Try to get the data from Redis cache
  4458. $cachedData = $this->redisCache->get($cacheKey);
  4459. if ($cachedData !== null) {
  4460. // Return the cached data if available
  4461. return $cachedData;
  4462. }
  4463. // If cache is empty, get the data from the API
  4464. $client = new Client(['verify' => false]);
  4465. $url = sprintf(
  4466. '%s/%s--%s:PT%sH/surge_amplitude:%s/%s,%s/%s?model=%s',
  4467. $this->apiBaseUrl,
  4468. $startDate,
  4469. $endDate,
  4470. $hour,
  4471. $unit,
  4472. $latitude,
  4473. $longitude,
  4474. $format,
  4475. $model
  4476. );
  4477. $response = $client->request('GET', $url, [
  4478. 'auth' => [$this->username, $this->password],
  4479. ]);
  4480. $statusCode = $response->getStatusCode();
  4481. $data = json_decode($response->getBody(), true);
  4482. if ($statusCode != 200) {
  4483. return $this->createErrorResponse($statusCode);
  4484. }
  4485. // Set the data to Redis cache
  4486. $this->redisCache->set($cacheKey, $data);
  4487. return $data;
  4488. }
  4489. } catch (GuzzleHttp\Exception\RequestException $e) {
  4490. return throw new \Exception($e->getMessage());
  4491. } catch (\Exception $e) {
  4492. return throw new \Exception($e->getMessage());
  4493. }
  4494. }
  4495. /**
  4496. * Heat index
  4497. *
  4498. * @param string $hourly The hour for which forecast is required in 24-hour format
  4499. * @param string $format return type of json
  4500. * @param string $unit request unit type (e.g, 'C')
  4501. * @param string $startDate The type of data date (e.g., '2023-09-01')
  4502. * @param string $endDate The type of data date (e.g., '2023-09-01')
  4503. * @param array $coordinates The latitude and longitude of location
  4504. * @param string $model type of data Api return (e.g., 'mix')
  4505. * @throws \InvalidArgumentException If any parameter has an invalid data type
  4506. */
  4507. public function getHeatIndex(int $hour, $startDate, $endDate, array $coordinates, $unit, $format, $model)
  4508. {
  4509. try {
  4510. if (count($coordinates) > 1) {
  4511. // Validate the input parameters
  4512. foreach ($coordinates as $coordinate) {
  4513. if (count($coordinate) < 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  4514. throw new \InvalidArgumentException('Invalid coordinates');
  4515. }
  4516. }
  4517. if (empty($startDate) || empty($endDate)) {
  4518. throw new \InvalidArgumentException('Invalid dates');
  4519. }
  4520. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  4521. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  4522. $cacheKey = \App\Lib\Utility::generateKey($hour, $startDate, $endDate, $coordinate, $model, $unit, $format);
  4523. // Try to get the data from Redis cache
  4524. $cachedData = $this->redisCache->get($cacheKey);
  4525. if ($cachedData !== null) {
  4526. // Return the cached data if available
  4527. return $cachedData;
  4528. }
  4529. // If cache is empty, get the data from the API
  4530. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  4531. $client = new Client(['verify' => false]);
  4532. $url = sprintf(
  4533. '%s/%s--%s:PT%sH/heat_index:%s,t_2m:%s/%s/%s?model=%s',
  4534. $this->apiBaseUrl,
  4535. $startDate,
  4536. $endDate,
  4537. $hour,
  4538. $unit,
  4539. $unit,
  4540. $coordinateString,
  4541. $format,
  4542. $model
  4543. );
  4544. $response = $client->request('GET', $url, [
  4545. 'auth' => [$this->username, $this->password],
  4546. ]);
  4547. $statusCode = $response->getStatusCode();
  4548. $data = json_decode($response->getBody(), true);
  4549. if ($statusCode != 200) {
  4550. return $this->createErrorResponse($statusCode);
  4551. }
  4552. // Set the data to Redis cache
  4553. $this->redisCache->set($cacheKey, $data);
  4554. return $data;
  4555. } else {
  4556. if ($coordinates) {
  4557. $latitude = $coordinates[0][0];
  4558. $longitude = $coordinates[0][1];
  4559. }
  4560. // Validate the input parameters
  4561. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  4562. throw new \InvalidArgumentException('Invalid latitude');
  4563. }
  4564. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  4565. throw new \InvalidArgumentException('Invalid longitude');
  4566. }
  4567. if (empty($startDate)) {
  4568. throw new \InvalidArgumentException('Invalid startDate');
  4569. }
  4570. if (empty($endDate)) {
  4571. throw new \InvalidArgumentException('Invalid endDate');
  4572. }
  4573. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  4574. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  4575. // Create a Redis key for the cache
  4576. $cacheKey = \App\Lib\Utility::generateKey($hour, $startDate, $endDate, $latitude, $longitude, $unit, $model, $format);
  4577. // Try to get the data from Redis cache
  4578. $cachedData = $this->redisCache->get($cacheKey);
  4579. if ($cachedData !== null) {
  4580. // Return the cached data if available
  4581. return $cachedData;
  4582. }
  4583. // If cache is empty, get the data from the API
  4584. $client = new Client(['verify' => false]);
  4585. $url = sprintf(
  4586. '%s/%s--%s:PT%sH/heat_index:%s,t_2m:%s/%s,%s/%s?model=%s',
  4587. $this->apiBaseUrl,
  4588. $startDate,
  4589. $endDate,
  4590. $hour,
  4591. $unit,
  4592. $unit,
  4593. $latitude,
  4594. $longitude,
  4595. $format,
  4596. $model
  4597. );
  4598. $response = $client->request('GET', $url, [
  4599. 'auth' => [$this->username, $this->password],
  4600. ]);
  4601. $statusCode = $response->getStatusCode();
  4602. $data = json_decode($response->getBody(), true);
  4603. if ($statusCode != 200) {
  4604. return $this->createErrorResponse($statusCode);
  4605. }
  4606. // Set the data to Redis cache
  4607. $this->redisCache->set($cacheKey, $data);
  4608. return $data;
  4609. }
  4610. } catch (GuzzleHttp\Exception\RequestException $e) {
  4611. return throw new \Exception($e->getMessage());
  4612. } catch (\Exception $e) {
  4613. return throw new \Exception($e->getMessage());
  4614. }
  4615. }
  4616. /**
  4617. * Atmospheric Density
  4618. *
  4619. * @param string $hourly The hour for which forecast is required in 24-hour format
  4620. * @param string $format return type of json
  4621. * @param string $level The type of level request (e.g., '2m')
  4622. * @param string $unit The type of unit request (e.g., 'kgm3')
  4623. * @param string $startDate The type of data date (e.g., '2023-09-01')
  4624. * @param string $endDate The type of data date (e.g., '2023-09-01')
  4625. * @param array $coordinates The latitude and longitude of location
  4626. * @throws \InvalidArgumentException If any parameter has an invalid data type
  4627. */
  4628. public function getAirdensity(int $hour, $startDate, $endDate, array $coordinates, $level, $unit, $format)
  4629. {
  4630. try {
  4631. if (count($coordinates) > 1) {
  4632. // Validate the input parameters
  4633. foreach ($coordinates as $coordinate) {
  4634. if (count($coordinate) < 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  4635. throw new \InvalidArgumentException('Invalid coordinates');
  4636. }
  4637. }
  4638. if (empty($startDate) || empty($endDate)) {
  4639. throw new \InvalidArgumentException('Invalid dates');
  4640. }
  4641. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  4642. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  4643. $cacheKey = \App\Lib\Utility::generateKey($hour, $startDate, $endDate, $coordinates, $level, $unit, $format);
  4644. // Try to get the data from Redis cache
  4645. $cachedData = $this->redisCache->get($cacheKey);
  4646. if ($cachedData !== null) {
  4647. // Return the cached data if available
  4648. return $cachedData;
  4649. }
  4650. // If cache is empty, get the data from the API
  4651. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  4652. $client = new Client(['verify' => false]);
  4653. $url = sprintf(
  4654. '%s/%s--%s:PT%sH/air_density_%s:%s/%s/%s',
  4655. $this->apiBaseUrl,
  4656. $startDate,
  4657. $endDate,
  4658. $hour,
  4659. $level,
  4660. $unit,
  4661. $coordinateString,
  4662. $format
  4663. );
  4664. $response = $client->request('GET', $url, [
  4665. 'auth' => [$this->username, $this->password],
  4666. ]);
  4667. $statusCode = $response->getStatusCode();
  4668. $data = json_decode($response->getBody(), true);
  4669. if ($statusCode != 200) {
  4670. return $this->createErrorResponse($statusCode);
  4671. }
  4672. // Set the data to Redis cache
  4673. $this->redisCache->set($cacheKey, $data);
  4674. return $data;
  4675. } else {
  4676. if ($coordinates) {
  4677. $latitude = $coordinates[0][0];
  4678. $longitude = $coordinates[0][1];
  4679. }
  4680. // Validate the input parameters
  4681. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  4682. throw new \InvalidArgumentException('Invalid latitude');
  4683. }
  4684. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  4685. throw new \InvalidArgumentException('Invalid longitude');
  4686. }
  4687. if (empty($startDate)) {
  4688. throw new \InvalidArgumentException('Invalid startDate');
  4689. }
  4690. if (empty($endDate)) {
  4691. throw new \InvalidArgumentException('Invalid endDate');
  4692. }
  4693. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  4694. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  4695. $cacheKey = \App\Lib\Utility::generateKey($hour, $startDate, $endDate, $latitude, $longitude, $level, $unit, $format);
  4696. // Try to get the data from Redis cache
  4697. $cachedData = $this->redisCache->get($cacheKey);
  4698. if ($cachedData !== null) {
  4699. // Return the cached data if available
  4700. return $cachedData;
  4701. }
  4702. // If cache is empty, get the data from the API
  4703. $client = new Client(['verify' => false]);
  4704. $url = sprintf(
  4705. '%s/%s--%s:PT%sH/air_density_%s:%s/%s,%s/%s',
  4706. $this->apiBaseUrl,
  4707. $startDate,
  4708. $endDate,
  4709. $hour,
  4710. $level,
  4711. $unit,
  4712. $latitude,
  4713. $longitude,
  4714. $format
  4715. );
  4716. $response = $client->request('GET', $url, [
  4717. 'auth' => [$this->username, $this->password],
  4718. ]);
  4719. $statusCode = $response->getStatusCode();
  4720. $data = json_decode($response->getBody(), true);
  4721. if ($statusCode != 200) {
  4722. return $this->createErrorResponse($statusCode);
  4723. }
  4724. // Set the data to Redis cache
  4725. $this->redisCache->set($cacheKey, $data);
  4726. return $data;
  4727. }
  4728. } catch (GuzzleHttp\Exception\RequestException $e) {
  4729. return throw new \Exception($e->getMessage());
  4730. } catch (\Exception $e) {
  4731. return throw new \Exception($e->getMessage());
  4732. }
  4733. }
  4734. /**
  4735. * Soil Moisture Index
  4736. *
  4737. * @param string $hourly The hour for which forecast is required in 24-hour format
  4738. * @param string $format return type of json
  4739. * @param string $level The type of level request (e.g., '2m')
  4740. * @param string $unit The type of unit request (e.g., 'kgm3')
  4741. * @param string $startDate The type of data date (e.g., '2023-09-01')
  4742. * @param string $endDate The type of data date (e.g., '2023-09-01')
  4743. * @param array $coordinates The latitude and longitude of location
  4744. * @throws \InvalidArgumentException If any parameter has an invalid data type
  4745. */
  4746. public function getSoilMoistureIndex(int $hour, $startDate, $endDate, array $coordinates, $level, $unit, $format)
  4747. {
  4748. try {
  4749. if (count($coordinates) > 1) {
  4750. // Validate the input parameters
  4751. foreach ($coordinates as $coordinate) {
  4752. if (count($coordinate) < 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  4753. throw new \InvalidArgumentException('Invalid coordinates');
  4754. }
  4755. }
  4756. if (empty($startDate) || empty($endDate)) {
  4757. throw new \InvalidArgumentException('Invalid dates');
  4758. }
  4759. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  4760. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  4761. $cacheKey = \App\Lib\Utility::generateKey($hour, $startDate, $endDate, $coordinates, $level, $unit, $format);
  4762. // Try to get the data from Redis cache
  4763. $cachedData = $this->redisCache->get($cacheKey);
  4764. if ($cachedData !== null) {
  4765. // Return the cached data if available
  4766. return $cachedData;
  4767. }
  4768. // If cache is empty, get the data from the API
  4769. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  4770. $client = new Client(['verify' => false]);
  4771. $url = sprintf(
  4772. '%s/%s--%s:PT%sH/soil_moisture_index_%s:%s/%s/%s',
  4773. $this->apiBaseUrl,
  4774. $startDate,
  4775. $endDate,
  4776. $hour,
  4777. $level,
  4778. $unit,
  4779. $coordinateString,
  4780. $format
  4781. );
  4782. $response = $client->request('GET', $url, [
  4783. 'auth' => [$this->username, $this->password],
  4784. ]);
  4785. $statusCode = $response->getStatusCode();
  4786. $data = json_decode($response->getBody(), true);
  4787. if ($statusCode != 200) {
  4788. return $this->createErrorResponse($statusCode);
  4789. }
  4790. // Set the data to Redis cache
  4791. $this->redisCache->set($cacheKey, $data);
  4792. return $data;
  4793. } else {
  4794. if ($coordinates) {
  4795. $latitude = $coordinates[0][0];
  4796. $longitude = $coordinates[0][1];
  4797. }
  4798. // Validate the input parameters
  4799. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  4800. throw new \InvalidArgumentException('Invalid latitude');
  4801. }
  4802. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  4803. throw new \InvalidArgumentException('Invalid longitude');
  4804. }
  4805. if (empty($startDate)) {
  4806. throw new \InvalidArgumentException('Invalid startDate');
  4807. }
  4808. if (empty($endDate)) {
  4809. throw new \InvalidArgumentException('Invalid endDate');
  4810. }
  4811. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  4812. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  4813. $cacheKey = \App\Lib\Utility::generateKey($hour, $startDate, $endDate, $latitude, $longitude, $level, $unit, $format);
  4814. // Try to get the data from Redis cache
  4815. $cachedData = $this->redisCache->get($cacheKey);
  4816. if ($cachedData !== null) {
  4817. // Return the cached data if available
  4818. return $cachedData;
  4819. }
  4820. // If cache is empty, get the data from the API
  4821. $client = new Client(['verify' => false]);
  4822. $url = sprintf(
  4823. '%s/%s--%s:PT%sH/soil_moisture_index_%s:%s/%s,%s/%s',
  4824. $this->apiBaseUrl,
  4825. $startDate,
  4826. $endDate,
  4827. $hour,
  4828. $level,
  4829. $unit,
  4830. $latitude,
  4831. $longitude,
  4832. $format
  4833. );
  4834. $response = $client->request('GET', $url, [
  4835. 'auth' => [$this->username, $this->password],
  4836. ]);
  4837. $statusCode = $response->getStatusCode();
  4838. $data = json_decode($response->getBody(), true);
  4839. if ($statusCode != 200) {
  4840. return $this->createErrorResponse($statusCode);
  4841. }
  4842. // Set the data to Redis cache
  4843. $this->redisCache->set($cacheKey, $data);
  4844. return $data;
  4845. }
  4846. } catch (GuzzleHttp\Exception\RequestException $e) {
  4847. return throw new \Exception($e->getMessage());
  4848. } catch (\Exception $e) {
  4849. return throw new \Exception($e->getMessage());
  4850. }
  4851. }
  4852. /**
  4853. * Frost & Thaw Depth
  4854. *
  4855. * @param string $hourly The hour for which forecast is required in 24-hour format
  4856. * @param string $format return type of json
  4857. * @param string $startDate The type of data date (e.g., '2023-09-01')
  4858. * @param string $endDate The type of data date (e.g., '2023-09-01')
  4859. * @param string $unit The type of data request (e.g., 'cm')
  4860. * @param array $coordinates The latitude and longitude of location
  4861. * @throws \InvalidArgumentException If any parameter has an invalid data type
  4862. */
  4863. public function getFrostThawAndDepth(int $hour, $startDate, $endDate, $unit, array $coordinates, $format)
  4864. {
  4865. try {
  4866. if (count($coordinates) > 1) {
  4867. // Validate the input parameters
  4868. foreach ($coordinates as $coordinate) {
  4869. if (count($coordinate) < 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  4870. throw new \InvalidArgumentException('Invalid coordinates');
  4871. }
  4872. }
  4873. if (empty($startDate) || empty($endDate)) {
  4874. throw new \InvalidArgumentException('Invalid dates');
  4875. }
  4876. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  4877. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  4878. $cacheKey = \App\Lib\Utility::generateKey($hour, $startDate, $endDate, $unit, $coordinates, $format);
  4879. // Try to get the data from Redis cache
  4880. $cachedData = $this->redisCache->get($cacheKey);
  4881. if ($cachedData !== null) {
  4882. // Return the cached data if available
  4883. return $cachedData;
  4884. }
  4885. // If cache is empty, get the data from the API
  4886. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  4887. $client = new Client(['verify' => false]);
  4888. $url = sprintf(
  4889. '%s/%s--%s:PT%sH/frost_depth:%s,thaw_depth:%s/%s/%s',
  4890. $this->apiBaseUrl,
  4891. $startDate,
  4892. $endDate,
  4893. $hour,
  4894. $unit,
  4895. $unit,
  4896. $coordinateString,
  4897. $format
  4898. );
  4899. $response = $client->request('GET', $url, [
  4900. 'auth' => [$this->username, $this->password],
  4901. ]);
  4902. $statusCode = $response->getStatusCode();
  4903. $data = json_decode($response->getBody(), true);
  4904. if ($statusCode != 200) {
  4905. return $this->createErrorResponse($statusCode);
  4906. }
  4907. // Set the data to Redis cache
  4908. $this->redisCache->set($cacheKey, $data);
  4909. return $data;
  4910. } else {
  4911. if ($coordinates) {
  4912. $latitude = $coordinates[0][0];
  4913. $longitude = $coordinates[0][1];
  4914. }
  4915. // Validate the input parameters
  4916. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  4917. throw new \InvalidArgumentException('Invalid latitude');
  4918. }
  4919. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  4920. throw new \InvalidArgumentException('Invalid longitude');
  4921. }
  4922. if (empty($startDate)) {
  4923. throw new \InvalidArgumentException('Invalid startDate');
  4924. }
  4925. if (empty($endDate)) {
  4926. throw new \InvalidArgumentException('Invalid endDate');
  4927. }
  4928. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  4929. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  4930. $cacheKey = \App\Lib\Utility::generateKey($hour, $startDate, $endDate, $unit, $latitude, $longitude, $format);
  4931. // Try to get the data from Redis cache
  4932. $cachedData = $this->redisCache->get($cacheKey);
  4933. if ($cachedData !== null) {
  4934. // Return the cached data if available
  4935. return $cachedData;
  4936. }
  4937. // If cache is empty, get the data from the API
  4938. $client = new Client(['verify' => false]);
  4939. $url = sprintf(
  4940. '%s/%s--%s:PT%sH/frost_depth:%s,thaw_depth:%s/%s,%s/%s',
  4941. $this->apiBaseUrl,
  4942. $startDate,
  4943. $endDate,
  4944. $hour,
  4945. $unit,
  4946. $unit,
  4947. $latitude,
  4948. $longitude,
  4949. $format
  4950. );
  4951. $response = $client->request('GET', $url, [
  4952. 'auth' => [$this->username, $this->password],
  4953. ]);
  4954. $statusCode = $response->getStatusCode();
  4955. $data = json_decode($response->getBody(), true);
  4956. if ($statusCode != 200) {
  4957. return $this->createErrorResponse($statusCode);
  4958. }
  4959. // Set the data to Redis cache
  4960. $this->redisCache->set($cacheKey, $data);
  4961. return $data;
  4962. }
  4963. } catch (GuzzleHttp\Exception\RequestException $e) {
  4964. return throw new \Exception($e->getMessage());
  4965. } catch (\Exception $e) {
  4966. return throw new \Exception($e->getMessage());
  4967. }
  4968. }
  4969. public function getReportForecastData(array $coordinates, string $startDate, string $endDate, int $hours, string $model = "ksancm-wrf-48", array $parameters = [], $translator, array $cities, $params)
  4970. {
  4971. try {
  4972. if (count($coordinates) > 1) {
  4973. // Validate the input parameters
  4974. foreach ($coordinates as $coordinate) {
  4975. if (count($coordinate) < 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  4976. // throw new \InvalidArgumentException('Invalid coordinates');
  4977. return ["success" => false, "message" => $translator->trans("invalid_coordinates")];
  4978. }
  4979. }
  4980. }else{
  4981. if ($coordinates) {
  4982. $latitude = $coordinates[0][0];
  4983. $longitude = $coordinates[0][1];
  4984. }
  4985. // Validate the input parameters
  4986. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  4987. // throw new \InvalidArgumentException('Invalid latitude');
  4988. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  4989. }
  4990. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  4991. // throw new \InvalidArgumentException('Invalid longitude');
  4992. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  4993. }
  4994. }
  4995. if (empty($startDate) || empty($endDate)) {
  4996. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  4997. // throw new \InvalidArgumentException('Invalid dates');
  4998. }
  4999. if ($hours < 1 and $hours > 24) {
  5000. throw new \InvalidArgumentException('Invalid hour');
  5001. }
  5002. $startDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($startDate)));
  5003. $endDate = date('Y-m-d\TH:i:s.v\Z', (strtotime($endDate)));
  5004. if (empty($parameters)) {
  5005. $parameters = [
  5006. 't_2m:C',
  5007. 't_max_2m_%sh:C',
  5008. 't_min_2m_%sh:C',
  5009. 't_apparent_min_%sh:C',
  5010. 't_apparent_max_%sh:C',
  5011. 'wind_speed_mean_10m_%sh:kmh',
  5012. 'wind_dir_mean_10m_%sh:d',
  5013. 'prob_precip_%sh:p',
  5014. 'precip_%sh:mm',
  5015. 'relative_humidity_mean_2m_%sh:p',
  5016. 'effective_cloud_cover_mean_%sh:octas',
  5017. 'dew_point_mean_2m_%sh:C',
  5018. 'wind_gusts_10m_%sh:kn',
  5019. 'visibility:km',
  5020. ];
  5021. }
  5022. // Create a Redis key for the cache
  5023. $cacheKey = sprintf('daily_forecast_%s_%s_%s', implode('_', array_map(function ($coordinate) {
  5024. return implode('_', $coordinate);
  5025. }, $coordinates)), (($startDate) . '-' . ($endDate)) . $model, implode('_', $parameters));
  5026. // Try to get the data from Redis cache
  5027. $cachedData = $this->redisCache->get($cacheKey);
  5028. if ($cachedData !== null) {
  5029. // Return the cached data if available
  5030. return $cachedData;
  5031. }
  5032. $timeResolutions = [
  5033. '_24h' => '+1 day',
  5034. '_12h' => '+12 hours',
  5035. '_6h' => '+6 hours',
  5036. '_3h' => '+3 hours',
  5037. '_1h' => '+1 hour'
  5038. ];
  5039. $adjustedParameters = [];
  5040. $nonAdjustedParameters = [];
  5041. foreach ($parameters as $parameter) {
  5042. $matched = false;
  5043. foreach ($timeResolutions as $key => $adjustment) {
  5044. if (strpos($parameter, $key) !== false) {
  5045. $matched = true;
  5046. $adjustedParameters[$adjustment][] = $parameter;
  5047. break;
  5048. }
  5049. }
  5050. if (!$matched) {
  5051. $nonAdjustedParameters[] = $parameter;
  5052. }
  5053. }
  5054. // Construct the URL
  5055. // $url = "{$this->apiBaseUrl}/{$startDate}--{$endDate}:{$duration}/t_2m:C,wind_speed_10m:kmh,wind_dir_10m:d,sfs_pressure:hPa,precip_10min:mm,relative_humidity_2m:p,visibility:km,dew_point_2m:c,wind_gusts_10m_1h:kmh,ceiling_height_agl:m,geopotential_height:m,t_min_2m_24h:C,t_max_2m_24h:C,precip_24h:mm,total_cloud_cover:octas/metar_{$metar}/json?source=mix-obs&on_invalid=fill_with_invalid";
  5056. $dataFinal = [
  5057. 'data' => []
  5058. ];
  5059. foreach ($adjustedParameters as $adjustment => $adjParams) {
  5060. $adjustedStartDate = (new \DateTime($startDate))->modify($adjustment)->format('Y-m-d\TH:i:s.v\Z');
  5061. $adjustedEndDate = (new \DateTime($endDate))->modify($adjustment)->format('Y-m-d\TH:i:s.v\Z');
  5062. $data = $this->fetchReportForecastData($coordinates,$adjustedStartDate,$adjustedEndDate,$hours, $model,$adjParams,$translator,$cities, $params);
  5063. // Revert dates to original range
  5064. if (isset($data['data']) && is_array($data['data'])) {
  5065. foreach ($data['data'] as &$datum) {
  5066. if (isset($datum['coordinates']) && is_array($datum['coordinates'])) {
  5067. foreach ($datum['coordinates'] as &$coordinate) {
  5068. if (isset($coordinate['dates']) && is_array($coordinate['dates'])) {
  5069. foreach ($coordinate['dates'] as &$date) {
  5070. if (isset($date['date'])) {
  5071. // Convert the date back by subtracting the adjustment
  5072. $adjustmentValue = str_replace(['+', ' '], '', $adjustment); // Remove '+' and extra spaces
  5073. $date['date'] = (new \DateTime($date['date']))
  5074. ->modify('-' . $adjustmentValue)
  5075. ->format('Y-m-d\TH:i:s\Z');
  5076. }
  5077. }
  5078. }
  5079. }
  5080. }
  5081. }
  5082. } else {
  5083. $data['data'] = []; // Ensure 'data' exists
  5084. }
  5085. // Merge into $dataFinal
  5086. if (empty($dataFinal['version'])) {
  5087. $dataFinal['version'] = $data['version'] ?? null;
  5088. $dataFinal['user'] = $data['user'] ?? null;
  5089. $dataFinal['dateGenerated'] = $data['dateGenerated'] ?? null;
  5090. $dataFinal['status'] = $data['status'] ?? null;
  5091. }
  5092. if (isset($data['data']) && is_array($data['data'])) {
  5093. $dataFinal['data'] = array_merge($dataFinal['data'], $data['data']);
  5094. }
  5095. }
  5096. // Process non-adjusted parameters
  5097. if (!empty($nonAdjustedParameters)) {
  5098. $data= $this->fetchReportForecastData($coordinates,$startDate,$endDate,$hours, $model ,$nonAdjustedParameters,$translator,$cities, $params);
  5099. if (isset($data['data']) && is_array($data['data'])) {
  5100. $dataFinal['data'] = array_merge($dataFinal['data'], $data['data']);
  5101. }
  5102. }
  5103. // Reorder data based on parameters
  5104. $dataFinal['data'] = isset($dataFinal['data']) && is_array($dataFinal['data'])
  5105. ? $this->orderResults($dataFinal['data'], $parameters)
  5106. : [];
  5107. $result = [];
  5108. foreach ($dataFinal['data'] as $param) {
  5109. foreach ($param['coordinates'] as $paramCoordinates) {
  5110. // exit;
  5111. $paramCoordinates['lat'] = number_format($paramCoordinates['lat'], 6, '.', '');
  5112. $paramCoordinates['lon'] = number_format($paramCoordinates['lon'], 6, '.', '');
  5113. $cityKey = $paramCoordinates['lat'] . '|' . $paramCoordinates['lon'];
  5114. if (!isset($result[$cityKey])) {
  5115. $result[$cityKey] = [];
  5116. if (isset($cities[$cityKey]['en'])) {
  5117. $result[$cityKey] = [
  5118. 'cityEn' => $cities[$cityKey]['en'],
  5119. 'cityAr' => $cities[$cityKey]['ar'],
  5120. 'lat' => $paramCoordinates['lat'],
  5121. 'lon' => $paramCoordinates['lon'],
  5122. 'parameters' => [],
  5123. ];
  5124. } else {
  5125. $result[$cityKey] = [
  5126. 'city' => $cities[$cityKey],
  5127. 'lat' => $paramCoordinates['lat'],
  5128. 'lon' => $paramCoordinates['lon'],
  5129. 'parameters' => [],
  5130. ];
  5131. }
  5132. }
  5133. $parameterData = [
  5134. 'parameter' => $param['parameter'],
  5135. 'dates' => [],
  5136. ];
  5137. foreach ($paramCoordinates['dates'] as $item) {
  5138. $parameterData['dates'][] = [
  5139. 'date' => $item['date'], // You can modify this as needed
  5140. 'value' => $item['value'], // You can modify this as needed
  5141. ];
  5142. }
  5143. // p_r($parameterData);
  5144. $result[$cityKey]['parameters'][] = $parameterData;
  5145. }
  5146. }
  5147. $result = array_values($result);
  5148. if (isset($params['report_type_id']) && !empty($params['report_type_id'])) {
  5149. $latestReport = new Report\Listing();
  5150. $latestReport->setCondition('reportType__id = ?', [$params['report_type_id']]);
  5151. $latestReport->setOrderKey("o_creationDate");
  5152. $latestReport->setOrder("desc");
  5153. $latestReport = $latestReport->current();
  5154. if ($latestReport) {
  5155. $jsonData = json_decode($latestReport->getJsonData(), true);
  5156. foreach ($result as &$value) {
  5157. // Compare latitude and longitude
  5158. foreach ($jsonData as $jsonEntry) {
  5159. if ($value['lat'] == $jsonEntry['lat'] && $value['lon'] == $jsonEntry['lon']) {
  5160. // Latitude and longitude match, proceed with parameter comparison
  5161. foreach ($value['parameters'] as &$paramValue) {
  5162. foreach ($jsonEntry['parameters'] as $jsonParam) {
  5163. if ($jsonParam['parameter'] == $paramValue['parameter']) {
  5164. // Parameter matches, check dates now
  5165. foreach ($paramValue['dates'] as &$dateValue) {
  5166. foreach ($jsonParam['dates'] as $jsonDate) {
  5167. if ($dateValue['date'] == $jsonDate['date']) {
  5168. // Exact match found, override the value
  5169. $dateValue['value'] = $jsonDate['value'];
  5170. // Continue checking all dates, no break here
  5171. }
  5172. }
  5173. }
  5174. unset($dateValue); // Ensure reference is not carried over
  5175. }
  5176. }
  5177. }
  5178. unset($paramValue); // Ensure reference is not carried over
  5179. }
  5180. }
  5181. }
  5182. unset($value); // Ensure reference is not carried over
  5183. }
  5184. }
  5185. // Set the data to Redis cache
  5186. $this->redisCache->set($cacheKey, $dataFinal);
  5187. return $result;
  5188. } catch (GuzzleHttp\Exception\RequestException $e) {
  5189. return throw new \Exception($e->getMessage());
  5190. } catch (\Exception $e) {
  5191. return throw new \Exception($e->getMessage());
  5192. }
  5193. }
  5194. public function fetchReportForecastData($coordinates,$startDate,$endDate,$hours, $model,$parameters,$translator,$cities, $params){
  5195. $dataFinal = [];
  5196. $client = new Client(['verify' => false]);
  5197. if (count($coordinates) > 1) {
  5198. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  5199. }else{
  5200. if ($coordinates) {
  5201. $latitude = $coordinates[0][0];
  5202. $longitude = $coordinates[0][1];
  5203. }
  5204. }
  5205. while (!empty($parameters)) {
  5206. $batchParameters = array_splice($parameters, 0, 10); // Take up to 10 parameters
  5207. $batchQueryString = implode(',', $batchParameters);
  5208. $batchQueryString = str_replace('%s', $hours, $batchQueryString);
  5209. if (count($coordinates) > 1) {
  5210. $url = sprintf(
  5211. '%s/%s--%s:PT%sH/%s/%s/json?model=%s',
  5212. $this->apiBaseUrl,
  5213. $startDate,
  5214. $endDate,
  5215. $hours,
  5216. $batchQueryString,
  5217. $coordinateString,
  5218. $model
  5219. );
  5220. }else{
  5221. $url = sprintf(
  5222. '%s/%s--%s:PT%sH/%s/%s,%s/json?model=%s',
  5223. $this->apiBaseUrl,
  5224. $startDate,
  5225. $endDate,
  5226. $hours,
  5227. $batchQueryString,
  5228. $latitude,
  5229. $longitude,
  5230. $model
  5231. );
  5232. }
  5233. $response = $client->request('GET', $url, [
  5234. 'auth' => [$this->username, $this->password],
  5235. ]);
  5236. $statusCode = $response->getStatusCode();
  5237. $data = json_decode($response->getBody(), true);
  5238. // Merge data from the current API call into the final data
  5239. $dataFinal['data'] = array_merge($dataFinal['data'] ?? [], $data['data']);
  5240. }
  5241. foreach ($coordinates as $coOrd) {
  5242. $weatherSymbols = $this->getWeatherSymbols([$coOrd], $startDate, $endDate, 'PT' . $hours . 'H', $hours . 'h', true);
  5243. if (isset($weatherSymbols['data'][0]['parameter'])) {
  5244. $dataFinal['symbols'][$coOrd[0] . '|' . $coOrd[1]] = $weatherSymbols['data'][0];
  5245. }
  5246. }
  5247. // p_r($dataFinal);
  5248. // exit;
  5249. // Convert the associative array to indexed array
  5250. return $dataFinal;
  5251. }
  5252. public function getAdminReportForecastData(array $coordinates, string $startDate, string $endDate, string $model = "ksancm-wrf-48", array $parameters12h = [], array $parameters24h = [], $translator, array $cities, $params)
  5253. {
  5254. try {
  5255. if (isset($params['report_type_id']) && !empty($params['report_type_id'])) {
  5256. $latestReport = new Report\Listing();
  5257. $reportType = \Pimcore\Model\DataObject::getById($params['report_type_id']);
  5258. $latestReport->filterByReportType($reportType);
  5259. $latestReport->setOrderKey("createdOn");
  5260. $latestReport->setLimit(1);
  5261. $latestReport->setOrder("desc");
  5262. $latestReport = $latestReport->current();
  5263. }
  5264. if (count($coordinates) > 1) {
  5265. // Validate the input parameters
  5266. foreach ($coordinates as $coordinate) {
  5267. if (count($coordinate) < 2 || !is_numeric($coordinate[0]) || !is_numeric($coordinate[1])) {
  5268. // throw new \InvalidArgumentException('Invalid coordinates');
  5269. return ["success" => false, "message" => $translator->trans("invalid_coordinates")];
  5270. }
  5271. }
  5272. if (empty($startDate) || empty($endDate)) {
  5273. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  5274. // throw new \InvalidArgumentException('Invalid dates');
  5275. }
  5276. // Create a Redis key for the cache
  5277. $cacheKey = sprintf('daily_forecast_%s_%s_%s_%s', implode('_', array_map(function ($coordinate) {
  5278. return implode('_', $coordinate);
  5279. }, $coordinates)), (($startDate) . '-' . ($endDate)) . $model, implode('_', $parameters12h), implode('_', $parameters24h));
  5280. // Try to get the data from Redis cache
  5281. $cachedData = $this->redisCache->get($cacheKey);
  5282. if ($cachedData !== null) {
  5283. // Return the cached data if available
  5284. // return $cachedData;
  5285. }
  5286. // If cache is empty, get the data from the API
  5287. $client = new Client(['verify' => false]);
  5288. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  5289. $dataFinal = [];
  5290. // Function to perform the API requests for given parameters and hours
  5291. $performApiRequests = function ($parameters, $hours, $startDate, $endDate) use ($client, $coordinateString, $model) {
  5292. $data = [];
  5293. while (!empty($parameters)) {
  5294. $batchParameters = array_splice($parameters, 0, 10); // Take up to 10 parameters
  5295. $batchQueryString = implode(',', $batchParameters);
  5296. $url = sprintf(
  5297. '%s/%s--%s:PT%sH/%s/%s/json?model=%s',
  5298. $this->apiBaseUrl,
  5299. $startDate,
  5300. $endDate,
  5301. $hours,
  5302. $batchQueryString,
  5303. $coordinateString,
  5304. $model
  5305. );
  5306. $response = $client->request('GET', $url, [
  5307. 'auth' => [$this->username, $this->password],
  5308. ]);
  5309. $statusCode = $response->getStatusCode();
  5310. $dataBatch = json_decode($response->getBody(), true);
  5311. // Merge data from the current API call into the data array
  5312. $data['data'] = array_merge($data['data'] ?? [], $dataBatch['data']);
  5313. }
  5314. return $data;
  5315. };
  5316. // Process 24h parameters
  5317. if (!empty($parameters24h)) {
  5318. $startDateUpdated = new DateTime($startDate);
  5319. $endDateUpdated = new DateTime($endDate);
  5320. $startDateUpdated = $startDateUpdated->modify('+1 day')->format('Y-m-d\TH:i:s\Z');
  5321. $endDateUpdated = $endDateUpdated->modify('+1 day')->format('Y-m-d\TH:i:s\Z');
  5322. $data24h = $performApiRequests($parameters24h, 24, $startDateUpdated, $endDateUpdated);
  5323. if (!empty($data24h)) {
  5324. foreach ($data24h['data'] as &$datum) {
  5325. if (isset($datum['coordinates'])) {
  5326. foreach ($datum['coordinates'] as &$coordinate) {
  5327. foreach ($coordinate['dates'] as &$date) {
  5328. $dateObj = new DateTime($date['date']);
  5329. $date['date'] = $dateObj->modify('-1 day')->format('Y-m-d\TH:i:s\Z');
  5330. }
  5331. }
  5332. }
  5333. }
  5334. // Correctly append 24h data to $datainal
  5335. if (!empty($data24h['data'])) {
  5336. // Existing code to adjust dates in $data24h['data']
  5337. if (empty($dataFinal['data'])) {
  5338. $dataFinal['data'] = $data24h['data'];
  5339. } else {
  5340. $dataFinal['data'] = array_merge($dataFinal['data'], $data24h['data']);
  5341. }
  5342. }
  5343. }
  5344. }
  5345. // Process 12h parameters
  5346. if (!empty($parameters12h)) {
  5347. $startDateUpdated = new DateTime($startDate);
  5348. $endDateUpdated = new DateTime($endDate);
  5349. $startDateUpdated = $startDateUpdated->modify('+1 day')->format('Y-m-d\T12\Z');
  5350. $endDateUpdated = $endDateUpdated->modify('+1 day')->format('Y-m-d\T00\Z');
  5351. $data12h = $performApiRequests($parameters12h, 12, $startDateUpdated, $endDateUpdated);
  5352. if (!empty($data12h)) {
  5353. foreach ($data12h['data'] as &$datum) {
  5354. if (isset($datum['coordinates'])) {
  5355. foreach ($datum['coordinates'] as &$coordinate) {
  5356. foreach ($coordinate['dates'] as &$date) {
  5357. $dateObj = new DateTime($date['date']);
  5358. $date['date'] = $dateObj->modify('-1 day')->format('Y-m-d\TH:i:s\Z');
  5359. }
  5360. }
  5361. }
  5362. }
  5363. // Correctly append 12h data to $dataFinal
  5364. if (!empty($data12h['data'])) {
  5365. // Existing code to adjust dates in $data12h['data']
  5366. if (empty($dataFinal['data'])) {
  5367. $dataFinal['data'] = $data12h['data'];
  5368. } else {
  5369. $dataFinal['data'] = array_merge($dataFinal['data'], $data12h['data']);
  5370. }
  5371. }
  5372. }
  5373. }
  5374. foreach ($coordinates as $coOrd) {
  5375. $weatherSymbols = $this->getWeatherSymbols([$coOrd], $startDate, $endDate, 'PT12H', '12h');
  5376. if (isset($weatherSymbols['data'][0]['parameter'])) {
  5377. $dataFinal['symbols'][$coOrd[0] . '|' . $coOrd[1]] = $weatherSymbols['data'][0];
  5378. }
  5379. }
  5380. $result = [];
  5381. foreach ($dataFinal['data'] as $param) {
  5382. foreach ($param['coordinates'] as $paramCoordinates) {
  5383. $paramCoordinates['lat'] = number_format($paramCoordinates['lat'], 6, '.', '');
  5384. $paramCoordinates['lon'] = number_format($paramCoordinates['lon'], 6, '.', '');
  5385. $cityKey = $paramCoordinates['lat'] . '|' . $paramCoordinates['lon'];
  5386. if (!isset($result[$cityKey])) {
  5387. $result[$cityKey] = [];
  5388. if (isset($cities[$cityKey]['en'])) {
  5389. if (isset($paramCoordinates['lat']) && isset($paramCoordinates['lon'])) {
  5390. $result[$cityKey] = [
  5391. 'cityEn' => $cities[$cityKey]['en'],
  5392. 'cityAr' => $cities[$cityKey]['ar'],
  5393. 'lat' => $paramCoordinates['lat'],
  5394. 'lon' => $paramCoordinates['lon'],
  5395. 'parameters' => [],
  5396. ];
  5397. }
  5398. } else {
  5399. if (isset($paramCoordinates['lat']) && isset($paramCoordinates['lon'])) {
  5400. $result[$cityKey] = [
  5401. 'city' => $cities[$cityKey],
  5402. 'lat' => $paramCoordinates['lat'],
  5403. 'lon' => $paramCoordinates['lon'],
  5404. 'parameters' => [],
  5405. ];
  5406. }
  5407. }
  5408. }
  5409. $parameterData = [
  5410. 'parameter' => $param['parameter'],
  5411. 'dates' => [],
  5412. ];
  5413. foreach ($paramCoordinates['dates'] as $date) {
  5414. $parameterData['dates'][] = [
  5415. 'date' => $date['date'], // You can modify this as needed
  5416. 'value' => $date['value'], // You can modify this as needed
  5417. ];
  5418. }
  5419. $result[$cityKey]['parameters'][] = $parameterData;
  5420. }
  5421. }
  5422. // Convert the associative array to indexed array
  5423. $result = array_values($result);
  5424. if (isset($params['report_type_id']) && !empty($params['report_type_id'])) {
  5425. // $latestReport = new Report\Listing();
  5426. // $reportType = \Pimcore\Model\DataObject::getById($params['report_type_id']);
  5427. // $latestReport->filterByReportType($reportType);
  5428. // $latestReport->setOrderKey("createdOn");
  5429. // $latestReport->setOrder("desc");
  5430. // $latestReport = $latestReport->current();
  5431. if ($latestReport) {
  5432. $jsonData = json_decode($latestReport->getJsonData(), true);
  5433. foreach ($result as &$value) {
  5434. // Compare latitude and longitude
  5435. foreach ($jsonData as $jsonEntry) {
  5436. if (isset($value['lat']) && isset($jsonEntry['lat']) && ($value['lat'] == $jsonEntry['lat']) && isset($value['lon']) && isset($jsonEntry['lon']) && ($value['lon'] == $jsonEntry['lon'])) {
  5437. // Latitude and longitude match, proceed with parameter comparison
  5438. foreach ($value['parameters'] as &$paramValue) {
  5439. foreach ($jsonEntry['parameters'] as $jsonParam) {
  5440. if ($jsonParam['parameter'] == $paramValue['parameter']) {
  5441. // Parameter matches, check dates now
  5442. foreach ($paramValue['dates'] as &$dateValue) {
  5443. foreach ($jsonParam['dates'] as $jsonDate) {
  5444. if ($dateValue['date'] == $jsonDate['date']) {
  5445. // Exact match found, override the value
  5446. $dateValue['value'] = $jsonDate['value'];
  5447. // Continue checking all dates, no break here
  5448. }
  5449. }
  5450. }
  5451. unset($dateValue); // Ensure reference is not carried over
  5452. }
  5453. }
  5454. }
  5455. unset($paramValue); // Ensure reference is not carried over
  5456. }
  5457. }
  5458. }
  5459. unset($value); // Ensure reference is not carried over
  5460. }
  5461. }
  5462. } else {
  5463. if ($coordinates) {
  5464. $latitude = $coordinates[0][0];
  5465. $longitude = $coordinates[0][1];
  5466. }
  5467. // Validate the input parameters
  5468. if (!preg_match('/^[-]?[0-9]{1,2}\.[0-9]+/', $latitude)) {
  5469. // throw new \InvalidArgumentException('Invalid latitude');
  5470. return ["success" => false, "message" => $translator->trans("invalid_latitude")];
  5471. }
  5472. if (!preg_match('/^[-]?[0-9]{1,3}\.[0-9]+/', $longitude)) {
  5473. // throw new \InvalidArgumentException('Invalid longitude');
  5474. return ["success" => false, "message" => $translator->trans("invalid_longitude")];
  5475. }
  5476. if (empty($startDate) || empty($endDate)) {
  5477. return ["success" => false, "message" => $translator->trans("invalid_dates")];
  5478. }
  5479. // Create a Redis key for the cache
  5480. $cacheKey = sprintf('daily_forecast_%s_%s_%s_%s', implode('_', array_map(function ($coordinate) {
  5481. return implode('_', $coordinate);
  5482. }, $coordinates)), (($startDate) . '-' . ($endDate)) . $model, implode('_', $parameters12h), implode('_', $parameters24h));
  5483. // Try to get the data from Redis cache
  5484. $cachedData = $this->redisCache->get($cacheKey);
  5485. if ($cachedData !== null) {
  5486. // Return the cached data if available
  5487. return $cachedData;
  5488. }
  5489. // If cache is empty, get the data from the API
  5490. $client = new Client(['verify' => false]);
  5491. $coordinateString = implode('+', array_map(fn($coords) => implode(',', $coords), $coordinates));
  5492. $dataFinal = [];
  5493. // Function to perform the API requests for given parameters and hours
  5494. $performApiRequests = function ($parameters, $hours, $startDate, $endDate) use ($client, $latitude, $longitude, $model) {
  5495. $data = [];
  5496. while (!empty($parameters)) {
  5497. $batchParameters = array_splice($parameters, 0, 10); // Take up to 10 parameters
  5498. $batchQueryString = implode(',', $batchParameters);
  5499. $url = sprintf(
  5500. '%s/%s--%s:PT%sH/%s/%s,%s/json?model=%s',
  5501. $this->apiBaseUrl,
  5502. $startDate,
  5503. $endDate,
  5504. $hours,
  5505. $batchQueryString,
  5506. $latitude,
  5507. $longitude,
  5508. $model
  5509. );
  5510. $response = $client->request('GET', $url, [
  5511. 'auth' => [$this->username, $this->password],
  5512. 'timeout' => 600
  5513. ]);
  5514. $statusCode = $response->getStatusCode();
  5515. $dataBatch = json_decode($response->getBody(), true);
  5516. // Merge data from the current API call into the data array
  5517. $data['data'] = array_merge($data['data'] ?? [], $dataBatch['data']);
  5518. }
  5519. return $data;
  5520. };
  5521. // Process 24h parameters
  5522. if (!empty($parameters24h)) {
  5523. $startDateUpdated = new DateTime($startDate);
  5524. $endDateUpdated = new DateTime($endDate);
  5525. $startDateUpdated = $startDateUpdated->modify('+1 day')->format('Y-m-d\TH:i:s\Z');
  5526. $endDateUpdated = $endDateUpdated->modify('+1 day')->format('Y-m-d\TH:i:s\Z');
  5527. $data24h = $performApiRequests($parameters24h, 24, $startDateUpdated, $endDateUpdated);
  5528. if (!empty($data24h)) {
  5529. foreach ($data24h['data'] as &$datum) {
  5530. if (isset($datum['coordinates'])) {
  5531. foreach ($datum['coordinates'] as &$coordinate) {
  5532. foreach ($coordinate['dates'] as &$date) {
  5533. $dateObj = new DateTime($date['date']);
  5534. $date['date'] = $dateObj->modify('-1 day')->format('Y-m-d\TH:i:s\Z');
  5535. }
  5536. }
  5537. }
  5538. }
  5539. // Correctly append 24h data to $datainal
  5540. if (!empty($data24h['data'])) {
  5541. // Existing code to adjust dates in $data24h['data']
  5542. if (empty($dataFinal['data'])) {
  5543. $dataFinal['data'] = $data24h['data'];
  5544. } else {
  5545. $dataFinal['data'] = array_merge($dataFinal['data'], $data24h['data']);
  5546. }
  5547. }
  5548. }
  5549. }
  5550. // Process 12h parameters
  5551. if (!empty($parameters12h)) {
  5552. $startDateUpdated = new DateTime($startDate);
  5553. $endDateUpdated = new DateTime($endDate);
  5554. $startDateUpdated = $startDateUpdated->modify('+1 day')->format('Y-m-d\T12\Z');
  5555. $endDateUpdated = $endDateUpdated->modify('+1 day')->format('Y-m-d\T00\Z');
  5556. $data12h = $performApiRequests($parameters12h, 12, $startDateUpdated, $endDateUpdated);
  5557. if (!empty($data12h)) {
  5558. foreach ($data12h['data'] as &$datum) {
  5559. if (isset($datum['coordinates'])) {
  5560. foreach ($datum['coordinates'] as &$coordinate) {
  5561. foreach ($coordinate['dates'] as &$date) {
  5562. $dateObj = new DateTime($date['date']);
  5563. $date['date'] = $dateObj->modify('-1 day')->format('Y-m-d\TH:i:s\Z');
  5564. }
  5565. }
  5566. }
  5567. }
  5568. // Correctly append 12h data to $dataFinal
  5569. if (!empty($data12h['data'])) {
  5570. // Existing code to adjust dates in $data12h['data']
  5571. if (empty($dataFinal['data'])) {
  5572. $dataFinal['data'] = $data12h['data'];
  5573. } else {
  5574. $dataFinal['data'] = array_merge($dataFinal['data'], $data12h['data']);
  5575. }
  5576. }
  5577. }
  5578. }
  5579. foreach ($coordinates as $coOrd) {
  5580. $weatherSymbols = $this->getWeatherSymbols([$coOrd], $startDate, $endDate, 'PT12H', '12h');
  5581. if (isset($weatherSymbols['data'][0]['parameter'])) {
  5582. $dataFinal['symbols'][$coOrd[0] . '|' . $coOrd[1]] = $weatherSymbols['data'][0];
  5583. }
  5584. }
  5585. $result = [];
  5586. foreach ($dataFinal['data'] as $param) {
  5587. foreach ($param['coordinates'] as $paramCoordinates) {
  5588. $paramCoordinates['lat'] = number_format($paramCoordinates['lat'], 6, '.', '');
  5589. $paramCoordinates['lon'] = number_format($paramCoordinates['lon'], 6, '.', '');
  5590. $cityKey = $paramCoordinates['lat'] . '|' . $paramCoordinates['lon'];
  5591. if (!isset($result[$cityKey])) {
  5592. $result[$cityKey] = [];
  5593. if (isset($cities[$cityKey]['en'])) {
  5594. $result[$cityKey] = [
  5595. 'cityEn' => $cities[$cityKey]['en'],
  5596. 'cityAr' => $cities[$cityKey]['ar'],
  5597. 'lat' => $paramCoordinates['lat'],
  5598. 'lon' => $paramCoordinates['lon'],
  5599. 'parameters' => [],
  5600. ];
  5601. } else {
  5602. $result[$cityKey] = [
  5603. 'city' => $cities[$cityKey],
  5604. 'lat' => $paramCoordinates['lat'],
  5605. 'lon' => $paramCoordinates['lon'],
  5606. 'parameters' => [],
  5607. ];
  5608. }
  5609. }
  5610. $parameterData = [
  5611. 'parameter' => $param['parameter'],
  5612. 'dates' => [],
  5613. ];
  5614. foreach ($paramCoordinates['dates'] as $date) {
  5615. $parameterData['dates'][] = [
  5616. 'date' => $date['date'], // You can modify this as needed
  5617. 'value' => $date['value'], // You can modify this as needed
  5618. ];
  5619. }
  5620. $result[$cityKey]['parameters'][] = $parameterData;
  5621. }
  5622. }
  5623. // Convert the associative array to indexed array
  5624. $result = array_values($result);
  5625. if (isset($params['report_type_id']) && !empty($params['report_type_id'])) {
  5626. // $latestReport = new Report\Listing();
  5627. // $reportType = \Pimcore\Model\DataObject::getById($params['report_type_id']);
  5628. // $latestReport->filterByReportType($reportType);
  5629. // $latestReport->setOrderKey("createdOn");
  5630. // $latestReport->setOrder("desc");
  5631. // $latestReport = $latestReport->current();
  5632. if ($latestReport) {
  5633. $jsonData = json_decode($latestReport->getJsonData(), true);
  5634. foreach ($result as &$value) {
  5635. // Compare latitude and longitude
  5636. foreach ($jsonData as $jsonEntry) {
  5637. if ($value['lat'] == $jsonEntry['lat'] && $value['lon'] == $jsonEntry['lon']) {
  5638. // Latitude and longitude match, proceed with parameter comparison
  5639. foreach ($value['parameters'] as &$paramValue) {
  5640. foreach ($jsonEntry['parameters'] as $jsonParam) {
  5641. if ($jsonParam['parameter'] == $paramValue['parameter']) {
  5642. // Parameter matches, check dates now
  5643. foreach ($paramValue['dates'] as &$dateValue) {
  5644. foreach ($jsonParam['dates'] as $jsonDate) {
  5645. if ($dateValue['date'] == $jsonDate['date']) {
  5646. // Exact match found, override the value
  5647. $dateValue['value'] = $jsonDate['value'];
  5648. // Continue checking all dates, no break here
  5649. }
  5650. }
  5651. }
  5652. unset($dateValue); // Ensure reference is not carried over
  5653. }
  5654. }
  5655. }
  5656. unset($paramValue); // Ensure reference is not carried over
  5657. }
  5658. }
  5659. }
  5660. unset($value); // Ensure reference is not carried over
  5661. }
  5662. }
  5663. }
  5664. // Set the data to Redis cache
  5665. // $this->redisCache->set($cacheKey, $dataFinal);
  5666. return $result;
  5667. } catch (GuzzleHttp\Exception\RequestException $e) {
  5668. // p_r($e->getMessage());
  5669. return throw new \Exception($e->getMessage());
  5670. } catch (\Exception $e) {
  5671. return throw new \Exception($e->getMessage());
  5672. }
  5673. }
  5674. public function getBarbs(string $windSpeed, string $dateTime, string $resolution, string $accessToken): array
  5675. {
  5676. try {
  5677. $client = new Client(['verify' => false]);
  5678. $url = sprintf(
  5679. "%s/mvt/barbs/%s/style.json?datetime=%s&resolution=%s&access_token=%s",
  5680. $this->apiBaseUrl,
  5681. $windSpeed,
  5682. $dateTime,
  5683. $resolution,
  5684. $accessToken
  5685. );
  5686. $response = $client->request('GET', $url, ['auth' => [$this->username, $this->password]]);
  5687. $data = json_decode($response->getBody(), true);
  5688. return $data;
  5689. } catch (RequestException $e) {
  5690. throw new \Exception($e->getMessage());
  5691. } catch (\Exception $e) {
  5692. throw new \Exception($e->getMessage());
  5693. }
  5694. }
  5695. public function getIsolines(string $measure, string $dateTime, string $accessToken): array
  5696. {
  5697. try {
  5698. $client = new Client(['verify' => false]);
  5699. $url = sprintf(
  5700. "%s/mvt/isolines/%s/style.json?datetime=%s&access_token=%s",
  5701. $this->apiBaseUrl,
  5702. $measure,
  5703. $dateTime,
  5704. $accessToken
  5705. );
  5706. $response = $client->request('GET', $url, ['auth' => [$this->username, $this->password]]);
  5707. $data = json_decode($response->getBody(), true);
  5708. return $data;
  5709. } catch (RequestException $e) {
  5710. throw new \Exception($e->getMessage());
  5711. } catch (\Exception $e) {
  5712. throw new \Exception($e->getMessage());
  5713. }
  5714. }
  5715. public function getAviationReports(string $meter, string $dateTime, string $accessToken): array
  5716. {
  5717. try {
  5718. $client = new Client(['verify' => false]);
  5719. $url = sprintf(
  5720. "%s/mvt/aviation_reports/%s/style.json?datetime=%s&access_token=%s",
  5721. $this->apiBaseUrl,
  5722. $meter,
  5723. $dateTime,
  5724. $accessToken
  5725. );
  5726. $response = $client->request('GET', $url, ['auth' => [$this->username, $this->password]]);
  5727. $data = json_decode($response->getBody(), true);
  5728. return $data;
  5729. } catch (RequestException $e) {
  5730. throw new \Exception($e->getMessage());
  5731. } catch (\Exception $e) {
  5732. throw new \Exception($e->getMessage());
  5733. }
  5734. }
  5735. public function getOceanCurrentSpeed(string $dateTime, string $bBox): array
  5736. {
  5737. try {
  5738. $client = new Client(['verify' => false]);
  5739. $url = sprintf(
  5740. "%s/%s/ocean_current_u:ms,ocean_current_v:ms/%s/json",
  5741. $this->apiBaseUrl,
  5742. $dateTime,
  5743. $bBox
  5744. );
  5745. $response = $client->request('GET', $url, ['auth' => [$this->username, $this->password]]);
  5746. $data = json_decode($response->getBody(), true);
  5747. return $data;
  5748. } catch (RequestException $e) {
  5749. throw new \Exception($e->getMessage());
  5750. } catch (\Exception $e) {
  5751. throw new \Exception($e->getMessage());
  5752. }
  5753. }
  5754. public function getWMSMAP(string $legendGraphics, string $format, string $layer, string $style = '')
  5755. {
  5756. try {
  5757. $client = new Client(['verify' => false]);
  5758. // Conditionally add the STYLE parameter to the URL if it's provided
  5759. $stylePart = $style ? "&STYLE=" . urlencode($style) : '';
  5760. $url = sprintf(
  5761. "%s/wms?VERSION=1.3.0&REQUEST=%s&FORMAT=%s&LAYER=%s%s",
  5762. $this->apiBaseUrl,
  5763. $legendGraphics,
  5764. $format,
  5765. $layer,
  5766. $stylePart
  5767. );
  5768. $response = $client->request('GET', $url, ['auth' => [$this->username, $this->password]]);
  5769. // Get the raw body content
  5770. $content = $response->getBody()->getContents();
  5771. // Set headers and create a response object for the PNG image
  5772. $httpResponse = new Response($content, Response::HTTP_OK, [
  5773. 'Content-Type' => $format
  5774. ]);
  5775. return $httpResponse;
  5776. } catch (RequestException $e) {
  5777. throw new \Exception($e->getMessage());
  5778. } catch (\Exception $e) {
  5779. throw new \Exception($e->getMessage());
  5780. }
  5781. }
  5782. public function getWeatherStationData(string $typeName, string $parameters, string $dateTime, string $bBox)
  5783. {
  5784. try {
  5785. $client = new Client(['verify' => false]);
  5786. $url = sprintf(
  5787. "%s/wfs?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=%s&PARAMETERS=%s&TIME=%s&BBOX=%s",
  5788. $this->apiBaseUrl,
  5789. $typeName,
  5790. $parameters,
  5791. $dateTime,
  5792. $bBox
  5793. );
  5794. $response = $client->request('GET', $url, ['auth' => [$this->username, $this->password]]);
  5795. // Get the raw body content
  5796. $content = $response->getBody()->getContents();
  5797. // Set headers and create a response object for the XML File
  5798. $httpResponse = new Response($content, Response::HTTP_OK, [
  5799. 'Content-Type' => 'application/xml'
  5800. ]);
  5801. return $httpResponse;
  5802. // return $response->getBody();
  5803. } catch (RequestException $e) {
  5804. throw new \Exception($e->getMessage());
  5805. } catch (\Exception $e) {
  5806. throw new \Exception($e->getMessage());
  5807. }
  5808. }
  5809. function getGridLayer($dateTime, $parameter, $coordinates, $resolution, $format, $source, $bbox, $calibrated, $translator)
  5810. {
  5811. try {
  5812. if (!preg_match('/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/', $dateTime)) {
  5813. throw new \InvalidArgumentException($translator->trans('invalid_date_format'));
  5814. }
  5815. if (!$parameter) {
  5816. throw new \InvalidArgumentException($translator->trans('invalid_unit'));
  5817. }
  5818. if (count($coordinates) < 1 || !is_array($coordinates[0]) || count($coordinates[0]) < 2) {
  5819. throw new \InvalidArgumentException($translator->trans('invalid_co_ordinates'));
  5820. }
  5821. if (!preg_match('/^\d+x\d+$/', $resolution)) {
  5822. throw new \InvalidArgumentException($translator->trans('invalid_resolution'));
  5823. }
  5824. if (!in_array($format, ['json', 'xml', 'csv'])) {
  5825. throw new \InvalidArgumentException($translator->trans('invalid_format'));
  5826. }
  5827. if (!is_string($source)) {
  5828. throw new \InvalidArgumentException($translator->trans('invalid_source'));
  5829. }
  5830. if (count($bbox) !== 4) {
  5831. throw new \InvalidArgumentException($translator->trans('invalid_bbox_elements'));
  5832. }
  5833. if (!in_array($calibrated, ['true', 'false'])) {
  5834. throw new \InvalidArgumentException($translator->trans('invalid_calibrated_value'));
  5835. }
  5836. $dateTime = urlencode($dateTime);
  5837. $coordinateString = implode('_', array_map(fn($coords) => implode(',', $coords), $coordinates));
  5838. $bboxString = implode(',', $bbox);
  5839. $cacheKey = \App\Lib\Utility::generateKey($dateTime, $coordinateString, $bboxString, $parameter);
  5840. $cachedData = $this->redisCache->get($cacheKey);
  5841. if ($cachedData !== null) {
  5842. // Return the cached data if available
  5843. return $cachedData;
  5844. }
  5845. // Construct the URL
  5846. $url = "{$this->apiBaseUrl}/{$dateTime}/{$parameter}/{$coordinateString}:{$resolution}/{$format}?source={$source}&bbox={$bboxString}&calibrated={$calibrated}";
  5847. $client = new Client(['verify' => false]);
  5848. $response = $client->request('GET', $url, [
  5849. 'auth' => [$this->username, $this->password],
  5850. ]);
  5851. $statusCode = $response->getStatusCode();
  5852. $data = json_decode($response->getBody(), true);
  5853. if ($statusCode != 200) {
  5854. return $this->createErrorResponse($statusCode);
  5855. }
  5856. // Set the data to Redis cache
  5857. $this->redisCache->set($cacheKey, $data);
  5858. return $data;
  5859. } catch (RequestException $e) {
  5860. throw new \Exception($e->getMessage());
  5861. } catch (\Exception $e) {
  5862. throw new \Exception($e->getMessage());
  5863. }
  5864. }
  5865. public function getMetarData($startDate, $endDate, $metar, $duration, $translator, $parameter, $genExcel)
  5866. {
  5867. try {
  5868. // if (!preg_match('/^20\d{2}-\d{2}-\d{2}T\d{2}Z$/', $startDate)) {
  5869. // throw new \InvalidArgumentException($translator->trans('invalid_date_format'));
  5870. // }
  5871. // if (!preg_match('/^20\d{2}-\d{2}-\d{2}T\d{2}Z$/', $endDate)) {
  5872. // throw new \InvalidArgumentException($translator->trans('invalid_date_format'));
  5873. // }
  5874. if (!preg_match('/^20\d{2}-\d{2}-\d{2}T\d{2}(:\d{2}(:\d{2}(\.\d{3})?)?)?Z$/', $startDate)) {
  5875. throw new \InvalidArgumentException($translator->trans('invalid_date_format'));
  5876. }
  5877. if (!preg_match('/^20\d{2}-\d{2}-\d{2}T\d{2}(:\d{2}(:\d{2}(\.\d{3})?)?)?Z$/', $endDate)) {
  5878. throw new \InvalidArgumentException($translator->trans('invalid_date_format'));
  5879. }
  5880. $cacheKey = \App\Lib\Utility::generateKey($startDate, $endDate, $metar, $duration, $parameter);
  5881. $cachedData = $this->redisCache->get($cacheKey);
  5882. if ($cachedData !== null && !$genExcel) {
  5883. // Return the cached data if available
  5884. return $cachedData;
  5885. }
  5886. if ($genExcel) {
  5887. $parameters[] = $parameter;
  5888. } elseif (!empty($parameter) && !$genExcel) {
  5889. $parameters = $parameter;
  5890. } else {
  5891. $parameters = [
  5892. 't_2m:C',
  5893. 'wind_speed_10m:kmh',
  5894. 'wind_dir_10m:d',
  5895. 'msl_pressure:hPa',
  5896. 'precip_1h:mm',
  5897. 'relative_humidity_2m:p',
  5898. 'visibility:km',
  5899. 'dew_point_2m:c',
  5900. 'wind_gusts_10m_1h:kmh',
  5901. 'ceiling_height_agl:m',
  5902. 'geopotential_height:m',
  5903. 't_min_2m_24h:C',
  5904. 't_max_2m_24h:C',
  5905. 'precip_24h:mm',
  5906. 'total_cloud_cover:octas'
  5907. ];
  5908. }
  5909. $timeResolutions = [
  5910. '_24h' => '+1 day',
  5911. '_12h' => '+12 hours',
  5912. '_6h' => '+6 hours',
  5913. '_3h' => '+3 hours',
  5914. '_1h' => '+1 hour'
  5915. ];
  5916. $adjustedParameters = [];
  5917. $nonAdjustedParameters = [];
  5918. foreach ($parameters as $parameter) {
  5919. $matched = false;
  5920. foreach ($timeResolutions as $key => $adjustment) {
  5921. if (strpos($parameter, $key) !== false) {
  5922. $matched = true;
  5923. $adjustedParameters[$adjustment][] = $parameter;
  5924. break;
  5925. }
  5926. }
  5927. if (!$matched) {
  5928. $nonAdjustedParameters[] = $parameter;
  5929. }
  5930. }
  5931. // Construct the URL
  5932. // $url = "{$this->apiBaseUrl}/{$startDate}--{$endDate}:{$duration}/t_2m:C,wind_speed_10m:kmh,wind_dir_10m:d,sfs_pressure:hPa,precip_10min:mm,relative_humidity_2m:p,visibility:km,dew_point_2m:c,wind_gusts_10m_1h:kmh,ceiling_height_agl:m,geopotential_height:m,t_min_2m_24h:C,t_max_2m_24h:C,precip_24h:mm,total_cloud_cover:octas/metar_{$metar}/json?source=mix-obs&on_invalid=fill_with_invalid";
  5933. $dataFinal = [
  5934. 'version' => null,
  5935. 'user' => null,
  5936. 'dateGenerated' => null,
  5937. 'status' => null,
  5938. 'data' => []
  5939. ];
  5940. foreach ($adjustedParameters as $adjustment => $params) {
  5941. $adjustedStartDate = (new \DateTime($startDate))->modify($adjustment)->format('Y-m-d\TH:i:s.v\Z');
  5942. $adjustedEndDate = (new \DateTime($endDate))->modify($adjustment)->format('Y-m-d\TH:i:s.v\Z');
  5943. $data = $this->fetchMetarData($adjustedStartDate, $adjustedEndDate, $metar, $duration, $params);
  5944. // Revert dates to original range
  5945. if (isset($data['data']) && is_array($data['data'])) {
  5946. foreach ($data['data'] as &$datum) {
  5947. if (isset($datum['coordinates'][0]['dates']) && is_array($datum['coordinates'][0]['dates'])) {
  5948. foreach ($datum['coordinates'][0]['dates'] as &$date) {
  5949. if (isset($date['date'])) {
  5950. $date['date'] = (new \DateTime($date['date']))
  5951. ->modify('-' . ltrim($adjustment, '+'))
  5952. ->format('Y-m-d\TH:i:s.v\Z');
  5953. }
  5954. }
  5955. }
  5956. }
  5957. } else {
  5958. $data['data'] = []; // Ensure 'data' exists
  5959. }
  5960. // Merge into $dataFinal
  5961. if (empty($dataFinal['version'])) {
  5962. $dataFinal['version'] = $data['version'] ?? null;
  5963. $dataFinal['user'] = $data['user'] ?? null;
  5964. $dataFinal['dateGenerated'] = $data['dateGenerated'] ?? null;
  5965. $dataFinal['status'] = $data['status'] ?? null;
  5966. }
  5967. if (isset($data['data']) && is_array($data['data'])) {
  5968. $dataFinal['data'] = array_merge($dataFinal['data'], $data['data']);
  5969. }
  5970. }
  5971. if (!empty($nonAdjustedParameters)) {
  5972. $data = $this->fetchMetarData($startDate, $endDate, $metar, $duration, $nonAdjustedParameters);
  5973. if (isset($data['data']) && is_array($data['data'])) {
  5974. $dataFinal['version'] = $data['version'] ?? null;
  5975. $dataFinal['user'] = $data['user'] ?? null;
  5976. $dataFinal['dateGenerated'] = $data['dateGenerated'] ?? null;
  5977. $dataFinal['status'] = $data['status'] ?? null;
  5978. $dataFinal['data'] = array_merge($dataFinal['data'], $data['data']);
  5979. }
  5980. }
  5981. // Reorder data based on parameters
  5982. $dataFinal['data'] = isset($dataFinal['data']) && is_array($dataFinal['data'])
  5983. ? $this->orderResults($dataFinal['data'], $parameters)
  5984. : [];
  5985. if (strpos($metar, ',') === false) {
  5986. //get station name and alternative id
  5987. if (count($dataFinal['data']) > 0) {
  5988. $metarData = $this->getWeatherStationDataByHashId($dataFinal['data']);
  5989. $dataFinal['data'] = $metarData;
  5990. }
  5991. }
  5992. if ($genExcel) {
  5993. $csvData[] = ['Station ID', 'Station Name', 'Parameter(c)', 'Last Update'];
  5994. if (count($dataFinal['data']) > 0) {
  5995. foreach ($dataFinal['data'] as $key => $value) {
  5996. foreach ($value as $key1 => $value1) {
  5997. if (!is_array($value1)) {
  5998. $parameter = $value1;
  5999. }
  6000. if (is_array($value1)) {
  6001. foreach ($value1 as $key2 => $value2) {
  6002. $station_hash = $value2['station_id'] ?? null;
  6003. $station_name = $value2['station_name'] ?? null;
  6004. $station_id = $value2['station_alternativeIds'] ?? null;
  6005. if (is_array($value2['dates'])) {
  6006. foreach ($value2['dates'] as $key3 => $value3) {
  6007. $date = $value3['date'];
  6008. $value = $value3['value'];
  6009. $csvData[] = [$station_id, $station_name, $value, $date];
  6010. }
  6011. }
  6012. }
  6013. }
  6014. }
  6015. }
  6016. }
  6017. $xlsxReport = \Pimcore\Model\Asset::getByPath("/report/StationCsv/metar_station_weather_data.xlsx");
  6018. if ($xlsxReport !== null) {
  6019. $xlsxReport->delete();
  6020. }
  6021. $excelData = ExcelGenerator::createAndSaveXlsx($csvData, "metar_station_weather_data", true, 'StationCsv');
  6022. return $excelData;
  6023. }
  6024. // Set the data to Redis cache
  6025. $this->redisCache->set($cacheKey, $dataFinal);
  6026. return $dataFinal;
  6027. } catch (RequestException $e) {
  6028. throw new \Exception($e->getMessage());
  6029. } catch (\Exception $e) {
  6030. throw new \Exception($e->getMessage());
  6031. }
  6032. }
  6033. private function fetchMetarData($startDate, $endDate, $metar, $duration, $parameters)
  6034. {
  6035. $dataFinal = [];
  6036. while (!empty($parameters)) {
  6037. $batchParameters = array_splice($parameters, 0, 10); // Take up to 10 parameters
  6038. $batchQueryString = implode(',', $batchParameters);
  6039. $url = sprintf(
  6040. '%s/%s--%s:%s/%s/%s/json?source=mix-obs&on_invalid=fill_with_invalid',
  6041. $this->apiBaseUrl,
  6042. $startDate,
  6043. $endDate,
  6044. $duration,
  6045. $batchQueryString,
  6046. $metar
  6047. );
  6048. $client = new Client(['verify' => false]);
  6049. $response = $client->request('GET', $url, [
  6050. 'auth' => [$this->username, $this->password],
  6051. ]);
  6052. $statusCode = $response->getStatusCode();
  6053. $data = json_decode($response->getBody(), true);
  6054. if ($statusCode != 200) {
  6055. return $this->createErrorResponse($statusCode);
  6056. }
  6057. // Merge data from the current API call into the final data
  6058. $dataFinal['version'] = $data['version'];
  6059. $dataFinal['user'] = $data['user'];
  6060. $dataFinal['dateGenerated'] = $data['dateGenerated'];
  6061. $dataFinal['status'] = $data['status'];
  6062. $dataFinal['data'] = array_merge($dataFinal['data'] ?? [], $data['data']);
  6063. }
  6064. // Check if there is no comma in $metar
  6065. return $dataFinal;
  6066. }
  6067. public function getWeatherStationDataByHashId($metarData)
  6068. {
  6069. $response = [];
  6070. foreach ($metarData as &$metarParam) {
  6071. if (count($metarParam['coordinates']) > 0) {
  6072. foreach ($metarParam['coordinates'] as &$coordinates) {
  6073. $stationData = \Pimcore\Model\DataObject\WeatherStations::getByHash($coordinates['station_id'], true);
  6074. if ($stationData) {
  6075. $newData = [
  6076. 'station_name' => $stationData->getName(),
  6077. 'station_name_ar' => $stationData->getName('ar'),
  6078. 'station_wmo_id' => $stationData->getWmoId(),
  6079. 'station_alternativeIds' => $stationData->getAlternativeIds(),
  6080. ];
  6081. // Append the new data to the existing coordinates without reindexing
  6082. $coordinates += $newData;
  6083. }
  6084. }
  6085. // Reset array keys to be sequential
  6086. $metarParam['coordinates'] = array_values($metarParam['coordinates']);
  6087. }
  6088. }
  6089. return $metarData;
  6090. }
  6091. // public function getWeatherStatioData($startDate, $endDate, $parameters, $hash, $genExcel, $translator, $interval = 'PT24H')
  6092. // {
  6093. // try {
  6094. // if (!preg_match('/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/', $startDate)) {
  6095. // throw new \InvalidArgumentException($translator->trans('invalid_start_date_format'));
  6096. // }
  6097. // if (!preg_match('/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/', $startDate)) {
  6098. // throw new \InvalidArgumentException($translator->trans('invalid_end_date_format'));
  6099. // }
  6100. // $cacheKey = \App\Lib\Utility::generateKey($startDate, $endDate, $parameters, $hash);
  6101. // $cachedData = $this->redisCache->get($cacheKey);
  6102. // if ($cachedData !== null && !$genExcel) {
  6103. // // Return the cached data if available
  6104. // return $cachedData;
  6105. // }
  6106. // $dataFinal = [];
  6107. // while (!empty($parameters)) {
  6108. // $batchParameters = array_splice($parameters, 0, 10); // Take up to 10 parameters
  6109. // $batchQueryString = implode(',', $batchParameters);
  6110. // $url = sprintf(
  6111. // "%s/%s--%s:%s/%s/%s/json?source=mix-obs&on_invalid=fill_with_invalid",
  6112. // $this->apiBaseUrl,
  6113. // $startDate,
  6114. // $endDate,
  6115. // $interval,
  6116. // $batchQueryString,
  6117. // $hash
  6118. // );
  6119. // $client = new Client(['verify' => false]);
  6120. // $response = $client->request('GET', $url, [
  6121. // 'auth' => [$this->username, $this->password],
  6122. // ]);
  6123. // $statusCode = $response->getStatusCode();
  6124. // $data = json_decode($response->getBody(), true);
  6125. // if ($statusCode != 200) {
  6126. // return $this->createErrorResponse($statusCode);
  6127. // }
  6128. // // Merge data from the current API call into the final data
  6129. // $dataFinal['version'] = $data['version'];
  6130. // $dataFinal['user'] = $data['user'];
  6131. // $dataFinal['dateGenerated'] = $data['dateGenerated'];
  6132. // $dataFinal['status'] = $data['status'];
  6133. // $dataFinal['data'] = array_merge($dataFinal['data'] ?? [], $data['data']);
  6134. // }
  6135. // if (count($dataFinal['data']) > 0) {
  6136. // $metarData = $this->getWeatherStationDataByHashId($dataFinal['data']);
  6137. // $dataFinal['data'] = $metarData;
  6138. // }
  6139. // if ($genExcel) {
  6140. // $csvData[] = ['parameter', 'station id', 'station name', 'date', 'value'];
  6141. // if (count($dataFinal['data']) > 0) {
  6142. // foreach ($dataFinal['data'] as $key => $value) {
  6143. // foreach ($value as $key1 => $value1) {
  6144. // if (!is_array($value1)) {
  6145. // $parameter = $value1;
  6146. // }
  6147. // if (is_array($value1)) {
  6148. // foreach ($value1 as $key2 => $value2) {
  6149. // $station_hash = $value2['station_id'];
  6150. // $station_name = $value2['station_name'];
  6151. // $station_id = $value2['station_alternativeIds'];
  6152. // if (is_array($value2['dates'])) {
  6153. // foreach ($value2['dates'] as $key3 => $value3) {
  6154. // $date = $value3['date'];
  6155. // $value = $value3['value'];
  6156. // $csvData[] = [$parameter, $station_id, $station_name, $date, $value];
  6157. // }
  6158. // }
  6159. // }
  6160. // }
  6161. // }
  6162. // }
  6163. // }
  6164. // $xlsxReport = \Pimcore\Model\Asset::getByPath("/report/StationCsv/historical_weather_data.xlsx");
  6165. // if ($xlsxReport !== null) {
  6166. // $xlsxReport->delete();
  6167. // }
  6168. // $excelData = ExcelGenerator::createAndSaveXlsx($csvData, "historical_weather_data", true, 'StationCsv');
  6169. // return $excelData;
  6170. // }
  6171. // // Set the data to Redis cache
  6172. // $this->redisCache->set($cacheKey, $dataFinal);
  6173. // return $dataFinal;
  6174. // } catch (RequestException $e) {
  6175. // throw new \Exception($e->getMessage());
  6176. // } catch (\Exception $e) {
  6177. // throw new \Exception($e->getMessage());
  6178. // }
  6179. // }
  6180. public function getWeatherStatioData($startDate, $endDate, $parameters, $hash, $genExcel, $translator, $interval = 'PT24H')
  6181. {
  6182. try {
  6183. if (!preg_match('/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/', $startDate)) {
  6184. throw new \InvalidArgumentException($translator->trans('invalid_start_date_format'));
  6185. }
  6186. if (!preg_match('/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/', $endDate)) {
  6187. throw new \InvalidArgumentException($translator->trans('invalid_end_date_format'));
  6188. }
  6189. $timeResolutions = [
  6190. '_24h' => '+1 day',
  6191. '_12h' => '+12 hours',
  6192. '_6h' => '+6 hours',
  6193. '_3h' => '+3 hours',
  6194. '_1h' => '+1 hour'
  6195. ];
  6196. $adjustedParameters = [];
  6197. $nonAdjustedParameters = [];
  6198. foreach ($parameters as $parameter) {
  6199. $matched = false;
  6200. foreach ($timeResolutions as $key => $adjustment) {
  6201. if (strpos($parameter, $key) !== false) {
  6202. $matched = true;
  6203. $adjustedParameters[$adjustment][] = $parameter;
  6204. break;
  6205. }
  6206. }
  6207. if (!$matched) {
  6208. $nonAdjustedParameters[] = $parameter;
  6209. }
  6210. }
  6211. // Initialize $dataFinal
  6212. $dataFinal = [
  6213. 'version' => null,
  6214. 'user' => null,
  6215. 'dateGenerated' => null,
  6216. 'status' => null,
  6217. 'data' => []
  6218. ];
  6219. // Process adjusted parameters
  6220. foreach ($adjustedParameters as $adjustment => $params) {
  6221. $adjustedStartDate = (new \DateTime($startDate))->modify($adjustment)->format('Y-m-d\TH:i:s.v\Z');
  6222. $adjustedEndDate = (new \DateTime($endDate))->modify($adjustment)->format('Y-m-d\TH:i:s.v\Z');
  6223. $data = $this->fetchWeatherData($adjustedStartDate, $adjustedEndDate, $params, $hash, $interval);
  6224. // Revert dates to original range
  6225. if (isset($data['data']) && is_array($data['data'])) {
  6226. foreach ($data['data'] as &$datum) {
  6227. if (isset($datum['coordinates'][0]['dates']) && is_array($datum['coordinates'][0]['dates'])) {
  6228. foreach ($datum['coordinates'][0]['dates'] as &$date) {
  6229. if (isset($date['date'])) {
  6230. $date['date'] = (new \DateTime($date['date']))
  6231. ->modify('-' . ltrim($adjustment, '+'))
  6232. ->format('Y-m-d\TH:i:s.v\Z');
  6233. }
  6234. }
  6235. }
  6236. }
  6237. } else {
  6238. $data['data'] = []; // Ensure 'data' exists
  6239. }
  6240. // Merge into $dataFinal
  6241. if (empty($dataFinal['version'])) {
  6242. $dataFinal['version'] = $data['version'] ?? null;
  6243. $dataFinal['user'] = $data['user'] ?? null;
  6244. $dataFinal['dateGenerated'] = $data['dateGenerated'] ?? null;
  6245. $dataFinal['status'] = $data['status'] ?? null;
  6246. }
  6247. if (isset($data['data']) && is_array($data['data'])) {
  6248. $dataFinal['data'] = array_merge($dataFinal['data'], $data['data']);
  6249. }
  6250. }
  6251. // Process non-adjusted parameters
  6252. if (!empty($nonAdjustedParameters)) {
  6253. $data = $this->fetchWeatherData($startDate, $endDate, $nonAdjustedParameters, $hash, $interval);
  6254. if (isset($data['data']) && is_array($data['data'])) {
  6255. $dataFinal['data'] = array_merge($dataFinal['data'], $data['data']);
  6256. }
  6257. }
  6258. // Reorder data based on parameters
  6259. $dataFinal['data'] = isset($dataFinal['data']) && is_array($dataFinal['data'])
  6260. ? $this->orderResults($dataFinal['data'], $parameters)
  6261. : [];
  6262. if (count($dataFinal['data']) > 0) {
  6263. $metarData = $this->getWeatherStationDataByHashId($dataFinal['data']);
  6264. $dataFinal['data'] = $metarData;
  6265. }
  6266. if ($genExcel) {
  6267. return $this->generateExcel($dataFinal);
  6268. }
  6269. return $dataFinal;
  6270. } catch (RequestException $e) {
  6271. throw new \Exception($e->getMessage());
  6272. } catch (\Exception $e) {
  6273. throw new \Exception($e->getMessage());
  6274. }
  6275. }
  6276. private function fetchWeatherData($startDate, $endDate, $parameters, $hash, $interval)
  6277. {
  6278. $dataFinal = [];
  6279. while (!empty($parameters)) {
  6280. $batchParameters = array_splice($parameters, 0, 10);
  6281. $batchQueryString = implode(',', $batchParameters);
  6282. $url = sprintf(
  6283. "%s/%s--%s:%s/%s/%s/json?source=mix-obs&on_invalid=fill_with_invalid",
  6284. $this->apiBaseUrl,
  6285. $startDate,
  6286. $endDate,
  6287. $interval,
  6288. $batchQueryString,
  6289. $hash
  6290. );
  6291. $client = new Client(['verify' => false]);
  6292. $response = $client->request('GET', $url, [
  6293. 'auth' => [$this->username, $this->password],
  6294. ]);
  6295. if ($response->getStatusCode() != 200) {
  6296. throw new \Exception("Failed to fetch data from API");
  6297. }
  6298. $data = json_decode($response->getBody(), true);
  6299. $dataFinal = array_merge_recursive($dataFinal, $data);
  6300. }
  6301. return $dataFinal;
  6302. }
  6303. private function generateExcel($data)
  6304. {
  6305. $csvData[] = ['parameter', 'station id', 'station name', 'date', 'value'];
  6306. if (count($data['data']) > 0) {
  6307. foreach ($data['data'] as $key => $value) {
  6308. foreach ($value as $key1 => $value1) {
  6309. if (!is_array($value1)) {
  6310. $parameter = $value1;
  6311. }
  6312. if (is_array($value1)) {
  6313. foreach ($value1 as $key2 => $value2) {
  6314. $station_hash = $value2['station_id'];
  6315. $station_name = $value2['station_name'];
  6316. $station_id = $value2['station_alternativeIds'];
  6317. if (is_array($value2['dates'])) {
  6318. foreach ($value2['dates'] as $key3 => $value3) {
  6319. $date = $value3['date'];
  6320. $value = $value3['value'];
  6321. $csvData[] = [$parameter, $station_id, $station_name, $date, $value];
  6322. }
  6323. }
  6324. }
  6325. }
  6326. }
  6327. }
  6328. }
  6329. $xlsxReport = \Pimcore\Model\Asset::getByPath("/report/StationCsv/historical_weather_data.xlsx");
  6330. if ($xlsxReport !== null) {
  6331. $xlsxReport->delete();
  6332. }
  6333. $excelData = ExcelGenerator::createAndSaveXlsx($csvData, "historical_weather_data", true, 'StationCsv');
  6334. return $excelData;
  6335. }
  6336. private function orderResults($data, $parameters)
  6337. {
  6338. $orderedResults = [];
  6339. foreach ($parameters as $parameter) {
  6340. foreach ($data as $key => $item) {
  6341. if ($item['parameter'] === $parameter) {
  6342. $orderedResults[] = $item;
  6343. break;
  6344. }
  6345. }
  6346. }
  6347. return $orderedResults;
  6348. }
  6349. // Assuming $data1 and $data2 contain the response data from URL1 and URL2 respectively
  6350. // and that the relevant date fields in these responses are in a format that can be converted to a timestamp
  6351. function adjustResponseDates(&$data, $hours)
  6352. {
  6353. error_log(print_r($data, true)); // Log the initial data
  6354. if ($hours == 24) {
  6355. $timeAdjustment = '-1 day';
  6356. } elseif ($hours == 1) {
  6357. $timeAdjustment = '-1 hour';
  6358. } elseif ($hours == 12) {
  6359. $timeAdjustment = '-12 hour';
  6360. } else {
  6361. $timeAdjustment = '';
  6362. }
  6363. foreach ($data as &$parameter) { // Iterate through each parameter
  6364. if ($timeAdjustment && isset($parameter['coordinates'])) {
  6365. foreach ($parameter['coordinates'] as $coordKey => $coordinate) {
  6366. if (isset($coordinate['dates']) && is_array($coordinate['dates'])) {
  6367. foreach ($coordinate['dates'] as $dateKey => $dateInfo) {
  6368. if (isset($dateInfo['date'])) {
  6369. // p_r($dateInfo['date']);exit;
  6370. $adjustedDate = date('Y-m-d\TH:i:s.v\Z', strtotime($timeAdjustment, strtotime($dateInfo['date'])));
  6371. $parameter['coordinates'][$coordKey]['dates'][$dateKey]['date'] = $adjustedDate;
  6372. error_log("Adjusted Date: " . $adjustedDate); // Log each adjusted date
  6373. }
  6374. }
  6375. }
  6376. }
  6377. }
  6378. }
  6379. unset($parameter); // Break the reference with the last element
  6380. error_log("Final Data: " . print_r($data, true)); // Log the final modified data
  6381. }
  6382. }