introduced abstract DeclinationImporter class of which now NOAAImporter and ColoradoImporter are two different implementations

Change-Id: Icc993ccb6b7111a5e30046f09c595d5445ccbf7c
This commit is contained in:
Axel Uhl committed 2018-01-23 18:14:30 +01:00
1 parent ddd42f6b5e
commit 3e7e96f4d0
4 files changed
+102 -48

No files matched your search

@@ -3,6 +3,9 @@ package com.sap.sailing.declination.test;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.text.ParseException;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import javax.xml.parsers.ParserConfigurationException;
@@ -11,14 +14,30 @@ import org.xml.sax.SAXException;
import com.sap.sailing.declination.Declination;
import com.sap.sailing.declination.impl.ColoradoImporter;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class ColoradoTest {
@Test
public void simpleDocumentParsingTest() throws SAXException, IOException, ParserConfigurationException {
final Declination declination = new ColoradoImporter().getDeclinationFromXml(getClass().getResourceAsStream("/colorado.xml"));
validateSomeDeclinationInJanuary2018(declination);
}
private void validateSomeDeclinationInJanuary2018(final Declination declination) {
assertEquals(10.0, declination.getPosition().getLatDeg(), 0.000001);
assertEquals(20.0, declination.getPosition().getLngDeg(), 0.000001);
assertEquals(1.89255, declination.getBearing().getDegrees(), 0.000001);
assertEquals(0.10649, declination.getAnnualChange().getDegrees(), 0.000001);
}
@Test
public void simpleOnlineTest() throws IOException, ParseException {
final GregorianCalendar cal = new GregorianCalendar(2018, 0, 23);
cal.setTimeZone(TimeZone.getTimeZone("UTC"));
final Declination declination = new ColoradoImporter().getDeclination(new DegreePosition(10, 20),
new MillisecondsTimePoint(cal.getTime()),
/* timeoutForOnlineFetchInMilliseconds */ 5000);
validateSomeDeclinationInJanuary2018(declination);
}
}
@@ -2,6 +2,10 @@ package com.sap.sailing.declination.impl;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
@@ -11,8 +15,10 @@ import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import com.sap.sailing.declination.Declination;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
/**
@@ -43,7 +49,9 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
* @author Axel Uhl (D043530)
*
*/
public class ColoradoImporter {
public class ColoradoImporter extends DeclinationImporter {
private static final String URL_PATTERN = "http://magcalc.geomag.info/?model=WMM2015&sourcePage=oldCalc&decimalLatitude=%f&decimalLongitude=%f&minYear=%d&minMonth=%d&minDay=%d";
private static class XmlElementHandler extends DefaultHandler {
private double dateAsDecimalYear;
private double latDeg;
@@ -104,4 +112,13 @@ public class ColoradoImporter {
SAXParserFactory.newInstance().newSAXParser().parse(is, handler);
return handler.getDeclination();
}
@Override
public Declination importRecord(Position position, TimePoint timePoint)
throws IOException, ParserConfigurationException, SAXException {
final Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
cal.setTime(timePoint.asDate());
final URL url = new URL(String.format(URL_PATTERN, position.getLatDeg(), position.getLngDeg(), cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH)));
return getDeclinationFromXml(url.openStream());
}
}
@@ -0,0 +1,63 @@
package com.sap.sailing.declination.impl;
import java.io.IOException;
import java.text.ParseException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import com.sap.sailing.declination.Declination;
import com.sap.sailing.domain.common.Position;
import com.sap.sse.common.TimePoint;
public abstract class DeclinationImporter {
private static final Logger logger = Logger.getLogger(DeclinationImporter.class.getName());
public abstract Declination importRecord(Position position, TimePoint timePoint)
throws IOException, ParserConfigurationException, SAXException;
/**
* Tries two things in parallel: fetch a more or less precise response from the online service and load
* the requested year's declination values from a stored resource to look up a value that comes close.
* The online lookup will be given preference. However, should it take longer than
* <code>timeoutForOnlineFetchInMilliseconds</code>, then the method will return whatever it found
* in the stored file, or <code>null</code> if no file exists for the year of <code>timePoint</code>.
*
* @param timeoutForOnlineFetchInMilliseconds if 0, this means wait forever for the online result
* @throws ParseException
* @throws ClassNotFoundException
* @throws IOException
*/
public Declination getDeclination(final Position position, final TimePoint timePoint,
long timeoutForOnlineFetchInMilliseconds) throws IOException, ParseException {
final Declination[] result = new Declination[1];
Thread fetcher = new Thread("Declination fetcher for "+position+"@"+timePoint) {
@Override
public void run() {
try {
Declination fetched = importRecord(position, timePoint);
synchronized (result) {
result[0] = fetched;
result.notifyAll();
}
} catch (IOException | ParserConfigurationException | SAXException e) {
logger.log(Level.FINE, "Exception while trying to load magnetic declination online", e);
}
}
};
fetcher.start();
synchronized (result) {
if (result[0] == null) {
try {
result.wait(timeoutForOnlineFetchInMilliseconds);
} catch (InterruptedException e) {
// ignore; simply use value from file in this case
}
}
}
return result[0];
}
}
@@ -4,12 +4,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
@@ -31,8 +28,7 @@ import com.sap.sse.common.TimePoint;
* @author Axel Uhl (d043530)
*
*/
public class NOAAImporter {
private static final Logger logger = Logger.getLogger(NOAAImporter.class.getName());
public class NOAAImporter extends DeclinationImporter {
private static final String QUERY_URL = "https://www.ngdc.noaa.gov/geomag-web/calculators/calculateDeclination";
private static final String REGEXP_DECLINATION = "<p class=\"indent\"><b>Declination</b> = ([0-9]*)&deg; ([0-9]*)' *([EW])";
private static final String REGEXP_ANNUAL_CHANGE = "changing by *([0-9]*)&deg; *([0-9]*)' ([EW])/year *</p>";
@@ -54,6 +50,7 @@ public class NOAAImporter {
return annualChangePattern;
}
@Override
public Declination importRecord(Position position, TimePoint timePoint) throws IOException, ParserConfigurationException, SAXException {
Declination result = null;
Date date = timePoint.asDate();
@@ -80,46 +77,4 @@ public class NOAAImporter {
return result;
}
/**
* Tries two things in parallel: fetch a more or less precise response from the online service and load
* the requested year's declination values from a stored resource to look up a value that comes close.
* The online lookup will be given preference. However, should it take longer than
* <code>timeoutForOnlineFetchInMilliseconds</code>, then the method will return whatever it found
* in the stored file, or <code>null</code> if no file exists for the year of <code>timePoint</code>.
*
* @param timeoutForOnlineFetchInMilliseconds if 0, this means wait forever for the online result
* @throws ParseException
* @throws ClassNotFoundException
* @throws IOException
*/
public Declination getDeclination(final Position position, final TimePoint timePoint,
long timeoutForOnlineFetchInMilliseconds) throws IOException, ParseException {
final Declination[] result = new Declination[1];
Thread fetcher = new Thread("Declination fetcher for "+position+"@"+timePoint) {
@Override
public void run() {
try {
Declination fetched = importRecord(position, timePoint);
synchronized (result) {
result[0] = fetched;
result.notifyAll();
}
} catch (IOException | ParserConfigurationException | SAXException e) {
logger.log(Level.FINE, "Exception while trying to load magnetic declination online", e);
}
}
};
fetcher.start();
synchronized (result) {
if (result[0] == null) {
try {
result.wait(timeoutForOnlineFetchInMilliseconds);
} catch (InterruptedException e) {
// ignore; simply use value from file in this case
}
}
}
return result[0];
}
}