From c92fe4cea95b44bce75dde486654fa5843f125c2 Mon Sep 17 00:00:00 2001 From: Tim Molter <tim.molter@gmail.com> Date: Sun, 17 Feb 2013 21:54:18 +0100 Subject: [PATCH] another rearrange --- .../com/xeiam/xchart/demo/XChartDemo.java | 4 - .../DateChart01.java} | 27 ++-- .../xchart/demo/charts/line/LineChart09.java | 6 +- .../AxisTickCalculator.java | 104 +++++++++---- .../DateAxisTickCalculator.java | 144 +++++++++++------- .../axistickcalculator/DateFormatter.java | 127 --------------- .../NumberAxisTickCalculator.java | 30 ++++ .../axistickcalculator/NumberFormatter.java | 108 ------------- .../com/xeiam/xchart/style/StyleManager.java | 91 +++++++++-- .../xeiam/xchart/unit/ValueFormatterTest.java | 71 ++++----- 10 files changed, 324 insertions(+), 388 deletions(-) rename xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/{line/LineChart04.java => date/DateChart01.java} (73%) delete mode 100644 xchart/src/main/java/com/xeiam/xchart/internal/chartpart/axistickcalculator/DateFormatter.java delete mode 100644 xchart/src/main/java/com/xeiam/xchart/internal/chartpart/axistickcalculator/NumberFormatter.java diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/demo/XChartDemo.java b/xchart-demo/src/main/java/com/xeiam/xchart/demo/XChartDemo.java index 6c15a09a..f906f95a 100644 --- a/xchart-demo/src/main/java/com/xeiam/xchart/demo/XChartDemo.java +++ b/xchart-demo/src/main/java/com/xeiam/xchart/demo/XChartDemo.java @@ -33,7 +33,6 @@ import com.xeiam.xchart.demo.charts.area.AreaChart01; import com.xeiam.xchart.demo.charts.line.LineChart01; import com.xeiam.xchart.demo.charts.line.LineChart02; import com.xeiam.xchart.demo.charts.line.LineChart03; -import com.xeiam.xchart.demo.charts.line.LineChart04; import com.xeiam.xchart.demo.charts.line.LineChart05; import com.xeiam.xchart.demo.charts.line.LineChart06; import com.xeiam.xchart.demo.charts.line.LineChart07; @@ -140,9 +139,6 @@ public class XChartDemo extends JPanel implements TreeSelectionListener { chart = new DefaultMutableTreeNode(new ChartInfo("LineChart03 - Multiple curves on one Chart", new LineChart03().getChart())); category.add(chart); - chart = new DefaultMutableTreeNode(new ChartInfo("LineChart04 - Date Axis", new LineChart04().getChart())); - category.add(chart); - chart = new DefaultMutableTreeNode(new ChartInfo("LineChart05 - Vertical and horizontal lines", new LineChart05().getChart())); category.add(chart); diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart04.java b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/date/DateChart01.java similarity index 73% rename from xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart04.java rename to xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/date/DateChart01.java index fa954642..f1234c92 100644 --- a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart04.java +++ b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/date/DateChart01.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.xeiam.xchart.demo.charts.line; +package com.xeiam.xchart.demo.charts.date; import java.text.DateFormat; import java.text.ParseException; @@ -25,22 +25,15 @@ import java.util.TimeZone; import com.xeiam.xchart.Chart; import com.xeiam.xchart.SwingWrapper; +import com.xeiam.xchart.demo.charts.line.ExampleChart; import com.xeiam.xchart.style.Series; import com.xeiam.xchart.style.StyleManager.LegendPosition; -/** - * Date Axis - * <p> - * Demonstrates the following: - * <ul> - * <li>Date-tyoe X Axis Data - * <li>Placing legend inside northwest corner of plot area - */ -public class LineChart04 implements ExampleChart { +public class DateChart01 implements ExampleChart { public static void main(String[] args) { - ExampleChart exampleChart = new LineChart04(); + ExampleChart exampleChart = new DateChart01(); Chart chart = exampleChart.getChart(); new SwingWrapper(chart).displayChart(); } @@ -55,12 +48,12 @@ public class LineChart04 implements ExampleChart { Collection<Date> xData = new ArrayList<Date>(); Collection<Number> yData = new ArrayList<Number>(); - DateFormat sdf = new SimpleDateFormat("yyyy.MM.dd.HH"); + DateFormat sdf = new SimpleDateFormat("ss:S"); Date date = null; for (int i = 1; i <= 10; i++) { try { - date = sdf.parse("2012.12.22." + (10 + i)); + date = sdf.parse("31:" + (100 * i)); } catch (ParseException e) { e.printStackTrace(); } @@ -69,15 +62,13 @@ public class LineChart04 implements ExampleChart { } // Customize Chart - chart.setChartTitle("LineChart04"); - chart.setXAxisTitle("time of day"); - chart.setYAxisTitle("gigawatts"); - chart.getStyleManager().getDateFormatter().setTimezone(TimeZone.getTimeZone("UTC")); + chart.setChartTitle("DateChart01"); + chart.getStyleManager().setTimezone(TimeZone.getTimeZone("UTC")); chart.getStyleManager().setLegendPosition(LegendPosition.InsideNW); Series series = chart.addDateSeries("value", xData, yData); return chart; - } + } } diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart09.java b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart09.java index 39348ac9..c91f5353 100644 --- a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart09.java +++ b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart09.java @@ -81,9 +81,9 @@ public class LineChart09 implements ExampleChart { chart.getStyleManager().setLegendFont(new Font(Font.SERIF, Font.PLAIN, 18)); chart.getStyleManager().setAxisTitleFont(new Font(Font.SANS_SERIF, Font.ITALIC, 18)); chart.getStyleManager().setAxisTickLabelsFont(new Font(Font.SERIF, Font.PLAIN, 11)); - chart.getStyleManager().getDateFormatter().setDatePattern("dd-MMM"); - chart.getStyleManager().getDecimalFormatter().setNormalDecimalPattern("#.000"); - chart.getStyleManager().getDateFormatter().setLocale(Locale.GERMAN); + chart.getStyleManager().setDatePattern("dd-MMM"); + chart.getStyleManager().setNormalDecimalPattern("#.000"); + chart.getStyleManager().setLocale(Locale.GERMAN); Series series = chart.addDateSeries("Fake Data", xData, yData); series.setLineColor(SeriesColor.BLUE); diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/axistickcalculator/AxisTickCalculator.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/axistickcalculator/AxisTickCalculator.java index 35bf74aa..efacecbd 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/axistickcalculator/AxisTickCalculator.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/axistickcalculator/AxisTickCalculator.java @@ -22,12 +22,13 @@ package com.xeiam.xchart.internal.chartpart.axistickcalculator; import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.text.NumberFormat; import java.util.LinkedList; import java.util.List; import com.xeiam.xchart.internal.chartpart.Axis.AxisType; import com.xeiam.xchart.internal.chartpart.Axis.Direction; -import com.xeiam.xchart.internal.chartpart.AxisPair; import com.xeiam.xchart.style.StyleManager; /** @@ -65,43 +66,90 @@ public abstract class AxisTickCalculator { this.maxValue = maxValue; this.styleManager = styleManager; - calculate(); } - private void calculate() { - - // a check if all axis data are the exact same values - if (minValue == maxValue) { - if (getAxisType() == AxisType.Number) { - tickLabels.add(styleManager.getDecimalFormatter().formatNumber(maxValue)); - } else if (getAxisType() == AxisType.Date) { - tickLabels.add(styleManager.getDateFormatter().formatDateValue(maxValue, maxValue, maxValue)); + BigDecimal getGridStepDecimal(double gridStepHint) { + + // gridStepHint --> significand * 10 ** exponent + // e.g. 724.1 --> 7.241 * 10 ** 2 + double significand = gridStepHint; + int exponent = 0; + if (significand == 0) { + exponent = 1; + } else if (significand < 1) { + while (significand < 1) { + significand *= 10.0; + exponent--; + } + } else { + while (significand >= 10) { + significand /= 10.0; + exponent++; } - tickLocations.add((int) (workingSpace / 2.0)); - return; } - // tick space - a percentage of the working space available for ticks, i.e. 95% - int tickSpace = AxisPair.getTickSpace(workingSpace); // in plot space + // calculate the grid step with hint. + BigDecimal gridStep; + if (significand > 7.5) { + // gridStep = 10.0 * 10 ** exponent + gridStep = BigDecimal.TEN.multiply(pow(10, exponent)); + } else if (significand > 3.5) { + // gridStep = 5.0 * 10 ** exponent + gridStep = new BigDecimal(new Double(5).toString()).multiply(pow(10, exponent)); + } else if (significand > 1.5) { + // gridStep = 2.0 * 10 ** exponent + gridStep = new BigDecimal(new Double(2).toString()).multiply(pow(10, exponent)); + } else { + // gridStep = 1.0 * 10 ** exponent + gridStep = pow(10, exponent); + } + return gridStep; + } - // where the tick should begin in the working space in pixels - int margin = AxisPair.getTickStartOffset(workingSpace, tickSpace); // in plot space BigDecimal gridStep = getGridStepForDecimal(tickSpace); + /** + * Calculates the value of the first argument raised to the power of the second argument. + * + * @param base the base + * @param exponent the exponent + * @return the value <tt>a<sup>b</sup></tt> in <tt>BigDecimal</tt> + */ + private BigDecimal pow(double base, int exponent) { + + BigDecimal value; + if (exponent > 0) { + value = new BigDecimal(new Double(base).toString()).pow(exponent); + } else { + value = BigDecimal.ONE.divide(new BigDecimal(new Double(base).toString()).pow(-exponent)); + } + return value; + } - BigDecimal gridStep = getGridStep(tickSpace); - BigDecimal firstPosition = getFirstPosition(minValue, gridStep); + /** + * Format a number value, if the override patterns are null, it uses defaults + * + * @param value + * @return + */ + public String formatNumber(BigDecimal value) { - // generate all tickLabels and tickLocations from the first to last position - for (BigDecimal tickPosition = firstPosition; tickPosition.compareTo(maxValue) <= 0; tickPosition = tickPosition.add(gridStep)) { + NumberFormat numberFormat = NumberFormat.getNumberInstance(styleManager.getLocale()); + + BigDecimal absoluteValue = value.abs(); + + if (absoluteValue.compareTo(new BigDecimal("10000.000001")) == -1 && absoluteValue.compareTo(new BigDecimal(".0009999999")) == 1 || BigDecimal.ZERO.compareTo(value) == 0) { + + DecimalFormat normalFormat = (DecimalFormat) numberFormat; + normalFormat.applyPattern(styleManager.getNormalDecimalPattern()); + return normalFormat.format(value); + + } else { + + DecimalFormat scientificFormat = (DecimalFormat) numberFormat; + scientificFormat.applyPattern(styleManager.getScientificDecimalPattern()); + return scientificFormat.format(value); - if (getAxisType() == AxisType.Number) { - tickLabels.add(styleManager.getDecimalFormatter().formatNumber(tickPosition)); - } else if (getAxisType() == AxisType.Date) { - tickLabels.add(styleManager.getDateFormatter().formatDateValue(tickPosition, minValue, maxValue)); - } - // here we convert tickPosition finally to plot space, i.e. pixels - int tickLabelPosition = (int) (margin + ((tickPosition.subtract(minValue)).doubleValue() / (maxValue.subtract(minValue)).doubleValue() * tickSpace)); - tickLocations.add(tickLabelPosition); } + } public List<Integer> getTickLocations() { diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/axistickcalculator/DateAxisTickCalculator.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/axistickcalculator/DateAxisTickCalculator.java index b007ff60..58ef2836 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/axistickcalculator/DateAxisTickCalculator.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/axistickcalculator/DateAxisTickCalculator.java @@ -22,9 +22,12 @@ package com.xeiam.xchart.internal.chartpart.axistickcalculator; import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.util.concurrent.TimeUnit; import com.xeiam.xchart.internal.chartpart.Axis.AxisType; import com.xeiam.xchart.internal.chartpart.Axis.Direction; +import com.xeiam.xchart.internal.chartpart.AxisPair; import com.xeiam.xchart.style.StyleManager; /** @@ -34,6 +37,14 @@ import com.xeiam.xchart.style.StyleManager; */ public class DateAxisTickCalculator extends AxisTickCalculator { + public static final long SEC_SCALE = TimeUnit.SECONDS.toMillis(1L); + public static final long MIN_SCALE = TimeUnit.MINUTES.toMillis(1L); + public static final long HOUR_SCALE = TimeUnit.HOURS.toMillis(1L); + public static final long DAY_SCALE = TimeUnit.DAYS.toMillis(1L); + public static final long WEEK_SCALE = TimeUnit.DAYS.toMillis(1L) * 7; + public static final long MONTH_SCALE = TimeUnit.DAYS.toMillis(1L) * 31; + public static final long YEAR_SCALE = TimeUnit.DAYS.toMillis(1L) * 365; + /** * Constructor * @@ -46,7 +57,35 @@ public class DateAxisTickCalculator extends AxisTickCalculator { public DateAxisTickCalculator(Direction axisDirection, int workingSpace, BigDecimal minValue, BigDecimal maxValue, StyleManager styleManager) { super(axisDirection, workingSpace, minValue, maxValue, styleManager); + calculate(); + } + + private void calculate() { + + // a check if all axis data are the exact same values + if (minValue == maxValue) { + tickLabels.add(formatDateValue(maxValue, maxValue, maxValue)); + tickLocations.add((int) (workingSpace / 2.0)); + return; + } + + // tick space - a percentage of the working space available for ticks, i.e. 95% + int tickSpace = AxisPair.getTickSpace(workingSpace); // in plot space + + // where the tick should begin in the working space in pixels + int margin = AxisPair.getTickStartOffset(workingSpace, tickSpace); // in plot space BigDecimal gridStep = getGridStepForDecimal(tickSpace); + + BigDecimal gridStep = getGridStep(tickSpace); + BigDecimal firstPosition = getFirstPosition(minValue, gridStep); + + // generate all tickLabels and tickLocations from the first to last position + for (BigDecimal tickPosition = firstPosition; tickPosition.compareTo(maxValue) <= 0; tickPosition = tickPosition.add(gridStep)) { + tickLabels.add(formatDateValue(tickPosition, minValue, maxValue)); + // here we convert tickPosition finally to plot space, i.e. pixels + int tickLabelPosition = (int) (margin + ((tickPosition.subtract(minValue)).doubleValue() / (maxValue.subtract(minValue)).doubleValue() * tickSpace)); + tickLocations.add(tickLabelPosition); + } } /** @@ -61,67 +100,21 @@ public class DateAxisTickCalculator extends AxisTickCalculator { // the span of the data double span = Math.abs(maxValue.subtract(minValue).doubleValue()); // in data space - int tickMarkSpaceHint = (axisDirection == Direction.X ? DEFAULT_TICK_MARK_STEP_HINT_X : DEFAULT_TICK_MARK_STEP_HINT_Y); + double gridStepHint = span / tickSpace * DEFAULT_TICK_MARK_STEP_HINT_X; - // for very short plots, squeeze some more ticks in than normal - if (axisDirection == Direction.Y && tickSpace < 160) { - tickMarkSpaceHint = 25; - } + BigDecimal gridStep; + if (span < SEC_SCALE) { + // decimal + gridStep = getGridStepDecimal(gridStepHint); - double gridStepHint = span / tickSpace * tickMarkSpaceHint; - - // gridStepHint --> significand * 10 ** exponent - // e.g. 724.1 --> 7.241 * 10 ** 2 - double significand = gridStepHint; - int exponent = 0; - if (significand == 0) { - exponent = 1; - } else if (significand < 1) { - while (significand < 1) { - significand *= 10.0; - exponent--; - } } else { - while (significand >= 10) { - significand /= 10.0; - exponent++; - } - } - // calculate the grid step with hint. - BigDecimal gridStep; - if (significand > 7.5) { - // gridStep = 10.0 * 10 ** exponent - gridStep = BigDecimal.TEN.multiply(pow(10, exponent)); - } else if (significand > 3.5) { - // gridStep = 5.0 * 10 ** exponent - gridStep = new BigDecimal(new Double(5).toString()).multiply(pow(10, exponent)); - } else if (significand > 1.5) { - // gridStep = 2.0 * 10 ** exponent - gridStep = new BigDecimal(new Double(2).toString()).multiply(pow(10, exponent)); - } else { - // gridStep = 1.0 * 10 ** exponent - gridStep = pow(10, exponent); - } - return gridStep; - } + // date + gridStep = getGridStepDecimal(gridStepHint); - /** - * Calculates the value of the first argument raised to the power of the second argument. - * - * @param base the base - * @param exponent the exponent - * @return the value <tt>a<sup>b</sup></tt> in <tt>BigDecimal</tt> - */ - private BigDecimal pow(double base, int exponent) { - - BigDecimal value; - if (exponent > 0) { - value = new BigDecimal(new Double(base).toString()).pow(exponent); - } else { - value = BigDecimal.ONE.divide(new BigDecimal(new Double(base).toString()).pow(-exponent)); } - return value; + + return gridStep; } @Override @@ -136,6 +129,47 @@ public class DateAxisTickCalculator extends AxisTickCalculator { return firstPosition; } + /** + * Format a date value + * + * @param value + * @param min + * @param max + * @return + */ + public String formatDateValue(BigDecimal value, BigDecimal min, BigDecimal max) { + + String datePattern; + // TODO check if min and max are the same, then calculate this differently + + // intelligently set date pattern if none is given + long diff = max.subtract(min).longValue(); + + if (diff < SEC_SCALE) { + datePattern = "ss:S"; + } else if (diff < MIN_SCALE) { + datePattern = "mm:ss"; + } else if (diff < HOUR_SCALE) { + datePattern = "HH:mm"; + } else if (diff < DAY_SCALE) { + datePattern = "EEE HH:mm"; + } else if (diff < WEEK_SCALE) { + datePattern = "EEE"; + } else if (diff < MONTH_SCALE) { + datePattern = "MMM-dd"; + } else if (diff < YEAR_SCALE) { + datePattern = "yyyy:MMM"; + } else { + datePattern = "yyyy"; + } + + SimpleDateFormat simpleDateformat = new SimpleDateFormat(datePattern, styleManager.getLocale()); + simpleDateformat.setTimeZone(styleManager.getTimezone()); + simpleDateformat.applyPattern(datePattern); + + return simpleDateformat.format(value.longValueExact()); + } + @Override public AxisType getAxisType() { diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/axistickcalculator/DateFormatter.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/axistickcalculator/DateFormatter.java deleted file mode 100644 index 448002e0..00000000 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/axistickcalculator/DateFormatter.java +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Copyright (C) 2013 Xeiam LLC http://xeiam.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package com.xeiam.xchart.internal.chartpart.axistickcalculator; - -import java.math.BigDecimal; -import java.text.SimpleDateFormat; -import java.util.Locale; -import java.util.TimeZone; -import java.util.concurrent.TimeUnit; - -/** - * @author timmolter - */ -public class DateFormatter { - - private String datePattern; - private Locale locale; - private TimeZone timezone; - - private static final long SEC_SCALE = TimeUnit.SECONDS.toMillis(1L); - private static final long MIN_SCALE = TimeUnit.MINUTES.toMillis(1L); - private static final long HOUR_SCALE = TimeUnit.HOURS.toMillis(1L); - private static final long DAY_SCALE = TimeUnit.DAYS.toMillis(1L); - private static final long WEEK_SCALE = TimeUnit.DAYS.toMillis(1L) * 7; - private static final long MONTH_SCALE = TimeUnit.DAYS.toMillis(1L) * 31; - private static final long YEAR_SCALE = TimeUnit.DAYS.toMillis(1L) * 365; - - /** - * Constructor - */ - public DateFormatter() { - - datePattern = "HHmmss"; - locale = Locale.getDefault(); - timezone = TimeZone.getDefault(); - } - - /** - * Format a date value - * - * @param value - * @param min - * @param max - * @return - */ - public String formatDateValue(BigDecimal value, BigDecimal min, BigDecimal max) { - - // TODO check if min and max are the same, then calculate this differently - - // intelligently set date pattern if none is given - long diff = max.subtract(min).longValue(); - - if (diff < SEC_SCALE) { - datePattern = "ss:S"; - } else if (diff < MIN_SCALE) { - datePattern = "mm:ss"; - } else if (diff < HOUR_SCALE) { - datePattern = "HH:mm"; - } else if (diff < DAY_SCALE) { - datePattern = "EEE HH:mm"; - } else if (diff < WEEK_SCALE) { - datePattern = "EEE"; - } else if (diff < MONTH_SCALE) { - datePattern = "MMM-dd"; - } else if (diff < YEAR_SCALE) { - datePattern = "yyyy:MMM"; - } else { - datePattern = "yyyy"; - } - - SimpleDateFormat simpleDateformat = new SimpleDateFormat(datePattern, locale); - simpleDateformat.setTimeZone(timezone); - simpleDateformat.applyPattern(datePattern); - return simpleDateformat.format(value.longValueExact()); - - } - - /** - * Set the String formatter for Data x-axis - * - * @param pattern - the pattern describing the date and time format - */ - public void setDatePattern(String datePattern) { - - this.datePattern = datePattern; - } - - /** - * Set the locale to use for rendering the chart - * - * @param locale - the locale to use when formatting Strings and dates for the axis tick labels - */ - public void setLocale(Locale locale) { - - this.locale = locale; - } - - /** - * Set the timezone to use for formatting Date axis tick labels - * - * @param timezone the timezone to use when formatting date data - */ - public void setTimezone(TimeZone timezone) { - - this.timezone = timezone; - } - -} diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/axistickcalculator/NumberAxisTickCalculator.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/axistickcalculator/NumberAxisTickCalculator.java index 936eba15..7f286d4d 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/axistickcalculator/NumberAxisTickCalculator.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/axistickcalculator/NumberAxisTickCalculator.java @@ -25,6 +25,7 @@ import java.math.BigDecimal; import com.xeiam.xchart.internal.chartpart.Axis.AxisType; import com.xeiam.xchart.internal.chartpart.Axis.Direction; +import com.xeiam.xchart.internal.chartpart.AxisPair; import com.xeiam.xchart.style.StyleManager; /** @@ -46,6 +47,35 @@ public class NumberAxisTickCalculator extends AxisTickCalculator { public NumberAxisTickCalculator(Direction axisDirection, int workingSpace, BigDecimal minValue, BigDecimal maxValue, StyleManager styleManager) { super(axisDirection, workingSpace, minValue, maxValue, styleManager); + calculate(); + } + + private void calculate() { + + // a check if all axis data are the exact same values + if (minValue == maxValue) { + tickLabels.add(formatNumber(maxValue)); + tickLocations.add((int) (workingSpace / 2.0)); + return; + } + + // tick space - a percentage of the working space available for ticks, i.e. 95% + int tickSpace = AxisPair.getTickSpace(workingSpace); // in plot space + + // where the tick should begin in the working space in pixels + int margin = AxisPair.getTickStartOffset(workingSpace, tickSpace); // in plot space BigDecimal gridStep = getGridStepForDecimal(tickSpace); + + BigDecimal gridStep = getGridStep(tickSpace); + BigDecimal firstPosition = getFirstPosition(minValue, gridStep); + + // generate all tickLabels and tickLocations from the first to last position + for (BigDecimal tickPosition = firstPosition; tickPosition.compareTo(maxValue) <= 0; tickPosition = tickPosition.add(gridStep)) { + + tickLabels.add(formatNumber(tickPosition)); + // here we convert tickPosition finally to plot space, i.e. pixels + int tickLabelPosition = (int) (margin + ((tickPosition.subtract(minValue)).doubleValue() / (maxValue.subtract(minValue)).doubleValue() * tickSpace)); + tickLocations.add(tickLabelPosition); + } } /** diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/axistickcalculator/NumberFormatter.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/axistickcalculator/NumberFormatter.java deleted file mode 100644 index 594fa777..00000000 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/axistickcalculator/NumberFormatter.java +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Copyright (C) 2013 Xeiam LLC http://xeiam.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package com.xeiam.xchart.internal.chartpart.axistickcalculator; - -import java.math.BigDecimal; -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.util.Locale; - -/** - * @author timmolter - */ -public class NumberFormatter { - - private String normalDecimalPattern; - private String scientificDecimalPattern; - // TODO move to parent class?? - private Locale locale; - - /** - * Constructor - */ - public NumberFormatter() { - - normalDecimalPattern = "#.####"; - scientificDecimalPattern = "0.##E0"; - locale = Locale.getDefault(); - - } - - /** - * Format a number value, if the override patterns are null, it uses defaults - * - * @param value - * @return - */ - public String formatNumber(BigDecimal value) { - - NumberFormat numberFormat = NumberFormat.getNumberInstance(locale); - - BigDecimal absoluteValue = value.abs(); - - if (absoluteValue.compareTo(new BigDecimal("10000.000001")) == -1 && absoluteValue.compareTo(new BigDecimal(".0009999999")) == 1 || BigDecimal.ZERO.compareTo(value) == 0) { - - DecimalFormat normalFormat = (DecimalFormat) numberFormat; - normalFormat.applyPattern(normalDecimalPattern); - return normalFormat.format(value); - - } else { - - DecimalFormat scientificFormat = (DecimalFormat) numberFormat; - scientificFormat.applyPattern(scientificDecimalPattern); - return scientificFormat.format(value); - - } - - } - - /** - * Set the decimal formatter for all tick labels - * - * @param pattern - the pattern describing the decimal format - */ - public void setNormalDecimalPattern(String normalDecimalPattern) { - - this.normalDecimalPattern = normalDecimalPattern; - } - - /** - * Set the scientific notation formatter for all tick labels - * - * @param pattern - the pattern describing the scientific notation format - */ - public void setScientificDecimalPattern(String scientificDecimalPattern) { - - this.scientificDecimalPattern = scientificDecimalPattern; - } - - /** - * Set the locale to use for rendering the chart - * - * @param locale - the locale to use when formatting Strings and dates for the axis tick labels - */ - public void setLocale(Locale locale) { - - this.locale = locale; - } - -} diff --git a/xchart/src/main/java/com/xeiam/xchart/style/StyleManager.java b/xchart/src/main/java/com/xeiam/xchart/style/StyleManager.java index 244f8344..00507e5f 100644 --- a/xchart/src/main/java/com/xeiam/xchart/style/StyleManager.java +++ b/xchart/src/main/java/com/xeiam/xchart/style/StyleManager.java @@ -24,9 +24,9 @@ package com.xeiam.xchart.style; import java.awt.Color; import java.awt.Font; import java.awt.Stroke; +import java.util.Locale; +import java.util.TimeZone; -import com.xeiam.xchart.internal.chartpart.axistickcalculator.DateFormatter; -import com.xeiam.xchart.internal.chartpart.axistickcalculator.NumberFormatter; import com.xeiam.xchart.style.theme.Theme; import com.xeiam.xchart.style.theme.XChartTheme; @@ -97,9 +97,11 @@ public class StyleManager { private Color errorBarsColor; // Formatting //////////////////////////////// - - private NumberFormatter decimalFormatter; - private DateFormatter dateFormatter; + private String datePattern; + private Locale locale; + private TimeZone timezone; + private String normalDecimalPattern; + private String scientificDecimalPattern; /** * Constructor @@ -160,8 +162,11 @@ public class StyleManager { errorBarsColor = theme.getErrorBarsColor(); // Formatting //////////////////////////////// - decimalFormatter = new NumberFormatter(); - dateFormatter = new DateFormatter(); + datePattern = "HHmmss"; + locale = Locale.getDefault(); + timezone = TimeZone.getDefault(); + normalDecimalPattern = "#.####"; + scientificDecimalPattern = "0.##E0"; } /** @@ -755,14 +760,78 @@ public class StyleManager { // Formatting //////////////////////////////// - public NumberFormatter getDecimalFormatter() { + /** + * Set the String formatter for Data x-axis + * + * @param pattern - the pattern describing the date and time format + */ + public void setDatePattern(String datePattern) { + + this.datePattern = datePattern; + } + + public String getDatePattern() { + + return datePattern; + } + + /** + * Set the locale to use for rendering the chart + * + * @param locale - the locale to use when formatting Strings and dates for the axis tick labels + */ + public void setLocale(Locale locale) { + + this.locale = locale; + } - return decimalFormatter; + public Locale getLocale() { + + return locale; } - public DateFormatter getDateFormatter() { + /** + * Set the timezone to use for formatting Date axis tick labels + * + * @param timezone the timezone to use when formatting date data + */ + public void setTimezone(TimeZone timezone) { - return dateFormatter; + this.timezone = timezone; } + public TimeZone getTimezone() { + + return timezone; + } + + /** + * Set the decimal formatter for all tick labels + * + * @param pattern - the pattern describing the decimal format + */ + public void setNormalDecimalPattern(String normalDecimalPattern) { + + this.normalDecimalPattern = normalDecimalPattern; + } + + public String getNormalDecimalPattern() { + + return normalDecimalPattern; + } + + /** + * Set the scientific notation formatter for all tick labels + * + * @param pattern - the pattern describing the scientific notation format + */ + public void setScientificDecimalPattern(String scientificDecimalPattern) { + + this.scientificDecimalPattern = scientificDecimalPattern; + } + + public String getScientificDecimalPattern() { + + return scientificDecimalPattern; + } } diff --git a/xchart/src/test/java/com/xeiam/xchart/unit/ValueFormatterTest.java b/xchart/src/test/java/com/xeiam/xchart/unit/ValueFormatterTest.java index ac59498f..5a27d906 100644 --- a/xchart/src/test/java/com/xeiam/xchart/unit/ValueFormatterTest.java +++ b/xchart/src/test/java/com/xeiam/xchart/unit/ValueFormatterTest.java @@ -30,8 +30,9 @@ import java.util.TimeZone; import org.junit.Test; -import com.xeiam.xchart.internal.chartpart.axistickcalculator.DateFormatter; -import com.xeiam.xchart.internal.chartpart.axistickcalculator.NumberFormatter; +import com.xeiam.xchart.internal.chartpart.Axis.Direction; +import com.xeiam.xchart.internal.chartpart.axistickcalculator.DateAxisTickCalculator; +import com.xeiam.xchart.style.StyleManager; /** * @author timmolter @@ -43,59 +44,60 @@ public class ValueFormatterTest { @Test public void testNumberFormatting() { - NumberFormatter axisTickLabelFormatter = new NumberFormatter(); + StyleManager styleManager = new StyleManager(); + DateAxisTickCalculator dateAxisTickCalculator = new DateAxisTickCalculator(Direction.X, 1000, new BigDecimal(10), new BigDecimal(90), styleManager); // big - axisTickLabelFormatter.setLocale(locale); + styleManager.setLocale(locale); BigDecimal value = new BigDecimal("1"); - String stringValue = axisTickLabelFormatter.formatNumber(value); + String stringValue = dateAxisTickCalculator.formatNumber(value); assertThat(stringValue, equalTo("1")); value = new BigDecimal(1000L); - stringValue = axisTickLabelFormatter.formatNumber(value); + stringValue = dateAxisTickCalculator.formatNumber(value); assertThat(stringValue, equalTo("1000")); value = new BigDecimal("9999"); - stringValue = axisTickLabelFormatter.formatNumber(value); + stringValue = dateAxisTickCalculator.formatNumber(value); assertThat(stringValue, equalTo("9999")); value = new BigDecimal(20000L); - stringValue = axisTickLabelFormatter.formatNumber(value); + stringValue = dateAxisTickCalculator.formatNumber(value); assertThat(stringValue, equalTo("2E4")); value = new BigDecimal("200.23"); - stringValue = axisTickLabelFormatter.formatNumber(value); + stringValue = dateAxisTickCalculator.formatNumber(value); assertThat(stringValue, equalTo("200.23")); // small value = new BigDecimal("0.01"); - stringValue = axisTickLabelFormatter.formatNumber(value); + stringValue = dateAxisTickCalculator.formatNumber(value); assertThat(stringValue, equalTo("0.01")); value = new BigDecimal("0.001"); - stringValue = axisTickLabelFormatter.formatNumber(value); + stringValue = dateAxisTickCalculator.formatNumber(value); assertThat(stringValue, equalTo("0.001")); value = new BigDecimal("0.0012"); - stringValue = axisTickLabelFormatter.formatNumber(value); + stringValue = dateAxisTickCalculator.formatNumber(value); assertThat(stringValue, equalTo("0.0012")); value = new BigDecimal("0.0001"); - stringValue = axisTickLabelFormatter.formatNumber(value); + stringValue = dateAxisTickCalculator.formatNumber(value); assertThat(stringValue, equalTo("1E-4")); value = new BigDecimal(".00012"); - stringValue = axisTickLabelFormatter.formatNumber(value); + stringValue = dateAxisTickCalculator.formatNumber(value); assertThat(stringValue, equalTo("1.2E-4")); value = new BigDecimal("0.0"); - stringValue = axisTickLabelFormatter.formatNumber(value); + stringValue = dateAxisTickCalculator.formatNumber(value); assertThat(stringValue, equalTo("0")); value = new BigDecimal("0"); - stringValue = axisTickLabelFormatter.formatNumber(value); + stringValue = dateAxisTickCalculator.formatNumber(value); assertThat(stringValue, equalTo("0")); // other case @@ -111,24 +113,24 @@ public class ValueFormatterTest { // assertThat(stringValue, equalTo("0.01")); // non-default - axisTickLabelFormatter.setLocale(Locale.GERMANY); + styleManager.setLocale(Locale.GERMANY); value = new BigDecimal("0.01"); - stringValue = axisTickLabelFormatter.formatNumber(value); + stringValue = dateAxisTickCalculator.formatNumber(value); assertThat(stringValue, equalTo("0,01")); value = new BigDecimal("200.23"); - stringValue = axisTickLabelFormatter.formatNumber(value); + stringValue = dateAxisTickCalculator.formatNumber(value); assertThat(stringValue, equalTo("200,23")); - axisTickLabelFormatter.setNormalDecimalPattern("#.#"); + styleManager.setNormalDecimalPattern("#.#"); value = new BigDecimal("200.23"); - stringValue = axisTickLabelFormatter.formatNumber(value); + stringValue = dateAxisTickCalculator.formatNumber(value); assertThat(stringValue, equalTo("200,2")); - axisTickLabelFormatter.setScientificDecimalPattern("0.#E0"); + styleManager.setScientificDecimalPattern("0.#E0"); value = new BigDecimal("2009764.23"); - stringValue = axisTickLabelFormatter.formatNumber(value); + stringValue = dateAxisTickCalculator.formatNumber(value); assertThat(stringValue, equalTo("2E6")); } @@ -136,67 +138,68 @@ public class ValueFormatterTest { @Test public void testDateFormatting() { - DateFormatter axisTickLabelFormatter = new DateFormatter(); + StyleManager styleManager = new StyleManager(); + DateAxisTickCalculator dateAxisTickCalculator = new DateAxisTickCalculator(Direction.X, 1000, new BigDecimal(10), new BigDecimal(90), styleManager); TimeZone timeZone = TimeZone.getTimeZone("UTC"); - axisTickLabelFormatter.setLocale(locale); - axisTickLabelFormatter.setTimezone(timeZone); + styleManager.setLocale(locale); + styleManager.setTimezone(timeZone); // ms BigDecimal value = new BigDecimal(1358108105531L); BigDecimal min = new BigDecimal(1358108105100L); BigDecimal max = new BigDecimal(1358108105900L); - String stringValue = axisTickLabelFormatter.formatDateValue(value, min, max); + String stringValue = dateAxisTickCalculator.formatDateValue(value, min, max); assertThat(stringValue, equalTo("05:531")); // sec value = new BigDecimal(1358108105000L); min = new BigDecimal(1358108101000L); max = new BigDecimal(1358108109000L); - stringValue = axisTickLabelFormatter.formatDateValue(value, min, max); + stringValue = dateAxisTickCalculator.formatDateValue(value, min, max); assertThat(stringValue, equalTo("15:05")); // min value = new BigDecimal(1358111750000L); min = new BigDecimal(1358111690000L); max = new BigDecimal(1358111870000L); - stringValue = axisTickLabelFormatter.formatDateValue(value, min, max); + stringValue = dateAxisTickCalculator.formatDateValue(value, min, max); assertThat(stringValue, equalTo("21:15")); // hour value = new BigDecimal(1358111870000L); min = new BigDecimal(1358101070000L); max = new BigDecimal(1358115470000L); - stringValue = axisTickLabelFormatter.formatDateValue(value, min, max); + stringValue = dateAxisTickCalculator.formatDateValue(value, min, max); assertThat(stringValue, equalTo("Sun 21:17")); // day value = new BigDecimal(1358112317000L); min = new BigDecimal(1357939517000L); max = new BigDecimal(1358285117000L); - stringValue = axisTickLabelFormatter.formatDateValue(value, min, max); + stringValue = dateAxisTickCalculator.formatDateValue(value, min, max); assertThat(stringValue, equalTo("Sun")); // week value = new BigDecimal(1358112317000L); min = new BigDecimal(1357075517000L); max = new BigDecimal(1359149117000L); - stringValue = axisTickLabelFormatter.formatDateValue(value, min, max); + stringValue = dateAxisTickCalculator.formatDateValue(value, min, max); assertThat(stringValue, equalTo("Jan-13")); // month value = new BigDecimal(1358112838000L); min = new BigDecimal(1354397638000L); max = new BigDecimal(1361223238000L); - stringValue = axisTickLabelFormatter.formatDateValue(value, min, max); + stringValue = dateAxisTickCalculator.formatDateValue(value, min, max); assertThat(stringValue, equalTo("2013:Jan")); // year value = new BigDecimal(1358113402000L); min = new BigDecimal(1263419002000L); max = new BigDecimal(1421185402000L); - stringValue = axisTickLabelFormatter.formatDateValue(value, min, max); + stringValue = dateAxisTickCalculator.formatDateValue(value, min, max); assertThat(stringValue, equalTo("2013")); } -- GitLab