«Яндекс.Погода» для сайта в деталях

от автора

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

Список городов и их идентификаторы можно увидеть по ссылке.
В моем случае было необходимо написать универсальный блок погоды с авто определением города. Пришлось извлечь информацию из 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. Что у меня получилось в итоге.

image

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

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

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

ссылка на оригинал статьи http://habrahabr.ru/post/232529/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *