package com.hourlyweather.yrno.sunrise; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.MutableDateTime; import org.joda.time.chrono.ISOChronology; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; import com.hourlyweather.forecast.HourlyForecast; import com.hourlyweather.yrno.XmlParserUtil; public class SunriseFetcher { private static final DateTimeFormatter df = DateTimeFormat.forPattern( "YYYY-MM-dd'T'HH:mm:SS'Z'").withZone(DateTimeZone.UTC); public static void addSunlightDurationToForecast(HourlyForecast forecast) { XmlPullParser xpp; try { XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); factory.setNamespaceAware(true); xpp = factory.newPullParser(); } catch (XmlPullParserException e) { System.out.println("error creating xml parser: " + e.getMessage()); return; } InputStream input = getSunriseData(forecast); try { xpp.setInput(new BufferedReader(new InputStreamReader(input))); int eventType = xpp.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { if (eventType == XmlPullParser.START_TAG && "sun".equalsIgnoreCase(xpp.getName())) { // found the sun rise data, now get the duration String riseString = XmlParserUtil.getAttributeByName(xpp, "rise"); String setString = XmlParserUtil.getAttributeByName(xpp, "set"); if (riseString != null && setString != null) { DateTime rise = df.parseDateTime(riseString).withZone(forecast.getTimeZone()); DateTime set = df.parseDateTime(setString).withZone(forecast.getTimeZone()); setSunStateTimesToForecast(forecast, rise, set); } break; } eventType = xpp.next(); } input.close(); } catch (Exception e) { e.printStackTrace(); } } private static DateTime roundToTheHour(DateTime rise) { MutableDateTime tem = rise.toMutableDateTime(); tem.setRounding(ISOChronology.getInstance().hourOfDay(), MutableDateTime.ROUND_HALF_EVEN); return tem.toDateTime(); } /** * adds the suns state changes(rise and set) to the forecast * * @param forecast * @param rise * @param set */ private static void setSunStateTimesToForecast(HourlyForecast forecast, DateTime rise, DateTime set) { // round sun set and rise times before applying to forecast rise = roundToTheHour(rise); set = roundToTheHour(set); // check if the start time is day or night int hour = forecast.getStart().getHourOfDay(); int riseHour = rise.getHourOfDay(); int setHour = set.getHourOfDay(); // if hours cross midnight add 24 hours to account for the roll over if (riseHour > setHour) { hour += 24; setHour += 24; } // mark as day or night forecast.markHourAsSunChange(hour > riseHour && hour < setHour, 0); // reset the hour and setHour fields if they were changed if (hour >= 24) { setHour = set.getHourOfDay(); hour = forecast.getStart().getHourOfDay(); } int diff = 0; for (int i = 0; diff < forecast.getHours(); i++) { // mark the sun rises if they are in the future diff = riseHour + (24 * i) - hour; if (diff >= 0) forecast.markHourAsSunChange(true, diff); // mark the sun sets in the future diff = setHour + (24 * i) - hour; if (diff >= 0) forecast.markHourAsSunChange(false, diff); } } private static InputStream getSunriseData(HourlyForecast forecast) { // pull the sunrise xml from the yr.no api try { URL sunriseUrl = new URL( "http://api.yr.no/weatherapi/sunrise/1.0/?lat=" + forecast.getLat() + ";lon=" + forecast.getLon() + ";date=" + forecast.getStart().toString("YYYY-MM-dd")); return sunriseUrl.openStream(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } }