/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.wcs2_0; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import java.io.File; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.logging.Level; 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.CoverageDimensionInfo; import org.geoserver.catalog.CoverageInfo; import org.geoserver.catalog.CoverageStoreInfo; import org.geoserver.catalog.CoverageView; import org.geoserver.catalog.CoverageView.CompositionType; import org.geoserver.catalog.CoverageView.CoverageBand; import org.geoserver.catalog.CoverageView.InputCoverageBand; import org.geoserver.catalog.DimensionPresentation; import org.geoserver.catalog.LayerInfo; import org.geoserver.catalog.ProjectionPolicy; import org.geoserver.catalog.ResourceInfo; import org.geoserver.data.test.CiteTestData; import org.geoserver.data.test.SystemTestData; import org.geoserver.test.TestSetup; import org.geoserver.test.TestSetupFrequency; import org.geoserver.wcs.WCSInfo; import org.geoserver.wcs2_0.response.GranuleStack; import org.geoserver.web.netcdf.DataPacking; import org.geoserver.web.netcdf.NetCDFSettingsContainer; import org.geoserver.web.netcdf.NetCDFSettingsContainer.ExtraVariable; import org.geoserver.web.netcdf.NetCDFSettingsContainer.GlobalAttribute; import org.geoserver.web.netcdf.NetCDFSettingsContainer.VariableAttribute; import org.geoserver.web.netcdf.layer.NetCDFLayerSettingsContainer; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.coverage.io.netcdf.crs.NetCDFCRSAuthorityFactory; import org.geotools.feature.NameImpl; import org.geotools.imageio.netcdf.utilities.NetCDFUtilities; import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; import org.springframework.mock.web.MockHttpServletResponse; import ucar.ma2.Array; import ucar.ma2.DataType; import ucar.ma2.Range; import ucar.ma2.Section; import ucar.nc2.Attribute; import ucar.nc2.Dimension; import ucar.nc2.Variable; import ucar.nc2.dataset.NetcdfDataset; @TestSetup(run=TestSetupFrequency.ONCE) public class WCSNetCDFMosaicTest extends WCSNetCDFBaseTest { private static final double DELTA = 1E-6; private static final double PACKED_FILL_VALUE = -32768d; private static final String ORIGINAL_UNIT = "km"; private static final String CANONICAL_UNIT = "m"; private final static double ORIGINAL_FILL_VALUE = -9999.0d; private final static double ORIGINAL_PIXEL_VALUE = 9219.328d; public static QName LATLONMOSAIC = new QName(CiteTestData.WCS_URI, "2DLatLonCoverage", CiteTestData.WCS_PREFIX); public static QName DUMMYMOSAIC = new QName(CiteTestData.WCS_URI, "DummyCoverage", CiteTestData.WCS_PREFIX); public static QName VISIBILITYCF = new QName(CiteTestData.WCS_URI, "visibilityCF", CiteTestData.WCS_PREFIX); public static QName VISIBILITYPACKED = new QName(CiteTestData.WCS_URI, "visibilityPacked", CiteTestData.WCS_PREFIX); public static QName VISIBILITYCOMPRESSED = new QName(CiteTestData.WCS_URI, "visibilityCompressed", CiteTestData.WCS_PREFIX); public static QName VISIBILITYCFPACKED = new QName(CiteTestData.WCS_URI, "visibilityCFPacked", CiteTestData.WCS_PREFIX); public static QName TEMPERATURE_SURFACE = new QName(CiteTestData.WCS_URI, "Temperature_surface", CiteTestData.WCS_PREFIX); private final static String STANDARD_NAME = "visibility_in_air"; private final static Section NETCDF_SECTION; private CoverageView coverageView = null; static{ System.setProperty("org.geotools.referencing.forceXY", "true"); final List<Range> ranges = new LinkedList<Range>(); ranges.add(new Range(1)); ranges.add(new Range(1)); NETCDF_SECTION = new Section(ranges); } @Before public void init() { // make sure CRS ordering is correct System.setProperty("org.geotools.referencing.forceXY", "true"); System.setProperty("user.timezone", "GMT"); } @AfterClass public static void close() { System.clearProperty("org.geotools.referencing.forceXY"); System.clearProperty("user.timezone"); System.clearProperty(NetCDFCRSAuthorityFactory.SYSTEM_DEFAULT_USER_PROJ_FILE); } @Override protected void setUpTestData(SystemTestData testData) throws Exception { super.setUpTestData(testData); testData.setUpDefaultRasterLayers(); } @Override protected void onSetUp(SystemTestData testData) throws Exception { // workaround to add our custom multi dimensional format try { Field field = GetCoverage.class.getDeclaredField("mdFormats"); field.setAccessible(true); ((Set<String>) field.get(null)).add(WCSResponseInterceptor.MIME_TYPE); } catch (NoSuchFieldException e) { } catch (SecurityException e) { } catch (IllegalArgumentException e) { } catch (IllegalAccessException e) { } super.onSetUp(testData); testData.addRasterLayer(LATLONMOSAIC, "2DLatLonCoverage.zip", null, null, this.getClass(), getCatalog()); setupRasterDimension(getLayerId(LATLONMOSAIC), ResourceInfo.TIME, DimensionPresentation.LIST, null); setupRasterDimension(getLayerId(LATLONMOSAIC), ResourceInfo.CUSTOM_DIMENSION_PREFIX + "BANDS", DimensionPresentation.LIST, null); testData.addRasterLayer(DUMMYMOSAIC, "gom.zip", null, null, this.getClass(), getCatalog()); createCoverageView(); addViewToCatalog(); testData.addRasterLayer(VISIBILITYCF, "visibility.zip", null, null, this.getClass(), getCatalog()); setupNetCDFoutSettings(VISIBILITYCF); testData.addRasterLayer(VISIBILITYPACKED, "visibility.zip", null, null, this.getClass(), getCatalog()); setupNetCDFoutSettings(VISIBILITYPACKED); testData.addRasterLayer(VISIBILITYCFPACKED, "visibility.zip", null, null, this.getClass(), getCatalog()); setupNetCDFoutSettings(VISIBILITYCFPACKED); testData.addRasterLayer(VISIBILITYCOMPRESSED, "visibility.zip", null, null, this.getClass(), getCatalog()); setupNetCDFoutSettings(VISIBILITYCOMPRESSED); testData.addRasterLayer(TEMPERATURE_SURFACE, "Temperature_surface.zip", null, null, this.getClass(), getCatalog()); setupRasterDimension(getLayerId(TEMPERATURE_SURFACE), ResourceInfo.TIME, DimensionPresentation.LIST, null); configureTemperatureSurface(); } private void setupNetCDFoutSettings(QName name) { CoverageInfo info = getCatalog().getCoverageByName(getLayerId(name)); // Set the Declared SRS info.setSRS("EPSG:4326"); info.setProjectionPolicy(ProjectionPolicy.REPROJECT_TO_DECLARED); String layerName = name.getLocalPart().toUpperCase(); boolean isPackedLayer = layerName.contains("PACKED"); boolean isCF = layerName.contains("CF"); boolean isCompressed = layerName.contains("COMPRESSED"); // Update the UnitOfMeasure to km and noData to -9999 CoverageDimensionInfo dimension = info.getDimensions().get(0); String originalUnit = ORIGINAL_UNIT; dimension.setUnit(originalUnit); List<Double> nullValues = dimension.getNullValues(); if (nullValues != null) { nullValues.clear(); nullValues.add(ORIGINAL_FILL_VALUE); } NetCDFLayerSettingsContainer container = new NetCDFLayerSettingsContainer(); container.setCompressionLevel(isCompressed ? 9 : 0); container.setShuffle(true); container.setDataPacking(isPackedLayer ? DataPacking.SHORT : DataPacking.NONE); List<GlobalAttribute> attributes = new ArrayList<GlobalAttribute>(); attributes.add(new GlobalAttribute("custom_attribute", "testing WCS")); attributes.add(new GlobalAttribute("Conventions", "CF-1.6")); attributes.add(new GlobalAttribute("NULLAttribute", null)); container.setGlobalAttributes(attributes); // Setting a different name from the standard table as well as the proper // canonical unit container.setLayerName(STANDARD_NAME); container.setLayerUOM(isCF ? CANONICAL_UNIT : originalUnit); String key = "NetCDFOutput.Key"; info.getMetadata().put(key, container); getCatalog().save(info); } @Test public void testRequestCoverage() throws Exception { // http response from the request inside the string MockHttpServletResponse response = getAsServletResponse("ows?request=GetCoverage&service=WCS&version=2.0.1" + "&coverageId=wcs__2DLatLonCoverage&format=application/custom&subset=time,http://www.opengis.net/def/trs/ISO-8601/0/Gregorian UTC(\"2013-11-01T00:00:00.000Z\")&subset=BANDS(\"MyBand\")"); assertNotNull(response); GridCoverage2D lastResult = applicationContext.getBean(WCSResponseInterceptor.class).getLastResult(); assertTrue(lastResult instanceof GranuleStack); GranuleStack stack = (GranuleStack) lastResult; //we expect a single granule which covers the entire mosaic for(GridCoverage2D c : stack.getGranules()){ assertEquals(30., c.getEnvelope2D().getHeight(),0.001); assertEquals(45., c.getEnvelope2D().getWidth(),0.001); } assertEquals(1, stack.getGranules().size()); } @Test public void testRequestCoverageLatLon() throws Exception { final WCSInfo wcsInfo = getWCS(); final boolean oldLatLon = wcsInfo.isLatLon(); wcsInfo.setLatLon(true); getGeoServer().save(wcsInfo); try { // http response from the request inside the string MockHttpServletResponse response = getAsServletResponse("ows?request=GetCoverage&service=WCS&version=2.0.1" + "&coverageId=wcs__2DLatLonCoverage&format=application/custom&subset=time,http://www.opengis.net/def/trs/ISO-8601/0/Gregorian UTC(\"2013-11-01T00:00:00.000Z\")&subset=BANDS(\"MyBand\")"); assertNotNull(response); GridCoverage2D lastResult = applicationContext.getBean(WCSResponseInterceptor.class) .getLastResult(); assertTrue(lastResult instanceof GranuleStack); GranuleStack stack = (GranuleStack) lastResult; // we expect a single granule which covers the entire mosaic for (GridCoverage2D c : stack.getGranules()) { System.out.println(c.getEnvelope()); assertEquals(45., c.getEnvelope2D().getHeight(), 0.001); assertEquals(30., c.getEnvelope2D().getWidth(), 0.001); } assertEquals(1, stack.getGranules().size()); } finally { wcsInfo.setLatLon(oldLatLon); getGeoServer().save(wcsInfo); } } @Test public void testRequestCoverageView() throws Exception { // http response from the request inside the string MockHttpServletResponse response = getAsServletResponse("ows?request=GetCoverage&service=WCS&version=2.0.1" + "&coverageId=wcs__dummyView&format=application/x-netcdf&subset=http://www.opengis.net/def/axis/OGC/0/time(\"2013-01-08T00:00:00.000Z\")"); assertNotNull(response); assertEquals("application/x-netcdf", response.getContentType()); byte[] netcdfOut = getBinary(response); File file = File.createTempFile("netcdf", "out.nc", new File("./target")); try { FileUtils.writeByteArrayToFile(file, netcdfOut); NetcdfDataset dataset = NetcdfDataset.openDataset(file.getAbsolutePath()); assertNotNull(dataset); dataset.close(); } finally { FileUtils.deleteQuietly(file); } } @Test public void testRequestNetCDF4() throws Exception { boolean isNC4Available = NetCDFUtilities.isNC4CAvailable(); if (!isNC4Available && LOGGER.isLoggable(Level.INFO)) { LOGGER.info("NetCDF C library not found. NetCDF4 output will not be created"); } // http response from the request inside the string MockHttpServletResponse response = getAsServletResponse("ows?request=GetCoverage&service=WCS&version=2.0.1" + "&coverageId=wcs__dummyView&format=application/x-netcdf4&subset=http://www.opengis.net/def/axis/OGC/0/time(\"2013-01-08T00:00:00.000Z\")"); assertNotNull(response); assertEquals((isNC4Available ? "application/x-netcdf4" : "application/xml"), response.getContentType()); if (isNC4Available) { byte[] netcdfOut = getBinary(response); File file = File.createTempFile("netcdf", "out.nc", new File("./target")); FileUtils.writeByteArrayToFile(file, netcdfOut); NetcdfDataset dataset = NetcdfDataset.openDataset(file.getAbsolutePath()); assertNotNull(dataset); dataset.close(); } } @Test public void testRequestNetCDFUomConversion() throws Exception { // http response from the request inside the string CoverageInfo info = getCatalog().getCoverageByName(new NameImpl("wcs", "visibilityCF")); assertTrue(info.getDimensions().get(0).getUnit().equalsIgnoreCase(ORIGINAL_UNIT)); MockHttpServletResponse response = getAsServletResponse("ows?request=GetCoverage&service=WCS&version=2.0.1" + "&coverageId=wcs__visibilityCF&format=application/x-netcdf"); assertNotNull(response); byte[] netcdfOut = getBinary(response); File file = File.createTempFile("netcdf", "outCF.nc", new File("./target")); FileUtils.writeByteArrayToFile(file, netcdfOut); NetcdfDataset dataset = NetcdfDataset.openDataset(file.getAbsolutePath()); Variable var = dataset.findVariable(STANDARD_NAME); assertNotNull(var); // Check the unit has been converted to meter String unit = var.getUnitsString(); assertEquals(CANONICAL_UNIT, unit); Array readData = var.read(NETCDF_SECTION); assertEquals(DataType.FLOAT, readData.getDataType()); float data = readData.getFloat(0); // Data have been converted to canonical unit (m) from km. // Data value is bigger assertEquals(data, (ORIGINAL_PIXEL_VALUE) * 1000, DELTA); Attribute fillValue = var.findAttribute(NetCDFUtilities.FILL_VALUE); Attribute standardName = var.findAttribute(NetCDFUtilities.STANDARD_NAME); assertNotNull(standardName); assertEquals(STANDARD_NAME, standardName.getStringValue()); assertNotNull(fillValue); assertEquals(ORIGINAL_FILL_VALUE, fillValue.getNumericValue().doubleValue(), DELTA); // Check global attributes have been added Attribute attribute = dataset.findGlobalAttribute("custom_attribute"); assertNotNull(attribute); assertEquals("testing WCS", attribute.getStringValue()); dataset.close(); } @Test public void testRequestNetCDFCompression() throws Exception { boolean isNC4Available = NetCDFUtilities.isNC4CAvailable(); if (!isNC4Available && LOGGER.isLoggable(Level.INFO)) { LOGGER.info("NetCDF C library not found. NetCDF4 output will not be created"); } // http response from the request inside the string MockHttpServletResponse response = getAsServletResponse("ows?request=GetCoverage&service=WCS&version=2.0.1" + "&coverageId=wcs__visibilityCompressed&format=application/x-netcdf4"); assertNotNull(response); assertEquals((isNC4Available ? "application/x-netcdf4" : "application/xml"), response.getContentType()); if (isNC4Available) { byte[] netcdfOut = getBinary(response); File file = File.createTempFile("netcdf", "outCompressed.nc", new File("./target")); FileUtils.writeByteArrayToFile(file, netcdfOut); NetcdfDataset dataset = NetcdfDataset.openDataset(file.getAbsolutePath()); assertNotNull(dataset); Variable var = dataset.findVariable(STANDARD_NAME); assertNotNull(var); final long varByteSize = var.getSize() * var.getDataType().getSize(); // The output file is smaller than the size of the underlying variable. // Compression successfully occurred assertTrue(netcdfOut.length < varByteSize); dataset.close(); } } @Test public void testRequestNetCDFDataPacking() throws Exception { // http response from the request inside the string MockHttpServletResponse response = getAsServletResponse("ows?request=GetCoverage&service=WCS&version=2.0.1" + "&coverageId=wcs__visibilityPacked&format=application/x-netcdf"); assertNotNull(response); byte[] netcdfOut = getBinary(response); File file = File.createTempFile("netcdf", "outPK.nc", new File("./target")); FileUtils.writeByteArrayToFile(file, netcdfOut); NetcdfDataset dataset = NetcdfDataset.openDataset(file.getAbsolutePath()); Variable var = dataset.findVariable(STANDARD_NAME); assertNotNull(var); // Check the unit hasn't been converted String unit = var.getUnitsString(); assertEquals(ORIGINAL_UNIT, unit); Attribute fillValue = var.findAttribute(NetCDFUtilities.FILL_VALUE); assertNotNull(fillValue); // There is dataPacking, therefore, fillValue should have been changed assertEquals(PACKED_FILL_VALUE, fillValue.getNumericValue().doubleValue(), 1E-6); Attribute addOffsetAttr = var.findAttribute(DataPacking.ADD_OFFSET); assertNotNull(addOffsetAttr); Attribute scaleFactorAttr = var.findAttribute(DataPacking.SCALE_FACTOR); assertNotNull(scaleFactorAttr); double scaleFactor = scaleFactorAttr.getNumericValue().doubleValue(); double addOffset = addOffsetAttr.getNumericValue().doubleValue(); Array readData = var.read(NETCDF_SECTION); assertEquals(DataType.SHORT, readData.getDataType()); short data = readData.getShort(0); // Data has been packed to short double packedData = (ORIGINAL_PIXEL_VALUE - addOffset) / scaleFactor; assertEquals((short)(packedData + 0.5), data , DELTA); dataset.close(); } @Test public void testRequestNetCDFCFDataPacking() throws Exception { // http response from the request inside the string MockHttpServletResponse response = getAsServletResponse("ows?request=GetCoverage&service=WCS&version=2.0.1" + "&coverageId=wcs__visibilityCFPacked&format=application/x-netcdf"); assertNotNull(response); byte[] netcdfOut = getBinary(response); File file = File.createTempFile("netcdf", "outCFPK.nc", new File("./target")); FileUtils.writeByteArrayToFile(file, netcdfOut); NetcdfDataset dataset = NetcdfDataset.openDataset(file.getAbsolutePath()); Variable var = dataset.findVariable(STANDARD_NAME); assertNotNull(var); // Check the unit has been converted to meter String unit = var.getUnitsString(); assertEquals(CANONICAL_UNIT, unit); Attribute addOffsetAttr = var.findAttribute(DataPacking.ADD_OFFSET); assertNotNull(addOffsetAttr); Attribute scaleFactorAttr = var.findAttribute(DataPacking.SCALE_FACTOR); assertNotNull(scaleFactorAttr); double scaleFactor = scaleFactorAttr.getNumericValue().doubleValue(); double addOffset = addOffsetAttr.getNumericValue().doubleValue(); Array readData = var.read(NETCDF_SECTION); assertEquals(DataType.SHORT, readData.getDataType()); short data = readData.getShort(0); // Data has been packed to short // Going from original unit to canonical, then packing double packedData = ((ORIGINAL_PIXEL_VALUE * 1000) - addOffset) / scaleFactor; assertEquals((short)(packedData + 0.5), data , DELTA); Attribute fillValue = var.findAttribute(NetCDFUtilities.FILL_VALUE); assertNotNull(fillValue); // There is dataPacking, therefore, fillValue should have been changed assertEquals(PACKED_FILL_VALUE, fillValue.getNumericValue().doubleValue(), DELTA); // Check global attributes have been added Attribute attribute = dataset.findGlobalAttribute("custom_attribute"); assertNotNull(attribute); assertEquals("testing WCS", attribute.getStringValue()); dataset.close(); } private void addViewToCatalog() throws Exception { final Catalog cat = getCatalog(); final CoverageStoreInfo storeInfo = cat.getCoverageStoreByName(DUMMYMOSAIC.getLocalPart()); final CatalogBuilder builder = new CatalogBuilder(cat); builder.setStore(storeInfo); final CoverageInfo coverageInfo = coverageView.createCoverageInfo("dummyView", storeInfo, builder); coverageInfo.getParameters().put("USE_JAI_IMAGEREAD","false"); cat.add(coverageInfo); final LayerInfo layerInfo = builder.buildLayer(coverageInfo); cat.add(layerInfo); setupRasterDimension("dummyView", ResourceInfo.TIME, DimensionPresentation.LIST, null); } private void createCoverageView() throws Exception { final InputCoverageBand band1 = new InputCoverageBand("NO2", "0"); final CoverageBand outputBand1 = new CoverageBand(Collections.singletonList(band1), "NO2@0", 0, CompositionType.BAND_SELECT); final InputCoverageBand band2 = new InputCoverageBand("BrO", "0"); final CoverageBand outputBand2 = new CoverageBand(Collections.singletonList(band2), "BrO@0", 1, CompositionType.BAND_SELECT); final List<CoverageBand> coverageBands = new ArrayList<CoverageBand>(2); coverageBands.add(outputBand1); coverageBands.add(outputBand2); coverageView = new CoverageView("dummyView", coverageBands); } /** * Configure NetCDF output settings for <code>Temperature_surface</code>. */ private void configureTemperatureSurface() { NetCDFLayerSettingsContainer container = new NetCDFLayerSettingsContainer(); container.setCopyAttributes(true); List<VariableAttribute> variableAttributes = new ArrayList<VariableAttribute>(); variableAttributes .add(new VariableAttribute("test-variable-attribute", "Test Variable Attribute")); variableAttributes.add(new VariableAttribute("Grib2_Parameter_Category", "Test Category")); container.setVariableAttributes(variableAttributes); List<ExtraVariable> extraVariables = new ArrayList<ExtraVariable>(); extraVariables.add(new ExtraVariable("reftime", "forecast_reference_time", "time")); extraVariables.add(new ExtraVariable("reftime", "scalar_forecast_reference_time", "")); container.setExtraVariables(extraVariables); List<GlobalAttribute> globalAttributes = new ArrayList<GlobalAttribute>(); globalAttributes.add(new GlobalAttribute("test-global-attribute", "Test Global Attribute")); globalAttributes.add(new GlobalAttribute("test-global-attribute-integer", "42")); globalAttributes.add(new GlobalAttribute("test-global-attribute-double", "1.5")); container.setGlobalAttributes(globalAttributes); CoverageInfo info = getCatalog().getCoverageByName(getLayerId(TEMPERATURE_SURFACE)); info.getMetadata().put(NetCDFSettingsContainer.NETCDFOUT_KEY, container); getCatalog().save(info); } /** * Test <code>Temperature_surface</code> extra variables, variable attributes, and global attributes of different types, for NetCDF-3 output. */ @Test public void testExtraVariablesNetcdf3() throws Exception { checkExtraVariables("application/x-netcdf"); } /** * Test <code>Temperature_surface</code> extra variables, variable attributes, and global attributes of different types, for NetCDF-4 output. */ @Test public void testExtraVariablesNetcdf4() throws Exception { assumeTrue(NetCDFUtilities.isNC4CAvailable()); checkExtraVariables("application/x-netcdf4"); } /** * Check <code>Temperature_surface</code> extra variables, variable attributes, and global attributes of different type. * * @param format the output format MIME type */ private void checkExtraVariables(String format) throws Exception { MockHttpServletResponse response = getAsServletResponse( "wcs?service=WCS&version=2.0.1&request=GetCoverage" + "&coverageid=wcs__Temperature_surface&format=" + format); assertNotNull(response); assertEquals(200, response.getStatus()); assertEquals(format, response.getContentType()); byte[] responseBytes = getBinary(response); File file = File.createTempFile("extra-variable-", "-wcs__Temperature_surface.nc", new File("./target")); FileUtils.writeByteArrayToFile(file, responseBytes); try (NetcdfDataset dataset = NetcdfDataset.openDataset(file.getAbsolutePath())) { assertNotNull(dataset); // check dimensions Dimension timeDim = dataset.findDimension("time"); assertNotNull(timeDim); assertEquals(2, timeDim.getLength()); Dimension rlonDim = dataset.findDimension("rlon"); assertNotNull(rlonDim); assertEquals(7, rlonDim.getLength()); Dimension rlatDim = dataset.findDimension("rlat"); assertNotNull(rlatDim); assertEquals(5, rlatDim.getLength()); // check coordinate variables Variable timeVar = dataset.findVariable("time"); assertNotNull(timeVar); assertEquals(1, timeVar.getDimensions().size()); assertEquals(timeDim, timeVar.getDimensions().get(0)); assertEquals("time", timeVar.findAttribute("long_name").getStringValue()); assertEquals("time", timeVar.findAttribute("description").getStringValue()); assertEquals("seconds since 1970-01-01 00:00:00 UTC", timeVar.findAttribute("units").getStringValue()); assertArrayEquals(new double[] { 1461664800, 1461708000 }, (double[]) timeVar.read().copyTo1DJavaArray(), (double) DELTA); Variable rlonVar = dataset.findVariable("rlon"); assertNotNull(rlonVar); assertEquals(1, rlonVar.getDimensions().size()); assertEquals(rlonDim, rlonVar.getDimensions().get(0)); assertEquals("grid_longitude", rlonVar.findAttribute("long_name").getStringValue()); assertEquals("grid_longitude", rlonVar.findAttribute("standard_name").getStringValue()); assertEquals("degrees", rlonVar.findAttribute("units").getStringValue()); assertArrayEquals(new float[] { -30, -20, -10, 0, 10, 20, 30 }, (float[]) rlonVar.read().copyTo1DJavaArray(), (float) DELTA); Variable rlatVar = dataset.findVariable("rlat"); assertNotNull(rlatVar); assertEquals(1, rlatVar.getDimensions().size()); assertEquals(rlatDim, rlatVar.getDimensions().get(0)); assertEquals("grid_latitude", rlatVar.findAttribute("long_name").getStringValue()); assertEquals("grid_latitude", rlatVar.findAttribute("standard_name").getStringValue()); assertEquals("degrees", rlatVar.findAttribute("units").getStringValue()); assertArrayEquals(new float[] { -20, -10, 0, 10, 20 }, (float[]) rlatVar.read().copyTo1DJavaArray(), (float) DELTA); // check projection variable Variable projVar = dataset.findVariable("rotated_latitude_longitude"); assertNotNull(projVar); assertEquals("rotated_latitude_longitude", projVar.findAttribute("grid_mapping_name").getStringValue()); assertEquals(74.0, projVar.findAttribute("grid_north_pole_longitude").getNumericValue() .doubleValue(), DELTA); assertEquals(36.0, projVar.findAttribute("grid_north_pole_latitude").getNumericValue() .doubleValue(), DELTA); // check data variable Variable tempVar = dataset.findVariable("Temperature_surface"); assertNotNull(tempVar); assertEquals("rotated_latitude_longitude", tempVar.findAttribute("grid_mapping").getStringValue()); assertEquals("K", tempVar.findAttribute("units").getStringValue()); assertEquals(3, tempVar.getDimensions().size()); assertEquals(timeDim, tempVar.getDimensions().get(0)); assertEquals(rlatDim, tempVar.getDimensions().get(1)); assertEquals(rlonDim, tempVar.getDimensions().get(2)); assertArrayEquals(new float[] { 300, 299, 298, 297, 296, 295, 294, 299, 300, 299, 298, 297, 296, 295, 298, 299, 300, 299, 298, 297, 296, 297, 298, 299, 300, 299, 298, 297, 296, 297, 298, 299, 300, 299, 298, 301, 300, 299, 298, 297, 296, 295, 300, 301, 300, 299, 298, 297, 296, 299, 300, 301, 300, 299, 298, 297, 298, 299, 300, 301, 300, 299, 298, 297, 298, 299, 300, 301, 300, 299 }, (float[]) tempVar.read().copyTo1DJavaArray(), (float) DELTA); // some attributes expected to copied from source variable assertEquals("TMP", tempVar.findAttribute("abbreviation").getStringValue()); assertEquals("Forecast", tempVar.findAttribute("Grib2_Generating_Process_Type").getStringValue()); // should not be copied from source variable as in the blacklist assertNull(tempVar.findAttribute("coordinates")); // test that copied variable attributes can be overwritten assertEquals("Test Category", tempVar.findAttribute("Grib2_Parameter_Category").getStringValue()); // test that a new variable attribute can be added assertEquals("Test Variable Attribute", tempVar.findAttribute("test-variable-attribute").getStringValue()); // extra variable copied from source with dimensions "time" Variable reftimeVar = dataset.findVariable("forecast_reference_time"); assertEquals(1, reftimeVar.getDimensions().size()); assertEquals(timeDim, reftimeVar.getDimensions().get(0)); assertEquals("Hour since 2016-04-25T22:00:00Z", reftimeVar.findAttribute("units").getStringValue()); assertEquals("forecast_reference_time", reftimeVar.findAttribute("standard_name").getStringValue()); assertEquals("GRIB reference time", reftimeVar.findAttribute("long_name").getStringValue()); assertArrayEquals(new double[] { 6, 3 }, (double[]) reftimeVar.read().copyTo1DJavaArray(), (double) DELTA); // scalar extra variable copied from source with dimensions "" Variable scalarReftimeVar = dataset.findVariable("scalar_forecast_reference_time"); assertEquals(0, scalarReftimeVar.getDimensions().size()); assertEquals("Hour since 2016-04-25T22:00:00Z", scalarReftimeVar.findAttribute("units").getStringValue()); assertEquals("forecast_reference_time", scalarReftimeVar.findAttribute("standard_name").getStringValue()); assertEquals("GRIB reference time", scalarReftimeVar.findAttribute("long_name").getStringValue()); double t = scalarReftimeVar.read().getDouble(0); // the value is nondeterministic because it depends // on which of two granules is used as the sample assertTrue(t == 6 || t == 3); // string global attribute assertEquals("Test Global Attribute", dataset.findGlobalAttribute("test-global-attribute").getStringValue()); // integer global attribute assertEquals(DataType.INT, dataset.findGlobalAttribute("test-global-attribute-integer").getDataType()); assertEquals(42, dataset.findGlobalAttribute("test-global-attribute-integer").getNumericValue()); // double global attribute assertEquals(DataType.DOUBLE, dataset.findGlobalAttribute("test-global-attribute-double").getDataType()); assertEquals(1.5, dataset.findGlobalAttribute("test-global-attribute-double").getNumericValue()); } finally { FileUtils.deleteQuietly(file); } } }