diff --git a/src/com/flaremicro/visualforecast/datamart/CanadaDatamartProvider.java b/src/com/flaremicro/visualforecast/datamart/CanadaDatamartProvider.java index 72681c8..6e4f9b4 100644 --- a/src/com/flaremicro/visualforecast/datamart/CanadaDatamartProvider.java +++ b/src/com/flaremicro/visualforecast/datamart/CanadaDatamartProvider.java @@ -67,9 +67,23 @@ public class CanadaDatamartProvider extends ForecastProvider { String code = data[0].trim(); String town = data[1].trim(); String province = data[2].trim(); + String supportedDisplaysString = propertyManager.getStringNoSet("displays-enabled.code."+code, ""); + if(supportedDisplaysString.trim().length() <= 0) + supportedDisplaysString = propertyManager.getString("displays-enabled.town."+town.toLowerCase().replaceAll("\\s", "_")+"."+province.toLowerCase().replace("\\s", "_"), ""); + 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, priority)); + TownInfo townInfo = new TownInfo(code, town, province, latitude, longitude, priority); + if(supportedDisplaysString.trim().length() > 0) + { + String[] displays = supportedDisplaysString.split(","); + for(int i = 0; i < displays.length; i++) + { + displays[i] = displays[i].trim(); + } + townInfo.setSupportedDisplays(displays); + } + towns.add(townInfo); } } } diff --git a/src/com/flaremicro/visualforecast/datamart/DatamartTranslation.java b/src/com/flaremicro/visualforecast/datamart/DatamartTranslation.java index dab7c34..7674be3 100644 --- a/src/com/flaremicro/visualforecast/datamart/DatamartTranslation.java +++ b/src/com/flaremicro/visualforecast/datamart/DatamartTranslation.java @@ -4,6 +4,7 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.HashMap; +import java.util.Map; import com.flaremicro.util.Util; import com.flaremicro.visualforecast.icons.IconProvider; @@ -12,6 +13,7 @@ public class DatamartTranslation { private HashMap iconTranslation = new HashMap(); private HashMap stringTranslation = new HashMap(); + private HashMap fallBacks = new HashMap(); public DatamartTranslation() { iconTranslation.put(0, IconProvider.SUN.id); @@ -65,16 +67,18 @@ public class DatamartTranslation { { reader = new BufferedReader(new InputStreamReader(this.getClass().getResourceAsStream("/translation.csv"))); String line; - while((line = reader.readLine()) != null) + while ((line = reader.readLine()) != null) { String[] info = line.split(","); - if(info.length == 2) + if (info.length == 2) { stringTranslation.put(info[0].toLowerCase(), new WeatherLines(info[1], "")); + fallBacks.put(info[1], new WeatherLines(info[1], "")); } - else if(info.length == 3) + else if (info.length == 3) { stringTranslation.put(info[0].toLowerCase(), new WeatherLines(info[1], info[2])); + fallBacks.put(info[1] + " " + info[2], new WeatherLines(info[1], info[2])); } } } @@ -90,20 +94,164 @@ public class DatamartTranslation { } public byte icon(int icon) { - if(!iconTranslation.containsKey(icon)) + if (!iconTranslation.containsKey(icon)) return IconProvider.INVALID.id; return iconTranslation.get(icon); } public WeatherLines weatherName(String desc) { WeatherLines lines = stringTranslation.get(desc.toLowerCase()); - if(lines == null) + if (lines == null) { - System.out.println("FAILED:"+desc.toLowerCase()); - return new WeatherLines("TRANSLAT.", "FAILURE"); + System.out.println("FAILED:" + desc.toLowerCase()); + return getClosestMatching(desc); } else return lines; } + + public WeatherLines getClosestMatching(String desc) { + WeatherLines bestMatch = new WeatherLines("TRANSLAT.", "FAILURE"); + desc = desc.toLowerCase(); + double bestRatio = Integer.MAX_VALUE; + for (Map.Entry set : fallBacks.entrySet()) + { + double ratio2 = getLevenshteinDistance(desc, set.getKey()); + if (ratio2 < bestRatio) + { + bestRatio = ratio2; + bestMatch = set.getValue(); + } + } + System.out.println("Got " + bestMatch.line1 + " " + bestMatch.line2 + " for desc " + desc); + return bestMatch; + } + + public static int getLevenshteinDistance(CharSequence s, CharSequence t) { + if (s == null || t == null) + { + throw new IllegalArgumentException("Strings must not be null"); + } + int n = s.length(); + int m = t.length(); + + if (n == 0) + { + return m; + } + else if (m == 0) + { + return n; + } + + if (n > m) + { + // swap the input strings to consume less memory + final CharSequence tmp = s; + s = t; + t = tmp; + n = m; + m = t.length(); + } + + final int[] p = new int[n + 1]; + // indexes into strings s and t + int i; // iterates through s + int j; // iterates through t + int upper_left; + int upper; + + char t_j; // jth character of t + int cost; + + for (i = 0; i <= n; i++) + { + p[i] = i; + } + + for (j = 1; j <= m; j++) + { + upper_left = p[0]; + t_j = t.charAt(j - 1); + p[0] = j; + + for (i = 1; i <= n; i++) + { + upper = p[i]; + cost = s.charAt(i - 1) == t_j ? 0 : 1; + // minimum of cell to the left+1, to the top+1, diagonally left and up +cost + p[i] = Math.min(Math.min(p[i - 1] + 1, p[i] + 1), upper_left + cost); + upper_left = upper; + } + } + + return p[n]; + } + + double findSimilarityRatio(String sentence1, String sentence2) { + + HashMap firstSentenceMap = new HashMap(); + HashMap secondSentenceMap = new HashMap(); + + String[] firstSentenceWords = sentence1.split(" "); + String[] secondSentenceWords = sentence2.split(" "); + + for (String word : firstSentenceWords) + { + if (firstSentenceMap.containsKey(word)) + { + firstSentenceMap.put(word, firstSentenceMap.get(word) + 1); + } + else + { + firstSentenceMap.put(word, 1); + } + } + + for (String word : secondSentenceWords) + { + if (secondSentenceMap.containsKey(word)) + { + secondSentenceMap.put(word, secondSentenceMap.get(word) + 1); + } + else + { + secondSentenceMap.put(word, 1); + } + } + + double totalWords = 0; + double totalHits = 0; + + if (firstSentenceWords.length >= secondSentenceWords.length) + { + totalWords = firstSentenceWords.length; + for (Map.Entry entry : firstSentenceMap.entrySet()) + { + String key = entry.getKey(); + + if (secondSentenceMap.containsKey(key)) + { + totalHits = totalHits + Math.min(secondSentenceMap.get(key), firstSentenceMap.get(key)); + } + } + } + else + { + totalWords = secondSentenceWords.length; + for (Map.Entry entry : secondSentenceMap.entrySet()) + { + String key = entry.getKey(); + + if (firstSentenceMap.containsKey(key)) + { + totalHits = totalHits + Math.min(secondSentenceMap.get(key), firstSentenceMap.get(key)); + } + } + + } + + return totalHits / totalWords; + } } class WeatherLines { diff --git a/src/com/flaremicro/visualforecast/datamart/ForecastProcessor.java b/src/com/flaremicro/visualforecast/datamart/ForecastProcessor.java index 8a8627f..3ce6715 100644 --- a/src/com/flaremicro/visualforecast/datamart/ForecastProcessor.java +++ b/src/com/flaremicro/visualforecast/datamart/ForecastProcessor.java @@ -11,7 +11,6 @@ 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; import javax.xml.parsers.DocumentBuilder; @@ -26,6 +25,7 @@ import org.xml.sax.SAXException; import com.flaremicro.util.Util; import com.flaremicro.visualforecast.forecast.DayForecast; +import com.flaremicro.visualforecast.forecast.DetailedForecast; import com.flaremicro.visualforecast.forecast.ForecastDetails; import com.flaremicro.visualforecast.forecast.HourlyForecast; import com.flaremicro.visualforecast.forecast.TownForecast; @@ -77,7 +77,7 @@ public class ForecastProcessor implements Runnable { return -1; } - public void processHourlyForecast(TownForecast forecast, Document doc) { + public HourlyForecast[] processHourlyForecast(Document doc) { DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmm"); dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); NodeList hourlyForecast = doc.getElementsByTagName("hourlyForecast"); @@ -106,7 +106,7 @@ public class ForecastProcessor implements Runnable { } } } - forecast.setHourlyForecast(hourlyForecastArray.toArray(new HourlyForecast[0])); + return hourlyForecastArray.toArray(new HourlyForecast[0]); } public void processForecasts() { @@ -115,7 +115,6 @@ public class ForecastProcessor implements Runnable { ArrayList townForecasts = new ArrayList(); for (TownInfo townInfo : towns) { - DayForecast[] dayForecasts = new DayForecast[8]; InputStream is = null; Document doc = null; try @@ -145,105 +144,136 @@ public class ForecastProcessor implements Runnable { if (doc != null) { - NodeList nodeList = doc.getElementsByTagName("forecast"); - for (int i = 0; i < nodeList.getLength(); i++) + TownForecast tf = new TownForecast(townInfo.townName + ", " + townInfo.province, process7DayForecast(doc)); + townForecasts.add(tf); + tf.setHourlyForecast(this.processHourlyForecast(doc)); + tf.setDetailedForecast(process36HourForecast(doc)); + tf.setSupportedDisplays(townInfo.getSupportedDisplays()); + } + } + + forecastDetails.setTownForecast(townForecasts.toArray(new TownForecast[0])); + setMostRecentForecast(forecastDetails); + } + + private DetailedForecast[] process36HourForecast(Document doc) + { + DetailedForecast[] detailedForecast = new DetailedForecast[3]; + NodeList nodeList = doc.getElementsByTagName("forecast"); + for (int i = 0; i < Math.min(detailedForecast.length, nodeList.getLength()); i++) + { + if (nodeList.item(1).getNodeType() == Node.ELEMENT_NODE) + { + if (nodeList.item(1).getNodeType() == Node.ELEMENT_NODE) { - if (nodeList.item(1).getNodeType() == Node.ELEMENT_NODE) + Element node = (Element) nodeList.item(i); + String title = XMLUtils.getStringFromTagAttribute(node, "period", "textForecastName"); + String textForecast = XMLUtils.getStringFromTag(node, "textSummary"); + detailedForecast[i] = new DetailedForecast(title, textForecast); + } + } + } + return detailedForecast; + } + + private DayForecast[] process7DayForecast(Document doc) + { + DayForecast[] dayForecasts = new DayForecast[8]; + NodeList nodeList = doc.getElementsByTagName("forecast"); + for (int i = 0; i < nodeList.getLength(); i++) + { + if (nodeList.item(1).getNodeType() == Node.ELEMENT_NODE) + { + 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", 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); + byte icon = dmt.icon(iconIndex); + + byte lo = ValueCheck.NO_DATA_BYTE; + byte hi = ValueCheck.NO_DATA_BYTE; + + Element element = XMLUtils.getFistElement(node, "temperatures"); + NodeList temps = element.getElementsByTagName("temperature"); + for (int j = 0; j < temps.getLength(); j++) + { + Node n = temps.item(j); + try { - 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", 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); - byte icon = dmt.icon(iconIndex); - - byte lo = ValueCheck.NO_DATA_BYTE; - byte hi = ValueCheck.NO_DATA_BYTE; - - Element element = XMLUtils.getFistElement(node, "temperatures"); - NodeList temps = element.getElementsByTagName("temperature"); - for (int j = 0; j < temps.getLength(); j++) + byte val = Byte.parseByte(n.getTextContent().trim()); + if (XMLUtils.getStringFromAttribute(n, "class").trim().equalsIgnoreCase("high")) { - Node n = temps.item(j); - try - { - byte val = Byte.parseByte(n.getTextContent().trim()); - if (XMLUtils.getStringFromAttribute(n, "class").trim().equalsIgnoreCase("high")) - { - hi = val; - } - else if (XMLUtils.getStringFromAttribute(n, "class").trim().equalsIgnoreCase("low")) - { - lo = val; - } - } - catch (NumberFormatException ex) - { - - } + hi = val; } - - if (dayIndex >= 0 && dayIndex <= 7) + else if (XMLUtils.getStringFromAttribute(n, "class").trim().equalsIgnoreCase("low")) { - if (dayForecasts[dayIndex] != null) + lo = val; + } + } + catch (NumberFormatException ex) + { + + } + } + + if (dayIndex >= 0 && dayIndex <= 7) + { + if (dayForecasts[dayIndex] != null) + { + if (lo != ValueCheck.NO_DATA_BYTE && dayForecasts[dayIndex].loTemp == ValueCheck.NO_DATA_BYTE) + { + dayForecasts[dayIndex].loTemp = lo; + } + if (hi != ValueCheck.NO_DATA_BYTE && dayForecasts[dayIndex].hiTemp == ValueCheck.NO_DATA_BYTE) + { + dayForecasts[dayIndex].hiTemp = hi; + } + if (!ValueCheck.valueNoData(percip)) + { + if (ValueCheck.valueNoData(dayForecasts[dayIndex].percipPercent)) { - if (lo != ValueCheck.NO_DATA_BYTE && dayForecasts[dayIndex].loTemp == ValueCheck.NO_DATA_BYTE) - { - dayForecasts[dayIndex].loTemp = lo; - } - if (hi != ValueCheck.NO_DATA_BYTE && dayForecasts[dayIndex].hiTemp == ValueCheck.NO_DATA_BYTE) - { - 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; - } - } + dayForecasts[dayIndex].percipPercent = percip; } else { - dayForecasts[dayIndex] = new DayForecast(hi, lo, icon, lines.line1, lines.line2, percip); + dayForecasts[dayIndex].percipPercent = (dayForecasts[dayIndex].percipPercent + percip) - (percip * dayForecasts[dayIndex].percipPercent) / 100F; } } } - } - for (int i = 0; i < dayForecasts.length; i++) - { - if (dayForecasts[i] == null) + else { - dayForecasts[i] = new DayForecast(); + dayForecasts[dayIndex] = new DayForecast(hi, lo, icon, lines.line1, lines.line2, percip); } } - TownForecast tf = new TownForecast(townInfo.townName + ", " + townInfo.province, dayForecasts); - townForecasts.add(tf); - this.processHourlyForecast(tf, doc); } } - forecastDetails.setTownForecast(townForecasts.toArray(new TownForecast[0])); - setMostRecentForecast(forecastDetails); + for (int i = 0; i < dayForecasts.length; i++) + { + if (dayForecasts[i] == null) + { + dayForecasts[i] = new DayForecast(); + } + } + return dayForecasts; } public void begin() { if (!running) { running = true; - new Thread(this).start(); + self = new Thread(this); + self.start(); } } public void end() { running = false; - self.interrupt(); + if(self != null) + self.interrupt(); } public ForecastDetails getMostRecentForecast() { @@ -262,7 +292,6 @@ public class ForecastProcessor implements Runnable { @Override public void run() { - self = Thread.currentThread(); while (running) { try diff --git a/src/com/flaremicro/visualforecast/datamart/TownInfo.java b/src/com/flaremicro/visualforecast/datamart/TownInfo.java index 8c02286..5da4f58 100644 --- a/src/com/flaremicro/visualforecast/datamart/TownInfo.java +++ b/src/com/flaremicro/visualforecast/datamart/TownInfo.java @@ -1,5 +1,11 @@ package com.flaremicro.visualforecast.datamart; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + public class TownInfo implements Comparable { public final String code; public final String townName; @@ -7,6 +13,7 @@ public class TownInfo implements Comparable { public final float northLat; public final float westLong; public final int priority; + private Set displays = new HashSet(); public TownInfo(String code, String townName, String province, float northLat, float westLong, int priority) { this.code = code; @@ -21,4 +28,24 @@ public class TownInfo implements Comparable { public int compareTo(TownInfo o) { return priority - o.priority; } + + public void addSupportedDisplay(String display) + { + this.displays.add(display); + } + + public void setSupportedDisplays(String ... displays){ + setSupportedDisplays(Arrays.asList(displays)); + } + + public void setSupportedDisplays(Collection displays){ + this.displays.clear(); + this.displays.addAll(displays); + } + + public Set getSupportedDisplays() + { + return Collections.unmodifiableSet(displays); + } + } diff --git a/src/translation.csv b/src/translation.csv index e14a7f8..1ffac7f 100644 --- a/src/translation.csv +++ b/src/translation.csv @@ -14,10 +14,11 @@ partly cloudy,Partly,Cloudy, showers or drizzle,Showers/,Drizzle Chance of showers,Chance,Showers A few showers,Few,Showers +chance of drizzle,Chance,Drizzle 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,Showers/Flurries +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 @@ -34,6 +35,7 @@ Overcast,Overcast, Showers,Showers, Chance of showers,Chance,Showers Periods of rain,Scattered,Rain +Periods of drizzle,Scattered,Rain Mostly Cloudy,Mostly,Cloudy Rain at times heavy,Heavy,Rain A few showers or drizzle,Drizzle @@ -102,3 +104,4 @@ Snow and blowing snow,Blowing,Snow Windy,Windy Smoke,Smoke rain showers or flurries,Showers/,Flurries +chance of wet flurries,Wet,Flurries