Skip to content

Updated the UVIndex implementation #132

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
May 29, 2019
12 changes: 8 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ language: php

matrix:
include:
- php: hhvm
- php: nightly
- php: 7.3
- php: 7.2
- php: 7.1
- php: 7.0
Expand All @@ -19,11 +19,15 @@ matrix:
allow_failures:
- php: nightly

install:
- if [[ ! $TRAVIS_PHP_VERSION = hhvm* ]]; then phpenv config-rm xdebug.ini || echo "xdebug not available"; fi
- if [[ ! $TRAVIS_PHP_VERSION = hhvm* ]]; then INI_FILE=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; else INI_FILE=/etc/hhvm/php.ini; fi
before_install:
- phpenv config-rm xdebug.ini || echo "xdebug not available"
- INI_FILE=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
- echo date.timezone = Europe/Berlin >> $INI_FILE
- echo memory_limit = -1 >> $INI_FILE

install:
# Use composer update instead of composer install to install a working set of
# dependencies on all PHP versions.
- composer update

script:
Expand Down
187 changes: 100 additions & 87 deletions Cmfcmf/OpenWeatherMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class OpenWeatherMap
/**
* @var string The basic api url to fetch uv index data from.
*/
private $uvIndexUrl = 'https://api.openweathermap.org/v3/uvi';
private $uvIndexUrl = 'https://api.openweathermap.org/data/2.5/uvi';

/**
* @var AbstractCache|bool $cache The cache to use.
Expand Down Expand Up @@ -315,47 +315,73 @@ public function getWeatherHistory($query, \DateTime $start, $endOrCount = 1, $ty
/**
* Returns the current uv index at the location you specified.
*
* @param float $lat The location's latitude.
* @param float $lon The location's longitude.
* @param float $lat The location's latitude.
* @param float $lon The location's longitude.
*
* @throws OpenWeatherMap\Exception If OpenWeatherMap returns an error.
* @throws \InvalidArgumentException If an argument error occurs.
*
* @return UVIndex The uvi object.
* @return UVIndex
*
* @api
*/
public function getCurrentUVIndex($lat, $lon)
{
$answer = $this->getRawCurrentUVIndexData($lat, $lon);
$answer = $this->getRawUVIndexData('current', $lat, $lon);
$json = $this->parseJson($answer);

return new UVIndex($json);
}

/**
* Returns the uv index at date, time and location you specified.
* Returns a forecast of the uv index at the specified location.
* The optional $cnt parameter determines the number of days to forecase.
* The maximum supported number of days is 8.
*
* @param float $lat The location's latitude.
* @param float $lon The location's longitude.
* @param \DateTimeInterface $dateTime The date and time to request data for.
* @param string $timePrecision This decides about the timespan OWM will look for the uv index. The tighter
* the timespan, the less likely it is to get a result. Can be 'year', 'month',
* 'day', 'hour', 'minute' or 'second', defaults to 'day'.
* @param float $lat The location's latitude.
* @param float $lon The location's longitude.
* @param int $cnt Number of returned days (default to 8).
*
* @throws OpenWeatherMap\Exception If OpenWeatherMap returns an error.
* @throws \InvalidArgumentException If an argument error occurs.
*
* @return UVIndex The uvi object.
* @return UVIndex[]
*
* @api
*/
public function getUVIndex($lat, $lon, $dateTime, $timePrecision = 'day')
public function getForecastUVIndex($lat, $lon, $cnt = 8)
{
$answer = $this->getRawUVIndexData($lat, $lon, $dateTime, $timePrecision);
$json = $this->parseJson($answer);
$answer = $this->getRawUVIndexData('forecast', $lat, $lon, $cnt);
$data = $this->parseJson($answer);

return new UVIndex($json);
return array_map(function ($entry) {
return new UVIndex($entry);
}, $data);
}

/**
* Returns the historic uv index at the specified location.
*
* @param float $lat The location's latitude.
* @param float $lon The location's longitude.
* @param \DateTime $start Starting point of time period.
* @param \DateTime $end Final point of time period.
*
* @throws OpenWeatherMap\Exception If OpenWeatherMap returns an error.
* @throws \InvalidArgumentException If an argument error occurs.
*
* @return UVIndex[]
*
* @api
*/
public function getHistoricUVIndex($lat, $lon, $start, $end)
{
$answer = $this->getRawUVIndexData('historic', $lat, $lon, null, $start, $end);
$data = $this->parseJson($answer);

return array_map(function ($entry) {
return new UVIndex($entry);
}, $data);
}

