Список городов и их идентификаторы можно увидеть по ссылке.
В моем случае было необходимо написать универсальный блок погоды с авто определением города. Пришлось извлечь информацию из 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. Что у меня получилось в итоге.
Рабочий вариант можно увидеть на странице превью.
Исходник в один файл можно посмотреть здесь.
Благодарю за внимание и жду советов по оптимизации и дополнению.
ссылка на оригинал статьи http://habrahabr.ru/post/232529/
Добавить комментарий