/*
* Copyright (C) 2014 Alec Dhuse
*
* 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 co.foldingmap.GUISupport.panels;
import co.foldingmap.GUISupport.CellEditorRenderer;
import co.foldingmap.GUISupport.SpringUtilities;
import co.foldingmap.GUISupport.components.ColorGradientComboBox;
import co.foldingmap.GUISupport.components.HeatMapValueTableModel;
import co.foldingmap.Logger;
import co.foldingmap.actions.Actions;
import co.foldingmap.map.DigitalMap;
import co.foldingmap.map.MapObject;
import co.foldingmap.map.MapObjectList;
import co.foldingmap.map.themes.ColorRamp;
import co.foldingmap.map.vector.VectorLayer;
import co.foldingmap.map.vector.VectorObject;
import co.foldingmap.map.vector.VectorObjectList;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.Collections;
import javax.swing.*;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
/**
*
* @author Alec
*/
public class BubbleChartProperties extends ActionPanel
implements ItemListener,
ListSelectionListener {
private Actions actions;
private ArrayList<String> customDataFields;
private boolean areNumbers;
private ButtonGroup objectsToUseButtonGroup, keyOrientationButtonGroup;
private ColorGradientComboBox comboGradientStyle;
private DigitalMap mapData;
private HeatMapValueTableModel heatMapValueTableModel;
private JComboBox comboKeyPosition;
private JComboBox comboColorVariable, comboSizeVariable;
private JLabel labelKeyOrientation;
private JPanel panelGradientStyle;
private JPanel panelKeyOptions, panelKeyOrientation, panelKeyPosition;
private JPanel panelLayerName;
private JPanel panelObjectsToUse, panelObjectsToUseButtons, panelOptions;
private JPanel panelColorVariable, panelSizeVariable;
private JPanel panelTransparency;
private JRadioButton radioUseAllObject, radioUseSelectedObjects;
private JRadioButton radioHorizontal, radioVertical;
private JSlider sliderTransparency;
private JTable tableObjectValues;
private JTextField textName;
private JScrollPane spaneOptions, spaneStringValues;
private String[] keyPositions = {"No Key", "Top Left", "Top Right", "Bottom Left", "Bottom Right"};
private ValueRangePanel colorValueRange, sizeValueRange;
private VectorObjectList<VectorObject> selectedVectorObjects;
private VectorLayer vectorLayer;
public BubbleChartProperties(Actions actions, DigitalMap mapData, VectorLayer vectorLayer) {
this.mapData = mapData;
this.vectorLayer = vectorLayer;
//temp
this.selectedVectorObjects = new VectorObjectList<VectorObject>();
this.actions = actions;
init();
updateColorMinMax();
}
@Override
public void actionPerformed(ActionEvent ae) {
ColorRamp ramp = getColorRamp();
MapObjectList<MapObject> mapObjects;
mapData.getTheme().addColorRamp(ramp);
selectedVectorObjects = new VectorObjectList<VectorObject>();
if (radioUseAllObject.isSelected()) {
mapObjects = mapData.getAllMapObjects();
} else {
mapObjects = mapData.getSelectedObjects();
}
for (MapObject object: mapObjects) {
if (object instanceof VectorObject) {
selectedVectorObjects.add((VectorObject) object);
}
}
actions.CreateBubbleChart(mapData, selectedVectorObjects, ramp.getID(), (String) comboColorVariable.getSelectedItem(), (String) comboSizeVariable.getSelectedItem());
}
private void addObjectsToFrame(boolean numericalValues) {
panelOptions.removeAll();
//center panel
this.setLayout(new BorderLayout());
this.add(spaneOptions, BorderLayout.CENTER);
//options panel
panelOptions.add(panelLayerName);
panelOptions.add(panelObjectsToUse);
panelOptions.add(panelColorVariable);
panelOptions.add(panelSizeVariable);
//panelOptions.add(panelKeyOptions);
//objects to use
panelObjectsToUseButtons.add(radioUseAllObject);
panelObjectsToUseButtons.add(radioUseSelectedObjects);
objectsToUseButtonGroup.add(radioUseAllObject);
objectsToUseButtonGroup.add(radioUseSelectedObjects);
//Layer Name Panel
panelLayerName.setBorder(new TitledBorder("Layer Name"));
panelLayerName.add(textName);
//Objects to Use Panel
panelObjectsToUse.add(panelObjectsToUseButtons);
panelObjectsToUse.setBorder(new TitledBorder("Objects To Use"));
//Color Variable
panelColorVariable.removeAll();
panelColorVariable.setBorder(new TitledBorder("Bubble Color Variable"));
panelColorVariable.add(comboColorVariable);
//Value Range Panel
colorValueRange.setBorder(new TitledBorder("Value Range"));
if (numericalValues) {
panelGradientStyle.setBorder(new TitledBorder("Style"));
panelGradientStyle.add(comboGradientStyle);
panelColorVariable.add(colorValueRange);
panelColorVariable.add(panelGradientStyle);
} else {
panelColorVariable.add(spaneStringValues);
}
panelColorVariable.add(panelTransparency);
panelTransparency.add(sliderTransparency);
panelTransparency.setBorder(new TitledBorder("Transparency"));
//Size Variable Options Panel
panelSizeVariable.setBorder(new TitledBorder("Bubble Size Variable"));
panelSizeVariable.add(comboSizeVariable);
panelSizeVariable.add(sizeValueRange);
sizeValueRange.setBorder(new TitledBorder("Value Range"));
SpringUtilities.makeCompactGrid(panelSizeVariable, 2, 1, 3, 3, 10, 10);
//Key Orientation
keyOrientationButtonGroup.add(radioHorizontal);
keyOrientationButtonGroup.add(radioVertical);
panelKeyPosition.add(comboKeyPosition);
panelKeyPosition.setBorder(new TitledBorder("Key Position"));
panelKeyOrientation.add(radioHorizontal);
panelKeyOrientation.add(radioVertical);
panelKeyOrientation.setBorder(new TitledBorder("Key Orientation"));
panelKeyOptions.add(panelKeyPosition);
panelKeyOptions.add(panelKeyOrientation);
SpringUtilities.makeCompactGrid(panelOptions, 4, 1, 3, 3, 10, 10);
if (numericalValues) {
SpringUtilities.makeCompactGrid(panelColorVariable, 4, 1, 3, 3, 10, 10);
} else {
SpringUtilities.makeCompactGrid(panelColorVariable, 3, 1, 3, 3, 10, 10);
}
this.validate();
this.repaint();
}
/**
* Returns the color from the Gradient associated with the given value.
* The min an max from the textFields will be used to determine this color.
*
* @param value
* @return
*/
public Color getColorFromGradient(float value) {
Color color;
float adjustedValue, max, min, valueRatio;
int intColorInteger, colorPosistion;
int red, blue, green, alpha;
int[] pixelData;
color = Color.BLACK;
try {
max = (float) colorValueRange.getMaximum();
min = (float) colorValueRange.getMinimum();
adjustedValue = value - min;
valueRatio = adjustedValue / max;
colorPosistion = (int) (valueRatio * 255);
pixelData = comboGradientStyle.getSelectedGradientPixelData();
//adjust for custom min and max
if (colorPosistion > 255) {
colorPosistion = 255;
} else if (colorPosistion < 0) {
colorPosistion = 0;
}
if (pixelData != null) {
intColorInteger = pixelData[colorPosistion];
red = (intColorInteger & 0x00ff0000) >> 16;
green = (intColorInteger & 0x0000ff00) >> 8;
blue = (intColorInteger & 0x000000ff);
alpha = sliderTransparency.getValue();
color = new Color(red, green, blue, alpha);
}
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in BubbleChartProperties.getColorFromGradient(float) - " + e);
}
return color;
}
public ColorRamp getColorRamp() {
ArrayList<String> fieldValues;
boolean addPair, areNumericalValues;
Color color;
ColorRamp colorRamp;
int numberOfValues;
fieldValues = getColorFieldValues();
//test to see if field value are numbers or not
if (getValuesAsNumbers(fieldValues) != null) {
areNumericalValues = true;
} else {
areNumericalValues = false;
}
//Create the HashMap with the initial capacity equal to the number of field values
numberOfValues = fieldValues.size();
colorRamp = new ColorRamp(textName.getText() + "-ramp", numberOfValues);
//Load the HashMap
for (String value: fieldValues) {
if (areNumericalValues) {
if (!value.equals("")) {
color = getColorFromGradient(Float.parseFloat(value));
addPair = true;
} else {
//make blanks transparent
color = new Color(0,0,0,0);
addPair = false;
}
} else {
color = heatMapValueTableModel.getColorForValue(value);
addPair = true;
}
if (addPair == true)
colorRamp.addEntry(value, color);
}
return colorRamp;
}
/**
* Initializes all displayable objects
*/
private void init() {
try {
customDataFields = mapData.getAllCustomDataFields();
customDataFields.add("Altitude");
Collections.sort(customDataFields);
colorValueRange = new ValueRangePanel();
comboGradientStyle = new ColorGradientComboBox();
comboKeyPosition = new JComboBox(keyPositions);
comboColorVariable = new JComboBox(customDataFields.toArray());
comboSizeVariable = new JComboBox(customDataFields.toArray());
heatMapValueTableModel = new HeatMapValueTableModel(this, customDataFields);
keyOrientationButtonGroup = new ButtonGroup();
labelKeyOrientation = new JLabel("Key Orientation");
tableObjectValues = new JTable(heatMapValueTableModel);
panelColorVariable = new JPanel(new SpringLayout());
panelGradientStyle = new JPanel(new FlowLayout(FlowLayout.CENTER));
panelLayerName = new JPanel(new GridLayout(1, 2));
panelObjectsToUse = new JPanel(new GridLayout(1, 2));
panelObjectsToUseButtons = new JPanel(new GridLayout(1, 2));
panelOptions = new JPanel(new SpringLayout());
panelKeyOptions = new JPanel(new GridLayout(2, 1));
panelKeyPosition = new JPanel(new GridLayout(1, 1));
panelKeyOrientation = new JPanel(new GridLayout(1, 2));
panelSizeVariable = new JPanel(new SpringLayout());
panelTransparency = new JPanel(new GridLayout(1, 1));
objectsToUseButtonGroup = new ButtonGroup();
radioHorizontal = new JRadioButton("Horizontal", true);
radioVertical = new JRadioButton("Vertical");
radioUseAllObject = new JRadioButton("All Objects");
radioUseSelectedObjects = new JRadioButton("Selected objects");
sizeValueRange = new ValueRangePanel();
sliderTransparency = new JSlider(0, 255, 180);
spaneOptions = new JScrollPane(panelOptions);
spaneStringValues = new JScrollPane(tableObjectValues);
textName = new JTextField("Bubble Chart");
//Set default range values
colorValueRange.setRange(mapData.getMinimumFieldValue(customDataFields.get(0)), mapData.getMaximumFieldValue(customDataFields.get(0)));
this.setLayout(new SpringLayout());
radioUseAllObject.addActionListener(this);
radioUseSelectedObjects.addActionListener(this);
tableObjectValues.setDefaultRenderer(Component.class, new CellEditorRenderer());
tableObjectValues.setDefaultEditor(Component.class, new CellEditorRenderer());
tableObjectValues.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
tableObjectValues.getColumnModel().getColumn(0).setPreferredWidth(220);
tableObjectValues.getColumnModel().getColumn(1).setPreferredWidth(80);
tableObjectValues.getColumnModel().getColumn(2).setPreferredWidth(70);
spaneStringValues.setPreferredSize( new Dimension(245, 180));
textName.setMaximumSize(new Dimension(250, 25));
panelObjectsToUseButtons.setMaximumSize(new Dimension(250, 30));
sliderTransparency.setMaximumSize(new Dimension(250, 30));
panelGradientStyle.setMaximumSize(new Dimension(250, 30));
comboKeyPosition.addItemListener(this);
labelKeyOrientation.setEnabled(false);
radioHorizontal.setEnabled(false);
radioVertical.setEnabled(false);
comboColorVariable.addItemListener(this);
comboSizeVariable.addItemListener(this);
//Set the option buttons of what objects to use, baised on selected objects.
if (selectedVectorObjects.size() > 1) {
radioUseAllObject.setSelected(false);
radioUseSelectedObjects.setSelected(true);
} else {
radioUseAllObject.setSelected(true);
radioUseSelectedObjects.setSelected(false);
}
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in BubbleChartProperties.init() - " + e);
}
}
@Override
public void itemStateChanged(ItemEvent ie) {
if (ie.getSource() == comboKeyPosition) {
if (comboKeyPosition.getSelectedIndex() == 0) {
labelKeyOrientation.setEnabled(false);
radioHorizontal.setEnabled(false);
radioVertical.setEnabled(false);
} else {
labelKeyOrientation.setEnabled(true);
radioHorizontal.setEnabled(true);
radioVertical.setEnabled(true);
}
} else if (ie.getSource() == comboColorVariable) {
updateColorMinMax();
} else if (ie.getSource() == comboSizeVariable) {
updateSizeMinMax();
}
}
/**
* Gets the field values to be used when making the Bubble Chart.
*
* @return
*/
public ArrayList<String> getColorFieldValues() {
ArrayList<String> currentValues, fieldValues;
Object colorObject;
fieldValues = new ArrayList<String>();
colorObject = comboColorVariable.getSelectedItem();
if (radioUseSelectedObjects.isSelected()) {
currentValues = selectedVectorObjects.getCustomDataFieldValue((String) colorObject);
for (String s: currentValues) {
if (!fieldValues.contains(s))
fieldValues.add(s);
}
} else {
currentValues = mapData.getCustomDataFieldValue((String) colorObject);
for (String s: currentValues) {
if (!fieldValues.contains(s))
fieldValues.add(s);
}
}
return fieldValues;
}
/**
* Gets the field values to be used when making the Bubble Chart.
*
* @return
*/
public ArrayList<String> getSizeFieldValues() {
ArrayList<String> currentValues, fieldValues;
Object colorObject;
fieldValues = new ArrayList<String>();
colorObject = comboSizeVariable.getSelectedItem();
if (radioUseSelectedObjects.isSelected()) {
currentValues = selectedVectorObjects.getCustomDataFieldValue((String) colorObject);
for (String s: currentValues) {
if (!fieldValues.contains(s))
fieldValues.add(s);
}
} else {
currentValues = mapData.getCustomDataFieldValue((String) colorObject);
for (String s: currentValues) {
if (!fieldValues.contains(s))
fieldValues.add(s);
}
}
return fieldValues;
}
/**
* Returns the String values as numbers, if they can be converted.
* Returns null if they cannot, meaning they are not all numbers.
*
* @param values
* @return
*/
public ArrayList<Float> getValuesAsNumbers(ArrayList<String> values) {
ArrayList<Float> numbers;
float number;
numbers = new ArrayList<Float>();
try {
for (String s: values) {
if (!s.equals("")) {
number = Float.parseFloat(s);
numbers.add(number);
} else {
//ignore blanks
}
}
return numbers;
} catch (Exception e) {
//Formatting Error, not a number
return null;
}
}
/**
* Updates the min and max color text fields.
*/
private void updateColorMinMax() {
ArrayList<Float> numbers;
ArrayList<String> currentValues, fieldValues;
Object colorObject, sizeObject;
String selectedColorField, selcectedSizeField;
try {
colorObject = comboColorVariable.getSelectedItem();
sizeObject = comboSizeVariable.getSelectedItem();
selectedColorField = (String) comboColorVariable.getSelectedItem();
fieldValues = getColorFieldValues();
numbers = getValuesAsNumbers(fieldValues);
if (numbers != null && numbers.size() > 0) {
areNumbers = true;
Collections.sort(numbers);
colorValueRange.setMinimum(numbers.get(0));
if (numbers.size() > 1) {
colorValueRange.setMaximum(numbers.get(numbers.size() - 1));
} else {
colorValueRange.setMinimum(numbers.get(0));
}
addObjectsToFrame(true);
} else {
areNumbers = false;
Collections.sort(fieldValues);
heatMapValueTableModel.setTableData(fieldValues);
addObjectsToFrame(false);
}
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in BubbleChartProperties.updateColorMinMax() - " + e);
}
}
/**
* Updates the min and max color text fields.
*/
private void updateSizeMinMax() {
ArrayList<Float> numbers;
ArrayList<String> currentValues, fieldValues;
Object colorObject, sizeObject;
String selectedSizeField;
try {
sizeObject = comboSizeVariable.getSelectedItem();
selectedSizeField = (String) comboColorVariable.getSelectedItem();
fieldValues = getSizeFieldValues();
numbers = getValuesAsNumbers(fieldValues);
if (numbers != null && numbers.size() > 0) {
areNumbers = true;
Collections.sort(numbers);
sizeValueRange.setMinimum(numbers.get(0));
if (numbers.size() > 1) {
sizeValueRange.setMaximum(numbers.get(numbers.size() - 1));
} else {
sizeValueRange.setMinimum(numbers.get(0));
}
addObjectsToFrame(true);
} else {
areNumbers = false;
Collections.sort(fieldValues);
heatMapValueTableModel.setTableData(fieldValues);
addObjectsToFrame(false);
}
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in BubbleChartProperties.updateSizeMinMax() - " + e);
}
}
@Override
public void valueChanged(ListSelectionEvent lse) {
updateColorMinMax();
}
}