Package nl.topicus.onderwijs.dashboard.modules.wettercom

Source Code of nl.topicus.onderwijs.dashboard.modules.wettercom.WetterComService

package nl.topicus.onderwijs.dashboard.modules.wettercom;

import java.io.StringReader;
import java.security.MessageDigest;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import nl.topicus.onderwijs.dashboard.config.ISettings;
import nl.topicus.onderwijs.dashboard.datasources.Weather;
import nl.topicus.onderwijs.dashboard.datatypes.WeatherReport;
import nl.topicus.onderwijs.dashboard.datatypes.WeatherType;
import nl.topicus.onderwijs.dashboard.keys.Key;
import nl.topicus.onderwijs.dashboard.modules.AbstractService;
import nl.topicus.onderwijs.dashboard.modules.DashboardRepository;
import nl.topicus.onderwijs.dashboard.modules.ServiceConfiguration;
import nl.topicus.onderwijs.dashboard.modules.topicus.RetrieverUtils;
import nl.topicus.onderwijs.dashboard.modules.topicus.StatusPageResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

@Service
@ServiceConfiguration(interval = 10, unit = TimeUnit.MINUTES)
public class WetterComService extends AbstractService {
  private static final Logger log = LoggerFactory
      .getLogger(WetterComService.class);

  private Map<Key, WeatherReport> reports = new ConcurrentHashMap<Key, WeatherReport>();

  @Autowired
  public WetterComService(ISettings settings) {
    super(settings);
  }

  @Override
  public void onConfigure(DashboardRepository repository) {
    for (Key key : getSettings().getKeysWithConfigurationFor(
        WetterComService.class)) {
      repository.addDataSource(key, Weather.class, new WeatherImpl(key,
          this));
    }
  }

