/* * Copyright (C) 2014 Brockmann Consult GmbH (info@brockmann-consult.de) * * 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.snap.binning.operator.ui; import com.bc.ceres.binding.ConversionException; import com.bc.ceres.binding.Property; import com.bc.ceres.binding.PropertySet; import com.bc.ceres.binding.ValidationException; import com.bc.ceres.core.ProgressMonitor; import com.bc.ceres.swing.progress.ProgressMonitorSwingWorker; import org.esa.snap.binning.AggregatorConfig; import org.esa.snap.binning.AggregatorDescriptor; import org.esa.snap.binning.TypedDescriptorsRegistry; import org.esa.snap.binning.operator.BinningOp; import org.esa.snap.binning.operator.VariableConfig; import org.esa.snap.core.datamodel.Product; import org.esa.snap.core.gpf.GPF; import org.esa.snap.core.gpf.OperatorSpi; import org.esa.snap.core.gpf.ui.OperatorMenu; import org.esa.snap.core.gpf.ui.OperatorParameterSupport; import org.esa.snap.core.gpf.ui.ParameterUpdater; import org.esa.snap.core.gpf.ui.SingleTargetProductDialog; import org.esa.snap.core.gpf.ui.TargetProductSelectorModel; import org.esa.snap.core.util.io.FileUtils; import org.esa.snap.ui.AppContext; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; /** * UI for binning operator. * * @author Olaf Danne * @author Thomas Storm */ public class BinningDialog extends SingleTargetProductDialog { private static final String OPERATOR_NAME = "Binning"; private final BinningForm form; private final BinningFormModel formModel; protected BinningDialog(AppContext appContext, String title, String helpID) { super(appContext, title, ID_APPLY_CLOSE_HELP, helpID, new TargetProductSelectorModel(), true); formModel = new BinningFormModel(); form = new BinningForm(appContext, formModel, getTargetProductSelector()); OperatorSpi operatorSpi = GPF.getDefaultInstance().getOperatorSpiRegistry().getOperatorSpi(OPERATOR_NAME); ParameterUpdater parameterUpdater = new BinningParameterUpdater(); OperatorParameterSupport parameterSupport = new OperatorParameterSupport(operatorSpi.getOperatorDescriptor(), formModel.getPropertySet(), formModel.getParameterMap(), parameterUpdater); OperatorMenu operatorMenu = new OperatorMenu(this.getJDialog(), operatorSpi.getOperatorDescriptor(), parameterSupport, appContext, helpID); getJDialog().setJMenuBar(operatorMenu.createDefaultMenu()); } @Override protected boolean verifyUserInput() { AggregatorConfig[] aggregatorConfigs = formModel.getAggregatorConfigs(); if (!isAtLeastOneAggreatorConfigDefined(aggregatorConfigs)) { return false; } if (!doUsedVariablesStillExist(aggregatorConfigs)) { return false; } if (!areTargetNamesUnique(aggregatorConfigs)) { return false; } if (!isTimeFilterWellConfigured()) { return false; } return true; } @Override protected Product createTargetProduct() throws Exception { final TargetProductCreator targetProductCreator = new TargetProductCreator(); targetProductCreator.executeWithBlocking(); return targetProductCreator.get(); } @Override public int show() { setContent(form); return super.show(); } @Override public void hide() { form.prepareClose(); super.hide(); } private boolean doUsedVariablesStillExist(AggregatorConfig[] aggregatorConfigs) { Product contextProduct = formModel.getContextProduct(); // assuming that the variables are defined in the context product TypedDescriptorsRegistry registry = TypedDescriptorsRegistry.getInstance(); for (AggregatorConfig aggregatorConfig : aggregatorConfigs) { String aggregatorConfigName = aggregatorConfig.getName(); AggregatorDescriptor descriptor = registry.getDescriptor(AggregatorDescriptor.class, aggregatorConfigName); String[] sourceVarNames = descriptor.getSourceVarNames(aggregatorConfig); for (String sourceVarName : sourceVarNames) { if (Objects.isNull(contextProduct) || !contextProduct.containsBand(sourceVarName)) { String msg = String.format( "Source band name '%s' of aggregator '%s' is unknown.\nIt is neither one of the bands of the source products,\n" + "nor is it defined by an intermediate source band.", sourceVarName, aggregatorConfigName ); showErrorDialog(msg); return false; } } } return true; } private boolean isTimeFilterWellConfigured() { if (formModel.getTimeFilterMethod() == BinningOp.TimeFilterMethod.SPATIOTEMPORAL_DATA_DAY || formModel.getTimeFilterMethod() == BinningOp.TimeFilterMethod.TIME_RANGE) { if (formModel.getStartDateTime() == null) { showErrorDialog("Start date/time must be provided when time filter method 'spatiotemporal data day' or 'time range' is chosen."); return false; } if (formModel.getPeriodDuration() == null) { showErrorDialog("Period duration must be provided when time filter method 'spatiotemporal data day' or 'time range' is chosen."); return false; } } if (formModel.getTimeFilterMethod() == BinningOp.TimeFilterMethod.SPATIOTEMPORAL_DATA_DAY) { if (formModel.getMinDataHour() == null) { showErrorDialog("Min data hour must be provided when time filter method 'spatiotemporal data day' is chosen."); return false; } } return true; } private boolean isAtLeastOneAggreatorConfigDefined(AggregatorConfig[] aggregatorConfigs) { if (aggregatorConfigs.length == 0) { showErrorDialog("Please configure at least a single aggregator."); return false; } return true; } private boolean areTargetNamesUnique(AggregatorConfig[] aggregatorConfigs) { List<String> targetVarNameList = new ArrayList<>(); TypedDescriptorsRegistry registry = TypedDescriptorsRegistry.getInstance(); for (AggregatorConfig aggregatorConfig : aggregatorConfigs) { AggregatorDescriptor descriptor = registry.getDescriptor(AggregatorDescriptor.class, aggregatorConfig.getName()); String[] targetNames = descriptor.getTargetVarNames(aggregatorConfig); for (String targetName : targetNames) { if (targetVarNameList.contains(targetName)) { showErrorDialog(String.format("The target band with the name '%s' is defined twice.", targetName)); return false; } else { targetVarNameList.add(targetName); } } } return true; } private void updateParameterMap(Map<String, Object> parameters) { parameters.put("variableConfigs", formModel.getVariableConfigs()); parameters.put("aggregatorConfigs", formModel.getAggregatorConfigs()); parameters.put("outputFile", getTargetProductSelector().getModel().getProductFile().getPath()); parameters.put("maskExpr", formModel.getMaskExpr()); parameters.put("region", formModel.getRegion()); parameters.put("numRows", formModel.getNumRows()); parameters.put("superSampling", formModel.getSuperSampling()); parameters.put("sourceProductPaths", formModel.getSourceProductPaths()); BinningOp.TimeFilterMethod method = formModel.getTimeFilterMethod(); parameters.put("timeFilterMethod", method); if (method == BinningOp.TimeFilterMethod.SPATIOTEMPORAL_DATA_DAY) { parameters.put("minDataHour", formModel.getMinDataHour()); parameters.put("startDateTime", formModel.getStartDateTime()); parameters.put("periodDuration", formModel.getPeriodDuration()); } else if (method == BinningOp.TimeFilterMethod.TIME_RANGE) { parameters.put("startDateTime", formModel.getStartDateTime()); parameters.put("periodDuration", formModel.getPeriodDuration()); } } private void updateFormModel(Map<String, Object> parameterMap) throws ValidationException { final PropertySet propertySet = formModel.getBindingContext().getPropertySet(); final Set<Map.Entry<String, Object>> entries = parameterMap.entrySet(); for (Map.Entry<String, Object> entry : entries) { Property property = propertySet.getProperty(entry.getKey()); if (property != null) { property.setValue(entry.getValue()); } } if (parameterMap.containsKey("outputFile")) { File outputFile = new File((String) parameterMap.get("outputFile")); File outputDir = outputFile.getParentFile(); if (outputDir != null) { getTargetProductSelector().getModel().setProductDir(outputDir); } getTargetProductSelector().getModel().setProductName(FileUtils.getFilenameWithoutExtension(outputFile)); } BinningConfigurationPanel configurationPanel = form.getBinningConfigurationPanel(); VariableTableController variableTableController = configurationPanel.getVariableTableController(); VariableConfig[] variableConfigs = new VariableConfig[0]; if (parameterMap.containsKey("variableConfigs")) { variableConfigs = (VariableConfig[]) parameterMap.get("variableConfigs"); } variableTableController.setVariableConfigs(variableConfigs); AggregatorTableController aggregatorTableController = configurationPanel.getAggregatorTableController(); AggregatorConfig[] aggregatorConfigs = new AggregatorConfig[0]; if (parameterMap.containsKey("aggregatorConfigs")) { aggregatorConfigs = (AggregatorConfig[]) parameterMap.get("aggregatorConfigs"); } aggregatorTableController.setAggregatorConfigs(aggregatorConfigs); } private class TargetProductCreator extends ProgressMonitorSwingWorker<Product, Void> { protected TargetProductCreator() { super(BinningDialog.this.getJDialog(), "Creating target product"); } @Override protected Product doInBackground(ProgressMonitor pm) throws Exception { pm.beginTask("Binning...", 100); final Map<String, Object> parameters = new HashMap<>(); updateParameterMap(parameters); final Product targetProduct = GPF.createProduct("Binning", parameters, formModel.getSourceProducts()); pm.done(); return targetProduct; } } private class BinningParameterUpdater implements ParameterUpdater { @Override public void handleParameterSaveRequest(Map<String, Object> parameterMap) throws ValidationException, ConversionException { formModel.getBindingContext().adjustComponents(); updateParameterMap(parameterMap); } @Override public void handleParameterLoadRequest(Map<String, Object> parameterMap) throws ValidationException, ConversionException { updateFormModel(parameterMap); formModel.getBindingContext().adjustComponents(); } } }