/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2015, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.coverage.io.netcdf;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.TimeZone;
import org.apache.commons.io.FileUtils;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.DimensionDescriptor;
import org.geotools.coverage.grid.io.GridFormatFinder;
import org.geotools.imageio.netcdf.cv.ClimatologicalTimeCoordinateVariable;
import org.geotools.imageio.netcdf.cv.ClimatologicalTimeHandlerSpi.ClimatologicalTimeHandler;
import org.geotools.imageio.netcdf.cv.CoordinateHandlerFinder;
import org.geotools.imageio.netcdf.cv.CoordinateHandlerSpi.CoordinateHandler;
import org.geotools.imageio.netcdf.cv.CoordinateVariable;
import org.geotools.imageio.netcdf.utilities.NetCDFTimeUtilities;
import org.geotools.test.TestData;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.TemporalCRS;
import org.opengis.referencing.crs.VerticalCRS;
import ucar.nc2.Dimension;
import ucar.nc2.dataset.CoordinateAxis;
import ucar.nc2.dataset.CoordinateAxis1D;
import ucar.nc2.dataset.NetcdfDataset;
/**
* @author Simone Giannecchini, GeoSolutions SAS
*
*/
public class CoordinateVariableTest extends Assert {
@BeforeClass
public static void init() {
System.setProperty("user.timezone", "GMT");
System.setProperty("netcdf.coordinates.enablePlugins", "true");
}
@AfterClass
public static void close() {
System.clearProperty("user.timezone");
}
/** Simple CoordinateAxis1D wrapper to override Units */
class CoordinateAxis1DUnitWrapper extends CoordinateAxis1D {
String units;
@Override
public String getUnitsString() {
return units;
}
CoordinateAxis1DUnitWrapper(NetcdfDataset ncd, CoordinateAxis1D axis, String units) {
super(ncd, axis);
this.units = units;
}
}
@Test
public void timeUnitsTest() throws Exception {
final NetcdfDataset dataset = NetcdfDataset.openDataset(TestData.url(this, "O3-NO2.nc")
.toExternalForm());
Dimension dim = dataset.findDimension("time");
// check type
CoordinateAxis coordinateAxis = dataset.findCoordinateAxis(dim.getShortName());
final int year = 2012;
final int month = 4;
final int day = 1;
final int hour = 0;
final int minute = 0;
final int second = 0;
final String refTime = year + "-" + (month < 10 ? ("0" + month) : month) + "-"
+ (day < 10 ? ("0" + day) : day) + " " + (hour < 10 ? ("0" + hour) : hour) + ":"
+ (minute < 10 ? ("0" + minute) : minute) + ":"
+ (second < 10 ? ("0" + second) : second);
// //
// Plural form units check
// //
checkTimes(dataset, coordinateAxis, refTime, year, month, day, hour, minute, second, false);
// //
// Singular form units check
// //
checkTimes(dataset, coordinateAxis, refTime, year, month, day, hour, minute, second, true);
dataset.close();
}
private void checkTimes(NetcdfDataset dataset, CoordinateAxis coordinateAxis, String refTime,
int year, int month, int day, int hour, int minute, int second, boolean singularForm) throws IOException {
String units = "";
CoordinateVariable<?> cv = null;
GregorianCalendar cal = null;
String unitsOriginSuffix = (singularForm ? "" : "s") // day vs days, hour vs hours, and so on
+ " since " + refTime;
// MONTHS
units = "month" + unitsOriginSuffix;
cv = getCoordinateVariable(dataset, coordinateAxis, units);
cal = getCalendar(year, month, day, hour, minute, second, GregorianCalendar.MONTH);
assertEquals(cal.getTime(), cv.getMaximum());
// DAYS
units = "day" + unitsOriginSuffix;
cv = getCoordinateVariable(dataset, coordinateAxis, units);
cal = getCalendar(year, month, day, hour, minute, second, GregorianCalendar.DAY_OF_MONTH);
assertEquals(cal.getTime(), cv.getMaximum());
// HOURS
units = "hour" + unitsOriginSuffix;
cv = getCoordinateVariable(dataset, coordinateAxis, units);
cal = getCalendar(year, month, day, hour, minute, second, GregorianCalendar.HOUR_OF_DAY);
assertEquals(cal.getTime(), cv.getMaximum());
// MINUTES
units = "minute" + unitsOriginSuffix;
cv = getCoordinateVariable(dataset, coordinateAxis, units);
cal = getCalendar(year, month, day, hour, minute, second, GregorianCalendar.MINUTE);
assertEquals(cal.getTime(), cv.getMaximum());
// SECONDS
units = "second" + unitsOriginSuffix;
cv = getCoordinateVariable(dataset, coordinateAxis, units);
cal = getCalendar(year, month, day, hour, minute, second, GregorianCalendar.SECOND);
assertEquals(cal.getTime(), cv.getMaximum());
}
private GregorianCalendar getCalendar(int year, int month, int day, int hour, int minute,
int second, int unit) {
GregorianCalendar cal = new GregorianCalendar(NetCDFTimeUtilities.UTC_TIMEZONE);
// Months are zero based
if (unit == GregorianCalendar.YEAR) {
year++;
} else if (unit == GregorianCalendar.MONTH) {
month++;
} else if (unit == GregorianCalendar.DAY_OF_MONTH) {
day++;
} else if (unit == GregorianCalendar.HOUR_OF_DAY) {
hour++;
} else if (unit == GregorianCalendar.MINUTE) {
minute++;
} else if (unit == GregorianCalendar.SECOND) {
second++;
}
month--;
cal.set(year, month, day, hour, minute, second);
cal.set(GregorianCalendar.MILLISECOND, 0);
return cal;
}
private CoordinateVariable<?> getCoordinateVariable(NetcdfDataset dataset,
CoordinateAxis coordinateAxis, String units) {
CoordinateAxis1DUnitWrapper wrapper = new CoordinateAxis1DUnitWrapper(dataset, (CoordinateAxis1D) coordinateAxis, units);
return CoordinateVariable.create((CoordinateAxis1D) wrapper);
}
@Test
public void polyphemus() throws Exception {
// acquire dataset
final NetcdfDataset dataset = NetcdfDataset.openDataset(TestData.url(this, "O3-NO2.nc")
.toExternalForm());
assertNotNull(dataset);
final List<CoordinateAxis> cvs = dataset.getCoordinateAxes();
assertNotNull(cvs);
assertSame(4, cvs.size());
//
// cloud_formations is short
//
Dimension dim = dataset.findDimension("time");
assertNotNull(dim);
assertEquals("time", dim.getShortName());
// check type
CoordinateAxis coordinateAxis = dataset.findCoordinateAxis(dim.getShortName());
assertNotNull(coordinateAxis);
assertTrue(coordinateAxis instanceof CoordinateAxis1D);
Class<?> binding = CoordinateVariable.suggestBinding((CoordinateAxis1D) coordinateAxis);
assertNotNull(binding);
assertSame(Date.class, binding);
CoordinateVariable<?> cv = CoordinateVariable.create((CoordinateAxis1D) coordinateAxis);
assertSame(Date.class, cv.getType());
List<?> list = cv.read();
assertNotNull(list);
assertEquals(2, list.size());
final GregorianCalendar cal = new GregorianCalendar(NetCDFTimeUtilities.UTC_TIMEZONE);
cal.set(2012, 3, 1, 0, 0, 0);
cal.set(GregorianCalendar.MILLISECOND, 0);
assertEquals(cal.getTime(), cv.getMinimum());
assertEquals(list.get(0), cv.getMinimum());
cal.set(2012, 3, 1, 1, 0, 0);
assertEquals(cal.getTime(), cv.getMaximum());
assertEquals(list.get(1), cv.getMaximum());
assertEquals(2, cv.getSize());
assertEquals("hours since 2012-04-01 0:00:00", cv.getUnit());
CoordinateReferenceSystem crs = cv.getCoordinateReferenceSystem();
assertNotNull(crs);
assertTrue(crs instanceof TemporalCRS);
//
// lat is float
//
dim = dataset.findDimension("z");
assertNotNull(dim);
assertEquals("z", dim.getShortName());
// check type
coordinateAxis = dataset.findCoordinateAxis(dim.getShortName());
assertNotNull(coordinateAxis);
assertTrue(coordinateAxis instanceof CoordinateAxis1D);
binding = CoordinateVariable.suggestBinding((CoordinateAxis1D) coordinateAxis);
assertNotNull(binding);
assertSame(Float.class, binding);
cv = CoordinateVariable.create((CoordinateAxis1D) coordinateAxis);
list = cv.read();
assertNotNull(list);
assertEquals(2, list.size());
assertSame(Float.class, cv.getType());
assertEquals(10f, cv.getMinimum());
assertEquals(450f, cv.getMaximum());
assertEquals(list.get(0), cv.getMinimum());
assertEquals(list.get(1), cv.getMaximum());
assertEquals(2, cv.getSize());
assertEquals("meters", cv.getUnit());
crs = cv.getCoordinateReferenceSystem();
assertNotNull(crs);
assertTrue(crs instanceof VerticalCRS);
dataset.close();
}
@Test
@Ignore
public void testIASI() throws Exception {
//
// IASI does not have time or runtime, it only contains double variables besides lat e long
//
// acquire dataset
final NetcdfDataset dataset = NetcdfDataset.openDataset(TestData.url(this,
"IASI_C_EUMP_20121120062959_31590_eps_o_l2.nc").toExternalForm());
assertNotNull(dataset);
final List<CoordinateAxis> cvs = dataset.getCoordinateAxes();
assertNotNull(cvs);
assertSame(8, cvs.size());
//
// cloud_formations is short
//
Dimension dim = dataset.findDimension("cloud_formations");
assertNotNull(dim);
assertEquals("cloud_formations", dim.getShortName());
// check type
CoordinateAxis coordinateAxis = dataset.findCoordinateAxis(dim.getShortName());
assertNotNull(coordinateAxis);
assertTrue(coordinateAxis instanceof CoordinateAxis1D);
Class<?> binding = CoordinateVariable.suggestBinding((CoordinateAxis1D) coordinateAxis);
assertNotNull(binding);
assertSame(Short.class, binding);
CoordinateVariable<?> cv = CoordinateVariable.create((CoordinateAxis1D) coordinateAxis);
assertSame(Short.class, cv.getType());
assertEquals((short) 0, cv.getMinimum());
assertEquals((short) 2, cv.getMaximum());
assertEquals(3, cv.getSize());
assertEquals("level", cv.getUnit());
//
// lat is float
//
dim = dataset.findDimension("lat");
assertNotNull(dim);
assertEquals("lat", dim.getShortName());
// check type
coordinateAxis = dataset.findCoordinateAxis(dim.getShortName());
assertNotNull(coordinateAxis);
assertTrue(coordinateAxis instanceof CoordinateAxis1D);
binding = CoordinateVariable.suggestBinding((CoordinateAxis1D) coordinateAxis);
assertNotNull(binding);
assertSame(Float.class, binding);
cv = CoordinateVariable.create((CoordinateAxis1D) coordinateAxis);
assertNotNull(cv);
assertSame(Float.class, cv.getType());
assertEquals(-77.327934f, cv.getMinimum());
assertEquals(89.781555f, cv.getMaximum());
assertEquals(766, cv.getSize());
assertEquals("degrees_north", cv.getUnit());
assertTrue(cv.isRegular());
assertEquals(cv.getMinimum(), cv.getStart());
assertEquals(0.2184437770469516, cv.getIncrement());
//
// lon is float
//
dim = dataset.findDimension("lon");
assertNotNull(dim);
assertEquals("lon", dim.getShortName());
// check type
coordinateAxis = dataset.findCoordinateAxis(dim.getShortName());
assertNotNull(coordinateAxis);
assertTrue(coordinateAxis instanceof CoordinateAxis1D);
binding = CoordinateVariable.suggestBinding((CoordinateAxis1D) coordinateAxis);
assertNotNull(binding);
assertSame(Float.class, binding);
cv = CoordinateVariable.create((CoordinateAxis1D) coordinateAxis);
assertNotNull(cv);
assertEquals("degrees_east", cv.getUnit());
//
// pressure_levels_ozone is Double
//
dim = dataset.findDimension("nlo");
assertNotNull(dim);
assertEquals("nlo", dim.getShortName());
// check type
coordinateAxis = dataset.findCoordinateAxis(dim.getShortName());
assertNotNull(coordinateAxis);
assertTrue(coordinateAxis instanceof CoordinateAxis1D);
binding = CoordinateVariable.suggestBinding((CoordinateAxis1D) coordinateAxis);
assertNotNull(binding);
assertSame(Double.class, binding);
cv = CoordinateVariable.create((CoordinateAxis1D) coordinateAxis);
assertNotNull(cv);
assertEquals("Pa", cv.getUnit());
//
// pressure_levels_ozone is Double
//
dim = dataset.findDimension("nlt");
assertNotNull(dim);
assertEquals("nlt", dim.getShortName());
// check type
coordinateAxis = dataset.findCoordinateAxis(dim.getShortName());
assertNotNull(coordinateAxis);
assertTrue(coordinateAxis instanceof CoordinateAxis1D);
binding = CoordinateVariable.suggestBinding((CoordinateAxis1D) coordinateAxis);
assertNotNull(binding);
assertSame(Double.class, binding);
cv = CoordinateVariable.create((CoordinateAxis1D) coordinateAxis);
assertNotNull(cv);
assertEquals("Pa", cv.getUnit());
dataset.close();
}
@Test
public void testClimatologicalTimeVariable() throws MalformedURLException, IOException {
// Selection of the input file
final File workDir = new File(TestData.file(this, "."), "climatologicalaxis");
if (!workDir.mkdir()) {
FileUtils.deleteDirectory(workDir);
assertTrue("Unable to create workdir:" + workDir, workDir.mkdir());
}
FileUtils.copyFile(TestData.file(this, "climatological.zip"), new File(workDir, "climatological.zip"));
TestData.unzipFile(this, "climatologicalaxis/climatological.zip");
final NetcdfDataset dataset = NetcdfDataset.openDataset(TestData.url(this, "climatologicalaxis/climatological.nc")
.toExternalForm());
Dimension dim = dataset.findDimension("time");
CoordinateAxis1D coordinateAxis = (CoordinateAxis1D) dataset.findCoordinateAxis(dim.getShortName());
try {
CoordinateHandler handler = CoordinateHandlerFinder.findHandler(coordinateAxis);
assertNotNull(handler);
assertTrue(handler instanceof ClimatologicalTimeHandler);
ClimatologicalTimeHandler timeHandler = (ClimatologicalTimeHandler) handler;
CoordinateVariable<Date> coordinateVariable = timeHandler.createCoordinateVariable(coordinateAxis);
assertNotNull(coordinateVariable);
assertTrue(coordinateVariable instanceof ClimatologicalTimeCoordinateVariable);
ClimatologicalTimeCoordinateVariable timeVariable = (ClimatologicalTimeCoordinateVariable) coordinateVariable;
CoordinateReferenceSystem crs = timeVariable.getCoordinateReferenceSystem();
assertTrue(crs instanceof TemporalCRS);
assertEquals(12, timeVariable.getSize());
Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
calendar.set(0, 0, 16, 0, 0, 0);
calendar.set(Calendar.MILLISECOND, 0);
assertEquals(calendar.getTimeInMillis(), timeVariable.getMinimum().getTime());
calendar.set(Calendar.MONTH, 11);
assertEquals(calendar.getTimeInMillis(), timeVariable.getMaximum().getTime());
calendar.set(Calendar.MONTH, 2);
assertEquals(calendar.getTimeInMillis(), timeVariable.read(
Collections.singletonMap("time", 2)).getTime());
} finally {
dataset.close();
FileUtils.deleteDirectory(TestData.file(this, "climatologicalaxis"));
}
}
}