KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jrobin > graph > Grapher


1 /* ============================================================
2  * JRobin : Pure java implementation of RRDTool's functionality
3  * ============================================================
4  *
5  * Project Info: http://www.jrobin.org
6  * Project Lead: Sasa Markovic (saxon@jrobin.org)
7  *
8  * Developers: Sasa Markovic (saxon@jrobin.org)
9  * Arne Vandamme (cobralord@jrobin.org)
10  *
11  * (C) Copyright 2003, by Sasa Markovic.
12  *
13  * This library is free software; you can redistribute it and/or modify it under the terms
14  * of the GNU Lesser General Public License as published by the Free Software Foundation;
15  * either version 2.1 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
18  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19  * See the GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License along with this
22  * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */

25 package org.jrobin.graph;
26
27 import java.io.File JavaDoc;
28 import java.io.IOException JavaDoc;
29 import java.util.*;
30 import javax.imageio.ImageIO JavaDoc;
31 import java.awt.Font JavaDoc;
32 import java.awt.Color JavaDoc;
33 import java.awt.Graphics2D JavaDoc;
34 import java.awt.BasicStroke JavaDoc;
35 import java.awt.RenderingHints JavaDoc;
36 import java.awt.image.RenderedImage JavaDoc;
37 import java.awt.image.BufferedImage JavaDoc;
38
39 import org.jrobin.core.RrdException;
40
41 /**
42  * <p>Creates a BufferedImage of a graph, based on data from a GraphDef.</p>
43  *
44  * @author Arne Vandamme (cobralord@jrobin.org)
45  */

