From fbeeef7cc119b19d6f64f50a9f3500d39c6a7259 Mon Sep 17 00:00:00 2001 From: John McNamara Date: Thu, 26 Jan 2017 00:07:38 +0000 Subject: [PATCH] Initial chart error bars. --- .indent.pro | 4 + include/xlsxwriter/chart.h | 105 +++++++--- src/chart.c | 180 ++++++++++++++++++ test/functional/src/test_chart_errorbars01.c | 51 +++++ test/functional/src/test_chart_errorbars02.c | 62 ++++++ test/functional/src/test_chart_errorbars03.c | 56 ++++++ test/functional/test_chart_errorbars.py | 23 +++ .../xlsx_files/chart_errorbars01.xlsx | Bin 0 -> 9357 bytes .../xlsx_files/chart_errorbars02.xlsx | Bin 0 -> 9395 bytes .../xlsx_files/chart_errorbars03.xlsx | Bin 0 -> 9411 bytes 10 files changed, 459 insertions(+), 22 deletions(-) create mode 100644 test/functional/src/test_chart_errorbars01.c create mode 100644 test/functional/src/test_chart_errorbars02.c create mode 100644 test/functional/src/test_chart_errorbars03.c create mode 100644 test/functional/test_chart_errorbars.py create mode 100644 test/functional/xlsx_files/chart_errorbars01.xlsx create mode 100644 test/functional/xlsx_files/chart_errorbars02.xlsx create mode 100644 test/functional/xlsx_files/chart_errorbars03.xlsx diff --git a/.indent.pro b/.indent.pro index 8254648e..2cd06365 100644 --- a/.indent.pro +++ b/.indent.pro @@ -54,6 +54,9 @@ -T lxw_chart_axis_tick_mark -T lxw_chart_axis_tick_position -T lxw_chart_blank +-T lxw_chart_error_bar_cap +-T lxw_chart_error_bar_direction +-T lxw_chart_error_bar_type -T lxw_chart_fill -T lxw_chart_font -T lxw_chart_gridline @@ -108,6 +111,7 @@ -T lxw_row_t -T lxw_selection -T lxw_series_data_point +-T lxw_series_error_bar -T lxw_series_range -T lxw_sst -T lxw_styles diff --git a/include/xlsxwriter/chart.h b/include/xlsxwriter/chart.h index 5d471c2c..a4e5840e 100644 --- a/include/xlsxwriter/chart.h +++ b/include/xlsxwriter/chart.h @@ -569,28 +569,6 @@ typedef enum lxw_chart_axis_tick_mark { LXW_CHART_AXIS_TICK_MARK_CROSSING } lxw_chart_tick_mark; -/** - * @brief Define how blank values are displayed in a chart. - */ -typedef enum lxw_chart_blank { - - /** Show empty chart cells as gaps in the data. The default. */ - LXW_CHART_BLANKS_AS_GAP, - - /** Show empty chart cells as zeros. */ - LXW_CHART_BLANKS_AS_ZERO, - - /** Show empty chart cells as connected. Only for charts with lines. */ - LXW_CHART_BLANKS_AS_CONNECTED -} lxw_chart_blank; - -enum lxw_chart_position { - LXW_CHART_AXIS_RIGHT, - LXW_CHART_AXIS_LEFT, - LXW_CHART_AXIS_TOP, - LXW_CHART_AXIS_BOTTOM -}; - typedef struct lxw_series_range { char *formula; char *sheetname; @@ -776,6 +754,81 @@ typedef struct lxw_chart_point { } lxw_chart_point; +/** + * @brief Define how blank values are displayed in a chart. + */ +typedef enum lxw_chart_blank { + + /** Show empty chart cells as gaps in the data. The default. */ + LXW_CHART_BLANKS_AS_GAP, + + /** Show empty chart cells as zeros. */ + LXW_CHART_BLANKS_AS_ZERO, + + /** Show empty chart cells as connected. Only for charts with lines. */ + LXW_CHART_BLANKS_AS_CONNECTED +} lxw_chart_blank; + +enum lxw_chart_position { + LXW_CHART_AXIS_RIGHT, + LXW_CHART_AXIS_LEFT, + LXW_CHART_AXIS_TOP, + LXW_CHART_AXIS_BOTTOM +}; + +/** + * @brief Type/amount of data series error bar. + */ +typedef enum lxw_chart_error_bar_type { + /** Error bar type: Standard error. */ + LXW_CHART_ERROR_BAR_TYPE_STD_ERROR, + + /** Error bar type: Fixed value. */ + LXW_CHART_ERROR_BAR_TYPE_FIXED, + + /** Error bar type: Percentage. */ + LXW_CHART_ERROR_BAR_TYPE_PERCENTAGE, + + /** Error bar type: Standard deviation(s). */ + LXW_CHART_ERROR_BAR_TYPE_STD_DEV +} lxw_chart_error_bar_type; + +/** + * @brief Direction for a data series error bar. + */ +typedef enum lxw_chart_error_bar_direction { + + /** Error bar extends in both directions. The default. */ + LXW_CHART_ERROR_BAR_DIR_BOTH, + + /** Error bar extends in positive direction. */ + LXW_CHART_ERROR_BAR_DIR_PLUS, + + /** Error bar extends in negative direction. */ + LXW_CHART_ERROR_BAR_DIR_MINUS +} lxw_chart_error_bar_direction; + +/** + * @brief End cap styles for a data series error bar. + */ +typedef enum lxw_chart_error_bar_cap { + /** Flat end cap. The default. */ + LXW_CHART_ERROR_BAR_END_CAP, + + /** No end cap. */ + LXW_CHART_ERROR_BAR_NO_CAP +} lxw_chart_error_bar_cap; + +typedef struct lxw_series_error_bar { + uint8_t type; + uint8_t direction; + uint8_t endcap; + uint8_t has_value; + double value; + lxw_chart_line *line; + +} lxw_series_error_bar; + /** * @brief Struct to represent an Excel chart data series. * @@ -812,6 +865,11 @@ typedef struct lxw_chart_series { char *label_num_format; lxw_chart_font *label_font; + lxw_series_error_bar x_error_bar; + lxw_series_error_bar y_error_bar; + uint8_t has_x_error_bar; + uint8_t has_y_error_bar; + STAILQ_ENTRY (lxw_chart_series) list_pointers; } lxw_chart_series; @@ -1699,6 +1757,9 @@ void chart_series_set_labels_num_format(lxw_chart_series *series, void chart_series_set_labels_font(lxw_chart_series *series, lxw_chart_font *font); +void chart_series_set_y_error_bars_line(lxw_chart_series *series, + lxw_chart_line *line); + /** * @brief Set the name caption of the an axis. * diff --git a/src/chart.c b/src/chart.c index d8dcabfa..7cfd8441 100644 --- a/src/chart.c +++ b/src/chart.c @@ -110,6 +110,9 @@ _chart_series_free(lxw_chart_series *series) _chart_free_range(series->title.range); _chart_free_points(series); + free(series->x_error_bar.line); + free(series->y_error_bar.line); + free(series); } @@ -2239,6 +2242,161 @@ _chart_write_d_lbls(lxw_chart *self, lxw_chart_series *series) lxw_xml_end_tag(self->file, "c:dLbls"); } +/* + * Write the element. + */ +STATIC void +_chart_write_error_val(lxw_chart *self, double value) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_DBL("val", value); + + lxw_xml_empty_tag(self->file, "c:val", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_no_end_cap(lxw_chart *self) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + LXW_PUSH_ATTRIBUTES_STR("val", "1"); + + lxw_xml_empty_tag(self->file, "c:noEndCap", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_err_val_type(lxw_chart *self, uint8_t type) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (type == LXW_CHART_ERROR_BAR_TYPE_FIXED) + LXW_PUSH_ATTRIBUTES_STR("val", "fixedVal"); + else if (type == LXW_CHART_ERROR_BAR_TYPE_PERCENTAGE) + LXW_PUSH_ATTRIBUTES_STR("val", "percentage"); + else if (type == LXW_CHART_ERROR_BAR_TYPE_STD_DEV) + LXW_PUSH_ATTRIBUTES_STR("val", "stdDev"); + else + LXW_PUSH_ATTRIBUTES_STR("val", "stdErr"); + + lxw_xml_empty_tag(self->file, "c:errValType", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_err_bar_type(lxw_chart *self, uint8_t direction) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (direction == LXW_CHART_ERROR_BAR_DIR_PLUS) + LXW_PUSH_ATTRIBUTES_STR("val", "plus"); + else if (direction == LXW_CHART_ERROR_BAR_DIR_MINUS) + LXW_PUSH_ATTRIBUTES_STR("val", "minus"); + else + LXW_PUSH_ATTRIBUTES_STR("val", "both"); + + lxw_xml_empty_tag(self->file, "c:errBarType", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_err_dir(lxw_chart *self, uint8_t vertical) +{ + struct xml_attribute_list attributes; + struct xml_attribute *attribute; + + LXW_INIT_ATTRIBUTES(); + + if (vertical) + LXW_PUSH_ATTRIBUTES_STR("val", "y"); + else + LXW_PUSH_ATTRIBUTES_STR("val", "n"); + + lxw_xml_empty_tag(self->file, "c:errDir", &attributes); + + LXW_FREE_ATTRIBUTES(); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_err_bars(lxw_chart *self, uint8_t vertical, + lxw_chart_series *series) +{ + lxw_series_error_bar *error_bar; + + if (vertical) + error_bar = &series->y_error_bar; + else + error_bar = &series->x_error_bar; + + lxw_xml_start_tag(self->file, "c:errBars", NULL); + + /* Write the c:errDir element. */ + _chart_write_err_dir(self, vertical); + + /* Write the c:errBarType element. */ + _chart_write_err_bar_type(self, error_bar->direction); + + /* Write the c:errValType element. */ + _chart_write_err_val_type(self, error_bar->type); + + /* Write the c:noEndCap element. */ + if (error_bar->endcap == LXW_CHART_ERROR_BAR_NO_CAP) + _chart_write_no_end_cap(self); + + /* Write the c:val element. */ + if (error_bar->has_value) + _chart_write_error_val(self, error_bar->value); + + /* Write the c:spPr element. */ + _chart_write_sp_pr(self, error_bar->line, NULL, NULL); + + lxw_xml_end_tag(self->file, "c:errBars"); +} + +/* + * Write the element. + */ +STATIC void +_chart_write_error_bars(lxw_chart *self, lxw_chart_series *series) +{ + if (series->has_x_error_bar) + _chart_write_err_bars(self, LXW_FALSE, series); + + if (series->has_y_error_bar) + _chart_write_err_bars(self, LXW_TRUE, series); +} + /* * Write the element. */ @@ -2450,6 +2608,9 @@ _chart_write_ser(lxw_chart *self, lxw_chart_series *series) /* Write the c:dLbls element. */ _chart_write_d_lbls(self, series); + /* Write the c:errBars element. */ + _chart_write_error_bars(self, series); + /* Write the c:cat element. */ _chart_write_cat(self, series); @@ -2495,6 +2656,9 @@ _chart_write_xval_ser(lxw_chart *self, lxw_chart_series *series) /* Write the c:dLbls element. */ _chart_write_d_lbls(self, series); + /* Write the c:errBars element. */ + _chart_write_error_bars(self, series); + /* Write the c:xVal element. */ _chart_write_x_val(self, series); @@ -4947,6 +5111,22 @@ chart_series_set_labels_font(lxw_chart_series *series, lxw_chart_font *font) series->label_font = _chart_convert_font_args(font); } +/* + * Set a line type for a series marker. + */ +void +chart_series_set_y_error_bars_line(lxw_chart_series *series, + lxw_chart_line *line) +{ + if (!line) + return; + + /* Free any previously allocated resource. */ + free(series->y_error_bar.line); + + series->y_error_bar.line = _chart_convert_line_args(line); +} + /* * Set an axis caption. */ diff --git a/test/functional/src/test_chart_errorbars01.c b/test/functional/src/test_chart_errorbars01.c new file mode 100644 index 00000000..4ca5e8b1 --- /dev/null +++ b/test/functional/src/test_chart_errorbars01.c @@ -0,0 +1,51 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = new_workbook("test_chart_errorbars01.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_LINE); + + /* For testing, copy the randomly generated axis ids in the target file. */ + chart->axis_id_1 = 63386752; + chart->axis_id_2 = 63388288; + + uint8_t data[5][3] = { + {1, 2, 3}, + {2, 4, 6}, + {3, 6, 9}, + {4, 8, 12}, + {5, 10, 15} + }; + + int row, col; + for (row = 0; row < 5; row++) + for (col = 0; col < 3; col++) + worksheet_write_number(worksheet, row, col, data[row][col], NULL); + + lxw_chart_series *series1 = chart_add_series(chart, + "=Sheet1!$A$1:$A$5", + "=Sheet1!$B$1:$B$5" + ); + + chart_add_series(chart, + "=Sheet1!$A$1:$A$5", + "=Sheet1!$C$1:$C$5" + ); + + + series1->has_y_error_bar = 1; + + worksheet_insert_chart(worksheet, CELL("E9"), chart); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_chart_errorbars02.c b/test/functional/src/test_chart_errorbars02.c new file mode 100644 index 00000000..2c454e1d --- /dev/null +++ b/test/functional/src/test_chart_errorbars02.c @@ -0,0 +1,62 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = new_workbook("test_chart_errorbars02.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_LINE); + + /* For testing, copy the randomly generated axis ids in the target file. */ + chart->axis_id_1 = 63385984; + chart->axis_id_2 = 63387904; + + uint8_t data[5][3] = { + {1, 2, 3}, + {2, 4, 6}, + {3, 6, 9}, + {4, 8, 12}, + {5, 10, 15} + }; + + int row, col; + for (row = 0; row < 5; row++) + for (col = 0; col < 3; col++) + worksheet_write_number(worksheet, row, col, data[row][col], NULL); + + lxw_chart_series *series1 = chart_add_series(chart, + "=Sheet1!$A$1:$A$5", + "=Sheet1!$B$1:$B$5" + ); + + lxw_chart_series *series2 = chart_add_series(chart, + "=Sheet1!$A$1:$A$5", + "=Sheet1!$C$1:$C$5" + ); + + series1->has_y_error_bar = 1; + series1->y_error_bar.type = LXW_CHART_ERROR_BAR_TYPE_FIXED; + series1->y_error_bar.direction = LXW_CHART_ERROR_BAR_DIR_MINUS; + series1->y_error_bar.endcap = LXW_CHART_ERROR_BAR_NO_CAP; + series1->y_error_bar.has_value = 1; + series1->y_error_bar.value = 2; + + series2->has_y_error_bar = 1; + series2->y_error_bar.type = LXW_CHART_ERROR_BAR_TYPE_PERCENTAGE; + series2->y_error_bar.direction = LXW_CHART_ERROR_BAR_DIR_PLUS; + series2->y_error_bar.endcap = LXW_CHART_ERROR_BAR_END_CAP; + series2->y_error_bar.has_value = 1; + series2->y_error_bar.value = 5; + + worksheet_insert_chart(worksheet, CELL("E9"), chart); + + return workbook_close(workbook); +} diff --git a/test/functional/src/test_chart_errorbars03.c b/test/functional/src/test_chart_errorbars03.c new file mode 100644 index 00000000..ec7af065 --- /dev/null +++ b/test/functional/src/test_chart_errorbars03.c @@ -0,0 +1,56 @@ +/***************************************************************************** + * Test cases for libxlsxwriter. + * + * Test to compare output against Excel files. + * + * Copyright 2014-2017, John McNamara, jmcnamara@cpan.org + * + */ + +#include "xlsxwriter.h" + +int main() { + + lxw_workbook *workbook = new_workbook("test_chart_errorbars03.xlsx"); + lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); + lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_LINE); + + /* For testing, copy the randomly generated axis ids in the target file. */ + chart->axis_id_1 = 52288896; + chart->axis_id_2 = 53605504; + + uint8_t data[5][3] = { + {1, 2, 3}, + {2, 4, 6}, + {3, 6, 9}, + {4, 8, 12}, + {5, 10, 15} + }; + + int row, col; + for (row = 0; row < 5; row++) + for (col = 0; col < 3; col++) + worksheet_write_number(worksheet, row, col, data[row][col], NULL); + + lxw_chart_series *series1 = chart_add_series(chart, + "=Sheet1!$A$1:$A$5", + "=Sheet1!$B$1:$B$5" + ); + + chart_add_series(chart, + "=Sheet1!$A$1:$A$5", + "=Sheet1!$C$1:$C$5" + ); + + + lxw_chart_line line = {.color = LXW_COLOR_RED, + .dash_type = LXW_CHART_LINE_DASH_ROUND_DOT}; + + series1->has_y_error_bar = 1; + series1->y_error_bar.type = LXW_CHART_ERROR_BAR_TYPE_STD_ERROR; + chart_series_set_y_error_bars_line(series1, &line); + + worksheet_insert_chart(worksheet, CELL("E9"), chart); + + return workbook_close(workbook); +} diff --git a/test/functional/test_chart_errorbars.py b/test/functional/test_chart_errorbars.py new file mode 100644 index 00000000..b0a765ab --- /dev/null +++ b/test/functional/test_chart_errorbars.py @@ -0,0 +1,23 @@ +############################################################################### +# +# Tests for libxlsxwriter. +# +# Copyright 2014-2017, John McNamara, jmcnamara@cpan.org +# + +import base_test_class + +class TestCompareXLSXFiles(base_test_class.XLSXBaseTest): + """ + Test file created with libxlsxwriter against a file created by Excel. + + """ + + def test_chart_errorbars01(self): + self.run_exe_test('test_chart_errorbars01') + + def test_chart_errorbars02(self): + self.run_exe_test('test_chart_errorbars02') + + def test_chart_errorbars03(self): + self.run_exe_test('test_chart_errorbars03') diff --git a/test/functional/xlsx_files/chart_errorbars01.xlsx b/test/functional/xlsx_files/chart_errorbars01.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..c8e607f8bcfa34d273aafaf35e145b6455dec4d2 GIT binary patch literal 9357 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 TS^T5Q#{`(d4FQ|-$FKhZK6osa literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/chart_errorbars02.xlsx b/test/functional/xlsx_files/chart_errorbars02.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..2c78325fa4687312ee97f992cd1248812202ed59 GIT binary patch literal 9395 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 D>fcqo literal 0 HcmV?d00001 diff --git a/test/functional/xlsx_files/chart_errorbars03.xlsx b/test/functional/xlsx_files/chart_errorbars03.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..1b927d4f8a5f3534f145426e11e0d5db651cf9a6 GIT binary patch literal 9411 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 Re{cgR07Hl)2oe4G^*<1#QE>nO literal 0 HcmV?d00001