Cleanup SI Unit classes and interface for REST responses; fixes to a few METAR parsing issues
This commit is contained in:
@@ -4,7 +4,11 @@ namespace App\Support;
|
||||
|
||||
use App\Support\Units\Altitude;
|
||||
use App\Support\Units\Distance;
|
||||
use App\Support\Units\Pressure;
|
||||
use App\Support\Units\Temperature;
|
||||
use App\Support\Units\Velocity;
|
||||
use PhpUnitsOfMeasure\Exception\NonNumericValue;
|
||||
use PhpUnitsOfMeasure\Exception\NonStringUnitName;
|
||||
|
||||
/**
|
||||
* Class Metar
|
||||
@@ -388,6 +392,87 @@ class Metar implements \ArrayAccess
|
||||
if (isset($this->result[$parameter])) {
|
||||
return $this->result[$parameter];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an Altitude value or object
|
||||
*
|
||||
* @param int|float $value
|
||||
* @param string $unit "feet" or "meters"
|
||||
*
|
||||
* @throws NonStringUnitName
|
||||
* @throws NonNumericValue
|
||||
*
|
||||
* @return Altitude
|
||||
*/
|
||||
protected function createAltitude($value, $unit)
|
||||
{
|
||||
return new Altitude($value, $unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Distance value or object
|
||||
*
|
||||
* @param int|float $value
|
||||
* @param string $unit "m" (meters) or "mi" (miles)
|
||||
*
|
||||
* @throws NonNumericValue
|
||||
* @throws NonStringUnitName
|
||||
*
|
||||
* @return Distance
|
||||
*/
|
||||
protected function createDistance($value, $unit)
|
||||
{
|
||||
return new Distance($value, $unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Pressure value or object
|
||||
*
|
||||
* @param int|float $value
|
||||
* @param string $unit "F" or "C"
|
||||
*
|
||||
* @throws NonNumericValue
|
||||
* @throws NonStringUnitName
|
||||
*
|
||||
* @return Pressure
|
||||
*/
|
||||
protected function createPressure($value, $unit)
|
||||
{
|
||||
return new Pressure($value, $unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Temperature value or object
|
||||
*
|
||||
* @param int|float $value
|
||||
* @param string $unit "F" or "C"
|
||||
*
|
||||
* @throws NonNumericValue
|
||||
* @throws NonStringUnitName
|
||||
*
|
||||
* @return Temperature
|
||||
*/
|
||||
protected function createTemperature($value, $unit)
|
||||
{
|
||||
return new Temperature($value, $unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new velocity unit
|
||||
* @param int|float $value
|
||||
* @param string $unit "knots", "km/hour", "m/s"
|
||||
*
|
||||
* @throws NonStringUnitName
|
||||
* @throws NonNumericValue
|
||||
*
|
||||
* @return Velocity
|
||||
*/
|
||||
protected function createVelocity($value, $unit)
|
||||
{
|
||||
return new Velocity($value, $unit);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -591,6 +676,8 @@ class Metar implements \ArrayAccess
|
||||
* Decodes station code.
|
||||
*
|
||||
* @param mixed $part
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function get_station($part)
|
||||
{
|
||||
@@ -609,6 +696,8 @@ class Metar implements \ArrayAccess
|
||||
* Format is ddhhmmZ where dd = day, hh = hours, mm = minutes in UTC time.
|
||||
*
|
||||
* @param mixed $part
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function get_time($part)
|
||||
{
|
||||
@@ -648,6 +737,8 @@ class Metar implements \ArrayAccess
|
||||
* Ignore station type if present.
|
||||
*
|
||||
* @param mixed $part
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function get_station_type($part)
|
||||
{
|
||||
@@ -750,8 +841,8 @@ class Metar implements \ArrayAccess
|
||||
*
|
||||
* @param mixed $part
|
||||
*
|
||||
* @throws \PhpUnitsOfMeasure\Exception\NonStringUnitName
|
||||
* @throws \PhpUnitsOfMeasure\Exception\NonNumericValue
|
||||
* @throws NonStringUnitName
|
||||
* @throws NonNumericValue
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@@ -772,7 +863,7 @@ class Metar implements \ArrayAccess
|
||||
|
||||
// Cloud and visibilty OK or ICAO visibilty greater than 10 km
|
||||
if ($found[1] === 'CAVOK' || $found[1] === '9999') {
|
||||
$this->set_result_value('visibility', new Distance(10000, 'm'));
|
||||
$this->set_result_value('visibility', $this->createDistance(10000, 'm'));
|
||||
$this->set_result_value('visibility_report', 'Greater than 10 km');
|
||||
/* @noinspection NotOptimalIfConditionsInspection */
|
||||
if ($found[1] === 'CAVOK') {
|
||||
@@ -787,7 +878,7 @@ class Metar implements \ArrayAccess
|
||||
|
||||
// ICAO visibility (in meters)
|
||||
if (isset($found[2]) && !empty($found[2])) {
|
||||
$visibility = new Distance((int) $found[2], 'm');
|
||||
$visibility = $this->createDistance((int)$found[2], 'm');
|
||||
} // US visibility (in miles)
|
||||
else {
|
||||
if (isset($found[3]) && !empty($found[3])) {
|
||||
@@ -800,7 +891,7 @@ class Metar implements \ArrayAccess
|
||||
$visibility = (int) $found[4];
|
||||
}
|
||||
|
||||
$visibility = new Distance($visibility, 'mi');
|
||||
$visibility = $this->createDistance($visibility, 'mi');
|
||||
}
|
||||
|
||||
$unit = ' meters';
|
||||
@@ -824,9 +915,9 @@ class Metar implements \ArrayAccess
|
||||
*
|
||||
* @param $part
|
||||
*
|
||||
* @throws \PhpUnitsOfMeasure\Exception\NonStringUnitName
|
||||
* @throws \PhpUnitsOfMeasure\Exception\NonNumericValue
|
||||
* @throws \PhpUnitsOfMeasure\Exception\NonStringUnitName
|
||||
* @throws NonStringUnitName
|
||||
* @throws NonNumericValue
|
||||
* @throws NonStringUnitName
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@@ -836,7 +927,7 @@ class Metar implements \ArrayAccess
|
||||
return false;
|
||||
}
|
||||
|
||||
$meters = new Distance((int) $found[1], 'm');
|
||||
$meters = $this->createDistance((int)$found[1], 'm');
|
||||
$this->set_result_value('visibility_min', $meters);
|
||||
|
||||
if (isset($found[2]) && !empty($found[2])) {
|
||||
@@ -854,8 +945,8 @@ class Metar implements \ArrayAccess
|
||||
*
|
||||
* @param $part
|
||||
*
|
||||
* @throws \PhpUnitsOfMeasure\Exception\NonStringUnitName
|
||||
* @throws \PhpUnitsOfMeasure\Exception\NonNumericValue
|
||||
* @throws NonStringUnitName
|
||||
* @throws NonNumericValue
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@@ -899,13 +990,13 @@ class Metar implements \ArrayAccess
|
||||
// Runway visual range
|
||||
if (isset($found[6])) {
|
||||
if (!empty($found[4])) {
|
||||
$observed['interval_min'] = new Distance($found[4], $unit);
|
||||
$observed['interval_max'] = new Distance($found[6], $unit);
|
||||
$observed['interval_min'] = $this->createDistance($found[4], $unit);
|
||||
$observed['interval_max'] = $this->createDistance($found[6], $unit);
|
||||
if (!empty($found[5])) {
|
||||
$observed['variable_prefix'] = $found[5];
|
||||
}
|
||||
} else {
|
||||
$observed['variable'] = new Distance($found[6], $unit);
|
||||
$observed['variable'] = $this->createDistance($found[6], $unit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -960,8 +1051,8 @@ class Metar implements \ArrayAccess
|
||||
*
|
||||
* @param $part
|
||||
*
|
||||
* @throws \PhpUnitsOfMeasure\Exception\NonStringUnitName
|
||||
* @throws \PhpUnitsOfMeasure\Exception\NonNumericValue
|
||||
* @throws NonStringUnitName
|
||||
* @throws NonNumericValue
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@@ -990,7 +1081,7 @@ class Metar implements \ArrayAccess
|
||||
}
|
||||
} // Cloud cover observed
|
||||
elseif (isset($found[5]) && !empty($found[5]) && is_numeric($found[5])) {
|
||||
$observed['height'] = new Altitude($found[5] * 100, 'feet');
|
||||
$observed['height'] = $this->createAltitude($found[5] * 100, 'feet');
|
||||
|
||||
// Cloud height
|
||||
if (null === $this->result['cloud_height']['m'] || $observed['height']['m'] < $this->result['cloud_height']['m']) {
|
||||
@@ -1047,8 +1138,10 @@ class Metar implements \ArrayAccess
|
||||
*
|
||||
* @param mixed $part
|
||||
*
|
||||
* @throws \PhpUnitsOfMeasure\Exception\NonStringUnitName
|
||||
* @throws \PhpUnitsOfMeasure\Exception\NonNumericValue
|
||||
* @throws NonNumericValue
|
||||
* @throws NonStringUnitName
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function get_temperature($part)
|
||||
{
|
||||
@@ -1065,20 +1158,20 @@ class Metar implements \ArrayAccess
|
||||
|
||||
// Temperature
|
||||
$temperature_c = (int) str_replace('M', '-', $found[1]);
|
||||
$temperature = new Temperature($temperature_c, 'C');
|
||||
$temperature = $this->createTemperature($temperature_c, 'C');
|
||||
|
||||
$this->set_result_value('temperature', $temperature);
|
||||
$this->calculate_wind_chill($temperature['f']);
|
||||
$this->calculate_wind_chill($temperature['F']);
|
||||
|
||||
// Dew point
|
||||
if (isset($found[2]) && '' !== $found[2] && $found[2] !== 'XX') {
|
||||
$dew_point_c = (int) str_replace('M', '-', $found[2]);
|
||||
$dew_point = new Temperature($dew_point_c, 'C');
|
||||
$dew_point = $this->createTemperature($dew_point_c, 'C');
|
||||
$rh = round(100 * (((112 - (0.1 * $temperature_c) + $dew_point_c) / (112 + (0.9 * $temperature_c))) ** 8));
|
||||
|
||||
$this->set_result_value('dew_point', $dew_point);
|
||||
$this->set_result_value('humidity', $rh);
|
||||
$this->calculate_heat_index($temperature['f'], $rh);
|
||||
$this->calculate_heat_index($temperature['F'], $rh);
|
||||
}
|
||||
|
||||
$this->method++;
|
||||
@@ -1096,21 +1189,26 @@ class Metar implements \ArrayAccess
|
||||
* 1 lb/sq in = 0.491154 in Hg = 0.014504 hPa
|
||||
* 1 atm = 0.33421 in Hg = 0.0009869 hPa
|
||||
*
|
||||
*
|
||||
* @param mixed $part
|
||||
*
|
||||
* @throws NonNumericValue
|
||||
* @throws NonStringUnitName
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function get_pressure($part)
|
||||
{
|
||||
if (!preg_match('@^(Q|A)(////|[\d]{4})@', $part, $found)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$pressure = (int) $found[2];
|
||||
if ($found[1] === 'A') {
|
||||
$pressure /= 100;
|
||||
}
|
||||
|
||||
$this->set_result_value('barometer', $pressure); // units are hPa
|
||||
$this->set_result_value('barometer_mb', $pressure); // units are hPa
|
||||
$this->set_result_value('barometer_in', round(0.02953 * $pressure, 2)); // convert to in Hg
|
||||
$this->set_result_value('barometer', $this->createPressure($pressure, 'hPa'));
|
||||
|
||||
$this->method++;
|
||||
return true;
|
||||
@@ -1121,6 +1219,8 @@ class Metar implements \ArrayAccess
|
||||
* Format is REww where ww = Weather phenomenon code (see get_present_weather above).
|
||||
*
|
||||
* @param mixed $part
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function get_recent_weather($part)
|
||||
{
|
||||
@@ -1133,6 +1233,8 @@ class Metar implements \ArrayAccess
|
||||
* C = extent of deposit, ee = depth of deposit, BB = friction coefficient.
|
||||
*
|
||||
* @param mixed $part
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function get_runways_report($part)
|
||||
{
|
||||
@@ -1241,6 +1343,8 @@ class Metar implements \ArrayAccess
|
||||
* Format is 'WS ALL RWY' or 'WS RWYdd' where dd = Runway designator (see get_runway_vr above).
|
||||
*
|
||||
* @param mixed $part
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function get_wind_shear($part)
|
||||
{
|
||||
@@ -1289,8 +1393,8 @@ class Metar implements \ArrayAccess
|
||||
* HH - Forecast hour, i.e. the time(hour) when the temperature is expected
|
||||
* Z - Time Zone indicator, Z=GMT.
|
||||
*
|
||||
* @throws \PhpUnitsOfMeasure\Exception\NonStringUnitName
|
||||
* @throws \PhpUnitsOfMeasure\Exception\NonNumericValue
|
||||
* @throws NonStringUnitName
|
||||
* @throws NonNumericValue
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@@ -1307,7 +1411,7 @@ class Metar implements \ArrayAccess
|
||||
|
||||
// Temperature
|
||||
$temperature_c = (int) str_replace('M', '-', $found[2]);
|
||||
$temperture = new Temperature($temperature_c, 'C');
|
||||
$temperture = $this->createTemperature($temperature_c, 'C');
|
||||
|
||||
$forecast = [
|
||||
'value' => $temperture,
|
||||
@@ -1336,6 +1440,8 @@ class Metar implements \ArrayAccess
|
||||
* LTDDhhmm or DDhh/DDhh, where hh = hours, mm = minutes, DD = day of month.
|
||||
*
|
||||
* @param mixed $part
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function get_trends($part)
|
||||
{
|
||||
@@ -1608,8 +1714,8 @@ class Metar implements \ArrayAccess
|
||||
* @param $temperature_f
|
||||
* @param $rh
|
||||
*
|
||||
* @throws \PhpUnitsOfMeasure\Exception\NonNumericValue
|
||||
* @throws \PhpUnitsOfMeasure\Exception\NonStringUnitName
|
||||
* @throws NonNumericValue
|
||||
* @throws NonStringUnitName
|
||||
*/
|
||||
private function calculate_heat_index($temperature_f, $rh): void
|
||||
{
|
||||
@@ -1621,7 +1727,7 @@ class Metar implements \ArrayAccess
|
||||
$hi_f = round($hi_f);
|
||||
$hi_c = round(($hi_f - 32) / 1.8);
|
||||
|
||||
$this->set_result_value('heat_index', new Temperature($hi_c, 'C'));
|
||||
$this->set_result_value('heat_index', $this->createTemperature($hi_c, 'C'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1631,20 +1737,20 @@ class Metar implements \ArrayAccess
|
||||
*
|
||||
* @param $temperature_f
|
||||
*
|
||||
* @throws \PhpUnitsOfMeasure\Exception\NonNumericValue
|
||||
* @throws \PhpUnitsOfMeasure\Exception\NonStringUnitName
|
||||
* @throws NonNumericValue
|
||||
* @throws NonStringUnitName
|
||||
*/
|
||||
private function calculate_wind_chill($temperature_f): void
|
||||
{
|
||||
if ($temperature_f < 51 && $this->result['wind_speed'] !== 0) {
|
||||
$windspeed = round(2.23694 * $this->result['wind_speed']); // convert m/s to mi/h
|
||||
$windspeed = $this->result['wind_speed']->toUnit('mph');
|
||||
if ($windspeed > 3) {
|
||||
$chill_f = 35.74 + 0.6215 * $temperature_f - 35.75 * ($windspeed ** 0.16);
|
||||
$chill_f += 0.4275 * $temperature_f * ($windspeed ** 0.16);
|
||||
$chill_f = round($chill_f);
|
||||
$chill_c = round(($chill_f - 32) / 1.8);
|
||||
|
||||
$this->set_result_value('wind_chill', new Temperature($chill_c, 'C'));
|
||||
$this->set_result_value('wind_chill', $this->createTemperature($chill_c, 'C'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1661,7 +1767,10 @@ class Metar implements \ArrayAccess
|
||||
* @param $speed
|
||||
* @param $unit
|
||||
*
|
||||
* @return float|null
|
||||
* @throws NonStringUnitName
|
||||
* @throws NonNumericValue
|
||||
*
|
||||
* @return Velocity
|
||||
*/
|
||||
private function convert_speed($speed, $unit)
|
||||
{
|
||||
@@ -1669,36 +1778,11 @@ class Metar implements \ArrayAccess
|
||||
|
||||
switch ($unit) {
|
||||
case 'KT':
|
||||
return round(0.514444 * $speed, 2); // from knots
|
||||
return $this->createVelocity($speed, 'knots');
|
||||
case 'KPH':
|
||||
return round(0.277778 * $speed, 2); // from km/h
|
||||
case 'MPS':
|
||||
return round($speed, 2); // m/s
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert distance into meters.
|
||||
* Some other common conversion factors:
|
||||
* 1 m = 3.28084 ft = 0.00062 mi
|
||||
* 1 ft = 0.3048 m = 0.00019 mi
|
||||
* 1 mi = 5279.99 ft = 1609.34 m
|
||||
*
|
||||
* @param $distance
|
||||
* @param $unit
|
||||
*
|
||||
* @return float|null
|
||||
*/
|
||||
private function convert_distance($distance, $unit)
|
||||
{
|
||||
// TODO: return dict w/ multiple units - NS
|
||||
switch ($unit) {
|
||||
case 'FT':
|
||||
return round(0.3048 * $distance); // from ft.
|
||||
case 'SM':
|
||||
return round(1609.34 * $distance); // from miles
|
||||
case 'M':
|
||||
return round($distance); // meters
|
||||
return $this->createVelocity($speed, 'km/hour');
|
||||
default:
|
||||
return $this->createVelocity($speed, 'm/s');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1706,12 +1790,16 @@ class Metar implements \ArrayAccess
|
||||
* Convert direction degrees to compass label.
|
||||
*
|
||||
* @param mixed $direction
|
||||
*
|
||||
* @return string Direction string
|
||||
*/
|
||||
private function convert_direction_label($direction)
|
||||
private function convert_direction_label($direction): string
|
||||
{
|
||||
if ($direction >= 0 && $direction <= 360) {
|
||||
return static::$direction_codes[round($direction / 22.5) % 16];
|
||||
}
|
||||
|
||||
return 'N';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user