- PVSM.RU - https://www.pvsm.ru -

Приветствую всех в этот прекрасный день ожидания праздника, это моя первая статья на хабре, в которой я хотел бы рассказа про открытый API погоды Яндекса. Статья является продолжением серии Java для начинающих [1]. Следует отметить, статья рассчитана на тех, кто недавно начал изучения язык или на тех, кто не знаком с данным сервисом, но в любом случае, рад я буду любым читателям (эх тавтология… ). Яндекс дает неплохую возможность для разработчиков, которым требуется в своей программе или на своем сайте разместить погоду, причем информации, которую Яндекс предоставляет более, чем достаточно.
Погоду Вы сможете выбрать за любой день на неделю вперед. Различные состояния (ясно, пасмурно и т. д.), множество языков (для городов, например русский и английский, для состояний все языки стран СНГ и не только: ясно, аяз, açık, ашық и т… д.), я не очень хорош в географии, но, кажется, информация там для всех стран, даже есть миниатюрные картинки состояния погоды, но самое главное, почему я выбрал этот сервис — простая и понятная структура. Сразу оговорюсь, за «рекламу» мне не платили.
Класс с внешним представлением, думаю описывать не надо, я лишь приведу код, чтобы потом не было несостыковок. Рекомендую даже не списывать, а сделать свой, ибо мой убогий:
public WFrame() {
today = null;
GridBagLayout layout = new GridBagLayout(); // выбрал его, потому что раньше с ним не работал, вот и экперементирую
setTitle("Weather");
setPreferredSize(new Dimension(350, 300));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
contentPane = getContentPane();
panel = new JPanel();
panel.setLayout(layout);
idField = new JtextField();
idField.setColumns(6);
// парсим содержимое xml, вытаскиваем от туда необходимые данные
// заполняем метки
btn = new JButton("Хочу все знать");
// расположение элементов, в GUI я не силен, критику приму
label = new JLabel("Наберите в поле ID вашего города");
label.setFont(new Font("Verdana", Font.PLAIN, 14));
tLabel = new JLabel("Погода завтра");
tLabel.setFont(new Font("Verdana", Font.PLAIN, 14));
iLabel = new JLabel();
panel.add(label,
new GBC(0, 0, 2, 1).setFill(GBC.NORTH).setWeight(100, 0));
panel.add(iLabel,
new GBC(0, 1, 2, 1).setFill(GBC.NORTH).setWeight(100, 0));
panel.add(btn, new GBC(0, 3, 2, 1).setFill(GBC.NORTH).setWeight(100, 0));
panel.add(idField,
new GBC(0, 2, 2, 1).setAnchor(GBC.NORTH)
.setFill(GBC.HORIZONTAL).setIpad(50, 0));
panel.add(tLabel,
new GBC(0, 4, 2, 1).setAnchor(GBC.NORTH)
.setFill(GBC.HORIZONTAL).setIpad(50, 0));
add(panel);
panel.setComponentPopupMenu(menu);
pack();
}
В idField мы будем записывать ID нашего города (список городов и государств можете найти здесь weather.yandex.ru/static/cities.xml [2]). Ну а в лэйблы — саму информацию. Критику я воспринимаю нормально, буду рад если сделаете замечания или поправки. Класс GBC – наследник GridBagConstraints, его код я приведу в конце поста.
Еще я бы добавил для удобства всплывающее меню, чтобы айди города каждый раз не набирать:
JPopupMenu menu = new JPopupMenu();
menu.add(new AbstractAction("Пенза") {
@Override
public void actionPerformed(ActionEvent e) {
setCity("27962");
}
});
Естественно добавить нужно не один город, но мне пока хватает Пензы. Для добавления городов в меню лучше создать отдельный класс, но это другая тема, я с этим возиться не стал.
Следующий класс, который я создал — класс Weather для хранения переменных с информацией о погоде. Тут ничего сложного:
public class Weather {
String city;
String weatherType;
String imgType;
String humidity;// Влажность
String tom, tomNight;
int temperature;
public String toString() {
return "Weather[city= " + city + ", weatherType=" + weatherType
+ ", temperature= " + temperature + ",humidity= " + humidity
+ "]";
}
}
Далее создаем документ для парсинга
Document doc = null;
URL url = new URL("http://export.yandex.ru/weather-ng/forecasts/"
+ cityID + ".xml");
//попробуйте пройти по ссылке и посмотреть, что именно мы парсим
// да и вообще саму структуру
URLConnection uc = url.openConnection();
InputStream is = uc.getInputStream();//создали поток
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.parse(is);//непосредственно парсинг
doc.getDocumentElement().normalize();
Связываем переменную класса NodeList с тегом forecast (см. export.yandex.ru/weather-ng/forecasts/29642.xml [3] — страница с погодой)
nl = doc.getElementsByTagName("forecast").item(0).getChildNodes();
Т.е. получилось у нас что-то типа:
try {
Document doc = null;
URL url = new URL("http://export.yandex.ru/weather-ng/forecasts/"
+ cityID + ".xml");
//попробуйте пройти по ссылке и посмотреть, что именно мы парсим
// да и вообще саму структуру
URLConnection uc = url.openConnection();
InputStream is = uc.getInputStream();//создали поток
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.parse(is);//непосредственно парсинг
doc.getDocumentElement().normalize();
nl = doc.getElementsByTagName("forecast").item(0).getChildNodes();
//из этого родителя нам и нужно содердимое
} catch (Exception ex) {
JOptionPane.showMessageDialog(null,
"Ошибка при запросе к Яндекс АПИ");
ex.printStackTrace();
}
Что ж связали и разобрали, пора извлекать непосредственно погоду. Как видим у forecast есть наследник fact, а в нем в свою очередь — station и temperature (в названии города иногда указывается аэропорт). Теперь из station и temperature вытащим погоду и название города:
for (int i = 0; i < nl.getLength(); i++) {
Node child = nl.item(i);
if (child instanceof Element) {
if (child.getNodeName().equals("fact")) {
Node childOfChild = null;
for (int j = 0; j < child.getChildNodes().getLength(); j++) {
childOfChild = child.getChildNodes().item(j);
if ("station".equals(childOfChild.getNodeName())) {
todayWeather.city = childOfChild.getTextContent();
}
if ("temperature".equals(childOfChild.getNodeName()))
todayWeather.temperature = Integer
.parseInt(childOfChild.getTextContent());
if ("weather_type_short".equals(childOfChild
.getNodeName())) {
todayWeather.weatherType = childOfChild
.getTextContent();
}
if ("image".equals(childOfChild.getNodeName())) {
todayWeather.imgType = childOfChild
.getTextContent();
}
if ("humidity".equals(childOfChild.getNodeName())) {
todayWeather.humidity = childOfChild
.getTextContent();
}
}
}
if (child.getNodeName().equals("informer")) {
Node childOfChild = null;
for (int j =0; j<child.getChildNodes().getLength(); j++){
childOfChild = child.getChildNodes().item(j);
if ((childOfChild.getNodeName().equals("temperature")) &&
(childOfChild.getAttributes().item(1).getTextContent().equals("night")))
{ todayWeather.tomNight = childOfChild.getTextContent(); }
if (childOfChild.getNodeName().equals("temperature") &&
(childOfChild.getAttributes().item(1).getTextContent().equals("tomorrow")))
{ todayWeather.tom = childOfChild.getTextContent();}
}
}
}
}
WFrame.today = todayWeather;
Циклом for перебираем всех детей и выбираем лишь нужных нам, как видите я еще выбрал влажность (humidity) и тип картинки. В остальном думаю все предельно понятно.
Осталось лишь написать обработчик события для кнопки:
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
String cityID = idField.getText();
if (cityID == null)
throw new IOException("Не введен ID города");
WeatherParser.parse(cityID); // процесс присваивания переменным необходимыми значениями
label.setText(today.city + "(сейчас): " + today.temperature
+ " С; " + today.weatherType + " Влж:"
+ today.humidity + " %");
iLabel = new JLabel(new ImageIcon(ImageIO.read(new URL(
"http://img.yandex.net/i/wiz" + today.imgType
+ ".png"))));
tLabel.setText("Погода завтра ночь/день: "+ today.tomNight+"/"+ today.tom);
//некорректные конечно названия переменных
} catch (IOException e1) {
e1.printStackTrace();
}
contentPane.add(iLabel);
}
});
В заключении хотелось бы попросить прощения за быдлокодинг в некоторых местах, буду рад если ткнете носом в таковой. Еще одна проблема, которая возникла у меня и которую я так и не решил — отображение картинки, которая появляется лишь если растянуть форму. Так что готов выслушать любую обоснованную критику.
Всем спасибо за внимание, удачного дня!
Код полностью:
1. Класс с фреймом
<code>
public class WFrame extends JFrame {
static Weather today;
JPanel panel;
JTextField idField;
JButton btn;
JLabel label;
JLabel tLabel;
JLabel iLabel;
Box b;
static String id;
static int t;
Container contentPane;
public WFrame() {
today = null;
JPopupMenu menu = new JPopupMenu();
menu.add(new AbstractAction("Пенза") {
@Override
public void actionPerformed(ActionEvent e) {
setCity("27962");
}
});
//несомненно, можно добавить любой город, список которых можно найти здесь
// http://weather.yandex.ru/static/cities.xml
GridBagLayout layout = new GridBagLayout(); // выбрал его, потому что раньше с ним не работал, вот и экперементирую
setTitle("Weather");
setPreferredSize(new Dimension(350, 300));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
contentPane = getContentPane();
panel = new JPanel();
panel.setLayout(layout);
idField = new JTextField();
idField.setColumns(6);
idField.addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent arg0) {
}
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
btn.doClick();
}
}
@Override
public void keyPressed(KeyEvent arg0) {
}
});
// парсим содержимое xml, вытаскиваем от туда необходимые данные
// заполняем метки
btn = new JButton("Хочу все знать");
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
String cityID = idField.getText();
if (cityID == null)
throw new IOException("Не введен ID города");
WeatherParser.parse(cityID); // процесс присваивания переменным необходимыми значениями
label.setText(today.city + "(сейчас): " + today.temperature
+ " С; " + today.weatherType + " Влж:"
+ today.humidity + " %");
iLabel = new JLabel(new ImageIcon(ImageIO.read(new URL(
"http://img.yandex.net/i/wiz" + today.imgType
+ ".png"))));
tLabel.setText("Погода завтра ночь/день: "+ today.tomNight+"/"+ today.tom);
//некорректные конечно названия переменных
} catch (IOException e1) {
e1.printStackTrace();
}
contentPane.add(iLabel);
}
});
// расположение элементов, в GUI я не силен, критику приму
label = new JLabel("Наберите в поле ID вашего города");
label.setFont(new Font("Verdana", Font.PLAIN, 14));
tLabel = new JLabel("Погода завтра");
tLabel.setFont(new Font("Verdana", Font.PLAIN, 14));
iLabel = new JLabel();
panel.add(label,
new GBC(0, 0, 2, 1).setFill(GBC.NORTH).setWeight(100, 0));
panel.add(iLabel,
new GBC(0, 1, 2, 1).setFill(GBC.NORTH).setWeight(100, 0));
panel.add(btn, new GBC(0, 3, 2, 1).setFill(GBC.NORTH).setWeight(100, 0));
panel.add(idField,
new GBC(0, 2, 2, 1).setAnchor(GBC.NORTH)
.setFill(GBC.HORIZONTAL).setIpad(50, 0));
panel.add(tLabel,
new GBC(0, 4, 2, 1).setAnchor(GBC.NORTH)
.setFill(GBC.HORIZONTAL).setIpad(50, 0));
add(panel);
panel.setComponentPopupMenu(menu);
pack();
}
public static void main(String[] args) {
// loginWithProxy();
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
WFrame frame = new WFrame();
frame.setDefaultLookAndFeelDecorated(true);
}
});
}
public void setCity(String id) {
this.id = id;
idField.setText(id);
}
}
2. Класс Weather
public class Weather {
String city;
String weatherType;
String imgType;
String humidity;// Влажность
String tom, tomNight;
int temperature;
public String toString() {
return "Weather[city= " + city + ", weatherType=" + weatherType
+ ", temperature= " + temperature + ",humidity= " + humidity
+ "]";
}
}
3. Класс парсинга:
public class WeatherParser {
static Weather tomorrowWeather;
WeatherParser(WFrame f) {
tomorrowWeather = new Weather();
}
public static void parse(String cityID) {
Weather todayWeather = new Weather();
NodeList nl = null;
try {
Document doc = null;
URL url = new URL("http://export.yandex.ru/weather-ng/forecasts/"
+ cityID + ".xml");
//попробуйте пройти по ссылке и посмотреть, что именно мы парсим
// да и вообще саму структуру
URLConnection uc = url.openConnection();
InputStream is = uc.getInputStream();//создали поток
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.parse(is);//непосредственно парсинг
doc.getDocumentElement().normalize();
nl = doc.getElementsByTagName("forecast").item(0).getChildNodes();
//из этого родителя нам и нужно содердимое
} catch (Exception ex) {
JOptionPane.showMessageDialog(null,
"Ошибка при запросе к Яндекс АПИ");
ex.printStackTrace();
}
for (int i = 0; i < nl.getLength(); i++) {
Node child = nl.item(i);
if (child instanceof Element) {
if (child.getNodeName().equals("fact")) {
Node childOfChild = null;
for (int j = 0; j < child.getChildNodes().getLength(); j++) {
childOfChild = child.getChildNodes().item(j);
if ("station".equals(childOfChild.getNodeName())) {
todayWeather.city = childOfChild.getTextContent();
}
if ("temperature".equals(childOfChild.getNodeName()))
todayWeather.temperature = Integer
.parseInt(childOfChild.getTextContent());
if ("weather_type_short".equals(childOfChild
.getNodeName())) {
todayWeather.weatherType = childOfChild
.getTextContent();
}
if ("image".equals(childOfChild.getNodeName())) {
todayWeather.imgType = childOfChild
.getTextContent();
}
if ("humidity".equals(childOfChild.getNodeName())) {
todayWeather.humidity = childOfChild
.getTextContent();
}
}
}
if (child.getNodeName().equals("informer")) {
Node childOfChild = null;
for (int j =0; j<child.getChildNodes().getLength(); j++){
childOfChild = child.getChildNodes().item(j);
if ((childOfChild.getNodeName().equals("temperature")) &&
(childOfChild.getAttributes().item(1).getTextContent().equals("night")))
{ todayWeather.tomNight = childOfChild.getTextContent(); }
if (childOfChild.getNodeName().equals("temperature") &&
(childOfChild.getAttributes().item(1).getTextContent().equals("tomorrow")))
{ todayWeather.tom = childOfChild.getTextContent();}
}
}
}
}
WFrame.today = todayWeather;
}
}
4. Ну и класс GBC
import java.awt.GridBagConstraints;
public class GBC extends GridBagConstraints
{
public GBC(int gridx, int gridy)
{
this.gridx = gridx;
this.gridy = gridy;
}
public GBC(int gridx, int gridy, int gridwidth, int gridheight)
{
this.gridx = gridx;
this.gridy = gridy;
this.gridwidth = gridwidth;
this.gridheight = gridheight;
}
public GBC setAnchor(int anchor)
{
this.anchor = anchor;
return this;
}
public GBC setFill(int fill)
{
this.fill = fill;
return this;
}
public GBC setWeight(double weightx, double weighty)
{
this.weightx = weightx;
this.weighty = weighty;
return this;
}
public GBC setInsets(int distance)
{
this.insets = new java.awt.Insets(
distance, distance, distance, distance);
return this;
}
public GBC setInsets(int top, int left, int bottom, int right)
{
this.insets = new java.awt.Insets(
top, left, bottom, right);
return this;
}
public GBC setIpad(int ipadx, int ipady)
{
this.ipadx = ipadx;
this.ipady = ipady;
return this;
}
}
Автор: vlad0058
Источник [4]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/23645
Ссылки в тексте:
[1] Java для начинающих: http://hitage.ru/taxonomy/term/103
[2] weather.yandex.ru/static/cities.xml: http://weather.yandex.ru/static/cities.xml
[3] export.yandex.ru/weather-ng/forecasts/29642.xml: http://export.yandex.ru/weather-ng/forecasts/29642.xml
[4] Источник: http://habrahabr.ru/post/164101/
Нажмите здесь для печати.