From bcfec3e180827414353d5449e4be6010c72914a6 Mon Sep 17 00:00:00 2001 From: Skylar Ittner Date: Sun, 16 May 2021 16:36:19 -0600 Subject: [PATCH] Add weather and reverse geocode APIs --- apiconfig.php | 23 +++ authenticator.php | 3 + endpoints/gis.geocode.reverse.php | 43 ++++++ endpoints/gis.weather.php | 239 ++++++++++++++++++++++++++++++ 4 files changed, 308 insertions(+) create mode 100644 endpoints/gis.geocode.reverse.php create mode 100644 endpoints/gis.weather.php diff --git a/apiconfig.php b/apiconfig.php index 1d2bdd0..987c6b9 100644 --- a/apiconfig.php +++ b/apiconfig.php @@ -12,6 +12,29 @@ $APIS = [ "vars" => [ ] ], +// "gis/geocode" => [ +// "load" => "gis.geocode.php", +// "vars" => [ +// "latitude" => "/\-?[0-9]{1,3}(\.[0-9]{0,10})?/", +// "longitude" => "/\-?[0-9]{1,3}(\.[0-9]{0,10})?/", +// "nocache (optional)" => "" +// ] +// ], + "gis/geocode/reverse" => [ + "load" => "gis.geocode.reverse.php", + "vars" => [ + "latitude" => "/\-?[0-9]{1,3}(\.[0-9]{0,10})?/", + "longitude" => "/\-?[0-9]{1,3}(\.[0-9]{0,10})?/" + ] + ], + "gis/weather" => [ + "load" => "gis.weather.php", + "vars" => [ + "latitude" => "/\-?[0-9]{1,3}(\.[0-9]{0,10})?/", + "longitude" => "/\-?[0-9]{1,3}(\.[0-9]{0,10})?/", + "nocache (optional)" => "" + ] + ], "network/whois" => [ "load" => "network.whois.php", "vars" => [ diff --git a/authenticator.php b/authenticator.php index eb6675d..7a7fd59 100644 --- a/authenticator.php +++ b/authenticator.php @@ -12,5 +12,8 @@ * @return bool true to let the request in, false otherwise */ function authenticaterequest(): bool { + if (strpos($_SERVER["REMOTE_ADDR"], "127.") === 0) { + return true; + } return true; } \ No newline at end of file diff --git a/endpoints/gis.geocode.reverse.php b/endpoints/gis.geocode.reverse.php new file mode 100644 index 0000000..9828b9c --- /dev/null +++ b/endpoints/gis.geocode.reverse.php @@ -0,0 +1,43 @@ +get("gis.geocode.reverse.$lat,$lon"); +if ($cacheresp !== false) { + exitWithJson(json_decode($cacheresp, true)); +} + +$json = file_get_contents("http://www.mapquestapi.com/geocoding/v1/reverse?outFormat=json&thumbMaps=false&location=$lat,$lon&key=" . env("mapquest_key", "")); + +$geocode = json_decode($json, TRUE); + +$location = $geocode['results'][0]['locations'][0]; + +$output = [ + "status" => "OK", + "address" => [ + "street" => $location['street'], + "city" => $location['adminArea5'], + "county" => $location['adminArea4'], + "state" => $location['adminArea3'], + "country" => $location['adminArea1'], + "postalCode" => $location['postalCode'] + ], + "coords" => [ + $lat, + $lon + ] +]; + +$memcache->set("gis.geocode.reverse.$lat,$lon", json_encode($output)); +exitWithJson($output); diff --git a/endpoints/gis.weather.php b/endpoints/gis.weather.php new file mode 100644 index 0000000..41a4d7a --- /dev/null +++ b/endpoints/gis.weather.php @@ -0,0 +1,239 @@ +get("gis.weather.$lat,$lon"); + if ($cacheresp !== false) { + exitWithJson(json_decode($cacheresp, true)); + } +} + +$json = file_get_contents("https://api.openweathermap.org/data/2.5/onecall?lat=$lat&lon=$lon&units=imperial&appid=" . env("openweathermap_appid", "")); +$weather = json_decode($json, TRUE); + +$minutely = []; + +foreach ($weather["minutely"] as $m) { + $minutely[] = [ + "time" => $m["dt"], + "precip" => round($m["precipitation"] / 25.4, 3) + ]; +} +$hourly = []; +$mintemp = false; +$maxtemp = false; +$maxwind = false; +$precipchance = false; +$preciptype = ""; +$maxcloudcover = false; +$cloudcovers = []; // For calculating average +$summaryicon = "clear"; +$oldicon = "clear"; +$nowicon = "clear"; +$lasttimestamp = time(); + +foreach ($weather["hourly"] as $w) { + if ($w["dt"] > strtotime("now + " . env("weather_summary_hours", 8) . " hours")) { + continue; + } + + $hourly[] = [ + "time" => $w["dt"], + "temp" => $w["temp"], + "feelslike" => $w["feels_like"], + "windspeed" => $w["wind_speed"], + "winddirection" => $w["wind_deg"], + "precipchance" => $w["pop"] + ]; + + if ($mintemp === false) { + $mintemp = $w["temp"]; + } + if ($maxtemp === false) { + $maxtemp = $w["temp"]; + } + if ($maxwind === false) { + $maxwind = $w["wind_speed"]; + } + if ($precipchance === false) { + $precipchance = $w["pop"]; + } + if ($maxcloudcover === false) { + $maxcloudcover = $w["clouds"]; + } + $mintemp = min($w["temp"], $mintemp); + $maxtemp = max($w["temp"], $maxtemp); + $maxwind = max($w["wind_speed"], $maxwind); + $precipchance = max($w["pop"], $precipchance); + $maxcloudcover = max($w["clouds"], $maxcloudcover); + $cloudcovers[] = $w["clouds"]; + $lasttimestamp = max($w["dt"], $lasttimestamp); +} + +$avgcloudcover = array_sum($cloudcovers) / count($cloudcovers); + +if ($precipchance > 0.2) { + $summaryicon = "rain"; + $oldicon = "rain"; + if ($maxtemp < 28) { + $summaryicon = "snow"; + $oldicon = "snow"; + } +} else if ($mintemp > 55 && $maxtemp > 85 && $avgcloudcover <= 25) { + $summaryicon = "heat"; +} else if ($maxwind >= 20) { + $summaryicon = "windy"; + $oldicon = "windy"; +} else if ($avgcloudcover >= 75 || $maxcloudcover >= 90) { + $summaryicon = "cloudy"; + $oldicon = "cloudy"; +} else if ($avgcloudcover <= 15) { + $summaryicon = "mostly-sunny"; + $oldicon = "partly-cloudy"; +} else if ($maxcloudcover >= 33) { + $summaryicon = "partly-cloudy"; + $oldicon = "partly-cloudy"; +} + +if (((string) $weather["current"]["weather"][0]["id"])[0] == "2") { + $nowicon = "rain"; +} else if (((string) $weather["current"]["weather"][0]["id"])[0] == "3") { + $nowicon = "rain"; +} else if (((string) $weather["current"]["weather"][0]["id"])[0] == "5") { + $nowicon = "rain"; +} else if (((string) $weather["current"]["weather"][0]["id"])[0] == "6") { + $nowicon = "snow"; +} else if (((string) $weather["current"]["weather"][0]["id"])[0] == "7") { + $nowicon = "fog"; + if ($weather["current"]["weather"][0]["id"] == 781) { + // Tornado!!! + $nowicon = "hazard"; + } +} else if ($weather["current"]["weather"][0]["id"] == 801) { + $nowicon = "mostly-sunny"; +} else if ($weather["current"]["weather"][0]["id"] == 802) { + $nowicon = "partly-cloudy"; +} else if ($weather["current"]["weather"][0]["id"] >= 803) { + $nowicon = "cloudy"; +} +if (($nowicon == "mostly-sunny" || $nowicon == "clear" || $nowicon == "partly-cloudy") && $weather["current"]["feels_like"] > 80) { + $nowicon = "heat"; +} + +$dailyforecast = []; + +foreach ($weather["daily"] as $w) { + $dailyforecast[] = [ + "date" => $w["dt"], + "temp" => [ + "min" => $w["temp"]["min"], + "max" => $w["temp"]["max"] + ], + "precipitation" => [ + "chance" => empty($w["pop"]) ? 0 : $w["pop"] + ], + "windspeed" => $w["wind_speed"], + "uv_index" => round($w["uvi"], 1) + ]; +} + +$reverse_geocode_url = ($_SERVER['SERVER_PORT'] == 443 ? "https://" : "http://") . $_SERVER['HTTP_HOST'] . str_replace("/gis/weather/", "/gis/geocode/reverse/", $_SERVER['REQUEST_URI']); +$geocode = json_decode(file_get_contents($reverse_geocode_url), TRUE); + +$location_name = ""; + +$epa_uv_index = null; + +if ($geocode["status"] == "OK") { + $location_name = implode(", ", [$geocode["address"]["city"], $geocode["address"]["state"]]); + $city = $geocode["address"]["city"]; + $state = $geocode["address"]["state"]; + try { + $epa_url = "https://enviro.epa.gov/enviro/efservice/getEnvirofactsUVHOURLY/CITY/$city/STATE/$state/JSON"; + + $epa_data = json_decode(ApiFetcher::get($epa_url), true); + + // Find which returned result is closest to the current date and time + $closest = $epa_data[0]; + foreach ($epa_data as $entry) { + if (abs(time() - strtotime(str_replace("/", " ", $entry["DATE_TIME"]))) < abs(time() - strtotime(str_replace("/", " ", $closest["DATE_TIME"])))) { + $closest = $entry; + } + } + $epa_uv_index = min($closest["UV_VALUE"], 11); + } catch (Exception $ex) { + $epa_uv_index = null; + } +} else { + $location_name = "Unknown"; +} + +$output = [ + "status" => "OK", + "summary" => [ + "icon" => $summaryicon, + "temp" => [ + "min" => $mintemp, + "max" => $maxtemp + ], + "precipitation" => [ + "type" => $preciptype, + "chance" => $precipchance + ], + "windspeed" => $maxwind, + "cloudcover" => $maxcloudcover, + "cloudcover_avg" => $avgcloudcover, + "forecast_hours" => env("weather_summary_hours", 8), + "forecast_until" => $lasttimestamp + ], + "now" => [ + "icon" => $nowicon, + "temp" => $weather["current"]["temp"], + "feelslike" => $weather["current"]["feels_like"], + "uv_index" => (is_null($epa_uv_index) ? min(round($weather["current"]["uvi"], 1), 11) : $epa_uv_index), + "windspeed" => $weather["current"]["wind_speed"], + "winddirection" => $weather["current"]["wind_deg"] + ], + "today" => [ + "minutely" => $minutely, + "hourly" => $hourly + ], + "forecast" => $dailyforecast, + // From here to the next comment is deprecated + "icon" => $oldicon, + "temp" => [ + "min" => $mintemp, + "max" => $maxtemp + ], + "precipitation" => [ + "type" => $preciptype, + "chance" => $precipchance + ], + "windspeed" => $maxwind, + "cloudcover" => $maxcloudcover, + "cloudcover_avg" => $avgcloudcover, + "forecast_hours" => env("weather_summary_hours", 8), + "forecast_until" => $lasttimestamp, + // the next comment + "location_name" => $location_name, + "latitude" => $lat, + "longitude" => $lon, + "source" => [ + "text" => "Data: OpenWeatherMap.org" . (is_null($epa_uv_index) ? "" : ", EPA"), + "url" => "https://openweathermap.org" + ] +]; + +$memcache->set("gis.weather.$lat,$lon", json_encode($output), 60 * 10); +exitWithJson($output);