package org.esa.snap.timeseries.core; import com.bc.ceres.core.ProgressMonitor; import com.bc.ceres.swing.progress.ProgressMonitorSwingWorker; import org.esa.snap.dataio.dimap.DimapProductConstants; import org.esa.snap.dataio.dimap.DimapProductReader; import org.esa.snap.dataio.dimap.DimapProductReaderPlugIn; import org.esa.snap.dataio.dimap.DimapProductWriter; import org.esa.snap.dataio.dimap.DimapProductWriterPlugIn; import org.esa.snap.core.dataio.ProductIOPlugInManager; import org.esa.snap.core.dataio.ProductReaderPlugIn; import org.esa.snap.core.dataio.ProductWriterPlugIn; import org.esa.snap.core.datamodel.MetadataAttribute; import org.esa.snap.core.datamodel.MetadataElement; import org.esa.snap.core.datamodel.Product; import org.esa.snap.core.datamodel.ProductData; import org.esa.snap.core.datamodel.ProductManager; import org.esa.snap.core.datamodel.ProductNode; import org.esa.snap.core.datamodel.RasterDataNode; import org.esa.snap.rcp.SnapApp; import org.esa.snap.rcp.SnapDialogs; import org.esa.snap.timeseries.core.timeseries.datamodel.AbstractTimeSeries; import org.esa.snap.timeseries.core.timeseries.datamodel.TimeSeriesFactory; import org.esa.snap.util.io.FileUtils; import org.openide.modules.OnStart; import org.openide.modules.OnStop; import java.awt.Window; import java.io.File; import java.net.URI; import java.util.Iterator; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import static org.esa.snap.timeseries.core.timeseries.datamodel.AbstractTimeSeries.*; public class TimeSeriesModule { public static final String TIME_PROPERTY = "timeProperty"; private static final ProductManager.Listener productManagerListener = createProductManagerListener(); private static final DimapProductWriter.WriterExtender writerExtender = createWriterExtender(); private static final DimapProductReader.ReaderExtender readerExtender = createReaderExtender(); @OnStart public static class StartOp implements Runnable { @Override public void run() { final ProductIOPlugInManager ioPlugInManager = ProductIOPlugInManager.getInstance(); final Iterator<ProductWriterPlugIn> allWriterPlugIns = ioPlugInManager.getWriterPlugIns(DimapProductConstants.DIMAP_FORMAT_NAME); while (allWriterPlugIns.hasNext()) { DimapProductWriterPlugIn writerPlugIn = (DimapProductWriterPlugIn) allWriterPlugIns.next(); writerPlugIn.addWriterExtender(writerExtender); } final Iterator<ProductReaderPlugIn> readerPlugIns = ioPlugInManager.getReaderPlugIns(DimapProductConstants.DIMAP_FORMAT_NAME); while (readerPlugIns.hasNext()) { DimapProductReaderPlugIn readerPlugin = (DimapProductReaderPlugIn) readerPlugIns.next(); readerPlugin.addReaderExtender(readerExtender); } SnapApp.getDefault().getProductManager().addListener(productManagerListener); } } @OnStop public static class StopOp implements Runnable { @Override public void run() { final ProductIOPlugInManager ioPlugInManager = ProductIOPlugInManager.getInstance(); final Iterator<ProductWriterPlugIn> writerPlugIns = ioPlugInManager.getWriterPlugIns(DimapProductConstants.DIMAP_FORMAT_NAME); while (writerPlugIns.hasNext()) { DimapProductWriterPlugIn writerPlugIn = (DimapProductWriterPlugIn) writerPlugIns.next(); writerPlugIn.removeWriterExtender(writerExtender); } final Iterator<ProductReaderPlugIn> readerPlugIns = ioPlugInManager.getReaderPlugIns(DimapProductConstants.DIMAP_FORMAT_NAME); while (readerPlugIns.hasNext()) { DimapProductReaderPlugIn readerPlugIn = (DimapProductReaderPlugIn) readerPlugIns.next(); readerPlugIn.removeReaderExtender(readerExtender); } SnapApp.getDefault().getProductManager().removeListener(productManagerListener); } } static void convertAbsolutPathsToRelative(Product product, File outputDir) { final MetadataElement tsRootElem = product.getMetadataRoot().getElement(AbstractTimeSeries.TIME_SERIES_ROOT_NAME); final MetadataElement productLocations = tsRootElem.getElement(AbstractTimeSeries.PRODUCT_LOCATIONS); final MetadataElement sourceProductPaths = tsRootElem.getElement(AbstractTimeSeries.SOURCE_PRODUCT_PATHS); replaceWithRelativePaths(productLocations.getElements(), outputDir); replaceWithRelativePaths(sourceProductPaths.getElements(), outputDir); } private static ProductManager.Listener createProductManagerListener() { return new ProductManager.Listener() { @Override public void productAdded(ProductManager.Event event) { } @Override public void productRemoved(ProductManager.Event event) { final Product product = event.getProduct(); if (product.getProductType().equals(TIME_SERIES_PRODUCT_TYPE)) { final TimeSeriesMapper timeSeriesMapper = TimeSeriesMapper.getInstance(); final AbstractTimeSeries timeSeries = timeSeriesMapper.getTimeSeries(product); final Product[] sourceProducts = timeSeries.getSourceProducts(); final ProductManager productManager = SnapApp.getDefault().getProductManager(); for (Product sourceProduct : sourceProducts) { if (!productManager.contains(sourceProduct)) { sourceProduct.dispose(); } } timeSeriesMapper.remove(product); } } }; } private static DimapProductWriter.WriterExtender createWriterExtender() { return new DimapProductWriter.WriterExtender() { @Override public boolean vetoableShouldWrite(ProductNode node) { if (!isTimeSerisProduct(node.getProduct())) { return true; } else if (node instanceof RasterDataNode) { return false; } return true; } @Override public void intendToWriteDimapHeaderTo(File outputDir, Product product) { if (isTimeSerisProduct(product)) { convertAbsolutPathsToRelative(product, outputDir); } } private boolean isTimeSerisProduct(Product product) { return product.getProductType().equals(AbstractTimeSeries.TIME_SERIES_PRODUCT_TYPE); } }; } private static void replaceWithRelativePaths(MetadataElement[] elements, File outputDir) { for (MetadataElement element : elements) { final String pathName = element.getAttributeString(AbstractTimeSeries.PL_PATH); final URI relativeUri = FileUtils.getRelativeUri(outputDir.toURI(), new File(pathName)); final MetadataAttribute pathAttr = element.getAttribute(AbstractTimeSeries.PL_PATH); final MetadataAttribute typeAttr = element.getAttribute(AbstractTimeSeries.PL_TYPE); element.removeAttribute(pathAttr); element.removeAttribute(typeAttr); pathAttr.dispose(); final MetadataAttribute newPathAttr = new MetadataAttribute(AbstractTimeSeries.PL_PATH, ProductData.createInstance(relativeUri.toString()), true); element.addAttribute(newPathAttr); element.addAttribute(typeAttr); } } private static DimapProductReader.ReaderExtender createReaderExtender() { return new DimapProductReader.ReaderExtender() { @Override public void completeProductNodesReading(final Product product) { if (product.getProductType().equals(TIME_SERIES_PRODUCT_TYPE)) { final SnapApp snapApp = SnapApp.getDefault(); final Window appWindow = snapApp.getMainFrame(); final ProgressMonitorSwingWorker<AbstractTimeSeries, Void> pm = new ProgressMonitorSwingWorker<AbstractTimeSeries, Void>(appWindow, "Creating Time Series...") { @Override protected void done() { try { get(); } catch (InterruptedException e) { handleError(e); } catch (ExecutionException e) { handleError(e.getCause()); } super.done(); } private void handleError(Throwable theCause) { snapApp.getLogger().log(Level.SEVERE, theCause.getMessage()); SnapDialogs.showError("Could not load time series", theCause.getMessage()); } @Override protected AbstractTimeSeries doInBackground(ProgressMonitor pm) throws Exception { return TimeSeriesFactory.create(product, pm); } }; pm.executeWithBlocking(); } } }; } }