/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2007 - 2016, 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.grib; import java.awt.geom.Point2D; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.net.MalformedURLException; import java.util.Arrays; import java.util.List; import javax.imageio.spi.ImageReaderSpi; import org.apache.commons.io.FileUtils; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.coverage.grid.GridGeometry2D; import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader; import org.geotools.coverage.grid.io.AbstractGridFormat; import org.geotools.coverage.grid.io.GridFormatFinder; import org.geotools.coverage.io.netcdf.NetCDFDriver; import org.geotools.coverage.io.netcdf.NetCDFReader; import org.geotools.coverage.io.netcdf.crs.NetCDFCoordinateReferenceSystemType; import org.geotools.coverage.io.netcdf.crs.NetCDFProjection; import org.geotools.data.DataSourceException; import org.geotools.geometry.DirectPosition2D; import org.geotools.geometry.GeneralEnvelope; import org.geotools.imageio.netcdf.NetCDFImageReaderSpi; import org.geotools.imageio.netcdf.utilities.NetCDFUtilities; import org.geotools.referencing.operation.projection.RotatedPole; import org.geotools.test.TestData; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.opengis.coverage.grid.GridEnvelope; import org.opengis.parameter.GeneralParameterValue; import org.opengis.parameter.ParameterValue; import org.opengis.parameter.ParameterValueGroup; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.crs.ProjectedCRS; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.Projection; import ucar.nc2.Attribute; import ucar.nc2.Variable; import ucar.nc2.dataset.NetcdfDataset; /** * Unit test for testing Grib data. */ public class GribTest extends Assert{ private final static Double DELTA = 1E-9; protected File cacheDir; @Before public void setup() throws FileNotFoundException, IOException { final File testDir = TestData.file(this, ".").getCanonicalFile(); cacheDir = new File(testDir, "cache"); if (!cacheDir.exists()) { cacheDir.mkdir(); } String cacheDirPath = cacheDir.getAbsolutePath(); System.setProperty("GRIB_CACHE_DIR", cacheDirPath); } @Test public void testWeirdGribFileCanBeOpened() throws FileNotFoundException, IOException { final String referenceDir = "testGrib"; final File workDir = new File(TestData.file(this, "."), referenceDir); if (!workDir.mkdir()) { FileUtils.deleteDirectory(workDir); assertTrue("Unable to create workdir:" + workDir, workDir.mkdir()); } final File zipFile = new File(workDir, "weird.zip"); FileUtils.copyFile(TestData.file(this, "weird.zip"), zipFile); TestData.unzipFile(this, referenceDir + "/weird.zip"); // This file is a valid GRIB file but it has wrong magic number and // it doesn't have a recognized grib extension final File file = new File(workDir, "weird.model"); NetCDFImageReaderSpi spi = new NetCDFImageReaderSpi(); assertTrue(spi.canDecodeInput(file)); } @Test public void testGribEarthShape() throws FileNotFoundException, IOException { final String referenceDir = "earthShape"; final File workDir = new File(TestData.file(this, "."), referenceDir); if (!workDir.mkdir()) { FileUtils.deleteDirectory(workDir); assertTrue("Unable to create workdir:" + workDir, workDir.mkdir()); } final File zipFile = new File(workDir, "TT_FC_INCA_EarthShape.zip"); FileUtils.copyFile(TestData.file(this, "TT_FC_INCA_EarthShape.zip"), zipFile); TestData.unzipFile(this, referenceDir + "/TT_FC_INCA_EarthShape.zip"); final File file = new File(workDir, "TT_FC_INCA_EarthShape.grb2"); NetcdfDataset dataset = NetcdfDataset.openDataset(file.getAbsolutePath()); Variable var = dataset.findVariable(null, "LambertConformal_Projection"); assertNotNull(var); // Before switching to NetCDF 4.6.2 there was a bug which was returning // semi_major_axis = 6.377397248E9 and inverse_flattening = 299.15349328722255; // https://github.com/Unidata/thredds/issues/133 // Checking that it's now reporting proper values Attribute attribute = var.findAttribute("semi_major_axis"); assertNotNull(attribute); assertEquals(6377397.0, attribute.getNumericValue().doubleValue(), DELTA); attribute = var.findAttribute("inverse_flattening"); assertNotNull(attribute); assertEquals(299.15550239234693, attribute.getNumericValue().doubleValue(), DELTA); } /** * Test if the Grib format is accepted by the NetCDF reader * * @throws IOException * @throws FileNotFoundException */ @Test public void testGribFormat() throws FileNotFoundException, IOException { // Selection of the input file final File file = TestData.file(this, "sampleGrib.grb2"); // Creation of a NetCDFFormat object for checking if the GRIB format is accepted GRIBFormat format = new GRIBFormat(); // Check if the format is accepted Assert.assertTrue(format.accepts(file)); // Creation of a NetCDF reader SPI object for checking if it can decode the input file ImageReaderSpi spi = new NetCDFImageReaderSpi(); // Check if the file can be decoded Assert.assertTrue(spi.canDecodeInput(file)); // Check not a grib file final File fileTif = TestData.file(this, "sample.tif"); Assert.assertFalse(format.accepts(fileTif)); } /** * Test if the Grib extension is supported by the NetCDF reader * * @throws IOException * @throws FileNotFoundException */ @Test public void testGribExtension() { // Creation of a NetCDFDriver NetCDFDriver driver = new NetCDFDriver(); // Selection of the extensions used by the NetCDF driver List<String> extensions = driver.getFileExtensions(); // Creation of a list of the grib extensions List<String> possibleExt = Arrays.asList(new String[] { "grb", "grb2", "grib" }); // Check if the extensions are contained Assert.assertTrue(extensions.containsAll(possibleExt)); // Creation of a NetCDFReaderSPI objectfor checking if it supports the GRIB extension ImageReaderSpi spi = new NetCDFImageReaderSpi(); // Selection of the suffixes, formatNames and mimetypes from the spi object List<String> suffixes = Arrays.asList(spi.getFileSuffixes()); List<String> formatNames = Arrays.asList(spi.getFormatNames()); List<String> MIMETypes = Arrays.asList(spi.getMIMETypes()); // Creation of similar lists containing the values for the grib format List<String> gribSuffixes = Arrays.asList(new String[] { "grib", "grb", "grb2" }); List<String> gribFormatNames = Arrays.asList(new String[] { "grib", "grib2", "GRIB", "GRIB2" }); List<String> gribMIMETypes = Arrays.asList(new String[] { "application/octet-stream" }); // Check if the grib informations are present Assert.assertTrue(suffixes.containsAll(gribSuffixes)); Assert.assertTrue(formatNames.containsAll(gribFormatNames)); Assert.assertTrue(MIMETypes.containsAll(gribMIMETypes)); } /** * Test on a Grib image. * * @throws DataSourceException * @throws MalformedURLException * @throws IOException */ @Test public void testGribImage() throws MalformedURLException, IOException { // Selection of the input file final File file = TestData.file(this, "sampleGrib.grb2"); // Testing the 2 points testGribFile(file, new Point2D.Double(304, 8), new Point2D.Double(304, 4)); } /** * Private method for checking if the selected point are nodata or not. This ensures that the images are flipped vertically only if needed. If the * image is not correctly flipped then, validpoint and nodatapoint should be inverted. * * @param inputFile * @param validPoint * @param nodataPoint * @throws MalformedURLException * @throws DataSourceException * @throws IOException */ private void testGribFile(final File inputFile, Point2D validPoint, Point2D nodataPoint) throws MalformedURLException, IOException { // Get format final AbstractGridFormat format = (AbstractGridFormat) GridFormatFinder.findFormat( inputFile.toURI().toURL(), null); assertTrue(format.accepts(inputFile)); AbstractGridCoverage2DReader reader = null; assertNotNull(format); try { reader = format.getReader(inputFile, null); assertNotNull(reader); // Selection of all the Coverage names String[] names = reader.getGridCoverageNames(); assertNotNull(names); // Selections of one Coverage GridCoverage2D grid = reader.read(names[0], null); assertNotNull(grid); // Selection of one coordinate from the Coverage and check if the // value is not a NaN float[] result = new float[1]; grid.evaluate(validPoint, result); Assert.assertTrue(!Float.isNaN(result[0])); // Selection of one coordinate from the Coverage and check if the // value is NaN float[] result_2 = new float[1]; grid.evaluate(nodataPoint, result_2); Assert.assertTrue(Float.isNaN(result_2[0])); } finally { // Dispose if (reader != null) { try { reader.dispose(); } catch (Throwable t) { // Does nothing } } } } /** * Test on a Grib image asking for a larger bounding box. * * @throws DataSourceException * @throws MalformedURLException * @throws IOException */ @Test public void testGribImageWithLargeBBOX() throws MalformedURLException, IOException { // Selection of the input file final File inputFile = TestData.file(this, "sampleGrib.grb2"); // Get format final AbstractGridFormat format = (AbstractGridFormat) GridFormatFinder.findFormat( inputFile.toURI().toURL(), null); assertTrue(format.accepts(inputFile)); AbstractGridCoverage2DReader reader = null; assertNotNull(format); try { reader = format.getReader(inputFile, null); assertNotNull(reader); // Selection of all the Coverage names String[] names = reader.getGridCoverageNames(); assertNotNull(names); // Name of the first coverage String coverageName = names[0]; // Parsing metadata values assertEquals("true", reader.getMetadataValue(coverageName, "HAS_TIME_DOMAIN")); // Expanding the envelope final ParameterValue<GridGeometry2D> gg = AbstractGridFormat.READ_GRIDGEOMETRY2D .createValue(); final GeneralEnvelope originalEnvelope = reader.getOriginalEnvelope(coverageName); final GeneralEnvelope newEnvelope = new GeneralEnvelope(originalEnvelope); newEnvelope.setCoordinateReferenceSystem(reader .getCoordinateReferenceSystem(coverageName)); newEnvelope.add(new DirectPosition2D(newEnvelope.getMinimum(0) - 10, newEnvelope .getMinimum(1) - 10)); // Selecting the same gridRange GridEnvelope gridRange = reader.getOriginalGridRange(coverageName); gg.setValue(new GridGeometry2D(gridRange, newEnvelope)); GeneralParameterValue[] values = new GeneralParameterValue[] { gg }; // Read with the larger BBOX GridCoverage2D grid = reader.read(coverageName, values); // Check if the result is not null assertNotNull(grid); } finally { // Dispose if (reader != null) { try { reader.dispose(); } catch (Throwable t) { // Does nothing } } } } /** * Check that a GRIB2 file is interpreted as a rotated pole projection with expected parameters. * * @param gribFileName name of the GRIB2 file * @param expectedCentralMeridian expected central meridian of rotated pole projection * @param expectedLatitudeOfOrigin expected latitude of origin of the rotated pole projection */ private void checkRotatedPole(String gribFileName, double expectedCentralMeridian, double expectedLatitudeOfOrigin) throws Exception { File file = TestData.file(this, gribFileName); NetCDFReader reader = null; try { reader = new NetCDFReader(file, null); String[] coverages = reader.getGridCoverageNames(); CoordinateReferenceSystem crs = reader.getCoordinateReferenceSystem(coverages[0]); NetCDFCoordinateReferenceSystemType crsType = NetCDFCoordinateReferenceSystemType .parseCRS(crs); assertSame(NetCDFCoordinateReferenceSystemType.ROTATED_POLE, crsType); assertSame(NetCDFCoordinateReferenceSystemType.NetCDFCoordinate.RLATLON_COORDS, crsType.getCoordinates()); assertSame(NetCDFProjection.ROTATED_POLE, crsType.getNetCDFProjection()); assertTrue(crs instanceof ProjectedCRS); ProjectedCRS projectedCRS = ((ProjectedCRS) crs); Projection projection = projectedCRS.getConversionFromBase(); MathTransform transform = projection.getMathTransform(); assertTrue(transform instanceof RotatedPole); RotatedPole rotatedPole = (RotatedPole) transform; ParameterValueGroup values = rotatedPole.getParameterValues(); assertEquals(expectedCentralMeridian, values.parameter(NetCDFUtilities.CENTRAL_MERIDIAN).doubleValue(), DELTA); assertEquals(expectedLatitudeOfOrigin, values.parameter(NetCDFUtilities.LATITUDE_OF_ORIGIN).doubleValue(), DELTA); } finally { if (reader != null) { reader.dispose(); } } } /** * Test that an RAP native GRIB2 file with GDS template 32769 is interpreted as a {@link RotatedPole} projection with expected parameters. */ @Test public void testRapNativeRotatedPole() throws Exception { checkRotatedPole("rap-native.grib2", -106, 54); } /** * Test that a COSMO EU GRIB2 file with GDS template 1 is interpreted as a {@link RotatedPole} projection with expected parameters. */ @Test public void testCosmoEuRotatedPole() throws Exception { checkRotatedPole("cosmo-eu.grib2", 10, 50); } }