...

четверг, 7 августа 2014 г.

[Из песочницы] «Яндекс.Погода» для сайта в деталях

Доброго времени суток. Этот пост будет написан на основе Яндекс.ТвояПогода с тем отличием, что здесь будет пролит свет на некоторые данные возвращаемых сервисом Яндекс.Погода, и, будет чуть больше кода.

Список городов и их идентификаторы можно увидеть по ссылке.

В моем случае было необходимо написать универсальный блок погоды с авто определением города. Пришлось извлечь информацию из 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://ift.tt/1ot4NCF}");
$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://ift.tt/1AWOPus'.$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. Что у меня получилось в итоге.


image


Рабочий вариант можно увидеть на странице превью.


Исходник в один файл можно посмотреть здесь.


Благодарю за внимание и жду советов по оптимизации и дополнению.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий