001: //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/graphics/displayelements/RotatedLabel.java $
002: /*---------------- FILE HEADER ------------------------------------------
003:
004: This file is part of deegree.
005: Copyright (C) 2001-2008 by:
006: EXSE, Department of Geography, University of Bonn
007: http://www.giub.uni-bonn.de/deegree/
008: lat/lon GmbH
009: http://www.lat-lon.de
010:
011: This library is free software; you can redistribute it and/or
012: modify it under the terms of the GNU Lesser General Public
013: License as published by the Free Software Foundation; either
014: version 2.1 of the License, or (at your option) any later version.
015:
016: This library is distributed in the hope that it will be useful,
017: but WITHOUT ANY WARRANTY; without even the implied warranty of
018: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: Lesser General Public License for more details.
020:
021: You should have received a copy of the GNU Lesser General Public
022: License along with this library; if not, write to the Free Software
023: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024:
025: Contact:
026:
027: Andreas Poth
028: lat/lon GmbH
029: Aennchenstr. 19
030: 53115 Bonn
031: Germany
032: E-Mail: poth@lat-lon.de
033:
034: Prof. Dr. Klaus Greve
035: Department of Geography
036: University of Bonn
037: Meckenheimer Allee 166
038: 53115 Bonn
039: Germany
040: E-Mail: greve@giub.uni-bonn.de
041:
042: ---------------------------------------------------------------------------*/
043: package org.deegree.graphics.displayelements;
044:
045: import java.awt.BasicStroke;
046: import java.awt.Color;
047: import java.awt.Font;
048: import java.awt.Graphics2D;
049: import java.awt.Rectangle;
050: import java.awt.TexturePaint;
051: import java.awt.font.LineMetrics;
052: import java.awt.geom.AffineTransform;
053: import java.awt.image.BufferedImage;
054:
055: import org.deegree.framework.log.ILogger;
056: import org.deegree.framework.log.LoggerFactory;
057: import org.deegree.graphics.sld.Fill;
058: import org.deegree.graphics.sld.GraphicFill;
059: import org.deegree.graphics.sld.Halo;
060: import org.deegree.model.feature.Feature;
061: import org.deegree.model.filterencoding.FilterEvaluationException;
062:
063: /**
064: * This is a rotated label with style information and screen coordinates, ready to be rendered to
065: * the view.
066: * <p>
067: *
068: * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider</a>
069: * @version $Revision: 9340 $ $Date: 2007-12-27 04:32:12 -0800 (Thu, 27 Dec 2007) $
070: */
071:
072: class RotatedLabel implements Label {
073:
074: private static final ILogger LOG = LoggerFactory
075: .getLogger(RotatedLabel.class);
076:
077: private String caption;
078:
079: private int[] xpoints;
080:
081: private int[] ypoints;
082:
083: private double rotation;
084:
085: private double anchorPoint[];
086:
087: // width and height of the caption
088: private int w;
089:
090: // width and height of the caption
091: private int h;
092:
093: private Color color;
094:
095: private Font font;
096:
097: private int descent;
098:
099: private int ascent;
100:
101: private Halo halo;
102:
103: private Feature feature;
104:
105: /**
106: *
107: * @param caption
108: * @param font
109: * @param color
110: * @param metrics
111: * @param feature
112: * @param halo
113: * @param x
114: * @param y
115: * @param w
116: * @param h
117: * @param rotation
118: * @param anchorPoint
119: * @param displacement
120: */
121: RotatedLabel(String caption, Font font, Color color,
122: LineMetrics metrics, Feature feature, Halo halo, int x,
123: int y, int w, int h, double rotation, double anchorPoint[],
124: double[] displacement) {
125:
126: this .caption = caption;
127: this .font = font;
128: this .color = color;
129: this .descent = (int) metrics.getDescent();
130: this .ascent = (int) metrics.getAscent();
131: this .feature = feature;
132: this .halo = halo;
133: this .rotation = rotation;
134: this .anchorPoint = anchorPoint;
135:
136: this .w = w;
137: this .h = h;
138:
139: // vertices of label boundary
140: int[] xpoints = new int[4];
141: int[] ypoints = new int[4];
142: xpoints[0] = x;
143: ypoints[0] = y;
144: xpoints[1] = x + w;
145: ypoints[1] = y;
146: xpoints[2] = x + w;
147: ypoints[2] = y - h;
148: xpoints[3] = x;
149: ypoints[3] = y - h;
150:
151: // get rotated + translated points
152: this .xpoints = new int[4];
153: this .ypoints = new int[4];
154: int tx = xpoints[0];
155: int ty = ypoints[0];
156:
157: // transform all vertices of the boundary
158: for (int i = 0; i < 4; i++) {
159: int[] point = transformPoint(xpoints[i], ypoints[i], tx,
160: ty, rotation, displacement[0], displacement[1]);
161: this .xpoints[i] = point[0];
162: this .ypoints[i] = point[1];
163: }
164: }
165:
166: /**
167: *
168: * @return caption
169: */
170: public String getCaption() {
171: return caption;
172: }
173:
174: /**
175: *
176: * @return rotation
177: */
178: public double getRotation() {
179: return rotation;
180: }
181:
182: /**
183: *
184: */
185: public void paintBoundaries(Graphics2D g) {
186: setColor(g, new Color(0x888888), 0.5);
187: g.fillPolygon(xpoints, ypoints, xpoints.length);
188: g.setColor(Color.BLACK);
189:
190: // get the current transform
191: AffineTransform saveAT = g.getTransform();
192:
193: // translation parameters (rotation)
194: AffineTransform transform = new AffineTransform();
195:
196: // render the text
197: transform.rotate(rotation / 180d * Math.PI, xpoints[0],
198: ypoints[0]);
199: g.setTransform(transform);
200: // g.drawString( caption, xpoints [0], ypoints [0] - descent);
201:
202: // restore original transform
203: g.setTransform(saveAT);
204: }
205:
206: /**
207: * Renders the label (including halo) to the submitted <tt>Graphics2D</tt> context.
208: * <p>
209: *
210: * @param g
211: * <tt>Graphics2D</tt> context to be used
212: */
213: public void paint(Graphics2D g) {
214:
215: // get the current transform
216: AffineTransform saveAT = g.getTransform();
217:
218: // perform transformation
219: AffineTransform transform = new AffineTransform();
220:
221: transform.rotate(rotation / 180d * Math.PI, xpoints[0],
222: ypoints[0]);
223: g.setTransform(transform);
224:
225: // render the halo (only if specified)
226: if (halo != null) {
227: try {
228: paintHalo(g, halo, (int) (xpoints[0] - w
229: * anchorPoint[0]),
230: (int) (ypoints[0] - descent + h
231: * anchorPoint[1]));
232: } catch (FilterEvaluationException e) {
233: e.printStackTrace();
234: }
235: }
236:
237: // render the text
238: setColor(g, color, 1.0);
239: g.setFont(font);
240: g.drawString(caption, (int) (xpoints[0] - w * anchorPoint[0]),
241: (int) (ypoints[0] - descent + h * anchorPoint[1]));
242:
243: // restore original transform
244: g.setTransform(saveAT);
245: }
246:
247: /**
248: * Renders the label's halo to the submitted <tt>Graphics2D</tt> context.
249: * <p>
250: *
251: * @param g
252: * <tt>Graphics2D</tt> context to be used
253: * @param halo
254: * <tt>Halo</tt> from the SLD
255: * @param x
256: * x-coordinate of the label
257: * @param y
258: * y-coordinate of the label
259: *
260: * @throws FilterEvaluationException
261: * if the evaluation of a <tt>ParameterValueType</tt> fails
262: */
263: private void paintHalo(Graphics2D g, Halo halo, int x, int y)
264: throws FilterEvaluationException {
265:
266: int radius = (int) halo.getRadius(feature);
267:
268: // only draw filled rectangle or circle, if Fill-Element is given
269: Fill fill = halo.getFill();
270:
271: if (fill != null) {
272: GraphicFill gFill = fill.getGraphicFill();
273:
274: if (gFill != null) {
275: BufferedImage texture = gFill.getGraphic().getAsImage(
276: feature);
277: Rectangle anchor = new Rectangle(0, 0, texture
278: .getWidth(null), texture.getHeight(null));
279: g.setPaint(new TexturePaint(texture, anchor));
280: } else {
281: double opacity = fill.getOpacity(feature);
282: Color color = fill.getFill(feature);
283: setColor(g, color, opacity);
284: }
285: } else {
286: g.setColor(Color.white);
287: }
288:
289: // radius specified -> draw circle
290: if (radius > 0) {
291: g.fillOval((x + (w >> 1)) - radius, y - (ascent >> 1)
292: - radius, radius << 1, radius << 1);
293: }
294: // radius unspecified -> draw rectangle
295: else {
296: g.fillRect(x - 1, y - ascent - 1, w + 2, h + 2);
297: }
298:
299: // only stroke outline, if Stroke-Element is given
300: org.deegree.graphics.sld.Stroke stroke = halo.getStroke();
301:
302: if (stroke != null) {
303: double opacity = stroke.getOpacity(feature);
304:
305: if (opacity > 0.01) {
306: Color color = stroke.getStroke(feature);
307: int alpha = (int) Math.round(opacity * 255);
308: int red = color.getRed();
309: int green = color.getGreen();
310: int blue = color.getBlue();
311: color = new Color(red, green, blue, alpha);
312: g.setColor(color);
313:
314: float[] dash = stroke.getDashArray(feature);
315:
316: // use a simple Stroke if dash == null or dash length < 2
317: BasicStroke bs = null;
318: float strokeWidth = (float) stroke.getWidth(feature);
319:
320: if ((dash == null) || (dash.length < 2)) {
321: bs = new BasicStroke(strokeWidth);
322: } else {
323: bs = new BasicStroke(strokeWidth, stroke
324: .getLineCap(feature), stroke
325: .getLineJoin(feature), 10.0f, dash, stroke
326: .getDashOffset(feature));
327: bs = new BasicStroke(strokeWidth, stroke
328: .getLineCap(feature), stroke
329: .getLineJoin(feature), 1.0f, dash, 1.0f);
330: }
331:
332: g.setStroke(bs);
333:
334: // radius specified -> draw circle
335: if (radius > 0) {
336: g.drawOval((x + (w >> 1)) - radius, y
337: - (ascent >> 1) - radius, radius << 1,
338: radius << 1);
339: }// radius unspecified -> draw rectangle
340: else {
341: g.drawRect(x - 1, y - ascent - 1, w + 2, h + 2);
342: }
343: }
344: }
345: }
346:
347: public int getX() {
348: return xpoints[0];
349: }
350:
351: public int getY() {
352: return ypoints[0];
353: }
354:
355: public int getMaxX() {
356: return xpoints[1];
357: }
358:
359: public int getMaxY() {
360: return ypoints[1];
361: }
362:
363: public int getMinX() {
364: return xpoints[3];
365: }
366:
367: public int getMinY() {
368: return ypoints[3];
369: }
370:
371: /**
372: * Determines if the label intersects with another label.
373: * <p>
374: *
375: * @param that
376: * label to test
377: * @return true if the labels intersect
378: */
379: public boolean intersects(Label that) {
380: LOG
381: .logInfo("Intersection test for rotated labels is not implemented yet!");
382: return false;
383: }
384:
385: private int[] transformPoint(int x, int y, int tx, int ty,
386: double rotation, double displacementX, double displacementY) {
387:
388: double cos = Math.cos(rotation);
389: double sin = Math.sin(rotation);
390:
391: double m00 = cos;
392: double m01 = -sin;
393: // double m02 = cos * dx - sin * dy + tx - tx * cos + ty * sin;
394: double m02 = tx - tx * cos + ty * sin;
395: double m10 = sin;
396: double m11 = cos;
397: // double m12 = sin * dx + cos * dy + ty - tx * sin - ty * cos;
398: double m12 = ty - tx * sin - ty * cos;
399:
400: int[] point2 = new int[2];
401:
402: point2[0] = (int) (m00 * x + m01 * y + m02 + 0.5);
403: point2[1] = (int) (m10 * x + m11 * y + m12 + 0.5);
404:
405: return point2;
406: }
407:
408: private Graphics2D setColor(Graphics2D g2, Color color,
409: double opacity) {
410: if (opacity < 0.999) {
411: final int alpha = (int) Math.round(opacity * 255);
412: final int red = color.getRed();
413: final int green = color.getGreen();
414: final int blue = color.getBlue();
415: color = new Color(red, green, blue, alpha);
416: }
417:
418: g2.setColor(color);
419: return g2;
420: }
421:
422: public String toString() {
423: return caption;
424: }
425: }
|