  @Override
  public void refreshData() {
    try {
      Map<Key, Map<String, ?>> serviceSettings = getSettings()
          .getServiceSettings(WetterComService.class);
      for (Map.Entry<Key, Map<String, ?>> curSettingEntry : serviceSettings
          .entrySet()) {
        Map<String, ?> wetterSettingsForProject = curSettingEntry
            .getValue();
        String apiKey = wetterSettingsForProject.get("apiKey")
            .toString();
        String applicationName = wetterSettingsForProject.get(
            "applicationName").toString();
        String cityKey = wetterSettingsForProject.get("cityKey")
            .toString();
        double latitude = (Double) wetterSettingsForProject
            .get("latitude");
        double longitude = (Double) wetterSettingsForProject
            .get("longitude");

        String total = applicationName + apiKey + cityKey;

        byte[] thedigest = MessageDigest.getInstance("MD5").digest(
            total.getBytes("UTF-8"));
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < thedigest.length; i++) {
          sb.append(Integer.toString((thedigest[i] & 0xff) + 0x100,
              16).substring(1));
        }

        StatusPageResponse response = RetrieverUtils
            .getStatuspage(String.format("http://api.wetter.com"
                + "/forecast/weather/city/%s/project/%s/cs/%s",
                cityKey, applicationName, sb.toString()));
        DocumentBuilderFactory dbf = DocumentBuilderFactory
            .newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        InputSource is = new InputSource();
        is.setCharacterStream(new StringReader(response
            .getPageContent()));

        Document doc = db.parse(is);
        Element time = findTime(doc);
        WeatherReport report = new WeatherReport();
        report.setType(WeatherType.findType(Integer
            .parseInt(getTextForElement(time, "w"))));
        report.setRainfallProbability(Integer
            .parseInt(getTextForElement(time, "pc")));
        report.setMinTemperature(Double.parseDouble(getTextForElement(
            time, "tn")));
        report.setMaxTemperature(Double.parseDouble(getTextForElement(
            time, "tx")));
        report.setWindDirection(Integer.parseInt(getTextForElement(
            time, "wd")));
        report.setWindSpeed(Double.parseDouble(getTextForElement(time,
            "ws")));
        report.setSunrise(getSunrize(latitude, longitude));
        report.setSunset(getSunset(latitude, longitude));

        reports.put(curSettingEntry.getKey(), report);
      }
    } catch (Exception e) {
      log.error("Unable to refresh data from wetter.com: {} {}", e
          .getClass().getSimpleName(), e);
    }
  }

  private Element findTime(Document doc) {
    long curTime = System.currentTimeMillis() / 1000;
    NodeList nodes = doc.getElementsByTagName("time");
    for (int index = 0; index < nodes.getLength(); index++) {
      Element curElement = (Element) nodes.item(index);
      long date = Long.parseLong(getTextForElement(curElement, "d"));
      long period = Long.parseLong(getTextForElement(curElement, "p"));
      if (date != 0 && period != 0) {
        if (date < curTime && (date + period * 3600) > curTime)
          return curElement;
      }
    }
    return null;
  }

  private String getTextForElement(Element root, String child) {
    NodeList subNodes = root.getChildNodes();
    for (int innerIndex = 0; innerIndex < subNodes.getLength(); innerIndex++) {
      Node curSubNode = subNodes.item(innerIndex);
      if (curSubNode.getNodeName().equals(child)) {
        return curSubNode.getTextContent();
      }
    }
    return null;
  }

  private static final double HOUR_IN_MS = 3600 * 1000;

  public static Date getSunrize(double latitude, double longitude) {
    return getSunrizeOrSet(latitude, longitude, true);
  }

  public static Date getSunset(double latitude, double longitude) {
    return getSunrizeOrSet(latitude, longitude, false);
  }

  private static Date getSunrizeOrSet(double latitude, double longitude,
      boolean rising) {
    double zenith = 90.0 + 50.0 / 60.0;
    // 1. first calculate the day of the year
    //       
    // N1 = floor(275 * month / 9)
    // N2 = floor((month + 9) / 12)
    // N3 = (1 + floor((year - 4 * floor(year / 4) + 2) / 3))
    // N = N1 - (N2 * N3) + day - 30
    int N = Calendar.getInstance().get(Calendar.DAY_OF_YEAR);

    // 2. convert the longitude to hour value and calculate an approximate
    // time
    //                                                                                                                                                                                                                                            
    // lngHour = longitude / 15
    double lngHour = longitude / 15.0;

    double t;
    // if rising time is desired:
    // t = N + ((6 - lngHour) / 24)
    if (rising)
      t = N + ((6.0 - lngHour) / 24.0);
    else
      t = N + ((18 - lngHour) / 24);

    // 3. calculate the Sun's mean anomaly
    // M = (0.9856 * t) - 3.289
    double M = (0.9856 * t) - 3.289;

    // 4. calculate the Sun's true longitude
    // L = M + (1.916 * sin(M)) + (0.020 * sin(2 * M)) + 282.634
    double L = M + (1.916 * sin(M)) + (0.020 * sin(2 * M)) + 282.634;

    // NOTE: L potentially needs to be adjusted into the range [0,360) by
    // adding/subtracting 360
    L = makeInRange(L, 360.0);

    // 5a. calculate the Sun's right ascension
    // RA = atan(0.91764 * tan(L))
    double RA = atan(0.91764 * tan(L));

    // NOTE: RA potentially needs to be adjusted into the range [0,360) by
    // adding/subtracting 360
    RA = makeInRange(RA, 360.0);

    // 5b. right ascension value needs to be in the same quadrant as L
    // Lquadrant = (floor( L/90)) * 90
    // RAquadrant = (floor(RA/90)) * 90
    // RA = RA + (Lquadrant - RAquadrant)
    double Lquadrant = (Math.floor(L / 90.0)) * 90.0;
    double RAquadrant = (Math.floor(RA / 90.0)) * 90.0;
    RA = RA + (Lquadrant - RAquadrant);

    // 5c. right ascension value needs to be converted into hours
    // RA = RA / 15
    RA = RA / 15.0;

    // 6. calculate the Sun's declination
    // sinDec = 0.39782 * sin(L)
    // cosDec = cos(asin(sinDec))
    double sinDec = 0.39782 * sin(L);
    double cosDec = cos(asin(sinDec));

    // 7a. calculate the Sun's local hour angle
    // cosH = (cos(zenith) - (sinDec * sin(latitude))) / (cosDec *
    // cos(latitude))
    double cosH = (cos(zenith) - (sinDec * sin(latitude)))
        / (cosDec * cos(latitude));

    // 7b. finish calculating H and convert into hours
    // if if rising time is desired:
    // H = 360 - acos(cosH)
    double H;
    if (rising)
      H = 360.0 - acos(cosH);
    else
      H = acos(cosH);

    // H = H / 15
    H = H / 15.0;

    // 8. calculate local mean time of rising/setting
    //
    // T = H + RA - (0.06571 * t) - 6.622
    double T = H + RA - (0.06571 * t) - 6.622;

    //
    // 9. adjust back to UTC
    //
    // UT = T - lngHour
    double UT = T - lngHour;

    // NOTE: UT potentially needs to be adjusted into the range [0,24) by
    // adding/subtracting 24
    // 10. convert UT value to local time zone of latitude/longitude
    // localT = UT + localOffset
    long timezone = TimeZone.getDefault().getOffset(
        System.currentTimeMillis());
    UT = makeInRange(UT + timezone / HOUR_IN_MS, 24.0);

    Calendar today = Calendar.getInstance();
    today.set(Calendar.HOUR_OF_DAY, 0);
    today.set(Calendar.MINUTE, 0);
    today.set(Calendar.SECOND, 0);
    today.set(Calendar.MILLISECOND, 0);
    today.setTimeInMillis(today.getTimeInMillis()
        + (long) (UT * HOUR_IN_MS));
    return today.getTime();
  }

  private static double acos(double a) {
    return Math.toDegrees(Math.acos(a));
  }

  private static double asin(double a) {
    return Math.toDegrees(Math.asin(a));
  }

  private static double atan(double a) {
    return Math.toDegrees(Math.atan(a));
  }

  private static double cos(double a) {
    return Math.cos(Math.toRadians(a));
  }

  private static double sin(double a) {
    return Math.sin(Math.toRadians(a));
  }

  private static double tan(double a) {
    return Math.tan(Math.toRadians(a));
  }

  private static double makeInRange(double val, double max) {
    if (val >= max)
      return val - max;
    else if (val < 0)
      return val + max;
    return val;
  }

  public WeatherReport getWeather(Key key) {
    return reports.get(key);
  }
}
TOP

Related Classes of nl.topicus.onderwijs.dashboard.modules.wettercom.WetterComService

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.