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

Погодник на java для начинающих и постарше

image

Приветствую всех в этот прекрасный день ожидания праздника, это моя первая статья на хабре, в которой я хотел бы рассказа про открытый 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/