[Из песочницы] «Яндекс.Погода» для сайта в деталях
Доброго времени суток. Этот пост будет написан на основе Яндекс.ТвояПогода с тем отличием, что здесь будет пролит свет на некоторые данные возвращаемых сервисом Яндекс.Погода, и, будет чуть больше кода.Список городов и их идентификаторы можно увидеть по ссылке.В моем случае было необходимо написать универсальный блок погоды с авто определением города. Пришлось извлечь информацию из xml файла и записать ее в виде json массива с соответствиями.
Как выглядит список.
Для определения города посетителя используется ipGeoBase
function getGeo ($ip) { if (! filter_var ($ip, FILTER_VALIDATE_IP, array ('flags' => FILTER_FLAG_IPV4))) return FALSE; $get = file_get_contents («http://ipgeobase.ru:7020/geo? ip={$ip}»); $xml = simplexml_load_string ($get); $city = isset ($xml→ip→city) ? strtolower ($xml→ip→city) : ''; return $city; } Здесь у меня вышла нестыковка. Сервис прекрасно работает в СНГ, но ни за что не определит, например, посетителя из Лондона.Поэтому файл со списком городов можно было существенно сжать, но руки покамест не дошли.
Далее сам класс для работы с погодой. Вы спросите «зачем понадобился класс»? Компонент писался под движок InstantCMS в котором компонент инициализируется только через класс с соответствующим названием.
Оговорюсь заранее, некоторые значения переменных лишь предположительны ($noon, $night)
class cms_model_weather{
private $cache_dir; private $city_id; private $noon; //Время дня. По-умолчанию 4 — основная часть дня 11:00–20:00 private $night; //Время ночи. По-умолчанию 5
//Инициализируем класс public function __construct (){ $this→cache_dir=$_SERVER['DOCUMENT_ROOT'].'/habr/cache/weather'; $this→noon=4; $this→night=5; if (isset ($_SESSION['weather_city'])){$cityname=$_SESSION['wether_city'];} //Берем название города из сессии else{$cityname=getGeo ($_SERVER['REMOTE_ADDR']); }// Получаем название города по автоопределению $cities=json_decode (file_get_contents ($_SERVER['DOCUMENT_ROOT'].'/habr/YandexCities.txt')); if (isset ($cities→$cityname)){ $this→city_id=$cities→$cityname; } else { $this→city_id=$cities→{'Москва'}; } } Если название города есть в списке яндекса, присваиваем его ид переменной.
Далее получаем xml файл с данными о погоде и парсим его.Здесь для «локализации» даты я воспользовался массивом соответствий. Возможно, есть более адекватный способ, но те, которые предлагал мне гугл, не помогли. Можно было обойтись и без этого, но задача стояла конкретная.
//Получаем погоду для определенного города public function getCityWeather (){ $_LANG['MONTHS'] = array ( 'Jan'=>'январь', 'Feb'=>'февраль', 'Mar'=>'март', 'Apr'=>'апрель', 'May'=>'май','Jun'=>'июнь', 'Jul'=>'июль','Aug'=>'август', 'Sep'=>'сентябрь', 'Oct'=>'октябрь','Nov'=>'ноябрь','Dec'=>'декабрь'); $_LANG['WEEKDAYS'] = array ( 'Sunday'=>'воскресенье', 'Monday'=>'понедельник', 'Tuesday'=>'вторник', 'Wednesday'=>'среда', 'Thursday'=>'четверг', 'Friday'=>'пятница', 'Saturday'=>'суббота'); $city_cache=$this→{cache_dir}.$this→{city_id}; //Если время последнего изменения кэш файла меньше шести минут, возвращаем данные из кэша if (file_exists ($city_cache) && filemtime ($city_cache)>time ()-360){ $jsoned_weather=json_decode (file_get_contents ($city_cache), TRUE); } else { //Если кэш отсутствует, получаем данные с яндекса $weather=simplexml_load_file ('http://export.yandex.ru/weather-ng/forecasts/'.$this→city_id.'.xml'); if ($weather!=null){ //Погода на семь дней for ($i=1; $i<=7; $i++) { if($weather->day[$i]!=null){ $days['sevendays'][]=array ( 'weekday' => strtr (date ('l', strtotime ($weather→day[$i]→attributes ()→date)), $_LANG['WEEKDAYS']), 'data'=>strtr (date («j M», strtotime ($weather→day[$i]→attributes ()→date)), $_LANG['MONTHS']), 'temp_day' => $this→checkZeroMark ($weather→day[$i]→day_part[$this→noon]→temperature), 'temp_night' => $this→checkZeroMark ($weather→day[$i]→day_part[$this→night]→temperature), 'weather_type' => $weather→day[$i]→day_part[$this→noon]→weather_type, 'wind_speed' => $weather→day[$i]→day_part[$this→noon]→wind_speed, 'humidity' => $weather→day[$i]→day_part[$this→noon]→humidity, 'pressure' => $weather→day[$i]→day_part[$this→noon]→pressure, 'image' => $weather→day[$i]→day_part[$this→noon]→{'image-v3'} ); } } //Погода на сегодня $days['today']=array ( 'weekday' => strtr (date ('l'), $_LANG['WEEKDAYS']), 'data'=>strtr (date ('j M Y, G: i'), $_LANG['MONTHS']), 'temperature' => $this→checkZeroMark ($weather→fact→temperature), 'weather_type' => $weather→fact→weather_type, 'wind_speed' => $weather→fact→wind_speed, 'humidity' => $weather→fact→humidity, 'pressure' => $weather→fact→pressure, 'water_temp' => $this→checkZeroMark ($weather→fact→water_temperature), 'sunrise' => $weather→day→sunrise, 'sunset' => $weather→day→sunset, 'image' => $weather→fact→{'image-v3'}, 'city'=>$weather→attributes ()→city, 'country' => $weather→attributes ()→country ); file_put_contents ($city_cache, json_encode ($days)); $jsoned_weather=json_decode (file_get_contents ($city_cache), TRUE); } else{ return null; } } return $jsoned_weather; } //Функция для проверки знака температуры //Возвращает плюс, минус или ноль в зависимости от температуры public function checkZeroMark ($temperature){ if ($temperature>0){ return '+'.$temperature; } elseif ($temperature<0){ return '-'.$temperature; } return $temperature; } } В объекте $weather возвращается такое количество данных, что не влезает в var_dump. Так как требования были в том, чтобы сделать некий аналог погоды, который есть, не кидайте помидорами-воля начальства =), на майлру, пришлось провести достаточно нудный анализ всех данных.Легко догадаться, что $weather->fact содержит информацию о погоде на сегодняшний день. Кстати говоря, интересный пункт $weather→fact→water_temperature обозначается только для тех городов, рядом с которыми есть море. Для остальных результат либо 0(неприятный сюрприз), либо null.$weather→attributes () содержит информацию о ключевых параметрах, по которым была запрошена информация из сервиса. Здесь примечательно то, что названия некоторых городов возвращаются латиницей. Есть подозрения, что зависит от вашей геолокации.$weather→fact→{'image-v3'} возвращает название миниатюры погоды. Список всех названий я перебрал прокручивая слот-машину погоды яндекса в разных городах. Имейте ввиду, на ночное время выдаются совершенно другие иконки. Те, которые я нашел (вполне возможно, далеко не все)
Skc_d — солнечно (ясно)skc_n — то же самое, но для ночного времениbkn_d — облачно, с прояснениямиbkn_-ra_d — облачно с прояснениями, возможен дождьbkn_-ra_n — то же самое, но ночное время сутокovc — облачноovc_ra — дождьovc_-ra — слабый дождь (не помню, как в оригинале)jg_d — туманbl — метельovc_sn — снегovc_ts_ra — гроза
Теперь о $weather→day[]. Здесь хранится информация о погоде на неделю вперед включительно с сегодняшнего дня. Однако информация разбиена на 6 периодов с детальным раскрытием каждого. Путем небольшой сводки я пришел к выводу, что за основную часть дня отвечает массив под четвертым значением. Логически, 24 часа разбито на 6 частей, но сводка показала, что каждая часть в часовом эквиваленте разнится с остальными. Поправьте, если я ошибаюсь.
В целом, яндекс порадовал обилием необходимых и не очень данных. Например я так и не понял, для чего может понадобиться moonrise/moonset. Что у меня получилось в итоге.
Рабочий вариант можно увидеть на странице превью.
Исходник в один файл можно посмотреть здесь.
Благодарю за внимание и жду советов по оптимизации и дополнению.