/* * 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.Property; import com.bc.ceres.binding.PropertyDescriptor; import com.bc.ceres.binding.PropertySet; import com.bc.ceres.binding.ValidationException; import com.bc.ceres.binding.accessors.DefaultPropertyAccessor; import com.bc.ceres.swing.binding.BindingContext; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.io.ParseException; import com.vividsolutions.jts.io.WKTReader; import org.esa.snap.binning.AggregatorConfig; import org.esa.snap.binning.operator.BinningOp; import org.esa.snap.binning.operator.VariableConfig; import org.esa.snap.core.datamodel.Band; import org.esa.snap.core.datamodel.FlagCoding; import org.esa.snap.core.datamodel.Mask; import org.esa.snap.core.datamodel.Product; import org.esa.snap.core.datamodel.ProductData; import org.esa.snap.core.datamodel.TiePointGrid; import org.esa.snap.core.datamodel.VectorDataNode; import org.esa.snap.core.gpf.annotations.ParameterDescriptorFactory; import org.esa.snap.core.util.StringUtils; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.HashMap; /** * The model responsible for managing the binning parameters. * * @author Thomas Storm */ class BinningFormModel { static final String PROPERTY_KEY_WEST_BOUND = "westBound"; static final String PROPERTY_KEY_NORTH_BOUND = "northBound"; static final String PROPERTY_KEY_EAST_BOUND = "eastBound"; static final String PROPERTY_KEY_SOUTH_BOUND = "southBound"; static final String PROPERTY_KEY_WKT = "manualWkt"; static final String PROPERTY_KEY_AGGREGATOR_CONFIGS = "aggregatorConfigs"; static final String PROPERTY_KEY_VARIABLE_CONFIGS = "variableConfigs"; static final String PROPERTY_KEY_REGION = "region"; static final String PROPERTY_KEY_BOUNDS = "bounds"; static final String PROPERTY_KEY_COMPUTE_REGION = "compute"; static final String PROPERTY_KEY_GLOBAL = "global"; static final String PROPERTY_KEY_MASK_EXPR = "maskExpr"; static final String PROPERTY_KEY_TIME_FILTER_METHOD = "timeFilterMethod"; static final String PROPERTY_KEY_START_DATE_TIME = "startDateTime"; static final String PROPERTY_KEY_PERIOD_DURATION = "periodDuration"; static final String PROPERTY_KEY_MIN_DATA_HOUR = "minDataHour"; static final String PROPERTY_KEY_NUM_ROWS = "numRows"; static final String PROPERTY_KEY_SUPERSAMPLING = "superSampling"; static final String PROPERTY_KEY_MANUAL_WKT = "manualWktKey"; static final String PROPERTY_KEY_SOURCE_PRODUCTS = "sourceProducts"; static final String PROPERTY_KEY_SOURCE_PRODUCT_PATHS = "sourceProductPaths"; static final String PROPERTY_KEY_CONTEXT_SOURCE_PRODUCT = "contextSourceProduct"; static final String GLOBAL_WKT = "polygon((-180 -90, 180 -90, 180 90, -180 90, -180 -90))"; static final int DEFAULT_NUM_ROWS = 2160; private PropertySet propertySet; private BindingContext bindingContext; private HashMap<String, Object> parameterMap; public BinningFormModel() { parameterMap = new HashMap<>(); propertySet = ParameterDescriptorFactory.createMapBackedOperatorPropertyContainer("Binning", parameterMap); hideProperties(); // Just for GUI propertySet.addProperty(createTransientProperty(PROPERTY_KEY_GLOBAL, Boolean.class)); // temp propertySet.addProperty(createTransientProperty(PROPERTY_KEY_COMPUTE_REGION, Boolean.class)); // temp propertySet.addProperty(createTransientProperty(PROPERTY_KEY_MANUAL_WKT, Boolean.class)); // temp propertySet.addProperty(createTransientProperty(PROPERTY_KEY_WKT, String.class)); // temp propertySet.addProperty(createTransientProperty(PROPERTY_KEY_BOUNDS, Boolean.class)); // temp propertySet.addProperty(createTransientProperty(PROPERTY_KEY_EAST_BOUND, Double.class)); // temp propertySet.addProperty(createTransientProperty(PROPERTY_KEY_NORTH_BOUND, Double.class)); // temp propertySet.addProperty(createTransientProperty(PROPERTY_KEY_WEST_BOUND, Double.class)); // temp propertySet.addProperty(createTransientProperty(PROPERTY_KEY_SOUTH_BOUND, Double.class)); // temp propertySet.addProperty(createTransientProperty(PROPERTY_KEY_SOURCE_PRODUCTS, Product[].class)); // temp propertySet.addProperty(createTransientProperty(PROPERTY_KEY_CONTEXT_SOURCE_PRODUCT, Product.class)); // temp propertySet.setDefaultValues(); propertySet.getProperty(PROPERTY_KEY_REGION).addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { Geometry newGeometry = (Geometry) evt.getNewValue(); propertySet.setValue(PROPERTY_KEY_MANUAL_WKT, true); propertySet.setValue(PROPERTY_KEY_WKT, newGeometry.toText()); } }); } void hideProperties() { // those properties are not shown in GUI and shall not go into parameter file propertySet.getProperty("metadataPropertiesFile").getDescriptor().setTransient(true); propertySet.getProperty("metadataTemplateDir").getDescriptor().setTransient(true); propertySet.getProperty("outputType").getDescriptor().setTransient(true); propertySet.getProperty("outputFormat").getDescriptor().setTransient(true); propertySet.getProperty("outputBinnedData").getDescriptor().setTransient(true); propertySet.getProperty("outputMappedProduct").getDescriptor().setTransient(true); } public PropertySet getPropertySet() { return propertySet; } public HashMap<String, Object> getParameterMap() { return parameterMap; } public Product[] getSourceProducts() { final Product[] products = getPropertyValue(BinningFormModel.PROPERTY_KEY_SOURCE_PRODUCTS); if (products == null) { return new Product[0]; } return products; } public String[] getSourceProductPaths() { return getPropertyValue(BinningFormModel.PROPERTY_KEY_SOURCE_PRODUCT_PATHS); } public Product getContextProduct() { return getPropertyValue(BinningFormModel.PROPERTY_KEY_CONTEXT_SOURCE_PRODUCT); } public void useAsContextProduct(Product contextProduct) { Product currentContextProduct = createContextProduct(contextProduct); propertySet.setValue(BinningFormModel.PROPERTY_KEY_CONTEXT_SOURCE_PRODUCT, currentContextProduct); } private Product createContextProduct(Product srcProduct) { if (srcProduct == null) { return null; } Product contextProduct = new Product("contextProduct", srcProduct.getProductType(), 10, 10); for (String flagCodingName : srcProduct.getFlagCodingGroup().getNodeNames()) { FlagCoding srcFC = srcProduct.getFlagCodingGroup().get(flagCodingName); String[] flagNames = srcFC.getFlagNames(); FlagCoding contextFC = new FlagCoding(flagCodingName); for (String flagName : flagNames) { contextFC.addFlag(flagName, srcFC.getFlagMask(flagName), srcFC.getFlag(flagName).getDescription()); } contextProduct.getFlagCodingGroup().add(contextFC); } for (Band srcBand : srcProduct.getBands()) { Band contextBand = contextProduct.addBand(srcBand.getName(), srcBand.getDataType()); if (srcBand.isFlagBand()) { contextBand.setSampleCoding(contextProduct.getFlagCodingGroup().get(srcBand.getFlagCoding().getName())); } } for (TiePointGrid grid : srcProduct.getTiePointGrids()) { contextProduct.addTiePointGrid(new TiePointGrid(grid.getName(), 10, 10, 0, 0, 1, 1, new float[100])); } for (String vectorDataName : srcProduct.getVectorDataGroup().getNodeNames()) { VectorDataNode vectorData = srcProduct.getVectorDataGroup().get(vectorDataName); contextProduct.getVectorDataGroup().add(new VectorDataNode(vectorDataName, vectorData.getFeatureType())); } for (String maskName : srcProduct.getMaskGroup().getNodeNames()) { Mask mask = srcProduct.getMaskGroup().get(maskName); contextProduct.addMask(maskName, mask.getImageType()); } VariableConfig[] variableConfigs = getVariableConfigs(); for (VariableConfig variableConfig : variableConfigs) { contextProduct.addBand(new VariableConfigBand(variableConfig.getName())); } return contextProduct; } public AggregatorConfig[] getAggregatorConfigs() { AggregatorConfig[] aggregatorConfigs = getPropertyValue(PROPERTY_KEY_AGGREGATOR_CONFIGS); if (aggregatorConfigs == null) { aggregatorConfigs = new AggregatorConfig[0]; } return aggregatorConfigs; } public VariableConfig[] getVariableConfigs() { VariableConfig[] variableConfigs = getPropertyValue(PROPERTY_KEY_VARIABLE_CONFIGS); if (variableConfigs == null) { variableConfigs = new VariableConfig[0]; } return variableConfigs; } public void setVariableConfigs(VariableConfig[] variableConfigs) throws ValidationException { if (variableConfigs == null) { variableConfigs = new VariableConfig[0]; } setProperty(PROPERTY_KEY_VARIABLE_CONFIGS, variableConfigs); Product contextProduct = getContextProduct(); if (contextProduct != null) { removeAllVariableConfigBands(contextProduct); for (VariableConfig variableConfig : variableConfigs) { contextProduct.addBand(new VariableConfigBand(variableConfig.getName())); } } } void removeAllVariableConfigBands(Product contextProduct) { Band[] bands = contextProduct.getBands(); for (Band band : bands) { if(band instanceof VariableConfigBand) { contextProduct.removeBand(band); } } } public Geometry getRegion() { if (Boolean.TRUE.equals(getPropertyValue(PROPERTY_KEY_GLOBAL))) { return toGeometry(GLOBAL_WKT); } else if (Boolean.TRUE.equals(getPropertyValue(PROPERTY_KEY_COMPUTE_REGION))) { return null; } else if (Boolean.TRUE.equals(getPropertyValue(PROPERTY_KEY_BOUNDS))) { final double westValue = getPropertyValue(PROPERTY_KEY_WEST_BOUND); final double eastValue = getPropertyValue(PROPERTY_KEY_EAST_BOUND); final double northValue = getPropertyValue(PROPERTY_KEY_NORTH_BOUND); final double southValue = getPropertyValue(PROPERTY_KEY_SOUTH_BOUND); Coordinate[] coordinates = { new Coordinate(westValue, southValue), new Coordinate(westValue, northValue), new Coordinate(eastValue, northValue), new Coordinate(eastValue, southValue), new Coordinate(westValue, southValue) }; final GeometryFactory geometryFactory = new GeometryFactory(); return geometryFactory.createPolygon(geometryFactory.createLinearRing(coordinates), null); } else if (Boolean.TRUE.equals(getPropertyValue(PROPERTY_KEY_MANUAL_WKT))) { return toGeometry((String) getPropertyValue(PROPERTY_KEY_WKT)); } throw new IllegalStateException("Should never come here"); } Geometry toGeometry(String wkt) { try { return new WKTReader().read(wkt); } catch (ParseException e) { throw new IllegalStateException("WKT for region is not valid:\n" + wkt); } } public String getMaskExpr() { final String propertyValue = getPropertyValue(PROPERTY_KEY_MASK_EXPR); if (StringUtils.isNullOrEmpty(propertyValue)) { return "true"; } return propertyValue; } public BinningOp.TimeFilterMethod getTimeFilterMethod() { return propertySet.getProperty(PROPERTY_KEY_TIME_FILTER_METHOD).getValue(); } public String getStartDateTime() { BinningOp.TimeFilterMethod temporalFilter = getPropertyValue(PROPERTY_KEY_TIME_FILTER_METHOD); switch (temporalFilter) { case NONE: { return null; } case TIME_RANGE: case SPATIOTEMPORAL_DATA_DAY: { return getPropertyValue(PROPERTY_KEY_START_DATE_TIME); } } throw new IllegalStateException("Illegal temporal filter method: '" + temporalFilter + "'"); } public Double getPeriodDuration() { return getPropertyValue(PROPERTY_KEY_PERIOD_DURATION); } public Double getMinDataHour() { return getPropertyValue(PROPERTY_KEY_MIN_DATA_HOUR); } public int getSuperSampling() { if (getPropertyValue(PROPERTY_KEY_SUPERSAMPLING) == null) { return 1; } return (Integer) getPropertyValue(PROPERTY_KEY_SUPERSAMPLING); } public int getNumRows() { if (getPropertyValue(PROPERTY_KEY_NUM_ROWS) == null) { return DEFAULT_NUM_ROWS; } return (Integer) getPropertyValue(PROPERTY_KEY_NUM_ROWS); } public void setProperty(String key, Object value) throws ValidationException { if (propertySet.isPropertyDefined(key)) { final Property property = propertySet.getProperty(key); property.setValue(value); } else { throw new IllegalStateException("Unknown property: " + key); } } public void addPropertyChangeListener(PropertyChangeListener propertyChangeListener) { propertySet.addPropertyChangeListener(propertyChangeListener); } public BindingContext getBindingContext() { if (bindingContext == null) { bindingContext = new BindingContext(propertySet); } return bindingContext; } @SuppressWarnings("unchecked") <T> T getPropertyValue(String key) { final Property property = propertySet.getProperty(key); if (property != null) { return (T) property.getValue(); } return null; } private static Property createTransientProperty(String name, Class type) { final DefaultPropertyAccessor defaultAccessor = new DefaultPropertyAccessor(); final PropertyDescriptor descriptor = new PropertyDescriptor(name, type); descriptor.setTransient(true); descriptor.setDefaultConverter(); return new Property(descriptor, defaultAccessor); } private static class VariableConfigBand extends Band { public VariableConfigBand(String varName) { super(varName, ProductData.TYPE_FLOAT32, 10, 10); } } }