/**
Expand Down Expand Up @@ -492,55 +518,45 @@ public function getRawWeatherHistory($query, \DateTime $start, $endOrCount = 1,
}

/**
* Directly returns the json string returned by OpenWeatherMap for the current UV index data.
* Directly returns the json string returned by OpenWeatherMap for the UV index data.
*
* @param float $lat The location's latitude.
* @param float $lon The location's longitude.
* @param string $mode The type of requested data (['historic', 'forecast', 'current']).
* @param float $lat The location's latitude.
* @param float $lon The location's longitude.
* @param int $cnt Number of returned days (only allowed for 'forecast' data).
* @param \DateTime $start Starting point of time period (only allowed and required for 'historic' data).
* @param \DateTime $end Final point of time period (only allowed and required for 'historic' data).
*
* @return bool|string Returns the fetched data.
*
* @api
*/
public function getRawCurrentUVIndexData($lat, $lon)
public function getRawUVIndexData($mode, $lat, $lon, $cnt = null, $start = null, $end = null)
{
if (!$this->apiKey) {
throw new \RuntimeException('Before using this method, you must set the api key using ->setApiKey()');
if (!in_array($mode, array('current', 'forecast', 'historic'), true)) {
throw new \InvalidArgumentException("$mode must be one of 'historic', 'forecast', 'current'.");
}
if (!is_float($lat) || !is_float($lon)) {
throw new \InvalidArgumentException('$lat and $lon must be floating point numbers');
}
$url = $this->buildUVIndexUrl($lat, $lon);

return $this->cacheOrFetchResult($url);
}

/**
* Directly returns the json string returned by OpenWeatherMap for the UV index data.
*
* @param float $lat The location's latitude.
* @param float $lon The location's longitude.
* @param \DateTimeInterface $dateTime The date and time to request data for.
* @param string $timePrecision This decides about the timespan OWM will look for the uv index. The tighter
* the timespan, the less likely it is to get a result. Can be 'year', 'month',
* 'day', 'hour', 'minute' or 'second', defaults to 'day'.
*
* @return bool|string Returns the fetched data.
*
* @api
*/
public function getRawUVIndexData($lat, $lon, $dateTime, $timePrecision = 'day')
{
if (!$this->apiKey) {
throw new \RuntimeException('Before using this method, you must set the api key using ->setApiKey()');
if (isset($cnt) && (!is_int($cnt) || $cnt > 8 || $cnt < 1)) {
throw new \InvalidArgumentException('$cnt must be an int between 1 and 8');
}
if (!is_float($lat) || !is_float($lon)) {
throw new \InvalidArgumentException('$lat and $lon must be floating point numbers');
if (isset($start) && !$start instanceof \DateTime) {
throw new \InvalidArgumentException('$start must be an instance of \DateTime');
}
if (isset($end) && !$end instanceof \DateTime) {
throw new \InvalidArgumentException('$end must be an instance of \DateTime');
}
if (interface_exists('DateTimeInterface') && !$dateTime instanceof \DateTimeInterface || !$dateTime instanceof \DateTime) {
throw new \InvalidArgumentException('$dateTime must be an instance of \DateTime or \DateTimeInterface');
if ($mode === 'current' && (isset($start) || isset($end) || isset($cnt))) {
throw new \InvalidArgumentException('Neither $start, $end, nor $cnt must be set for current data.');
} elseif ($mode === 'forecast' && (isset($start) || isset($end) || !isset($cnt))) {
throw new \InvalidArgumentException('$cnt needs to be set and both $start and $end must not be set for forecast data.');
} elseif ($mode === 'historic' && (!isset($start) || !isset($end) || isset($cnt))) {
throw new \InvalidArgumentException('Both $start and $end need to be set and $cnt must not be set for historic data.');
}
$url = $this->buildUVIndexUrl($lat, $lon, $dateTime, $timePrecision);

$url = $this->buildUVIndexUrl($mode, $lat, $lon, $cnt, $start, $end);
return $this->cacheOrFetchResult($url);
}

Expand Down Expand Up @@ -575,10 +591,12 @@ private function cacheOrFetchResult($url)
/** @var AbstractCache $cache */
$cache = $this->cache;
$cache->setSeconds($this->seconds);

if ($cache->isCached($url)) {
$this->wasCached = true;
return $cache->getCached($url);
}

$result = $this->fetcher->fetch($url);
$cache->setCached($url, $result);
} else {
Expand Down Expand Up @@ -612,47 +630,41 @@ private function buildUrl($query, $units, $lang, $appid, $mode, $url)
}

/**
* @param float $lat
* @param float $lon
* @param \DateTime|\DateTimeImmutable $dateTime
* @param string $timePrecision
* @param string $mode The type of requested data.
* @param float $lat The location's latitude.
* @param float $lon The location's longitude.
* @param int $cnt Number of returned days.
* @param \DateTime $start Starting point of time period.
* @param \DateTime $end Final point of time period.
*
* @return string
*/
private function buildUVIndexUrl($lat, $lon, $dateTime = null, $timePrecision = null)
private function buildUVIndexUrl($mode, $lat, $lon, $cnt = null, \DateTime $start = null, \DateTime $end = null)
{
if ($dateTime !== null) {
$format = '\Z';
switch ($timePrecision) {
/** @noinspection PhpMissingBreakStatementInspection */
case 'second':
$format = ':s' . $format;
/** @noinspection PhpMissingBreakStatementInspection */
case 'minute':
$format = ':i' . $format;
/** @noinspection PhpMissingBreakStatementInspection */
case 'hour':
$format = '\TH' . $format;
/** @noinspection PhpMissingBreakStatementInspection */
case 'day':
$format = '-d' . $format;
/** @noinspection PhpMissingBreakStatementInspection */
case 'month':
$format = '-m' . $format;
case 'year':
$format = 'Y' . $format;
break;
default:
throw new \InvalidArgumentException('$timePrecision is invalid.');
}
// OWM only accepts UTC timezones.
$dateTime->setTimezone(new \DateTimeZone('UTC'));
$dateTime = $dateTime->format($format);
} else {
$dateTime = 'current';
$params = array(
'appid' => $this->apiKey,
'lat' => $lat,
'lon' => $lon,
);

switch ($mode) {
case 'historic':
$requestMode = '/history';
$params['start'] = $start->format('U');
$params['end'] = $end->format('U');
break;
case 'forecast':
$requestMode = '/forecast';
$params['cnt'] = $cnt;
break;
case 'current':
$requestMode = '';
break;
default:
throw new \InvalidArgumentException("Invalid mode $mode for uv index url");
}

return sprintf($this->uvIndexUrl . '/%s,%s/%s.json?appid=%s', $lat, $lon, $dateTime, $this->apiKey);
return sprintf($this->uvIndexUrl . '%s?%s', $requestMode, http_build_query($params));
}

/**
Expand Down Expand Up @@ -718,7 +730,8 @@ private function parseJson($answer)
{
$json = json_decode($answer);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new OWMException('OpenWeatherMap returned an invalid json object. JSON error was: ' . $this->json_last_error_msg());
throw new OWMException('OpenWeatherMap returned an invalid json object. JSON error was: "' .
$this->json_last_error_msg() . '". The retrieved json was: ' . $answer);
}
if (isset($json->message)) {
throw new OWMException('An error occurred: '. $json->message);
Expand Down
6 changes: 3 additions & 3 deletions Cmfcmf/OpenWeatherMap/UVIndex.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ class UVIndex
public function __construct($data)
{
$utctz = new \DateTimeZone('UTC');
$this->time = new \DateTime($data->time, $utctz);
$this->location = new Location($data->location->latitude, $data->location->longitude);
$this->uvIndex = (float)$data->data;
$this->time = new \DateTime($data->date_iso, $utctz);
$this->location = new Location($data->lat, $data->lon);
$this->uvIndex = (float)$data->value;
}
}
63 changes: 63 additions & 0 deletions Examples/UVIndex.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php
/**
* OpenWeatherMap-PHP-API — A php api to parse weather data from http://www.OpenWeatherMap.org .
*
* @license MIT
*
* Please see the LICENSE file distributed with this source code for further
* information regarding copyright and licensing.
*
* Please visit the following links to read about the usage policies and the license of
* OpenWeatherMap before using this class:
*
* @see http://www.OpenWeatherMap.org
* @see http://www.OpenWeatherMap.org/terms
* @see http://openweathermap.org/appid
*/
use Cmfcmf\OpenWeatherMap;
use Cmfcmf\OpenWeatherMap\Exception as OWMException;

require_once __DIR__ . '/bootstrap.php';

$cli = false;
$lf = '<br>';
if (php_sapi_name() === 'cli') {
$lf = "\n";
$cli = true;
}

// Language of data (try your own language here!):
$lang = 'de';

// Units (can be 'metric' or 'imperial' [default]):
$units = 'metric';

// Get OpenWeatherMap object. Don't use caching (take a look into Example_Cache.php to see how it works).
$owm = new OpenWeatherMap();
$owm->setApiKey($myApiKey);

// Example 1: Get current uv index in Berlin.
$uvIndex = $owm->getCurrentUVIndex(52.520008, 13.404954);
echo "EXAMPLE 1$lf";

echo "Current uv index: $uvIndex->uvIndex";
echo $lf;

// Example 2: Get uv index forecast in Berlin.
$forecast = $owm->getForecastUVIndex(52.520008, 13.404954);
echo "EXAMPLE 2$lf";

foreach ($forecast as $day) {
echo "{$day->time->format('r')} will have an uv index of: $day->uvIndex";
echo $lf;
}


// Example 3: Get historic uv index in Berlin.
$history = $owm->getHistoricUVIndex(52.520008, 13.404954, new DateTime('-4month'), new DateTime('-3month'));
echo "EXAMPLE 3$lf";

foreach ($history as $day) {
echo "{$day->time->format('r')} had an uv index of: $day->uvIndex";
echo $lf;
}
Loading