Location: PHPKode > scripts > SimpleNWS > cristianradu-simple-nws-c39fbf3/simple-nws/DWMLParser.php
<?php
namespace SimpleNWS;
/**
 * Parser for NOAA's Digital Weather Markup Language
 *
 * Generates the NWS URL based on the input parameters, retrieves the DWML and parses it
 *
 * @author Cristian Radu <hide@address.com>
 * @version 1.0
 * @package SimpleNWS
 */
class DWMLParser
{
    /**
     * @var float Latitude
     */
    private $_latitude;
    /**
     * @var float Longitude
     */
    private $_longitude;
    /**
     * @var string Timeframe. Check the configuration file for allowed values
     */
    private $_timeframe;

    /**
     * @var ForecastModel The forecast model
     */
    private $_forecast;

    
    /**
     * Class constructor
     *
     * @param float $lat Latitude
     * @param float $long Longitude
     * @param string $time Timeframe
     */
    public function __construct($lat, $long, $time)
	{
        $this->_latitude  = $lat;
        $this->_longitude = $long;
        $this->_timeframe = $time;
        
        // validate the input parameters
        $this->_validate();

        // create the forecast model object
        $this->_forecast = new ForecastModel();

        // build the URL based on parameters
        $requestURL = $this->_buildURL();
        $this->_forecast->setRequestURL($requestURL);

        // perform the request and get the XML data
        $xmlData = simplexml_load_file($requestURL);


        // check if the response is empty
        if (empty($xmlData))
        {
            throw new \Exception('Empty response from the National Weather Service. Please try again later.');
        }

        // check if the response is an error message
        if ($xmlData->h2 == 'ERROR')
        {
            throw new \Exception('Error response from the National Weather Service. Please check your input parameters and try again later.');
        }


        // parse the XML data into the forecast model
        $this->_parseXML($xmlData);
    }


    /**
     * Validates the input parameters
     *
     * @return boolean
     */
    private function _validate()
    {
        // check the datatype for latitude and make sure it's in the valid range
        if ((!is_float($this->_latitude)) || ($this->_latitude < Configuration::$minLatitude) || ($this->_latitude > Configuration::$maxLatitude))
        {
            // invalid latitude
            throw new \Exception('Invalid latitude. Allowed values are between '.Configuration::$minLatitude.' and '.Configuration::$maxLatitude);
        }

        // check the datatype for longitude and make sure it's in the valid range
        if ((!is_float($this->_longitude)) || ($this->_longitude < Configuration::$minLongitude) || ($this->_longitude > Configuration::$maxLongitude))
        {
            // invalid longitude
            throw new \Exception('Invalid longitude. Allowed values are between '.Configuration::$minLongitude.' and '.Configuration::$maxLongitude);
        }

        // check timeframe
        if (!in_array($this->_timeframe, Configuration::$allowedTimeframeValues))
        {
            // invalid timeframe
            throw new \Exception('Invalid timeframe. Allowed values: '.implode(', ', Configuration::$allowedTimeframeValues));
        }
    }


    /**
     * Generates the NWS URL
     *
     * Example URL: http://www.weather.gov/forecasts/xml/sample_products/browser_interface/ndfdXMLclient.php
     *                  ?lat=40.75&lon=-73.92&product=time-series&begin=2011-02-09T00:00:00-05:00&end=2011-02-10T00:00:00-05:00
     *                  &maxt=maxt&mint=mint&temp=temp&appt=appt&wx=wx&qpf=qpf&snow=snow&sky=sky&rh=rh
     *
     * @return string
     */
    private function _buildURL()
    {
        // start with the NWS URL
        $url  = Configuration::C_NWS_URL;

        // add the latidude and longitude
        $url .= 'lat='.$this->_latitude.'&lon='.$this->_longitude.'&';

        // add the product type
        $url .= Configuration::$productType.'&';

        // generate and add the start and end timestamps
        date_default_timezone_set('America/New_York');
        switch ($this->_timeframe)
        {
            case 'today':
                $startTimestamp = strtotime('today');
                $endTimestamp   = strtotime('tomorrow');
                break;
            case 'week':
                $startTimestamp = strtotime('today');
                $endTimestamp   = strtotime('today + 1 week');
                break;
            case 'now':
            default:
                $startTimestamp = strtotime('now');
                $endTimestamp   = strtotime('now');
                break;
        }
        $url .= 'begin='.date('c', $startTimestamp).'&end='.date('c', $endTimestamp).'&';

        // append all parameters defined in the configuration file (the format is $param=$param)
        $parameters = array();
        foreach (Configuration::$requestParameters as $param)
        {
            $parameters[] = $param.'='.$param;
        }
        $url .= implode('&', $parameters);

        return $url;
    }


