Mercurial > dive4elements > gnv-client
comparison gnv-artifacts/src/main/java/de/intevation/gnv/jfreechart/PolygonRenderer.java @ 875:5e9efdda6894
merged gnv-artifacts/1.0
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:13:56 +0200 |
parents | 22c18083225e |
children | f953c9a559d8 |
comparison
equal
deleted
inserted
replaced
722:bb3ffe7d719e | 875:5e9efdda6894 |
---|---|
1 package de.intevation.gnv.jfreechart; | |
2 | |
3 import java.awt.BasicStroke; | |
4 import java.awt.Color; | |
5 import java.awt.Font; | |
6 import java.awt.FontMetrics; | |
7 import java.awt.Graphics2D; | |
8 import java.awt.Paint; | |
9 import java.awt.Shape; | |
10 | |
11 import java.awt.geom.GeneralPath; | |
12 | |
13 import java.awt.geom.Rectangle2D.Double; | |
14 | |
15 import java.awt.geom.Rectangle2D; | |
16 | |
17 import java.util.ArrayList; | |
18 | |
19 import org.apache.log4j.Logger; | |
20 | |
21 import org.jfree.chart.axis.ValueAxis; | |
22 | |
23 import org.jfree.data.Range; | |
24 | |
25 import org.jfree.text.TextUtilities; | |
26 | |
27 import org.jfree.ui.RectangleEdge; | |
28 | |
29 /** | |
30 * This renderer is used to draw polygons into a Graphics object. | |
31 * | |
32 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> | |
33 * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> | |
34 */ | |
35 public class PolygonRenderer | |
36 { | |
37 private static Logger log = Logger.getLogger( | |
38 PolygonRenderer.class); | |
39 | |
40 /** | |
41 * This interfaces describes a single method to retrieve a Paint object | |
42 * for a given index. | |
43 */ | |
44 public interface PaintLookup { | |
45 | |
46 /** | |
47 * | |
48 * @param index Index. | |
49 * @return Paint | |
50 */ | |
51 Paint getPaint(int index); | |
52 | |
53 } // interface PaintLookup | |
54 | |
55 /** | |
56 * This class is used to generate labels for a given series. | |
57 */ | |
58 public static class DefaultLabelGenerator | |
59 implements PolygonSeriesLabelGenerator | |
60 { | |
61 /** | |
62 * Construts an empty DefaultLabelGenerator. | |
63 */ | |
64 public DefaultLabelGenerator() { | |
65 } | |
66 | |
67 /** | |
68 * | |
69 * @param series A PolygonSeries. | |
70 * @return The label of series. | |
71 */ | |
72 public String generateLabel(PolygonSeries series) { | |
73 Object label = series.getAttribute("label"); | |
74 return label != null | |
75 ? toString(label) | |
76 : null; | |
77 } | |
78 | |
79 /** | |
80 * | |
81 * @param label Object | |
82 * @return String representaton of label. | |
83 */ | |
84 protected String toString(Object label) { | |
85 return label.toString(); | |
86 } | |
87 } // class DefaultLabelGenerator | |
88 | |
89 /** | |
90 * Constructor. | |
91 */ | |
92 public static final PolygonSeriesLabelGenerator | |
93 DEFAULT_LABEL_GENERATOR_INSTANCE = new DefaultLabelGenerator(); | |
94 | |
95 protected PaintLookup lookup; | |
96 | |
97 protected PolygonSeriesLabelGenerator labelGenerator; | |
98 | |
99 | |
100 public PolygonRenderer(PaintLookup lookup) { | |
101 this(lookup, null); | |
102 } | |
103 | |
104 | |
105 public PolygonRenderer( | |
106 PaintLookup lookup, | |
107 PolygonSeriesLabelGenerator labelGenerator | |
108 ) { | |
109 this.lookup = lookup; | |
110 this.labelGenerator = labelGenerator; | |
111 } | |
112 | |
113 /** | |
114 * This method draws polygons of each series in <code>dataset</code> into | |
115 * the given graphics object. If a polygon has no attribute 'fill', we | |
116 * expect that it is a line, otherwise the polygon is filled. | |
117 * | |
118 */ | |
119 public void drawPolygons( | |
120 Graphics2D graphics, | |
121 PolygonPlot plot, | |
122 Rectangle2D area, | |
123 PolygonDataset dataset | |
124 ) { | |
125 int seriesCount = dataset.getSeriesCount(); | |
126 for (int i = 0; i < seriesCount; i++) { | |
127 PolygonSeries series = dataset.getSeries(i); | |
128 Integer colorIdx = (Integer)series.getAttribute("fill"); | |
129 | |
130 if (colorIdx != null) { | |
131 Paint paint = lookup.getPaint(colorIdx.intValue()); | |
132 graphics.setPaint(paint != null ? paint : Color.black); | |
133 graphics.fill(constructShape(plot, area, series, true)); | |
134 } | |
135 else { | |
136 Number lineWidth = (Number)series.getAttribute("line.width"); | |
137 BasicStroke stroke = new BasicStroke( | |
138 lineWidth != null ? lineWidth.floatValue() : 1f); | |
139 graphics.setStroke(stroke); | |
140 graphics.setPaint(Color.black); | |
141 graphics.draw(constructShape(plot, area, series, false)); | |
142 } | |
143 } | |
144 } | |
145 | |
146 /** | |
147 * Draw labels at each item of a series in the given dataset. If the series | |
148 * has no label attritue, no label is drawn. | |
149 * | |
150 */ | |
151 public void drawLabels( | |
152 final Graphics2D graphics, | |
153 final PolygonPlot plot, | |
154 final Rectangle2D area, | |
155 PolygonDataset dataset | |
156 ) { | |
157 if (labelGenerator == null) { | |
158 return; | |
159 } | |
160 | |
161 final ArrayList<Rectangle2D> bboxes = new ArrayList<Rectangle2D>(); | |
162 | |
163 Font font = graphics.getFont(); | |
164 font = font.deriveFont(Font.PLAIN, Math.max(8, font.getSize()-3)); | |
165 graphics.setFont(font); | |
166 FontMetrics metrics = graphics.getFontMetrics(font); | |
167 | |
168 for (int i = dataset.getSeriesCount()-1; i >= 0; --i) { | |
169 PolygonSeries series = dataset.getSeries(i); | |
170 | |
171 String label = labelGenerator.generateLabel(series); | |
172 if (label == null) { | |
173 continue; | |
174 } | |
175 | |
176 final Rectangle2D box = TextUtilities.getTextBounds( | |
177 label, graphics, metrics); | |
178 | |
179 for (int j = series.getItemCount()-1; j >= 0; --j) { | |
180 final CompactXYItems ring = series.getItem(j); | |
181 LevelOrderIndices loi = new LevelOrderIndices(ring.size()-1); | |
182 Rectangle2D r = (Rectangle2D)loi.visit( | |
183 new LevelOrderIndices.Visitor() | |
184 { | |
185 ValueAxis da = plot.getDomainAxis(); | |
186 ValueAxis ra = plot.getRangeAxis(); | |
187 RectangleEdge de = plot.getDomainAxisEdge(); | |
188 RectangleEdge re = plot.getRangeAxisEdge(); | |
189 Rectangle2D.Double r = new Rectangle2D.Double( | |
190 0d, 0d, box.getWidth(), box.getHeight()); | |
191 | |
192 public Object visit(int index) { | |
193 r.x = da.valueToJava2D(ring.getX(index), area, de) | |
194 - 0.5*box.getWidth(); | |
195 r.y = ra.valueToJava2D(ring.getY(index), area, re) | |
196 + 0.5*box.getHeight(); | |
197 | |
198 for (Rectangle2D b: bboxes) { | |
199 if (b.intersects(r)) { | |
200 return null; | |
201 } | |
202 } | |
203 return r; | |
204 } | |
205 }); | |
206 | |
207 if (r != null) { | |
208 bboxes.add(r); | |
209 graphics.drawString( | |
210 label, (float)r.getX(), (float)r.getY()); | |
211 } | |
212 } // for all items in series | |
213 } // for all series | |
214 } | |
215 | |
216 /** | |
217 * Creates a shape made up of the CompactXYItems object stored in the given | |
218 * series. | |
219 * | |
220 * @param plot The plot. | |
221 * @param area The boundary. | |
222 * @param series The series storing the items. | |
223 * @param close Specifies if the polygon should be closed or not. | |
224 * @return the constructed shape. | |
225 */ | |
226 protected Shape constructShape( | |
227 PolygonPlot plot, | |
228 Rectangle2D area, | |
229 PolygonSeries series, | |
230 boolean close | |
231 ) { | |
232 ValueAxis da = plot.getDomainAxis(); | |
233 ValueAxis ra = plot.getRangeAxis(); | |
234 RectangleEdge de = plot.getDomainAxisEdge(); | |
235 RectangleEdge re = plot.getRangeAxisEdge(); | |
236 | |
237 CompactXYItems [] rings = series.getRings(); | |
238 GeneralPath path = new GeneralPath(); | |
239 | |
240 for (int i = 0; i < rings.length; ++i) { | |
241 | |
242 CompactXYItems ring = rings[i]; | |
243 | |
244 double [] data = ring.getData(); | |
245 | |
246 if (data.length >= 2) { | |
247 path.moveTo( | |
248 (float)da.valueToJava2D(data[0], area, de), | |
249 (float)ra.valueToJava2D(data[1], area, re)); | |
250 } | |
251 for (int j = 2; j < data.length;) { | |
252 path.lineTo( | |
253 (float)da.valueToJava2D(data[j++], area, de), | |
254 (float)ra.valueToJava2D(data[j++], area, re)); | |
255 } | |
256 if (close) { | |
257 path.closePath(); | |
258 } | |
259 } | |
260 return path; | |
261 } | |
262 | |
263 /** | |
264 * Retrieves the bounding box of a dataset. | |
265 */ | |
266 public Rectangle2D getBoundingBox(PolygonDataset dataset) { | |
267 Rectangle2D bbox = null; | |
268 | |
269 for (int i = 0, N = dataset.getSeriesCount(); i < N; i++) { | |
270 Range domain = dataset.getSeries(i).getDomainBounds(); | |
271 Range range = dataset.getSeries(i).getRangeBounds(); | |
272 | |
273 double x = domain.getLowerBound(); | |
274 double y = range.getLowerBound(); | |
275 double w = Math.abs(domain.getUpperBound() - x); | |
276 double h = Math.abs(range.getUpperBound() - y); | |
277 | |
278 if (bbox == null) { | |
279 bbox = new Rectangle2D.Double(x, y, w, h); | |
280 } | |
281 else { | |
282 bbox.add(new Rectangle2D.Double(x, y, w, h)); | |
283 } | |
284 } | |
285 | |
286 return bbox; | |
287 } | |
288 | |
289 /** | |
290 * | |
291 * @return the bounds of a series. | |
292 */ | |
293 public Rectangle2D getBounds(PolygonSeries series) { | |
294 | |
295 Range domain = series.getDomainBounds(); | |
296 Range range = series.getRangeBounds(); | |
297 | |
298 return new Rectangle2D.Double( | |
299 domain.getLowerBound(), range.getLowerBound(), | |
300 domain.getUpperBound(), range.getUpperBound() | |
301 ); | |
302 } | |
303 } | |
304 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 : |