Java tutorial
/*---------------- FILE HEADER ------------------------------------------ This file is part of deegree. Copyright (C) 2001-2012 by: Department of Geography, University of Bonn http://www.giub.uni-bonn.de/deegree/ lat/lon GmbH http://www.lat-lon.de 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; either version 2.1 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact: lat/lon GmbH Aennchenstr. 19 53177 Bonn Germany E-Mail: info@lat-lon.de Prof. Dr. Klaus Greve Department of Geography University of Bonn Meckenheimer Allee 166 53115 Bonn Germany E-Mail: greve@giub.uni-bonn.de ---------------------------------------------------------------------------*/ package org.deegree.igeo.views.swing.style.component.classification; import static org.deegree.igeo.i18n.Messages.get; import static org.deegree.igeo.style.model.classification.Column.COLUMNTYPE.FILLCOLOR; import static org.deegree.igeo.style.model.classification.Column.COLUMNTYPE.LINESTYLE; import static org.deegree.igeo.style.model.classification.Column.COLUMNTYPE.VALUE; import static org.deegree.igeo.style.model.classification.ThematicGroupingInformation.GROUPINGTYPE.EQUAL; import static org.deegree.igeo.style.model.classification.ThematicGroupingInformation.GROUPINGTYPE.QUALITY; import static org.deegree.igeo.style.model.classification.ThematicGroupingInformation.GROUPINGTYPE.QUANTILE; import static org.deegree.igeo.style.model.classification.ThematicGroupingInformation.GROUPINGTYPE.UNIQUE; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.math.BigInteger; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import javax.swing.ButtonGroup; import javax.swing.DefaultCellEditor; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JSpinner; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.JTableHeader; import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import org.deegree.datatypes.QualifiedName; import org.deegree.datatypes.Types; import org.deegree.framework.log.ILogger; import org.deegree.framework.log.LoggerFactory; import org.deegree.graphics.sld.Rule; import org.deegree.igeo.style.LayerCache.CachedLayer; import org.deegree.igeo.style.classification.ClassificationFromSld; import org.deegree.igeo.style.model.DashArray; import org.deegree.igeo.style.model.GraphicSymbol; import org.deegree.igeo.style.model.Histogram; import org.deegree.igeo.style.model.PropertyValue; import org.deegree.igeo.style.model.RandomColors; import org.deegree.igeo.style.model.SldProperty; import org.deegree.igeo.style.model.SldValues; import org.deegree.igeo.style.model.WellKnownMark; import org.deegree.igeo.style.model.classification.AbstractClassification; import org.deegree.igeo.style.model.classification.ClassificationTableRow; import org.deegree.igeo.style.model.classification.Column; import org.deegree.igeo.style.model.classification.EqualIntervalClassification; import org.deegree.igeo.style.model.classification.IllegalClassificationException; import org.deegree.igeo.style.model.classification.Intervallable; import org.deegree.igeo.style.model.classification.Intervallables.DateIntervallable; import org.deegree.igeo.style.model.classification.Intervallables.DoubleIntervallable; import org.deegree.igeo.style.model.classification.Intervallables.StringIntervallable; import org.deegree.igeo.style.model.classification.ManualClassification; import org.deegree.igeo.style.model.classification.QualityClassification; import org.deegree.igeo.style.model.classification.QuantileClassification; import org.deegree.igeo.style.model.classification.ThematicGrouping; import org.deegree.igeo.style.model.classification.ThematicGroupingInformation; import org.deegree.igeo.style.model.classification.ThematicGroupingInformation.GROUPINGTYPE; import org.deegree.igeo.style.model.classification.UniqueValueGrouping; import org.deegree.igeo.style.model.classification.ValueRange; import org.deegree.igeo.views.swing.addlayer.QualifiedNameRenderer; import org.deegree.igeo.views.swing.style.SingleItem; import org.deegree.igeo.views.swing.style.SingleItemDisableComboBox; import org.deegree.igeo.views.swing.style.StyleDialog; import org.deegree.igeo.views.swing.style.StyleDialogUtils; import org.deegree.igeo.views.swing.style.VisualPropertyPanel; import org.deegree.igeo.views.swing.style.component.font.FontHelper; import org.deegree.igeo.views.swing.style.editor.ClassificationValuesEditor; import org.deegree.igeo.views.swing.style.editor.ColorTableCellEditor; import org.deegree.igeo.views.swing.style.editor.FillTableCellEditor; import org.deegree.igeo.views.swing.style.editor.PointCellEditor; import org.deegree.igeo.views.swing.style.editor.SpinnerTableCellEditor; import org.deegree.igeo.views.swing.style.renderer.ClassificationValuesRenderer; import org.deegree.igeo.views.swing.style.renderer.ColorTableCellRenderer; import org.deegree.igeo.views.swing.style.renderer.DashArrayRenderer; import org.deegree.igeo.views.swing.style.renderer.LocaleTableCellRenderer; import org.deegree.igeo.views.swing.style.renderer.SldPropertyCellRenderer; import org.deegree.igeo.views.swing.style.renderer.SldPropertyRenderer; import org.deegree.igeo.views.swing.style.renderer.SymbolRenderer; import org.deegree.igeo.views.swing.style.renderer.SymbolTableCellRenderer; import org.deegree.igeo.views.swing.util.IconRegistry; import org.deegree.model.feature.schema.FeatureType; import org.deegree.model.filterencoding.FilterEvaluationException; import org.deegree.model.filterencoding.PropertyName; import org.deegree.ogcbase.PropertyPath; import org.deegree.ogcbase.PropertyPathFactory; import com.jgoodies.forms.builder.ButtonBarBuilder; import com.jgoodies.forms.builder.DefaultFormBuilder; import com.jgoodies.forms.layout.CellConstraints; import com.jgoodies.forms.layout.FormLayout; /** * <code>AbstractClassificationPanel</code> TODO add class documentation * * @author <a href="mailto:wanhoff@lat-lon.de">Jeronimo Wanhoff</a> * @author <a href="mailto:buesching@lat-lon.de">Lyn Buesching</a> * @author last edited by: $Author$ * * @version $Revision$, $Date$ * */ public abstract class AbstractClassificationPanel extends JPanel implements ActionListener, TableModelListener { private static final long serialVersionUID = -7782382934136012149L; private static final ILogger LOG = LoggerFactory.getLogger(AbstractClassificationPanel.class); private SingleItem unknownClassification; private SingleItem uniqueValue; private SingleItem equalInterval; private SingleItem quantile; private SingleItem quality; public enum SYMBOLIZERTYPE { POLYGON, POINT, LINE, LABEL } protected VisualPropertyPanel assignedVisualPropPanel; protected JComboBox propertyCB; private ClassificationTableModel<?> model; private JCheckBox status; private SingleItemDisableComboBox classificationTypeCB; private JLabel isManual = new JLabel(get("$MD10735")); private JSpinner numberOfClassesSpinner; private JTable classesTable; private boolean isUpdating = false; private boolean isInitialising = true; private JButton addRowBt; private JButton removeRowBt; private JButton openHistogramBt; private String datePattern; private String decimalPattern; private Histogram histogram; private JRadioButton fullDatabase; private JRadioButton extentDatabase; /** * * @param assignedVisualPropPanel */ public AbstractClassificationPanel(VisualPropertyPanel assignedVisualPropPanel) { this.assignedVisualPropPanel = assignedVisualPropPanel; datePattern = assignedVisualPropPanel.getOwner().getSettings().getFormatsOptions() .getPattern("datePattern"); decimalPattern = assignedVisualPropPanel.getOwner().getSettings().getFormatsOptions() .getPattern("decimalPattern"); setLayout(new BorderLayout()); init(); } /** * @return true, if classification is enabled; false otherwise */ public boolean isActive() { return status.isSelected(); } /** * @return the list of rules, representing the classification */ public List<Rule> getRules() { PropertyName propertyName = new PropertyName((QualifiedName) propertyCB.getSelectedItem()); return model.getClassifiedData(getSymbolizerType(), propertyName); } /** * Constructs a manual classification out of the given rules. Only the first symbolizer of each rule will be * considered, if it is a PolygonSymbolizer!. * * @param the * rules to construct a classification * @param type * the type of the featuretypeName * @throws FilterEvaluationException */ public void setValues(List<Rule> rules, FeatureType featureType) throws IllegalClassificationException, FilterEvaluationException { PropertyName propertyName = ClassificationFromSld.detectPropertyName(rules); if (propertyName == null) { throw new IllegalClassificationException("PopertyNames are not valid!"); } // TODO: check if this is necessary!? boolean isTypeCorrect = ClassificationFromSld.isTypeCorrect(rules, getSymbolizerType()); if (!isTypeCorrect) { throw new IllegalClassificationException( "Could not create list of value ranges - symbolizer is not of type " + getSymbolizerType()); } int propertyType = ClassificationFromSld.detectPropertyType(propertyName, featureType); switch (propertyType) { case Types.INTEGER: case Types.SMALLINT: case Types.BIGINT: case Types.DOUBLE: case Types.FLOAT: setPropertyCB(propertyName); PropertyValue<?> pvDouble = getPropertyValue(); List<Intervallable<Double>> doubleData = getDoubleData(pvDouble); ThematicGroupingInformation<Double> tgiDouble = ClassificationFromSld.createDoubleClassification(rules, doubleData, propertyType, decimalPattern, assignedVisualPropPanel.getOwner().getSettings()); ThematicGrouping<Double> groupingDouble; groupingDouble = getGrouping(tgiDouble); numberOfClassesSpinner.setValue(tgiDouble.getRows().size()); groupingDouble.setData(doubleData); ClassificationTableModel<Double> tableModelDouble = new ClassificationTableModel<Double>(getColumns(), assignedVisualPropPanel.getOwner()); tableModelDouble.setClassification(tgiDouble.getRows(), tgiDouble.getType()); tableModelDouble.setThematicGrouping(groupingDouble, tgiDouble.getType()); configureClassesTable(tableModelDouble, groupingDouble.getAttributeHeader(), new ClassificationValuesRenderer<Double>(), new ClassificationValuesEditor<Double>(new DoubleIntervallable(0.0, decimalPattern))); break; case Types.DATE: setPropertyCB(propertyName); PropertyValue<?> pvDate = getPropertyValue(); List<Intervallable<Date>> dateData = getDateData(pvDate); ThematicGroupingInformation<Date> tgiDate = ClassificationFromSld.createDateClassification(rules, dateData, propertyType, datePattern, assignedVisualPropPanel.getOwner().getSettings()); ThematicGrouping<Date> groupingDate; groupingDate = getGrouping(tgiDate); numberOfClassesSpinner.setValue(tgiDate.getRows().size()); groupingDate.setData(dateData); ClassificationTableModel<Date> tableModelDate = new ClassificationTableModel<Date>(getColumns(), assignedVisualPropPanel.getOwner()); tableModelDate.setClassification(tgiDate.getRows(), tgiDate.getType()); tableModelDate.setThematicGrouping(groupingDate, tgiDate.getType()); configureClassesTable(tableModelDate, groupingDate.getAttributeHeader(), new ClassificationValuesRenderer<Date>(), new ClassificationValuesEditor<Date>(new DateIntervallable(new Date(), datePattern))); break; case Types.VARCHAR: setPropertyCB(propertyName); PropertyValue<?> pvString = getPropertyValue(); List<Intervallable<String>> stringData = getStringData(pvString); ThematicGroupingInformation<String> tgiString = ClassificationFromSld.createStringClassification(rules, stringData, propertyType, assignedVisualPropPanel.getOwner().getSettings()); ThematicGrouping<String> groupingString; switch (tgiString.getType()) { case UNIQUE: groupingString = new UniqueValueGrouping<String>(); classificationTypeCB.setSelectedItem(uniqueValue); numberOfClassesSpinner.setEnabled(false); break; case QUALITY: groupingString = new QualityClassification<String>(); classificationTypeCB.setSelectedItem(quality); numberOfClassesSpinner.setEnabled(false); break; default: groupingString = new ManualClassification<String>(); numberOfClassesSpinner.setValue(tgiString.getRows().size()); unknownClassification.setEnabled(true); classificationTypeCB.setSelectedItem(unknownClassification); break; } numberOfClassesSpinner.setValue(tgiString.getRows().size()); numberOfClassesSpinner.setEnabled(false); groupingString.setData(stringData); ClassificationTableModel<String> tableModelString = new ClassificationTableModel<String>(getColumns(), assignedVisualPropPanel.getOwner()); tableModelString.setClassification(tgiString.getRows(), tgiString.getType()); tableModelString.setThematicGrouping(groupingString, tgiString.getType()); configureClassesTable(tableModelString, groupingString.getAttributeHeader(), new ClassificationValuesRenderer<String>(), new ClassificationValuesEditor<String>(new StringIntervallable("dummy"))); break; default: LOG.logInfo("not supported type: " + propertyType); break; } status.setSelected(true); } private PropertyValue<?> getPropertyValue() { StyleDialog styleDialog = assignedVisualPropPanel.getOwner(); if (fullDatabase.isSelected()) { return styleDialog.getAllPropertyValue((QualifiedName) this.propertyCB.getSelectedItem()); } else if (extentDatabase.isSelected()) { return styleDialog.getExtentPropertyValue((QualifiedName) this.propertyCB.getSelectedItem()); } return styleDialog.getPropertyValue((QualifiedName) this.propertyCB.getSelectedItem()); } private <V extends Comparable<V>> ThematicGrouping<V> getGrouping( ThematicGroupingInformation<V> thematicGrouping) { GROUPINGTYPE type = thematicGrouping.getType(); // TODO: ask user for classification // if ( !thematicGrouping.getType().equals( UNIQUE ) && !thematicGrouping.getType().equals( QUANTILE ) // && !thematicGrouping.getType().equals( EQUAL ) && !thematicGrouping.getType().equals( QUALITY ) ) { // type = askUserForClassificationType(); // } ThematicGrouping<V> grouping; switch (type) { case UNIQUE: grouping = new UniqueValueGrouping<V>(); classificationTypeCB.setSelectedItem(uniqueValue); numberOfClassesSpinner.setEnabled(false); break; case QUANTILE: grouping = new QuantileClassification<V>(); classificationTypeCB.setSelectedItem(quantile); break; case EQUAL: grouping = new EqualIntervalClassification<V>(); classificationTypeCB.setSelectedItem(equalInterval); break; case QUALITY: grouping = new QualityClassification<V>(); classificationTypeCB.setSelectedItem(quality); break; default: grouping = new ManualClassification<V>(); unknownClassification.setEnabled(true); classificationTypeCB.setSelectedItem(unknownClassification); break; } return grouping; } protected ClassificationTableModel<?> getModel() { if (model == null) { updateTable(); } return model; } private void init() { status = new JCheckBox(); status.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (isActive()) { assignedVisualPropPanel.setActive(true); } } }); classesTable = new ClassesTable(); // nr of properties which can be classified List<QualifiedName> pvList = assignedVisualPropPanel.getOwner().getPropertyNames(Types.FLOAT, Types.DOUBLE, Types.INTEGER, Types.SMALLINT, Types.BIGINT, Types.DATE); List<QualifiedName> pvListString = assignedVisualPropPanel.getOwner().getPropertyNames(Types.VARCHAR); boolean isStringClassEnabled = true; boolean isClassEnabled = true; if (pvList.size() == 0) { isClassEnabled = false; if (pvListString.size() == 0) { isStringClassEnabled = false; } } uniqueValue = new SingleItem(get("$MD10724"), true); equalInterval = new SingleItem(get("$MD10725"), isClassEnabled); quantile = new SingleItem(get("$MD10726"), isClassEnabled); quality = new SingleItem(get("$MD11531"), isStringClassEnabled); unknownClassification = new SingleItem(get("$MD11849"), false); List<SingleItem> classificationItems = new ArrayList<SingleItem>(4); classificationItems.add(uniqueValue); classificationItems.add(equalInterval); classificationItems.add(quantile); classificationItems.add(quality); classificationItems.add(unknownClassification); classificationTypeCB = new SingleItemDisableComboBox(classificationItems); classificationTypeCB.addActionListener(this); propertyCB = new JComboBox(); QualifiedNameRenderer renderer = new QualifiedNameRenderer(); renderer.setPreferredSize(new Dimension(100, 18)); propertyCB.setRenderer(renderer); propertyCB.addActionListener(this); SpinnerModel noClassesModel = new SpinnerNumberModel(AbstractClassification.DEFAULTNOOFCLASSES, 1, Integer.MAX_VALUE, 1); numberOfClassesSpinner = new JSpinner(noClassesModel); numberOfClassesSpinner.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(ChangeEvent e) { if (!isManual.isVisible()) { model.getThematicGrouping().setNoOfClasses((Integer) numberOfClassesSpinner.getValue()); model.update(VALUE, true); } setClassificationActive(); } }); openHistogramBt = new JButton(get("$MD11049")); openHistogramBt.addActionListener(this); openHistogramBt.setVisible(true); histogram = new Histogram(); histogram.addWindowListener(new WindowAdapter() { public void windowClosed(java.awt.event.WindowEvent e) { openHistogramBt.setText(get("$MD11049")); }; }); initDatabaseButtonGroup(); classificationTypeCB.setSelectedItem(uniqueValue); isManual.setVisible(false); addRowBt = new JButton(get("$MD10739"), IconRegistry.getIcon("textfield_add.png")); addRowBt.addActionListener(this); removeRowBt = new JButton(get("$MD10740"), IconRegistry.getIcon("textfield_delete.png")); removeRowBt.setEnabled(false); removeRowBt.addActionListener(this); // set selection mode classesTable.setRowSelectionAllowed(true); classesTable.setColumnSelectionAllowed(false); classesTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); classesTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { if (classesTable.getSelectedRows().length > 0) { removeRowBt.setEnabled(true); } else { removeRowBt.setEnabled(false); } } }); // mouseListener classesTable.addMouseListener(new TableMouseListener(classesTable)); // set column model (width of the columns and editors) TableColumnModel columnModel = classesTable.getColumnModel(); for (int i = 0; i < classesTable.getColumnCount(); i++) { TableColumn column = columnModel.getColumn(i); switch (model.getColumnType(i)) { case VALUE: column.setPreferredWidth(100); break; case FILLCOLOR: case LINECOLOR: case FILLTRANSPARENCY: case LINETRANSPARENCY: case FONTTRANSPARENCY: case LINEWIDTH: case LINESTYLE: case SIZE: case LINECAP: case FONTFAMILY: case HALOCOLOR: case HALORADIUS: case FONTCOLOR: case FONTSIZE: case FONTSTYLE: case FONTWEIGHT: case ROTATION: column.setPreferredWidth(75); break; case SYMBOL: column.setPreferredWidth(100); break; } } updateTable(); int nrOfClasses = 6; if (model != null) { nrOfClasses = model.getRows().size(); } numberOfClassesSpinner.setValue(nrOfClasses); // numberOfClassesSpinner.setValue( (Integer) model.getRows().size() ); JTableHeader header = classesTable.getTableHeader(); header.addMouseListener(new TableHeaderMouseListener(classesTable, assignedVisualPropPanel)); header.setReorderingAllowed(false); JScrollPane scrollPane = new JScrollPane(classesTable); classesTable.setPreferredScrollableViewportSize(new Dimension(150, 190)); // layout FormLayout fl = new FormLayout( "10dlu, left:min:grow(0.2), min:grow(0.3), $rgap, min:grow(0.3), $rgap, min:grow(0.2)", "bottom:15dlu, $cpheight, $cpheight, $cpheight, default:grow(1.0), $cpheight"); DefaultFormBuilder builder = new DefaultFormBuilder(fl); builder.setBorder(StyleDialogUtils.createStyleAttributeBorder(get("$MD10719"))); CellConstraints cc = new CellConstraints(); builder.add(status, cc.xy(1, 1)); builder.addLabel(get("$MD10720"), cc.xy(2, 1)); builder.addLabel(get("$MD10721"), cc.xy(2, 2)); builder.add(classificationTypeCB, cc.xy(3, 2)); builder.add(isManual, cc.xy(5, 2)); builder.addLabel(get("$MD10722"), cc.xy(2, 3)); builder.add(propertyCB, cc.xywh(3, 3, 3, 1)); builder.add(openHistogramBt, cc.xy(5, 4)); builder.addLabel(get("$MD10723"), cc.xy(2, 4)); builder.add(numberOfClassesSpinner, cc.xy(3, 4)); builder.add(getDatabasePanel(), cc.xywh(7, 2, 1, 3)); builder.add(scrollPane, cc.xyw(1, 5, 7)); builder.add(buildTableChangerButtonBar(), cc.xyw(1, 6, 5, CellConstraints.CENTER, CellConstraints.BOTTOM)); add(builder.getPanel(), BorderLayout.CENTER); isInitialising = false; } private void initDatabaseButtonGroup() { fullDatabase = new JRadioButton(get("$MD11867")); fullDatabase.addActionListener(this); extentDatabase = new JRadioButton(get("$MD11868")); extentDatabase.addActionListener(this); ButtonGroup bgDatabase = new ButtonGroup(); bgDatabase.add(fullDatabase); bgDatabase.add(extentDatabase); extentDatabase.setSelected(true); } private JPanel getDatabasePanel() { FormLayout fl = new FormLayout("10dlu, left:default", "default, default, default"); DefaultFormBuilder builder = new DefaultFormBuilder(fl); CellConstraints cc = new CellConstraints(); builder.addLabel(get("$MD11866"), cc.xyw(1, 1, 2)); builder.add(extentDatabase, cc.xy(2, 2)); builder.add(fullDatabase, cc.xy(2, 3)); JPanel panel = builder.getPanel(); CachedLayer cachedLayer = assignedVisualPropPanel.getOwner().getCachedLayer(); if (cachedLayer.isFullLoadingSupported()) { panel.setVisible(true); } else { panel.setVisible(false); } return panel; } private void setPropertyCB(PropertyName propertyName) throws IllegalClassificationException { boolean isPropertyNameValid = false; // init Property ComboBox for (int i = 0; i < propertyCB.getItemCount(); i++) { PropertyPath itemAsPath = PropertyPathFactory .createPropertyPath((QualifiedName) propertyCB.getItemAt(i)); if (ClassificationFromSld.equalsPropertyNameWithotNS(itemAsPath, propertyName.getValue())) { propertyCB.setSelectedIndex(i); isPropertyNameValid = true; break; } } if (!isPropertyNameValid) { throw new IllegalClassificationException( "property name used in the classification is not available for classification"); } } private void configureClassesTable(ClassificationTableModel<?> model, String valueHeader, ClassificationValuesRenderer<?> renderer, ClassificationValuesEditor<?> editor) { classesTable.setModel(model); int valueIndex = model.getColumnIndex(VALUE); classesTable.getColumnModel().getColumn(valueIndex).setHeaderValue(valueHeader); classesTable.getColumnModel().getColumn(valueIndex).setCellRenderer(renderer); classesTable.getColumnModel().getColumn(valueIndex).setCellEditor(editor); model.addTableModelListener(this); this.model = model; editor.addCellEditorListener(model); } // returns a list of double intervallables for TYPES.INTEGER and TYPES.DOUBLE types private List<Intervallable<Double>> getDoubleData(PropertyValue<?> pv) { List<Intervallable<Double>> data = new ArrayList<Intervallable<Double>>(); for (Object o : pv.getValues()) { double doubleValue = Double.NaN; try { if (o != null && o instanceof String && ((String) o).length() > 0) { doubleValue = Double.parseDouble((String) o); } else if (o instanceof Number) { doubleValue = ((Number) o).doubleValue(); } } catch (Exception e) { LOG.logError("Could not cast value to double, where type is INTEGER or DOUBLE"); } data.add(new DoubleIntervallable(doubleValue, decimalPattern)); } return data; } private List<Intervallable<Date>> getDateData(PropertyValue<?> pv) { List<Intervallable<Date>> data = new ArrayList<Intervallable<Date>>(); for (Object o : pv.getValues()) { data.add(new DateIntervallable((Date) o, datePattern)); } return data; } private List<Intervallable<String>> getStringData(PropertyValue<?> pv) { List<Intervallable<String>> data = new ArrayList<Intervallable<String>>(); for (Object o : pv.getValues()) { String stringValue = ""; try { if (o != null && ((String) o).length() > 0) { stringValue = (String) o; } } catch (Exception e) { LOG.logError("Could not cast value to string, where type is VARCHAR"); } data.add(new StringIntervallable(stringValue)); } return data; } private void updateTable() { if (this.propertyCB.getSelectedItem() != null) { PropertyValue<?> pv = getPropertyValue(); switch (pv.getDatatyp()) { case Types.INTEGER: case Types.SMALLINT: case Types.BIGINT: List<Intervallable<Double>> dataInteger = new ArrayList<Intervallable<Double>>(); for (Object o : pv.getValues()) { if (pv.getDatatyp() == Types.BIGINT) { dataInteger.add(new DoubleIntervallable(((BigInteger) o).doubleValue(), decimalPattern)); } else { dataInteger.add(new DoubleIntervallable((double) ((Integer) o), decimalPattern)); } } ClassificationTableModel<Double> tableModelInteger = new ClassificationTableModel<Double>( getColumns(), assignedVisualPropPanel.getOwner()); ThematicGrouping<Double> groupingInteger = getGrouping(dataInteger, (Integer) numberOfClassesSpinner.getValue()); tableModelInteger.setThematicGrouping(groupingInteger, getGroupingType()); configureClassesTable(tableModelInteger, groupingInteger.getAttributeHeader(), new ClassificationValuesRenderer<Double>(), new ClassificationValuesEditor<Double>(new DoubleIntervallable(0.0, decimalPattern))); break; case Types.DOUBLE: case Types.FLOAT: List<Intervallable<Double>> dataDouble = new ArrayList<Intervallable<Double>>(); for (Object o : pv.getValues()) { dataDouble.add(new DoubleIntervallable((Double) o, decimalPattern)); } ClassificationTableModel<Double> tableModelDouble = new ClassificationTableModel<Double>( getColumns(), assignedVisualPropPanel.getOwner()); ThematicGrouping<Double> groupingDouble = getGrouping(dataDouble, (Integer) numberOfClassesSpinner.getValue()); tableModelDouble.setThematicGrouping(groupingDouble, getGroupingType()); configureClassesTable(tableModelDouble, groupingDouble.getAttributeHeader(), new ClassificationValuesRenderer<Double>(), new ClassificationValuesEditor<Double>(new DoubleIntervallable(0.0, decimalPattern))); break; case Types.DATE: ClassificationTableModel<Date> tableModelDate = new ClassificationTableModel<Date>(getColumns(), assignedVisualPropPanel.getOwner()); List<Intervallable<Date>> dataDate = new ArrayList<Intervallable<Date>>(); for (Object o : pv.getValues()) { dataDate.add(new DateIntervallable((Date) o, datePattern)); } ThematicGrouping<Date> groupingDate = getGrouping(dataDate, (Integer) numberOfClassesSpinner.getValue()); tableModelDate.setThematicGrouping(groupingDate, getGroupingType()); configureClassesTable(tableModelDate, groupingDate.getAttributeHeader(), new ClassificationValuesRenderer<Date>(), new ClassificationValuesEditor<Date>(new DateIntervallable(new Date(), datePattern))); break; case Types.VARCHAR: default: ClassificationTableModel<String> tableModelString = new ClassificationTableModel<String>( getColumns(), assignedVisualPropPanel.getOwner()); List<Intervallable<String>> dataString = new ArrayList<Intervallable<String>>(); for (Object o : pv.getValues()) { dataString.add(new StringIntervallable((String) o)); } ThematicGrouping<String> groupingString = getGrouping(dataString, (Integer) numberOfClassesSpinner.getValue()); tableModelString.setThematicGrouping(groupingString, getGroupingType()); configureClassesTable(tableModelString, groupingString.getAttributeHeader(), new ClassificationValuesRenderer<String>(), new ClassificationValuesEditor<String>(new StringIntervallable("dummy"))); break; } if (classificationTypeCB.getSelectedItem() == uniqueValue || classificationTypeCB.getSelectedItem() == quality) { getModel().getThematicGrouping().setFillColor(new RandomColors()); model.update(FILLCOLOR, true); } model.update(VALUE, true); } } private GROUPINGTYPE getGroupingType() { if (classificationTypeCB.getSelectedItem() == equalInterval) { return EQUAL; } else if (classificationTypeCB.getSelectedItem() == quantile) { return QUANTILE; } else if (classificationTypeCB.getSelectedItem() == quality) { return QUALITY; } else if (classificationTypeCB.getSelectedItem() == uniqueValue) { return UNIQUE; } return GROUPINGTYPE.MANUAL; } private <V extends Comparable<V>> ThematicGrouping<V> getGrouping(List<Intervallable<V>> data, int noOfClasses) { ThematicGrouping<V> grouping; if (classificationTypeCB.getSelectedItem() == equalInterval) { grouping = new EqualIntervalClassification<V>(); } else if (classificationTypeCB.getSelectedItem() == quantile) { grouping = new QuantileClassification<V>(); } else if (classificationTypeCB.getSelectedItem() == quality) { grouping = new QualityClassification<V>(); } else { grouping = new UniqueValueGrouping<V>(); } grouping.setData(data); grouping.setNoOfClasses(noOfClasses); return grouping; } private JPanel buildTableChangerButtonBar() { ButtonBarBuilder bbBuilder = new ButtonBarBuilder(); bbBuilder.addGriddedButtons(new JButton[] { addRowBt, removeRowBt }); return bbBuilder.getPanel(); } private void fillPropertyCB(List<QualifiedName> properties) { QualifiedName selectedQn = null; if (propertyCB.getSelectedItem() != null) { selectedQn = (QualifiedName) propertyCB.getSelectedItem(); } propertyCB.removeAllItems(); for (QualifiedName qn : properties) { propertyCB.addItem(qn); if (selectedQn != null && qn.equals(selectedQn)) { propertyCB.setSelectedItem(qn); } } } private void updateAfterClassificationChanged() { List<QualifiedName> pvList; boolean isClassesSpinnerEnabled = false; if (classificationTypeCB.getSelectedItem() == equalInterval) { pvList = assignedVisualPropPanel.getOwner().getPropertyNames(Types.FLOAT, Types.DOUBLE, Types.INTEGER, Types.DATE, Types.BIGINT, Types.SMALLINT); isClassesSpinnerEnabled = true; numberOfClassesSpinner.setValue(6); } else if (classificationTypeCB.getSelectedItem() == quantile) { pvList = assignedVisualPropPanel.getOwner().getPropertyNames(Types.FLOAT, Types.DOUBLE, Types.INTEGER, Types.DATE, Types.BIGINT, Types.SMALLINT); isClassesSpinnerEnabled = true; numberOfClassesSpinner.setValue(6); } else { pvList = assignedVisualPropPanel.getOwner().getPropertyNames(); } isUpdating = true; fillPropertyCB(pvList); isUpdating = false; numberOfClassesSpinner.setEnabled(isClassesSpinnerEnabled); updateTable(); numberOfClassesSpinner.setValue(model.getClassification().size()); initColumnValues(); } private void setClassificationActive() { if (!isInitialising) { status.setSelected(true); assignedVisualPropPanel.setActive(true); } } private void openHistogram() { if (propertyCB.getSelectedItem() != null) { String title = get("$MD11050", ((QualifiedName) propertyCB.getSelectedItem()).getLocalName()); List<ValueRange<?>> values = new ArrayList<ValueRange<?>>(); int dataSize = this.model.getThematicGrouping().getNumberOfData(); int classes = this.model.getRowCount(); if (dataSize / classes > 2) { for (ClassificationTableRow<?> row : this.model.getRows()) { values.add(row.getValue()); } histogram.update(title, values); openHistogramBt.setText(get("$MD11504")); } else { JOptionPane.showMessageDialog(this, get("$MD11530", dataSize, classes, 2), get("$MD11529"), JOptionPane.INFORMATION_MESSAGE); } } else { JOptionPane.showMessageDialog(this, get("$MD11051"), get("$DI10018"), JOptionPane.INFORMATION_MESSAGE); } } private void addManuellClassificationItem() { if (classificationTypeCB.getSelectedItem() != uniqueValue && ((classificationTypeCB.getItemCount() == 0) || !isManual.isVisible())) { isManual.setVisible(true); numberOfClassesSpinner.setEnabled(false); propertyCB.setEnabled(false); } } private void removeManuellClassificationItem() { isManual.setVisible(false); numberOfClassesSpinner.setEnabled(true); propertyCB.setEnabled(true); } /** * transfers the values from the single elmements of the style to the classification */ abstract public void initColumnValues(); abstract protected List<Column> getColumns(); abstract protected SYMBOLIZERTYPE getSymbolizerType(); // ////////////////////////////////////////////////////////////////////////////// // ACTIONLISTENER // ////////////////////////////////////////////////////////////////////////////// /* * (non-Javadoc) * * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */ public void actionPerformed(ActionEvent e) { Object src = e.getSource(); if (src == classificationTypeCB) { if (isManual.isVisible()) { removeManuellClassificationItem(); } unknownClassification.setEnabled(false); updateAfterClassificationChanged(); setClassificationActive(); } else if (src == propertyCB) { if (this.propertyCB.getSelectedItem() != null && !isUpdating) { updateTable(); } setClassificationActive(); } else if (src == addRowBt) { int row = classesTable.getSelectedRow(); if (row < 0) { row = model.getRowCount(); } model.addRowBefore(row); numberOfClassesSpinner.setValue(classesTable.getRowCount()); setClassificationActive(); } else if (src == removeRowBt) { // stop cell editing before removing selected rows int editingRow = classesTable.getEditingRow(); int editingColumn = classesTable.getEditingColumn(); if (editingRow > -1 && editingColumn > -1 && classesTable.getCellEditor(editingRow, editingColumn) != null) { classesTable.getCellEditor(editingRow, editingColumn).cancelCellEditing(); } model.removeRows(classesTable.getSelectedRows()); numberOfClassesSpinner.setValue(classesTable.getRowCount()); setClassificationActive(); } else if (src == openHistogramBt) { openHistogram(); } else if (src == fullDatabase || src == extentDatabase) { updateTable(); } } // ////////////////////////////////////////////////////////////////////////////// // TABLEMODELLISTENER // ////////////////////////////////////////////////////////////////////////////// public void tableChanged(TableModelEvent e) { // mark as manuell classification, if user changed a class limit in the table or // inserted/removed a row if ((model.getColumnIndex(VALUE) == e.getColumn() && !isManual.isVisible()) || (e.getType() == TableModelEvent.INSERT) || (e.getType() == TableModelEvent.DELETE)) { addManuellClassificationItem(); } } // ////////////////////////////////////////////////////////////////////////////// // inner classes // ////////////////////////////////////////////////////////////////////////////// private class ClassesTable extends JTable { private static final long serialVersionUID = 8303943959907931881L; @Override public TableCellRenderer getCellRenderer(int row, int column) { switch (model.getColumnType(column)) { case FILLCOLOR: case LINECOLOR: case FONTCOLOR: case HALOCOLOR: return new ColorTableCellRenderer(); case SYMBOL: return new SymbolTableCellRenderer(); case LINECAP: return new SldPropertyCellRenderer(); default: return new LocaleTableCellRenderer(); } } @Override public TableCellEditor getCellEditor(int row, int column) { TableCellEditor editor; switch (model.getColumnType(column)) { case FILLCOLOR: editor = new FillTableCellEditor( assignedVisualPropPanel.getOwner().getSettings().getGraphicOptions(), SldValues.getDefaultColor()); break; case LINECOLOR: case HALOCOLOR: case FONTCOLOR: editor = new ColorTableCellEditor(); break; case FILLTRANSPARENCY: case LINETRANSPARENCY: case FONTTRANSPARENCY: int opAsInt = SldValues.getOpacityInPercent(SldValues.getDefaultOpacity()); editor = new SpinnerTableCellEditor(opAsInt, 0, 100, 1); break; case LINEWIDTH: editor = new SpinnerTableCellEditor(SldValues.getDefaultLineWidth(), 0.0, Integer.MAX_VALUE, 0.5); break; case LINESTYLE: JComboBox lineStyleCB = new JComboBox(); int lineStyleCol = model.getColumnIndex(LINESTYLE); lineStyleCB.setRenderer(new DashArrayRenderer( getTableHeader().getColumnModel().getColumn(lineStyleCol).getWidth(), 15)); for (DashArray da : SldValues.getDashArrays()) { lineStyleCB.addItem(da); } Map<String, DashArray> dashArrays = assignedVisualPropPanel.getOwner().getSettings() .getGraphicOptions().getDashArrays(); for (DashArray da : dashArrays.values()) { lineStyleCB.addItem(da); } editor = new DefaultCellEditor(lineStyleCB); break; case SIZE: editor = new SpinnerTableCellEditor(SldValues.getDefaultSize(), 0.00001, Integer.MAX_VALUE, 1.0); break; case SYMBOL: JComboBox symbolCB = new JComboBox(); symbolCB.setRenderer(new SymbolRenderer()); for (WellKnownMark mark : SldValues.getWellKnownMarks()) { symbolCB.addItem(mark); } try { Map<String, GraphicSymbol> symbols = assignedVisualPropPanel.getOwner().getSettings() .getGraphicOptions().getSymbolDefinitions(); List<GraphicSymbol> values = new ArrayList<GraphicSymbol>(); values.addAll(symbols.values()); Collections.sort((List<GraphicSymbol>) values); for (GraphicSymbol symbol : values) { symbolCB.addItem(symbol); } } catch (MalformedURLException e) { JOptionPane.showMessageDialog(this, get("$MD10788"), get("$DI10017"), JOptionPane.ERROR_MESSAGE); } editor = new DefaultCellEditor(symbolCB); break; case LINECAP: JComboBox lineCapCB = new JComboBox(); lineCapCB.setRenderer(new SldPropertyRenderer()); for (SldProperty lc : SldValues.getLineCaps()) { lineCapCB.addItem(lc); } editor = new DefaultCellEditor(lineCapCB); break; case FONTFAMILY: FontHelper fhf = new FontHelper(); editor = new DefaultCellEditor(fhf.createFontFamilyChooser()); break; case FONTSTYLE: FontHelper fhs = new FontHelper(); editor = new DefaultCellEditor(fhs.createFontStyleChooser()); break; case FONTWEIGHT: FontHelper fhw = new FontHelper(); editor = new DefaultCellEditor(fhw.createFontWeightChooser()); break; case FONTSIZE: editor = new SpinnerTableCellEditor(SldValues.getDefaultFontSize(), 1.0, Double.MAX_VALUE, 1.0); break; case HALORADIUS: editor = new SpinnerTableCellEditor(SldValues.getDefaultHaloRadius(), 0d, 50d, 1d); break; case ROTATION: editor = new SpinnerTableCellEditor(SldValues.getDefaultRotation(), 0.0, 360.0, 5.0d); break; case DISPLACEMENT: editor = new PointCellEditor(SldValues.getDefaultDisplacement()); break; case ANCHORPOINT: editor = new PointCellEditor(SldValues.getDefaultAnchorPoint()); break; default: return super.getCellEditor(row, column); } // add cell editor listener, to be informed, when cell editing was stopped or // canceled; was necessary, because of problems after deleting an editing row! editor.addCellEditorListener(this); return editor; } // Implement table header to show tool tips. @Override protected JTableHeader createDefaultTableHeader() { return new JTableHeader(columnModel) { private static final long serialVersionUID = 7937684822965281600L; public String getToolTipText(MouseEvent e) { java.awt.Point p = e.getPoint(); int index = columnModel.getColumnIndexAtX(p.x); int realIndex = columnModel.getColumn(index).getModelIndex(); return model.getColumnTooltip(realIndex); } }; } }; }