/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2015 - 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.netcdf;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.geotools.coverage.io.netcdf.crs.NetCDFCRSAuthorityFactory;
import org.geotools.coverage.io.netcdf.crs.NetCDFCoordinateReferenceSystemType;
import org.geotools.coverage.io.netcdf.crs.NetCDFProjection;
import org.geotools.coverage.io.netcdf.crs.ProjectionBuilder;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.imageio.netcdf.utilities.NetCDFUtilities;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.referencing.operation.DefiningConversion;
import org.geotools.referencing.operation.projection.AlbersEqualArea;
import org.geotools.referencing.operation.projection.LambertConformal1SP;
import org.geotools.referencing.operation.projection.MapProjection.AbstractProvider;
import org.geotools.referencing.operation.projection.RotatedPole;
import org.geotools.referencing.operation.projection.TransverseMercator;
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.ParameterValueGroup;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.GeographicCRS;
import org.opengis.referencing.crs.ProjectedCRS;
import org.opengis.referencing.datum.Ellipsoid;
import org.opengis.referencing.datum.GeodeticDatum;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.Projection;
/**
* Testing NetCDF Projection management machinery
*
* @author Daniele Romagnoli, GeoSolutions SAS
*/
public class NetCDFCRSTest extends Assert {
private final static double DELTA = 1E-6;
/**
* Sets up the custom definitions
*/
@Before
public void setUp() throws Exception {
String netcdfPropertiesPath = TestData.file(this, "netcdf.projections.properties").getCanonicalPath();
System.setProperty(NetCDFCRSAuthorityFactory.SYSTEM_DEFAULT_USER_PROJ_FILE, netcdfPropertiesPath);
}
@Test
public void testUTMDatasetSpatialRef() throws Exception {
final File file = TestData.file(this, "utm.nc");
NetCDFReader reader = null;
try {
reader = new NetCDFReader(file, null);
String[] coverages = reader.getGridCoverageNames();
CoordinateReferenceSystem crs = reader.getCoordinateReferenceSystem(coverages[0]);
assertTrue(crs instanceof ProjectedCRS);
ProjectedCRS projectedCRS = ((ProjectedCRS) crs);
GeographicCRS baseCRS = projectedCRS.getBaseCRS();
// Dealing with SPATIAL_REF Attribute
assertTrue(CRS.equalsIgnoreMetadata(baseCRS, DefaultGeographicCRS.WGS84));
Projection projection = projectedCRS.getConversionFromBase();
MathTransform transform = projection.getMathTransform();
assertTrue(transform instanceof TransverseMercator);
} finally {
if (reader != null) {
try {
reader.dispose();
} catch (Throwable t) {
// Does nothing
}
}
}
}
@Test
public void testUTMDatasetNoCode() throws Exception {
final File file = TestData.file(this, "utmnocode.nc");
NetCDFReader reader = null;
try {
reader = new NetCDFReader(file, null);
String[] coverages = reader.getGridCoverageNames();
CoordinateReferenceSystem crs = reader.getCoordinateReferenceSystem(coverages[0]);
assertTrue(crs instanceof ProjectedCRS);
ProjectedCRS projectedCRS = ((ProjectedCRS) crs);
Projection projection = projectedCRS.getConversionFromBase();
MathTransform transform = projection.getMathTransform();
assertTrue(transform instanceof TransverseMercator);
// Check the proper CRS Type has been recognized
NetCDFCoordinateReferenceSystemType crsType = NetCDFCoordinateReferenceSystemType.parseCRS(crs);
assertSame(NetCDFCoordinateReferenceSystemType.TRANSVERSE_MERCATOR, crsType);
assertSame(NetCDFCoordinateReferenceSystemType.NetCDFCoordinate.YX_COORDS, crsType.getCoordinates());
assertSame(NetCDFProjection.TRANSVERSE_MERCATOR, crsType.getNetCDFProjection());
} finally {
if (reader != null) {
try {
reader.dispose();
} catch (Throwable t) {
// Does nothing
}
}
}
}
@Test
public void testAlbersEqualAreaDataset() throws Exception {
final File file = TestData.file(this, "albersequal.nc");
NetCDFReader reader = null;
try {
reader = new NetCDFReader(file, null);
String[] coverages = reader.getGridCoverageNames();
CoordinateReferenceSystem crs = reader.getCoordinateReferenceSystem(coverages[0]);
assertTrue(crs instanceof ProjectedCRS);
ProjectedCRS projectedCRS = ((ProjectedCRS) crs);
Projection projection = projectedCRS.getConversionFromBase();
MathTransform transform = projection.getMathTransform();
assertTrue(transform instanceof AlbersEqualArea);
// Check the proper CRS Type has been recognized
NetCDFCoordinateReferenceSystemType crsType = NetCDFCoordinateReferenceSystemType.parseCRS(crs);
assertSame(NetCDFCoordinateReferenceSystemType.ALBERS_EQUAL_AREA, crsType);
assertSame(NetCDFCoordinateReferenceSystemType.NetCDFCoordinate.YX_COORDS, crsType.getCoordinates());
assertSame(NetCDFProjection.ALBERS_EQUAL_AREA, crsType.getNetCDFProjection());
} finally {
if (reader != null) {
try {
reader.dispose();
} catch (Throwable t) {
// Does nothing
}
}
}
}
@Test
public void testRotatedPoleDataset() throws Exception {
final File file = TestData.file(this, "rotated-pole.nc");
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(-106.0, values.parameter(NetCDFUtilities.CENTRAL_MERIDIAN).doubleValue(),
DELTA);
assertEquals(54.0, values.parameter(NetCDFUtilities.LATITUDE_OF_ORIGIN).doubleValue(),
DELTA);
} finally {
if (reader != null) {
reader.dispose();
}
}
}
@Test
public void testProjectionSetup() throws Exception {
ParameterValueGroup params = ProjectionBuilder.getProjectionParameters(NetCDFProjection.LAMBERT_CONFORMAL_CONIC_1SP.getOGCName());
params.parameter("central_meridian").setValue(-95.0);
params.parameter("latitude_of_origin").setValue(25.0);
params.parameter("scale_factor").setValue(1.0);
params.parameter("false_easting").setValue(0.0);
params.parameter("false_northing").setValue(0.0);
Map<String, Number> ellipsoidParams = new HashMap<String, Number>();
ellipsoidParams.put(NetCDFUtilities.SEMI_MAJOR, 6378137);
ellipsoidParams.put(NetCDFUtilities.INVERSE_FLATTENING, 298.257223563);
Ellipsoid ellipsoid = ProjectionBuilder.createEllipsoid("WGS 84", ellipsoidParams);
ProjectionBuilder.updateEllipsoidParams(params, ellipsoid);
GeodeticDatum datum = ProjectionBuilder.createGeodeticDatum("WGS_1984", ellipsoid);
GeographicCRS geoCRS = ProjectionBuilder.createGeographicCRS("WGS 84", datum);
MathTransform transform = ProjectionBuilder.createTransform(params);
DefiningConversion conversionFromBase = ProjectionBuilder.createConversionFromBase("lambert_conformal_mercator_1sp", transform);
CoordinateReferenceSystem crs = ProjectionBuilder.createProjectedCRS(Collections.singletonMap("name", "custom_lambert_conformal_conic_1sp"), geoCRS, conversionFromBase, transform);
assertTrue(crs instanceof ProjectedCRS);
ProjectedCRS projectedCRS = ((ProjectedCRS) crs);
GeographicCRS baseCRS = projectedCRS.getBaseCRS();
assertTrue(CRS.equalsIgnoreMetadata(baseCRS, DefaultGeographicCRS.WGS84));
assertTrue(transform instanceof LambertConformal1SP);
}
@Test
public void testDefaultDatumSetup() throws Exception {
ParameterValueGroup params = ProjectionBuilder.getProjectionParameters(NetCDFProjection.LAMBERT_CONFORMAL_CONIC_1SP.getOGCName());
params.parameter("central_meridian").setValue(-95.0);
params.parameter("latitude_of_origin").setValue(25.0);
params.parameter("scale_factor").setValue(1.0);
params.parameter("false_easting").setValue(0.0);
params.parameter("false_northing").setValue(0.0);
// Intentionally left empty
Map<String, Number> ellipsoidParams = new HashMap<String, Number>();
Ellipsoid ellipsoid = ProjectionBuilder.createEllipsoid("Unknown", ellipsoidParams);
ProjectionBuilder.updateEllipsoidParams(params, ellipsoid);
assertEquals(NetCDFUtilities.DEFAULT_EARTH_RADIUS, ellipsoid.getSemiMajorAxis(), DELTA);
assertEquals(NetCDFUtilities.DEFAULT_EARTH_RADIUS, ellipsoid.getSemiMinorAxis(), DELTA);
assertTrue(Double.isInfinite(ellipsoid.getInverseFlattening()));
}
@Test
public void testMultipleBoundingBoxesSupport() throws IOException, FactoryException {
final File testURL = TestData.file(this, "dualbbox.nc");
final NetCDFReader reader = new NetCDFReader(testURL, null);
assertNotNull(reader);
try {
String[] names = reader.getGridCoverageNames();
assertNotNull(names);
assertEquals(2, names.length);
assertEquals(names[0], "sample1");
assertEquals(names[1], "sample2");
GeneralEnvelope envelope1 = reader.getOriginalEnvelope("sample1");
GeneralEnvelope envelope2 = reader.getOriginalEnvelope("sample2");
// Check the envelopes are different
assertEquals(52000, envelope1.getMinimum(0), DELTA);
assertEquals(-78000, envelope1.getMinimum(1), DELTA);
assertEquals(68000, envelope1.getMaximum(0), DELTA);
assertEquals(-62000, envelope1.getMaximum(1), DELTA);
assertEquals(-58000, envelope2.getMinimum(0), DELTA);
assertEquals(0, envelope2.getMinimum(1), DELTA);
assertEquals(-46000, envelope2.getMaximum(0), DELTA);
assertEquals(12000, envelope2.getMaximum(1), DELTA);
// Check the envelopes have different span
assertEquals(16000, envelope1.getSpan(0), DELTA);
assertEquals(16000, envelope1.getSpan(1), DELTA);
assertEquals(12000, envelope2.getSpan(0), DELTA);
assertEquals(12000, envelope2.getSpan(1), DELTA);
// Double check on gridRanges
GridEnvelope gridRange1 = reader.getOriginalGridRange("sample1");
GridEnvelope gridRange2 = reader.getOriginalGridRange("sample2");
// Check the samples are 4x4 and 3x3 respectively
assertEquals(4, gridRange1.getSpan(0));
assertEquals(4, gridRange1.getSpan(1));
assertEquals(3, gridRange2.getSpan(0));
assertEquals(3, gridRange2.getSpan(1));
} finally {
if (reader != null) {
try {
reader.dispose();
} catch (Throwable t) {
// Does nothing
}
}
}
}
@Test
public void testMultipleBoundingBoxesAuxiliaryCoordinatesSupport() throws IOException, FactoryException {
final File testURL = TestData.file(this, "dualbboxAuxiliaryCoordinates.nc");
final NetCDFReader reader = new NetCDFReader(testURL, null);
assertNotNull(reader);
try {
String[] names = reader.getGridCoverageNames();
assertNotNull(names);
assertEquals(2, names.length);
assertEquals(names[0], "sample1");
assertEquals(names[1], "sample2");
GeneralEnvelope envelope1 = reader.getOriginalEnvelope("sample1");
GeneralEnvelope envelope2 = reader.getOriginalEnvelope("sample2");
// Check the envelopes are different
assertEquals(52000, envelope1.getMinimum(0), DELTA);
assertEquals(-78000, envelope1.getMinimum(1), DELTA);
assertEquals(68000, envelope1.getMaximum(0), DELTA);
assertEquals(-62000, envelope1.getMaximum(1), DELTA);
assertEquals(-58000, envelope2.getMinimum(0), DELTA);
assertEquals(0, envelope2.getMinimum(1), DELTA);
assertEquals(-46000, envelope2.getMaximum(0), DELTA);
assertEquals(12000, envelope2.getMaximum(1), DELTA);
// Check the envelopes have different span
assertEquals(16000, envelope1.getSpan(0), DELTA);
assertEquals(16000, envelope1.getSpan(1), DELTA);
assertEquals(12000, envelope2.getSpan(0), DELTA);
assertEquals(12000, envelope2.getSpan(1), DELTA);
// Double check on gridRanges
GridEnvelope gridRange1 = reader.getOriginalGridRange("sample1");
GridEnvelope gridRange2 = reader.getOriginalGridRange("sample2");
// Check the samples are 4x4 and 3x3 respectively
assertEquals(4, gridRange1.getSpan(0));
assertEquals(4, gridRange1.getSpan(1));
assertEquals(3, gridRange2.getSpan(0));
assertEquals(3, gridRange2.getSpan(1));
} finally {
if (reader != null) {
try {
reader.dispose();
} catch (Throwable t) {
// Does nothing
}
}
}
}
/**
* Test that an unsupported grid_mapping_name falls back to WGS 84.
*/
@Test
public void testUnsupportedGridMappingName() throws IOException {
File file = TestData.file(this, "unsupported-grid-mapping-name.nc");
NetCDFReader reader = new NetCDFReader(file, null);
assertNotNull(reader);
assertTrue(CRS.equalsIgnoreMetadata(reader.getCoordinateReferenceSystem(),
DefaultGeographicCRS.WGS84));
assertTrue(CRS.equalsIgnoreMetadata(reader.getCoordinateReferenceSystem("foo"),
DefaultGeographicCRS.WGS84));
}
/**
* Cleanup the custom definitions
*/
@After
public void cleanUpDefinitions() throws Exception {
System.clearProperty(NetCDFCRSAuthorityFactory.SYSTEM_DEFAULT_USER_PROJ_FILE);
}
}