001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.renderer.style;
017:
018: // J2SE dependencies
019: import java.awt.BasicStroke;
020: import java.awt.Composite;
021: import java.awt.Font;
022: import java.awt.Graphics2D;
023: import java.awt.Paint;
024: import java.awt.Rectangle;
025: import java.awt.Shape;
026: import java.awt.font.GlyphVector;
027: import java.awt.image.BufferedImage;
028: import java.text.Bidi;
029:
030: import org.geotools.resources.Utilities;
031:
032: /**
033: * Style used to represent labels over lines, polygons and points
034: *
035: *
036: * @author Andrea Aime
037: * @author dblasby
038: * @version $Id: TextStyle2D.java 27390 2007-10-09 08:09:54Z aaime $
039: */
040:
041: /** DJB:
042: *
043: * This class was fundamentally wrong - it tried to convert <LinePlacement> into <PointPlacement>.
044: * Not only was it doing a really crappy job, but its fundamentally the wrong place to do it.
045: *
046: * The SLD spec defines a <PointPlacement> as:
047: * <xsd:sequence>
048: * <xsd:element ref="sld:AnchorPoint" minOccurs="0"/>
049: * <xsd:element ref="sld:Displacement" minOccurs="0"/>
050: * <xsd:element ref="sld:Rotation" minOccurs="0"/>
051: * </xsd:sequence>
052: *
053: * and <LinePlacement> as:
054: * <xsd:sequence>
055: * <xsd:element ref="sld:PerpendicularOffset "minOccurs="0"/>
056: * </xsd:sequence>
057: *
058: * its annotated as:
059: * A "PerpendicularOffset" gives the perpendicular distance away from a line to draw a label.
060: * which is a bit vague, but there's a little more details here:
061: *
062: * The PerpendicularOffset element of a LinePlacement gives the perpendicular distance away from a line to draw a label. ...
063: * The distance is in pixels and is positive to the left-hand.
064: *
065: * Left hand/right hand for perpendicularOffset is just crap - I'm assuming them mean +ive --> "up" and -ive --> "down".
066: * See the actual label code for how it deals with this.
067: *
068: * I've removed all the absoluteLineDisplacement stuff and replaced it with
069: * isPointPlacement() (true) --> render normally (PointPlacement Attributes)
070: * isPointPlacement() (false) --> render LinePlacement
071: *
072: * This replaces the old behavior which converted a LinePlacement -> pointplacement and set the absoluteLineDisplacement flag!
073: *
074: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/render/src/main/java/org/geotools/renderer/style/TextStyle2D.java $
075: * */
076:
077: public class TextStyle2D extends Style2D {
078: GlyphVector textGlyphVector;
079: Shape haloShape;
080: String label;
081: Font font;
082: double rotation;
083: /** yes = <PointPlacement> no = <LinePlacement> default = yes**/
084: boolean pointPlacement = true;
085: int perpendicularOffset = 0; // only valid when using a LinePlacement
086: double anchorX;
087: double anchorY;
088: double displacementX;
089: double displacementY;
090: Paint haloFill;
091: Composite haloComposite;
092: float haloRadius;
093: Style2D graphic;
094:
095: /** Holds value of property fill. */
096: private Paint fill;
097:
098: /** Holds value of property composite. */
099: private Composite composite;
100:
101: /**
102: */
103: public double getAnchorX() {
104: return anchorX;
105: }
106:
107: /**
108: */
109: public double getAnchorY() {
110: return anchorY;
111: }
112:
113: /**
114: */
115: public Font getFont() {
116: return font;
117: }
118:
119: /**
120: */
121: public Composite getHaloComposite() {
122: return haloComposite;
123: }
124:
125: /**
126: */
127: public Paint getHaloFill() {
128: return haloFill;
129: }
130:
131: /**
132: */
133: public float getHaloRadius() {
134: return haloRadius;
135: }
136:
137: /**
138: */
139: public double getRotation() {
140: return rotation;
141: }
142:
143: /**
144: * recompute each time
145: */
146: public GlyphVector getTextGlyphVector(Graphics2D graphics) {
147: // arabic and hebrew are scripted and right to left, they do require full layout
148: // whilst western chars are easier to deal with. Find out which case we're dealing with,
149: // and create the glyph vector with the appropriate call
150: final char[] chars = label.toCharArray();
151: final int length = label.length();
152: if (Bidi.requiresBidi(chars, 0, length)
153: && new Bidi(label, Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT)
154: .isRightToLeft())
155: textGlyphVector = font.layoutGlyphVector(graphics
156: .getFontRenderContext(), chars, 0, length,
157: Font.LAYOUT_RIGHT_TO_LEFT);
158: else
159: textGlyphVector = font.createGlyphVector(graphics
160: .getFontRenderContext(), chars);
161: return textGlyphVector;
162: }
163:
164: /**
165: */
166: public Shape getHaloShape(Graphics2D graphics) {
167: GlyphVector gv = getTextGlyphVector(graphics);
168: haloShape = new BasicStroke(2f * haloRadius,
169: BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)
170: .createStrokedShape(gv.getOutline());
171: return haloShape;
172: }
173:
174: /**
175: * @param f
176: */
177: public void setAnchorX(double f) {
178: anchorX = f;
179: }
180:
181: /**
182: * @param f
183: */
184: public void setAnchorY(double f) {
185: anchorY = f;
186: }
187:
188: /**
189: * @param font
190: */
191: public void setFont(Font font) {
192: this .font = font;
193: }
194:
195: /**
196: * @param composite
197: */
198: public void setHaloComposite(Composite composite) {
199: haloComposite = composite;
200: }
201:
202: /**
203: * @param paint
204: */
205: public void setHaloFill(Paint paint) {
206: haloFill = paint;
207: }
208:
209: /**
210: * @param f
211: */
212: public void setHaloRadius(float f) {
213: haloRadius = f;
214: }
215:
216: /**
217: * @param f
218: */
219: public void setRotation(double f) {
220: rotation = f;
221: }
222:
223: /**
224: * @return Returns the label.
225: */
226: public String getLabel() {
227: return label;
228: }
229:
230: /**
231: * @param label The label to set.
232: */
233: public void setLabel(String label) {
234: this .label = label;
235: }
236:
237: /**
238: * @return Returns the pointPlacement (true => point placement, false => line placement)
239: */
240: public boolean isPointPlacement() {
241: return pointPlacement;
242: }
243:
244: /**
245: * @param pointPlacement (true => point placement, false => line placement.)
246: */
247: public void setPointPlacement(boolean pointPlacement) {
248: this .pointPlacement = pointPlacement;
249: }
250:
251: /**
252: * @return Returns the displacementX.
253: */
254: public double getDisplacementX() {
255: return displacementX;
256: }
257:
258: /**
259: * @param displacementX The displacementX to set.
260: */
261: public void setDisplacementX(double displacementX) {
262: this .displacementX = displacementX;
263: }
264:
265: /**
266: * @return Returns the displacementY.
267: */
268: public double getDisplacementY() {
269: return displacementY;
270: }
271:
272: /**
273: * @param displacementY The displacementY to set.
274: */
275: public void setDisplacementY(double displacementY) {
276: this .displacementY = displacementY;
277: }
278:
279: /**
280: * Getter for property fill.
281: *
282: * @return Value of property fill.
283: */
284: public Paint getFill() {
285: return this .fill;
286: }
287:
288: /**
289: * Setter for property fill.
290: *
291: * @param fill New value of property fill.
292: */
293: public void setFill(Paint fill) {
294: this .fill = fill;
295: }
296:
297: /**
298: * only valid for a isPointPlacement=false (ie. a lineplacement)
299: * @param displace in pixels
300: */
301: public void setPerpendicularOffset(int displace) {
302: perpendicularOffset = displace;
303: }
304:
305: /**
306: * only valid for a isPointPlacement=false (ie. a lineplacement)
307: * @return displacement in pixels
308: */
309: public int getPerpendicularOffset() {
310: return perpendicularOffset;
311: }
312:
313: /**
314: * Getter for property composite.
315: *
316: * @return Value of property composite.
317: */
318: public Composite getComposite() {
319: return this .composite;
320: }
321:
322: /**
323: * Setter for property composite.
324: *
325: * @param composite New value of property composite.
326: */
327: public void setComposite(Composite composite) {
328: this .composite = composite;
329: }
330:
331: /**
332: * Returns a string representation of this style.
333: */
334: public String toString() {
335: return Utilities.getShortClassName(this ) + "[\"" + label
336: + "\"]";
337: }
338:
339: /**
340: * Sets the style2D to be drawn underneath this text
341: */
342: public void setGraphic(Style2D s) {
343: graphic = s;
344: }
345:
346: /**
347: * gets the Style2D to be drawn underneath this text
348: */
349: public Style2D getGraphic() {
350: return graphic;
351: }
352:
353: public Rectangle getGraphicDimensions() {
354: if (graphic instanceof MarkStyle2D)
355: return ((MarkStyle2D) graphic).getShape().getBounds();
356: else if (graphic instanceof GraphicStyle2D) {
357: BufferedImage i = ((GraphicStyle2D) graphic).getImage();
358: return new Rectangle(i.getWidth(), i.getHeight());
359: } else {
360: throw new RuntimeException(
361: "Can't render graphic which is not a MarkStyle2D or a GraphicStyle2D");
362: }
363: }
364:
365: }
|