-1 if it is unknown
+ */
+ public int getDateBandIndex() {
+ return dateBandIndex;
+ }
+
+ /**
+ * Sets the (zero-based) date band index.
+ *
+ * @param dateBandIndex the (zero-based) spectral band index or -1 if it is unknown
+ */
+ public void setDateBandIndex(int dateBandIndex) {
+ if (this.dateBandIndex != dateBandIndex) {
+ this.dateBandIndex = dateBandIndex;
+ fireProductNodeChanged(PROPERTY_NAME_DATE_BAND_INDEX);
+ setModified(true);
+ }
+ }
+
+ /**
+ * Gets the date.
+ *
+ * @return the date for this band, or zero if this is not a date band or the date is
+ * not known.
+ */
+ public String getDate() {
+ return date;
+ }
+
+ /**
+ * Sets the date.
+ *
+ * @param date YYYY-MM-DD of this band, or 1601-01-01 if this is not a date band or
+ * the date is not known.
+ */
+ public void setDate(String date) {
+ if (this.date != date) {
+ this.date = date;
+ fireProductNodeChanged(PROPERTY_NAME_DATE);
+ setModified(true);
+ }
+ }
+
/**
* Gets the spectral bandwidth in nm (nanomater) units.
*
@@ -454,6 +535,8 @@ public String toString() {
+ ProductData.getTypeString(getDataType()) + "," +
+getRasterWidth() + "," +
+getRasterHeight() + "," +
+ +getAngularBandIndex() + "," +
+ +getAngularValue() + "," +
+getSpectralBandIndex() + "," +
+getSpectralWavelength() + "," +
+getSpectralBandwidth() + "," +
diff --git a/snap-core/src/main/java/org/esa/snap/core/util/ProductUtils.java b/snap-core/src/main/java/org/esa/snap/core/util/ProductUtils.java
index 6e67e9cf55f..9017ef36a1e 100644
--- a/snap-core/src/main/java/org/esa/snap/core/util/ProductUtils.java
+++ b/snap-core/src/main/java/org/esa/snap/core/util/ProductUtils.java
@@ -49,10 +49,9 @@
import java.awt.image.*;
import java.io.IOException;
import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
+import java.text.ParseException;
+import java.util.*;
+import java.util.List;
/**
* This class provides many static factory methods to be used in conjunction with data products.
@@ -61,6 +60,21 @@
*/
public class ProductUtils {
+ private static String GLOBAL_ATTRIBUTES_KEY = "Global_Attributes";
+ public static String METADATA_PROJECTION_KEY = "map_projection";
+ public static String[] METADATA_POSSIBLE_PROJECTION_KEYS = {METADATA_PROJECTION_KEY, "projection", "crs"};
+ public static String[] METADATA_POSSIBLE_START_TIME_KEYS = {"time_coverage_start"};
+ public static String[] METADATA_POSSIBLE_END_TIME_KEYS = {"time_coverage_end"};
+ public static String[] METADATA_POSSIBLE_SENSOR_KEYS = {"sensor_name", "instrument", "sensor"};
+ public static String[] METADATA_POSSIBLE_PLATFORM_KEYS = {"platform"};
+ public static String[] METADATA_POSSIBLE_PROCESSING_VERSION_KEYS = {"processing_version"};
+ public static String[] METADATA_POSSIBLE_DAY_NIGHT_KEYS = {"day_night_flag", "day_night"};
+ public static String[] METADATA_POSSIBLE_ORBIT_KEYS = {"orbit_number", "orbit"};
+ public static String[] METADATA_POSSIBLE_START_ORBIT_KEYS = {"start_orbit_number", "end_orbit"};
+ public static String[] METADATA_POSSIBLE_END_ORBIT_KEYS = {"end_orbit_number", "end_orbit"};
+
+ public static String METADATA_RESOLUTION_KEY = "spatial_resolution";
+ public static String[] METADATA_POSSIBLE_RESOLUTION_KEYS = {METADATA_RESOLUTION_KEY, "resolution"};
private static final int[] RGB_BAND_OFFSETS = new int[]{
2, 1, 0
};
@@ -883,6 +897,8 @@ public static void copySpectralBandProperties(Band sourceBand, Band targetBand)
targetBand.setSpectralBandIndex(sourceBand.getSpectralBandIndex());
targetBand.setSpectralWavelength(sourceBand.getSpectralWavelength());
targetBand.setSpectralBandwidth(sourceBand.getSpectralBandwidth());
+ targetBand.setAngularValue(sourceBand.getAngularValue());
+ targetBand.setAngularBandIndex(sourceBand.getAngularBandIndex());
targetBand.setSolarFlux(sourceBand.getSolarFlux());
}
@@ -1007,6 +1023,364 @@ public static void copyTiePointGrids(Product sourceProduct, Product targetProduc
}
}
+ public static void markAsDerivedFromMetaDataField(Product product, String attribute) {
+ // Created by Daniel Knowles
+
+ String tag = "Derived from (";
+
+ if (attribute != null && attribute.length() > 0 && product != null && product.getMetadataRoot() != null && product.getMetadataRoot().getElement(GLOBAL_ATTRIBUTES_KEY) != null) {
+ MetadataElement metadataElement = product.getMetadataRoot().getElement(GLOBAL_ATTRIBUTES_KEY);
+
+ String value = "";
+
+ MetadataAttribute metadataAttribute = metadataElement.getAttribute(attribute);
+
+ if (metadataAttribute != null) {
+ value = metadataAttribute.getData().toString();
+
+ if (value != null && value.length() > 0 && !value.startsWith(tag)) {
+ value = tag + value + ")";
+ setMetaDataField(product, attribute, value);
+ }
+ }
+ }
+
+ }
+
+
+ public static void markProductMetaDataFieldAsDerivedFrom(Product product) {
+ // Created by Daniel Knowles
+
+ markAsDerivedFromMetaDataField(product, "product_name");
+ markAsDerivedFromMetaDataField(product, "title");
+ markAsDerivedFromMetaDataField(product, "processing_level");
+
+ String tag = "Derived from (";
+
+ if (product != null) {
+ String productType = product.getProductType();
+
+ if (productType != null && productType.length() > 0 && !productType.startsWith(tag)) {
+ productType = tag + productType + ")";
+ product.setProductType(productType);
+ }
+ }
+
+ }
+
+
+ public static void prependHistoryMetaDataField(Product product, String latestHistoryEvent) {
+ // Created by Daniel Knowles
+
+ String HISTORY_KEY = "history";
+ String history = latestHistoryEvent;
+
+ if (product != null && product.getMetadataRoot() != null && product.getMetadataRoot().getElement(GLOBAL_ATTRIBUTES_KEY) != null) {
+
+ MetadataElement metadataElement = product.getMetadataRoot().getElement(GLOBAL_ATTRIBUTES_KEY);
+
+ if (metadataElement.getAttribute(HISTORY_KEY) != null) {
+ String previousHistory = metadataElement.getAttribute(HISTORY_KEY).getData().toString();
+
+ if (previousHistory.length() > 0) {
+ history = latestHistoryEvent + " :: " + previousHistory;
+ }
+ }
+
+ setMetaDataField(product, HISTORY_KEY, history);
+ }
+ }
+
+
+ public static void setResolutionMetaDataField(Product product, boolean markAsDerivedFrom) {
+ // Created by Daniel Knowles
+
+ setMetaDataFieldFromPossible(product, METADATA_RESOLUTION_KEY, METADATA_POSSIBLE_RESOLUTION_KEYS);
+
+ if (markAsDerivedFrom) {
+ markAsDerivedFromMetaDataField(product, METADATA_RESOLUTION_KEY);
+ }
+ }
+
+
+ public static void setMetaDataFieldFromPossible(Product product, String attribute, String[] possibleAttributes) {
+ // Created by Daniel Knowles
+ // if attribute does not exist then set to first value found from the possibleAttributes
+
+ if (product != null && product.getMetadataRoot() != null) {
+ MetadataElement metadataElement = product.getMetadataRoot().getElement(GLOBAL_ATTRIBUTES_KEY);
+
+ if (metadataElement != null) {
+ MetadataAttribute metadataAttribute = metadataElement.getAttribute(attribute);
+
+ if (metadataAttribute == null) {
+ for (String key : possibleAttributes) {
+ String[] keyVariations = StringUtils.getStringCaseVariations(key);
+ for (String keyVariation : keyVariations) {
+ if (metadataElement.getAttribute(keyVariation) != null) {
+ String value = metadataElement.getAttribute(keyVariation).getData().toString();
+ setMetaDataField(product, attribute, value);
+ break;
+ }
+ }
+ }
+
+ for (String key : possibleAttributes) {
+ String[] keyVariations = StringUtils.getStringCaseVariations(key);
+ for (String keyVariation : keyVariations) {
+ if (keyVariation != null && !keyVariation.toLowerCase().equals(attribute.toLowerCase())) {
+ deleteMetaDataField(product, keyVariation);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ public static void setMapProjectionMetaDataFields(Product product) {
+ // Created by Daniel Knowles
+
+ String MAP_PROJECTION_KEY = "map_projection";
+ String CRS_KEY = "crs";
+ String UNDEFINED_VALUE = "undefined";
+ String[] keysToDelete = {"projection", "mapprojection", "coordinatesystem", "coordinatereferencesystem", "coordinate_system", "geocoding", "geo_coding"};
+
+
+ if (product != null && product.getMetadataRoot() != null && product.getMetadataRoot().getElement(GLOBAL_ATTRIBUTES_KEY) != null) {
+
+ deleteMetaDataFields(product, keysToDelete);
+
+ String mapProjectionString = UNDEFINED_VALUE;
+ String crsString = UNDEFINED_VALUE;
+
+ if (product.getSceneGeoCoding() != null && product.getSceneGeoCoding().getMapCRS() != null) {
+ if (product.getSceneGeoCoding().getMapCRS().getName() != null) {
+ mapProjectionString = product.getSceneGeoCoding().getMapCRS().getName().toString();
+ }
+ if (product.getSceneGeoCoding().getMapCRS().toString() != null) {
+ crsString = product.getSceneGeoCoding().getMapCRS().toString().replaceAll("\n", "").replaceAll(" ", "");
+ }
+ }
+
+ setMetaDataField(product, MAP_PROJECTION_KEY, mapProjectionString);
+ setMetaDataField(product, CRS_KEY, crsString);
+ }
+ }
+
+
+ public static void deleteMetaDataFields(Product product, String[] fields) {
+ // Created by Daniel Knowles
+
+ MetadataElement metadataElement = product.getMetadataRoot().getElement(GLOBAL_ATTRIBUTES_KEY);
+
+ for (String key : fields) {
+ MetadataAttribute metadataAttribute = metadataElement.getAttribute(key);
+ if (metadataAttribute != null) {
+ metadataAttribute.setReadOnly(false);
+ metadataElement.removeAttribute(metadataAttribute);
+ }
+ }
+ }
+
+
+ public static void deleteMetaDataField(Product product, String field) {
+ // Created by Daniel Knowles
+
+ String[] fields = {field};
+ deleteMetaDataFields(product, fields);
+ }
+
+
+ public static void setMetaDataField(Product product, String field, String value) {
+ // Created by Daniel Knowles
+
+ String UNDEFINED_VALUE = "undefined";
+
+ if (product != null && product.getMetadataRoot() != null && product.getMetadataRoot().getElement(GLOBAL_ATTRIBUTES_KEY) != null) {
+
+ MetadataElement metadataElement = product.getMetadataRoot().getElement(GLOBAL_ATTRIBUTES_KEY);
+
+ MetadataAttribute metadataAttribute = metadataElement.getAttribute(field);
+ if (metadataAttribute != null) {
+ metadataAttribute.setReadOnly(false);
+ metadataAttribute.setModified(true);
+ }
+
+ // this wont do anything if attribute is not a String type
+ try {
+ if (value != null) {
+ metadataElement.setAttributeString(field, value);
+ } else {
+ metadataElement.setAttributeString(field, UNDEFINED_VALUE);
+ }
+ } catch (Exception e) {
+
+ }
+
+ if (metadataAttribute != null) {
+ metadataAttribute.setReadOnly(true);
+ }
+ }
+ }
+
+
+
+
+ public static String getMetaData(Product product, String[] keys) {
+ // Created by Daniel Knowles
+ String metaData = "";
+
+ for (String key : keys) {
+ metaData = getMetaData(product, key, false);
+
+ if (metaData.length() > 0) {
+ return metaData;
+ }
+ }
+
+ return metaData;
+ }
+
+ public static String getMetaData(Product product, String key) {
+ // Created by Daniel Knowles
+ String metaData = "";
+
+ if (key != null && key.length() > 0) {
+ try {
+ metaData = product.getMetadataRoot().getElement("Global_Attributes").getAttribute(key).getData().getElemString();
+ } catch (Exception ignore) {
+ }
+ }
+
+ return metaData;
+ }
+
+
+
+ static ProductData.UTC parseUtcDate(String timeString) {
+ try {
+ if (timeString.matches("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z")) {
+ // ISO
+ return ProductData.UTC.parse(timeString, "yyyy-MM-dd'T'HH:mm:ss'Z'");
+ } else if (timeString.matches("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z")) {
+ // ISO with micros
+ timeString = timeString.substring(0, timeString.length() - 1);
+ return ProductData.UTC.parse(timeString, "yyyy-MM-dd'T'HH:mm:ss");
+ } else if (timeString.matches("\\d{4}\\d{2}\\d{2}T\\d{6}Z")) {
+ // ISO no-punctation
+ return ProductData.UTC.parse(timeString, "yyyyMMdd'T'HHmmss'Z'");
+ } else if (timeString.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{6}")) {
+ // MODIS
+ return ProductData.UTC.parse(timeString, "yyyy-MM-dd HH:mm:ss");
+ } else if (timeString.matches("\\d{4}\\d{2}\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{6}")) {
+ // OCTS
+ return ProductData.UTC.parse(timeString, "yyyyMMdd HH:mm:ss");
+ } else if (timeString.matches("\\d{4}\\d{3}\\d{2}\\d{2}\\d{2}\\d{3}")) {
+ Date date = ProductData.UTC.createDateFormat("yyyyDDDHHmmssSSS").parse(timeString);
+ String milliSeconds = timeString.substring(timeString.length() - 3);
+ return ProductData.UTC.create(date, Long.parseLong(milliSeconds) * 1000);
+ }
+ } catch (ParseException ignored) {
+ }
+ return null;
+ }
+
+
+
+ public static boolean isMetadataKeyExists(Product product, String key) {
+ // Created by Daniel Knowles
+ boolean keyExists = false;
+
+ MetadataAttribute metadataAttribute;
+
+ if (key != null && key.length() > 0) {
+ try {
+ metadataAttribute = product.getMetadataRoot().getElement("Global_Attributes").getAttribute(key);
+ if (metadataAttribute != null) {
+ keyExists = true;
+ }
+ } catch (Exception ignore) {
+ }
+ }
+
+ return keyExists;
+ }
+
+ public static String getBandMetaData(Product product, String key, String band) {
+ // Created by Daniel Knowles
+ String metaData = "";
+
+ if (key != null && key.length() > 0 && band != null && band.length() > 0) {
+ try {
+ metaData = product.getMetadataRoot().getElement("Band_Attributes").getElement(band).getAttribute(key).getData().getElemString();
+ } catch (Exception ignore) {
+ }
+ }
+
+ return metaData;
+ }
+
+
+
+
+ public static String getMetaData(Product product, String key, boolean exact) {
+ // Created by Daniel Knowles
+ if (key == null) {
+ return null;
+ }
+
+ if (exact) {
+ return getMetaData(product, key);
+ } else {
+ String metaData = "";
+ String[] keyVariations = StringUtils.getStringCaseVariations(key);
+ for (String keyVariation : keyVariations) {
+ metaData = getMetaData(product, keyVariation);
+ if (metaData != null && metaData.length() > 0) {
+ return metaData;
+ }
+ }
+
+ return metaData;
+ }
+ }
+
+
+
+
+
+
+
+
+
+
+ public static String getMetaDataOrbit(Product product) {
+ // Created by Daniel Knowles
+ // Note this tries to retrieve an orbit or orbit range name
+ String metaData = getMetaData(product, METADATA_POSSIBLE_ORBIT_KEYS);
+
+ if (metaData != null && metaData.length() > 0) {
+ return metaData;
+ }
+
+ try {
+ String startOrbit = getMetaData(product, METADATA_POSSIBLE_START_ORBIT_KEYS);
+ String endOrbit = getMetaData(product, METADATA_POSSIBLE_END_ORBIT_KEYS);
+ if (startOrbit != null && startOrbit.length() > 0 && endOrbit != null && endOrbit.length() > 0) {
+ metaData = startOrbit + "-" + endOrbit;
+ } else if (endOrbit == null && startOrbit != null && startOrbit.length() > 0) {
+ metaData = startOrbit;
+ } else if (startOrbit == null && endOrbit != null && endOrbit.length() > 0) {
+ metaData = endOrbit;
+ }
+ } catch (Exception ignored) {
+ }
+
+
+ return metaData;
+ }
public static void copyVectorData(Product sourceProduct, Product targetProduct) {
ProductNodeGroup
+ * Support for operator parameters input/output.
+ *
+ * @author Norman Fomferra
+ * @author Marco Zühlke
+ */
+public class OperatorParameterSupport {
+
+ private static final ParameterUpdater DEFAULT = new ParameterUpdater() {
+ @Override
+ public void handleParameterSaveRequest(Map
+ * If a property set and a parameter map are given the client as to keep them in sync.
+ * The {@code parameterUpdater} will be called before each save and after each load request to
+ * enable custom updating.
+ *
+ * @param operatorDescriptor The operator descriptor.
+ * @param propertySet The property set (can be null). If supplied a parameter map is required as well.
+ * @param parameterMap the parameter map (can be null)
+ * @param parameterUpdater The parameter updater (can be null)
+ *
+ */
+ public OperatorParameterSupport(OperatorDescriptor operatorDescriptor,
+ PropertySet propertySet,
+ Map
@@ -165,7 +168,7 @@ public class ReprojectionOp extends Operator {
description = "The method used for resampling of floating-point raster data.",
valueSet = {"Nearest", "Bilinear", "Bicubic"},
defaultValue = "Nearest")
- private String resamplingName;
+ private String resampling;
@Parameter(description = "The X-position of the reference pixel.")
private Double referencePixelX;
@@ -210,6 +213,19 @@ public class ReprojectionOp extends Operator {
defaultValue = "false")
private boolean addDeltaBands;
+ @Parameter(description = "Apply the valid pixel expression to determine whether a source pixel gets used",
+ defaultValue = "true")
+ private boolean applyValidPixelExpression;
+
+ @Parameter(description = "Copy source valid pixel expression(s) to the corresponding bands of the reprojected file",
+ defaultValue = "true")
+ private boolean retainValidPixelExpression;
+
+
+ @Parameter(description = "Logical expression of masks and/or bands to apply to determine whether a source pixel gets used",
+ defaultValue = "")
+ private String maskExpression;
+
private ReprojectionSettingsProvider reprojectionSettingsProvider;
private ElevationModel elevationModel;
@@ -223,7 +239,7 @@ public void initialize() throws OperatorException {
} catch (OperatorException e) {
//Catch exception in order to clarify the error message
throw new OperatorException("The product selected as reference (Collocation product) contains rasters of different sizes and cannot be used as input.\n" +
- "Please consider resampling it so that all rasters have the same size.");
+ "Please consider resampling it so that all rasters have the same size.");
}
}
validateSourceProduct();
@@ -234,25 +250,25 @@ public void initialize() throws OperatorException {
validateSARProduct();
/*
- * 1. Compute the target CRS
- */
+ * 1. Compute the target CRS
+ */
GeoCoding sceneGeoCoding = sourceProduct.getSceneGeoCoding();
final GeoPos centerGeoPos =
getCenterGeoPos(sceneGeoCoding, sourceProduct.getSceneRasterWidth(), sourceProduct.getSceneRasterHeight());
CoordinateReferenceSystem targetCrs = createTargetCRS(centerGeoPos);
/*
- * 2. Compute the target geometry
- */
+ * 2. Compute the target geometry
+ */
ImageGeometry targetImageGeometry = createImageGeometry(targetCrs);
// determineDefaultSourceModel();
/*
- * 3. Create the target product
- */
+ * 3. Create the target product
+ */
Rectangle targetRect = targetImageGeometry.getImageRect();
targetProduct = new Product("projected_" + sourceProduct.getName(),
- sourceProduct.getProductType(),
- targetRect.width,
- targetRect.height);
+ sourceProduct.getProductType(),
+ targetRect.width,
+ targetRect.height);
targetProduct.setDescription(sourceProduct.getDescription());
Dimension tileSize;
if (tileSizeX != null && tileSizeY != null) {
@@ -264,14 +280,14 @@ public void initialize() throws OperatorException {
if (sourceProductPreferredTileSize.width == sourceProduct.getSceneRasterWidth()) {
tileSize.width = targetProduct.getSceneRasterWidth();
tileSize.height = Math.min(sourceProductPreferredTileSize.height,
- targetProduct.getSceneRasterHeight());
+ targetProduct.getSceneRasterHeight());
}
}
}
targetProduct.setPreferredTileSize(tileSize);
/*
- * 4. Define some target properties
- */
+ * 4. Define some target properties
+ */
if (orthorectify) {
elevationModel = createElevationModel();
}
@@ -280,8 +296,8 @@ public void initialize() throws OperatorException {
copyIndexCoding();
try {
targetProduct.setSceneGeoCoding(new CrsGeoCoding(targetImageGeometry.getMapCrs(),
- targetRect,
- targetImageGeometry.getImage2MapTransform()));
+ targetRect,
+ targetImageGeometry.getImage2MapTransform()));
} catch (Exception e) {
throw new OperatorException(e);
}
@@ -301,6 +317,36 @@ public void initialize() throws OperatorException {
ProductUtils.copyOverlayMasks(sourceProduct, targetProduct);
targetProduct.setAutoGrouping(sourceProduct.getAutoGrouping());
+ // todo Danny added these metadata lines
+ String operatorName = "Reproject";
+
+ OperatorDescriptor operatorDescriptor = null;
+ OperatorParameterSupport parameterSupport = null;
+
+ ProductUtils.setMapProjectionMetaDataFields(targetProduct);
+ ProductUtils.setResolutionMetaDataField(targetProduct, true);
+ ProductUtils.prependHistoryMetaDataField(targetProduct, operatorName);
+ ProductUtils.markProductMetaDataFieldAsDerivedFrom(targetProduct);
+
+ final Map