/* (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.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;
import javax.xml.namespace.QName;
import org.apache.commons.io.FileUtils;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CatalogBuilder;
import org.geoserver.catalog.CoverageInfo;
import org.geoserver.catalog.CoverageStoreInfo;
import org.geoserver.catalog.DimensionDefaultValueSetting;
import org.geoserver.catalog.DimensionDefaultValueSetting.Strategy;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.PublishedType;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.config.GeoServerDataDirectory;
import org.geoserver.data.test.MockData;
import org.geoserver.data.test.SystemTestData;
import org.geoserver.platform.GeoServerResourceLoader;
import org.geoserver.util.IOUtils;
import org.geoserver.wms.WMS;
import org.geoserver.wms.WMSDimensionsTestSupport;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.GridCoverage2DReader;
import org.geotools.coverage.grid.io.GridFormatFinder;
import org.geotools.data.DataUtilities;
import org.geotools.feature.NameImpl;
import org.geotools.feature.type.DateUtil;
import org.geotools.gce.imagemosaic.ImageMosaicFormat;
import org.geotools.io.DefaultFileFilter;
import org.geotools.util.Range;
import org.junit.Before;
import org.junit.Test;
/**
* Tests the WMS default value support for TIME dimension raster layers.
*
* @author Ilkka Rinne <ilkka.rinne@spatineo.com>
*/
public class RasterTimeDimensionDefaultValueTest extends WMSDimensionsTestSupport {
static final QName WATTEMP_FUTURE = new QName(MockData.SF_URI, "watertemp_future_generated",
MockData.SF_PREFIX);
WMS wms;
@Override
protected void onSetUp(SystemTestData testData) throws Exception {
super.onSetUp(testData);
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
prepareFutureCoverageData(WATTEMP_FUTURE, this.getDataDirectory(), this.getCatalog());
}
@Before
public void setup() throws Exception {
wms = getWMS(); //with the initialized application context
}
@Test
public void testDefaultTimeCoverageSelector() throws Exception {
// Use default default value strategy:
setupResourceDimensionDefaultValue(WATTEMP_FUTURE, ResourceInfo.TIME, null);
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));
long todayMidnight = cal.getTimeInMillis();
CoverageInfo coverage = getCatalog().getCoverageByName(WATTEMP_FUTURE.getLocalPart());
java.util.Date d = (java.util.Date) wms.getDefaultTime(coverage);
assertTrue("Returns a valid Default time", d != null);
assertTrue("Default time should be the closest one", d.getTime() == todayMidnight);
}
@Test
public void testExplicitCurrentTimeCoverageSelector() throws Exception {
// Use explicit default value strategy:
DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting();
defaultValueSetting.setStrategyType(Strategy.NEAREST);
defaultValueSetting.setReferenceValue(DimensionDefaultValueSetting.TIME_CURRENT);
setupResourceDimensionDefaultValue(WATTEMP_FUTURE, ResourceInfo.TIME, defaultValueSetting);
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));
long todayMidnight = cal.getTimeInMillis();
CoverageInfo coverage = getCatalog().getCoverageByName(WATTEMP_FUTURE.getLocalPart());
java.util.Date d = (java.util.Date) wms.getDefaultTime(coverage);
assertTrue("Returns a valid Default time", d != null);
assertTrue("Default time should be the closest one", d.getTime() == todayMidnight);
}
@Test
public void testFixedTimeRange() throws Exception {
// Use explicit default value strategy:
DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting();
defaultValueSetting.setStrategyType(Strategy.FIXED);
defaultValueSetting.setReferenceValue("P1M/PRESENT");
setupResourceDimensionDefaultValue(WATTEMP_FUTURE, ResourceInfo.TIME, defaultValueSetting);
CoverageInfo coverage = getCatalog().getCoverageByName(WATTEMP_FUTURE.getLocalPart());
// the default is a single value, as we get the nearest to the range
java.util.Date curr = new java.util.Date();
Range d = (Range) wms.getDefaultTime(coverage);
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 testExplicitMinTimeCoverageSelector() throws Exception {
// Use explicit default value strategy:
DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting();
defaultValueSetting.setStrategyType(Strategy.MINIMUM);
setupResourceDimensionDefaultValue(WATTEMP_FUTURE, ResourceInfo.TIME, defaultValueSetting);
//From src/test/resources/org/geoserver/wms/watertemp.zip:
Date expected = Date.valueOf("2008-10-31");
CoverageInfo coverage = getCatalog().getCoverageByName(WATTEMP_FUTURE.getLocalPart());
java.util.Date d = (java.util.Date) wms.getDefaultTime(coverage);
assertTrue("Returns a valid Default time", d != null);
assertTrue("Default time should be the smallest one", d.getTime() == expected.getTime());
}
@Test
public void testExplicitMaxTimeCoverageSelector() throws Exception {
// Use explicit default value strategy:
DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting();
defaultValueSetting.setStrategyType(Strategy.MAXIMUM);
setupResourceDimensionDefaultValue(WATTEMP_FUTURE, ResourceInfo.TIME, defaultValueSetting);
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));
// This is what the test data setup does, and it makes a difference at the
// end of the month (e.g. 29 Jan)
cal.set(Calendar.MONTH, cal.get(Calendar.MONTH) + 1);
cal.set(Calendar.MONTH, cal.get(Calendar.MONTH) - 1);
cal.set(Calendar.YEAR, cal.get(Calendar.YEAR) + 1);
long oneYearInFuture = cal.getTimeInMillis();
CoverageInfo coverage = getCatalog().getCoverageByName(WATTEMP_FUTURE.getLocalPart());
java.util.Date d = (java.util.Date) wms.getDefaultTime(coverage);
assertTrue("Returns a valid Default time", d != null);
assertTrue("Default time should be the biggest one", d.getTime() == oneYearInFuture);
}
@Test
public void testExplicitFixedTimeCoverageSelector() throws Exception {
String fixedTimeStr = "2012-06-01T03:00:00.000Z";
// Use explicit default value strategy:
DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting();
defaultValueSetting.setStrategyType(Strategy.FIXED);
defaultValueSetting.setReferenceValue(fixedTimeStr);
setupResourceDimensionDefaultValue(WATTEMP_FUTURE, ResourceInfo.TIME, defaultValueSetting);
long fixedTime = DateUtil.parseDateTime(fixedTimeStr);
CoverageInfo coverage = getCatalog().getCoverageByName(WATTEMP_FUTURE.getLocalPart());
java.util.Date d = (java.util.Date) wms.getDefaultTime(coverage);
assertTrue("Returns a valid Default time", d != null);
assertTrue("Default time should be the fixed one", d.getTime() == fixedTime);
}
@Test
public void testExplicitNearestToGivenTimeCoverageSelector() throws Exception {
String preferredTimeStr = "2009-01-01T00:00:00.000Z";
// Use explicit default value strategy:
DimensionDefaultValueSetting defaultValueSetting = new DimensionDefaultValueSetting();
defaultValueSetting.setStrategyType(Strategy.NEAREST);
defaultValueSetting.setReferenceValue(preferredTimeStr);
setupResourceDimensionDefaultValue(WATTEMP_FUTURE, ResourceInfo.TIME, defaultValueSetting);
//From src/test/resources/org/geoserver/wms/watertemp.zip:
Date expected = Date.valueOf("2008-11-01");
CoverageInfo coverage = getCatalog().getCoverageByName(WATTEMP_FUTURE.getLocalPart());
java.util.Date d = (java.util.Date) wms.getDefaultTime(coverage);
assertTrue("Returns a valid Default time", d != null);
assertTrue("Default time should be the closest one", d.getTime() == expected.getTime());
}
public static void prepareFutureCoverageData(QName coverageName, GeoServerDataDirectory dataDirectory, Catalog catalog) throws IOException {
SimpleDateFormat tsFormatter = new SimpleDateFormat("yyyyMMdd");
// Prepare the target dates for the dummy coverages to be created
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));
long justPast = cal.getTimeInMillis();
cal.set(Calendar.MONTH, cal.get(Calendar.MONTH) + 1);
long oneMonthInFuture = cal.getTimeInMillis();
cal.set(Calendar.MONTH, cal.get(Calendar.MONTH) - 1);
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);
long oneYearInFuture = cal.getTimeInMillis();
// Copy watertemp.zip test coverage resource in the data dir under a different name:
GeoServerResourceLoader loader = catalog.getResourceLoader();
File targetDir = loader.createDirectory(dataDirectory.root(), coverageName.getPrefix()
+ File.separator + coverageName.getLocalPart());
File target = new File(targetDir, coverageName.getLocalPart() + ".zip");
loader.copyFromClassPath("org/geoserver/wms/dimension/watertemp.zip", target);
// unpack the archive
IOUtils.decompress(target, targetDir);
// delete archive
target.delete();
// Make three new dummy coverage files with the needed timestamps:
File input = null;
File output = null;
FilenameFilter tiffFilter = new DefaultFileFilter("*.tiff");
String[] tiffnames = targetDir.list(tiffFilter);
if (tiffnames != null) {
if (tiffnames.length > 0) {
input = new File(targetDir, tiffnames[0]);
output = new File(targetDir, "DUMMY_watertemp_000_"
+ tsFormatter.format(new Date(justPast)) + "T0000000_12.tiff");
FileUtils.copyFile(input, output);
output = new File(targetDir, "DUMMY_watertemp_000_"
+ tsFormatter.format(new Date(oneMonthInFuture)) + "T0000000_12.tiff");
FileUtils.copyFile(input, output);
output = new File(targetDir, "DUMMY_watertemp_000_"
+ tsFormatter.format(new Date(oneYearInFuture)) + "T0000000_12.tiff");
FileUtils.copyFile(input, output);
}
}
addRasterLayerFromDataDir(WATTEMP_FUTURE, dataDirectory, catalog);
}
/*
* This method is necessary here, because SystemTestData#addRasterLayer assumes that the raster data file is somewhere in the classpath and should
* be copied to the data directory before registering the new layer. This method skips the copy and extract and assumes that the coverage data is
* already in contained in the data directory.
*/
private static void addRasterLayerFromDataDir(QName qName, GeoServerDataDirectory dataDirectory, Catalog catalog) throws IOException {
String prefix = qName.getPrefix();
String name = qName.getLocalPart();
// setup the data
File file = new File(dataDirectory.root() + File.separator + prefix, name);
if (!file.exists()) {
throw new IllegalArgumentException("There is no file with name '" + prefix
+ File.separator + name + "' in the data directory");
}
// load the format/reader
AbstractGridFormat format = (AbstractGridFormat) GridFormatFinder.findFormat(file);
if (format == null) {
throw new RuntimeException("No format for " + file.getCanonicalPath());
}
GridCoverage2DReader reader = null;
try {
reader = (GridCoverage2DReader) format.getReader(file);
if (reader == null) {
throw new RuntimeException("No reader for " + file.getCanonicalPath()
+ " with format " + format.getName());
}
// configure workspace if it doesn't already exist
if (catalog.getWorkspaceByName(prefix) == null) {
((SystemTestData) testData).addWorkspace(prefix, qName.getNamespaceURI(),
catalog);
}
// create the store
CoverageStoreInfo store = catalog.getCoverageStoreByName(prefix, name);
if (store == null) {
store = catalog.getFactory().createCoverageStore();
}
store.setName(name);
store.setWorkspace(catalog.getWorkspaceByName(prefix));
store.setEnabled(true);
store.setURL(DataUtilities.fileToURL(file).toString());
store.setType(format.getName());
if (store.getId() == null) {
catalog.add(store);
} else {
catalog.save(store);
}
// create the coverage
CatalogBuilder builder = new CatalogBuilder(catalog);
builder.setStore(store);
CoverageInfo coverage = null;
try {
coverage = builder.buildCoverage(reader, null);
// coverage read params
if (format instanceof ImageMosaicFormat) {
// make sure we work in immediate mode
coverage.getParameters()
.put(AbstractGridFormat.USE_JAI_IMAGEREAD.getName().getCode(),
Boolean.FALSE);
}
} catch (Exception e) {
throw new IOException(e);
}
coverage.setName(name);
coverage.setTitle(name);
coverage.setDescription(name);
coverage.setEnabled(true);
CoverageInfo cov = catalog.getCoverageByCoverageStore(store, name);
if (cov == null) {
catalog.add(coverage);
} else {
builder.updateCoverage(cov, coverage);
catalog.save(cov);
coverage = cov;
}
LayerInfo layer = catalog.getLayerByName(new NameImpl(qName));
if (layer == null) {
layer = catalog.getFactory().createLayer();
}
layer.setResource(coverage);
layer.setDefaultStyle(catalog.getStyleByName(SystemTestData.DEFAULT_RASTER_STYLE));
layer.setType(PublishedType.RASTER);
layer.setEnabled(true);
if (layer.getId() == null) {
catalog.add(layer);
} else {
catalog.save(layer);
}
} finally {
if (reader != null) {
reader.dispose();
}
}
}
}