Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Geotiff improvements 12 06 2020 #12

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@
<version>1.10.17</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.esa.snap</groupId>
<artifactId>snap-runtime</artifactId>
<version>${snap.test.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>

<!-- snap reader dependencies -->
<dependency>
Expand Down Expand Up @@ -220,6 +227,19 @@
<scope>test</scope>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.esa.snap</groupId>
<artifactId>snap-ndvi</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.esa.snap</groupId>
<artifactId>snap-ndvi</artifactId>
<version>${project.version}</version>
<scope>test</scope>
<type>test-jar</type>
</dependency>

<!-- s3tbx dependencies for snap reader tests -->
<dependency>
Expand Down Expand Up @@ -523,6 +543,19 @@
<scope>test</scope>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.esa.s2tbx</groupId>
<artifactId>s2tbx-radiometric-indices</artifactId>
<version>${s2tbx.test.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.esa.s2tbx</groupId>
<artifactId>s2tbx-radiometric-indices</artifactId>
<version>${s2tbx.test.version}</version>
<scope>test</scope>
<type>test-jar</type>
</dependency>

<!-- s1tbx dependencies for snap reader tests-->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public class ProductReaderAcceptanceTest {
private static final String PROPERTYNAME_FAIL_ON_MISSING_DATA = "snap.reader.tests.failOnMissingData";
private static final String PROPERTYNAME_FAIL_ON_INTENDED = "snap.reader.tests.failOnMultipleIntendedReaders";
private static final String PROPERTYNAME_LOG_FILE_PATH = "snap.reader.tests.log.file";
private static final String PROPERTYNAME_CASS_NAME = "snap.reader.tests.class.name";
private static final String PROPERTYNAME_CLASS_NAME = "snap.reader.tests.class.name";
private static final boolean FAIL_ON_MISSING_DATA = Boolean.parseBoolean(System.getProperty(PROPERTYNAME_FAIL_ON_MISSING_DATA, "true"));
private static final String INDENT = "\t";
private static final ProductList testProductList = new ProductList();
Expand Down Expand Up @@ -471,7 +471,7 @@ private static void loadProductReaderTestDefinitions() throws IOException {
final Iterable<ProductReaderPlugIn> readerPlugIns = SystemUtils.loadServices(ProductReaderPlugIn.class);
testDefinitionList = new TestDefinitionList();

final String className = System.getProperty(PROPERTYNAME_CASS_NAME);
final String className = System.getProperty(PROPERTYNAME_CLASS_NAME);

for (ProductReaderPlugIn readerPlugIn : readerPlugIns) {
final Class<? extends ProductReaderPlugIn> readerPlugInClass = readerPlugIn.getClass();
Expand Down
305 changes: 305 additions & 0 deletions src/test/java/org/esa/snap/dataio/RunProductReaderTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
package org.esa.snap.dataio;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.esa.s2tbx.dataio.gdal.GDALLibraryInstaller;
import org.esa.snap.core.dataio.ProductReader;
import org.esa.snap.core.dataio.ProductReaderPlugIn;
import org.esa.snap.core.datamodel.Product;
import org.esa.snap.core.util.SystemUtils;
import org.esa.snap.dataio.netcdf.NetCdfActivator;
import org.esa.snap.jp2.reader.OpenJPEGLibraryInstaller;
import org.esa.snap.runtime.LogUtils4Tests;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.rules.ErrorCollector;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import static org.junit.Assert.fail;

public class RunProductReaderTest {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this class necessary? Is ProductReaderAcceptanceTest not sufficient?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RunProductReaderTest is used to test certain product ids from the reader plugin JSON file. Also, the logger can be configured to enabled/disabled the messages for some classes to avoid unusefull messages in the console.

An example to run only the Muscate test for product id 'MUSCATE-v20':
-Dsnap.reader.tests.data.dir=\CV-DEV-SRV01\Satellite_Imagery\TestingJUnitFiles
-Dsnap.reader.tests.class.name=org.esa.s2tbx.dataio.spot.SpotViewProductReaderPlugin
-Dsnap.reader.tests.product.ids=MUSCATE-v20
-Dorg.esa.snap.level=FINE
-Dorg.esa.snap.core.metadata.level=SEVERE
-Dorg.esa.snap.jp2.reader.internal.level=SEVERE
-Dsnap.reader.tests.failOnMissingData=false
-Drapid.eye.force.read.product.with.nitf.api=true
-Dorg.esa.s2tbx.dataio.rapideye.level=FINE

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, to be able to specify the product IDs is a nice feature, but this would have been better adde to the ProductReaderAcceptanceTest class. and then documented on the wiki page.

Same for the logging options.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to not modify the ProductReaderAcceptanceTest class and create a new class with implementation I need to run the tests for reading the products from the JSON file.
Of course, the implementation can be copied/moved in the ProductReaderAcceptanceTest class if it is needed there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it would be good if you can move into the ProductReaderAcceptanceTest.
I just want to avoid that we have several runners. That Luis creates next week another onot and the week after BC creates also one. And all implementations need to be maintained.
So better update and improve the existing one as long as it applicable. Of course very special needs can arise which make it necessary to create a new implementation. But then the name of the implementation should already expression the special case.


private static final Logger logger = Logger.getLogger(RunProductReaderTest.class.getName());

private static final String PROPERTY_NAME_FAIL_ON_MISSING_DATA = "snap.reader.tests.failOnMissingData";
private static final String PROPERTY_NAME_DATA_DIR = "snap.reader.tests.data.dir";
private static final String PROPERTY_NAME_CASS_NAME = "snap.reader.tests.class.name";
private static final String PROPERTY_NAME_PRODUCT_IDS = "snap.reader.tests.product.ids";

private static List<TestDefinition> testDefinitionList;
private static File dataRootDir;
private static boolean isFailOnMissingData;

private ErrorCollector errorCollector = new ErrorCollector();

public RunProductReaderTest() {
}

@BeforeClass
public static void initialize() throws Exception {
LogUtils4Tests.initLogger();

String dataDirPropertyValue = System.getProperty(PROPERTY_NAME_DATA_DIR);
if (dataDirPropertyValue == null) {
fail("The test data property '"+PROPERTY_NAME_DATA_DIR+"' representing the folder which contains the source products is not set.");
}

String readerPlugInClassNameToTest = System.getProperty(PROPERTY_NAME_CASS_NAME);
String productIdsToTest = System.getProperty(PROPERTY_NAME_PRODUCT_IDS);
String failOnMissingData = System.getProperty(PROPERTY_NAME_FAIL_ON_MISSING_DATA);

if (logger.isLoggable(Level.FINE)) {
StringBuilder message = new StringBuilder();
message.append("Input test parameters: data folder path: ")
.append(dataDirPropertyValue)
.append(", reader plugin class: ")
.append(readerPlugInClassNameToTest)
.append(", product ids: ")
.append(productIdsToTest)
.append(", fail on missing data: ")
.append(failOnMissingData)
.append(".");
logger.log(Level.FINE, message.toString());
}

dataRootDir = new File(dataDirPropertyValue);
if (!dataRootDir.exists()) {
fail("The path '" + dataDirPropertyValue+"' representing the folder which contains the source products does not exist.");
}
if (!dataRootDir.isDirectory()) {
fail("The path '" + dataDirPropertyValue+"' exists and it does not denote a folder.");
}

isFailOnMissingData = true;
if (failOnMissingData != null) {
isFailOnMissingData = Boolean.parseBoolean(failOnMissingData);
}

SystemUtils.init3rdPartyLibs(RunProductReaderTest.class);

if (!isFailOnMissingData) {
logger.warning("Tests will not fail if test data is missing!");
}

testDefinitionList = loadProductReaderTestDefinitions(readerPlugInClassNameToTest, productIdsToTest);

OpenJPEGLibraryInstaller.install();
GDALLibraryInstaller.install();
NetCdfActivator.activate();
}

/**
* The required parameters to run the method:
* -Dsnap.reader.tests.data.dir=\\CV-DEV-SRV01\Satellite_Imagery\TestingJUnitFiles
*
* Optional parameters:
* -Dsnap.reader.tests.class.name=org.esa.s2tbx.dataio.muscate.MuscateProductReaderPlugin
* -Dsnap.reader.tests.product.ids=MUSCATE-Zip-31TFK,MUSCATE-v18,MUSCATE-v20
* -Dsnap.reader.tests.failOnMissingData=false
*
* @throws Exception
*/
@Test
public void testReadIntendedProductContent() throws IOException {
logger.info("Test product content for " + testDefinitionList.size() + " reader plugins.");

for (int k=0; k<testDefinitionList.size(); k++) {
TestDefinition testDefinition = testDefinitionList.get(k);
if (k > 0) {
logger.info(""); // write an empty line
}
final List<String> intendedProductIds = testDefinition.getDecodableProductIds();
String readerPluginClassName = testDefinition.getProductReaderPlugin().getClass().getSimpleName();

logger.info("Test products content: reader plugin class: " + readerPluginClassName+ ", product count: " + intendedProductIds.size()+".");

for (String productId : intendedProductIds) {
TestProduct foundTestProduct = null;
for (TestProduct testProduct : testDefinition.getAllProducts()) {
if (testProduct.getId().equalsIgnoreCase(productId)) {
foundTestProduct = testProduct;
break;
}
}
if (foundTestProduct == null) {
String message = "The test product with id '"+ productId+"' does not exist.";
if (isFailOnMissingData) {
fail(message);
} else {
logger.info(message);
}
} else if (foundTestProduct.exists()) {
logger.info("Start testing the product content: reader plugin class: " + readerPluginClassName+ ", product id: " + foundTestProduct.getId()+", product relative path: " + foundTestProduct.getRelativePath()+".");

long startTime = System.currentTimeMillis();

final File testProductFile = new File(dataRootDir, foundTestProduct.getRelativePath());

long startTimeReadProduct = System.currentTimeMillis();

final ProductReader productReader = testDefinition.getProductReaderPlugin().createReaderInstance();
final Product product = productReader.readProductNodes(testProductFile, null);
if (product == null) {
String message = "The product can not be read from file '" + testProductFile.getAbsolutePath()+"' does not exist.";
if (isFailOnMissingData) {
fail(message);
} else {
logger.log(Level.SEVERE, message);
}
} else {
try {
if (logger.isLoggable(Level.FINE)) {
double elapsedSecondsReadProduct = (System.currentTimeMillis() - startTimeReadProduct) / 1000.0d;
StringBuilder message = new StringBuilder();
message.append("Finish reading the product id: ")
.append(foundTestProduct.getId())
.append(", relative path: ")
.append(foundTestProduct.getRelativePath())
.append(", reader class: ")
.append(productReader.getClass().getName())
.append(", size: ")
.append(product.getSceneRasterWidth())
.append("x")
.append(product.getSceneRasterHeight())
.append(", elapsed time: ")
.append(elapsedSecondsReadProduct)
.append(" seconds.");
logger.log(Level.FINE, message.toString());
}

assertExpectedContent(testDefinition, foundTestProduct, product);
} catch (Throwable t) {
errorCollector.addError(new Throwable("[" + productId + "] " + t.getMessage(), t));
} finally {
product.dispose();
}

if (logger.isLoggable(Level.FINE)) {
double elapsedSeconds = (System.currentTimeMillis() - startTime) / 1000.0d;
StringBuilder message = new StringBuilder();
message.append("Finish testing the product content id: ")
.append(foundTestProduct.getId())
.append(", relative path: ")
.append(foundTestProduct.getRelativePath())
.append(", elapsed time: ")
.append(elapsedSeconds)
.append(" seconds.");
logger.log(Level.FINE, message.toString());
}
}
} else {
logger.info("The test product with id '"+ foundTestProduct.getId()+"' is missing from the disc.");
}
}
}
}

private static void assertExpectedContent(TestDefinition testDefinition, TestProduct testProduct, Product product) {
final ExpectedContent expectedContent = testDefinition.getExpectedContent(testProduct.getId());
if (expectedContent == null) {
String message = "No expected content for product id '" + testProduct.getId()+"'.";
if (isFailOnMissingData) {
fail(message);
} else {
logger.log(Level.SEVERE, message);
}
} else {
long startTime = System.currentTimeMillis();

final ContentAssert contentAssert = new ContentAssert(expectedContent, testProduct.getId(), product);
contentAssert.assertProductContent();

if (logger.isLoggable(Level.FINE)) {
double elapsedSeconds = (System.currentTimeMillis() - startTime) / 1000.0d;
StringBuilder message = new StringBuilder();
message.append("Finish testing expected content: product id: ")
.append(testProduct.getId())
.append(", relative path: ")
.append(testProduct.getRelativePath())
.append(", elapsed time: ")
.append(elapsedSeconds)
.append(" seconds.");
logger.log(Level.FINE, message.toString());
}
}
}

private static List<TestDefinition> loadProductReaderTestDefinitions(String readerPlugInClassNameToTest, String productIdsToTest) throws IOException {
final ObjectMapper mapper = new ObjectMapper();
final Iterable<ProductReaderPlugIn> readerPlugIns = SystemUtils.loadServices(ProductReaderPlugIn.class);

String[] productIds = null;
if (productIdsToTest != null) {
productIds = productIdsToTest.split(",");
if (productIds.length == 0) {
throw new IllegalArgumentException("The product ids array is empty.");
} else {
for (int k=0; k<productIds.length; k++) {
productIds[k] = productIds[k].trim();
}
}
}

List<TestDefinition> testDefinitionList = new ArrayList<>();
for (ProductReaderPlugIn readerPlugIn : readerPlugIns) {
final Class<? extends ProductReaderPlugIn> readerPlugInClass = readerPlugIn.getClass();
if (readerPlugInClassNameToTest != null && !readerPlugInClass.getName().startsWith(readerPlugInClassNameToTest)) {
continue;
}

final String path = readerPlugInClass.getName().replace(".", "/");
final String dataResourceName = "/" + path + "-data.json";
final URL dataResource = readerPlugInClass.getResource(dataResourceName);
if (dataResource == null) {
logger.warning("The reader plugin class '"+readerPlugInClass.getSimpleName() + "' does not define test data.");
continue;
}

final ProductList productList = mapper.readValue(dataResource, ProductList.class);

final TestDefinition testDefinition = new TestDefinition();
testDefinition.setProductReaderPlugin(readerPlugIn);
for (TestProduct testProduct : productList.getAll()) {
boolean canTestProduct;
if (productIds == null) {
canTestProduct = true;
} else {
canTestProduct = false;
for (int k=0; k<productIds.length && !canTestProduct; k++) {
if (productIds[k].equalsIgnoreCase(testProduct.getId())) {
canTestProduct = true;
}
}
}
if (canTestProduct) {
final File productFile = new File(dataRootDir, testProduct.getRelativePath());
if (!productFile.exists()) {
testProduct.exists(false);
String message = "The product '" + productFile.getAbsolutePath()+"' of the reader plugin '"+ readerPlugInClass.getSimpleName()+"' does not exist.";
if (isFailOnMissingData) {
fail(message);
} else {
logger.info(message);
}
}
testDefinition.getAllProducts().add(testProduct);
final String fileResourceName = testProduct.getId() + ".json";
final URL fileResource = readerPlugInClass.getResource(fileResourceName);
if (fileResource == null) {
fail("The resource file '" + fileResourceName+"' of the reader plugin '"+ readerPlugInClass.getSimpleName()+"' does not exist.");
}
final ExpectedDataset expectedDataset = mapper.readValue(fileResource, ExpectedDataset.class);
testDefinition.addExpectedDataset(expectedDataset);
}
}
testDefinitionList.add(testDefinition);
}
return testDefinitionList;
}
}
Loading