/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2014 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.wms.dimension; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.sql.Date; import java.util.Calendar; import java.util.Collections; import java.util.TimeZone; import javax.xml.namespace.QName; import org.geoserver.catalog.CoverageInfo; import org.geoserver.catalog.DimensionDefaultValueSetting; import org.geoserver.catalog.DimensionInfo; import org.geoserver.catalog.DimensionPresentation; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.ResourceInfo; import org.geoserver.catalog.DimensionDefaultValueSetting.Strategy; import org.geoserver.catalog.impl.DimensionInfoImpl; import org.geoserver.data.test.MockData; import org.geoserver.data.test.SystemTestData; import org.geoserver.wms.WMS; import org.geoserver.wms.WMSDimensionsTestSupport; import org.geoserver.wms.WMSTestSupport; import org.geotools.data.DataUtilities; import org.geotools.data.FeatureStore; import org.geotools.data.memory.MemoryFeatureCollection; import org.geotools.feature.type.DateUtil; import org.geotools.util.Range; import org.junit.Before; import org.junit.Test; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; /** * Tests the WMS default value support for TIME dimension for * vector layers. * * @author Ilkka Rinne <ilkka.rinne@spatineo.com> */ public class VectorTimeDimensionDefaultValueTest extends WMSDimensionsTestSupport { static final QName TIME_WITH_START_END = new QName(MockData.SF_URI, "TimeWithStartEnd", MockData.SF_PREFIX); WMS wms; @Override protected void onSetUp(SystemTestData testData) throws Exception { super.onSetUp(testData); TimeZone.setDefault(TimeZone.getTimeZone("UTC")); } @Before public void setup() throws Exception { wms = getWMS(); //with the initialized application context ((SystemTestData)testData).addVectorLayer(TIME_WITH_START_END,Collections.EMPTY_MAP,"TimeElevationWithStartEnd.properties", getClass(), getCatalog()); } @Test public void testDefaultTimeVectorSelector() throws Exception { int fid = 1000; //Use default DimensionInfo setup, should return the "current" time: setupFeatureTimeDimension(null); FeatureTypeInfo timeWithStartEnd = getCatalog().getFeatureTypeByName( TIME_WITH_START_END.getLocalPart()); Date twoDaysAgo = addFeatureWithTimeTwoDaysAgo(fid++); this.addFeature(fid++, twoDaysAgo,Double.valueOf(0d)); java.util.Date d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the closest one", d.getTime() == twoDaysAgo.getTime()); // Add some features with timestamps in the future: Date dayAfterTomorrow = addFeatureWithTimeDayAfterTomorrow(fid++); addFeatureWithTimeOneYearFromNow(fid++); d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the closest one", d.getTime() == dayAfterTomorrow.getTime()); Date todayMidnight = addFeatureWithTimeTodayMidnight(fid++); d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the closest one", d.getTime() == todayMidnight .getTime()); } @Test public void testExplicitCurrentTimeVectorSelector() throws Exception { int fid = 1000; //Use explicit default value DimensionInfo setup: DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); defaultValueSetting.setStrategyType(Strategy.NEAREST); defaultValueSetting.setReferenceValue(DimensionDefaultValueSetting.TIME_CURRENT); setupFeatureTimeDimension(defaultValueSetting); FeatureTypeInfo timeWithStartEnd = getCatalog().getFeatureTypeByName( TIME_WITH_START_END.getLocalPart()); Date twoDaysAgo = addFeatureWithTimeTwoDaysAgo(fid++); this.addFeature(fid++, twoDaysAgo,Double.valueOf(0d)); java.util.Date d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the closest one", d.getTime() == twoDaysAgo.getTime()); // Add some features with timestamps in the future: Date dayAfterTomorrow = addFeatureWithTimeDayAfterTomorrow(fid++); addFeatureWithTimeOneYearFromNow(fid++); d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the closest one", d.getTime() == dayAfterTomorrow.getTime()); Date todayMidnight = addFeatureWithTimeTodayMidnight(fid++); d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the closest one", d.getTime() == todayMidnight .getTime()); } @Test public void testExplicitMinTimeVectorSelector() throws Exception { int fid = 1000; //Use explicit default value DimensionInfo setup: DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); defaultValueSetting.setStrategyType(Strategy.MINIMUM); setupFeatureTimeDimension(defaultValueSetting); FeatureTypeInfo timeWithStartEnd = getCatalog().getFeatureTypeByName( TIME_WITH_START_END.getLocalPart()); Date smallest = Date.valueOf("2012-02-11"); Date twoDaysAgo = addFeatureWithTimeTwoDaysAgo(fid++); this.addFeature(fid++, twoDaysAgo,Double.valueOf(0d)); java.util.Date d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the smallest one", d.getTime() == smallest.getTime()); // Add some features with timestamps in the future: addFeatureWithTimeDayAfterTomorrow(fid++); addFeatureWithTimeOneYearFromNow(fid++); d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the smallest one", d.getTime() == smallest.getTime()); addFeatureWithTimeTodayMidnight(fid++); d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the smallest one", d.getTime() == smallest .getTime()); } @Test public void testExplicitMaxTimeVectorSelector() throws Exception { int fid = 1000; //Use explicit default value DimensionInfo setup: DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); defaultValueSetting.setStrategyType(Strategy.MAXIMUM); setupFeatureTimeDimension(defaultValueSetting); FeatureTypeInfo timeWithStartEnd = getCatalog().getFeatureTypeByName( TIME_WITH_START_END.getLocalPart()); Date twoDaysAgo = addFeatureWithTimeTwoDaysAgo(fid++); this.addFeature(fid++, twoDaysAgo, Double.valueOf(0d)); java.util.Date d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the biggest one", d.getTime() == twoDaysAgo.getTime()); // Add some features with timestamps in the future: addFeatureWithTimeDayAfterTomorrow(fid++); Date oneYearFromNow = addFeatureWithTimeOneYearFromNow(fid++); d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the biggest one", d.getTime() == oneYearFromNow.getTime()); addFeatureWithTimeTodayMidnight(fid++); d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the biggest one", d.getTime() == oneYearFromNow .getTime()); } @Test public void testExplicitFixedTimeVectorSelector() throws Exception { int fid = 1000; String fixedTimeStr = "2012-06-01T03:00:00.000Z"; //Use explicit default value DimensionInfo setup: DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); defaultValueSetting.setStrategyType(Strategy.FIXED); defaultValueSetting.setReferenceValue(fixedTimeStr); long fixedTime = DateUtil.parseDateTime(fixedTimeStr); setupFeatureTimeDimension(defaultValueSetting); FeatureTypeInfo timeWithStartEnd = getCatalog().getFeatureTypeByName( TIME_WITH_START_END.getLocalPart()); Date twoDaysAgo = addFeatureWithTimeTwoDaysAgo(fid++); this.addFeature(fid++, twoDaysAgo,Double.valueOf(0d)); java.util.Date d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the fixed one", d.getTime() == fixedTime); // Add some features with timestamps in the future: addFeatureWithTimeDayAfterTomorrow(fid++); addFeatureWithTimeOneYearFromNow(fid++); d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the fixed one", d.getTime() == fixedTime); addFeatureWithTimeTodayMidnight(fid++); d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the fixed one", d.getTime() == fixedTime); } @Test public void testFixedRange() throws Exception { // Use explicit default value DimensionInfo setup: DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); defaultValueSetting.setStrategyType(Strategy.FIXED); defaultValueSetting.setReferenceValue("P1M/PRESENT"); setupFeatureTimeDimension(defaultValueSetting); FeatureTypeInfo timeWithStartEnd = getCatalog().getFeatureTypeByName( TIME_WITH_START_END.getLocalPart()); // the default should be the range we requested java.util.Date curr = new java.util.Date(); Range d = (Range) wms.getDefaultTime(timeWithStartEnd); assertTrue("Returns a valid Default range", d != null); // check "now" it's in the same minute... should work for even the slowest build server assertDateEquals(curr, (java.util.Date) d.getMaxValue(), MILLIS_IN_MINUTE); // the beginning assertDateEquals(new Date(curr.getTime() - 30l * MILLIS_IN_DAY), (java.util.Date) d.getMinValue(), 60000); } @Test public void testExplicitNearestToGivenTimeVectorSelector() throws Exception { int fid = 1000; String preferredTimeStr = "2012-06-01T03:00:00.000Z"; //Use explicit default value DimensionInfo setup: DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting(); defaultValueSetting.setStrategyType(Strategy.NEAREST); defaultValueSetting.setReferenceValue(preferredTimeStr); //From src/test/resources/org/geoserver/wms/TimeElevationWithStartEnd.properties: Date expected = Date.valueOf("2012-02-12"); setupFeatureTimeDimension(defaultValueSetting); FeatureTypeInfo timeWithStartEnd = getCatalog().getFeatureTypeByName( TIME_WITH_START_END.getLocalPart()); Date twoDaysAgo = addFeatureWithTimeTwoDaysAgo(fid++); this.addFeature(fid++, twoDaysAgo, Double.valueOf(0d)); java.util.Date d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the closest one", d.getTime() == expected.getTime()); // Add some features with timestamps in the future: addFeatureWithTimeDayAfterTomorrow(fid++); addFeatureWithTimeOneYearFromNow(fid++); d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the closest one", d.getTime() == expected.getTime()); addFeatureWithTimeTodayMidnight(fid++); d = (java.util.Date) wms.getDefaultTime(timeWithStartEnd); assertTrue("Default time is null", d != null); assertTrue("Default time should be the closest one", d.getTime() == expected.getTime()); } protected void setupFeatureTimeDimension(DimensionDefaultValueSetting defaultValue) { FeatureTypeInfo info = getCatalog() .getFeatureTypeByName(TIME_WITH_START_END.getLocalPart()); DimensionInfo di = new DimensionInfoImpl(); di.setEnabled(true); di.setAttribute("startTime"); di.setDefaultValue(defaultValue); di.setPresentation(DimensionPresentation.LIST); info.getMetadata().put(ResourceInfo.TIME, di); getCatalog().save(info); } protected void addFeature(int id, Date time, Double elevation) throws IOException { FeatureTypeInfo timeWithStartEnd = getCatalog().getFeatureTypeByName( TIME_WITH_START_END.getLocalPart()); FeatureStore fs = (FeatureStore) timeWithStartEnd.getFeatureSource(null, null); SimpleFeatureType type = (SimpleFeatureType) timeWithStartEnd.getFeatureType(); MemoryFeatureCollection coll = new MemoryFeatureCollection(type); StringBuffer content = new StringBuffer(); content.append(id); content.append('|'); content.append(time.toString()); content.append("||"); content.append(elevation); content.append('|'); SimpleFeature f = DataUtilities.createFeature(type, content.toString()); coll.add(f); org.geotools.data.Transaction tx = fs.getTransaction(); fs.addFeatures(coll); tx.commit(); } private java.sql.Date addFeatureWithTimeTodayMidnight(int fid) throws IOException{ TimeZone.setDefault(TimeZone.getTimeZone("UTC")); Calendar cal = Calendar.getInstance(); cal.set(Calendar.HOUR_OF_DAY, cal.getActualMinimum(Calendar.HOUR_OF_DAY)); cal.set(Calendar.MINUTE, cal.getActualMinimum(Calendar.MINUTE)); cal.set(Calendar.SECOND, cal.getActualMinimum(Calendar.SECOND)); cal.set(Calendar.MILLISECOND, cal.getActualMinimum(Calendar.MILLISECOND)); Date todayMidnight = new Date(cal.getTimeInMillis()); this.addFeature(fid, todayMidnight,Double.valueOf(0d)); return todayMidnight; } private java.sql.Date addFeatureWithTimeTwoDaysAgo(int fid) throws IOException{ Calendar cal = Calendar.getInstance(); cal.set(Calendar.HOUR_OF_DAY, cal.getActualMinimum(Calendar.HOUR_OF_DAY)); cal.set(Calendar.MINUTE, cal.getActualMinimum(Calendar.MINUTE)); cal.set(Calendar.SECOND, cal.getActualMinimum(Calendar.SECOND)); cal.set(Calendar.MILLISECOND, cal.getActualMinimum(Calendar.MILLISECOND)); cal.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH) - 2); Date twoDaysAgo = new Date(cal.getTimeInMillis()); this.addFeature(fid, twoDaysAgo,Double.valueOf(0d)); return twoDaysAgo; } private java.sql.Date addFeatureWithTimeDayAfterTomorrow(int fid) throws IOException{ Calendar cal = Calendar.getInstance(); cal.set(Calendar.HOUR_OF_DAY, cal.getActualMinimum(Calendar.HOUR_OF_DAY)); cal.set(Calendar.MINUTE, cal.getActualMinimum(Calendar.MINUTE)); cal.set(Calendar.SECOND, cal.getActualMinimum(Calendar.SECOND)); cal.set(Calendar.MILLISECOND, cal.getActualMinimum(Calendar.MILLISECOND)); cal.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH) + 2); Date tomorrow = new Date(cal.getTimeInMillis()); this.addFeature(fid++, tomorrow, Double.valueOf(0d)); return tomorrow; } private java.sql.Date addFeatureWithTimeOneYearFromNow(int fid) throws IOException{ Calendar cal = Calendar.getInstance(); cal.set(Calendar.HOUR_OF_DAY, cal.getActualMinimum(Calendar.HOUR_OF_DAY)); cal.set(Calendar.MINUTE, cal.getActualMinimum(Calendar.MINUTE)); cal.set(Calendar.SECOND, cal.getActualMinimum(Calendar.SECOND)); cal.set(Calendar.MILLISECOND, cal.getActualMinimum(Calendar.MILLISECOND)); cal.set(Calendar.YEAR, cal.get(Calendar.YEAR) + 1); Date oneYearFromNow = new Date(cal.getTimeInMillis()); this.addFeature(fid++, oneYearFromNow, Double.valueOf(0d)); return oneYearFromNow; } }