package thredds.server.services; import com.eclipsesource.restfuse.Destination; import com.eclipsesource.restfuse.HttpJUnitRunner; import com.eclipsesource.restfuse.Method; import com.eclipsesource.restfuse.Response; import com.eclipsesource.restfuse.annotation.Context; import com.eclipsesource.restfuse.annotation.HttpTest; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.JDOMException; import org.jdom2.Namespace; import org.jdom2.filter.Filters; import org.jdom2.input.SAXBuilder; import org.jdom2.output.Format; import org.jdom2.output.XMLOutputter; import org.jdom2.xpath.XPath; import org.jdom2.xpath.XPathExpression; import org.jdom2.xpath.XPathFactory; import org.junit.Before; import org.junit.Rule; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import thredds.TestWithLocalServer; import ucar.nc2.Attribute; import ucar.nc2.NetcdfFile; import ucar.nc2.constants.CDM; import ucar.nc2.constants.CF; import ucar.nc2.dataset.CoordinateAxis1D; import ucar.nc2.dataset.CoordinateAxis1DTime; import ucar.nc2.dataset.NetcdfDataset; import ucar.nc2.time.Calendar; import ucar.nc2.time.CalendarDate; import ucar.nc2.time.CalendarDateUnit; import ucar.unidata.util.test.category.NeedsCdmUnitTest; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import static com.eclipsesource.restfuse.Assert.assertOk; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** * Consistency of calendar dates across services: ncss, wms, wcs */ @RunWith(HttpJUnitRunner.class) @Category(NeedsCdmUnitTest.class) public class ConsistentDatesTest { private static final boolean show = true; @Rule public Destination destination = new Destination(TestWithLocalServer.server); @Context private Response response; // will be injected after every request private final String[] expectedDateTime = { "0000-01-16T06:00:00Z", // these are the actual dates from cdmUnitTest/ncss/climatology/PF5_SST_Climatology_Monthly_1985_2001.nc "0000-02-15T16:29:06Z", // this does not have a 360 calendar, so we need to find a dataset that does to test 360calendar "0000-03-17T02:58:12Z", "0000-04-16T13:27:18Z", "0000-05-16T23:56:24Z", "0000-06-16T10:25:30Z", "0000-07-16T20:54:36Z", "0000-08-16T07:23:42Z", "0000-09-15T17:52:48Z", "0000-10-16T04:21:54Z", "0000-11-15T14:51:00Z", "0000-12-16T01:20:06Z"}; private final List<String> expectedDatesAsDateTime = Collections.unmodifiableList(Arrays.asList(expectedDateTime)); //private final List<DateTime> expectedWMSDatesAsDateTime = Collections.unmodifiableList(Arrays.asList(expectedDateTime)); @Before public void setUp() { } @HttpTest(method = Method.GET, path = "/wms/cdmUnitTest/ncss/climatology/PF5_SST_Climatology_Monthly_1985_2001.nc?service=WMS&version=1.3.0&request=GetCapabilities") public void checkWMSDates() throws JDOMException, IOException { assertOk(response); assertTrue(response.hasBody()); String xml = response.getBody(String.class); Reader in = new StringReader(xml); SAXBuilder sb = new SAXBuilder(); Document doc = sb.build(in); if (show) { XMLOutputter fmt = new XMLOutputter(Format.getPrettyFormat()); fmt.output(doc, System.out); } XPath xPath = XPath.newInstance("//wms:Dimension"); xPath.addNamespace("wms", doc.getRootElement().getNamespaceURI()); Element dimNode = (Element) xPath.selectSingleNode(doc); //List<String> content = Arrays.asList(dimNode.getText().trim().split(",")); List<String> content = new ArrayList<>(); for (String d : Arrays.asList(dimNode.getText().trim().split(","))) { // System.out.printf("Date= %s%n", d); CalendarDate cd = CalendarDate.parseISOformat(null, d); content.add(cd.toString()); } assertEquals(expectedDatesAsDateTime, content); } @HttpTest(method = Method.GET, path = "/wcs/cdmUnitTest/ncss/climatology/PF5_SST_Climatology_Monthly_1985_2001.nc?service=WCS&version=1.0.0&request=DescribeCoverage&coverage=sst") public void checkWCSDates() throws JDOMException, IOException { assertOk(response); assertTrue(response.hasBody()); String xml = response.getBody(String.class); Reader in = new StringReader(xml); SAXBuilder sb = new SAXBuilder(); Document doc = sb.build(in); // old way - deprecated //XPath xPath = XPath.newInstance("//wcs:temporalDomain/gml:timePosition"); //xPath.addNamespace("wcs", doc.getRootElement().getNamespaceURI()); // List<Element> timePositionNodes = xPath.selectNodes(doc); Namespace wcs = Namespace.getNamespace("wcs", doc.getRootElement().getNamespaceURI()); Namespace gml = Namespace.getNamespace("gml", "http://www.opengis.net/gml"); XPathExpression<Element> xpath = XPathFactory.instance().compile("//wcs:temporalDomain/gml:timePosition", Filters.element(), null, wcs, gml); List<Element> timePositionNodes = xpath.evaluate(doc); List<String> timePositionDateTime = new ArrayList<>(); for (Element e : timePositionNodes) { System.out.printf("Date= %s%n", e.getText()); CalendarDate cd = CalendarDate.parseISOformat(null, e.getText()); timePositionDateTime.add(cd.toString()); } assertEquals(expectedDatesAsDateTime, timePositionDateTime); } @HttpTest(method = Method.GET, path = "/ncss/cdmUnitTest/ncss/climatology/PF5_SST_Climatology_Monthly_1985_2001.nc?var=sst&latitude=45&longitude=-20&temporal=all") public void checkNCSSDates() throws JDOMException, IOException { assertOk(response); assertTrue(response.hasBody()); String xml = response.getBody(String.class); Reader in = new StringReader(xml); SAXBuilder sb = new SAXBuilder(); Document doc = sb.build(in); // old way //XPath xPath = XPath.newInstance("/grid/point/data[@name='date']"); //List<Element> dataTimeNodes = xPath.selectNodes(doc); XPathExpression<Element> xpath = XPathFactory.instance().compile("/grid/point/data[@name='date']", Filters.element()); List<Element> dataTimeNodes = xpath.evaluate(doc); List<String> timePositionDateTime = new ArrayList<>(); for (Element e : dataTimeNodes) { System.out.printf("Date= %s%n", e.getText()); CalendarDate cd = CalendarDate.parseISOformat(null, e.getText()); timePositionDateTime.add(cd.toString());; } assertEquals(expectedDatesAsDateTime, timePositionDateTime); } // PF5_SST_Climatology: :units = "hour since 0000-01-01 00:00:00"; @HttpTest(method = Method.GET, path = "/ncss/cdmUnitTest/ncss/climatology/PF5_SST_Climatology_Monthly_1985_2001.nc?var=sst&latitude=45&longitude=-20&temporal=all&accept=netcdf") public void checkNCSSDatesInNetcdf() throws JDOMException, IOException { assertOk(response); assertTrue(response.hasBody()); NetcdfFile nf = NetcdfFile.openInMemory("test_data.ncs", response.getBody(byte[].class)); NetcdfDataset ds = new NetcdfDataset(nf); CoordinateAxis1D tAxis = (CoordinateAxis1D) ds.findCoordinateAxis("time"); Attribute calendarAtt = tAxis.findAttribute(CF.CALENDAR); Calendar calendar; if (calendarAtt == null) { calendar = Calendar.getDefault(); } else { calendar = Calendar.get(calendarAtt.getStringValue()); } Attribute units = tAxis.findAttribute(CDM.UNITS); double[] values = tAxis.getCoordValues(); System.out.printf("actual%n"); List<String> ccdd = new ArrayList<>(); CalendarDateUnit dateUnit = CalendarDateUnit.withCalendar(calendar, units.getStringValue()); for (double value : values) { CalendarDate cd = dateUnit.makeCalendarDate(value); System.out.printf(" \"%s\",%n", cd); ccdd.add(cd.toString()); } assertEquals(expectedDatesAsDateTime, ccdd); //FAIL!!! ??? //CoordinateAxis1DTime tAxis2 = CoordinateAxis1DTime.factory(ds, ds.findCoordinateAxis("time") , null); //assertTrue(tAxis2.getCalendarDates().equals(expectedDatesAsDateTime)); } /* pr_HRM3_2038-2070.CO.nc: double time(time=95040); :units = "days since 2038-01-01 00:00:00"; :standard_name = "time"; :long_name = "time"; :calendar = "360_day"; :bounds = "time_bnds"; */ @HttpTest(method = Method.GET, path = "/ncss/scanCdmUnitTests/ncss/test/pr_HRM3_2038-2070.CO.nc?var=pr&latitude=40.019&longitude=-105.293&time_start=2038-01-01T03%3A00%3A00Z&time_end=2038-01-02T03%3A00%3A00Z&accept=netcdf") public void checkNCSSDatesInNetcdfWithCalendars() throws JDOMException, IOException { //Dates for noleap calendar CalendarDate[] expectedCalendarDates = { CalendarDate.parseISOformat(Calendar.uniform30day.toString(), "2038-01-01T03:00:00Z"), CalendarDate.parseISOformat(Calendar.uniform30day.toString(), "2038-01-01T06:00:00Z"), CalendarDate.parseISOformat(Calendar.uniform30day.toString(), "2038-01-01T09:00:00Z"), CalendarDate.parseISOformat(Calendar.uniform30day.toString(), "2038-01-01T12:00:00Z"), CalendarDate.parseISOformat(Calendar.uniform30day.toString(), "2038-01-01T15:00:00Z"), CalendarDate.parseISOformat(Calendar.uniform30day.toString(), "2038-01-01T18:00:00Z"), CalendarDate.parseISOformat(Calendar.uniform30day.toString(), "2038-01-01T21:00:00Z"), CalendarDate.parseISOformat(Calendar.uniform30day.toString(), "2038-01-02T00:00:00Z"), CalendarDate.parseISOformat(Calendar.uniform30day.toString(), "2038-01-02T03:00:00Z") }; List<CalendarDate> expectedDatesAsCalendarDate = Collections.unmodifiableList(Arrays.asList(expectedCalendarDates)); assertOk(response); assertTrue(response.hasBody()); NetcdfFile nf = NetcdfFile.openInMemory("test_data.ncs", response.getBody(byte[].class)); NetcdfDataset ds = new NetcdfDataset(nf); CoordinateAxis1DTime tAxis = CoordinateAxis1DTime.factory(ds, ds.findCoordinateAxis("time"), null); List<CalendarDate> dates = tAxis.getCalendarDates(); assert dates != null; assertEquals(expectedDatesAsCalendarDate, dates); } }