46 class Grapher extends RrdExporter
47 {
48     // ================================================================
49
// -- Members
50
// ================================================================
51
protected static final String JavaDoc SPACER = " "; // default comment spacer (two blank spaces)
52
protected static final int GRAPH_RESOLUTION = 400; // default graph resolution
53
protected static final int DEFAULT_WIDTH = 400; // default width in pixels of the chart area
54
protected static final int DEFAULT_HEIGHT = 100; // default height in pixels of the chart area
55

56     // Border space definitions
57
protected static final int UBORDER_SPACE = 10; // padding from graph upper border
58
protected static final int BBORDER_SPACE = 10; // padding from graph lower border
59
protected static final int LBORDER_SPACE = 10; // padding from graph left border
60
protected static final int RBORDER_SPACE = 13; // padding from graph right border
61

62     protected static final int CHART_UPADDING = 5; // padding space above the chart area
63
protected static final int CHART_BPADDING = 25; // default padding space below the chart area
64
protected static final int CHART_RPADDING = 10; // padding space on the right of the chart area
65
protected static final int CHART_LPADDING = 50; // default padding space on the left of the chart area
66
protected static final int CHART_BPADDING_NM = 10; // default padding below chart if no legend markers
67
protected static final int CHART_LPADDING_NM = 10; // default padding left of chart if no legend markers
68

69     protected static final int LINE_PADDING = 4; // default padding between two consecutive text lines
70

71     // Default fonts
72
protected static final Font JavaDoc TITLE_FONT = new Font JavaDoc("Lucida Sans Typewriter", Font.BOLD, 12);
73     protected static final Font JavaDoc NORMAL_FONT = new Font JavaDoc("Lucida Sans Typewriter", Font.PLAIN, 10);
74     
75     private Font JavaDoc title_font = TITLE_FONT; // font used for the title
76
private Font JavaDoc normal_font = NORMAL_FONT; // font used for all default text
77
private Color JavaDoc normalFontColor = null;
78     private int numPoints = GRAPH_RESOLUTION; // number of points used to calculate the graph
79

80     private int chart_lpadding, chart_bpadding; // calculated padding on the left and below the chart area
81
private int imgWidth, imgHeight; // dimensions of the entire image
82
private int chartWidth, chartHeight; // dimensions of the chart area within the image
83
private int nfont_width, nfont_height, tfont_width, tfont_height; // font dimennsion specs (approximated)
84
private int commentBlock; // size in pixels of the block below the chart itself
85
private int graphOriginX, graphOriginY, x_offset, y_offset;
86     
87     private RrdGraphDef graphDef;
88
89     private PlotDef[] plotDefs;
90     private long[] tsChart;
91
92     private ValueFormatter valueFormat;
93     private BasicStroke JavaDoc defaultStroke;
94     private ValueGrid vGrid;
95     private TimeGrid tGrid;
96
97     // ================================================================
98
// -- Constructors
99
// ================================================================
100
/**
101      * Constructs a grapher object, used for creating a graph image based on a <code>RrdGraphDef</code> object.
102      * A reference to a <code>RrdGraph</code> object is kept for <code>RrdDb</code> pooling.
103      * @param graphDef Graph definition for the graph to be created.
104      * @param rrdGraph RrdGraph object that takes care of saving the images.
105      */

106     Grapher( RrdGraphDef graphDef, RrdGraph rrdGraph )
107     {
108         super( graphDef, rrdGraph );
109         this.graphDef = graphDef;
110         
111         // Set font dimension specifics
112
if ( graphDef.getDefaultFont() != null )
113             normal_font = graphDef.getDefaultFont();
114         if ( graphDef.getTitleFont() != null )
115             title_font = graphDef.getTitleFont();
116         normalFontColor = graphDef.getDefaultFontColor();
117         
118         nfont_height = normal_font.getSize(); // Determine font dimensions for regular comment font
119
nfont_width = nfont_height / 2 + 1;
120         
121         // Bold font is higher
122
tfont_height = ( title_font.isBold() ? title_font.getSize() + 2 : title_font.getSize() );
123         tfont_width = ( title_font.isBold() ? tfont_height / 2 : tfont_height / 2 + 1 );
124         
125         // Create the shared valueformatter
126
valueFormat = new ValueFormatter( graphDef.getBaseValue(), graphDef.getScaleIndex() );
127         
128         // Set default graph stroke
129
defaultStroke = new BasicStroke JavaDoc();
130
131         startTime = graphDef.getStartTime();
132         endTime = graphDef.getStartTime();
133     }
134     
135     
136     // ================================================================
137
// -- Protected (package) methods
138
// ================================================================
139
/**
140      * Calculates the graph and chart dimensions.
141      * @param cWidth Width of the chart area in pixels.
142      * @param cHeight Height of the chart area in pixels.
143      */

144     private void calculateDimensions( int cWidth, int cHeight )
145     {
146         // Calculate chart dimensions
147
chartWidth = ( cWidth == 0 ? DEFAULT_WIDTH : cWidth );
148         chartHeight = ( cHeight == 0 ? DEFAULT_HEIGHT : cHeight );
149
150         if ( cWidth > 0 ) numPoints = cWidth;
151
152         // Padding depends on grid visibility
153
chart_lpadding = ( graphDef.showMajorGridY() ? graphDef.getChartLeftPadding() : CHART_LPADDING_NM );
154         chart_bpadding = ( graphDef.showMajorGridX() ? CHART_BPADDING : CHART_BPADDING_NM );
155
156         // Size of all lines below chart
157
commentBlock = 0;
158         if ( graphDef.showLegend() )
159             commentBlock = graphDef.getCommentLineCount() * (nfont_height + LINE_PADDING) - LINE_PADDING;
160
161         // x_offset and y_offset define the starting corner of the actual graph
162
x_offset = LBORDER_SPACE;
163         if ( graphDef.getVerticalLabel() != null )
164             x_offset += nfont_height + LINE_PADDING;
165         imgWidth = chartWidth + x_offset + RBORDER_SPACE + chart_lpadding + CHART_RPADDING;
166
167         y_offset = UBORDER_SPACE;
168         if ( graphDef.getTitle() != null ) // Title *always* gets a extra LF automatically
169
y_offset += ((tfont_height + LINE_PADDING) * graphDef.getTitle().getLineCount() + tfont_height) + LINE_PADDING;
170         imgHeight = chartHeight + commentBlock + y_offset + BBORDER_SPACE + CHART_UPADDING + CHART_BPADDING;
171     }
172
173     /**
174      * Calculates the graph and chart dimensions.
175      * @param cWidth Width of the entire image in pixels.
176      * @param cHeight Height of the entire image in pixels.
177      */

178     private void calculateDimensionsGlobal( int cWidth, int cHeight )
179     {
180         imgWidth = cWidth;
181         imgHeight = cHeight;
182
183         if ( cWidth > 0 ) numPoints = cWidth;
184
185         // Padding depends on grid visibility
186
chart_lpadding = ( graphDef.showMajorGridY() ? graphDef.getChartLeftPadding() : CHART_LPADDING_NM );
187         chart_bpadding = ( graphDef.showMajorGridX() ? CHART_BPADDING : CHART_BPADDING_NM );
188
189         // Size of all lines below chart
190
commentBlock = 0;
191         if ( graphDef.showLegend() )
192             commentBlock = graphDef.getCommentLineCount() * (nfont_height + LINE_PADDING) - LINE_PADDING;
193
194         // x_offset and y_offset define the starting corner of the actual graph
195
x_offset = LBORDER_SPACE;
196         if ( graphDef.getVerticalLabel() != null )
197             x_offset += nfont_height + LINE_PADDING;
198         chartWidth = imgWidth - x_offset - RBORDER_SPACE - chart_lpadding - CHART_RPADDING;
199
200         y_offset = UBORDER_SPACE;
201         if ( graphDef.getTitle() != null ) // Title *always* gets a extra LF automatically
202
y_offset += ((tfont_height + LINE_PADDING) * graphDef.getTitle().getLineCount() + tfont_height) + LINE_PADDING;
203         chartHeight = imgHeight - commentBlock - y_offset - BBORDER_SPACE - CHART_UPADDING - CHART_BPADDING;
204     }
205
206     /**
207      * Creates the actual graph based on the GraphDef definition.
208      * The graph is created as a <code>java.awt.image.BufferedImage</code>.
209      * @param cWidth Width of the chart area in pixels.
210      * @param cHeight Height of the chart area in pixels.
211      * @return The created graph as a BufferedImage.
212      * @throws RrdException Thrown in case of a JRobin specific error.
213      * @throws IOException Thrown in case of a I/O related error.
214      */

215     protected BufferedImage JavaDoc createImage( int cWidth, int cHeight, int colorType ) throws RrdException, IOException JavaDoc
216     {
217         calculateDimensions( cWidth, cHeight );
218
219         // Create graphics object
220
BufferedImage JavaDoc bImg = new BufferedImage JavaDoc( imgWidth, imgHeight, colorType );
221         Graphics2D JavaDoc graphics = (Graphics2D JavaDoc) bImg.getGraphics();
222
223         render( graphics );
224
225         // Dispose graphics context
226
graphics.dispose();
227
228         return bImg;
229     }
230
231     /**
232      * Creates the actual graph based on the GraphDef definition.
233      * The graph is created as a <code>java.awt.image.BufferedImage</code>.
234      * @param cWidth Width of the entire image in pixels.
235      * @param cHeight Height of the entire image in pixels.
236      * @return The created graph as a BufferedImage.
237      * @throws RrdException Thrown in case of a JRobin specific error.
238      * @throws IOException Thrown in case of a I/O related error.
239      */

240     protected BufferedImage JavaDoc createImageGlobal( int cWidth, int cHeight, int colorType ) throws RrdException, IOException JavaDoc
241     {
242         calculateDimensionsGlobal( cWidth, cHeight );
243
244         // Create graphics object
245
BufferedImage JavaDoc bImg = new BufferedImage JavaDoc( imgWidth, imgHeight, colorType );
246         Graphics2D JavaDoc graphics = (Graphics2D JavaDoc) bImg.getGraphics();
247
248         render( graphics );
249
250         // Dispose graphics context
251
graphics.dispose();
252
253         return bImg;
254     }
255
256     /**
257      * Creates the actual graph based on the GraphDef definition.
258      * The graph is rendered on the Graphics2D object passed as a parameter.
259      * @param cWidth Width of the entire image in pixels.
260      * @param cHeight Height of the entire image in pixels.
261      * @param graphics The handle to the Graphics2D object to render the graph on.
262      * @param useGlobal True if the dimensions specified are those of the entire image.
263      * @throws RrdException Thrown in case of a JRobin specific error.
264      * @throws IOException Thrown in case of a I/O related error.
265      */

266     protected void renderImage( int cWidth, int cHeight, Graphics2D JavaDoc graphics, boolean useGlobal ) throws RrdException, IOException JavaDoc
267     {
268         if ( useGlobal )
269             calculateDimensionsGlobal( cWidth, cHeight );
270         else
271             calculateDimensions( cWidth, cHeight );
272
273         render( graphics );
274     }
275
276     /**
277      * If the lazy flag of the GraphDef has been set, this method will
278      * check the most recent update stamp of the FetchSources, and return
279      * true if the update timestamp is larger than the timestamp of the
280      * previous graph generation (passed on as a parameter).
281      *
282      * @param prevGenTime Timestamp of the previous graph generation.
283      * @return True if the graph should be generated, false if not.
284      */

285     protected boolean shouldGenerate( long prevGenTime ) throws RrdException, IOException JavaDoc
286     {
287         FetchSourceList fetchSources = graphDef.getFetchSources();
288         fetchSources.setRrdOpener( getRrdOpener() );
289
290         fetchSources.openAll();
291
292         if ( graphDef.isLazy() && fetchSources.getLastUpdateTime() * 1000 < prevGenTime )
293         {
294             // Should not generate, release immediately
295
fetchSources.releaseAll();
296
297             return false;
298         }
299
300         return true;
301     }
302
303     // ================================================================
304
// -- Private methods
305
// ================================================================
306
/**
307      * Renders the actual graph onto the specified Graphics2D object
308      * @param graphics The handle to the Graphics2D object to render the graph on.
309      * @throws RrdException Thrown in case of a JRobin specific error.
310      * @throws IOException Thrown in case of a I/O related error.
311      */

312     private void render( Graphics2D JavaDoc graphics ) throws RrdException, IOException JavaDoc
313     {
314         // Do the actual graphing
315
calculateSeries(); // calculate all datasources
316

317         plotImageBackground( graphics ); // draw the image background
318

319         plotChart( graphics ); // draw the actual chart
320

321         plotComments( graphics ); // draw all comment lines
322

323         plotOverlay( graphics ); // draw a possible image overlay
324

325         plotSignature( graphics ); // draw the JRobin signature
326

327         // Dispose graphics context
328
graphics.dispose();
329     }
330
331     /**
332      * Fetches and calculates all datasources used in the graph.
333      *
334      * @throws RrdException Thrown in case of a JRobin specific error.
335      * @throws IOException Thrown in case of a I/O related error.
336      */

337     private void calculateSeries() throws RrdException, IOException JavaDoc
338     {
339         // Calculate the reduced set of datasources
340
super.calculateSeries( chartWidth );
341
342         numPoints = numRows;
343
344         /**
345          * Expand the reduced set back to a set matching the chart width,
346          * this allows for much nicer graphing.
347          */

348         tsChart = new long[ chartWidth ];
349         plotDefs = graphDef.getPlotDefs();
350
351         for ( int i = 0; i < plotDefs.length; i++ )
352         {
353             plotDefs[i].setSource( sources, sourceIndex );
354             plotDefs[i].prepareValues( chartWidth );
355         }
356
357         for ( int i = 0; i < chartWidth; i++ )
358         {
359             long t = (long) (startTime + i * ((endTime - startTime) / (double) (chartWidth - 1)));
360
361             for ( int j = 0; j < plotDefs.length; j++ )
362                 plotDefs[j].setValue( i, t, timestamps );
363
364             tsChart[i] = t;
365         }
366     }
367
368     /**
369      * Draws the image background, title and value axis label.
370      * @param g Handle of a Graphics2D context to draw on.
371      */

372     private void plotImageBackground( Graphics2D JavaDoc g )
373     {
374         // Draw general background color
375
g.setColor( graphDef.getBackColor() );
376         g.fillRect(0, 0, imgWidth, imgHeight );
377     
378         // Draw a background image, if background image fails, just continue
379
try {
380             File JavaDoc bgImage = graphDef.getBackground();
381             if ( bgImage != null ) {
382                 RenderedImage JavaDoc img = ImageIO.read(bgImage);
383                 g.drawRenderedImage( img, null );
384             }
385         } catch (IOException JavaDoc e) {}
386     
387         // Set the image border
388
Color JavaDoc bc = graphDef.getBorderColor();
389         BasicStroke JavaDoc bs = graphDef.getBorderStroke();
390
391         if ( bs != null && bc != null ) // custom single line border
392
{
393             g.setColor( bc );
394             g.setStroke( bs );
395             
396             // Check for 'visible' line width
397
int w = new Float JavaDoc(bs.getLineWidth()).intValue();
398             if ( w > 0 )
399                 g.drawRect( w / 2, w / 2, imgWidth - w, imgHeight - w);
400             
401             g.setStroke( defaultStroke );
402         }
403         else // default slightly beveled border
404
{
405             g.setColor( new Color JavaDoc( 0xdc, 0xdc, 0xdc ) );
406             g.fillRect( 0, 0, 2, imgHeight - 1 );
407             g.fillRect( 0, 0, imgWidth - 1, 2 );
408             g.setColor( Color.GRAY );
409             g.drawLine( 0, imgHeight - 1, imgWidth, imgHeight - 1 );
410             g.drawLine( imgWidth - 1, 0, imgWidth - 1, imgHeight );
411             g.drawLine( 1, imgHeight - 2, imgWidth, imgHeight - 2 );
412             g.drawLine( imgWidth - 2, 1, imgWidth - 2, imgHeight );
413         }
414     
415         plotImageTitle( g );
416         
417         plotVerticalLabel( g );
418     }
419     
420     /**
421      * Plots all datasources on the graph, uses all values gathered in {@link #calculateSeries() }.
422      * @param graphics Handle of a Graphics2D context to draw on.
423      * @throws RrdException Thrown in case of a JRobin specific error.
424      */

425     private void plotChart( Graphics2D JavaDoc graphics ) throws RrdException
426     {
427         int lux = x_offset + chart_lpadding;
428         int luy = y_offset + CHART_UPADDING;
429
430         // Canvas color should only be drawn if no background image is set
431
// If there's a background image, canvas should be transparent
432
if ( graphDef.getBackground() == null ) {
433             graphics.setColor( graphDef.getCanvasColor() );
434             graphics.fillRect( lux, luy, chartWidth, chartHeight );
435         }
436     
437         // Draw the chart area frame
438
graphics.setColor( graphDef.getFrameColor() );
439         graphics.drawRect( lux, luy, chartWidth, chartHeight );
440
441         double val;
442         double[] tmpSeries = new double[numPoints];
443
444         boolean rigid = false;
445         double lowerValue = Double.MAX_VALUE;
446         double upperValue = Double.MIN_VALUE;
447
448         GridRange range = graphDef.getGridRange();
449         if ( range != null )
450         {
451             rigid = range.isRigid();
452             lowerValue = range.getLowerValue();
453             upperValue = range.getUpperValue();
454
455             if ( Double.isNaN(lowerValue) ) lowerValue = Double.MAX_VALUE;
456             if ( Double.isNaN(upperValue) ) upperValue = Double.MIN_VALUE;
457         }
458
459         // For autoscale, detect lower and upper limit of values
460
for ( int i = 0; i < plotDefs.length; i++ )
461         {
462             Source src = plotDefs[i].getSource();
463         
464             // Only try autoscale when we do not have a rigid grid
465
if ( !rigid && src != null )
466             {
467                 double min = src.getAggregate( Source.AGG_MINIMUM );
468                 double max = src.getAggregate( Source.AGG_MAXIMUM );
469
470                 // If the plotdef is a stack, evaluate ALL previous values to find a possible max
471
if ( plotDefs[i].plotType == PlotDef.PLOT_STACK && i >= 1 )
472                 {
473                     if ( plotDefs[i - 1].plotType == PlotDef.PLOT_STACK ) { // Use this source plus stack of previous ones
474

475                         for (int j = 0; j < tmpSeries.length; j++)
476                         {
477                             val = tmpSeries[j] + plotDefs[i].getValue(j, timestamps);
478     
479                             if ( val < lowerValue ) lowerValue = val;
480                             if ( val > upperValue ) upperValue = val;
481     
482                             tmpSeries[j] = val;
483                         }
484                     }
485                     else { // Use this source plus the previous one
486

487                         for (int j = 0; j < tmpSeries.length; j++)
488                         {
489                             val = plotDefs[i - 1].getValue(j, timestamps) + plotDefs[i].getValue(j, timestamps);
490                         
491                             if ( val < lowerValue ) lowerValue = val;
492                             if ( val > upperValue ) upperValue = val;
493                         
494                             tmpSeries[j] = val;
495                         }
496     
497                     }
498                 }
499                 else // Only use min/max of a single datasource
500
{
501                     if ( min < lowerValue ) lowerValue = min;
502                     if ( max > upperValue ) upperValue = max;
503                 }
504             }
505         
506         }
507
508         vGrid = new ValueGrid( range, lowerValue, upperValue, graphDef.getValueAxis(), graphDef.getBaseValue() );
509         tGrid = new TimeGrid( startTime, endTime, graphDef.getTimeAxis(), graphDef.getFirstDayOfWeek() );
510         
511         lowerValue = vGrid.getLowerValue();
512         upperValue = vGrid.getUpperValue();
513
514         // Use a special graph 'object' that takes care of resizing and reversing y coordinates
515
ChartGraphics g = new ChartGraphics( graphics );
516         g.setDimensions( chartWidth, chartHeight );
517         g.setXRange( tGrid.getStartTime(), tGrid.getEndTime() );
518         g.setYRange( lowerValue, upperValue );
519
520         // Set the chart origin point
521
double diff = 1.0d;
522         if ( lowerValue < 0 )
523             diff = 1.0d - ( lowerValue / ( -upperValue + lowerValue ));
524         graphOriginX = lux;
525         graphOriginY = new Double JavaDoc(luy + chartHeight * diff).intValue();
526
527         // If the grid is behind the plots, draw it first
528
if ( !graphDef.isFrontGrid() ) plotChartGrid( g );
529
530         // Use AA if necessary
531
if ( graphDef.useAntiAliasing() )
532             graphics.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
533
534         // Prepare clipping area and origin
535
graphics.setClip( lux, luy, chartWidth, chartHeight);
536         graphics.translate( graphOriginX, graphOriginY );
537  
538         int lastPlotType = PlotDef.PLOT_LINE;
539         double[] parentSeries = new double[tsChart.length];
540
541         // Pre calculate x positions of the corresponding timestamps
542
int[] xValues = new int[tsChart.length];
543         for (int i = 0; i < tsChart.length; i++)
544             xValues[i] = g.getX(tsChart[i]);
545
546         // Draw all graphed values
547
for ( int i = 0; i < plotDefs.length; i++ )
548         {
549             plotDefs[i].draw( g, xValues, parentSeries, lastPlotType );
550             if( plotDefs[i].plotType != PlotDef.PLOT_STACK )
551                 lastPlotType = plotDefs[i].plotType;
552         }
553
554         // Reset clipping area, origin and AA settings
555
graphics.translate( -graphOriginX, -graphOriginY );
556         graphics.setClip( 0, 0, imgWidth, imgHeight);
557         graphics.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
558
559         // If the grid is in front of the plots, draw it now
560
if ( graphDef.isFrontGrid() ) plotChartGrid( g );
561     }
562     
563     /**
564      * Plots the chart grid on the graph, both value and time axis minor and major grid lines.
565      * Accompanied by approriate labels at defined intervals.
566      * @param chartGraph ChartGraphics object containing a Graphics2D handle to draw on.
567      */

568     private void plotChartGrid( ChartGraphics chartGraph )
569     {
570         Graphics2D JavaDoc g = chartGraph.getGraphics();
571         g.setFont( normal_font );
572
573         int lux = x_offset + chart_lpadding;
574         int luy = y_offset + CHART_UPADDING;
575
576         boolean minorX = graphDef.showMinorGridX();
577         boolean minorY = graphDef.showMinorGridY();
578         boolean majorX = graphDef.showMajorGridX();
579         boolean majorY = graphDef.showMajorGridY();
580         
581         Color JavaDoc minColor = graphDef.getMinorGridColor();
582         Color JavaDoc majColor = graphDef.getMajorGridColor();
583         
584         // Dashed line
585
float[] dashPattern = { 1, 1 };
586         BasicStroke JavaDoc dStroke = new BasicStroke JavaDoc(1, BasicStroke.CAP_BUTT,
587                                 BasicStroke.JOIN_MITER, 10,
588                                 dashPattern, 0);
589
590         // Draw basic axis
591
int tmpx = lux + chartWidth;
592         int tmpy = luy + chartHeight;
593
594         // Draw X axis with arrow
595
g.setColor( graphDef.getAxisColor() );
596         g.drawLine( lux - 4, tmpy, tmpx + 4, tmpy );
597         g.setColor( graphDef.getArrowColor() );
598         g.drawLine( tmpx + 4, tmpy - 3, tmpx + 4, tmpy + 3 );
599         g.drawLine( tmpx + 4, tmpy - 3, tmpx + 9, tmpy );
600         g.drawLine( tmpx + 4, tmpy + 3, tmpx + 9, tmpy );
601
602         // Draw X axis time grid and labels
603
if ( graphDef.showGridX() )
604         {
605             TimeMarker[] timeList = tGrid.getTimeMarkers();
606             boolean labelCentered = tGrid.centerLabels();
607             long labelGridWidth = tGrid.getMajorGridWidth();
608             
609             int pixWidth = 0;
610             if ( labelCentered )
611                 pixWidth = ( chartGraph.getX( labelGridWidth ) - chartGraph.getX( 0 ) );
612             
613             for (int i = 0; i < timeList.length; i++)
614             {
615                 long secTime = timeList[i].getTimestamp();
616                 int posRel = chartGraph.getX(secTime);
617                 int pos = lux + posRel;
618                 String JavaDoc label = timeList[i].getLabel();
619                 
620                 if ( posRel >= 0 ) {
621                     if ( majorX && timeList[i].isLabel() )
622                     {
623                         g.setColor( majColor );
624                         g.setStroke( dStroke );
625                         g.drawLine( pos, luy, pos, luy + chartHeight );
626                         g.setStroke( defaultStroke );
627                         g.drawLine( pos, luy - 2, pos, luy + 2 );
628                         g.drawLine( pos, luy + chartHeight - 2, pos, luy + chartHeight + 2 );
629                         // Only draw label itself if we are far enough from the side axis
630
// Use extra 2 pixel padding (3 pixels from border total at least)
631
int txtDistance = (label.length() * nfont_width) / 2;
632                 
633                         if ( labelCentered )
634                         {
635                             if ( pos + pixWidth <= lux + chartWidth )
636                                 graphString( g, label, pos + 2 + pixWidth/2 - txtDistance, luy + chartHeight + nfont_height + LINE_PADDING );
637                         }
638                         else if ( (pos - lux > txtDistance + 2) && (pos + txtDistance + 2 < lux + chartWidth) )
639                             graphString( g, label, pos - txtDistance, luy + chartHeight + nfont_height + LINE_PADDING );
640                     }
641                     else if ( minorX )
642                     {
643                         g.setColor( minColor );
644                         g.setStroke( dStroke );
645                         g.drawLine( pos, luy, pos, luy + chartHeight );
646                         g.setStroke( defaultStroke );
647                         g.drawLine( pos, luy - 1, pos, luy + 1 );
648                         g.drawLine( pos, luy + chartHeight - 1, pos, luy + chartHeight + 1 );
649             
650                     }
651                 }
652             }
653         }
654         
655         // Draw Y axis value grid and labels
656
valueFormat.setScaling( true, false ); // always scale the label values
657
if ( graphDef.showGridY() )
658         {
659             ValueMarker[] valueList = vGrid.getValueMarkers();
660             
661             for (int i = 0; i < valueList.length; i++)
662             {
663                 int valRel = chartGraph.getY( valueList[i].getValue() );
664
665                 valueFormat.setFormat( valueList[i].getValue(), 2, 0 );
666                 String JavaDoc label = (valueFormat.getScaledValue() + " " + valueFormat.getPrefix()).trim();
667     
668                 if ( majorY && valueList[i].isMajor() )
669                 {
670                     g.setColor( majColor );
671                     g.setStroke( dStroke );
672                     g.drawLine( graphOriginX, graphOriginY - valRel, graphOriginX + chartWidth, graphOriginY - valRel );
673                     g.setStroke( defaultStroke );
674                     g.drawLine( graphOriginX - 2, graphOriginY - valRel, graphOriginX + 2, graphOriginY - valRel );
675                     g.drawLine( graphOriginX + chartWidth - 2, graphOriginY - valRel, graphOriginX + chartWidth + 2, graphOriginY - valRel );
676                     graphString( g, label, graphOriginX - (label.length() * nfont_width) - 7, graphOriginY - valRel + nfont_height/2 - 1 );
677                 }
678                 else if ( minorY )
679                 {
680                     g.setColor( minColor );
681                     g.setStroke( dStroke );
682                     g.drawLine( graphOriginX, graphOriginY - valRel, graphOriginX + chartWidth, graphOriginY - valRel );
683                     g.setStroke( defaultStroke );
684                     g.drawLine( graphOriginX - 1, graphOriginY - valRel, graphOriginX + 1, graphOriginY - valRel );
685                     g.drawLine( graphOriginX + chartWidth - 1, graphOriginY - valRel, graphOriginX + chartWidth + 1, graphOriginY - valRel );
686                 }
687
688             }
689         }
690         
691     }
692     
693     /**
694      * Plots all comments and legends on graph.
695      * @param g Handle of a Graphics2D context to draw on.
696      * @throws RrdException Thrown in case of a JRobin specific error.
697      */

698     private void plotComments( Graphics2D JavaDoc g ) throws RrdException
699     {
700         if ( !graphDef.showLegend() ) return;
701         
702         LinkedList markerList = new LinkedList();
703         
704         // Position the cursor just below the chart area
705
int posy = y_offset + chartHeight + CHART_UPADDING + CHART_BPADDING + ( graphDef.showMajorGridX() ? nfont_height : 0 );
706         int posx = LBORDER_SPACE;
707
708         g.setColor( normalFontColor );
709         g.setFont( normal_font );
710         
711         Comment[] clist = graphDef.getComments();
712         StringBuffer JavaDoc tmpStr = new StringBuffer JavaDoc("");
713
714         boolean newLine = false;
715         boolean drawText = false;
716         
717         for (int i = 0; i < clist.length; i++)
718         {
719             if ( clist[i].commentType == Comment.CMT_LEGEND )
720             {
721                 markerList.addLast( new LegendMarker( tmpStr.length() * nfont_width, ((Legend) clist[i]).getColor() ) );
722                 tmpStr.append( " " ); // Add 3 spaces where the mark will be
723
}
724             else if ( clist[i].commentType == Comment.CMT_GPRINT )
725                 ((Gprint) clist[i]).setValue( sources, sourceIndex, valueFormat );
726             
727             ArrayList tknpairs = clist[i].getTokens();
728             
729             for (int j = 0; j < tknpairs.size(); j++)
730             {
731                 String JavaDoc str = (String JavaDoc) tknpairs.get(j++);
732                 Byte JavaDoc tkn = (Byte JavaDoc) tknpairs.get(j);
733                 
734                 if ( clist[i].trimString() )
735                     tmpStr.append( str.trim() );
736                 else
737                     tmpStr.append( str );
738                     
739                 if ( tkn != Comment.TKN_NULL )
740                 {
741                     drawText = true;
742                     if ( tkn == Comment.TKN_ALF ) {
743                         newLine = true;
744                         posx = LBORDER_SPACE;
745                     }
746                     else if ( tkn == Comment.TKN_ARF ) {
747                         newLine = true;
748                         posx = imgWidth - RBORDER_SPACE - (tmpStr.length() * nfont_width);
749                     }
750                     else if ( tkn == Comment.TKN_ACF ) {
751                         newLine = true;
752                         posx = imgWidth / 2 - (tmpStr.length() * nfont_width) / 2;
753                     }
754                     else if ( tkn == Comment.TKN_AL )
755                         posx = LBORDER_SPACE;
756                     else if ( tkn == Comment.TKN_AR )
757                         posx = imgWidth - RBORDER_SPACE - (tmpStr.length() * nfont_width);
758                     else if ( tkn == Comment.TKN_AC )
759                         posx = imgWidth / 2 - (tmpStr.length() * nfont_width) / 2;
760                 }
761                 
762                 if ( !newLine && clist[i].addSpacer() )
763                     tmpStr.append( SPACER );
764                                 
765                 // Plot the string
766
if ( drawText ) {
767                     
768                     graphString( g, tmpStr.toString(), posx, posy );
769                     tmpStr = new StringBuffer JavaDoc("");
770                     drawText = false;
771
772                     // Plot the markers
773
while ( !markerList.isEmpty() ) {
774                         LegendMarker lm = (LegendMarker) markerList.removeFirst();
775                         g.setColor( lm.getColor() );
776                         g.fillRect( posx + lm.getXPosition(), posy - 9, 10, 10 );
777                         g.setColor( normalFontColor );
778                         g.drawRect( posx + lm.getXPosition(), posy - 9, 10, 10 );
779                     }
780                 }
781                 
782                 if ( newLine ) {
783                     posy += nfont_height + LINE_PADDING;
784                     newLine = false;
785                 }
786                 
787             }
788         }
789         
790         if ( tmpStr.length() > 0)
791         {
792             posx = LBORDER_SPACE;
793             graphString( g, tmpStr.toString(), posx, posy );
794             tmpStr = new StringBuffer JavaDoc("");
795             drawText = false;
796
797             // Plot the markers
798
while ( !markerList.isEmpty() ) {
799                 LegendMarker lm = (LegendMarker) markerList.removeFirst();
800                 g.setColor( lm.getColor() );
801                 g.fillRect( posx + lm.getXPosition(), posy - 9, 10, 10 );
802                 g.setColor( normalFontColor );
803                 g.drawRect( posx + lm.getXPosition(), posy - 9, 10, 10 );
804             }
805         }
806     }
807     
808     /**
809      * Plots a possible overlay image over the current graph. All white pixels
810      * are ignored and treated as 100% transparent.
811      * @param g Handle of a Graphics2D context to draw on.
812      */

813     private void plotOverlay( Graphics2D JavaDoc g )
814     {
815         // If overlay drawing fails, just ignore it
816
try
817         {
818             File JavaDoc overlayImg = graphDef.getOverlay();
819             if ( overlayImg != null )
820             {
821                 BufferedImage JavaDoc img = ImageIO.read(overlayImg);
822             
823                 int w = img.getWidth();
824                 int h = img.getHeight();
825                 int rgbWhite = Color.WHITE.getRGB();
826                 int pcolor, red, green, blue;
827
828                 // For better performance we might want to load all color
829
// ints of the overlay in one go
830
for (int i = 0; i < w; i++) {
831                     for (int j = 0; j < h; j++) {
832                         pcolor = img.getRGB(i, j);
833                         if ( pcolor != rgbWhite )
834                         {
835                             red = (pcolor >> 16) & 0xff;
836                             green = (pcolor >> 8) & 0xff;
837                             blue = pcolor & 0xff;
838
839                             g.setColor( new Color JavaDoc(red, green, blue) );
840                             g.drawLine( i, j, i, j );
841                         }
842                     }
843                 }
844             }
845         } catch (IOException JavaDoc e) {}
846     }
847     
848     /**
849      * Plots the graph title in the corresponding title font.
850      * @param g Handle of a Graphics2D context to draw on.
851      */

852     private void plotImageTitle( Graphics2D JavaDoc g )
853     {
854         Title graphTitle = graphDef.getTitle();
855         
856         // No title to draw
857
if ( graphTitle == null )
858             return;
859         
860         // Position the cursor just above the chart area
861
int posy = tfont_height - 1 + UBORDER_SPACE;
862         int posx = LBORDER_SPACE;
863
864         // Set drawing specifics
865
g.setColor( graphDef.getTitleFontColor() );
866         g.setFont( title_font );
867
868         // Parse and align the title text
869
StringBuffer JavaDoc tmpStr = new StringBuffer JavaDoc("");
870         boolean newLine = false;
871
872         ArrayList tknpairs = graphTitle.getTokens();
873         for (int j = 0; j < tknpairs.size(); j++)
874         {
875             String JavaDoc str = (String JavaDoc) tknpairs.get(j++);
876             Byte JavaDoc tkn = (Byte JavaDoc) tknpairs.get(j);
877
878             tmpStr.append( str );
879             if ( tkn != Comment.TKN_NULL )
880             {
881                 if ( tkn == Comment.TKN_ALF ) {
882                     newLine = true;
883                     posx = LBORDER_SPACE;
884                 }
885                 else if ( tkn == Comment.TKN_ARF ) {
886                     newLine = true;
887                     posx = imgWidth - RBORDER_SPACE - (tmpStr.length() * tfont_width) - tfont_width;
888                 }
889                 else if ( tkn == Comment.TKN_ACF ) {
890                     newLine = true;
891                     posx = imgWidth / 2 - (tmpStr.length() * tfont_width) / 2;
892                 }
893                 else if ( tkn == Comment.TKN_AL )
894                     posx = LBORDER_SPACE;
895                 else if ( tkn == Comment.TKN_AR )
896                     posx = imgWidth - RBORDER_SPACE - (tmpStr.length() * tfont_width) - tfont_width;
897                 else if ( tkn == Comment.TKN_AC )
898                     posx = imgWidth / 2 - (tmpStr.length() * tfont_width) / 2;
899             }
900             else { // default is a center alignment for title
901
posx = imgWidth / 2 - (tmpStr.length() * tfont_width) / 2;
902             }
903
904             // Plot the string
905
g.drawString( tmpStr.toString(), posx, posy );
906             tmpStr = new StringBuffer JavaDoc("");
907
908             // Go to next line
909
if ( newLine )
910             {
911                 posy += tfont_height + LINE_PADDING;
912                 newLine = false;
913             }
914         }
915         
916     }
917     
918     /**
919      * Plots the vertical label on the left hand side of the chart area.
920      * @param g Handle of a Graphics2D context to draw on.
921      */

922     private void plotVerticalLabel( Graphics2D JavaDoc g )
923     {
924         String JavaDoc valueAxisLabel = graphDef.getVerticalLabel();
925         
926         if ( valueAxisLabel == null )
927             return;
928         
929         g.setColor( normalFontColor );
930         int labelWidth = valueAxisLabel.length() * nfont_width;
931
932         // draw a rotated label text as vertical label
933
g.setFont( normal_font );
934         g.rotate( -Math.PI/2.0 );
935         graphString( g, valueAxisLabel, - y_offset - CHART_UPADDING
936                                         - chartHeight / 2
937                                         - labelWidth / 2,
938                                         LBORDER_SPACE + nfont_height
939                                         );
940         g.rotate( Math.PI/2.0 );
941     }
942
943     /**
944      * Draws the standard JRobin signature on the image.
945      * @param g Handle of a Graphics2D context to draw on.
946      */

947     private void plotSignature( Graphics2D JavaDoc g )
948     {
949         if ( !graphDef.showSignature() )
950             return;
951         
952         String JavaDoc sig = "www.jrobin.org";
953         g.setColor( Color.GRAY );
954         g.setFont( new Font JavaDoc("Courier", Font.PLAIN, 10) );
955     
956         g.rotate( Math.PI/2.0 );
957         g.drawString( sig, 5, - imgWidth + 9 );
958         g.rotate( -Math.PI/2.0 );
959     }
960
961     /**
962      * Graphs a text string onto a graphics2d context, using the specified default font color.
963      * @param g Handle of a Graphics2D context to draw on.
964      * @param str String to draw.
965      * @param x X start position of the string.
966      * @param y Y start position of the string.
967      */

968     private void graphString( Graphics2D JavaDoc g, String JavaDoc str, int x, int y )
969     {
970         Color JavaDoc oc = g.getColor();
971         
972         g.setColor( normalFontColor );
973         g.drawString( str, x, y );
974         
975         g.setColor( oc );
976     }
977 }
978
Popular Tags