/*
* Copyright (C) 2015 by Array Systems Computing Inc. http://www.array.ca
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option)
* any later version.
* This program 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 General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see http://www.gnu.org/licenses/
*/
package org.esa.s1tbx.io.sentinel1;
import org.esa.s1tbx.io.SARReader;
import org.esa.s1tbx.io.XMLProductDirectory;
import org.esa.s1tbx.io.imageio.ImageIOFile;
import org.esa.snap.core.datamodel.Band;
import org.esa.snap.core.datamodel.GeoCoding;
import org.esa.snap.core.datamodel.GeoPos;
import org.esa.snap.core.datamodel.MetadataElement;
import org.esa.snap.core.datamodel.PixelPos;
import org.esa.snap.core.datamodel.Product;
import org.esa.snap.core.datamodel.ProductData;
import org.esa.snap.core.datamodel.TiePointGeoCoding;
import org.esa.snap.core.datamodel.TiePointGrid;
import org.esa.snap.core.dataop.downloadable.XMLSupport;
import org.esa.snap.core.util.SystemUtils;
import org.esa.snap.core.util.io.FileUtils;
import org.esa.snap.core.util.math.MathUtils;
import org.esa.snap.engine_utilities.datamodel.AbstractMetadata;
import org.esa.snap.engine_utilities.datamodel.Unit;
import org.esa.snap.engine_utilities.datamodel.metadata.AbstractMetadataIO;
import org.esa.snap.engine_utilities.eo.Constants;
import org.esa.snap.engine_utilities.gpf.OperatorUtils;
import org.esa.snap.engine_utilities.gpf.ReaderUtils;
import org.jdom2.Document;
import org.jdom2.Element;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.DateFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import static org.esa.snap.engine_utilities.datamodel.AbstractMetadata.NO_METADATA_STRING;
/**
* This class represents a product directory.
*/
public class Sentinel1Level1Directory extends XMLProductDirectory implements Sentinel1Directory {
private final Map<Band, TiePointGeoCoding> bandGeocodingMap = new HashMap<>(5);
private final transient Map<String, String> imgBandMetadataMap = new HashMap<>(4);
private String acqMode = "";
private final static Double NoDataValue = 0.0;//-9999.0;
public Sentinel1Level1Directory(final File inputFile) {
super(inputFile);
}
protected String getHeaderFileName() {
return Sentinel1Constants.PRODUCT_HEADER_NAME;
}
protected String getRelativePathToImageFolder() {
return getRootFolder() + "measurement" + '/';
}
protected void addImageFile(final String imgPath, final MetadataElement newRoot) throws IOException {
final String name = getBandFileNameFromImage(imgPath);
if ((name.endsWith("tiff"))) {
try {
final Dimension bandDimensions = getBandDimensions(newRoot, imgBandMetadataMap.get(name));
final InputStream inStream = getInputStream(imgPath);
final ImageInputStream imgStream = ImageIOFile.createImageInputStream(inStream, bandDimensions);
final ImageIOFile img;
if (isSLC()) {
img = new ImageIOFile(name, imgStream, ImageIOFile.getTiffIIOReader(imgStream),
1, 1, ProductData.TYPE_INT32, productInputFile);
} else {
img = new ImageIOFile(name, imgStream, ImageIOFile.getTiffIIOReader(imgStream),
1, 1, ProductData.TYPE_INT32, productInputFile);
}
bandImageFileMap.put(img.getName(), img);
} catch (Exception e) {
SystemUtils.LOG.severe(imgPath +" not found");
}
}
}
@Override
protected void addBands(final Product product) {
final MetadataElement absRoot = AbstractMetadata.getAbstractedMetadata(product);
int cnt = 1;
for (Map.Entry<String, ImageIOFile> stringImageIOFileEntry : bandImageFileMap.entrySet()) {
final ImageIOFile img = stringImageIOFileEntry.getValue();
final String imgName = img.getName().toLowerCase();
final MetadataElement bandMetadata = absRoot.getElement(imgBandMetadataMap.get(imgName));
final String swath = bandMetadata.getAttributeString(AbstractMetadata.swath);
final String pol = bandMetadata.getAttributeString(AbstractMetadata.polarization);
final int width = bandMetadata.getAttributeInt(AbstractMetadata.num_samples_per_line);
final int height = bandMetadata.getAttributeInt(AbstractMetadata.num_output_lines);
int numImages = img.getNumImages();
String tpgPrefix = "";
String suffix = pol;
if (isSLC()) {
numImages *= 2; // real + imaginary
if(isTOPSAR()) {
suffix = swath + '_' + pol;
tpgPrefix = swath;
} else if(acqMode.equals("WV")) {
suffix = suffix + '_' + cnt;
++cnt;
}
}
String bandName;
boolean real = true;
Band lastRealBand = null;
for (int i = 0; i < numImages; ++i) {
if (isSLC()) {
String unit;
for (int b = 0; b < img.getNumBands(); ++b) {
if (real) {
bandName = "i" + '_' + suffix;
unit = Unit.REAL;
} else {
bandName = "q" + '_' + suffix;
unit = Unit.IMAGINARY;
}
final Band band = new Band(bandName, ProductData.TYPE_INT16, width, height);
band.setUnit(unit);
band.setNoDataValueUsed(true);
band.setNoDataValue(NoDataValue);
product.addBand(band);
bandMap.put(band, new ImageIOFile.BandInfo(band, img, i, b));
AbstractMetadata.addBandToBandMap(bandMetadata, bandName);
if (real) {
lastRealBand = band;
} else {
ReaderUtils.createVirtualIntensityBand(product, lastRealBand, band, '_' + suffix);
}
real = !real;
// add tiepointgrids and geocoding for band
addTiePointGrids(band, imgName, tpgPrefix);
// reset to null so it doesn't adopt a geocoding from the bands
product.setSceneGeoCoding(null);
}
} else {
for (int b = 0; b < img.getNumBands(); ++b) {
bandName = "Amplitude" + '_' + suffix;
final Band band = new Band(bandName, ProductData.TYPE_INT32, width, height);
band.setUnit(Unit.AMPLITUDE);
band.setNoDataValueUsed(true);
band.setNoDataValue(NoDataValue);
product.addBand(band);
bandMap.put(band, new ImageIOFile.BandInfo(band, img, i, b));
AbstractMetadata.addBandToBandMap(bandMetadata, bandName);
SARReader.createVirtualIntensityBand(product, band, '_' + suffix);
// add tiepointgrids and geocoding for band
addTiePointGrids(band, imgName, tpgPrefix);
}
}
}
}
}
@Override
protected void addAbstractedMetadataHeader(final MetadataElement root) throws IOException {
final MetadataElement absRoot = AbstractMetadata.addAbstractedMetadataHeader(root);
final MetadataElement origProdRoot = AbstractMetadata.addOriginalProductMetadata(root);
addManifestMetadata(getProductName(), absRoot, origProdRoot, false);
acqMode = absRoot.getAttributeString(AbstractMetadata.ACQUISITION_MODE);
setSLC(absRoot.getAttributeString(AbstractMetadata.SAMPLE_TYPE).equals("COMPLEX"));
// get metadata for each band
addBandAbstractedMetadata(absRoot, origProdRoot);
addCalibrationAbstractedMetadata(origProdRoot);
addNoiseAbstractedMetadata(origProdRoot);
}
static void addManifestMetadata(final String productName, final MetadataElement absRoot,
final MetadataElement origProdRoot, boolean isOCN) {
final String defStr = NO_METADATA_STRING;
final int defInt = AbstractMetadata.NO_METADATA;
final MetadataElement XFDU = origProdRoot.getElement("XFDU");
final MetadataElement informationPackageMap = XFDU.getElement("informationPackageMap");
final MetadataElement contentUnit = informationPackageMap.getElement("contentUnit");
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.PRODUCT, productName);
final String descriptor = contentUnit.getAttributeString("textInfo", defStr);
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.SPH_DESCRIPTOR, descriptor);
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.antenna_pointing, "right");
final MetadataElement metadataSection = XFDU.getElement("metadataSection");
final MetadataElement[] metadataObjectList = metadataSection.getElements();
final DateFormat sentinelDateFormat = ProductData.UTC.createDateFormat("yyyy-MM-dd_HH:mm:ss");
for (MetadataElement metadataObject : metadataObjectList) {
final String id = metadataObject.getAttributeString("ID", defStr);
if (id.endsWith("Annotation") || id.endsWith("Schema")) {
// continue;
} else if (id.equals("processing")) {
final MetadataElement processing = findElement(metadataObject, "processing");
final MetadataElement facility = processing.getElement("facility");
final MetadataElement software = facility.getElement("software");
final String org = facility.getAttributeString("organisation");
final String name = software.getAttributeString("name");
final String version = software.getAttributeString("version");
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.ProcessingSystemIdentifier, org + ' ' + name + ' ' + version);
final ProductData.UTC start = getTime(processing, "start", sentinelDateFormat);
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.PROC_TIME, start);
} else if (id.equals("acquisitionPeriod")) {
final MetadataElement acquisitionPeriod = findElement(metadataObject, "acquisitionPeriod");
final ProductData.UTC startTime = getTime(acquisitionPeriod, "startTime", sentinelDateFormat);
final ProductData.UTC stopTime = getTime(acquisitionPeriod, "stopTime", sentinelDateFormat);
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.first_line_time, startTime);
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.last_line_time, stopTime);
} else if (id.equals("platform")) {
final MetadataElement platform = findElement(metadataObject, "platform");
String missionName = platform.getAttributeString("familyName", "Sentinel-1");
final String number = platform.getAttributeString("number", defStr);
if (!missionName.equals("ENVISAT"))
missionName += number;
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.MISSION, missionName);
final MetadataElement instrument = platform.getElement("instrument");
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.SWATH, instrument.getAttributeString("swath", defStr));
String acqMode = instrument.getAttributeString("mode", defStr);
if (acqMode == null || acqMode.equals(defStr)) {
final MetadataElement extensionElem = instrument.getElement("extension");
if (extensionElem != null) {
final MetadataElement instrumentModeElem = extensionElem.getElement("instrumentMode");
if (instrumentModeElem != null)
acqMode = instrumentModeElem.getAttributeString("mode", defStr);
}
}
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.ACQUISITION_MODE, acqMode);
} else if (id.equals("measurementOrbitReference")) {
final MetadataElement orbitReference = findElement(metadataObject, "orbitReference");
final MetadataElement orbitNumber = findElementContaining(orbitReference, "OrbitNumber", "type", "start");
final MetadataElement relativeOrbitNumber = findElementContaining(orbitReference, "relativeOrbitNumber", "type", "start");
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.ABS_ORBIT, orbitNumber.getAttributeInt("orbitNumber", defInt));
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.REL_ORBIT, relativeOrbitNumber.getAttributeInt("relativeOrbitNumber", defInt));
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.CYCLE, orbitReference.getAttributeInt("cycleNumber", defInt));
String pass = orbitReference.getAttributeString("pass", defStr);
if (pass.equals(defStr)) {
final MetadataElement extension = orbitReference.getElement("extension");
final MetadataElement orbitProperties = extension.getElement("orbitProperties");
pass = orbitProperties.getAttributeString("pass", defStr);
}
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.PASS, pass);
} else if (id.equals("measurementFrameSet")) {
} else if (id.equals("generalProductInformation")) {
MetadataElement generalProductInformation = findElement(metadataObject, "generalProductInformation");
if (generalProductInformation == null)
generalProductInformation = findElement(metadataObject, "standAloneProductInformation");
String productType = "unknown";
if (isOCN) {
productType = "OCN";
} else {
if (generalProductInformation != null)
productType = generalProductInformation.getAttributeString("productType", defStr);
}
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.PRODUCT_TYPE, productType);
if (productType.contains("SLC")) {
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.SAMPLE_TYPE, "COMPLEX");
} else {
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.SAMPLE_TYPE, "DETECTED");
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.srgr_flag, 1);
}
}
}
}
private void determineProductDimensions(final MetadataElement absRoot) throws IOException {
int totalWidth = 0, maxHeight = 0, k = 0;
String pol = null;
for (Map.Entry<String, ImageIOFile> stringImageIOFileEntry : bandImageFileMap.entrySet()) {
final ImageIOFile img = stringImageIOFileEntry.getValue();
final String imgName = img.getName().toLowerCase();
final String bandMetadataName = imgBandMetadataMap.get(imgName);
if (bandMetadataName == null) {
throw new IOException("Metadata for measurement dataset " + imgName + " not found");
}
if (k == 0) {
pol = bandMetadataName.substring(bandMetadataName.lastIndexOf("_") + 1);
} else if (!bandMetadataName.substring(bandMetadataName.lastIndexOf("_") + 1).equals(pol)) {
continue;
}
k++;
final MetadataElement bandMetadata = absRoot.getElement(bandMetadataName);
int width = bandMetadata.getAttributeInt(AbstractMetadata.num_samples_per_line);
int height = bandMetadata.getAttributeInt(AbstractMetadata.num_output_lines);
totalWidth += width;
if (height > maxHeight) {
maxHeight = height;
}
}
if (isSLC() && isTOPSAR()) { // approximate does not account for overlap
absRoot.setAttributeInt(AbstractMetadata.num_samples_per_line, totalWidth);
absRoot.setAttributeInt(AbstractMetadata.num_output_lines, maxHeight);
}
}
private void addBandAbstractedMetadata(final MetadataElement absRoot,
final MetadataElement origProdRoot) throws IOException {
MetadataElement annotationElement = origProdRoot.getElement("annotation");
if (annotationElement == null) {
annotationElement = new MetadataElement("annotation");
origProdRoot.addElement(annotationElement);
}
// collect range and azimuth spacing
double rangeSpacingTotal = 0;
double azimuthSpacingTotal = 0;
boolean commonMetadataRetrieved = false;
final DateFormat sentinelDateFormat = ProductData.UTC.createDateFormat("yyyy-MM-dd_HH:mm:ss");
double heightSum = 0.0;
int numBands = 0;
final String annotFolder = getRootFolder() + "annotation";
final String[] filenames = listFiles(annotFolder);
if (filenames != null) {
for (String metadataFile : filenames) {
final Document xmlDoc = XMLSupport.LoadXML(getInputStream(annotFolder + '/' + metadataFile));
final Element rootElement = xmlDoc.getRootElement();
final MetadataElement nameElem = new MetadataElement(metadataFile);
annotationElement.addElement(nameElem);
AbstractMetadataIO.AddXMLMetadata(rootElement, nameElem);
final MetadataElement prodElem = nameElem.getElement("product");
final MetadataElement adsHeader = prodElem.getElement("adsHeader");
final String swath = adsHeader.getAttributeString("swath");
final String pol = adsHeader.getAttributeString("polarisation");
final ProductData.UTC startTime = getTime(adsHeader, "startTime", sentinelDateFormat);
final ProductData.UTC stopTime = getTime(adsHeader, "stopTime", sentinelDateFormat);
final String bandRootName = AbstractMetadata.BAND_PREFIX + swath + '_' + pol;
final MetadataElement bandAbsRoot = AbstractMetadata.addBandAbstractedMetadata(absRoot, bandRootName);
final String imgName = FileUtils.exchangeExtension(metadataFile, ".tiff");
imgBandMetadataMap.put(imgName, bandRootName);
AbstractMetadata.setAttribute(bandAbsRoot, AbstractMetadata.SWATH, swath);
AbstractMetadata.setAttribute(bandAbsRoot, AbstractMetadata.polarization, pol);
AbstractMetadata.setAttribute(bandAbsRoot, AbstractMetadata.annotation, metadataFile);
AbstractMetadata.setAttribute(bandAbsRoot, AbstractMetadata.first_line_time, startTime);
AbstractMetadata.setAttribute(bandAbsRoot, AbstractMetadata.last_line_time, stopTime);
if (AbstractMetadata.isNoData(absRoot, AbstractMetadata.mds1_tx_rx_polar)) {
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.mds1_tx_rx_polar, pol);
} else if(!absRoot.getAttributeString(AbstractMetadata.mds1_tx_rx_polar, NO_METADATA_STRING).equals(pol)){
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.mds2_tx_rx_polar, pol);
}
final MetadataElement imageAnnotation = prodElem.getElement("imageAnnotation");
final MetadataElement imageInformation = imageAnnotation.getElement("imageInformation");
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.data_take_id,
Integer.parseInt(adsHeader.getAttributeString("missionDataTakeId")));
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.slice_num,
Integer.parseInt(imageInformation.getAttributeString("sliceNumber")));
rangeSpacingTotal += imageInformation.getAttributeDouble("rangePixelSpacing");
azimuthSpacingTotal += imageInformation.getAttributeDouble("azimuthPixelSpacing");
AbstractMetadata.setAttribute(bandAbsRoot, AbstractMetadata.line_time_interval,
imageInformation.getAttributeDouble("azimuthTimeInterval"));
AbstractMetadata.setAttribute(bandAbsRoot, AbstractMetadata.num_samples_per_line,
imageInformation.getAttributeInt("numberOfSamples"));
AbstractMetadata.setAttribute(bandAbsRoot, AbstractMetadata.num_output_lines,
imageInformation.getAttributeInt("numberOfLines"));
AbstractMetadata.setAttribute(bandAbsRoot, AbstractMetadata.sample_type,
imageInformation.getAttributeString("pixelValue").toUpperCase());
heightSum += getBandTerrainHeight(prodElem);
if (!commonMetadataRetrieved) {
// these should be the same for all swaths
// set to absRoot
final MetadataElement generalAnnotation = prodElem.getElement("generalAnnotation");
final MetadataElement productInformation = generalAnnotation.getElement("productInformation");
final MetadataElement processingInformation = imageAnnotation.getElement("processingInformation");
final MetadataElement swathProcParamsList = processingInformation.getElement("swathProcParamsList");
final MetadataElement swathProcParams = swathProcParamsList.getElement("swathProcParams");
final MetadataElement rangeProcessing = swathProcParams.getElement("rangeProcessing");
final MetadataElement azimuthProcessing = swathProcParams.getElement("azimuthProcessing");
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.range_sampling_rate,
productInformation.getAttributeDouble("rangeSamplingRate") / Constants.oneMillion);
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.radar_frequency,
productInformation.getAttributeDouble("radarFrequency") / Constants.oneMillion);
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.line_time_interval,
imageInformation.getAttributeDouble("azimuthTimeInterval"));
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.slant_range_to_first_pixel,
imageInformation.getAttributeDouble("slantRangeTime") * Constants.halfLightSpeed);
final MetadataElement downlinkInformationList = generalAnnotation.getElement("downlinkInformationList");
final MetadataElement downlinkInformation = downlinkInformationList.getElement("downlinkInformation");
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.pulse_repetition_frequency,
downlinkInformation.getAttributeDouble("prf"));
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.range_bandwidth,
rangeProcessing.getAttributeDouble("processingBandwidth") / Constants.oneMillion);
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.azimuth_bandwidth,
azimuthProcessing.getAttributeDouble("processingBandwidth"));
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.range_looks,
rangeProcessing.getAttributeDouble("numberOfLooks"));
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.azimuth_looks,
azimuthProcessing.getAttributeDouble("numberOfLooks"));
if (!isTOPSAR() || !isSLC()) {
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.num_output_lines,
imageInformation.getAttributeInt("numberOfLines"));
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.num_samples_per_line,
imageInformation.getAttributeInt("numberOfSamples"));
}
addOrbitStateVectors(absRoot, generalAnnotation.getElement("orbitList"));
addSRGRCoefficients(absRoot, prodElem.getElement("coordinateConversion"));
addDopplerCentroidCoefficients(absRoot, prodElem.getElement("dopplerCentroid"));
commonMetadataRetrieved = true;
}
++numBands;
}
}
// set average to absRoot
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.range_spacing,
rangeSpacingTotal / (double) numBands);
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.azimuth_spacing,
azimuthSpacingTotal / (double) numBands);
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.avg_scene_height, heightSum / filenames.length);
}
private double getBandTerrainHeight(final MetadataElement prodElem) {
final MetadataElement generalAnnotation = prodElem.getElement("generalAnnotation");
final MetadataElement terrainHeightList = generalAnnotation.getElement("terrainHeightList");
double heightSum = 0.0;
final MetadataElement[] heightList = terrainHeightList.getElements();
int cnt = 0;
for (MetadataElement terrainHeight : heightList) {
heightSum += terrainHeight.getAttributeDouble("value");
++cnt;
}
return heightSum / cnt;
}
private void addCalibrationAbstractedMetadata(final MetadataElement origProdRoot) throws IOException {
MetadataElement calibrationElement = origProdRoot.getElement("calibration");
if (calibrationElement == null) {
calibrationElement = new MetadataElement("calibration");
origProdRoot.addElement(calibrationElement);
}
final String calibFolder = getRootFolder() + "annotation" + '/' + "calibration";
final String[] filenames = listFiles(calibFolder);
if (filenames != null) {
for (String metadataFile : filenames) {
if (metadataFile.startsWith("calibration")) {
final Document xmlDoc = XMLSupport.LoadXML(getInputStream(calibFolder + '/' + metadataFile));
final Element rootElement = xmlDoc.getRootElement();
final String name = metadataFile.replace("calibration-", "");
final MetadataElement nameElem = new MetadataElement(name);
calibrationElement.addElement(nameElem);
AbstractMetadataIO.AddXMLMetadata(rootElement, nameElem);
}
}
}
}
private void addNoiseAbstractedMetadata(final MetadataElement origProdRoot) throws IOException {
MetadataElement noiseElement = origProdRoot.getElement("noise");
if (noiseElement == null) {
noiseElement = new MetadataElement("noise");
origProdRoot.addElement(noiseElement);
}
final String calibFolder = getRootFolder() + "annotation" + '/' + "calibration";
final String[] filenames = listFiles(calibFolder);
if (filenames != null) {
for (String metadataFile : filenames) {
if (metadataFile.startsWith("noise")) {
final Document xmlDoc = XMLSupport.LoadXML(getInputStream(calibFolder + '/' + metadataFile));
final Element rootElement = xmlDoc.getRootElement();
final String name = metadataFile.replace("noise-", "");
final MetadataElement nameElem = new MetadataElement(name);
noiseElement.addElement(nameElem);
AbstractMetadataIO.AddXMLMetadata(rootElement, nameElem);
}
}
}
}
private static MetadataElement findElement(final MetadataElement elem, final String name) {
final MetadataElement metadataWrap = elem.getElement("metadataWrap");
final MetadataElement xmlData = metadataWrap.getElement("xmlData");
return xmlData.getElement(name);
}
private static MetadataElement findElementContaining(final MetadataElement parent, final String elemName,
final String attribName, final String attValue) {
final MetadataElement[] elems = parent.getElements();
for (MetadataElement elem : elems) {
if (elem.getName().equalsIgnoreCase(elemName) && elem.containsAttribute(attribName)) {
String value = elem.getAttributeString(attribName);
if (value != null && value.equalsIgnoreCase(attValue))
return elem;
}
}
return null;
}
private void addOrbitStateVectors(final MetadataElement absRoot, final MetadataElement orbitList) {
final MetadataElement orbitVectorListElem = absRoot.getElement(AbstractMetadata.orbit_state_vectors);
final MetadataElement[] stateVectorElems = orbitList.getElements();
for (int i = 1; i <= stateVectorElems.length; ++i) {
addVector(AbstractMetadata.orbit_vector, orbitVectorListElem, stateVectorElems[i - 1], i);
}
// set state vector time
if (absRoot.getAttributeUTC(AbstractMetadata.STATE_VECTOR_TIME, AbstractMetadata.NO_METADATA_UTC).
equalElems(AbstractMetadata.NO_METADATA_UTC)) {
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.STATE_VECTOR_TIME,
ReaderUtils.getTime(stateVectorElems[0], "time", sentinelDateFormat));
}
}
private void addVector(final String name, final MetadataElement orbitVectorListElem,
final MetadataElement orbitElem, final int num) {
final MetadataElement orbitVectorElem = new MetadataElement(name + num);
final MetadataElement positionElem = orbitElem.getElement("position");
final MetadataElement velocityElem = orbitElem.getElement("velocity");
orbitVectorElem.setAttributeUTC(AbstractMetadata.orbit_vector_time,
ReaderUtils.getTime(orbitElem, "time", sentinelDateFormat));
orbitVectorElem.setAttributeDouble(AbstractMetadata.orbit_vector_x_pos,
positionElem.getAttributeDouble("x", 0));
orbitVectorElem.setAttributeDouble(AbstractMetadata.orbit_vector_y_pos,
positionElem.getAttributeDouble("y", 0));
orbitVectorElem.setAttributeDouble(AbstractMetadata.orbit_vector_z_pos,
positionElem.getAttributeDouble("z", 0));
orbitVectorElem.setAttributeDouble(AbstractMetadata.orbit_vector_x_vel,
velocityElem.getAttributeDouble("x", 0));
orbitVectorElem.setAttributeDouble(AbstractMetadata.orbit_vector_y_vel,
velocityElem.getAttributeDouble("y", 0));
orbitVectorElem.setAttributeDouble(AbstractMetadata.orbit_vector_z_vel,
velocityElem.getAttributeDouble("z", 0));
orbitVectorListElem.addElement(orbitVectorElem);
}
private void addSRGRCoefficients(final MetadataElement absRoot, final MetadataElement coordinateConversion) {
if (coordinateConversion == null) return;
final MetadataElement coordinateConversionList = coordinateConversion.getElement("coordinateConversionList");
if (coordinateConversionList == null) return;
final MetadataElement srgrCoefficientsElem = absRoot.getElement(AbstractMetadata.srgr_coefficients);
int listCnt = 1;
for (MetadataElement elem : coordinateConversionList.getElements()) {
final MetadataElement srgrListElem = new MetadataElement(AbstractMetadata.srgr_coef_list + '.' + listCnt);
srgrCoefficientsElem.addElement(srgrListElem);
++listCnt;
final ProductData.UTC utcTime = ReaderUtils.getTime(elem, "azimuthTime", sentinelDateFormat);
srgrListElem.setAttributeUTC(AbstractMetadata.srgr_coef_time, utcTime);
final double grOrigin = elem.getAttributeDouble("gr0", 0);
AbstractMetadata.addAbstractedAttribute(srgrListElem, AbstractMetadata.ground_range_origin,
ProductData.TYPE_FLOAT64, "m", "Ground Range Origin");
AbstractMetadata.setAttribute(srgrListElem, AbstractMetadata.ground_range_origin, grOrigin);
final String coeffStr = elem.getElement("grsrCoefficients").getAttributeString("grsrCoefficients", "");
if (!coeffStr.isEmpty()) {
final StringTokenizer st = new StringTokenizer(coeffStr);
int cnt = 1;
while (st.hasMoreTokens()) {
final double coefValue = Double.parseDouble(st.nextToken());
final MetadataElement coefElem = new MetadataElement(AbstractMetadata.coefficient + '.' + cnt);
srgrListElem.addElement(coefElem);
++cnt;
AbstractMetadata.addAbstractedAttribute(coefElem, AbstractMetadata.srgr_coef,
ProductData.TYPE_FLOAT64, "", "SRGR Coefficient");
AbstractMetadata.setAttribute(coefElem, AbstractMetadata.srgr_coef, coefValue);
}
}
}
}
private void addDopplerCentroidCoefficients(
final MetadataElement absRoot, final MetadataElement dopplerCentroid) {
if (dopplerCentroid == null) return;
final MetadataElement dcEstimateList = dopplerCentroid.getElement("dcEstimateList");
if (dcEstimateList == null) return;
final MetadataElement dopplerCentroidCoefficientsElem = absRoot.getElement(AbstractMetadata.dop_coefficients);
int listCnt = 1;
for (MetadataElement elem : dcEstimateList.getElements()) {
final MetadataElement dopplerListElem = new MetadataElement(AbstractMetadata.dop_coef_list + '.' + listCnt);
dopplerCentroidCoefficientsElem.addElement(dopplerListElem);
++listCnt;
final ProductData.UTC utcTime = ReaderUtils.getTime(elem, "azimuthTime", sentinelDateFormat);
dopplerListElem.setAttributeUTC(AbstractMetadata.dop_coef_time, utcTime);
final double refTime = elem.getAttributeDouble("t0", 0) * 1e9; // s to ns
AbstractMetadata.addAbstractedAttribute(dopplerListElem, AbstractMetadata.slant_range_time,
ProductData.TYPE_FLOAT64, "ns", "Slant Range Time");
AbstractMetadata.setAttribute(dopplerListElem, AbstractMetadata.slant_range_time, refTime);
final String coeffStr = elem.getElement("geometryDcPolynomial").getAttributeString("geometryDcPolynomial", "");
if (!coeffStr.isEmpty()) {
final StringTokenizer st = new StringTokenizer(coeffStr);
int cnt = 1;
while (st.hasMoreTokens()) {
final double coefValue = Double.parseDouble(st.nextToken());
final MetadataElement coefElem = new MetadataElement(AbstractMetadata.coefficient + '.' + cnt);
dopplerListElem.addElement(coefElem);
++cnt;
AbstractMetadata.addAbstractedAttribute(coefElem, AbstractMetadata.dop_coef,
ProductData.TYPE_FLOAT64, "", "Doppler Centroid Coefficient");
AbstractMetadata.setAttribute(coefElem, AbstractMetadata.dop_coef, coefValue);
}
}
}
}
@Override
protected void addGeoCoding(final Product product) {
TiePointGrid latGrid = product.getTiePointGrid(OperatorUtils.TPG_LATITUDE);
TiePointGrid lonGrid = product.getTiePointGrid(OperatorUtils.TPG_LONGITUDE);
if (latGrid != null && lonGrid != null) {
setLatLongMetadata(product, latGrid, lonGrid);
final TiePointGeoCoding tpGeoCoding = new TiePointGeoCoding(latGrid, lonGrid);
product.setSceneGeoCoding(tpGeoCoding);
return;
}
final MetadataElement absRoot = AbstractMetadata.getAbstractedMetadata(product);
final String acquisitionMode = absRoot.getAttributeString(AbstractMetadata.ACQUISITION_MODE);
int numOfSubSwath;
switch (acquisitionMode) {
case "IW":
numOfSubSwath = 3;
break;
case "EW":
numOfSubSwath = 5;
break;
default:
return;
}
String[] bandNames = product.getBandNames();
Band firstSWBand = null, lastSWBand = null;
boolean firstSWBandFound = false, lastSWBandFound = false;
for (String bandName : bandNames) {
if (!firstSWBandFound && bandName.contains(acquisitionMode + 1)) {
firstSWBand = product.getBand(bandName);
firstSWBandFound = true;
}
if (!lastSWBandFound && bandName.contains(acquisitionMode + numOfSubSwath)) {
lastSWBand = product.getBand(bandName);
lastSWBandFound = true;
}
}
if (firstSWBand == null && lastSWBand == null)
return;
final GeoCoding firstSWBandGeoCoding = bandGeocodingMap.get(firstSWBand);
final int firstSWBandHeight = firstSWBand.getRasterHeight();
final GeoCoding lastSWBandGeoCoding = bandGeocodingMap.get(lastSWBand);
final int lastSWBandWidth = lastSWBand.getRasterWidth();
final int lastSWBandHeight = lastSWBand.getRasterHeight();
final PixelPos ulPix = new PixelPos(0, 0);
final PixelPos llPix = new PixelPos(0, firstSWBandHeight - 1);
final GeoPos ulGeo = new GeoPos();
final GeoPos llGeo = new GeoPos();
firstSWBandGeoCoding.getGeoPos(ulPix, ulGeo);
firstSWBandGeoCoding.getGeoPos(llPix, llGeo);
final PixelPos urPix = new PixelPos(lastSWBandWidth - 1, 0);
final PixelPos lrPix = new PixelPos(lastSWBandWidth - 1, lastSWBandHeight - 1);
final GeoPos urGeo = new GeoPos();
final GeoPos lrGeo = new GeoPos();
lastSWBandGeoCoding.getGeoPos(urPix, urGeo);
lastSWBandGeoCoding.getGeoPos(lrPix, lrGeo);
final float[] latCorners = {(float) ulGeo.getLat(), (float) urGeo.getLat(), (float) llGeo.getLat(), (float) lrGeo.getLat()};
final float[] lonCorners = {(float) ulGeo.getLon(), (float) urGeo.getLon(), (float) llGeo.getLon(), (float) lrGeo.getLon()};
ReaderUtils.addGeoCoding(product, latCorners, lonCorners);
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.first_near_lat, ulGeo.getLat());
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.first_near_long, ulGeo.getLon());
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.first_far_lat, urGeo.getLat());
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.first_far_long, urGeo.getLon());
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.last_near_lat, llGeo.getLat());
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.last_near_long, llGeo.getLon());
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.last_far_lat, lrGeo.getLat());
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.last_far_long, lrGeo.getLon());
// add band geocoding
final Band[] bands = product.getBands();
for (Band band : bands) {
band.setGeoCoding(bandGeocodingMap.get(band));
}
}
@Override
protected void addTiePointGrids(final Product product) {
// replaced by call to addTiePointGrids(band)
}
private void addTiePointGrids(final Band band, final String imgXMLName, final String tpgPrefix) {
//System.out.println("S1L1Dir.addTiePointGrids: band = " + band.getName() + " imgXMLName = " + imgXMLName + " tpgPrefix = " + tpgPrefix);
final Product product = band.getProduct();
String pre = "";
if (!tpgPrefix.isEmpty())
pre = tpgPrefix + '_';
final TiePointGrid existingLatTPG = product.getTiePointGrid(pre + OperatorUtils.TPG_LATITUDE);
final TiePointGrid existingLonTPG = product.getTiePointGrid(pre + OperatorUtils.TPG_LONGITUDE);
if (existingLatTPG != null && existingLonTPG != null) {
//System.out.println("for band = " + band.getName() + ", use existing TPG");
// reuse geocoding
final TiePointGeoCoding tpGeoCoding = new TiePointGeoCoding(existingLatTPG, existingLonTPG);
band.setGeoCoding(tpGeoCoding);
return;
}
//System.out.println("add new TPG for band = " + band.getName());
final String annotation = FileUtils.exchangeExtension(imgXMLName, ".xml");
final MetadataElement origProdRoot = AbstractMetadata.getOriginalProductMetadata(product);
final MetadataElement annotationElem = origProdRoot.getElement("annotation");
final MetadataElement imgElem = annotationElem.getElement(annotation);
final MetadataElement productElem = imgElem.getElement("product");
final MetadataElement geolocationGrid = productElem.getElement("geolocationGrid");
final MetadataElement geolocationGridPointList = geolocationGrid.getElement("geolocationGridPointList");
final MetadataElement[] geoGrid = geolocationGridPointList.getElements();
//System.out.println("geoGrid.length = " + geoGrid.length);
final double[] latList = new double[geoGrid.length];
final double[] lngList = new double[geoGrid.length];
final double[] incidenceAngleList = new double[geoGrid.length];
final double[] elevAngleList = new double[geoGrid.length];
final double[] rangeTimeList = new double[geoGrid.length];
final int[] x = new int[geoGrid.length];
final int[] y = new int[geoGrid.length];
// Loop through the list of geolocation grid points, assuming that it represents a row-major rectangular grid.
int gridWidth = 0, gridHeight = 0;
int i = 0;
for (MetadataElement ggPoint : geoGrid) {
latList[i] = ggPoint.getAttributeDouble("latitude", 0);
lngList[i] = ggPoint.getAttributeDouble("longitude", 0);
incidenceAngleList[i] = ggPoint.getAttributeDouble("incidenceAngle", 0);
elevAngleList[i] = ggPoint.getAttributeDouble("elevationAngle", 0);
rangeTimeList[i] = ggPoint.getAttributeDouble("slantRangeTime", 0) * Constants.oneBillion; // s to ns
x[i] = (int) ggPoint.getAttributeDouble("pixel", 0);
y[i] = (int) ggPoint.getAttributeDouble("line", 0);
if (x[i] == 0) {
// This means we are at the start of a new line
if (gridWidth == 0) // Here we are implicitly assuming that the pixel horizontal spacing is assumed to be the same from line to line.
gridWidth = i;
++gridHeight;
}
++i;
}
//System.out.println("geoGrid w = " + gridWidth + "; h = " + gridHeight);
final int newGridWidth = gridWidth;
final int newGridHeight = gridHeight;
final float[] newLatList = new float[newGridWidth * newGridHeight];
final float[] newLonList = new float[newGridWidth * newGridHeight];
final float[] newIncList = new float[newGridWidth * newGridHeight];
final float[] newElevList = new float[newGridWidth * newGridHeight];
final float[] newslrtList = new float[newGridWidth * newGridHeight];
final int sceneRasterWidth = band.getRasterWidth();
final int sceneRasterHeight = band.getRasterHeight();
final double subSamplingX = (double) sceneRasterWidth / (newGridWidth - 1);
final double subSamplingY = (double) sceneRasterHeight / (newGridHeight - 1);
getListInEvenlySpacedGrid(sceneRasterWidth, sceneRasterHeight, gridWidth, gridHeight, x, y, latList,
newGridWidth, newGridHeight, subSamplingX, subSamplingY, newLatList);
getListInEvenlySpacedGrid(sceneRasterWidth, sceneRasterHeight, gridWidth, gridHeight, x, y, lngList,
newGridWidth, newGridHeight, subSamplingX, subSamplingY, newLonList);
getListInEvenlySpacedGrid(sceneRasterWidth, sceneRasterHeight, gridWidth, gridHeight, x, y, incidenceAngleList,
newGridWidth, newGridHeight, subSamplingX, subSamplingY, newIncList);
getListInEvenlySpacedGrid(sceneRasterWidth, sceneRasterHeight, gridWidth, gridHeight, x, y, elevAngleList,
newGridWidth, newGridHeight, subSamplingX, subSamplingY, newElevList);
getListInEvenlySpacedGrid(sceneRasterWidth, sceneRasterHeight, gridWidth, gridHeight, x, y, rangeTimeList,
newGridWidth, newGridHeight, subSamplingX, subSamplingY, newslrtList);
TiePointGrid latGrid = product.getTiePointGrid(pre + OperatorUtils.TPG_LATITUDE);
if (latGrid == null) {
latGrid = new TiePointGrid(pre + OperatorUtils.TPG_LATITUDE,
newGridWidth, newGridHeight, 0.5f, 0.5f, subSamplingX, subSamplingY, newLatList);
latGrid.setUnit(Unit.DEGREES);
product.addTiePointGrid(latGrid);
}
TiePointGrid lonGrid = product.getTiePointGrid(pre + OperatorUtils.TPG_LONGITUDE);
if (lonGrid == null) {
lonGrid = new TiePointGrid(pre + OperatorUtils.TPG_LONGITUDE,
newGridWidth, newGridHeight, 0.5f, 0.5f, subSamplingX, subSamplingY, newLonList, TiePointGrid.DISCONT_AT_180);
lonGrid.setUnit(Unit.DEGREES);
product.addTiePointGrid(lonGrid);
}
if (product.getTiePointGrid(pre + OperatorUtils.TPG_INCIDENT_ANGLE) == null) {
final TiePointGrid incidentAngleGrid = new TiePointGrid(pre + OperatorUtils.TPG_INCIDENT_ANGLE,
newGridWidth, newGridHeight, 0.5f, 0.5f, subSamplingX, subSamplingY, newIncList);
incidentAngleGrid.setUnit(Unit.DEGREES);
product.addTiePointGrid(incidentAngleGrid);
}
if (product.getTiePointGrid(pre + OperatorUtils.TPG_ELEVATION_ANGLE) == null) {
final TiePointGrid elevAngleGrid = new TiePointGrid(pre + OperatorUtils.TPG_ELEVATION_ANGLE,
newGridWidth, newGridHeight, 0.5f, 0.5f, subSamplingX, subSamplingY, newElevList);
elevAngleGrid.setUnit(Unit.DEGREES);
product.addTiePointGrid(elevAngleGrid);
}
if (product.getTiePointGrid(pre + OperatorUtils.TPG_SLANT_RANGE_TIME) == null) {
final TiePointGrid slantRangeGrid = new TiePointGrid(pre + OperatorUtils.TPG_SLANT_RANGE_TIME,
newGridWidth, newGridHeight, 0.5f, 0.5f, subSamplingX, subSamplingY, newslrtList);
slantRangeGrid.setUnit(Unit.NANOSECONDS);
product.addTiePointGrid(slantRangeGrid);
}
final TiePointGeoCoding tpGeoCoding = new TiePointGeoCoding(latGrid, lonGrid);
bandGeocodingMap.put(band, tpGeoCoding);
}
private static void setLatLongMetadata(Product product, TiePointGrid latGrid, TiePointGrid lonGrid) {
final MetadataElement absRoot = AbstractMetadata.getAbstractedMetadata(product);
final int w = product.getSceneRasterWidth();
final int h = product.getSceneRasterHeight();
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.first_near_lat, latGrid.getPixelDouble(0, 0));
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.first_near_long, lonGrid.getPixelDouble(0, 0));
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.first_far_lat, latGrid.getPixelDouble(w, 0));
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.first_far_long, lonGrid.getPixelDouble(w, 0));
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.last_near_lat, latGrid.getPixelDouble(0, h));
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.last_near_long, lonGrid.getPixelDouble(0, h));
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.last_far_lat, latGrid.getPixelDouble(w, h));
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.last_far_long, lonGrid.getPixelDouble(w, h));
}
private boolean isTOPSAR() {
return acqMode.equals("IW") || acqMode.equals("EW");
}
@Override
protected String getProductName() {
String name = getBaseName();
if (name.toUpperCase().endsWith(".SAFE"))
return name.substring(0, name.length() - 5);
else if (name.toUpperCase().endsWith(".ZIP"))
return name.substring(0, name.length() - 4);
return name;
}
protected String getProductType() {
return "Level-1";
}
public static void getListInEvenlySpacedGrid(
final int sceneRasterWidth, final int sceneRasterHeight, final int sourceGridWidth,
final int sourceGridHeight, final int[] x, final int[] y, final double[] sourcePointList,
final int targetGridWidth, final int targetGridHeight, final double subSamplingX, final double subSamplingY,
final float[] targetPointList) {
if (sourcePointList.length != sourceGridWidth * sourceGridHeight) {
throw new IllegalArgumentException(
"Original tie point array size does not match 'sourceGridWidth' x 'sourceGridHeight'");
}
if (targetPointList.length != targetGridWidth * targetGridHeight) {
throw new IllegalArgumentException(
"Target tie point array size does not match 'targetGridWidth' x 'targetGridHeight'");
}
int k = 0;
for (int r = 0; r < targetGridHeight; r++) {
double newY = r * subSamplingY;
if (newY > sceneRasterHeight - 1) {
newY = sceneRasterHeight - 1;
}
double oldY0 = 0, oldY1 = 0;
int j0 = 0, j1 = 0;
for (int rr = 1; rr < sourceGridHeight; rr++) {
j0 = rr - 1;
j1 = rr;
oldY0 = y[j0 * sourceGridWidth];
oldY1 = y[j1 * sourceGridWidth];
if (oldY1 > newY) {
break;
}
}
final double wj = (newY - oldY0) / (oldY1 - oldY0);
for (int c = 0; c < targetGridWidth; c++) {
double newX = c * subSamplingX;
if (newX > sceneRasterWidth - 1) {
newX = sceneRasterWidth - 1;
}
double oldX0 = 0, oldX1 = 0;
int i0 = 0, i1 = 0;
for (int cc = 1; cc < sourceGridWidth; cc++) {
i0 = cc - 1;
i1 = cc;
oldX0 = x[i0];
oldX1 = x[i1];
if (oldX1 > newX) {
break;
}
}
final double wi = (newX - oldX0) / (oldX1 - oldX0);
targetPointList[k++] = (float) (MathUtils.interpolate2D(wi, wj,
sourcePointList[i0 + j0 * sourceGridWidth],
sourcePointList[i1 + j0 * sourceGridWidth],
sourcePointList[i0 + j1 * sourceGridWidth],
sourcePointList[i1 + j1 * sourceGridWidth]));
}
}
}
public static ProductData.UTC getTime(final MetadataElement elem, final String tag, final DateFormat sentinelDateFormat) {
String start = elem.getAttributeString(tag, NO_METADATA_STRING);
start = start.replace("T", "_");
return AbstractMetadata.parseUTC(start, sentinelDateFormat);
}
@Override
public Product createProduct() throws IOException {
final MetadataElement newRoot = addMetaData();
findImages(newRoot);
final MetadataElement absRoot = newRoot.getElement(AbstractMetadata.ABSTRACT_METADATA_ROOT);
determineProductDimensions(absRoot);
final int sceneWidth = absRoot.getAttributeInt(AbstractMetadata.num_samples_per_line);
final int sceneHeight = absRoot.getAttributeInt(AbstractMetadata.num_output_lines);
final Product product = new Product(getProductName(), getProductType(), sceneWidth, sceneHeight);
updateProduct(product, newRoot);
addBands(product);
addGeoCoding(product);
product.setName(getProductName());
//product.setProductType(getProductType());
product.setDescription(getProductDescription());
ReaderUtils.addMetadataIncidenceAngles(product);
ReaderUtils.addMetadataProductSize(product);
return product;
}
}