/*
* Copyright (C) 2011 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.rcp.statistics;
import org.esa.snap.core.datamodel.Mask;
import org.esa.snap.core.datamodel.PlacemarkDescriptor;
import org.esa.snap.core.datamodel.ProductNodeGroup;
import org.esa.snap.core.datamodel.VectorDataNode;
import org.esa.snap.core.util.SystemUtils;
import org.esa.snap.rcp.SnapApp;
import org.esa.snap.rcp.util.Dialogs;
import org.esa.snap.statistics.output.BandNameCreator;
import org.esa.snap.statistics.output.FeatureStatisticsWriter;
import org.esa.snap.statistics.output.StatisticsOutputContext;
import org.esa.snap.statistics.output.Util;
import org.esa.snap.ui.product.ProductSceneView;
import org.geotools.data.collection.ListFeatureCollection;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import javax.media.jai.Histogram;
import javax.swing.AbstractAction;
import javax.swing.JOptionPane;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author Thomas Storm
*/
class PutStatisticsIntoVectorDataAction extends AbstractAction {
private Mask[] selectedMasks;
private final Map<SimpleFeatureType, VectorDataNode> featureType2VDN = new HashMap<>();
private final Map<SimpleFeatureType, Set<Mask>> featureType2Mask = new HashMap<>();
private final Map<Mask, Histogram> mask2Histogram = new HashMap<>();
private final Map<Mask, String> mask2RegionName = new HashMap<>();
private final StatisticsDataProvider provider;
PutStatisticsIntoVectorDataAction(StatisticsDataProvider provider) {
super("Put statistics into vector data");
this.provider = provider;
}
@Override
public boolean isEnabled() {
boolean hasSelectedMasks = hasSelectedMasks();
final boolean hasTarget = getFeatureTypes().length != 0;
return super.isEnabled() && hasSelectedMasks && hasTarget;
}
private boolean hasSelectedMasks() {
boolean hasSelectedMasks = selectedMasks != null && selectedMasks.length != 0;
if (hasSelectedMasks) {
for (Mask selectedMask : selectedMasks) {
if (selectedMask != null) {
break;
}
hasSelectedMasks = false;
}
}
return hasSelectedMasks;
}
@Override
public void actionPerformed(ActionEvent e) {
if (selectedMasks[0] == null) {
return;
}
for (final SimpleFeatureType featureType : getFeatureTypes()) {
final VectorDataNode originalVDN = featureType2VDN.get(featureType);
if (originalVDN.isPermanent()) {
SystemUtils.LOG.warning("Unable to put statistics into permanent vector data.");
Dialogs.showError("Unable to put statistics into permanent vector data (such as pins/GCPs).");
continue;
}
FeatureStatisticsWriter featureStatisticsWriter =
FeatureStatisticsWriter.createFeatureStatisticsWriter(getFeatureCollection(featureType),
null,
new BandNameCreator());
featureStatisticsWriter.initialiseOutput(
StatisticsOutputContext.create(
new String[]{provider.getRasterDataNode().getName()},
new String[]{
"minimum",
"maximum",
"median",
"average",
"sigma",
"p90",
"p95",
"pxx_max_error",
"total"
}));
for (final Mask mask : getMasks(featureType)) {
HashMap<String, Number> statistics = new HashMap<>();
Histogram histogram = getHistogram(mask);
statistics.put("minimum", histogram.getLowValue(0));
statistics.put("maximum", histogram.getHighValue(0));
statistics.put("median", histogram.getPTileThreshold(0.5)[0]);
statistics.put("average", histogram.getMean()[0]);
statistics.put("sigma", histogram.getStandardDeviation()[0]);
statistics.put("p90", histogram.getPTileThreshold(0.9)[0]);
statistics.put("p95", histogram.getPTileThreshold(0.95)[0]);
statistics.put("pxx_max_error", StatisticsPanel.getBinSize(histogram));
statistics.put("total", histogram.getTotals()[0]);
featureStatisticsWriter.addToOutput(provider.getRasterDataNode().getName(), mask2RegionName.get(mask),
statistics);
}
exchangeVDN(featureType, featureStatisticsWriter);
Dialogs.showMessage("Extending vector data with statistics",
"The vector data have successfully been extended with the computed statistics.",
JOptionPane.INFORMATION_MESSAGE, null);
}
}
private void exchangeVDN(SimpleFeatureType featureType, FeatureStatisticsWriter featureStatisticsWriter) {
final VectorDataNode originalVDN = featureType2VDN.get(featureType);
final VectorDataNode vectorDataNode = createVectorDataNode(featureStatisticsWriter, originalVDN);
final ProductNodeGroup<VectorDataNode> vectorDataNodeGroup = provider.getVectorDataNodeGroup();
vectorDataNodeGroup.remove(originalVDN);
originalVDN.dispose();
vectorDataNodeGroup.add(vectorDataNode);
//todo solve this one
// final JInternalFrame internalFrame = VisatApp.getApp().findInternalFrame(originalVDN);
// if (internalFrame != null) {
// try {
// internalFrame.setClosed(true);
// } catch (PropertyVetoException ignored) {
// ok
// }
// }
final ProductSceneView sceneView = SnapApp.getDefault().getSelectedProductSceneView();
if (sceneView != null) {
sceneView.setLayersVisible(vectorDataNode);
}
}
private static VectorDataNode createVectorDataNode(FeatureStatisticsWriter featureStatisticsWriter,
VectorDataNode originalVDN) {
final SimpleFeatureType updatedFeatureType = featureStatisticsWriter.getUpdatedFeatureType();
final List<SimpleFeature> features = featureStatisticsWriter.getFeatures();
final ListFeatureCollection featureCollection = new ListFeatureCollection(updatedFeatureType, features);
final PlacemarkDescriptor placemarkDescriptor = originalVDN.getPlacemarkDescriptor();
final VectorDataNode vectorDataNode = new VectorDataNode(originalVDN.getName(), featureCollection, placemarkDescriptor);
vectorDataNode.setPermanent(originalVDN.isPermanent());
vectorDataNode.setModified(true);
vectorDataNode.setDescription(originalVDN.getDescription());
return vectorDataNode;
}
private Histogram getHistogram(Mask mask) {
return mask2Histogram.get(mask);
}
private Mask[] getMasks(SimpleFeatureType featureType) {
final Set<Mask> masks = featureType2Mask.get(featureType);
return masks.toArray(new Mask[masks.size()]);
}
private FeatureCollection<SimpleFeatureType, SimpleFeature> getFeatureCollection(SimpleFeatureType featureType) {
return featureType2VDN.get(featureType).getFeatureCollection();
}
private SimpleFeatureType[] getFeatureTypes() {
if (!hasSelectedMasks()) {
return new SimpleFeatureType[0];
}
List<SimpleFeatureType> result = new ArrayList<>();
final Histogram[] histograms = provider.getHistograms();
for (int i = 0; i < selectedMasks.length; i++) {
final Mask selectedMask = selectedMasks[i];
mask2Histogram.put(selectedMask, histograms[i]);
if (selectedMask.getImageType().getName().equals(Mask.VectorDataType.TYPE_NAME)) {
VectorDataNode vectorDataNode = Mask.VectorDataType.getVectorData(selectedMask);
SimpleFeatureType featureType = vectorDataNode.getFeatureType();
if (!result.contains(featureType)) {
result.add(featureType);
}
if (!featureType2Mask.containsKey(featureType)) {
featureType2Mask.put(featureType, new HashSet<Mask>());
}
featureType2Mask.get(featureType).add(selectedMask);
featureType2VDN.put(featureType, vectorDataNode);
setMaskRegionName(selectedMask, vectorDataNode);
}
}
return result.toArray(new SimpleFeatureType[result.size()]);
}
private void setMaskRegionName(Mask selectedMask, VectorDataNode vectorDataNode) {
FeatureIterator<SimpleFeature> features = vectorDataNode.getFeatureCollection().features();
mask2RegionName.put(selectedMask, Util.getFeatureName(features.next()));
features.close();
}
public void setSelectedMasks(Mask[] selectedMasks) {
this.selectedMasks = selectedMasks;
}
}