You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

191 lines
5.5 KiB

  1. <?php
  2. namespace p3k;
  3. use Config;
  4. use DateTime, DateTimeZone;
  5. class Weather {
  6. public static function weather_for_location($lat, $lng, $key) {
  7. $data = self::_fetch($lat, $lng, $key);
  8. if(!$data) return null;
  9. if(!property_exists($data, 'current_observation'))
  10. return null;
  11. $current = $data->current_observation;
  12. $weather = [
  13. 'description' => null,
  14. 'icon' => [
  15. 'url' => null,
  16. 'name' => null
  17. ],
  18. 'temp' => null,
  19. 'feelslike' => null,
  20. 'humidity' => null,
  21. 'wind' => null,
  22. 'pressure' => null,
  23. 'precip_today' => null,
  24. 'timezone' => [
  25. 'offset' => null,
  26. 'name' => null,
  27. 'abbr' => null
  28. ]
  29. ];
  30. if($current) {
  31. $loc = $current->display_location;
  32. if($loc) {
  33. $weather['location'] = [
  34. 'city' => $loc->city,
  35. 'state' => $loc->state,
  36. 'country' => $loc->country,
  37. 'zip' => $loc->zip
  38. ];
  39. $sunny = self::_sunny($current->display_location->latitude, $current->display_location->longitude, $current->local_tz_long);
  40. } else {
  41. $sunny = ['light'=>'day'];
  42. }
  43. $icon_name = self::_icon_name($current->icon, $sunny['light']);
  44. $weather['sun'] = $sunny;
  45. $weather['description'] = $current->weather;
  46. $weather['icon']['url'] = $current->icon_url;
  47. $weather['icon']['name'] = $icon_name;
  48. $weather['temp'] = [
  49. 'num' => (double)$current->temp_f,
  50. 'unit' => '°F'
  51. ];
  52. $weather['feelslike'] = [
  53. 'num' => (double)$current->feelslike_f,
  54. 'unit' => '°F'
  55. ];
  56. $weather['wind'] = [
  57. 'num' => $current->wind_mph,
  58. 'unit' => 'mph'
  59. ];
  60. $weather['pressure'] = [
  61. 'num' => (int)$current->pressure_mb,
  62. 'unit' => 'mb'
  63. ];
  64. $weather['precip_today'] = [
  65. 'num' => (double)$current->precip_today_in,
  66. 'unit' => 'in'
  67. ];
  68. $weather['humidity'] = [
  69. 'num' => (int)str_replace('%','',$current->relative_humidity),
  70. 'unit' => '%'
  71. ];
  72. $weather['timezone']['offset'] = $current->local_tz_offset;
  73. $weather['timezone']['name'] = $current->local_tz_long;
  74. $weather['timezone']['abbr'] = $current->local_tz_short;
  75. }
  76. #$weather['raw'] = $current;
  77. return $weather;
  78. }
  79. private static function _fetch($lat, $lng, $key) {
  80. $ch = curl_init();
  81. curl_setopt($ch, CURLOPT_URL, 'http://api.wunderground.com/api/'.$key.'/conditions/q/'.$lat.','.$lng.'.json');
  82. curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
  83. // curl_setopt($ch, CURLOPT_USERAGENT, '');
  84. curl_setopt($ch, CURLOPT_TIMEOUT, 5);
  85. $result = curl_exec($ch);
  86. if($result == FALSE)
  87. return FALSE;
  88. return json_decode($result);
  89. }
  90. // Returns "day" or "night" depending on whether the sun is up at the given location
  91. private static function _sunny($lat, $lng, $timezone) {
  92. // Get the beginning of the current day
  93. $now = new DateTime();
  94. if(!$timezone) {
  95. $timezone = \p3k\Timezone::timezone_for_location($lat, $lng);
  96. }
  97. $tz = new DateTimeZone($timezone);
  98. $now->setTimeZone($tz);
  99. $offset = $now->format('Z')/3600;
  100. $now = $now->format('H') + ($now->format('i')/60);
  101. if($lat !== null) {
  102. $sunrise = date_sunrise($now, SUNFUNCS_RET_DOUBLE, $lat, $lng, 108, $offset);
  103. $sunset = date_sunset($now, SUNFUNCS_RET_DOUBLE, $lat, $lng, 108, $offset);
  104. return [
  105. 'sunrise' => round($sunrise,2),
  106. 'sunset' => round($sunset,2),
  107. 'now' => round($now,2),
  108. 'light' => ($sunrise < $now && $now < $sunset) ? 'day' : 'night',
  109. ];
  110. } else {
  111. return [
  112. 'light' => 'unknown'
  113. ];
  114. }
  115. }
  116. private static function _icon_name($icon, $sunny) {
  117. // This list is from http://www.wunderground.com/weather/api/d/docs?d=resources/icon-sets
  118. // A mapping of wunderground to weather-icons is here https://erikflowers.github.io/weather-icons/api-list.html
  119. $map = [
  120. 'day' => [
  121. 'chanceflurries' => 'snow-wind',
  122. 'chancerain' => 'day-rain',
  123. 'chancesleet' => 'sleet',
  124. 'chancesnow' => 'snow',
  125. 'chancetstorms' => 'thunderstorm',
  126. 'clear' => 'day-sunny',
  127. 'cloudy' => 'cloudy',
  128. 'flurries' => 'snow-wind',
  129. 'fog' => 'day-haze',
  130. 'hazy' => 'day-haze',
  131. 'mostlycloudy' => 'cloud',
  132. 'mostlysunny' => 'day-sunny-overcast',
  133. 'partlycloudy' => 'day-cloudy',
  134. 'partlysunny' => 'day-sunny-overcast',
  135. 'sleet' => 'sleet',
  136. 'rain' => 'rain',
  137. 'snow' => 'snow',
  138. 'sunny' => 'day-sunny',
  139. 'tstorms' => 'thunderstorm',
  140. ],
  141. 'night' => [
  142. 'chanceflurries' => 'night-snow-wind',
  143. 'chancerain' => 'night-rain',
  144. 'chancesleet' => 'night-alt-sleet',
  145. 'chancesnow' => 'night-snow',
  146. 'chancetstorms' => 'night-thunderstorm',
  147. 'clear' => 'night-clear',
  148. 'cloudy' => 'cloudy',
  149. 'flurries' => 'night-alt-snow-wind',
  150. 'fog' => 'night-fog',
  151. 'hazy' => 'night-fog',
  152. 'mostlycloudy' => 'night-alt-cloudy',
  153. 'mostlysunny' => 'night-clear',
  154. 'partlycloudy' => 'night-alt-partly-cloudy',
  155. 'partlysunny' => 'night-alt-partly-cloudy',
  156. 'sleet' => 'night-alt-sleet',
  157. 'rain' => 'night-alt-showers',
  158. 'snow' => 'night-alt-snow',
  159. 'sunny' => 'night-clear',
  160. 'tstorms' => 'night-alt-thunderstorm',
  161. ]
  162. ];
  163. if(array_key_exists($icon, $map[$sunny])) {
  164. return 'wi-'.$map[$sunny][$icon];
  165. } else {
  166. return false;
  167. }
  168. }
  169. }