315 lines
9.2 KiB
Java
315 lines
9.2 KiB
Java
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 javax.xml.XMLConstants;
|
|
import javax.xml.parsers.DocumentBuilder;
|
|
import javax.xml.parsers.DocumentBuilderFactory;
|
|
import javax.xml.parsers.ParserConfigurationException;
|
|
|
|
import org.w3c.dom.Document;
|
|
import org.w3c.dom.Element;
|
|
import org.w3c.dom.Node;
|
|
import org.w3c.dom.NodeList;
|
|
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;
|
|
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;
|
|
private DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
|
|
|
private DatamartTranslation dmt = new DatamartTranslation();
|
|
|
|
public ForecastProcessor(TownInfo[] towns, CanadaDatamartProvider cdp) {
|
|
this.towns = towns;
|
|
this.cdp = cdp;
|
|
try
|
|
{
|
|
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
|
}
|
|
catch (ParserConfigurationException e)
|
|
{
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
public void stringToOffset(String dayString) {
|
|
|
|
}
|
|
|
|
public int getDayIndex(String dayString) {
|
|
dayString = dayString.trim();
|
|
if (dayString.equalsIgnoreCase("today") || dayString.equalsIgnoreCase("tonight"))
|
|
return 0;
|
|
dayString = dayString.toLowerCase().replace("night", "").trim();
|
|
Calendar calendar = Calendar.getInstance();
|
|
calendar.set(Calendar.HOUR_OF_DAY, 0);
|
|
calendar.set(Calendar.MINUTE, 0);
|
|
calendar.set(Calendar.SECOND, 0);
|
|
calendar.set(Calendar.MILLISECOND, 0);
|
|
for (int i = 1; i < 8; i++)
|
|
{
|
|
calendar.add(Calendar.HOUR, 24);
|
|
if (calendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.US).equalsIgnoreCase(dayString))
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public HourlyForecast[] processHourlyForecast(Document doc) {
|
|
DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmm");
|
|
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
|
NodeList hourlyForecast = doc.getElementsByTagName("hourlyForecast");
|
|
ArrayList<HourlyForecast> hourlyForecastArray = new ArrayList<HourlyForecast>(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();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return hourlyForecastArray.toArray(new HourlyForecast[0]);
|
|
}
|
|
|
|
public void processForecasts() {
|
|
System.out.println("Time to update");
|
|
ForecastDetails forecastDetails = new ForecastDetails();
|
|
ArrayList<TownForecast> townForecasts = new ArrayList<TownForecast>();
|
|
for (TownInfo townInfo : towns)
|
|
{
|
|
InputStream is = null;
|
|
Document doc = null;
|
|
try
|
|
{
|
|
|
|
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();
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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
|
|
{
|
|
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)
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
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))
|
|
{
|
|
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, percip);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
self = new Thread(this);
|
|
self.start();
|
|
}
|
|
}
|
|
|
|
public void end() {
|
|
running = false;
|
|
if(self != null)
|
|
self.interrupt();
|
|
}
|
|
|
|
public ForecastDetails getMostRecentForecast() {
|
|
synchronized (this)
|
|
{
|
|
return mostRecentForecast;
|
|
}
|
|
}
|
|
|
|
private void setMostRecentForecast(ForecastDetails forecast) {
|
|
synchronized (this)
|
|
{
|
|
mostRecentForecast = forecast;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void run() {
|
|
while (running)
|
|
{
|
|
try
|
|
{
|
|
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)
|
|
{
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
}
|