/*//GEN-FIRST:event_guiRemoveAllActionPerformed
* Geotoolkit - An Open Source Java GIS Toolkit//GEN-LAST:event_guiRemoveAllActionPerformed
* http://www.geotoolkit.org
*
* (C) 2009-2014 Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotoolkit.gui.swing.style;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.image.RenderedImage;
import java.beans.PropertyChangeEvent;
import java.io.IOException;
import java.util.AbstractMap;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.EventObject;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.AbstractCellEditor;
import javax.swing.BorderFactory;
import javax.swing.DefaultCellEditor;
import javax.swing.DefaultListCellRenderer;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
import org.apache.sis.feature.SingleAttributeTypeBuilder;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.util.ObjectConverters;
import org.geotoolkit.storage.coverage.CoverageReference;
import org.geotoolkit.storage.coverage.CoverageUtilities;
import org.geotoolkit.coverage.grid.GeneralGridGeometry;
import org.geotoolkit.coverage.grid.GridCoverage2D;
import org.geotoolkit.coverage.io.CoverageStoreException;
import org.geotoolkit.coverage.io.GridCoverageReadParam;
import org.geotoolkit.coverage.io.GridCoverageReader;
import org.geotoolkit.factory.FactoryFinder;
import org.geotoolkit.factory.Hints;
import org.geotoolkit.filter.DefaultLiteral;
import org.apache.sis.geometry.GeneralEnvelope;
import org.geotoolkit.gui.swing.propertyedit.PropertyPane;
import org.geotoolkit.gui.swing.propertyedit.featureeditor.ArrayEditor;
import org.geotoolkit.gui.swing.propertyedit.featureeditor.PropertyValueEditor;
import org.geotoolkit.gui.swing.propertyedit.styleproperty.PaletteCellRenderer;
import org.geotoolkit.font.FontAwesomeIcons;
import org.geotoolkit.font.IconBuilder;
import org.geotoolkit.gui.swing.resource.IconBundle;
import org.geotoolkit.gui.swing.resource.MessageBundle;
import org.geotoolkit.gui.swing.util.ColorCellEditor;
import org.geotoolkit.gui.swing.util.ColorCellRenderer;
import org.geotoolkit.gui.swing.util.NumberAlignRenderer;
import org.geotoolkit.image.palette.PaletteFactory;
import org.geotoolkit.map.CoverageMapLayer;
import org.geotoolkit.map.FeatureMapLayer;
import org.geotoolkit.map.LayerListener;
import org.geotoolkit.map.MapItem;
import org.geotoolkit.map.MapLayer;
import org.geotoolkit.processing.coverage.statistics.StatisticOp;
import org.geotoolkit.style.MutableFeatureTypeStyle;
import org.geotoolkit.style.MutableRule;
import org.geotoolkit.style.MutableStyleFactory;
import org.geotoolkit.style.StyleConstants;
import static org.geotoolkit.style.StyleConstants.*;
import org.geotoolkit.style.function.Categorize;
import org.geotoolkit.style.function.DefaultInterpolate;
import org.geotoolkit.style.function.DefaultInterpolationPoint;
import org.geotoolkit.style.function.Interpolate;
import org.geotoolkit.style.function.InterpolationPoint;
import org.geotoolkit.style.function.Jenks;
import org.geotoolkit.style.function.Method;
import org.geotoolkit.style.function.Mode;
import org.geotoolkit.style.function.ThreshholdsBelongTo;
import org.geotoolkit.style.interval.DefaultIntervalPalette;
import org.geotoolkit.style.interval.DefaultRandomPalette;
import org.geotoolkit.style.interval.Palette;
import org.geotoolkit.util.collection.CollectionChangeEvent;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.combobox.ListComboBoxModel;
import org.opengis.coverage.grid.GridCoverage;
import org.opengis.coverage.grid.GridEnvelope;
import org.opengis.feature.Property;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.expression.Function;
import org.opengis.filter.expression.Literal;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.opengis.style.ChannelSelection;
import org.opengis.style.ColorMap;
import org.opengis.style.ContrastEnhancement;
import org.opengis.style.ContrastMethod;
import org.opengis.style.Description;
import org.opengis.style.OverlapBehavior;
import org.opengis.style.RasterSymbolizer;
import org.opengis.style.SelectedChannelType;
import org.opengis.style.ShadedRelief;
import org.opengis.style.Symbolizer;
import org.apache.sis.geometry.Envelopes;
import org.apache.sis.measure.Units;
import org.opengis.feature.AttributeType;
/**
* Style editor which handle Raster colormap edition.
*
* @author Johann Sorel (Geomatys)
* @module
*/
public class JColorMapPane extends StyleElementEditor<ColorMap> implements PropertyPane, LayerListener{
private static final PaletteFactory PF = PaletteFactory.getDefault();
private static final List<Object> PALETTES = new ArrayList<Object>();
private static final List<Object> PALETTES_NAMED = new ArrayList<Object>();
static{
PALETTES.add(new DefaultRandomPalette());
final Set<String> paletteNames = PF.getAvailableNames();
for (String palName : paletteNames) {
PALETTES.add(palName);
PALETTES_NAMED.add(palName);
}
double[] fractions = new double[]{
-3000,
-1500,
-0.1,
+0,
556,
1100,
1600,
2200,
3000};
Color[] colors = new Color[]{
new Color(9, 9, 145, 255),
new Color(31, 131, 224, 255),
new Color(182, 240, 240, 255),
new Color(5, 90, 5, 255),
new Color(150, 200, 150, 255),
new Color(190, 150, 20, 255),
new Color(100, 100, 50, 255),
new Color(200, 210, 220, 255),
new Color(255, 255, 255, 255),
};
PALETTES.add(new DefaultIntervalPalette(fractions,colors));
}
private static final MutableStyleFactory SF = (MutableStyleFactory) FactoryFinder.getStyleFactory(new Hints(Hints.STYLE_FACTORY, MutableStyleFactory.class));
private static final FilterFactory FF = FactoryFinder.getFilterFactory(null);
private static final Literal TRS = SF.literal(new Color(0, 0, 0, 0));
private final String title;
private final ImageIcon icon;
private final Image preview;
private final String tooltip;
private final PropertyValueEditor noDataEditor = new ArrayEditor();
private final AttributeType nodataDesc;
private final Property noDataProp;
private ColorMapModel model = new InterpolateColorModel(Collections.EMPTY_LIST);
private MapLayer layer = null;
//listen to layer style change
private final LayerListener.Weak layerListener = new LayerListener.Weak(this);
//keep track of where the symbolizer was to avoid rewriting the complete style
private MutableRule parentRule = null;
private int parentIndex = 0;
private String name = "";
private String desc = "";
private List<Object> currentPaletteList = null;
public JColorMapPane() {
super(ColorMap.class);
title = MessageBundle.format("property_style_colormap");
icon = IconBundle.getIcon("16_classification_single");
preview = null;
tooltip = "";
initComponents();
//for noData
nodataDesc = new SingleAttributeTypeBuilder().setName("noData").setValueClass(double[].class).build();
noDataProp = nodataDesc.newInstance();
noDataEditor.setValue(nodataDesc, new double[0]);
noDataContainer.add(noDataEditor, BorderLayout.CENTER);
setPalettes(PALETTES);
guiPalette.setRenderer(new PaletteCellRenderer());
guiPalette.setSelectedIndex(0);
guiTable.setShowGrid(false, false);
final List<Class> methods = new ArrayList<>();
methods.add(Interpolate.class);
methods.add(Categorize.class);
methods.add(Jenks.class);
guiMethod.setModel(new ListComboBoxModel(methods));
guiMethod.setRenderer(new DefaultListCellRenderer(){
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
final JLabel lbl = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if(value instanceof Class){
lbl.setText(((Class)value).getSimpleName());
}
return lbl;
}
});
parse((RasterSymbolizer)null);
}
@Override
public final String getTitle() {
return title;
}
@Override
public final ImageIcon getIcon() {
return icon;
}
@Override
public final Image getPreview() {
return preview;
}
@Override
public final String getToolTip() {
return tooltip;
}
@Override
public final Component getComponent() {
return this;
}
@Override
public void setLayer(MapLayer layer) {
this.layer = layer;
postParse();
initBandSpinner();
if(this.layer!=null){
layerListener.unregisterSource(this.layer);
}
this.layer = layer;
if(this.layer!=null){
layerListener.registerSource(this.layer);
}
}
@Override
public MapLayer getLayer() {
return layer;
}
public String getSelectedBand(){
return String.valueOf(guiBand.getValue());
}
public void setSelectedBand(String name){
try{
final int n = Integer.parseInt(name);
guiBand.setValue(n);
}catch(Exception ex){/*not important*/}
}
private void setPalettes(List<Object> palettes){
if(currentPaletteList == palettes) return;
this.currentPaletteList = palettes;
guiPalette.setModel(new ListComboBoxModel(palettes));
}
private void parse(){
guiTable.revalidate();
guiTable.repaint();
guiNaN.setSelected(true);
RasterSymbolizer rs = null;
parentRule = null;
parentIndex = 0;
search:
if(layer != null){
for(final MutableFeatureTypeStyle fts : layer.getStyle().featureTypeStyles()){
for(MutableRule r : fts.rules()){
for(int i=0,n=r.symbolizers().size();i<n;i++){
Symbolizer s = r.symbolizers().get(i);
if(s instanceof RasterSymbolizer){
rs = (RasterSymbolizer) s;
parentRule = r;
parentIndex = i;
break search;
}
}
}
}
}
parse(rs);
}
public void parse(RasterSymbolizer rs){
//find channel
if(rs!=null){
final ChannelSelection selection = rs.getChannelSelection();
guiBand.setValue(0);
if(selection!=null){
final SelectedChannelType sct = selection.getGrayChannel();
if(sct!=null){
try{
guiBand.setValue(Integer.valueOf(sct.getChannelName()));
}catch(Exception ex){ //nullpointer or numberformat exception
LOGGER.log(Level.INFO, "chanel name is not a number : {0}", sct.getChannelName());
}
}
}
}
model = null;
name = "";
desc = "";
if(rs != null){
name = rs.getName();
desc = rs.getDescription()== null ? "" : ((rs.getDescription().getTitle()==null) ? "" : rs.getDescription().getTitle().toString());
parse(rs.getColorMap());
}else{
//create an empty interpolate colormodel
model = new InterpolateColorModel(Collections.EMPTY_LIST);
}
postParse();
}
@Override
public void parse(ColorMap target) {
if(target!=null && target.getFunction()!=null){
final Function fct = target.getFunction();
if(fct instanceof Interpolate){
final List<InterpolationPoint> points = ((Interpolate)fct).getInterpolationPoints();
model = new InterpolateColorModel(points);
}else if(fct instanceof Categorize){
final Map<Expression,Expression> th = ((Categorize)fct).getThresholds();
model = new CategorizeColorModel(th);
}else if(fct instanceof Jenks){
final Jenks jenks = (Jenks) fct;
double[] vals = (jenks.getNoData() != null) ? jenks.getNoData() : new double[0];
noDataEditor.setValue(nodataDesc, vals);
Literal palette = jenks.getPalette();
Literal classNumber = jenks.getClassNumber();
String paletteName = palette.evaluate(null, String.class);
Integer nbSteps = classNumber.evaluate(null, Integer.class);
model = new JenksColorModel(paletteName, nbSteps);
}else{
model = new InterpolateColorModel(Collections.EMPTY_LIST);
LOGGER.log(Level.WARNING, "Unknowned colormap function : {0}", fct);
}
}else{
//create an empty interpolate colormodel
model = new InterpolateColorModel(Collections.EMPTY_LIST);
}
postParse();
}
private void postParse(){
guiTable.setModel(model);
guiInvert.setEnabled(true);
guiMinSpinner.setEnabled(true);
guiMaxSpinner.setEnabled(true);
if(model instanceof InterpolateColorModel){
guiMethod.setSelectedItem(Interpolate.class);
setPalettes(PALETTES);
guiTable.getColumnModel().getColumn(0).setCellRenderer(new NumberAlignRenderer());
guiTable.getColumnModel().getColumn(0).setCellEditor(new DefaultCellEditor(new JTextField()));
guiTable.getColumnModel().getColumn(1).setCellRenderer(new ColorCellRenderer());
guiTable.getColumnModel().getColumn(1).setCellEditor(new ColorCellEditor());
guiTable.getColumnModel().getColumn(2).setCellRenderer(new DeleteRenderer());
guiTable.getColumnModel().getColumn(2).setCellEditor(new DeleteEditor());
guiTable.getColumnExt(2).setMaxWidth(20);
final InterpolateColorModel icm = (InterpolateColorModel) model;
//restore NaN and min/max values
boolean hasNaN = false;
double min = Double.NaN;
double max = Double.NaN;
for(int i=0,n=icm.points.size();i<n;i++){
final double v = icm.points.get(i).getData().doubleValue();
if(!Double.isNaN(v)){
min = Double.isNaN(min) ? v : Math.min(v, min);
max = Double.isNaN(max) ? v : Math.max(v, max);
}else{
hasNaN = true;
}
}
if(!Double.isNaN(min)){
guiMinSpinner.setValue(min);
guiMaxSpinner.setValue(max);
}
guiNbStep.setValue(icm.points.size()-(hasNaN?1:0));
guiNaN.setSelected(hasNaN);
}else if(model instanceof CategorizeColorModel){
guiMethod.setSelectedItem(Categorize.class);
setPalettes(PALETTES);
guiTable.getColumnModel().getColumn(0).setCellRenderer(new NumberAlignRenderer());
guiTable.getColumnModel().getColumn(0).setCellEditor(new DefaultCellEditor(new JTextField()));
guiTable.getColumnModel().getColumn(1).setCellRenderer(new ColorCellRenderer());
guiTable.getColumnModel().getColumn(1).setCellEditor(new ColorCellEditor());
guiTable.getColumnModel().getColumn(2).setCellRenderer(new NumberAlignRenderer());
guiTable.getColumnModel().getColumn(2).setCellEditor(new DefaultCellEditor(new JTextField()));
guiTable.getColumnModel().getColumn(3).setCellRenderer(new DeleteRenderer());
guiTable.getColumnModel().getColumn(3).setCellEditor(new DeleteEditor());
guiTable.getColumnExt(3).setMaxWidth(20);
final CategorizeColorModel ccm = (CategorizeColorModel) model;
//restore NaN and min/max values
boolean hasNaN = false;
double min = Double.NaN;
double max = Double.NaN;
for(int i=0,n=ccm.ths.size();i<n;i++){
final Entry<Expression,Expression> exps = ccm.ths.get(i);
Object val = exps.getKey().evaluate(n, Number.class);
if(val instanceof Number){
final double v = ((Number)val).doubleValue();
if(!Double.isNaN(v)){
if(!Double.isInfinite(v)){
min = Double.isNaN(min) ? v : Math.min(v, min);
max = Double.isNaN(max) ? v : Math.max(v, max);
}
}else{
hasNaN = true;
}
}
}
if(!Double.isNaN(min)){
guiMinSpinner.setValue(min);
guiMaxSpinner.setValue(max);
}
guiNbStep.setValue(ccm.ths.size()-(hasNaN?2:1));
guiNaN.setSelected(hasNaN);
}else if(model instanceof JenksColorModel){
guiMethod.setSelectedItem(Jenks.class);
final JenksColorModel jcm = (JenksColorModel) model;
setPalettes(PALETTES_NAMED);
if (jcm.getPaletteName() != null) {
guiPalette.setSelectedItem(jcm.getPaletteName());
} else {
guiPalette.setSelectedItem(0);
}
guiNbStep.setValue(jcm.getNbSteps());
guiInvert.setEnabled(false);
guiMinSpinner.setEnabled(false);
guiMaxSpinner.setEnabled(false);
}
//disable and hide value table for jenks method
guiAddOne.setVisible(!(model instanceof JenksColorModel));
guiRemoveAll.setVisible(!(model instanceof JenksColorModel));
guiTableScroll.setVisible(!(model instanceof JenksColorModel));
guiJenksMessage.setVisible(model instanceof JenksColorModel);
guiNoDataLabel.setVisible(model instanceof JenksColorModel);
noDataContainer.setVisible(model instanceof JenksColorModel);
if(layer instanceof CoverageMapLayer){
guiLblPalette.setVisible(true);
guiPalette.setVisible(true);
guiBand.setVisible(true);
guiLblBand.setVisible(true);
guiNaN.setVisible(true);
guiInvert.setVisible(true);
guiGenerate.setVisible(true);
guiLblStep.setVisible(true);
guiNbStep.setVisible(true);
}else{
guiLblPalette.setVisible(false);
guiPalette.setVisible(false);
guiBand.setVisible(false);
guiLblBand.setVisible(false);
guiNaN.setVisible(false);
guiInvert.setVisible(false);
guiGenerate.setVisible(false);
guiLblStep.setVisible(false);
guiNbStep.setVisible(false);
}
revalidate();
repaint();
//add a listener on the model to propage even
model.addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
firePropertyChange(PROPERTY_UPDATED, null, create());
}
});
}
private void initBandSpinner() {
//update nbBands spinner
try {
if (layer instanceof CoverageMapLayer) {
final CoverageReference covRef = ((CoverageMapLayer) layer).getCoverageReference();
final GridCoverageReader reader = covRef.acquireReader();
final GeneralGridGeometry gridGeometry = reader.getGridGeometry(covRef.getImageIndex());
if (gridGeometry.isDefined(GeneralGridGeometry.GRID_TO_CRS)
&& gridGeometry.isDefined(GeneralGridGeometry.EXTENT)) {
MathTransform gridToCRS = gridGeometry.getGridToCRS();
GridEnvelope extent = gridGeometry.getExtent();
int dim = extent.getDimension();
double[] low = new double[dim];
double[] high = new double[dim];
low[0] = extent.getLow(0);
high[0] = extent.getHigh(0);
low[1] = extent.getLow(1);
high[1] = extent.getHigh(1);
GeneralEnvelope sliceExtent = new GeneralEnvelope(gridGeometry.getCoordinateReferenceSystem());
for (int i = 0; i < dim; i++) {
sliceExtent.setRange(i, low[i], high[i]);
}
final double[] res = new double[dim];
for(int i=0;i<res.length;i++){
res[i] = high[i]-low[i];
}
GridCoverageReadParam readParam = new GridCoverageReadParam();
readParam.setEnvelope(Envelopes.transform(gridToCRS, sliceExtent));
readParam.setResolution(res);
readParam.setCoordinateReferenceSystem(gridGeometry.getCoordinateReferenceSystem());
readParam.setDeferred(true);
final GridCoverage coverage = reader.read(covRef.getImageIndex(), readParam);
int nbBands = coverage.getNumSampleDimensions() - 1;
guiBand.setModel(new SpinnerNumberModel(0, 0, nbBands, 1));
}
covRef.recycle(reader);
}
} catch (CoverageStoreException ex) {
LOGGER.log(Level.WARNING, ex.getMessage(), ex);
} catch (TransformException ex) {
LOGGER.log(Level.WARNING, ex.getMessage(), ex);
}
}
private void initializeSpinners() {
if(layer != null && layer instanceof CoverageMapLayer){
final CoverageMapLayer cml = (CoverageMapLayer)layer;
final CoverageReference cref = cml.getCoverageReference();
GridCoverageReader reader = null;
GeneralGridGeometry gridGeometry = null;
GridCoverageReadParam readParam = null;
GridCoverage coverage = null;
GridCoverage2D coverage2D = null;
RenderedImage image = null;
try {
reader = cref.acquireReader();
gridGeometry = reader.getGridGeometry(cref.getImageIndex());
if (gridGeometry.isDefined(GeneralGridGeometry.GRID_TO_CRS)
&& gridGeometry.isDefined(GeneralGridGeometry.EXTENT)) {
MathTransform gridToCRS = gridGeometry.getGridToCRS();
GridEnvelope extent = gridGeometry.getExtent();
int dim = extent.getDimension();
double[] low = new double[dim];
double[] high = new double[dim];
low[0] = extent.getLow(0);
high[0] = extent.getHigh(0);
low[1] = extent.getLow(1);
high[1] = extent.getHigh(1);
GeneralEnvelope sliceExtent = new GeneralEnvelope(gridGeometry.getCoordinateReferenceSystem());
for (int i = 0; i < dim; i++) {
sliceExtent.setRange(i, low[i], high[i]);
}
readParam = new GridCoverageReadParam();
readParam.setEnvelope(Envelopes.transform(gridToCRS, sliceExtent));
readParam.setCoordinateReferenceSystem(gridGeometry.getCoordinateReferenceSystem());
coverage = reader.read(cref.getImageIndex(), readParam);
coverage2D = CoverageUtilities.firstSlice(coverage);
image = coverage2D.getRenderedImage();
final Map<String, Object> an = StatisticOp.analyze(image);
final double[] minArray = (double[]) an.get(StatisticOp.MINIMUM);
final double[] maxArray = (double[]) an.get(StatisticOp.MAXIMUM);
final Integer index = (Integer) guiBand.getValue();
double min = minArray[index];
double max = maxArray[index];
final SpinnerModel minModel =
new SpinnerNumberModel(min, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 0.1d);
final SpinnerModel maxModel =
new SpinnerNumberModel(max, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 0.1d);
guiMinSpinner.setModel(minModel);
guiMaxSpinner.setModel(maxModel);
}
cref.recycle(reader);
} catch (CoverageStoreException ex) {
LOGGER.log(Level.WARNING, ex.getMessage(),ex);
} catch (DataStoreException ex) {
LOGGER.log(Level.WARNING, ex.getMessage(),ex);
} catch (TransformException ex) {
LOGGER.log(Level.WARNING, ex.getMessage(), ex);
}
}
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
guiAddOne = new JButton();
guiRemoveAll = new JButton();
jPanel1 = new JPanel();
guiNaN = new JCheckBox();
guiLblPalette = new JLabel();
guiGenerate = new JButton();
guiPalette = new JComboBox();
guiInvert = new JCheckBox();
guiLblBand = new JLabel();
guiBand = new JSpinner();
guiLblStep = new JLabel();
guiNbStep = new JSpinner();
jLabel1 = new JLabel();
guiMethod = new JComboBox();
minLabel = new JLabel();
guiMinSpinner = new JSpinner();
maxLabel = new JLabel();
guiMaxSpinner = new JSpinner();
guiNoDataLabel = new JLabel();
guiFitToData = new JButton();
noDataContainer = new JPanel();
guiTableScroll = new JScrollPane();
guiTable = new JXTable();
guiJenksMessage = new JLabel();
guiAddOne.setText(MessageBundle.format("add_value")); // NOI18N
guiAddOne.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
guiAddOneActionPerformed(evt);
}
});
guiRemoveAll.setText(MessageBundle.format("remove_all_values")); // NOI18N
guiRemoveAll.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
guiRemoveAllActionPerformed(evt);
}
});
jPanel1.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 5));
guiNaN.setSelected(true);
guiNaN.setText(MessageBundle.format("style_rastercolormappane_nan")); // NOI18N
guiNaN.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
guiNaNActionPerformed(evt);
}
});
guiLblPalette.setText(MessageBundle.format("style_rastercolormappane_palette")); // NOI18N
guiGenerate.setText(MessageBundle.format("generate")); // NOI18N
guiGenerate.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
guiGenerateActionPerformed(evt);
}
});
guiInvert.setText(MessageBundle.format("style_rastercolormappane_invert")); // NOI18N
guiLblBand.setHorizontalAlignment(SwingConstants.RIGHT);
guiLblBand.setText(MessageBundle.format("style_rastercolormappane_band")); // NOI18N
guiBand.setModel(new SpinnerNumberModel(Integer.valueOf(0), Integer.valueOf(0), null, Integer.valueOf(1)));
guiLblStep.setText(MessageBundle.format("style_rastersymbolizer_divisions")); // NOI18N
guiNbStep.setModel(new SpinnerNumberModel(Integer.valueOf(3), Integer.valueOf(0), null, Integer.valueOf(1)));
jLabel1.setText(MessageBundle.format("method")); // NOI18N
guiMethod.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent evt) {
guiMethodItemStateChanged(evt);
}
});
minLabel.setText(MessageBundle.format("minimum")); // NOI18N
guiMinSpinner.setModel(new SpinnerNumberModel(Double.valueOf(0.0d), null, null, Double.valueOf(0.0d)));
maxLabel.setText(MessageBundle.format("maximum")); // NOI18N
guiMaxSpinner.setModel(new SpinnerNumberModel(Double.valueOf(0.0d), null, null, Double.valueOf(1.0d)));
guiNoDataLabel.setText(MessageBundle.format("noData")); // NOI18N
guiFitToData.setText(MessageBundle.format("style_rastercolormappane_fittodata")); // NOI18N
guiFitToData.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
guiFitToDataActionPerformed(evt);
}
});
noDataContainer.setLayout(new BorderLayout());
GroupLayout jPanel1Layout = new GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addGroup(jPanel1Layout.createParallelGroup(Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(guiLblPalette)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(guiPalette, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(guiFitToData)
.addPreferredGap(ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(guiGenerate))
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(minLabel)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(guiMinSpinner, GroupLayout.PREFERRED_SIZE, 75, GroupLayout.PREFERRED_SIZE)
.addGap(12, 12, 12)
.addComponent(maxLabel)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(guiMaxSpinner, GroupLayout.PREFERRED_SIZE, 75, GroupLayout.PREFERRED_SIZE))
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(guiLblStep)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(guiNbStep, GroupLayout.PREFERRED_SIZE, 62, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(guiLblBand)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(guiBand, GroupLayout.PREFERRED_SIZE, 51, GroupLayout.PREFERRED_SIZE))
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(jLabel1)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(guiMethod, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(ComponentPlacement.UNRELATED)
.addComponent(guiNaN)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(guiInvert))
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(guiNoDataLabel)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(noDataContainer, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
);
jPanel1Layout.linkSize(SwingConstants.HORIZONTAL, new Component[] {guiBand, guiMaxSpinner, guiMinSpinner, guiNbStep});
jPanel1Layout.linkSize(SwingConstants.HORIZONTAL, new Component[] {guiInvert, guiLblBand, guiLblPalette, guiLblStep, guiNaN, jLabel1, maxLabel, minLabel});
jPanel1Layout.linkSize(SwingConstants.HORIZONTAL, new Component[] {guiFitToData, guiGenerate});
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(Alignment.LEADING)
.addGroup(Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
.addGap(0, 0, 0)
.addGroup(jPanel1Layout.createParallelGroup(Alignment.BASELINE)
.addComponent(guiPalette, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addComponent(guiLblPalette, GroupLayout.PREFERRED_SIZE, 25, GroupLayout.PREFERRED_SIZE))
.addPreferredGap(ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(Alignment.BASELINE)
.addComponent(jLabel1)
.addComponent(guiMethod, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addComponent(guiNaN)
.addComponent(guiInvert))
.addPreferredGap(ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(Alignment.TRAILING)
.addComponent(guiNoDataLabel)
.addComponent(noDataContainer, GroupLayout.PREFERRED_SIZE, 14, GroupLayout.PREFERRED_SIZE))
.addPreferredGap(ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(Alignment.BASELINE)
.addComponent(guiLblBand)
.addComponent(guiBand, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addComponent(guiLblStep)
.addComponent(guiNbStep, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
.addPreferredGap(ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(Alignment.BASELINE)
.addComponent(minLabel)
.addComponent(guiMinSpinner, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addComponent(maxLabel)
.addComponent(guiMaxSpinner, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
.addPreferredGap(ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(jPanel1Layout.createParallelGroup(Alignment.BASELINE)
.addComponent(guiGenerate)
.addComponent(guiFitToData)))
);
jPanel1Layout.linkSize(SwingConstants.VERTICAL, new Component[] {guiNaN, guiNoDataLabel, noDataContainer});
guiTableScroll.setViewportView(guiTable);
guiJenksMessage.setFont(guiJenksMessage.getFont().deriveFont((guiJenksMessage.getFont().getStyle() | Font.ITALIC) | Font.BOLD, guiJenksMessage.getFont().getSize()+1));
guiJenksMessage.setHorizontalAlignment(SwingConstants.CENTER);
guiJenksMessage.setText(MessageBundle.format("jenks_notable")); // NOI18N
GroupLayout layout = new GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(guiAddOne)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(guiRemoveAll)
.addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addComponent(jPanel1, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(guiJenksMessage, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(guiTableScroll, Alignment.TRAILING)
);
layout.setVerticalGroup(
layout.createParallelGroup(Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(jPanel1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(guiJenksMessage)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(guiTableScroll, GroupLayout.DEFAULT_SIZE, 192, Short.MAX_VALUE)
.addPreferredGap(ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(Alignment.BASELINE)
.addComponent(guiAddOne)
.addComponent(guiRemoveAll)))
);
}// </editor-fold>//GEN-END:initComponents
/**
* And or Remove NaN value in the model
* @param evt
*/
private void guiNaNActionPerformed(ActionEvent evt) {//GEN-FIRST:event_guiNaNActionPerformed
final boolean withNaN = guiNaN.isSelected();
if(model instanceof CategorizeColorModel){
final Map<Expression,Expression> values = new HashMap<Expression,Expression>();
final List<Entry<Expression, Expression>> ths = ((CategorizeColorModel)model).ths;
for(int i=0,n=ths.size();i<n;i++){
final Entry<Expression, Expression> entry = ths.get(i);
final Object num = ((Literal)entry.getKey()).getValue();
if(num instanceof Number && (Double.isNaN(((Number)num).doubleValue()) || Float.isNaN(((Number)num).floatValue()))){
if(withNaN){
//color model already has a NaN
return;
}else{
//remove it
}
}else{
values.put(entry.getKey(), entry.getValue());
}
}
if(withNaN){
//add NaN entry
values.put(new DefaultLiteral<Number>(Float.NaN), TRS);
}
model = new CategorizeColorModel(values);
postParse();
}else if(model instanceof InterpolateColorModel){
//we need to convert from interpolate to categorize
final List<InterpolationPoint> newPoints = new ArrayList<InterpolationPoint>();
final List<InterpolationPoint> points = ((InterpolateColorModel)model).points;
for(InterpolationPoint pt : points){
final Number num = pt.getData();
if(Double.isNaN(num.doubleValue()) || Float.isNaN(num.floatValue())){
if(withNaN){
//color model already has a NaN
return;
}else{
//remove it
}
}else{
newPoints.add(pt);
}
}
if(withNaN){
//add NaN entry
newPoints.add(SF.interpolationPoint(Float.NaN, TRS));
}
model = new InterpolateColorModel(newPoints);
postParse();
}
}//GEN-LAST:event_guiNaNActionPerformed
private void guiMethodItemStateChanged(ItemEvent evt) {//GEN-FIRST:event_guiMethodItemStateChanged
final Object method = guiMethod.getSelectedItem();
if(Interpolate.class.equals(method)){
if(model instanceof InterpolateColorModel){
//nothing to do
return;
}else if(model instanceof CategorizeColorModel){
//we need to convert from categorize thredholds to interpolation points.
final List<InterpolationPoint> points = new ArrayList<InterpolationPoint>();
final List<Entry<Expression, Expression>> ths = ((CategorizeColorModel)model).ths;
for(int i=1,n=ths.size();i<n;i++){
final Entry<Expression, Expression> entry = ths.get(i);
points.add(SF.interpolationPoint(entry.getKey().evaluate(n, Number.class), entry.getValue()));
}
model = new InterpolateColorModel(points);
postParse();
}else{
final JenksColorModel jcm = (JenksColorModel) model;
String paletteName = jcm.getPaletteName();
int steps = jcm.getNbSteps();
model = new InterpolateColorModel(Collections.EMPTY_LIST);
postParse();
guiPalette.setSelectedItem(paletteName);
guiNbStep.setValue(steps);
}
}else if(Categorize.class.equals(method)){
if(model instanceof CategorizeColorModel){
//nothing to do
return;
}else if(model instanceof InterpolateColorModel){
//we need to convert from interpolate to categorize
final Map<Expression, Expression> values = new HashMap<Expression, Expression>();
values.put( StyleConstants.CATEGORIZE_LESS_INFINITY, TRS);
final List<InterpolationPoint> points = ((InterpolateColorModel)model).points;
for(InterpolationPoint pt : points){
values.put(new DefaultLiteral(pt.getData()), pt.getValue());
}
model = new CategorizeColorModel(values);
postParse();
}else{
final JenksColorModel jcm = (JenksColorModel) model;
String paletteName = jcm.getPaletteName();
int steps = jcm.getNbSteps();
final Map<Expression, Expression> values = new HashMap<Expression, Expression>();
values.put(StyleConstants.CATEGORIZE_LESS_INFINITY, TRS);
values.put(new DefaultLiteral<Number>(0), TRS);
model = new CategorizeColorModel(values);
postParse();
guiPalette.setSelectedItem(paletteName);
guiNbStep.setValue(steps);
}
}else{
if(model instanceof JenksColorModel){
//nothing to do
return;
}else{
Object selectedItem = guiPalette.getSelectedItem();
Integer steps = (Integer) guiNbStep.getValue();
String paletteName = null;
if (selectedItem instanceof String) {
paletteName = (String)selectedItem;
}
model = new JenksColorModel(paletteName, steps);
postParse();
}
}
//ensure the NaN is set as defined
guiNaNActionPerformed(null);
}//GEN-LAST:event_guiMethodItemStateChanged
private void guiFitToDataActionPerformed(ActionEvent evt) {//GEN-FIRST:event_guiFitToDataActionPerformed
initializeSpinners();
}//GEN-LAST:event_guiFitToDataActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private JButton guiAddOne;
private JSpinner guiBand;
private JButton guiFitToData;
private JButton guiGenerate;
private JCheckBox guiInvert;
private JLabel guiJenksMessage;
private JLabel guiLblBand;
private JLabel guiLblPalette;
private JLabel guiLblStep;
private JSpinner guiMaxSpinner;
private JComboBox guiMethod;
private JSpinner guiMinSpinner;
private JCheckBox guiNaN;
private JSpinner guiNbStep;
private JLabel guiNoDataLabel;
private JComboBox guiPalette;
private JButton guiRemoveAll;
private JXTable guiTable;
private JScrollPane guiTableScroll;
private JLabel jLabel1;
private JPanel jPanel1;
private JLabel maxLabel;
private JLabel minLabel;
private JPanel noDataContainer;
// End of variables declaration//GEN-END:variables
private void guiAddOneActionPerformed(final ActionEvent evt) {
model.addValue(Float.NaN, Color.BLACK);
model.fireTableDataChanged();
}
private void guiRemoveAllActionPerformed(final ActionEvent evt) {
model.removeAll();
model.fireTableDataChanged();
}
private void guiGenerateActionPerformed(final ActionEvent evt) {
if(!(layer instanceof CoverageMapLayer)){
return;
}
model.removeAll();
//add the NaN if specified
if(guiNaN.isSelected()){
model.addValue(Float.NaN, new Color(0, 0, 0, 0));
}
boolean mustInterpolation = true;
final Object paletteValue = (Object) guiPalette.getSelectedItem();
List<Entry<Double, Color>> steps = new ArrayList<Entry<Double, Color>>();
if (paletteValue instanceof Palette) {
final Palette palette = (Palette) paletteValue;
steps = palette.getSteps();
} else if (paletteValue instanceof String) {
try {
final Color[] paletteColors = PF.getColors(String.valueOf(paletteValue));
final double stepValue = 1.0f/(paletteColors.length-1);
for (int i = 0; i < paletteColors.length; i++) {
final double fragment = i * stepValue;
steps.add(new AbstractMap.SimpleEntry(fragment, paletteColors[i]));
}
} catch (IOException ex) {
LOGGER.log(Level.WARNING, ex.getMessage(), ex);
}
}
for(int i=0,n=steps.size();i<n;i++){
final double k = steps.get(i).getKey();
if(k < -0.01 || k > 1.01){
mustInterpolation = false;
}
}
//recalculate number of steps
final int nbStep = (Integer)guiNbStep.getValue();
if(steps.size() != nbStep){
//recalculate steps
double min = steps.get(0).getKey();
double max = min;
final List<InterpolationPoint> points = new ArrayList<InterpolationPoint>();
for(int i=0;i<steps.size();i++){
points.add(new DefaultInterpolationPoint(steps.get(i).getKey(), SF.literal(steps.get(i).getValue())));
min = Math.min(min, steps.get(i).getKey());
max = Math.max(max, steps.get(i).getKey());
}
Interpolate inter = SF.interpolateFunction(DEFAULT_CATEGORIZE_LOOKUP, points,Method.COLOR, Mode.LINEAR, DEFAULT_FALLBACK);
//rebuild steps
steps.clear();
for(int i=0;i<nbStep;i++){
final double val = min + ( (max-min)/(nbStep-1) * i );
final Color color = inter.evaluate(val, Color.class);
steps.add(new AbstractMap.SimpleEntry(val,color));
}
}
if(guiInvert.isSelected()){
final List<Entry<Double, Color>> inverted = new ArrayList<Entry<Double, Color>>();
for(int i=0,n=steps.size();i<n;i++){
final double k = steps.get(i).getKey();
inverted.add(new SimpleImmutableEntry(
k, steps.get(n-1-i).getValue()));
}
steps = inverted;
}
if(layer instanceof CoverageMapLayer){
final CoverageMapLayer cml = (CoverageMapLayer)layer;
try {
if(mustInterpolation){
double min = (Double)guiMinSpinner.getValue();
double max = (Double)guiMaxSpinner.getValue();
getInterpolationPoints(min, max, steps);
}else{
for(int s=0,l=steps.size();s<l;s++){
final Entry<Double, Color> step = steps.get(s);
model.addValue(step.getKey(), step.getValue());
}
}
} catch (CoverageStoreException ex) {
LOGGER.log(Level.INFO, ex.getMessage(),ex);
}
}
model.fireTableDataChanged();
}
/**
* Find the min or max values in an array of double
* @param data double array
* @param min search min values or max values
* @return min or max value.
*/
private double findExtremum(final double[] data, final boolean min) {
if (data.length > 0) {
double extremum = data[0];
if (min) {
for (int i = 0; i < data.length; i++) {
extremum = Math.min(extremum, data[i]);
}
} else {
for (int i = 0; i < data.length; i++) {
extremum = Math.max(extremum, data[i]);
}
}
return extremum;
}
throw new IllegalArgumentException("Array of " + (min ? "min" : "max") + " values is empty.");
}
@Override
public boolean canHandle(Object target) {
return target instanceof MapLayer && !(target instanceof FeatureMapLayer);
}
@Override
public void setTarget(final Object layer) {
if(layer instanceof MapLayer){
setLayer((MapLayer)layer);
parse();
}else{
setLayer((MapLayer)null);
}
}
@Override
public void apply() {
if(layer == null) return;
final ChannelSelection selection = SF.channelSelection(
SF.selectedChannelType(getSelectedBand(),DEFAULT_CONTRAST_ENHANCEMENT));
final ColorMap colorMap = create();
final ContrastEnhancement enchance = SF.contrastEnhancement(LITERAL_ONE_FLOAT,ContrastMethod.NONE);
final ShadedRelief relief = SF.shadedRelief(LITERAL_ONE_FLOAT);
final Description desc = SF.description(this.desc, this.desc);
final RasterSymbolizer symbol = SF.rasterSymbolizer(
name, DEFAULT_GEOM, desc, Units.POINT, LITERAL_ONE_FLOAT,
selection, OverlapBehavior.LATEST_ON_TOP, colorMap, enchance, relief, null);
if(parentRule!=null){
parentRule.symbolizers().remove(parentIndex);
parentRule.symbolizers().add(parentIndex,symbol);
}else{
//style did not exist, add a new feature type style for it
final MutableFeatureTypeStyle fts = SF.featureTypeStyle(symbol);
fts.setDescription(SF.description("analyze", "analyze"));
layer.getStyle().featureTypeStyles().add(fts);
}
}
@Override
public void reset() {
if(layer != null){
parse();
}
}
@Override
public ColorMap create() {
final Function function = ((ColorMapModel)guiTable.getModel()).createFunction();
return SF.colorMap(function);
}
@Override
protected Object[] getFirstColumnComponents() {
return new Object[]{};
}
private void getInterpolationPoints(final double min, final double max, List<Entry<Double, Color>> steps) throws CoverageStoreException {
for(int s=0,l=steps.size();s<l;s++){
final Entry<Double, Color> step = steps.get(s);
model.addValue(min + (step.getKey()*(max-min)), step.getValue());
}
}
@Override
public void styleChange(MapLayer source, EventObject event) {
}
@Override
public void itemChange(CollectionChangeEvent<MapItem> event) {
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if(MapLayer.STYLE_PROPERTY.equals(evt.getPropertyName()) && evt.getOldValue()!=evt.getNewValue()){
parse();
}
}
private class DeleteRenderer extends DefaultTableCellRenderer{
@Override
public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column) {
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
DeleteRenderer.this.setIcon(IconBuilder.createIcon(FontAwesomeIcons.ICON_TRASH_O, 16, FontAwesomeIcons.DEFAULT_COLOR));
return DeleteRenderer.this;
}
}
private class DeleteEditor extends AbstractCellEditor implements TableCellEditor{
private final JButton button = new JButton();
private int row;
public DeleteEditor() {
button.setBorderPainted(false);
button.setContentAreaFilled(false);
button.setIcon(IconBuilder.createIcon(FontAwesomeIcons.ICON_TRASH_O, 16, FontAwesomeIcons.DEFAULT_COLOR));
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
model.remove(row);
fireEditingCanceled();
model.fireTableDataChanged();
}
});
}
@Override
public Object getCellEditorValue() {
return null;
}
@Override
public Component getTableCellEditorComponent(final JTable table, final Object value, final boolean isSelected, final int row, final int column) {
this.row = row;
return button;
}
}
private abstract class ColorMapModel extends AbstractTableModel{
public abstract void addValue(Number value, Color color);
public abstract void remove(int row);
public abstract void removeAll();
public abstract Function createFunction();
}
private class InterpolateColorModel extends ColorMapModel{
final Comparator<InterpolationPoint> COMP = new Comparator<InterpolationPoint>() {
@Override
public int compare(InterpolationPoint o1, InterpolationPoint o2) {
return (int)Math.signum( (o1.getData().doubleValue() - o2.getData().doubleValue()));
}
};
private final List<InterpolationPoint> points = new ArrayList<InterpolationPoint>();
public InterpolateColorModel(List<InterpolationPoint> points) {
this.points.addAll(points);
Collections.sort(this.points,COMP);
}
@Override
public int getRowCount() {
return points.size();
}
@Override
public int getColumnCount() {
return 3;
}
@Override
public boolean isCellEditable(final int rowIndex, final int columnIndex) {
return true;
}
@Override
public Object getValueAt(final int rowIndex, final int columnIndex) {
final InterpolationPoint pt = points.get(rowIndex);
switch(columnIndex){
case 0:
return pt.getData();
case 1:
Color c = pt.getValue().evaluate(null, Color.class);
final boolean isNaN = Double.isNaN(pt.getData().doubleValue());
Color cprevious = c;
Color cnext = c;
if(!isNaN && rowIndex!=0){
final InterpolationPoint ptprevious = points.get(rowIndex-1);
if(!Double.isNaN(ptprevious.getData().doubleValue())){
cprevious = ptprevious.getValue().evaluate(null, Color.class);
cprevious = DefaultInterpolate.interpolate(c, cprevious, 0.5);
}
}
if(!isNaN && rowIndex<points.size()-1){
final InterpolationPoint ptnext = points.get(rowIndex+1);
if(!Double.isNaN(ptnext.getData().doubleValue())){
cnext = ptnext.getValue().evaluate(null, Color.class);
cnext = DefaultInterpolate.interpolate(c, cnext, 0.5);
}
}
return new Color[]{cprevious,c,cnext};
}
return "";
}
@Override
public String getColumnName(final int columnIndex) {
switch(columnIndex){
case 0: return MessageBundle.format("style_rastersymbolizer_value");
case 1: return MessageBundle.format("style_rastersymbolizer_color");
}
return "";
}
@Override
public void setValueAt(final Object aValue, final int rowIndex, final int columnIndex) {
InterpolationPoint pt = points.get(rowIndex);
switch(columnIndex){
case 0:
Number n = ObjectConverters.convert(aValue, Number.class);
if(n == null){
n = Float.NaN;
}
pt = SF.interpolationPoint(n, pt.getValue());
break;
case 1:
Color c = (Color) aValue;
if(c == null){
c = new Color(0, 0, 0, 0);
}
pt = SF.interpolationPoint(pt.getData(),SF.literal(c));
break;
}
points.set(rowIndex, pt);
Collections.sort(points,COMP);
fireTableDataChanged();
}
@Override
public void addValue(Number value, Color color) {
final InterpolationPoint pt = SF.interpolationPoint(value, SF.literal(color));
points.add(pt);
Collections.sort(points,COMP);
fireTableDataChanged();
}
@Override
public void removeAll() {
points.clear();
fireTableDataChanged();
}
@Override
public void remove(int row) {
points.remove(row);
fireTableDataChanged();
}
@Override
public Function createFunction() {
return SF.interpolateFunction(DEFAULT_CATEGORIZE_LOOKUP, new ArrayList(points),
Method.COLOR, Mode.LINEAR, DEFAULT_FALLBACK);
}
}
private class CategorizeColorModel extends ColorMapModel{
final Comparator<Entry<Expression,Expression>> COMP = new Comparator<Entry<Expression, Expression>>() {
@Override
public int compare(Entry<Expression, Expression> o1, Entry<Expression, Expression> o2) {
final Double d0 = o1.getKey().evaluate(null, Double.class);
final Double d1 = o2.getKey().evaluate(null, Double.class);
if(d0==null) return -1;
if(d1==null) return +1;
return d0.compareTo(d1);
}
};
private final List<Entry<Expression,Expression>> ths = new ArrayList<Entry<Expression,Expression>>();
public CategorizeColorModel(List<Entry<Expression,Expression>> map) {
ths.addAll(map);
Collections.sort(ths,COMP);
}
public CategorizeColorModel(Map<Expression,Expression> map) {
ths.addAll(map.entrySet());
Collections.sort(ths,COMP);
}
@Override
public int getRowCount() {
return ths.size();
}
@Override
public int getColumnCount() {
return 4;
}
@Override
public boolean isCellEditable(final int rowIndex, final int columnIndex) {
if(columnIndex==3){
//first and last (infinite) rows can not be removed
return rowIndex > 0 && rowIndex < ths.size()-1;
}else if(columnIndex==2){
//a copy of the next threholds value, can not be removed
return false;
}else if(columnIndex==1){
//color column
return true;
}else{
//first column
return rowIndex>0;
}
}
@Override
public Object getValueAt(final int rowIndex, final int columnIndex) {
if(columnIndex==2){
//return next column value if any
if(rowIndex<ths.size()-1){
return getValueAt(rowIndex+1, 0);
}else{
//last line +infinity
return Double.POSITIVE_INFINITY;
}
}else if(columnIndex==3){
//delete column
return "";
}else if(columnIndex==1){
//color column
final Entry<Expression, Expression> entry = ths.get(rowIndex);
return entry.getValue().evaluate(null, Color.class);
}else{
//thresdhold value
final Entry<Expression, Expression> entry = ths.get(rowIndex);
Number n = entry.getKey().evaluate(null, Number.class);
if(n==null) n = Double.NEGATIVE_INFINITY;
return n;
}
}
@Override
public String getColumnName(final int columnIndex) {
switch(columnIndex){
case 0: return MessageBundle.format("style_rastersymbolizer_lower");
case 1: return MessageBundle.format("style_rastersymbolizer_color");
case 2: return MessageBundle.format("style_rastersymbolizer_upper");
}
return "";
}
@Override
public void setValueAt(final Object aValue, final int rowIndex, final int columnIndex) {
Entry<Expression,Expression> th = ths.get(rowIndex);
switch(columnIndex){
case 0:
Number n = ObjectConverters.convert(aValue, Number.class);
if(n == null){
n = Float.NaN;
}
th = new AbstractMap.SimpleEntry((Expression)FF.literal(n),th.getValue());
break;
case 1:
Color c = (Color) aValue;
if(c == null){
c = new Color(0, 0, 0, 0);
}
th = new AbstractMap.SimpleEntry(th.getKey(),(Expression)SF.literal(c));
break;
}
ths.set(rowIndex, th);
Collections.sort(ths,COMP);
fireTableDataChanged();
}
@Override
public void addValue(Number value, Color color) {
Entry<Expression,Expression> th = new AbstractMap.SimpleEntry(
(Expression)FF.literal(value),(Expression)SF.literal(color));
ths.add(th);
Collections.sort(ths,COMP);
fireTableDataChanged();
}
@Override
public void removeAll() {
ths.clear();
Entry<Expression,Expression> th1 = new AbstractMap.SimpleEntry(
(Expression)StyleConstants.CATEGORIZE_LESS_INFINITY,(Expression)TRS);
// Entry<Expression,Expression> th2 = new AbstractMap.SimpleEntry<>(
// (Expression)FF.literal(0d),(Expression)SF.literal(new Color(0f,0f,0f,0f)));
ths.add(th1);
// ths.add(th2);
fireTableDataChanged();
}
@Override
public void remove(int row) {
ths.remove(row);
fireTableDataChanged();
}
@Override
public Function createFunction() {
final Expression lookup = DEFAULT_CATEGORIZE_LOOKUP;
final Literal fallback = DEFAULT_FALLBACK;
final Map<Expression,Expression> map = new HashMap<Expression,Expression>();
for(Entry<Expression,Expression> exp : ths){
map.put(exp.getKey(), exp.getValue());
}
return SF.categorizeFunction(lookup, map, ThreshholdsBelongTo.PRECEDING, fallback);
}
}
private class JenksColorModel extends ColorMapModel{
private String paletteName;
private Integer nbSteps = 0;
private JenksColorModel(String paletteName, Integer nbSteps) {
this.paletteName = paletteName;
this.nbSteps = nbSteps;
}
public String getPaletteName() {
return paletteName;
}
public Integer getNbSteps() {
return nbSteps;
}
@Override
public void addValue(Number value, Color color) {
//do nothing, model is dynamic
}
@Override
public void remove(int row) {
//do nothing, model is dynamic
}
@Override
public void removeAll() {
//do nothing, model is dynamic
}
@Override
public Function createFunction() {
final Literal fallback = DEFAULT_FALLBACK;
final Object item = guiPalette.getSelectedItem();
String paletteName = "";
if(item instanceof String){
paletteName = (String)item;
}
final double [] noData = (double[])noDataEditor.getValue();
final Set<Literal> noDataLiteral = new HashSet<Literal>();
if (guiNaN.isSelected()) {
noDataLiteral.add(FF.literal(Double.NaN));
}
for (int i = 0; i < noData.length; i++) {
noDataLiteral.add(FF.literal(noData[i]));
}
return SF.jenksFunction(FF.literal(guiNbStep.getModel().getValue()), FF.literal(paletteName), fallback, new ArrayList<Literal>(noDataLiteral));
}
@Override
public int getRowCount() {
return 0;
}
@Override
public int getColumnCount() {
return 0;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
return null;
}
}
}