package org.esa.beam.smos.ee2netcdf;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.geom.Polygon;
import org.esa.beam.dataio.smos.DggFile;
import org.esa.beam.dataio.smos.DggUtils;
import org.esa.beam.dataio.smos.ProductFile;
import org.esa.beam.dataio.smos.SmosProductReader;
import org.esa.beam.framework.dataio.ProductIO;
import org.esa.beam.framework.dataio.ProductSubsetDef;
import org.esa.beam.framework.datamodel.GeoCoding;
import org.esa.beam.framework.datamodel.GeoPos;
import org.esa.beam.framework.datamodel.PixelPos;
import org.esa.beam.framework.datamodel.Product;
import org.esa.beam.framework.gpf.OperatorException;
import org.esa.beam.framework.gpf.OperatorSpi;
import org.esa.beam.framework.gpf.annotations.OperatorMetadata;
import org.esa.beam.util.io.FileUtils;
import java.awt.*;
import java.awt.geom.Area;
import java.awt.geom.PathIterator;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.TreeSet;
@SuppressWarnings("MismatchedReadAndWriteOfArray")
@OperatorMetadata(
alias = EEToNetCDFExporterOp.ALIAS,
version = "1.0",
authors = "Tom Block",
copyright = "(c) 2013, 2014 by Brockmann Consult",
description = "Converts SMOS EE Products to NetCDF format.",
autoWriteDisabled = true)
public class EEToNetCDFExporterOp extends AbstractNetCDFExporterOp {
public static final String ALIAS = "SmosEE2NetCDF";
@Override
public void initialize() throws OperatorException {
setDummyTargetProduct();
ExporterUtils.assertTargetDirectoryExists(targetDirectory);
if (sourceProducts != null) {
for (Product sourceProduct : sourceProducts) {
exportProduct(sourceProduct);
}
}
if (sourceProductPaths != null) {
final TreeSet<File> sourceFileSet = ExporterUtils.createInputFileSet(sourceProductPaths);
for (File inputFile : sourceFileSet) {
exportFile(inputFile);
}
}
}
public static File getOutputFile(File dblFile, File targetDirectory) {
File outFile = new File(targetDirectory, dblFile.getName());
outFile = FileUtils.exchangeExtension(outFile, ".nc");
return outFile;
}
// package access for testing only tb 2013-03-26
static MultiPolygon convertToPolygon(Area dataArea) {
final PathIterator pathIterator = dataArea.getPathIterator(null);
final ArrayList<double[]> coordList = new ArrayList<>();
final ArrayList<Polygon> polygonList = new ArrayList<>();
final GeometryFactory geometryFactory = new GeometryFactory();
while (!pathIterator.isDone()) {
final double[] coords = new double[6];
final int segType = pathIterator.currentSegment(coords);
if (segType == PathIterator.SEG_CLOSE) {
coordList.add(coordList.get(0));
final Coordinate[] coordinates = convert(coordList);
final Polygon polygon = geometryFactory.createPolygon(geometryFactory.createLinearRing(coordinates),
null);
polygonList.add(polygon);
coordList.clear();
} else {
coordList.add(coords);
}
pathIterator.next();
}
final Polygon[] polygons = polygonList.toArray(new Polygon[polygonList.size()]);
return new MultiPolygon(polygons, geometryFactory);
}
// package access for testing tb 2013-03-26
static Coordinate[] convert(ArrayList<double[]> coordList) {
final Coordinate[] coordinates = new Coordinate[coordList.size()];
for (int i = 0; i < coordinates.length; i++) {
final double[] coord = coordList.get(i);
coordinates[i] = new Coordinate(coord[0], coord[1]);
}
return coordinates;
}
// package access for testing only tb 2013-03-26
static Rectangle getDataBoundingRect(Product sourceProduct, Geometry dataArea) throws IOException {
final GeoCoding geoCoding = sourceProduct.getGeoCoding();
final GeoPos geoPos = new GeoPos(0.f, 0.f);
final PixelPos pixelPos = new PixelPos(0.f, 0.f);
double min_x = Integer.MAX_VALUE;
double max_x = Integer.MIN_VALUE;
double min_y = Integer.MAX_VALUE;
double max_y = Integer.MIN_VALUE;
final Coordinate[] coordinates = dataArea.getCoordinates();
for (final Coordinate coordinate : coordinates) {
geoPos.setLocation((float) coordinate.y, (float) coordinate.x);
geoCoding.getPixelPos(geoPos, pixelPos);
double ceil = Math.ceil(pixelPos.x);
if (ceil > max_x) {
max_x = ceil;
}
double floor = Math.floor(pixelPos.x);
if (floor < min_x) {
min_x = floor;
}
ceil = Math.ceil(pixelPos.y);
if (ceil > max_y) {
max_y = ceil;
}
floor = Math.floor(pixelPos.y);
if (floor < min_y) {
min_y = floor;
}
}
return new Rectangle((int) min_x, (int) min_y, (int) (max_x - min_x), (int) (max_y - min_y));
}
// package access for testing only - tb 2013-03-27
static ProductSubsetDef createSubsetDef(Rectangle rectangle) {
final ProductSubsetDef subsetDef = new ProductSubsetDef();
subsetDef.setRegion(rectangle);
return subsetDef;
}
private void exportFile(File inputFile) {
Product product = null;
try {
product = ProductIO.readProduct(inputFile);
if (product != null) {
final String productType = product.getProductType();
if (productType.matches(ExportParameter.PRODUCT_TYPE_REGEX)) {
exportProduct(product);
} else {
getLogger().info("Unable to convert file: " + inputFile.getAbsolutePath());
getLogger().info("Unsupported product of type: " + productType);
}
} else {
getLogger().warning("Unable to open file: " + inputFile.getAbsolutePath());
}
} catch (Exception e) {
getLogger().severe("Failed to convert file: " + inputFile.getAbsolutePath());
getLogger().severe(e.getMessage());
} finally {
if (product != null) {
product.dispose();
}
}
}
private void exportProduct(Product sourceProduct) {
try {
final SmosProductReader productReader = (SmosProductReader) sourceProduct.getProductReader();
final ProductFile productFile = productReader.getProductFile();
if (productFile instanceof DggFile) {
final Area dataArea = DggUtils.computeArea(((DggFile) productFile).getGridPointList());
Geometry polygon = convertToPolygon(dataArea);
if (region != null) {
polygon = region.intersection(polygon);
}
if (polygon.isEmpty()) {
getLogger().info("No geometric intersection: " + sourceProduct.getFileLocation());
return;
}
final Rectangle x_y_subset = getDataBoundingRect(sourceProduct, polygon);
final ProductSubsetDef subsetDef = createSubsetDef(x_y_subset);
final Product subset = sourceProduct.createSubset(subsetDef, "", "");
final File outFile = getOutputFile(productFile.getDataFile(), targetDirectory);
if (outFile.isFile() && overwriteTarget) {
if (!outFile.delete()) {
throw new IOException(
"Unable to delete already existing product: " + outFile.getAbsolutePath());
}
}
if (!outFile.createNewFile()) {
throw new IOException("Unable to create target product: " + outFile.getAbsolutePath());
}
ProductIO.writeProduct(subset, outFile, "NetCDF4-CF", false);
getLogger().info("Successfully converted: " + sourceProduct.getFileLocation());
} else {
getLogger().warning("Cannot convert file: " + sourceProduct.getFileLocation());
}
} catch (IOException e) {
getLogger().severe("Failed to convert file: " + sourceProduct.getFileLocation());
getLogger().severe(e.getMessage());
}
}
public static class Spi extends OperatorSpi {
public Spi() {
super(EEToNetCDFExporterOp.class);
}
}
}