jcckit.plot.AxisParameters Maven / Gradle / Ivy
/*
* Copyright 2003-2004, Franz-Josef Elmer, All rights reserved
*
* 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 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 Lesser General Public License for more details
* (http://www.gnu.org/copyleft/lesser.html).
*
* 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
*/
package jcckit.plot;
import java.util.Properties;
import jcckit.graphic.BasicGraphicAttributes;
import jcckit.graphic.GraphPoint;
import jcckit.graphic.LineAttributes;
import jcckit.graphic.ShapeAttributes;
import jcckit.graphic.TextAttributes;
import jcckit.util.ConfigData;
import jcckit.util.ConfigParameters;
import jcckit.util.ConfigParametersBasedConfigData;
import jcckit.util.Factory;
import jcckit.util.Format;
import jcckit.util.PropertiesBasedConfigData;
import jcckit.util.TicLabelFormat;
import jcckit.util.Util;
/**
* Helper class with various parameters defining an axis.
* This helper class is used by {@link CartesianCoordinateSystem}
* to set up a coordinate systems.
*
* This class holds more than a dozen parameters. There are two factory
* methods creating instances for x- and y-axis based on
* {@link ConfigParameters}. They differ in their default parameters for
* those axes.
*
* Note, that there is a direct access of these parameters without getters
* and setters but only for classes in the package jcckit.plot.
*
* @author Franz-Josef Elmer
*/
public class AxisParameters {
/** Configuration parameter key. */
public static final String LOG_SCALE_KEY = "logScale",
MINIMUM_KEY = "minimum",
MAXIMUM_KEY = "maximum",
AXIS_LENGTH_KEY = "axisLength",
AXIS_ATTRIBUTES_KEY = "axisAttributes",
AXIS_LABEL_KEY = "axisLabel",
AXIS_LABEL_POSITION_KEY = "axisLabelPosition",
AXIS_LABEL_ATTRIBUTES_KEY = "axisLabelAttributes",
AUTOMATIC_TIC_CALCULATION_KEY
= "automaticTicCalculation",
MINIMUM_TIC_KEY = "minimumTic",
MAXIMUM_TIC_KEY = "maximumTic",
NUMBER_OF_TICS_KEY = "numberOfTics",
TIC_LENGTH_KEY = "ticLength",
TIC_ATTRIBUTES_KEY = "ticAttributes",
TIC_LABEL_FORMAT_KEY = "ticLabelFormat",
TIC_LABEL_POSITION_KEY = "ticLabelPosition",
TIC_LABEL_ATTRIBUTES_KEY = "ticLabelAttributes",
GRID_KEY = "grid",
GRID_ATTRIBUTES_KEY = "gridAttributes";
private static final double LN10 = Math.log(10);
/** If true the scale is logarithmic otherwise linear. */
boolean logScale;
/** Minimum data value represented by the axis. */
double minimum;
/** Maximum data value represented by the axis. */
double maximum;
/** Length of the axis in device-independent graphical units. */
double axisLength;
/**
* Line attributes of the axis.
* Can be null which means default attributes.
*/
LineAttributes axisAttributes;
boolean automaticTicCalculation;
double minimumTic;
double maximumTic;
int numberOfTics;
/**
* Length of the tics in device-independent graphical units.
* If 0 no tics and tics label will be drawn.
*/
double ticLength;
/**
* Attributes of the tics.
* Can be null which means default attributes.
*/
LineAttributes ticAttributes;
/** Tic label formatter. */
TicLabelFormat ticLabelFormat;
/** Position of the tic label relative to the tic. */
GraphPoint ticLabelPosition;
/** Text attributes of the tic labels. */
TextAttributes ticLabelAttributes;
/** If true grid lines are drawn. */
boolean grid;
/**
* Attributes of the grid lines.
* Can be null which means default attributes.
*/
LineAttributes gridAttributes;
/** Axis label. */
String axisLabel;
/** Position of the axis label relative to the center of the axis. */
GraphPoint axisLabelPosition;
/** Text attributes of the axis label. */
TextAttributes axisLabelAttributes;
/**
* Calculate the tics based on minimumTic, maximumTic,
* and numberOfTics. If automaticTicCalculation == true
* appropriated values for these fields are calculated.
*/
double[] calculateTics() {
if (automaticTicCalculation) {
calculateTicsParameters();
}
double[] result = new double[numberOfTics];
if (numberOfTics > 0) {
double b = Util.log(minimumTic, logScale);
double a = Util.log(maximumTic, logScale);
a = numberOfTics > 1 ? (a - b) / (numberOfTics - 1) : 0;
for (int i = 0; i < result.length; i++) {
result[i] = Util.exp(a * i + b, logScale);
}
result[0] = adjust(minimum, result[0]);
result[numberOfTics - 1] = adjust(maximum, result[numberOfTics - 1]);
}
return result;
}
private void calculateTicsParameters() {
double min = Math.min(minimum, maximum);
double max = Math.max(minimum, maximum);
if (logScale) {
int minExponent = (int) (199.9999 + Math.log(min) / LN10) - 199;
int maxExponent = (int) (200.0001 + Math.log(max) / LN10) - 200;
minimumTic = Math.exp(LN10 * minExponent);
maximumTic = Math.exp(LN10 * maxExponent);
numberOfTics = maxExponent - minExponent + 1;
} else {
int baseExponent = (int) (199.69 + Math.log(max - min) / LN10) - 200;
double base = 0.2 * Math.exp(LN10 * baseExponent);
do
{
base *= 5;
int minInt = (int) (999999.999999 + min / base) - 999999;
int maxInt = (int) (1000000.000001 + max / base) - 1000000;
minimumTic = minInt * base;
maximumTic = maxInt * base;
numberOfTics = maxInt - minInt + 1;
} while (numberOfTics > 11);
}
}
/**
* Returns adjustingValue if value is very close
* to adjustingValue. Otherwise value is returned.
*/
private static double adjust(double adjustingValue, double value) {
return value != 0 && Math.abs(adjustingValue / value - 1) < 1e-11
? adjustingValue : value;
}
/**
* Returns a Properties object with those default parameters
* which are common for x- and y-axis.
*/
private static Properties createDefaultAxisProperties() {
Properties p = new Properties();
p.put(LOG_SCALE_KEY, "false");
p.put(MINIMUM_KEY, "0");
p.put(MAXIMUM_KEY, "1");
p.put(AXIS_LENGTH_KEY, "0.8");
p.put(AXIS_ATTRIBUTES_KEY + '/' + Factory.CLASS_NAME_KEY,
ShapeAttributes.class.getName());
p.put(AXIS_LABEL_KEY, "x");
p.put(AXIS_LABEL_POSITION_KEY, "0 -0.05");
p.put(AXIS_LABEL_ATTRIBUTES_KEY + '/' + Factory.CLASS_NAME_KEY,
BasicGraphicAttributes.class.getName());
p.put(AXIS_LABEL_ATTRIBUTES_KEY + '/'
+ BasicGraphicAttributes.HORIZONTAL_ANCHOR_KEY, "center");
p.put(AUTOMATIC_TIC_CALCULATION_KEY, "true");
p.put(TIC_LENGTH_KEY, "0.01");
p.put(TIC_ATTRIBUTES_KEY + '/' + Factory.CLASS_NAME_KEY,
ShapeAttributes.class.getName());
p.put(TIC_LABEL_POSITION_KEY, "0 -0.01");
p.put(TIC_LABEL_FORMAT_KEY, "%1.1f");
p.put(TIC_LABEL_ATTRIBUTES_KEY + '/' + Factory.CLASS_NAME_KEY,
BasicGraphicAttributes.class.getName());
p.put(TIC_LABEL_ATTRIBUTES_KEY + '/'
+ BasicGraphicAttributes.HORIZONTAL_ANCHOR_KEY, "center");
p.put(TIC_LABEL_ATTRIBUTES_KEY + '/'
+ BasicGraphicAttributes.VERTICAL_ANCHOR_KEY, "top");
p.put(GRID_KEY, "false");
p.put(GRID_ATTRIBUTES_KEY + '/' + Factory.CLASS_NAME_KEY,
ShapeAttributes.class.getName());
return p;
}
/**
* Returns a Properties object of the default parameters for
* an x-axis.
*/
private static Properties createDefaultXAxisProperties() {
Properties p = createDefaultAxisProperties();
p.put(AXIS_LABEL_KEY, "x");
p.put(AXIS_LABEL_POSITION_KEY, "0 -0.05");
p.put(AXIS_LABEL_ATTRIBUTES_KEY + '/'
+ BasicGraphicAttributes.VERTICAL_ANCHOR_KEY, "top");
p.put(TIC_LABEL_POSITION_KEY, "0 -0.01");
p.put(TIC_LABEL_ATTRIBUTES_KEY + '/'
+ BasicGraphicAttributes.HORIZONTAL_ANCHOR_KEY, "center");
p.put(TIC_LABEL_ATTRIBUTES_KEY + '/'
+ BasicGraphicAttributes.VERTICAL_ANCHOR_KEY, "top");
return p;
}
/**
* Creates an x axis based on the specified configuration parameters.
* All numbers (lengths, fontsizes, linethicknesses, etc.) are in
* device-independent units.
*
* Key & Default Value Type Mandatory
* Description
* automaticTicCalculation = true
* boolean no
* Has to be true if the tics should be calculated
* automatically.
* axisAttributes = default values of
* {@link ShapeAttributes}
* ConfigParameters no
* Attributes of the axis box.
* axisLabel = x
* String no
* Axis label.
* axisLabelAttributes = default values of
* {@link BasicGraphicAttributes} with a text anchor CENTER
* TOP.
* ConfigParameters no
* Text attributes of axis label.
* axisLabelPosition = 0 -0.05
* double[] no
* Position of the anchor of the axis
* label relative to the center of the x-axis line.
* axisLength = 0.8
* double no
* Length of the x-axis.
* grid = false
* boolean no
* If true grid lines will be drawn through the axis
* tics.
* gridAttributes = default values of
* {@link ShapeAttributes}
* ConfigParameters no
* Attributes of the grid lines.
* logScale = false
* boolean no
* If true the axis will be logarithmic. Otherwise
* the axis is linear.
* maximum = 1
* double no
* The corresponding data value of one end of the axis.
* maximumTic = result from automatic calculation
* double no
* The corresponding data value of the tic nearest the maximum end
* of the axis.
* minimum = 0
* double no
* The corresponding data value of one end of the axis.
* minimumTic = result from automatic calculation
* double no
* The corresponding data value of the tic nearest the minimum end
* of the axis.
* numberOfTics = result from automatic calculation
* int no
* Number of tics. The tics between the minimum and maximum tic
* are spaced equidistantly.
* ticAttributes = default values of
* {@link ShapeAttributes}
* ConfigParameters no
* Attributes of the tics.
* ticLabelAttributes = default values of
* {@link BasicGraphicAttributes} with a text anchor CENTER
* TOP.
* ConfigParameters no
* Text attributes of tic labels.
* ticLabelFormat = %1.1f
* String or ConfigParameters no
* Defines rendering of the tic label. By default a
* printf-like format string is given (see {@link Format}).
* Note, that an empty string means that tic labels are dropped.
*
* For non-numerical rendering an implementation of a
* {@link TicLabelFormat} can be specified (e.g.
* {@link TicLabelMap}). Note, that a configuration sub tree with
* a className key-value pair overwrites any string
* definition.
* ticLabelPosition = 0 -0.01
* double[] no
* Position of the anchor of the tic label relative to the
* tic position on the axis.
* ticLength = 0.01
* double no
* Length of the tics. Negative/positive values mean tics
* inside/outside the box.
*
*/
public static AxisParameters createXAxis(ConfigParameters config) {
return createAxis(config, createDefaultXAxisProperties());
}
/**
* Returns a Properties object of the default parameters for
* an x-axis.
*/
private static Properties createDefaultYAxisProperties() {
Properties p = createDefaultAxisProperties();
p.put(AXIS_LENGTH_KEY, "0.45");
p.put(AXIS_LABEL_KEY, "y");
p.put(AXIS_LABEL_POSITION_KEY, "-0.1 0");
p.put(AXIS_LABEL_ATTRIBUTES_KEY + '/'
+ BasicGraphicAttributes.VERTICAL_ANCHOR_KEY, "bottom");
p.put(AXIS_LABEL_ATTRIBUTES_KEY + '/'
+ BasicGraphicAttributes.ORIENTATION_ANGLE_KEY, "90");
p.put(TIC_LABEL_POSITION_KEY, "-0.01 0");
p.put(TIC_LABEL_ATTRIBUTES_KEY + '/'
+ BasicGraphicAttributes.HORIZONTAL_ANCHOR_KEY, "right");
p.put(TIC_LABEL_ATTRIBUTES_KEY + '/'
+ BasicGraphicAttributes.VERTICAL_ANCHOR_KEY, "center");
return p;
}
/**
* Creates an y axis based on the specified configuration parameters.
* All numbers (lengths, fontsizes, linethicknesses, etc.) are in
* device-independent units.
*
* Key & Default Value Type Mandatory
* Description
* automaticTicCalculation = true
* boolean no
* Has to be true if the tics should be calculated
* automatically.
* axisAttributes = default values of
* {@link ShapeAttributes}
* ConfigParameters no
* Attributes of the axis box.
* axisLabel = y
* String no
* Axis label.
* axisLabelAttributes = default values of
* {@link BasicGraphicAttributes} with a text anchor CENTER
* BOTTOM and the text rotated by 90 degree.
* ConfigParameters no
* Text attributes of axis label.
* axisLabelPosition = -0.1 0
* double[] no
* Position of the anchor of the axis
* label relative to the center of the y-axis line.
* axisLength = 0.45
* double no
* Length of the y-axis.
* grid = false
* boolean no
* If true grid lines will be drawn through the axis
* tics.
* gridAttributes = default values of
* {@link ShapeAttributes}
* ConfigParameters no
* Attributes of the grid lines.
* logScale = false
* boolean no
* If true the axis will be logarithmic. Otherwise
* the axis is linear.
* maximum = 1
* double no
* The corresponding data value of one end of the axis.
* maximumTic = result from automatic calculation
* double no
* The corresponding data value of the tic nearest the maximum end
* of the axis.
* minimum = 0
* double no
* The corresponding data value of one end of the axis.
* minimumTic = result from automatic calculation
* double no
* The corresponding data value of the tic nearest the minimum end
* of the axis.
* numberOfTics = result from automatic calculation
* int no
* Number of tics. The tics between the minimum and maximum tic
* are spaced equidistantly.
* ticAttributes = default values of
* {@link ShapeAttributes}
* ConfigParameters no
* Attributes of the tics.
* ticLabelAttributes = default values of
* {@link BasicGraphicAttributes} with a text anchor RIGHT CENTER.
*
* ConfigParameters no
* Text attributes of tic labels.
* ticLabelFormat = %1.1f
* String no
* Defines rendering of the tic label. By default a
* printf-like format string is given (see {@link Format}).
* Note, that an empty string means that tic labels are dropped.
*
* For non-numerical rendering an implementation of a
* {@link TicLabelFormat} can be specified (e.g.
* {@link TicLabelMap}). Note, that a configuration sub tree with
* a className key-value pair overwrites any string
* definition.
* ticLabelPosition = -0.01 0
* double[] no
* Position of the anchor of the tic label relative to the
* tic position on the axis.
* ticLength = 0.01
* double no
* Length of the tics. Negative/positive values mean tics
* inside/outside the box.
*
*/
public static AxisParameters createYAxis(ConfigParameters config) {
return createAxis(config, createDefaultYAxisProperties());
}
private static AxisParameters createAxis(ConfigParameters config,
Properties p) {
ConfigData cd = new PropertiesBasedConfigData(p);
ConfigParameters c = new ConfigParameters(cd);
cd = new ConfigParametersBasedConfigData(config, c);
c = new ConfigParameters(cd);
AxisParameters a = new AxisParameters();
a.logScale = c.getBoolean(LOG_SCALE_KEY);
a.minimum = c.getDouble(MINIMUM_KEY);
a.maximum = c.getDouble(MAXIMUM_KEY);
a.axisLength = c.getDouble(AXIS_LENGTH_KEY);
a.axisAttributes
= (LineAttributes) Factory.create(c.getNode(AXIS_ATTRIBUTES_KEY));
a.axisLabel = c.get(AXIS_LABEL_KEY);
a.axisLabelPosition
= new GraphPoint(c.getDoubleArray(AXIS_LABEL_POSITION_KEY));
a.axisLabelAttributes = (TextAttributes) Factory.create(
c.getNode(AXIS_LABEL_ATTRIBUTES_KEY));
a.ticLength = c.getDouble(TIC_LENGTH_KEY);
a.automaticTicCalculation
= c.getBoolean(AUTOMATIC_TIC_CALCULATION_KEY);
if (!a.automaticTicCalculation) {
a.calculateTicsParameters(); // calculate default parameters
a.minimumTic = c.getDouble(MINIMUM_TIC_KEY, a.minimumTic);
a.maximumTic = c.getDouble(MAXIMUM_TIC_KEY, a.maximumTic);
a.numberOfTics = c.getInt(NUMBER_OF_TICS_KEY, a.numberOfTics);
}
a.ticAttributes
= (LineAttributes) Factory.create(c.getNode(TIC_ATTRIBUTES_KEY));
a.ticLabelFormat = createTicLabelFormat(c);
a.ticLabelPosition
= new GraphPoint(c.getDoubleArray(TIC_LABEL_POSITION_KEY));
a.ticLabelAttributes = (TextAttributes) Factory.create(
c.getNode(TIC_LABEL_ATTRIBUTES_KEY));
a.grid = c.getBoolean(GRID_KEY);
a.gridAttributes
= (LineAttributes) Factory.create(c.getNode(GRID_ATTRIBUTES_KEY));
return a;
}
private static TicLabelFormat createTicLabelFormat(ConfigParameters c)
{
TicLabelFormat result = Format.create(c, TIC_LABEL_FORMAT_KEY);
ConfigParameters node = c.getNode(TIC_LABEL_FORMAT_KEY);
if (node.get(Factory.CLASS_NAME_KEY, null) != null) {
result = (TicLabelFormat) Factory.create(node);
}
return result;
}
}