1 // This may look like C code, but it's really -*- C++ -*-
2 /*
3 * Copyright (C) 2008 Emweb bv, Herent, Belgium.
4 *
5 * See the LICENSE file for terms of use.
6 */
7 #ifndef CHART_WDATA_SERIES_H_
8 #define CHART_WDATA_SERIES_H_
9
10 #include <Wt/Chart/WAxis.h>
11 #include <Wt/WBrush.h>
12 #include <Wt/WObject.h>
13 #include <Wt/WPen.h>
14 #include <Wt/WShadow.h>
15 #include <Wt/Chart/WChartGlobal.h>
16 #include <Wt/WPainterPath.h>
17
18 namespace Wt {
19
20 class WPointF;
21
22 namespace Chart {
23
24 class WAbstractChartModel;
25
26 /*! \brief Enumeration that indicates an aspect of the look.
27 *
28 * These flags are used to keep track of which aspects of the look
29 * that are overridden from the values provided by the chart palette,
30 * using one of the methods in this class.
31 *
32 * \sa setPen(), setBrush(), setMarkerPen(), setMarkerBrush(), setLabelColor()
33 */
34 enum class CustomFlag {
35 Pen = 0x1, //!< A custom pen is set.
36 Brush = 0x2, //!< A custom brush is set.
37 MarkerPen = 0x4, //!< A custom marker pen is set.
38 MarkerBrush = 0x8, //!< A custom marker brush is set.
39 LabelColor = 0x10 //!< A custom label color is set.
40 };
41
42 /*! \class WDataSeries Wt/Chart/WDataSeries.h Wt/Chart/WDataSeries.h
43 * \brief A single data series in a cartesian chart.
44 *
45 * This class configures all aspects for rendering a single data series
46 * in a cartesian chart. A data series renders Y data from a single
47 * model column against the X series configured for the chart.
48 *
49 * \if cpp
50 * The data column should contain data that can be converted to
51 * a number, but should not necessarily be of a number type, see
52 * also asNumber(const boost::any&).
53 * \elseif java
54 * The data column should contain data that can be converted to
55 * a number, but should not necessarily be of a number type, see
56 * also {javadoclink eu.webtoolkit.jwt.StringUtils#asNumber(Object)}.
57 * \endif
58 *
59 * Multiple series of different types may be combined on a single chart.
60 *
61 * \image html ChartWDataSeries-1.png "Different styles of data series"
62 *
63 * For a category chart, series may be stacked on top of each other.
64 * This is controlled by setStacked() for a series, which if enabled,
65 * will stack that series on top of the preceding data series. This
66 * works regardless of whether they are of the same type, but
67 * obviously works visually best if these series are of the same
68 * type. When not stacked, bar series are rendered next to each other.
69 * The margin between bars of different data series is controlled
70 * using WCartesianChart::setBarMargin().
71 *
72 * The line and color type are by default based on the \link
73 * WCartesianChart::palette() chart palette\endlink, but may be
74 * overridden for a series using setPen(), setBrush(), etc...
75 *
76 * \sa WCartesianChart::addSeries()
77 *
78 * \ingroup charts
79 */
80 class WT_API WDataSeries
81 #ifdef WT_TARGET_JAVA
82 : public Wt::WObject
83 #endif // WT_TARGET_JAVA
84 {
85 public:
86 /*! \brief Constructs a new data series.
87 *
88 * Creates a new data series which plots the Y values from the
89 * model column <i>modelColumn</i>, with the indicated
90 * <i>seriesType</i>. The Y values are mapped to the indicated
91 * <i>axis</i>, which should correspond to one of the two Y axes.
92 *
93 * \sa WCartesianChart::addSeries()
94 */
95 WDataSeries(int modelColumn, SeriesType seriesType = SeriesType::Point,
96 Axis axis = Axis::Y1);
97
98 /*! \brief Constructs a new data series.
99 *
100 * Creates a new data series which plots the Y values from the
101 * model column <i>modelColumn</i>, with the indicated
102 * <i>seriesType</i>. The Y values are mapped to the indicated
103 * <i>yAxis</i>, which should correspond to one of the two Y axes.
104 *
105 * \sa WCartesianChart::addSeries()
106 */
107 WDataSeries(int modelColumn, SeriesType seriesType, int yAxis);
108
109 /*! \brief Destructor.
110 */
111 ~WDataSeries();
112
113 /*! \brief Sets the bar width.
114 *
115 * The bar width specifies the bar width (in axis dimensions). For
116 * category plots, which may have several bars for different series
117 * next to each other, you will want to specify the same bar width
118 * for each series.
119 *
120 * For scatter plots, you may want to set the bar width to a natural
121 * size. E.g. if you are plotting weekly measurements, you could set
122 * the width to correspond to a week (=7).
123 *
124 * The default value is 0.8 (which leaves a 20% margin between bars
125 * for different categories in a category chart.
126 *
127 * \sa WCartesianChart::setBarMargin()
128 */
129 void setBarWidth(const double width);
130
131 /*! \brief Returns the bar width.
132 *
133 * \sa setBarWidth()
134 */
135 double barWidth() const;
136
137 /*! \brief Sets the series type.
138 *
139 * The series type specifies how the data is plotted, i.e. using
140 * mere point markers, lines, curves, or bars.
141 */
142 void setType(SeriesType t);
143
144 /*! \brief Returns the series type.
145 *
146 * \sa setType()
147 */
type()148 SeriesType type() const { return type_; }
149
150 /*! \brief Sets the model column.
151 *
152 * This specifies the model column from which the Y data is retrieved
153 * that is plotted by this series.
154 *
155 * The data column should contain data that can be converted to
156 * a number (but should not necessarily be of a number type).
157 * \if java
158 * See also {javadoclink eu.webtoolkit.jwt.StringUtils#asNumber(Object)}.
159 * \endif
160 *
161 * \if cpp
162 * \sa Wt::asNumber()
163 * \endif
164 */
165 void setModelColumn(int modelColumn);
166
167 /*! \brief Returns the model column.
168 *
169 * \sa setModelColumn()
170 */
modelColumn()171 int modelColumn() const { return modelColumn_; }
172
173 /*! \brief Sets the X series column.
174 *
175 * By default, the data series uses the X series column configured
176 * for the chart. For a scatter plot, each series can have its own
177 * matching X data, which is configured here. For other plots, this
178 * setting is ignored.
179 *
180 * The default value is -1, which indicates that
181 * WCartesianChart::XSeriesColumn() is to be used.
182 *
183 * \sa WCartesianChart::setXSeriesColumn()
184 */
185 void setXSeriesColumn(int modelColumn);
186
187 /*! \brief Returns the X series column.
188 *
189 * \sa setXSeriesColumn()
190 */
XSeriesColumn()191 int XSeriesColumn() const { return XSeriesColumn_; }
192
193 /*! \brief Sets whether this series is stacked on top of the preceding series.
194 *
195 * For category charts, data from different series may be rendered
196 * stacked on top of each other. The rendered value is the sum of the
197 * value of this series plus the rendered value of the preceding
198 * series. For line series, you probably will want to add filling
199 * under the curve. A stacked bar series is rendered by a bar on top
200 * of the preceding bar series.
201 *
202 * The default value is false.
203 */
204 void setStacked(bool stacked);
205
206 /*! \brief Returns whether this series is stacked on top of the preceding
207 * series.
208 *
209 * \sa setStacked()
210 */
isStacked()211 bool isStacked() const { return stacked_; }
212
213 /*! \brief Binds this series to a chart axis.
214 *
215 * A data series may be bound to either the first or second Y axis.
216 * Note that the second Y axis is by default not displayed.
217 *
218 * The default value is the first Y axis.
219 *
220 * \sa WAxis::setVisible()
221 */
222 void bindToAxis(Axis axis);
223
224 /*! \brief Binds this series to a chart's X axis.
225 *
226 * Note that the second Y axis will not be displayed by default.
227 *
228 * The default value is the first X axis.
229 *
230 * \sa WAxis::setVisible()
231 */
232 void bindToXAxis(int xAxis);
233
234 /*! \brief Binds this series to a chart's Y axis.
235 *
236 * Note that the second Y axis will not be displayed by default.
237 *
238 * The default value is the first Y axis.
239 *
240 * \sa WAxis::setVisible()
241 */
242 void bindToYAxis(int yAxis);
243
244 /*! \brief Returns the Y axis used for this series.
245 *
246 * \sa bindToAxis()
247 */
axis()248 Axis axis() const { return yAxis_ == 1 ? Axis::Y2 : Axis::Y1; }
249
250 /*! \brief Returns the Y axis used for this series.
251 *
252 * \sa bindToXAxis()
253 */
xAxis()254 int xAxis() const { return xAxis_; }
255
256 /*! \brief Returns the Y axis used for this series.
257 *
258 * \sa bindToYAxis()
259 */
yAxis()260 int yAxis() const { return yAxis_; }
261
262 /*! \brief Sets which aspects of the look are overriden.
263 *
264 * Set which aspects of the look, that are by default based on the
265 * chart palette, are overridden by custom settings.
266 *
267 * The default value is 0 (nothing overridden).
268 */
269 void setCustomFlags(WFlags<CustomFlag> customFlags);
270
271 /*! \brief Returns which aspects of the look are overriden.
272 *
273 * \sa setCustomFlags()
274 */
customFlags()275 WFlags<CustomFlag> customFlags() const { return customFlags_; }
276
277 /*! \brief Overrides the pen used for drawing lines for this series.
278 *
279 * Overrides the pen that is used to draw this series. Calling this
280 * method automatically adds CustomPen to the custom flags.
281 *
282 * The default value is a default WPen().
283 *
284 * \sa WChartPalette::strokePen(), WChartPalette::borderPen()
285 */
286 void setPen(const WPen& pen);
287
288 /*! \brief Returns the pen used for drawing lines for this series.
289 *
290 * \sa setPen()
291 */
292 WPen pen() const;
293
294 /*! \brief Overrides the brush used for filling areas for this series.
295 *
296 * Overrides the brush that is used to draw this series which is
297 * otherwise provided by the chart palette. For a bar plot, this is the
298 * brush used to fill the bars. For a line chart, this is the brush
299 * used to fill the area under (or above) the line. Calling this
300 * method automatically adds CustomBrush to the custom flags.
301 *
302 * \sa WChartPalette::brush()
303 */
304 void setBrush(const WBrush& brush);
305
306 /*! \brief Returns the brush used for filling areas for this series.
307 *
308 * \sa setBrush()
309 */
310 WBrush brush() const;
311
312 /*! \brief Sets a shadow used for stroking lines for this series.
313 */
314 void setShadow(const WShadow& shadow);
315
316 /*! \brief Returns the shadow used for stroking lines for this series.
317 *
318 * \sa setShadow()
319 */
320 const WShadow& shadow() const;
321
322 /*! \brief Sets the fill range for line or curve series.
323 *
324 * Line or curve series may be filled under or above the curve,
325 * using the brush(). This setting specifies the range that is
326 * filled. The default value for all but SeriesType::Bar is FillRangeType::None.
327 *
328 * Bar series may use FillRangeType::MinimumValue to configure the chart to
329 * render its bars from the data point to the bottom of the chart or
330 * FillRangeType::MaximumValue to render the bars from the data point to the
331 * top of the chart. The default value for SeriesType::Bar is
332 * FillRangeType::ZeroValue, which render bars from zero to the data value.
333 */
334 void setFillRange(FillRangeType fillRange);
335
336 /*! \brief Returns the fill range (for line, curve and bar series).
337 *
338 * \sa setFillRange()
339 */
340 FillRangeType fillRange() const;
341
342 /*! \brief Sets the data point marker.
343 *
344 * Specifies a marker that is displayed at the (X,Y) coordinate for each
345 * series data point.
346 *
347 * The default value is a MarkerType::Circle for a SeriesType::Point, or MarkerType::None
348 * otherwise.
349 *
350 * \sa setMarkerPen(), setMarkerBrush(), setCustomMarker()
351 */
352 void setMarker(MarkerType marker);
353
354 /*! \brief Sets the custom marker.
355 *
356 * This will also changes the marker type to MarkerType::Custom.
357 *
358 * \sa setMarker()
359 */
360 void setCustomMarker(const WPainterPath& path);
361
362 /*! \brief Returns the custom marker.
363 *
364 * \sa setCustomMarker()
365 */
customMarker()366 WPainterPath customMarker() const { return customMarker_; };
367
368 /*! \brief Returns the data point marker.
369 *
370 * \sa setMarker()
371 */
marker()372 MarkerType marker() const { return marker_; }
373
374 /*! \brief Sets the marker size.
375 *
376 * The default marker size is 6 pixels.
377 */
378 void setMarkerSize(double size);
379
380 /*! \brief Returns the marker size.
381 *
382 * \sa setMarkerSize()
383 */
markerSize()384 double markerSize() const { return markerSize_; }
385
386 /*! \brief Sets the marker pen.
387 *
388 * Overrides the pen used for stroking the marker. By default the
389 * marker pen is the same as pen(). Calling this method automatically adds
390 * CustomMarkerPen to the custom flags.
391 *
392 * \sa setPen(), setMarkerBrush()
393 */
394 void setMarkerPen(const WPen& pen);
395
396 /*! \brief Returns the marker pen.
397 *
398 * \sa setMarkerPen()
399 */
400 WPen markerPen() const;
401
402 /*! \brief Sets the marker brush.
403 *
404 * Overrides the brush used for filling the marker. By default the
405 * marker brush is the same as brush(). Calling this method
406 * automatically adds CustomMarkerBrush to the custom flags.
407 *
408 * \sa setBrush(), setMarkerPen()
409 */
410 void setMarkerBrush(const WBrush& brush);
411
412 /*! \brief Returns the marker brush.
413 *
414 * \sa setMarkerBrush()
415 */
416 WBrush markerBrush() const;
417
418 /*! \brief Enables the entry for this series in the legend.
419 *
420 * When <i>enabled</i>, this series is added to the chart
421 * legend.
422 *
423 * The default value is true.
424 *
425 * \sa WCartesianChart::setLegendEnabled().
426 */
427 void setLegendEnabled(bool enabled);
428
429 /*! \brief Returns whether this series has an entry in the legend.
430 *
431 * \sa setLegendEnabled()
432 */
433 bool isLegendEnabled() const;
434
435 /*! \brief Enables a label that is shown at the series data points.
436 *
437 * You may enable labels for the Axis::X, Axis::Y or both axes. The
438 * label that is displayed is the corresponding value on that
439 * axis. If both labels are enabled then they are combined in a
440 * single text using the format: "<x-value>: <y-value>".
441 *
442 * The default values are false for both axes (no labels).
443 *
444 * \sa isLabelsEnabled()
445 */
446 void setLabelsEnabled(Axis axis, bool enabled = true);
447
448 /*! \brief Returns whether labels are enabled for the given axis.
449 *
450 * \sa setLabelsEnabled()
451 */
452 bool isLabelsEnabled(Axis axis) const;
453
454 /*! \brief Sets the label color.
455 *
456 * Specify the color used for the rendering labels at the data
457 * points.
458 *
459 * \sa setLabelsEnabled()
460 */
461 void setLabelColor(const WColor& color);
462
463 /*! \brief Returns the label color.
464 *
465 * \sa setLabelColor()
466 */
467 WColor labelColor() const;
468
469 /*! \brief Hide/unhide this series.
470 *
471 * A hidden series will not be show in the chart and legend.
472 */
473 void setHidden(bool hidden);
474
475 /*! \brief Return whether the series is hidden.
476 *
477 * \sa setHidden()
478 */
479 bool isHidden() const;
480
481 /*! \brief Maps from device coordinates to model coordinates.
482 *
483 * Maps a position in the chart back to model coordinates, for data
484 * in this data series.
485 *
486 * This uses WChart::mapFromDevice() passing the axis() to which this
487 * series is bound.
488 *
489 * This method uses the axis dimensions that are based on the latest
490 * chart rendering. If you have not yet rendered the chart, or wish
491 * to already the mapping reflect model changes since the last
492 * rendering, you should call WCartesianChart::initLayout() first.
493 *
494 * \sa mapToDevice()
495 */
496 WPointF mapFromDevice(const WPointF& deviceCoordinates) const;
497
498 /*! \brief Maps from model values to device coordinates.
499 *
500 * Maps model values to device coordinates, for data in this data series.
501 *
502 * This uses WChart::mapToDevice() passing the axis() to which this
503 * series is bound.
504 *
505 * This method uses the axis dimensions that are based on the latest
506 * chart rendering. If you have not yet rendered the chart, or wish
507 * to already the mapping reflect model changes since the last
508 * rendering, you should call WCartesianChart::initLayout() first.
509 *
510 * \sa mapFromDevice()
511 */
512 WPointF mapToDevice(const cpp17::any& xValue, const cpp17::any& yValue,
513 int segment = 0) const;
514
515 /*! \brief Set an offset to draw the data series at.
516 *
517 * The Y position of the data series will be drawn at an offset,
518 * expressed in model coordinates. The axis labels won't follow
519 * the same offset.
520 *
521 * The offset can be manipulated client side using a mouse or touch
522 * drag if WCartesianChart::curveManipulationEnabled() is enabled.
523 *
524 * \note This is only supported for axes with linear scale.
525 *
526 * \sa setScale()
527 * \sa WCartesianChart::setCurveManipulationEnabled()
528 */
529 void setOffset(double offset);
530
531 /*! \brief Get the offset for this data series.
532 *
533 * \sa setOffset()
534 */
offset()535 double offset() const { return offset_; }
536
537
538 /*! \brief Set the scale to draw the data series at.
539 *
540 * The Y position of the data series will be scaled around the zero
541 * position, and offset by offset().
542 *
543 * The scale can be manipulated client side using the scroll wheel
544 * or a pinch motion if WCartesianChart::curveManipulationEnabled()
545 * is enabled.
546 *
547 * \note This is only supported for axes with linear scale.
548 *
549 * \sa setOffset()
550 * \sa WCartesianChart::setCurveManipulationEnabled()
551 */
552 void setScale(double scale);
553
554 /*! \brief Get the scale for this data series.
555 *
556 * \sa setScale()
557 */
scale()558 double scale() const { return scale_; }
559
560 /*! \brief Set a model for this data series.
561 *
562 * If no model is set for this data series, the model of
563 * the chart will be used.
564 *
565 * \note Individual models per data series are only supported for
566 * ChartType::Scatter type charts.
567 *
568 * \sa WCartesianChart::setModel()
569 */
570 void setModel(const std::shared_ptr<WAbstractChartModel>& model);
571
572 /*! \brief Get the model for this data series.
573 *
574 * This will return the model set for this data series,
575 * if it is set.
576 *
577 * If no model is set for this data series, and the series
578 * is associated with a chart, the model of the chart is returned.
579 *
580 * If no model is set for this data series, and the series is
581 * not associated with any data series, this will return null.
582 *
583 * \sa setModel()
584 * \sa WCartesianChart::setModel()
585 */
586 std::shared_ptr<WAbstractChartModel> model() const;
587
chart()588 WCartesianChart *chart() { return chart_; }
589
590 private:
591 WCartesianChart *chart_;
592 std::shared_ptr<WAbstractChartModel> model_;
593 int modelColumn_;
594 int XSeriesColumn_;
595 bool stacked_;
596 SeriesType type_;
597 int xAxis_;
598 int yAxis_;
599 WFlags<CustomFlag> customFlags_;
600 WPen pen_, markerPen_;
601 WBrush brush_, markerBrush_;
602 WColor labelColor_;
603 WShadow shadow_;
604 FillRangeType fillRange_;
605 MarkerType marker_;
606 double markerSize_;
607 bool legend_;
608 bool xLabel_;
609 bool yLabel_;
610 double barWidth_;
611 bool hidden_;
612 WPainterPath customMarker_;
613 double offset_;
614 double scale_;
615 mutable bool offsetDirty_;
616 mutable bool scaleDirty_;
617
618 // connections with the current model, used to disconnect from a model
619 // when the model changes.
620 std::vector<Wt::Signals::connection> modelConnections_;
621
622 void modelReset();
623
624 template <typename T>
625 bool set(T& m, const T& v);
626
627 void setChart(WCartesianChart *chart);
628 void update();
629
630 friend class WCartesianChart;
631 friend class LineSeriesRenderer;
632 };
633
634 template <typename T>
set(T & m,const T & v)635 bool WDataSeries::set(T& m, const T& v)
636 {
637 if (m != v) {
638 m = v;
639 update();
640 return true;
641 } else
642 return false;
643 }
644
645 }
646 }
647
648 #endif // CHART_WDATA_SERIES_H_
649