    /**
     * Parses the XML data into the forecast model
     *
     * @param SimpleXMLObject $xmlData
     */
    private function _parseXML($xmlData)
    {
        // first build the time intervals
        $timeLayout = $xmlData->data->{'time-layout'};

        // arrays to store the time/date intervals
        $timeLayouts = array();
        $dateLayouts = array();

        foreach ($timeLayout as $layout)
        {
            // the key is a unique string (i.e. "k-p24h-n7-1")
            $key = strval($layout->{'layout-key'});

            // we ngo through each value and reformat it as year-month-day-hour (there are no min/sec values)
            $values = array();
            foreach ($layout->{'start-valid-time'} as $time)
            {
                $values[]      = date('Y-m-d-H', strtotime(strval($time)));
                $dateLayouts[] = date('Y-m-d',   strtotime(strval($time)));
            }

            // add the key/value pair to the temp array
            $timeLayouts[$key] = $values;
        }

        // for the days, remove the duplicates and sort
        $dateLayouts = array_unique($dateLayouts);
        sort($dateLayouts);

        // save the intervals in the forecast model
        $this->_forecast->setTimeLayouts($timeLayouts);
        $this->_forecast->setDateLayouts($dateLayouts);


        // now iterate over the parameters
        $parameters  = $xmlData->data->parameters;

        // temperature values
        foreach ($parameters->temperature as $temperature)
        {
            // hourly recorded temperatures
            if ($temperature->attributes()->type == 'hourly')
            {
                $hourlyTemperatures = array();

                // get the time layout for this parameter
                $layout = strval($temperature->attributes()->{'time-layout'});

                // we'll go through each value and assign it to its specific time interval
                for ($i = 0; $i < count($temperature->value); $i++)
                {
                    // the timestamp for this index in the time layout
                    $key = $timeLayouts[$layout][$i];
                    // the temperature value for this index
                    $value = intval($temperature->value[$i]);

                    $hourlyTemperatures[$key] = $value;

                    $this->_forecast->setRawWeatherDataForTimestamp($key, 'recorded_temperature', $value);
                }

                // save the maximum temperature in the forecast model
                $this->_forecast->setHourlyRecordedTemperature($hourlyTemperatures);
            }

            // hourly apparent temperatures
            if ($temperature->attributes()->type == 'apparent')
            {
                $apparentTemperatures = array();

                // get the time layout for this parameter
                $layout = strval($temperature->attributes()->{'time-layout'});

                // we'll go through each value and assign it to its specific time interval
                for ($i = 0; $i < count($temperature->value); $i++)
                {
                    // the timestamp for this index in the time layout
                    $key = $timeLayouts[$layout][$i];
                    // the temperature value for this index
                    $value = intval($temperature->value[$i]);

                    $apparentTemperatures[$key] = $value;

                    $this->_forecast->setRawWeatherDataForTimestamp($key, 'apparent_temperature', $value);
                }

                // save the maximum temperature in the forecast model
                $this->_forecast->setHourlyApparentTemperature($apparentTemperatures);
            }

            // daily maximum temperatures
            if ($temperature->attributes()->type == 'maximum')
            {
                $maximumTemperatures = array();

                // get the time layout for this parameter
                $layout = strval($temperature->attributes()->{'time-layout'});

                // we'll go through each value and assign it to its specific time interval
                for ($i = 0; $i < count($temperature->value); $i++)
                {
                    // the timestamp for this index in the time layout
                    $key = $timeLayouts[$layout][$i];
                    // the temperature value for this index
                    $value = intval($temperature->value[$i]);

                    $maximumTemperatures[$key] = $value;
                }

                // save the maximum temperature in the forecast model
                $this->_forecast->setDailyMaximumTemperature($maximumTemperatures);
            }

            // daily minimum temperatures
            if ($temperature->attributes()->type == 'minimum')
            {
                $minimumTemperatures = array();

                // get the time layout for this parameter
                $layout = strval($temperature->attributes()->{'time-layout'});

                // we'll go through each value and assign it to its specific time interval
                for ($i = 0; $i < count($temperature->value); $i++)
                {
                    // the timestamp for this index in the time layout
                    $key = $timeLayouts[$layout][$i];
                    // the temperature value for this index
                    $value = intval($temperature->value[$i]);

                    $minimumTemperatures[$key] = $value;
                }

                // save the minimum temperature in the forecast model
                $this->_forecast->setDailyMinimumTemperature($minimumTemperatures);
            }
        }

        // precipitation values
        foreach ($parameters->precipitation as $precipitation)
        {
            // hourly precipitation
            if ($precipitation->attributes()->type == 'liquid')
            {
                $hourlyPrecipitation = array();

                // get the time layout for this parameter
                $layout = strval($precipitation->attributes()->{'time-layout'});

                // we'll go through each value and assign it to its specific time interval
                for ($i = 0; $i < count($precipitation->value); $i++)
                {
                    // the timestamp for this index in the time layout
                    $key = $timeLayouts[$layout][$i];
                    // the temperature value for this index
                    $value = floatval($precipitation->value[$i]);

                    $hourlyPrecipitation[$key] = $value;

                    $this->_forecast->setRawWeatherDataForTimestamp($key, 'liquid_precipitation', $value);
                }

                // save the liquid precipitation in the forecast model
                $this->_forecast->setHourlyPrecipitation($hourlyPrecipitation);
            }

            // hourly snow amount
            if ($precipitation->attributes()->type == 'snow')
            {
                $hourlySnowAmount = array();

                // get the time layout for this parameter
                $layout = strval($precipitation->attributes()->{'time-layout'});

                // we'll go through each value and assign it to its specific time interval
                for ($i = 0; $i < count($precipitation->value); $i++)
                {
                    // the timestamp for this index in the time layout
                    $key = $timeLayouts[$layout][$i];
                    // the temperature value for this index
                    $value = floatval($precipitation->value[$i]);

                    $hourlySnowAmount[$key] = $value;

                    $this->_forecast->setRawWeatherDataForTimestamp($key, 'snow_amount', $value);
                }

                // save the snow amount in the forecast model
                $this->_forecast->setHourlySnowAmount($hourlySnowAmount);
            }
        }

        // hourly cloud cover
        foreach ($parameters->{'cloud-amount'} as $cloudCover)
        {
            $hourlyCloudCover = array();

            // get the time layout for this parameter
            $layout = strval($cloudCover->attributes()->{'time-layout'});

            // we'll go through each value and assign it to its specific time interval
            for ($i = 0; $i < count($cloudCover->value); $i++)
            {
                // the timestamp for this index in the time layout
                $key = $timeLayouts[$layout][$i];
                // the temperature value for this index
                $value = intval($cloudCover->value[$i]);

                $hourlyCloudCover[$key] = $value;

                $this->_forecast->setRawWeatherDataForTimestamp($key, 'cloud_cover', $value);
            }

            // save the cloud cover in the forecast model
            $this->_forecast->setHourlyCloudCover($hourlyCloudCover);
        }

        // hourly relative humidity
        foreach ($parameters->humidity as $humidity)
        {
            $hourlyHumidity = array();

            // get the time layout for this parameter
            $layout = strval($humidity->attributes()->{'time-layout'});

            // we'll go through each value and assign it to its specific time interval
            for ($i = 0; $i < count($humidity->value); $i++)
            {
                // the timestamp for this index in the time layout
                $key = $timeLayouts[$layout][$i];
                // the temperature value for this index
                $value = intval($humidity->value[$i]);

                $hourlyHumidity[$key] = $value;

                $this->_forecast->setRawWeatherDataForTimestamp($key, 'humidity', $value);
            }

            // save the relative humidity in the forecast model
            $this->_forecast->setHourlyHumidity($hourlyHumidity);
        }


        // weather conditions
        $weather = $parameters->weather;

        // get the time layout for the weather conditions
        $layout = strval($weather->attributes()->{'time-layout'});

        $hourlyWeatherConditions = array();

        // we'll go through each value and assign it to its specific time interval
        for ($i = 0; $i < count($weather->{'weather-conditions'}); $i++)
        {
            // check if we have any value for this timestamp
            if ($weather->{'weather-conditions'}[$i]->value)
            {
                // the timestamp for this index in the time layout
                $key = $timeLayouts[$layout][$i];

                // the weather conditions for this index
                $conditions = array();
                $conditions['weather_type'] = strval($weather->{'weather-conditions'}[$i]->value->attributes()->{'weather-type'});
                $conditions['intensity']    = strval($weather->{'weather-conditions'}[$i]->value->attributes()->intensity);
                $conditions['coverage']     = strval($weather->{'weather-conditions'}[$i]->value->attributes()->coverage);

                $hourlyWeatherConditions[$key] = $conditions;

                $this->_forecast->setRawWeatherDataForTimestamp($key, 'weather_conditions', $conditions);
            }
        }

        // save the weather conditions in the forecast model
        $this->_forecast->setWeatherConditions($hourlyWeatherConditions);


        // organize the raw weather data by day, in a ready-to-use format
        $this->_forecast->organizeWeatherData();
    }


    /**
     * Getter for the forecast model
     *
     * @return ForecastModel
     */
    public function getForecast()
    {
        return $this->_forecast;
    }
}
?>
Return current item: SimpleNWS