Merge branch 'master' into bug4380

Change-Id: I37ab1f953eb87b2abdc7f18f73a1b3aa030f0bb0
This commit is contained in:
Axel Uhl
2018-01-23 21:20:31 +01:00
25 changed files with 523 additions and 112 deletions
@@ -2,6 +2,7 @@
<classpath>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="resources"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="output" path="bin"/>
</classpath>
@@ -1,4 +1,5 @@
source.. = src/
source.. = src/,\
resources/
output.. = bin/
bin.includes = META-INF/,\
.
@@ -0,0 +1,87 @@
<commandResult>
<MAGGRIDRESULT>
<VERSION>
0.5.0.7
</VERSION>
<RESULT>
<DATE>
2018.06027
</DATE>
<LATITUDE units = "Degree">
10.00000
</LATITUDE>
<LONGITUDE units="Degree">
20.00000
</LONGITUDE>
<ELEVATION units="km">
0.00000
</ELEVATION>
<DECLINATION units="Degree">
1.89255
</DECLINATION>
<INCLINATION units="Degree">
-1.78053
</INCLINATION>
<HORINTENSITY units="nanoTesla (nT)">
34687.2
</HORINTENSITY>
<TOTALINTENSITY units="nanoTesla (nT)">
34704.0
</TOTALINTENSITY>
<XCOMPONENT units="nanoTesla (nT)">
34668.3
</XCOMPONENT>
<YCOMPONENT units="nanoTesla (nT)">
1145.5
</YCOMPONENT>
<ZCOMPONENT units="nanoTesla (nT)">
-1078.3
</ZCOMPONENT>
<DECLINATION_SV units="Degree">
0.10649
</DECLINATION_SV>
<INCLINATION_SV units="Degree">
-0.01038
</INCLINATION_SV>
<HORINTENSITY_SV units="nanoTesla (nT)">
11.3
</HORINTENSITY_SV>
<TOTALINTENSITY_SV units="nanoTesla (nT)">
11.5
</TOTALINTENSITY_SV>
<XCOMPONENT_SV units="nanoTesla (nT)">
9.2
</XCOMPONENT_SV>
<YCOMPONENT_SV units="nanoTesla (nT)">
64.8
</YCOMPONENT_SV>
<ZCOMPONENT_SV units="nanoTesla (nT)">
-6.6
</ZCOMPONENT_SV>
<DECLINATION_UNCERTAINTY units="Degree">
0.29
</DECLINATION_UNCERTAINTY>
<INCLINATION_UNCERTAINTY units="Degree">
0.22
</INCLINATION_UNCERTAINTY>
<HORINTENSITY_UNCERTAINTY units="nanoTesla (nT)">
133
</HORINTENSITY_UNCERTAINTY>
<TOTALINTENSITY_UNCERTAINTY units="nanoTesla (nT)">
152
</TOTALINTENSITY_UNCERTAINTY>
<YCOMPONENT_UNCERTAINTY units="nanoTesla (nT)">
89
</YCOMPONENT_UNCERTAINTY>
<XCOMPONENT_UNCERTAINTY units="nanoTesla (nT)">
138
</XCOMPONENT_UNCERTAINTY>
<ZCOMPONENT_UNCERTAINTY units="nanoTesla (nT)">
165
</ZCOMPONENT_UNCERTAINTY>
</RESULT>
</MAGGRIDRESULT>
<version_number>
1.1
</version_number>
</commandResult>
@@ -2,17 +2,9 @@ package com.sap.sailing.declination.test;
import java.text.SimpleDateFormat;
import org.junit.Before;
import com.sap.sailing.declination.impl.DeclinationImporter;
import com.sap.sailing.declination.impl.NOAAImporterForTesting;
public abstract class AbstractDeclinationTest {
protected NOAAImporterForTesting importer;
public abstract class AbstractDeclinationTest<I extends DeclinationImporter> {
protected I importer;
protected final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
@Before
public void setUp() {
importer = new NOAAImporterForTesting();
}
}
@@ -0,0 +1,12 @@
package com.sap.sailing.declination.test;
import org.junit.Before;
import com.sap.sailing.declination.impl.ColoradoImporter;
public class ColoradoDeclinationImportTest extends DeclinationImportTest<ColoradoImporter> {
@Before
public void setUp() {
importer = new ColoradoImporter();
}
}
@@ -0,0 +1,14 @@
package com.sap.sailing.declination.test;
import org.junit.Before;
import com.sap.sailing.declination.impl.ColoradoImporter;
public class ColoradoDeclinationServiceTest extends DeclinationServiceTest<ColoradoImporter> {
@Override
@Before
public void setUp() {
importer = new ColoradoImporter();
super.setUp();
}
}
@@ -0,0 +1,14 @@
package com.sap.sailing.declination.test;
import org.junit.Before;
import com.sap.sailing.declination.impl.ColoradoImporter;
public class ColoradoDeclinationStoreTest extends DeclinationStoreTest<ColoradoImporter> {
@Override
@Before
public void setUp() {
importer = new ColoradoImporter();
super.setUp();
}
}
@@ -0,0 +1,12 @@
package com.sap.sailing.declination.test;
import org.junit.Before;
import com.sap.sailing.declination.impl.ColoradoImporter;
public class ColoradoSimpleDeclinationTest extends SimpleDeclinationTest<ColoradoImporter> {
@Before
public void setUp() {
importer = new ColoradoImporter();
}
}
@@ -0,0 +1,43 @@
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;
import org.junit.Test;
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,11 +2,9 @@ package com.sap.sailing.declination.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.text.ParseException;
import java.util.regex.Matcher;
import javax.xml.parsers.ParserConfigurationException;
@@ -14,34 +12,18 @@ import org.junit.Test;
import org.xml.sax.SAXException;
import com.sap.sailing.declination.Declination;
import com.sap.sailing.declination.impl.DeclinationImporter;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class DeclinationImportTest extends AbstractDeclinationTest {
@Test
public void testRegexpDeclination() {
Matcher declinationMatcher = importer.getDeclinationPattern().matcher(" <p class=\"indent\"><b>Declination</b> = 12&deg; 41' W");
assertTrue(declinationMatcher.find());
assertEquals("12", declinationMatcher.group(1));
assertEquals("41", declinationMatcher.group(2));
assertEquals("W", declinationMatcher.group(3));
}
@Test
public void testRegexpAnnualChange() {
Matcher annualChangeMatcher = importer.getAnnualChangePattern().matcher(" changing by 0&deg; 11' E/year </p>");
assertTrue(annualChangeMatcher.find());
assertEquals("0", annualChangeMatcher.group(1));
assertEquals("11", annualChangeMatcher.group(2));
assertEquals("E", annualChangeMatcher.group(3));
}
public abstract class DeclinationImportTest<I extends DeclinationImporter> extends AbstractDeclinationTest<I> {
@Test
public void importSimpleDeclination() throws IOException, ParseException, ParserConfigurationException, SAXException {
Declination record = importer.importRecord(new DegreePosition(53, 3),
new MillisecondsTimePoint(simpleDateFormat.parse("2016-05-27").getTime()));
assertEquals(0.26307, record.getBearing().getDegrees(), 0.0001);
assertEquals(0.14795, record.getAnnualChange().getDegrees(), 0.0001);
assertEquals(0.26307, record.getBearing().getDegrees(), 0.001);
assertEquals(0.14795, record.getAnnualChange().getDegrees(), 0.001);
}
@Test
@@ -50,8 +32,8 @@ public class DeclinationImportTest extends AbstractDeclinationTest {
Declination record = importer.importRecord(new DegreePosition(-10, 3),
new MillisecondsTimePoint(simpleDateFormat.parse("2017-05-27").getTime()));
System.out.println("took "+(System.currentTimeMillis()-start)+"ms");
assertEquals(-8.44581, record.getBearing().getDegrees(), 0.0001);
assertEquals(0.17712, record.getAnnualChange().getDegrees(), 0.0001);
assertEquals(-8.44581, record.getBearing().getDegrees(), 0.001);
assertEquals(0.17712, record.getAnnualChange().getDegrees(), 0.001);
}
@Test
@@ -12,17 +12,18 @@ import org.junit.Test;
import com.sap.sailing.declination.Declination;
import com.sap.sailing.declination.DeclinationService;
import com.sap.sailing.declination.impl.DeclinationImporter;
import com.sap.sailing.declination.impl.DeclinationServiceImpl;
import com.sap.sailing.domain.common.impl.CentralAngleDistance;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class DeclinationServiceTest extends AbstractDeclinationTest {
public abstract class DeclinationServiceTest<I extends DeclinationImporter> extends AbstractDeclinationTest<I> {
private DeclinationService service;
@Before
public void setUp() {
service = new DeclinationServiceImpl(new CentralAngleDistance(1./180.*Math.PI));
service = new DeclinationServiceImpl(new CentralAngleDistance(1./180.*Math.PI), importer);
}
@Test
@@ -36,10 +37,10 @@ public class DeclinationServiceTest extends AbstractDeclinationTest {
@Test
public void testDeclinationQueryNotMatchedInStore() throws IOException, ClassNotFoundException, ParseException {
Declination result = service.getDeclination(new MillisecondsTimePoint(simpleDateFormat.parse("2019-02-03").getTime()),
new DegreePosition(51, -5), /* timeoutForOnlineFetchInMilliseconds */ 30000);
new DegreePosition(51, -5), /* timeoutForOnlineFetchInMilliseconds */ 5000);
assertNotNull(result);
assertEquals(-1.9565, result.getBearing().getDegrees(), 0.0001);
assertEquals(0.15244, result.getAnnualChange().getDegrees(), 0.0001);
assertEquals(-1.9565, result.getBearing().getDegrees(), 0.001);
assertEquals(0.15244, result.getAnnualChange().getDegrees(), 0.001);
}
@Test
@@ -28,6 +28,7 @@ import org.junit.Ignore;
import org.junit.Test;
import com.sap.sailing.declination.Declination;
import com.sap.sailing.declination.impl.DeclinationImporter;
import com.sap.sailing.declination.impl.DeclinationRecordImpl;
import com.sap.sailing.declination.impl.DeclinationStore;
import com.sap.sailing.domain.common.Bearing;
@@ -38,13 +39,13 @@ import com.sap.sailing.domain.common.quadtree.QuadTree;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class DeclinationStoreTest extends AbstractDeclinationTest {
public abstract class DeclinationStoreTest<I extends DeclinationImporter> extends AbstractDeclinationTest<I> {
private DeclinationStore store;
private static final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd");
@Before
public void setUp() {
store = new DeclinationStore();
store = new DeclinationStore(importer);
}
@Test
@@ -325,7 +326,7 @@ public class DeclinationStoreTest extends AbstractDeclinationTest {
@Test
public void copyExistingDeclinationsToNewFormat() throws IOException, ClassNotFoundException, ParseException {
File resourcesDir = new File("../com.sap.sailing.declination/resources/");
DeclinationStore store = new DeclinationStore();
DeclinationStore store = new DeclinationStore(importer);
assertTrue(resourcesDir.isDirectory());
for (File f : resourcesDir.listFiles()) {
if (f.getName().startsWith("declination-") && f.getName().endsWith(".txt") &&
@@ -0,0 +1,14 @@
package com.sap.sailing.declination.test;
import org.junit.Before;
import org.junit.Ignore;
import com.sap.sailing.declination.impl.NOAAImporterForTesting;
@Ignore("US Government Shutdown around 2018-01-22")
public class NOAADeclinationImportTest extends DeclinationImportTest<NOAAImporterForTesting> {
@Before
public void setUp() {
importer = new NOAAImporterForTesting();
}
}
@@ -0,0 +1,16 @@
package com.sap.sailing.declination.test;
import org.junit.Before;
import org.junit.Ignore;
import com.sap.sailing.declination.impl.NOAAImporter;
@Ignore("US Government Shutdown around 2018-01-22")
public class NOAADeclinationServiceTest extends DeclinationServiceTest<NOAAImporter> {
@Override
@Before
public void setUp() {
importer = new NOAAImporter();
super.setUp();
}
}
@@ -0,0 +1,16 @@
package com.sap.sailing.declination.test;
import org.junit.Before;
import org.junit.Ignore;
import com.sap.sailing.declination.impl.NOAAImporter;
@Ignore("US Government Shutdown around 2018-01-22")
public class NOAADeclinationStoreTest extends DeclinationStoreTest<NOAAImporter> {
@Override
@Before
public void setUp() {
importer = new NOAAImporter();
super.setUp();
}
}
@@ -0,0 +1,36 @@
package com.sap.sailing.declination.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.regex.Matcher;
import org.junit.Before;
import org.junit.Test;
import com.sap.sailing.declination.impl.NOAAImporterForTesting;
public class NOAARegexpTest extends AbstractDeclinationTest<NOAAImporterForTesting> {
@Before
public void setUp() {
importer = new NOAAImporterForTesting();
}
@Test
public void testRegexpDeclination() {
Matcher declinationMatcher = importer.getDeclinationPattern().matcher(" <p class=\"indent\"><b>Declination</b> = 12&deg; 41' W");
assertTrue(declinationMatcher.find());
assertEquals("12", declinationMatcher.group(1));
assertEquals("41", declinationMatcher.group(2));
assertEquals("W", declinationMatcher.group(3));
}
@Test
public void testRegexpAnnualChange() {
Matcher annualChangeMatcher = importer.getAnnualChangePattern().matcher(" changing by 0&deg; 11' E/year </p>");
assertTrue(annualChangeMatcher.find());
assertEquals("0", annualChangeMatcher.group(1));
assertEquals("11", annualChangeMatcher.group(2));
assertEquals("E", annualChangeMatcher.group(3));
}
}
@@ -0,0 +1,14 @@
package com.sap.sailing.declination.test;
import org.junit.Before;
import org.junit.Ignore;
import com.sap.sailing.declination.impl.NOAAImporter;
@Ignore("US Government Shutdown around 2018-01-22")
public class NOAASimpleDeclinationTest extends SimpleDeclinationTest<NOAAImporter> {
@Before
public void setUp() {
importer = new NOAAImporter();
}
}
@@ -11,33 +11,33 @@ import org.junit.Test;
import org.xml.sax.SAXException;
import com.sap.sailing.declination.Declination;
import com.sap.sailing.declination.impl.DeclinationImporter;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class SimpleDeclinationTest extends AbstractDeclinationTest {
public abstract class SimpleDeclinationTest<I extends DeclinationImporter> extends AbstractDeclinationTest<I> {
@Test
public void testSimpleCorrection() throws IOException, ParseException, ParserConfigurationException, SAXException {
final MillisecondsTimePoint timePoint = new MillisecondsTimePoint(simpleDateFormat.parse("2019-05-27").getTime());
Declination record = importer.importRecord(new DegreePosition(53, 3), timePoint);
assertEquals(0.70594, record.getBearing().getDegrees(), 0.0001);
assertEquals(0.14745, record.getAnnualChange().getDegrees(), 0.0001);
assertEquals(0.70594, record.getBearing().getDegrees(), 0.001);
assertEquals(0.14745, record.getAnnualChange().getDegrees(), 0.001);
TimePoint oneYearLater = timePoint.plus(Duration.ONE_YEAR);
Bearing bearing = record.getBearingCorrectedTo(oneYearLater);
assertEquals(0.70594+0.14745, bearing.getDegrees(), 0.0001);
assertEquals(0.70594+0.14745, bearing.getDegrees(), 0.01);
}
@Test
public void testBackwardCorrection() throws IOException, ParseException, ParserConfigurationException, SAXException {
final MillisecondsTimePoint timePoint = new MillisecondsTimePoint(simpleDateFormat.parse("2019-05-27").getTime());
Declination record = importer.importRecord(new DegreePosition(53, 3), timePoint);
assertEquals(0.70594, record.getBearing().getDegrees(), 0.0001);
assertEquals(0.14745, record.getAnnualChange().getDegrees(), 0.0001);
assertEquals(0.70594, record.getBearing().getDegrees(), 0.001);
assertEquals(0.14745, record.getAnnualChange().getDegrees(), 0.001);
TimePoint oneYearEarlier = timePoint.minus(Duration.ONE_YEAR);
Bearing bearing = record.getBearingCorrectedTo(oneYearEarlier);
assertEquals(0.70594-0.14745, bearing.getDegrees(), 0.0001);
assertEquals(0.70594-0.14745, bearing.getDegrees(), 0.01);
}
}
@@ -3,6 +3,7 @@ package com.sap.sailing.declination;
import java.io.IOException;
import java.text.ParseException;
import com.sap.sailing.declination.impl.ColoradoImporter;
import com.sap.sailing.declination.impl.DeclinationServiceImpl;
import com.sap.sailing.declination.impl.NOAAImporter;
import com.sap.sailing.domain.common.Distance;
@@ -16,7 +17,7 @@ public interface DeclinationService {
* A default implementation with a spatial default precision of 1 {@link Mile#METERS_PER_GEOGRAPHICAL_MILE
* geographical mile} which equals the length of an arc with one degree on a meridian.
*/
DeclinationService INSTANCE = new DeclinationServiceImpl(new CentralAngleDistance(1./180.*Math.PI));
DeclinationService INSTANCE = new DeclinationServiceImpl(new CentralAngleDistance(1./180.*Math.PI), new ColoradoImporter());
/**
* Obtains declination information with the default precision of this declination service in time and space
@@ -0,0 +1,124 @@
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;
import org.xml.sax.Attributes;
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;
/**
* A typical request for the Geomag service looks like this:
* <pre>
* http://magcalc.geomag.info/?model=WMM2015&sourcePage=oldCalc&decimalLatitude=10&decimalLongitude=20&minYear=2018&minMonth=1&minDay=23
* </pre>
* The response serves an XML document that has elements like this:
* <pre>
* &lt;DATE&gt;
* 2018.06027
* &lt;/DATE&gt;
* &lt;LATITUDE units = "Degree"&gt;
* 10.00000
* &lt;/LATITUDE&gt;
* &lt;LONGITUDE units="Degree"&gt;
* 20.00000
* &lt;/LONGITUDE&gt;
* &lt;DECLINATION units="Degree"&gt;
* 1.89255
* &lt;/DECLINATION&gt;
* &lt;DECLINATION_SV units="Degree"&gt;
* 0.10649
* &lt;/DECLINATION_SV&gt;
* </pre>
* representing the declination in degrees, as well as its annual change.
*
* @author Axel Uhl (D043530)
*
*/
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;
private double lngDeg;
private double declinationDeg;
private double annualDeclinationChangeDeg;
private StringBuilder elementContent;
public XmlElementHandler() {
elementContent = new StringBuilder();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
elementContent.delete(0, elementContent.length());
super.startElement(uri, localName, qName, attributes);
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
elementContent.append(ch, start, length);
super.characters(ch, start, length);
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
final String content = elementContent.toString();
switch (qName) {
case "DATE":
dateAsDecimalYear = Double.valueOf(content);
break;
case "LATITUDE":
latDeg = Double.valueOf(content);
break;
case "LONGITUDE":
lngDeg = Double.valueOf(content);
break;
case "DECLINATION":
declinationDeg = Double.valueOf(content);
break;
case "DECLINATION_SV":
annualDeclinationChangeDeg = Double.valueOf(content);
break;
}
super.endElement(uri, localName, qName);
}
Declination getDeclination() {
return new DeclinationRecordImpl(new DegreePosition(latDeg, lngDeg), new MillisecondsTimePoint((long) ((dateAsDecimalYear-1970.)*365.0*24.0*3600.0*1000.0)),
new DegreeBearingImpl(declinationDeg), new DegreeBearingImpl(annualDeclinationChangeDeg));
}
}
public Declination getDeclinationFromXml(InputStream is) throws SAXException, IOException, ParserConfigurationException {
XmlElementHandler handler = new XmlElementHandler();
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];
}
}
@@ -17,7 +17,7 @@ import com.sap.sse.common.TimePoint;
public class DeclinationServiceImpl implements DeclinationService {
private final Distance defaultMaxDistance;
private final DeclinationStore persistentStore;
private final NOAAImporter noaaImporter;
private final DeclinationImporter declinationImporter;
/**
* Keys are years
@@ -33,10 +33,14 @@ public class DeclinationServiceImpl implements DeclinationService {
* Constructs a service that has a default position tolerance of <code>defaultMaxDistance</code>.
*/
public DeclinationServiceImpl(Distance defaultMaxDistance) {
this(defaultMaxDistance, new NOAAImporter());
}
public DeclinationServiceImpl(Distance defaultMaxDistance, DeclinationImporter declinationImporter) {
this.declinationImporter = declinationImporter;
this.defaultMaxDistance = defaultMaxDistance;
this.yearStore = new HashMap<Integer, QuadTree<Declination>>();
this.persistentStore = new DeclinationStore();
this.noaaImporter = new NOAAImporter();
this.persistentStore = new DeclinationStore(declinationImporter);
this.importerCache = new HashMap<Integer, QuadTree<Declination>>();
}
@@ -84,7 +88,7 @@ public class DeclinationServiceImpl implements DeclinationService {
// else it's further away from the requested position as demanded by maxDistance
}
}
result = noaaImporter.getDeclination(position, timePoint, timeoutForOnlineFetchInMilliseconds);
result = declinationImporter.getDeclination(position, timePoint, timeoutForOnlineFetchInMilliseconds);
if (result != null) {
if (importerCacheForYear == null) {
importerCacheForYear = new QuadTree<Declination>();
@@ -38,6 +38,12 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
public class DeclinationStore {
private static final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd");
private final DeclinationImporter importer;
public DeclinationStore(DeclinationImporter importer) {
this.importer = importer;
}
/**
* Returns <code>null</code> if no stored declinations exist for the <code>year</code> requested.
*/
@@ -122,7 +128,7 @@ public class DeclinationStore {
return result;
}
private void fetchAndAppendDeclination(TimePoint timePoint, Position position, NOAAImporter importer,
private void fetchAndAppendDeclination(TimePoint timePoint, Position position, DeclinationImporter importer,
Writer out) throws IOException {
Declination declination = null;
// re-try three times
@@ -153,7 +159,6 @@ public class DeclinationStore {
usage();
} else {
double grid = Double.valueOf(args[2]);
NOAAImporter importer = new NOAAImporter();
for (int year = fromYear; year <= toYear; year++) {
QuadTree<Declination> storedDeclinations = getStoredDeclinations(year);
if (storedDeclinations == null) {
@@ -182,7 +187,7 @@ public class DeclinationStore {
}
}
private void fetchAndAppendDeclinationForLatitude(double grid, NOAAImporter importer, int year,
private void fetchAndAppendDeclinationForLatitude(double grid, DeclinationImporter importer, int year,
QuadTree<Declination> storedDeclinations, Writer out, int month, TimePoint timePoint, double lat)
throws IOException {
System.out.println("Date: " + year + "/" + (month + 1) + ", Latitude: " + lat);
@@ -194,7 +199,7 @@ public class DeclinationStore {
}
}
private void fetchAndAppendDeclination(NOAAImporter importer, QuadTree<Declination> storedDeclinations, Writer out,
private void fetchAndAppendDeclination(DeclinationImporter importer, QuadTree<Declination> storedDeclinations, Writer out,
TimePoint timePoint, double lat, double lng) throws IOException {
Position point = new DegreePosition(lat, lng);
final Declination existingDeclinationRecord;
@@ -213,16 +218,19 @@ public class DeclinationStore {
* Launches the importer, writing to resources/declination-year.txt (where "year" represents the year for which the
* values are stored in the file) the declinations downloaded online for the years <code>args[0]</code> to
* <code>args[1]</code> (inclusive) for all positions with a grid of <code>args[2]</code> degrees each, starting at
* 0&deg;0.0'N and 0&deg;0.0'E.
* 0&deg;0.0'N and 0&deg;0.0'E. <code>args[3]</code> can optionally be used to select a non-default declination
* importer. By default, the {@link NOAAImporter} will be used. Using "c" here will use the {@link ColoradoImporter}
* instead.
*
* @throws ParseException
* @throws ClassNotFoundException
*/
public static void main(String[] args) throws IOException, ClassNotFoundException, ParseException {
DeclinationStore store = new DeclinationStore();
DeclinationStore store = new DeclinationStore(args.length > 3 && args[3].equals("c") ? new ColoradoImporter() : new NOAAImporter());
store.run(args);
}
private void usage() {
System.out.println("java " + NOAAImporter.class.getName() + " <fromYear> <toYear> <gridSizeInDegrees>");
System.out.println("java " + getClass().getName() + " <fromYear> <toYear> <gridSizeInDegrees>");
}
}
@@ -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];
}
}
@@ -2030,7 +2030,7 @@ totalDistanceFoiledInMetersTooltip=Total distance foiled, across the entire lead
threeSixtyVideoHint=This is a 360° video, the camera can be turned
processingMP4=Mp4 is analyzed (this will take a few Seconds)
resetStartTimeToDefault=Set to default
importFullExpeditionData=Import full expedition data
importFullExpeditionData=Import full Expedition data
importCanceledNoCompetitorAdded=No competitor was added! Import will be canceled.
importCanceledByUser=Import canceled!
importFinished=Finished importing data