Mercurial > dive4elements > river
comparison flys-artifacts/src/main/java/org/dive4elements/river/exports/TimeseriesChartGenerator.java @ 5831:bd047b71ab37
Repaired internal references
author | Sascha L. Teichmann <teichmann@intevation.de> |
---|---|
date | Thu, 25 Apr 2013 12:06:39 +0200 |
parents | flys-artifacts/src/main/java/de/intevation/flys/exports/TimeseriesChartGenerator.java@7eebd9e58641 |
children |
comparison
equal
deleted
inserted
replaced
5830:160f53ee0870 | 5831:bd047b71ab37 |
---|---|
1 package org.dive4elements.river.exports; | |
2 | |
3 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; | |
4 import org.dive4elements.river.artifacts.resources.Resources; | |
5 import org.dive4elements.river.jfree.Bounds; | |
6 import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation; | |
7 import org.dive4elements.river.jfree.DoubleBounds; | |
8 import org.dive4elements.river.jfree.FLYSAnnotation; | |
9 import org.dive4elements.river.jfree.StyledTimeSeries; | |
10 import org.dive4elements.river.jfree.TimeBounds; | |
11 | |
12 import java.awt.Color; | |
13 import java.awt.Font; | |
14 import java.text.DateFormat; | |
15 import java.text.ParseException; | |
16 import java.util.ArrayList; | |
17 import java.util.Date; | |
18 import java.util.HashMap; | |
19 import java.util.Iterator; | |
20 import java.util.List; | |
21 import java.util.Map; | |
22 | |
23 import javax.swing.ImageIcon; | |
24 | |
25 import org.apache.log4j.Logger; | |
26 import org.jfree.chart.ChartFactory; | |
27 import org.jfree.chart.JFreeChart; | |
28 import org.jfree.chart.LegendItem; | |
29 import org.jfree.chart.LegendItemCollection; | |
30 import org.jfree.chart.annotations.XYAnnotation; | |
31 import org.jfree.chart.annotations.XYImageAnnotation; | |
32 import org.jfree.chart.annotations.XYTextAnnotation; | |
33 import org.jfree.chart.axis.ValueAxis; | |
34 import org.jfree.chart.plot.Marker; | |
35 import org.jfree.chart.plot.XYPlot; | |
36 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; | |
37 import org.jfree.data.Range; | |
38 import org.jfree.data.general.Series; | |
39 import org.jfree.data.time.Day; | |
40 import org.jfree.data.time.TimeSeries; | |
41 import org.jfree.data.time.TimeSeriesCollection; | |
42 import org.jfree.data.xy.XYDataset; | |
43 import org.jfree.ui.Layer; | |
44 import org.json.JSONArray; | |
45 import org.json.JSONException; | |
46 import org.w3c.dom.Document; | |
47 | |
48 /** | |
49 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> | |
50 */ | |
51 public abstract class TimeseriesChartGenerator extends ChartGenerator { | |
52 | |
53 | |
54 /** | |
55 * Inner class TimeseriesAxisDataset stores TimeSeriesCollection. | |
56 */ | |
57 public class TimeseriesAxisDataset implements AxisDataset { | |
58 | |
59 protected int axisSymbol; | |
60 | |
61 protected List<TimeSeriesCollection> datasets; | |
62 | |
63 protected Range range; | |
64 | |
65 protected int plotAxisIndex; | |
66 | |
67 public TimeseriesAxisDataset(int axisSymbol) { | |
68 this.axisSymbol = axisSymbol; | |
69 this.datasets = new ArrayList<TimeSeriesCollection>(); | |
70 } | |
71 | |
72 | |
73 @Override | |
74 public void addDataset(XYDataset dataset) { | |
75 if (!(dataset instanceof TimeSeriesCollection)) { | |
76 logger.warn("Skip non TimeSeriesCollection dataset."); | |
77 return; | |
78 } | |
79 | |
80 TimeSeriesCollection tsc = (TimeSeriesCollection) dataset; | |
81 | |
82 datasets.add(tsc); | |
83 mergeRanges(tsc); | |
84 } | |
85 | |
86 | |
87 @Override | |
88 public XYDataset[] getDatasets() { | |
89 return datasets.toArray(new XYDataset[datasets.size()]); | |
90 } | |
91 | |
92 | |
93 @Override | |
94 public boolean isEmpty() { | |
95 return datasets.isEmpty(); | |
96 } | |
97 | |
98 | |
99 @Override | |
100 public void setRange(Range range) { | |
101 this.range = range; | |
102 } | |
103 | |
104 | |
105 @Override | |
106 public Range getRange() { | |
107 return range; | |
108 } | |
109 | |
110 | |
111 @Override | |
112 public void setPlotAxisIndex(int plotAxisIndex) { | |
113 this.plotAxisIndex = plotAxisIndex; | |
114 } | |
115 | |
116 | |
117 @Override | |
118 public int getPlotAxisIndex() { | |
119 return plotAxisIndex; | |
120 } | |
121 | |
122 | |
123 @Override | |
124 public boolean isArea(XYDataset dataset) { | |
125 logger.warn("This AxisDataset doesn't support Areas yet!"); | |
126 return false; | |
127 } | |
128 | |
129 | |
130 protected void mergeRanges(TimeSeriesCollection dataset) { | |
131 logger.debug("Range before merging: " + range); | |
132 Range subRange = null; | |
133 | |
134 // Determine min/max of range axis. | |
135 for (int i = 0; i < dataset.getSeriesCount(); i++) { | |
136 if (dataset.getSeries(i).getItemCount() == 0) { | |
137 continue; | |
138 } | |
139 double min = Double.MAX_VALUE; | |
140 double max = -Double.MAX_VALUE; | |
141 TimeSeries series = dataset.getSeries(i); | |
142 for (int j = 0; j < series.getItemCount(); j++) { | |
143 double tmp = series.getValue(j).doubleValue(); | |
144 min = tmp < min ? tmp : min; | |
145 max = tmp > max ? tmp : max; | |
146 } | |
147 if (subRange != null) { | |
148 subRange = new Range( | |
149 min < subRange.getLowerBound() ? | |
150 min : subRange.getLowerBound(), | |
151 max > subRange.getUpperBound() ? | |
152 max : subRange.getUpperBound()); | |
153 } | |
154 else { | |
155 subRange = new Range(min, max); | |
156 } | |
157 } | |
158 | |
159 // Avoid merging NaNs, as they take min/max place forever. | |
160 if (subRange == null || | |
161 Double.isNaN(subRange.getLowerBound()) || | |
162 Double.isNaN(subRange.getUpperBound())) { | |
163 return; | |
164 } | |
165 if (range == null) { | |
166 range = subRange; | |
167 return; | |
168 } | |
169 range = Range.combine(range, subRange); | |
170 } | |
171 | |
172 } // end of TimeseriesAxisDataset class | |
173 | |
174 protected List<Marker> domainMarker; | |
175 | |
176 protected List<Marker> valueMarker; | |
177 | |
178 protected Map<String, String> attributes; | |
179 | |
180 protected boolean domainZeroLineVisible; | |
181 | |
182 private static final Logger logger = | |
183 Logger.getLogger(TimeseriesChartGenerator.class); | |
184 | |
185 public static final int AXIS_SPACE = 5; | |
186 | |
187 protected Map<Integer, Bounds> xBounds; | |
188 | |
189 protected Map<Integer, Bounds> yBounds; | |
190 | |
191 | |
192 /** | |
193 * The default constructor that initializes internal datastructures. | |
194 */ | |
195 public TimeseriesChartGenerator() { | |
196 super(); | |
197 | |
198 xBounds = new HashMap<Integer, Bounds>(); | |
199 yBounds = new HashMap<Integer, Bounds>(); | |
200 domainMarker = new ArrayList<Marker>(); | |
201 valueMarker = new ArrayList<Marker>(); | |
202 attributes = new HashMap<String, String>(); | |
203 } | |
204 | |
205 | |
206 | |
207 @Override | |
208 public JFreeChart generateChart() { | |
209 logger.info("Generate Timeseries Chart."); | |
210 | |
211 JFreeChart chart = ChartFactory.createTimeSeriesChart( | |
212 getChartTitle(), | |
213 getXAxisLabel(), | |
214 getYAxisLabel(0), | |
215 null, | |
216 isLegendVisible(), | |
217 false, | |
218 false); | |
219 | |
220 XYPlot plot = (XYPlot) chart.getPlot(); | |
221 | |
222 chart.setBackgroundPaint(Color.WHITE); | |
223 plot.setBackgroundPaint(Color.WHITE); | |
224 | |
225 addSubtitles(chart); | |
226 adjustPlot(plot); | |
227 addDatasets(plot); | |
228 adjustAxes(plot); | |
229 addDomainAxisMarker(plot); | |
230 addValueAxisMarker(plot); | |
231 adaptZoom(plot); | |
232 | |
233 applySeriesAttributes(plot); | |
234 | |
235 addAnnotationsToRenderer(plot); | |
236 addLogo(plot); | |
237 aggregateLegendEntries(plot); | |
238 return chart; | |
239 } | |
240 | |
241 | |
242 /** | |
243 * Return left most data points x value (on first axis). | |
244 * Shortcut, especially to be overridden in (LS) charts where | |
245 * axis could be inverted. | |
246 */ | |
247 protected double getLeftX() { | |
248 return (Long)getXBounds(0).getLower(); | |
249 } | |
250 | |
251 | |
252 /** | |
253 * Return right most data points x value (on first axis). | |
254 * Shortcut, especially to be overridden in (LS) charts where | |
255 * axis could be inverted. | |
256 */ | |
257 protected double getRightX() { | |
258 return (Long)getXBounds(0).getUpper(); | |
259 } | |
260 | |
261 | |
262 /** | |
263 * Add a logo as background annotation to plot. | |
264 * Copy from XYChartGenerator. | |
265 */ | |
266 protected void addLogo(XYPlot plot) { | |
267 String logo = showLogo(); | |
268 if (logo == null) { | |
269 logger.debug("No logo to show chosen"); | |
270 return; | |
271 } | |
272 | |
273 ImageIcon imageIcon = null; | |
274 if (logo.equals("none")) { | |
275 return; | |
276 } | |
277 /* | |
278 If you want to add images, remember to change code in these places: | |
279 flys-artifacts: | |
280 XYChartGenerator.java | |
281 Timeseries*Generator.java and | |
282 in the flys-client projects Chart*Propert*Editor.java. | |
283 Also, these images have to be put in | |
284 flys-artifacts/src/main/resources/images/ | |
285 flys-client/src/main/webapp/images/ | |
286 */ | |
287 java.net.URL imageURL; | |
288 if (logo.equals("Intevation")) { | |
289 imageURL = XYChartGenerator.class.getResource("/images/intevation.png"); | |
290 } | |
291 else { // TODO else if ... | |
292 imageURL = XYChartGenerator.class.getResource("/images/bfg_logo.gif"); | |
293 } | |
294 imageIcon = new ImageIcon(imageURL); | |
295 double xPos = 0d, yPos = 0d; | |
296 | |
297 String placeh = logoHPlace(); | |
298 String placev = logoVPlace(); | |
299 | |
300 if (placev == null || placev.equals("none")) { | |
301 placev = "top"; | |
302 } | |
303 if (placev.equals("top")) { | |
304 yPos = (Double)getYBounds(0).getUpper(); | |
305 } | |
306 else if (placev.equals("bottom")) { | |
307 yPos = (Double)getYBounds(0).getLower(); | |
308 } | |
309 else if (placev.equals("center")) { | |
310 yPos = ((Double)getYBounds(0).getUpper() + (Double)getYBounds(0).getLower())/2d; | |
311 } | |
312 else { | |
313 logger.debug("Unknown place-v value: " + placev); | |
314 } | |
315 | |
316 if (placeh == null || placeh.equals("none")) { | |
317 placeh = "center"; | |
318 } | |
319 if (placeh.equals("left")) { | |
320 xPos = getLeftX(); | |
321 } | |
322 else if (placeh.equals("right")) { | |
323 xPos = getRightX(); | |
324 } | |
325 else if (placeh.equals("center")) { | |
326 xPos = ((Long)getXBounds(0).getUpper() + (Long)getXBounds(0).getLower())/2d; | |
327 } | |
328 else { | |
329 logger.debug("Unknown place-h value: " + placeh); | |
330 } | |
331 | |
332 logger.debug("logo position: " + xPos + "/" + yPos); | |
333 | |
334 org.jfree.ui.RectangleAnchor anchor | |
335 = org.jfree.ui.RectangleAnchor.TOP; | |
336 if (placev.equals("top")) { | |
337 if (placeh.equals("left")) { | |
338 anchor = org.jfree.ui.RectangleAnchor.TOP_LEFT; | |
339 } | |
340 else if (placeh.equals("right")) { | |
341 anchor = org.jfree.ui.RectangleAnchor.TOP_RIGHT; | |
342 } | |
343 else if (placeh.equals("center")) { | |
344 anchor = org.jfree.ui.RectangleAnchor.TOP; | |
345 } | |
346 } | |
347 else if (placev.equals("bottom")) { | |
348 if (placeh.equals("left")) { | |
349 anchor = org.jfree.ui.RectangleAnchor.BOTTOM_LEFT; | |
350 } | |
351 else if (placeh.equals("right")) { | |
352 anchor = org.jfree.ui.RectangleAnchor.BOTTOM_RIGHT; | |
353 } | |
354 else if (placeh.equals("center")) { | |
355 anchor = org.jfree.ui.RectangleAnchor.BOTTOM; | |
356 } | |
357 } | |
358 else if (placev.equals("center")) { | |
359 if (placeh.equals("left")) { | |
360 anchor = org.jfree.ui.RectangleAnchor.LEFT; | |
361 } | |
362 else if (placeh.equals("right")) { | |
363 anchor = org.jfree.ui.RectangleAnchor.RIGHT; | |
364 } | |
365 else if (placeh.equals("center")) { | |
366 anchor = org.jfree.ui.RectangleAnchor.CENTER; | |
367 } | |
368 } | |
369 | |
370 XYAnnotation xyannotation = | |
371 new XYImageAnnotation(xPos, yPos, imageIcon.getImage(), anchor); | |
372 plot.getRenderer().addAnnotation(xyannotation, org.jfree.ui.Layer.BACKGROUND); | |
373 } | |
374 | |
375 | |
376 @Override | |
377 protected Series getSeriesOf(XYDataset dataset, int idx) { | |
378 return ((TimeSeriesCollection) dataset).getSeries(idx); | |
379 } | |
380 | |
381 | |
382 /** | |
383 * This method creates new instances of TimeseriesAxisDataset. | |
384 * | |
385 * @param idx The symbol for the new TimeseriesAxisDataset. | |
386 */ | |
387 @Override | |
388 protected AxisDataset createAxisDataset(int idx) { | |
389 logger.debug("Create a new AxisDataset for index: " + idx); | |
390 return new TimeseriesAxisDataset(idx); | |
391 } | |
392 | |
393 | |
394 @Override | |
395 protected void combineXBounds(Bounds bounds, int index) { | |
396 if (bounds != null) { | |
397 Bounds old = getXBounds(index); | |
398 | |
399 if (old != null) { | |
400 bounds = bounds.combine(old); | |
401 } | |
402 | |
403 setXBounds(index, bounds); | |
404 } | |
405 } | |
406 | |
407 | |
408 @Override | |
409 protected void combineYBounds(Bounds bounds, int index) { | |
410 if (bounds != null) { | |
411 Bounds old = getYBounds(index); | |
412 | |
413 if (old != null) { | |
414 bounds = bounds.combine(old); | |
415 } | |
416 | |
417 setYBounds(index, bounds); | |
418 } | |
419 } | |
420 | |
421 | |
422 // TODO REPLACE THIS METHOD WITH getBoundsForAxis(index) | |
423 @Override | |
424 public Range[] getRangesForAxis(int index) { | |
425 // TODO | |
426 Bounds[] bounds = getBoundsForAxis(index); | |
427 | |
428 return new Range[] { | |
429 new Range( | |
430 bounds[0].getLower().doubleValue(), | |
431 bounds[0].getUpper().doubleValue()), | |
432 new Range( | |
433 bounds[1].getLower().doubleValue(), | |
434 bounds[1].getUpper().doubleValue()) | |
435 }; | |
436 } | |
437 | |
438 | |
439 @Override | |
440 public Bounds getXBounds(int axis) { | |
441 return xBounds.get(axis); | |
442 } | |
443 | |
444 | |
445 @Override | |
446 protected void setXBounds(int axis, Bounds bounds) { | |
447 xBounds.put(axis, bounds); | |
448 } | |
449 | |
450 | |
451 @Override | |
452 public Bounds getYBounds(int axis) { | |
453 return yBounds.get(axis); | |
454 } | |
455 | |
456 | |
457 @Override | |
458 protected void setYBounds(int axis, Bounds bounds) { | |
459 if (bounds != null) { | |
460 yBounds.put(axis, bounds); | |
461 } | |
462 } | |
463 | |
464 | |
465 public Bounds[] getBoundsForAxis(int index) { | |
466 logger.debug("Return x and y bounds for axis at: " + index); | |
467 | |
468 Bounds rx = getXBounds(Integer.valueOf(index)); | |
469 Bounds ry = getYBounds(Integer.valueOf(index)); | |
470 | |
471 if (rx == null) { | |
472 logger.warn("Range for x axis not set." + | |
473 " Using default values: 0 - 1."); | |
474 rx = new TimeBounds(0l, 1l); | |
475 } | |
476 | |
477 if (ry == null) { | |
478 logger.warn("Range for y axis not set." + | |
479 " Using default values: 0 - 1."); | |
480 ry = new DoubleBounds(0l, 1l); | |
481 } | |
482 | |
483 logger.debug("X Bounds at index " + index + " is: " + rx); | |
484 logger.debug("Y Bounds at index " + index + " is: " + ry); | |
485 | |
486 return new Bounds[] {rx, ry}; | |
487 } | |
488 | |
489 | |
490 /** Get (zoom)values from request. */ | |
491 public Bounds getDomainAxisRange() { | |
492 String[] ranges = getDomainAxisRangeFromRequest(); | |
493 | |
494 if (ranges == null || ranges.length < 2) { | |
495 logger.debug("No zoom range for domain axis specified."); | |
496 return null; | |
497 } | |
498 | |
499 if (ranges[0] == null || ranges[1] == null) { | |
500 logger.warn("Invalid ranges for domain axis specified!"); | |
501 return null; | |
502 } | |
503 | |
504 try { | |
505 double lower = Double.parseDouble(ranges[0]); | |
506 double upper = Double.parseDouble(ranges[1]); | |
507 | |
508 return new DoubleBounds(lower, upper); | |
509 } | |
510 catch (NumberFormatException nfe) { | |
511 logger.warn("Invalid ranges for domain axis specified: " + nfe); | |
512 } | |
513 | |
514 return null; | |
515 } | |
516 | |
517 | |
518 public Bounds getValueAxisRange() { | |
519 String[] ranges = getValueAxisRangeFromRequest(); | |
520 | |
521 if (ranges == null || ranges.length < 2) { | |
522 logger.debug("No zoom range for domain axis specified."); | |
523 return null; | |
524 } | |
525 | |
526 if (ranges[0] == null || ranges[1] == null) { | |
527 logger.warn("Invalid ranges for domain axis specified!"); | |
528 return null; | |
529 } | |
530 | |
531 try { | |
532 double lower = Double.parseDouble(ranges[0]); | |
533 double upper = Double.parseDouble(ranges[1]); | |
534 | |
535 return new DoubleBounds(lower, upper); | |
536 } | |
537 catch (NumberFormatException nfe) { | |
538 logger.warn("Invalid ranges for domain axis specified: " + nfe); | |
539 } | |
540 | |
541 return null; | |
542 } | |
543 | |
544 | |
545 protected void adaptZoom(XYPlot plot) { | |
546 logger.debug("Adapt zoom of Timeseries chart."); | |
547 | |
548 zoomX(plot, plot.getDomainAxis(), getXBounds(0), getDomainAxisRange()); | |
549 | |
550 Bounds valueAxisBounds = getValueAxisRange(); | |
551 | |
552 for (int j = 0, n = plot.getRangeAxisCount(); j < n; j++) { | |
553 zoomY( | |
554 plot, | |
555 plot.getRangeAxis(j), | |
556 getYBounds(j), | |
557 valueAxisBounds); | |
558 } | |
559 } | |
560 | |
561 | |
562 /** | |
563 * @param plot the plot. | |
564 * @param axis the value (x, time) axis of which to set bounds. | |
565 * @param total the current bounds (?). | |
566 */ | |
567 protected void zoomX( | |
568 XYPlot plot, | |
569 ValueAxis axis, | |
570 Bounds total,//we could equally nicely getXBounds(0) | |
571 Bounds user | |
572 ) { | |
573 if (logger.isDebugEnabled()) { | |
574 logger.debug("== Zoom X axis =="); | |
575 logger.debug(" Total axis range : " + total); | |
576 logger.debug(" User defined range: " + user); | |
577 } | |
578 | |
579 if (user != null) { | |
580 long min = total.getLower().longValue(); | |
581 long max = total.getUpper().longValue(); | |
582 long diff = max > min ? max - min : min - max; | |
583 | |
584 long newMin = Math.round(min + user.getLower().doubleValue() * diff); | |
585 long newMax = Math.round(min + user.getUpper().doubleValue() * diff); | |
586 | |
587 TimeBounds newBounds = new TimeBounds(newMin, newMax); | |
588 | |
589 logger.debug(" Zoom axis to: " + newBounds); | |
590 | |
591 newBounds.applyBounds(axis, AXIS_SPACE); | |
592 } | |
593 else { | |
594 logger.debug("No user specified zoom values found!"); | |
595 if (total != null && axis != null) { | |
596 total.applyBounds(axis, AXIS_SPACE); | |
597 } | |
598 } | |
599 } | |
600 | |
601 | |
602 /** | |
603 * @param user zoom values in percent. | |
604 */ | |
605 protected void zoomY( | |
606 XYPlot plot, | |
607 ValueAxis axis, | |
608 Bounds total, | |
609 Bounds user | |
610 ) { | |
611 if (logger.isDebugEnabled()) { | |
612 logger.debug("== Zoom Y axis =="); | |
613 logger.debug(" Total axis range : " + total); | |
614 logger.debug(" User defined range: " + user); | |
615 } | |
616 | |
617 if (user != null) { | |
618 double min = total.getLower().doubleValue(); | |
619 double max = total.getUpper().doubleValue(); | |
620 double diff = max > min ? max - min : min - max; | |
621 | |
622 double newMin = min + user.getLower().doubleValue() * diff; | |
623 double newMax = min + user.getUpper().doubleValue() * diff; | |
624 | |
625 DoubleBounds newBounds = new DoubleBounds(newMin, newMax); | |
626 | |
627 logger.debug(" Zoom axis to: " + newBounds); | |
628 | |
629 newBounds.applyBounds(axis, AXIS_SPACE); | |
630 } | |
631 else { | |
632 logger.debug("No user specified zoom values found!"); | |
633 if (total != null && axis != null) { | |
634 total.applyBounds(axis, AXIS_SPACE); | |
635 } | |
636 } | |
637 } | |
638 | |
639 | |
640 /** | |
641 * Adjusts the axes of a plot. This method sets the <i>labelFont</i> of the | |
642 * X axis. | |
643 * | |
644 * (Duplicate in XYChartGenerator). | |
645 * | |
646 * @param plot The XYPlot of the chart. | |
647 */ | |
648 protected void adjustAxes(XYPlot plot) { | |
649 ValueAxis xaxis = plot.getDomainAxis(); | |
650 | |
651 ChartSettings chartSettings = getChartSettings(); | |
652 if (chartSettings == null) { | |
653 return; | |
654 } | |
655 | |
656 Font labelFont = new Font( | |
657 DEFAULT_FONT_NAME, | |
658 Font.BOLD, | |
659 getXAxisLabelFontSize()); | |
660 | |
661 xaxis.setLabelFont(labelFont); | |
662 xaxis.setTickLabelFont(labelFont); | |
663 } | |
664 | |
665 | |
666 protected Date decodeXAxisValue(JSONArray array) throws JSONException, ParseException { | |
667 try { | |
668 double x = array.getDouble(0); | |
669 long l = (new Double(x)).longValue(); | |
670 return new Date(l); | |
671 } | |
672 catch(JSONException ex) { | |
673 String str = array.getString(0); | |
674 DateFormat df = DateFormat.getDateInstance( | |
675 DateFormat.MEDIUM, Resources.getLocale(context.getMeta())); | |
676 return df.parse(str); | |
677 } | |
678 } | |
679 | |
680 /** | |
681 * Do Points out. | |
682 */ | |
683 protected void doPoints( | |
684 Object o, | |
685 ArtifactAndFacet aandf, | |
686 Document theme, | |
687 boolean visible, | |
688 int axisIndex | |
689 ) { | |
690 String seriesName = aandf.getFacetDescription(); | |
691 TimeSeries series = new StyledTimeSeries(seriesName, theme); | |
692 | |
693 // Add text annotations for single points. | |
694 List<XYTextAnnotation> xy = new ArrayList<XYTextAnnotation>(); | |
695 HashMap<Day, String> names = new HashMap<Day, String>(); | |
696 | |
697 try { | |
698 JSONArray points = new JSONArray((String) o); | |
699 for (int i = 0, P = points.length(); i < P; i++) { | |
700 JSONArray array = points.getJSONArray(i); | |
701 | |
702 double y = array.getDouble(1); | |
703 String name = array.getString(2); | |
704 boolean act = array.getBoolean(3); | |
705 if (!act) { | |
706 continue; | |
707 } | |
708 | |
709 Date date = decodeXAxisValue(array); | |
710 | |
711 Day day = new Day(date); | |
712 series.add(day, y, false); | |
713 names.put(day, name); | |
714 } | |
715 } | |
716 catch(JSONException ex) { | |
717 logger.error("Could not decode json"); | |
718 } | |
719 catch(ParseException ex) { | |
720 logger.error("Could not parse date string"); | |
721 } | |
722 | |
723 TimeSeriesCollection tsc = new TimeSeriesCollection(); | |
724 tsc.addSeries(series); | |
725 // Add Annotations. | |
726 for (int i = 0, S = series.getItemCount(); i < S; i++) { | |
727 double x = tsc.getXValue(0, i); | |
728 double y = tsc.getYValue(0, i); | |
729 xy.add(new CollisionFreeXYTextAnnotation( | |
730 names.get(series.getTimePeriod(i)), x, y)); | |
731 logger.debug("doPoints(): x=" + x + " y=" + y); | |
732 } | |
733 FLYSAnnotation annotations = | |
734 new FLYSAnnotation(null, null, null, theme); | |
735 annotations.setTextAnnotations(xy); | |
736 | |
737 // Do not generate second legend entry. (null was passed for the aand before). | |
738 doAnnotations(annotations, null, theme, visible); | |
739 | |
740 addAxisDataset(tsc, axisIndex, visible); | |
741 } | |
742 | |
743 public void addDomainAxisMarker(XYPlot plot) { | |
744 logger.debug("domainmarkers: " + domainMarker.size()); | |
745 for (Marker marker: domainMarker) { | |
746 logger.debug("adding domain marker"); | |
747 plot.addDomainMarker(marker, Layer.BACKGROUND); | |
748 } | |
749 domainMarker.clear(); | |
750 } | |
751 | |
752 public void addValueAxisMarker(XYPlot plot) { | |
753 for (Marker marker: valueMarker) { | |
754 logger.debug("adding value marker.."); | |
755 plot.addRangeMarker(marker, Layer.BACKGROUND); | |
756 } | |
757 valueMarker.clear(); | |
758 } | |
759 | |
760 public void addAttribute(String seriesKey, String name) { | |
761 attributes.put(seriesKey, name); | |
762 } | |
763 | |
764 private LegendItem getLegendItemFor(XYPlot plot, String interSeriesKey) { | |
765 LegendItemCollection litems = plot.getLegendItems(); | |
766 Iterator<LegendItem> iter = litems.iterator(); | |
767 while(iter.hasNext()) { | |
768 LegendItem item = iter.next(); | |
769 if(interSeriesKey.startsWith(item.getSeriesKey().toString())) { | |
770 return item; | |
771 } | |
772 } | |
773 return null; | |
774 } | |
775 | |
776 protected void applySeriesAttributes(XYPlot plot) { | |
777 int count = plot.getDatasetCount(); | |
778 for (int i = 0; i < count; i++) { | |
779 XYDataset data = plot.getDataset(i); | |
780 if (data == null) { | |
781 continue; | |
782 } | |
783 | |
784 int seriesCount = data.getSeriesCount(); | |
785 for (int j = 0; j < seriesCount; j++) { | |
786 StyledTimeSeries series = | |
787 (StyledTimeSeries)getSeriesOf(data, j); | |
788 String key = series.getKey().toString(); | |
789 | |
790 if (attributes.containsKey(key)) { | |
791 // Interpolated points are drawn unfilled | |
792 if (attributes.get(key).equals("interpolate")) { | |
793 XYLineAndShapeRenderer renderer = | |
794 series.getStyle().getRenderer(); | |
795 renderer.setSeriesPaint( | |
796 j, | |
797 renderer.getSeriesFillPaint(j)); | |
798 renderer.setSeriesShapesFilled(j, false); | |
799 | |
800 LegendItem legendItem = getLegendItemFor(plot, key); | |
801 if(legendItem != null) { | |
802 LegendItem interLegend = new LegendItem( | |
803 legendItem.getLabel(), | |
804 legendItem.getDescription(), | |
805 legendItem.getToolTipText(), | |
806 legendItem.getURLText(), | |
807 legendItem.isShapeVisible(), | |
808 legendItem.getShape(), | |
809 false, // shapeFilled? | |
810 legendItem.getFillPaint(), | |
811 true, // shapeOutlineVisible? | |
812 renderer.getSeriesFillPaint(j), | |
813 legendItem.getOutlineStroke(), | |
814 legendItem.isLineVisible(), | |
815 legendItem.getLine(), | |
816 legendItem.getLineStroke(), | |
817 legendItem.getLinePaint() | |
818 ); | |
819 interLegend.setSeriesKey(series.getKey()); | |
820 logger.debug("applySeriesAttributes: draw unfilled legend item"); | |
821 plot.getLegendItems().add(interLegend); | |
822 } | |
823 } | |
824 } | |
825 | |
826 if (attributes.containsKey(key)) { | |
827 if(attributes.get(key).equals("outline")) { | |
828 XYLineAndShapeRenderer renderer = | |
829 series.getStyle().getRenderer(); | |
830 renderer.setSeriesPaint( | |
831 j, | |
832 renderer.getSeriesFillPaint(j)); | |
833 renderer.setDrawOutlines(true); | |
834 } | |
835 } | |
836 } | |
837 } | |
838 } | |
839 | |
840 /** Two Ranges that span a rectangular area. */ | |
841 public static class Area { | |
842 protected Range xRange; | |
843 protected Range yRange; | |
844 | |
845 public Area(Range rangeX, Range rangeY) { | |
846 this.xRange = rangeX; | |
847 this.yRange = rangeY; | |
848 } | |
849 | |
850 public Area(ValueAxis axisX, ValueAxis axisY) { | |
851 this.xRange = axisX.getRange(); | |
852 this.yRange = axisY.getRange(); | |
853 } | |
854 | |
855 public double ofLeft(double percent) { | |
856 return xRange.getLowerBound() | |
857 + xRange.getLength() * percent; | |
858 } | |
859 | |
860 public double ofRight(double percent) { | |
861 return xRange.getUpperBound() | |
862 - xRange.getLength() * percent; | |
863 } | |
864 | |
865 public double ofGround(double percent) { | |
866 return yRange.getLowerBound() | |
867 + yRange.getLength() * percent; | |
868 } | |
869 | |
870 public double atTop() { | |
871 return yRange.getUpperBound(); | |
872 } | |
873 | |
874 public double atGround() { | |
875 return yRange.getLowerBound(); | |
876 } | |
877 | |
878 public double atRight() { | |
879 return xRange.getUpperBound(); | |
880 } | |
881 | |
882 public double atLeft() { | |
883 return xRange.getLowerBound(); | |
884 } | |
885 | |
886 public double above(double percent, double base) { | |
887 return base + yRange.getLength() * percent; | |
888 } | |
889 } | |
890 | |
891 } | |
892 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : |