diff --git a/src/com/flaremicro/visualforecast/datamart/CanadaDatamartProvider.java b/src/com/flaremicro/visualforecast/datamart/CanadaDatamartProvider.java index 891640a..72681c8 100644 --- a/src/com/flaremicro/visualforecast/datamart/CanadaDatamartProvider.java +++ b/src/com/flaremicro/visualforecast/datamart/CanadaDatamartProvider.java @@ -6,6 +6,7 @@ import java.io.InputStreamReader; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import com.flaremicro.util.Util; @@ -35,8 +36,11 @@ public class CanadaDatamartProvider extends ForecastProvider { BufferedReader bufferedReader = null; try { - HashSet byCodes = new HashSet(Arrays.asList(byCode.split(","))); - HashSet byNameAndProvinces = new HashSet(Arrays.asList(byNameAndProvince.split(";"))); + ArrayList byCodesArray = new ArrayList(Arrays.asList(byCode.split(","))); + ArrayList byNameAndProvincesArray = new ArrayList(Arrays.asList(byNameAndProvince.split(";"))); + + HashSet byCodes = new HashSet(byCodesArray); + HashSet byNameAndProvinces = new HashSet(byNameAndProvincesArray); URL url = new URL("https://dd.weather.gc.ca/citypage_weather/docs/site_list_towns_en.csv"); bufferedReader = new BufferedReader(new InputStreamReader(url.openStream())); String line; @@ -51,12 +55,21 @@ public class CanadaDatamartProvider extends ForecastProvider { String[] data = line.trim().split(","); if (byCodes.contains(data[0].toLowerCase()) || byNameAndProvinces.contains(data[1].toLowerCase() + "," + data[2].toLowerCase())) { + int priority = Integer.MAX_VALUE; + if(byCodes.contains(data[0].toLowerCase())) + { + priority = byCodesArray.indexOf(data[0]); + } + else if(byNameAndProvinces.contains(data[1].toLowerCase() + "," + data[2].toLowerCase())) + { + priority = byNameAndProvincesArray.indexOf(data[1].toLowerCase() + "," + data[2].toLowerCase()); + } String code = data[0].trim(); String town = data[1].trim(); String province = data[2].trim(); float latitude = Float.parseFloat(data[3].trim().substring(0, data[3].length()-1)); float longitude = Float.parseFloat(data[4].trim().substring(0, data[4].length()-1)); - towns.add(new TownInfo(code, town, province, latitude, longitude)); + towns.add(new TownInfo(code, town, province, latitude, longitude, priority)); } } } @@ -69,11 +82,15 @@ public class CanadaDatamartProvider extends ForecastProvider { { Util.cleanClose(bufferedReader); } - forecastProcessor = new ForecastProcessor(towns.toArray(new TownInfo[0])); + Collections.sort(towns); + forecastProcessor = new ForecastProcessor(towns.toArray(new TownInfo[0]), this); forecastProcessor.processForecasts(); + this.getRenderPanel().notifyForecastProviderUpdate(); + forecastProcessor.begin(); + this.notifyForecastProviderUpdate(); ready = true; } - + @Override public ForecastDetails getForecast() { return forecastProcessor.getMostRecentForecast(); @@ -86,6 +103,7 @@ public class CanadaDatamartProvider extends ForecastProvider { @Override public void deinit() { + forecastProcessor.end(); propertyManager.store(); } diff --git a/src/com/flaremicro/visualforecast/datamart/ForecastProcessor.java b/src/com/flaremicro/visualforecast/datamart/ForecastProcessor.java index 8f06807..8a8627f 100644 --- a/src/com/flaremicro/visualforecast/datamart/ForecastProcessor.java +++ b/src/com/flaremicro/visualforecast/datamart/ForecastProcessor.java @@ -3,9 +3,14 @@ package com.flaremicro.visualforecast.datamart; import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; +import java.util.Date; import java.util.Locale; +import java.util.TimeZone; import java.util.concurrent.TimeUnit; import javax.xml.XMLConstants; @@ -22,11 +27,13 @@ import org.xml.sax.SAXException; import com.flaremicro.util.Util; import com.flaremicro.visualforecast.forecast.DayForecast; import com.flaremicro.visualforecast.forecast.ForecastDetails; +import com.flaremicro.visualforecast.forecast.HourlyForecast; import com.flaremicro.visualforecast.forecast.TownForecast; import com.flaremicro.visualforecast.forecast.ValueCheck; public class ForecastProcessor implements Runnable { private final TownInfo[] towns; + private final CanadaDatamartProvider cdp; private ForecastDetails mostRecentForecast = null; private boolean running = false; private Thread self; @@ -34,9 +41,9 @@ public class ForecastProcessor implements Runnable { private DatamartTranslation dmt = new DatamartTranslation(); - public ForecastProcessor(TownInfo[] towns) { + public ForecastProcessor(TownInfo[] towns, CanadaDatamartProvider cdp) { this.towns = towns; - + this.cdp = cdp; try { dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); @@ -70,20 +77,74 @@ public class ForecastProcessor implements Runnable { return -1; } + public void processHourlyForecast(TownForecast forecast, Document doc) { + DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmm"); + dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + NodeList hourlyForecast = doc.getElementsByTagName("hourlyForecast"); + ArrayList hourlyForecastArray = new ArrayList(12); + for (int i = 0; i < hourlyForecast.getLength(); i++) + { + if (hourlyForecast.item(i).getNodeType() == Node.ELEMENT_NODE) + { + Element element = (Element) hourlyForecast.item(i); + byte iconCode = dmt.icon(XMLUtils.getIntFromTag(element, "iconCode", ValueCheck.NO_DATA_INT)); + byte temp = XMLUtils.getByteFromTag(element, "temperature", ValueCheck.NO_DATA_BYTE); + byte windChill = XMLUtils.getByteFromTag(element, "windChill", ValueCheck.NO_DATA_BYTE); + float percip = XMLUtils.getFloatFromTag(element, "lop", ValueCheck.NO_DATA_FLOAT); + String dateTimeUTC = XMLUtils.getStringFromAttribute(element, "dateTimeUTC"); + if (dateTimeUTC != null) + { + try + { + Date date = dateFormat.parse(dateTimeUTC); + hourlyForecastArray.add(new HourlyForecast(date, iconCode, temp, (short) 0, percip, windChill)); + } + catch (ParseException e) + { + e.printStackTrace(); + } + } + } + } + forecast.setHourlyForecast(hourlyForecastArray.toArray(new HourlyForecast[0])); + } + public void processForecasts() { + System.out.println("Time to update"); ForecastDetails forecastDetails = new ForecastDetails(); ArrayList townForecasts = new ArrayList(); for (TownInfo townInfo : towns) { DayForecast[] dayForecasts = new DayForecast[8]; InputStream is = null; + Document doc = null; try { - URL url = new URL("https://dd.weather.gc.ca/citypage_weather/xml/BC/" + townInfo.code + "_e.xml"); + URL url = new URL("https://dd.weather.gc.ca/citypage_weather/xml/" + townInfo.province + "/" + townInfo.code + "_e.xml"); DocumentBuilder db = dbf.newDocumentBuilder(); is = url.openStream(); - Document doc = db.parse(is); + doc = db.parse(is); + } + catch (IOException e) + { + e.printStackTrace(); + } + catch (ParserConfigurationException e) + { + e.printStackTrace(); + } + catch (SAXException e) + { + e.printStackTrace(); + } + finally + { + Util.cleanClose(is); + } + + if (doc != null) + { NodeList nodeList = doc.getElementsByTagName("forecast"); for (int i = 0; i < nodeList.getLength(); i++) { @@ -92,7 +153,8 @@ public class ForecastProcessor implements Runnable { Element node = (Element) nodeList.item(i); int dayIndex = getDayIndex(XMLUtils.getStringFromTagAttribute(node, "period", "textForecastName")); Element abbForecast = XMLUtils.getFistElement(node, "abbreviatedForecast"); - int iconIndex = XMLUtils.getIntFromTag(abbForecast, "iconCode", 0); + int iconIndex = XMLUtils.getIntFromTag(abbForecast, "iconCode", ValueCheck.NO_DATA_INT); + float percip = XMLUtils.getFloatFromTag(abbForecast, "pop", ValueCheck.NO_DATA_FLOAT); String textForecast = XMLUtils.getStringFromTag(abbForecast, "textSummary"); WeatherLines lines = dmt.weatherName(textForecast); @@ -109,11 +171,11 @@ public class ForecastProcessor implements Runnable { try { byte val = Byte.parseByte(n.getTextContent().trim()); - if(XMLUtils.getStringFromAttribute(n, "class").trim().equalsIgnoreCase("high")) + if (XMLUtils.getStringFromAttribute(n, "class").trim().equalsIgnoreCase("high")) { hi = val; } - else if(XMLUtils.getStringFromAttribute(n, "class").trim().equalsIgnoreCase("low")) + else if (XMLUtils.getStringFromAttribute(n, "class").trim().equalsIgnoreCase("low")) { lo = val; } @@ -136,45 +198,49 @@ public class ForecastProcessor implements Runnable { { dayForecasts[dayIndex].hiTemp = hi; } - + if (!ValueCheck.valueNoData(percip)) + { + if (ValueCheck.valueNoData(dayForecasts[dayIndex].percipPercent)) + { + dayForecasts[dayIndex].percipPercent = percip; + } + else + { + dayForecasts[dayIndex].percipPercent = (dayForecasts[dayIndex].percipPercent + percip) - (percip * dayForecasts[dayIndex].percipPercent) / 100F; + } + } } else { - dayForecasts[dayIndex] = new DayForecast(hi, lo, icon, lines.line1, lines.line2, ValueCheck.NO_DATA_FLOAT); + dayForecasts[dayIndex] = new DayForecast(hi, lo, icon, lines.line1, lines.line2, percip); } } } } - } - catch (IOException e) - { - e.printStackTrace(); - } - catch (ParserConfigurationException e) - { - e.printStackTrace(); - } - catch (SAXException e) - { - e.printStackTrace(); - } - finally - { - Util.cleanClose(is); - } - for (int i = 0; i < dayForecasts.length; i++) - { - if (dayForecasts[i] == null) + for (int i = 0; i < dayForecasts.length; i++) { - dayForecasts[i] = new DayForecast(); + if (dayForecasts[i] == null) + { + dayForecasts[i] = new DayForecast(); + } } + TownForecast tf = new TownForecast(townInfo.townName + ", " + townInfo.province, dayForecasts); + townForecasts.add(tf); + this.processHourlyForecast(tf, doc); } - townForecasts.add(new TownForecast(townInfo.townName + ", " + townInfo.province, dayForecasts)); } forecastDetails.setTownForecast(townForecasts.toArray(new TownForecast[0])); setMostRecentForecast(forecastDetails); } + public void begin() { + if (!running) + { + running = true; + new Thread(this).start(); + } + } + public void end() { running = false; self.interrupt(); @@ -201,8 +267,14 @@ public class ForecastProcessor implements Runnable { { try { - Thread.sleep(TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS)); + long hourInMillis = 60 * 60 * 1000; + long startDateInMillis = System.currentTimeMillis(); + long millisSinceLastHourChange = startDateInMillis % hourInMillis; + long millisToNextHourChange = hourInMillis - millisSinceLastHourChange; + Thread.sleep(millisToNextHourChange); processForecasts(); + this.cdp.notifyForecastProviderUpdate(); + //Thread.sleep(TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS)); } catch (InterruptedException e) { diff --git a/src/com/flaremicro/visualforecast/datamart/TownInfo.java b/src/com/flaremicro/visualforecast/datamart/TownInfo.java index f619d2b..8c02286 100644 --- a/src/com/flaremicro/visualforecast/datamart/TownInfo.java +++ b/src/com/flaremicro/visualforecast/datamart/TownInfo.java @@ -1,17 +1,24 @@ package com.flaremicro.visualforecast.datamart; -public class TownInfo { +public class TownInfo implements Comparable { public final String code; public final String townName; public final String province; public final float northLat; public final float westLong; + public final int priority; - public TownInfo(String code, String townName, String province, float northLat, float westLong) { + public TownInfo(String code, String townName, String province, float northLat, float westLong, int priority) { this.code = code; this.townName = townName; this.province = province; this.northLat = northLat; this.westLong = westLong; + this.priority = priority; + } + + @Override + public int compareTo(TownInfo o) { + return priority - o.priority; } } diff --git a/src/com/flaremicro/visualforecast/datamart/XMLUtils.java b/src/com/flaremicro/visualforecast/datamart/XMLUtils.java index 317f269..f34a46f 100644 --- a/src/com/flaremicro/visualforecast/datamart/XMLUtils.java +++ b/src/com/flaremicro/visualforecast/datamart/XMLUtils.java @@ -11,22 +11,21 @@ public class XMLUtils { return l.item(0).getTextContent(); return null; } - + public static String getStringFromTagAttribute(Element parent, String tag, String attribute) { NodeList l = parent.getElementsByTagName(tag); if (l.getLength() > 0) { Node attrib = l.item(0).getAttributes().getNamedItem(attribute); - if(attrib != null) + if (attrib != null) return attrib.getTextContent(); } return null; } - - + public static String getStringFromAttribute(Node n, String attribute) { Node attrib = n.getAttributes().getNamedItem(attribute); - if(attrib != null) + if (attrib != null) return attrib.getTextContent(); return null; } @@ -41,15 +40,47 @@ public class XMLUtils { return null; } - public static Integer getIntFromTag(Element abbForecast, String tag, Integer fallback) { - String content = getStringFromTag(abbForecast, tag); + public static Integer getIntFromTag(Element parent, String tag, Integer fallback) { + String content = getStringFromTag(parent, tag); + if(content == null || content.length() == 0) + return fallback; try { return Integer.parseInt(content); } catch (NumberFormatException ex) { - + + } + return fallback; + } + + public static float getFloatFromTag(Element parent, String tag, float fallback) { + String content = getStringFromTag(parent, tag); + if(content == null || content.length() == 0) + return fallback; + try + { + return Float.parseFloat(content); + } + catch (NumberFormatException ex) + { + + } + return fallback; + } + + public static byte getByteFromTag(Element parent, String tag, Byte fallback) { + String content = getStringFromTag(parent, tag); + if(content == null || content.length() == 0) + return fallback; + try + { + return Byte.parseByte(content); + } + catch (NumberFormatException ex) + { + } return fallback; } diff --git a/src/translation.csv b/src/translation.csv index 926378c..e14a7f8 100644 --- a/src/translation.csv +++ b/src/translation.csv @@ -1,5 +1,6 @@ +Flurries or rain showers,Flurries/,Showers Mainly sunny,Mainly,Sunny -Mainly cloudy,Mainly, Cloudy +Mainly cloudy,Mainly,Cloudy Sunny,Sunny, A few clouds,Few,Clouds A mix of sun and cloud,Partly,Cloudy @@ -9,12 +10,15 @@ Cloudy with sunny periods,Sunny,Periods Increasing cloudiness,Increasing,Clouds Clearing,Clearing, Clear,Sun, +partly cloudy,Partly,Cloudy, +showers or drizzle,Showers/,Drizzle Chance of showers,Chance,Showers A few showers,Few,Showers Chance of drizzle or rain,Chance,Drizzle A few flurries or rain showers,Flurries/,Showers Chance of flurries or rain showers,Flurries/,Showers -Chance of rain showers or flurries,Flurries/,Showers +Chance of rain showers or flurries,Showers/Flurries +chance of rain showers or wet flurries,Showers/,Flurries A few flurries,Flurries, Chance of flurries,Chance,Flurries A few wet flurries,Wet,Flurries @@ -97,3 +101,4 @@ Chance of thunderstorms,Thunder,Storms Snow and blowing snow,Blowing,Snow Windy,Windy Smoke,Smoke +rain showers or flurries,Showers/,Flurries