001: /*
002: * Geotools2 - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2002, 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;
009: * version 2.1 of the License.
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: */
017: package org.geotools.renderer.shape;
018:
019: import org.geotools.renderer.lite.LabelCache;
020: import org.geotools.renderer.style.GraphicStyle2D;
021: import org.geotools.renderer.style.LineStyle2D;
022: import org.geotools.renderer.style.MarkStyle2D;
023: import org.geotools.renderer.style.PolygonStyle2D;
024: import org.geotools.renderer.style.Style2D;
025: import java.awt.AlphaComposite;
026: import java.awt.BasicStroke;
027: import java.awt.Canvas;
028: import java.awt.Graphics2D;
029: import java.awt.Image;
030: import java.awt.Paint;
031: import java.awt.RenderingHints;
032: import java.awt.Shape;
033: import java.awt.Stroke;
034: import java.awt.TexturePaint;
035: import java.awt.geom.AffineTransform;
036: import java.awt.geom.PathIterator;
037: import java.awt.geom.Point2D;
038: import java.awt.geom.Rectangle2D;
039: import java.awt.image.BufferedImage;
040: import java.util.logging.Level;
041: import java.util.logging.Logger;
042:
043: /**
044: * A simple class that knows how to paint a Shape object onto a Graphic given a
045: * Style2D. It's the last step of the rendering engine, and has been factored
046: * out since both renderers do use the same painting logic.
047: *
048: * @author Andrea Aime
049: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/extension/shapefile-renderer/src/main/java/org/geotools/renderer/shape/StyledShapePainter.java $
050: */
051: public class StyledShapePainter {
052: private static AffineTransform IDENTITY_TRANSFORM = new AffineTransform();
053:
054: /** Observer for image loading */
055: private static Canvas imgObserver = new Canvas();
056:
057: /** The logger for the rendering module. */
058: private static final Logger LOGGER = org.geotools.util.logging.Logging
059: .getLogger(StyledShapePainter.class.getName());
060: LabelCache labelCache;
061:
062: /**
063: * Construct <code>StyledShapePainter</code>.
064: *
065: * @param labelCache DOCUMENT ME!
066: */
067: public StyledShapePainter(LabelCache labelCache) {
068: this .labelCache = labelCache;
069: }
070:
071: /**
072: * Invoked automatically when a polyline is about to be draw. This
073: * implementation paints the polyline according to the rendered style
074: *
075: * @param graphics The graphics in which to draw.
076: * @param shape The polygon to draw.
077: * @param style The style to apply, or <code>null</code> if none.
078: * @param scale The scale denominator for the current zoom level
079: */
080: public void paint(final Graphics2D graphics, final Shape shape,
081: final Style2D style, final double scale) {
082: if (style == null) {
083: // TODO: what's going on? Should not be reached...
084: LOGGER
085: .severe("ShapePainter has been asked to paint a null style!!");
086:
087: return;
088: }
089:
090: // Is the current scale within the style scale range?
091: if (!style.isScaleInRange(scale)) {
092: LOGGER.fine("Out of scale");
093:
094: return;
095: }
096:
097: // if(LOGGER.isLoggable(Level.FINE)) {
098: // LOGGER.fine("Graphics transform: " + graphics.getTransform());
099: // }
100: if (style instanceof MarkStyle2D) {
101: PathIterator citer = shape
102: .getPathIterator(IDENTITY_TRANSFORM);
103:
104: // get the point onto the shape has to be painted
105: float[] coords = new float[2];
106: MarkStyle2D ms2d = (MarkStyle2D) style;
107:
108: while (!(citer.isDone())) {
109: citer.currentSegment(coords);
110:
111: Shape transformedShape = ms2d.getTransformedShape(
112: coords[0], coords[1]);
113:
114: if (transformedShape != null) {
115: if (ms2d.getFill() != null) {
116: graphics.setPaint(ms2d.getFill());
117: graphics.setComposite(ms2d.getFillComposite());
118: graphics.fill(transformedShape);
119: }
120:
121: if (ms2d.getContour() != null) {
122: graphics.setPaint(ms2d.getContour());
123: graphics.setStroke(ms2d.getStroke());
124: graphics.setComposite(ms2d
125: .getContourComposite());
126: graphics.draw(transformedShape);
127: }
128:
129: citer.next();
130: }
131: }
132: } else if (style instanceof GraphicStyle2D) {
133: // DJB: TODO: almost certainly you want to do the same here as with the MarkStyle2D (above)
134: // get the point onto the shape has to be painted
135: float[] coords = new float[2];
136: PathIterator iter = shape
137: .getPathIterator(IDENTITY_TRANSFORM);
138: iter.currentSegment(coords);
139:
140: GraphicStyle2D gs2d = (GraphicStyle2D) style;
141:
142: renderImage(graphics, coords[0], coords[1], (Image) gs2d
143: .getImage(), gs2d.getRotation(), gs2d.getOpacity());
144: } else {
145: // if the style is a polygon one, process it even if the polyline is not
146: // closed (by SLD specification)
147: if (style instanceof PolygonStyle2D) {
148: PolygonStyle2D ps2d = (PolygonStyle2D) style;
149:
150: if (ps2d.getFill() != null) {
151: Paint paint = ps2d.getFill();
152:
153: if (paint instanceof TexturePaint) {
154: TexturePaint tp = (TexturePaint) paint;
155: BufferedImage image = tp.getImage();
156: Rectangle2D rect = tp.getAnchorRect();
157: AffineTransform at = graphics.getTransform();
158: double width = rect.getWidth() * at.getScaleX();
159: double height = rect.getHeight()
160: * at.getScaleY();
161: Rectangle2D scaledRect = new Rectangle2D.Double(
162: 0, 0, width, height);
163: paint = new TexturePaint(image, scaledRect);
164: }
165:
166: graphics.setPaint(paint);
167: graphics.setComposite(ps2d.getFillComposite());
168: graphics.fill(shape);
169: }
170: }
171:
172: if (style instanceof LineStyle2D) {
173: LineStyle2D ls2d = (LineStyle2D) style;
174:
175: if (ls2d.getStroke() != null) {
176: // see if a graphic stroke is to be used, the drawing method is completely
177: // different in this case
178: if (ls2d.getGraphicStroke() != null) {
179: drawWithGraphicsStroke(graphics, shape, ls2d
180: .getGraphicStroke());
181: } else {
182: Paint paint = ls2d.getContour();
183:
184: if (paint instanceof TexturePaint) {
185: TexturePaint tp = (TexturePaint) paint;
186: BufferedImage image = tp.getImage();
187: Rectangle2D rect = tp.getAnchorRect();
188: AffineTransform at = graphics
189: .getTransform();
190: double width = rect.getWidth()
191: * at.getScaleX();
192: double height = rect.getHeight()
193: * at.getScaleY();
194: Rectangle2D scaledRect = new Rectangle2D.Double(
195: 0, 0, width, height);
196: paint = new TexturePaint(image, scaledRect);
197: }
198:
199: // debugShape(shape);
200: Stroke stroke = ls2d.getStroke();
201:
202: if (graphics
203: .getRenderingHint(RenderingHints.KEY_ANTIALIASING) == RenderingHints.VALUE_ANTIALIAS_ON) {
204: if (stroke instanceof BasicStroke) {
205: BasicStroke bs = (BasicStroke) stroke;
206: stroke = new BasicStroke(bs
207: .getLineWidth() + 0.5f, bs
208: .getEndCap(), bs.getLineJoin(),
209: bs.getMiterLimit(), bs
210: .getDashArray(), bs
211: .getDashPhase());
212: }
213: }
214:
215: graphics.setPaint(paint);
216: graphics.setStroke(stroke);
217: graphics.setComposite(ls2d
218: .getContourComposite());
219: graphics.draw(shape);
220: }
221: }
222: }
223: }
224: }
225:
226: // draws the image along the path
227: private void drawWithGraphicsStroke(Graphics2D graphics,
228: Shape shape, BufferedImage image) {
229: PathIterator pi = shape.getPathIterator(null, 10.0);
230: double[] coords = new double[4];
231: int type;
232:
233: // I suppose the image has been already scaled and its square
234: int imageSize = image.getWidth();
235:
236: double[] first = new double[2];
237: double[] previous = new double[2];
238: type = pi.currentSegment(coords);
239: first[0] = coords[0];
240: first[1] = coords[1];
241: previous[0] = coords[0];
242: previous[1] = coords[1];
243:
244: if (LOGGER.isLoggable(Level.FINEST)) {
245: LOGGER.finest("starting at " + first[0] + "," + first[1]);
246: }
247:
248: pi.next();
249:
250: while (!pi.isDone()) {
251: type = pi.currentSegment(coords);
252:
253: switch (type) {
254: case PathIterator.SEG_MOVETO:
255:
256: // nothing to do?
257: if (LOGGER.isLoggable(Level.FINEST)) {
258: LOGGER.finest("moving to " + coords[0] + ","
259: + coords[1]);
260: }
261:
262: break;
263:
264: case PathIterator.SEG_CLOSE:
265:
266: // draw back to first from previous
267: coords[0] = first[0];
268: coords[1] = first[1];
269:
270: if (LOGGER.isLoggable(Level.FINEST)) {
271: LOGGER.finest("closing from " + previous[0] + ","
272: + previous[1] + " to " + coords[0] + ","
273: + coords[1]);
274: }
275:
276: // no break here - fall through to next section
277: case PathIterator.SEG_LINETO:
278:
279: // draw from previous to coords
280: if (LOGGER.isLoggable(Level.FINEST)) {
281: LOGGER.finest("drawing from " + previous[0] + ","
282: + previous[1] + " to " + coords[0] + ","
283: + coords[1]);
284: }
285:
286: double dx = coords[0] - previous[0];
287: double dy = coords[1] - previous[1];
288: double len = Math.sqrt((dx * dx) + (dy * dy)); // - imageWidth;
289:
290: double theta = Math.atan2(dx, dy);
291: dx = (Math.sin(theta) * imageSize);
292: dy = (Math.cos(theta) * imageSize);
293:
294: if (LOGGER.isLoggable(Level.FINEST)) {
295: LOGGER.finest("dx = " + dx + " dy " + dy
296: + " step = "
297: + Math.sqrt((dx * dx) + (dy * dy)));
298: }
299:
300: double rotation = -(theta - (Math.PI / 2d));
301: double x = previous[0] + (dx / 2.0);
302: double y = previous[1] + (dy / 2.0);
303:
304: if (LOGGER.isLoggable(Level.FINEST)) {
305: LOGGER.finest("len =" + len + " imageSize "
306: + imageSize);
307: }
308:
309: double dist = 0;
310:
311: for (dist = 0; dist < (len - imageSize); dist += imageSize) {
312: /*graphic.drawImage(image2,(int)x-midx,(int)y-midy,null); */
313: renderImage(graphics, x, y, image, rotation, 1);
314:
315: x += dx;
316: y += dy;
317: }
318:
319: if (LOGGER.isLoggable(Level.FINEST)) {
320: LOGGER.finest("loop end dist " + dist + " len "
321: + len + " " + (len - dist));
322: }
323:
324: double remainder = len - dist;
325: int remainingWidth = (int) remainder;
326:
327: if (remainingWidth > 0) {
328: //clip and render image
329: if (LOGGER.isLoggable(Level.FINEST)) {
330: LOGGER.finest("about to use clipped image "
331: + remainder);
332: }
333:
334: BufferedImage img = new BufferedImage(
335: remainingWidth, imageSize, image.getType());
336: Graphics2D ig = img.createGraphics();
337: ig.drawImage(image, 0, 0, imgObserver);
338:
339: renderImage(graphics, x, y, img, rotation, 1);
340: }
341:
342: break;
343:
344: default:
345: LOGGER
346: .warning("default branch reached in drawWithGraphicStroke");
347: }
348:
349: previous[0] = coords[0];
350: previous[1] = coords[1];
351: pi.next();
352: }
353: }
354:
355: /**
356: * Renders an image on the device
357: *
358: * @param graphics the image location on the screen, x coordinate
359: * @param x the image location on the screen, y coordinate
360: * @param y the image
361: * @param image DOCUMENT ME!
362: * @param rotation the image rotatation
363: * @param opacity DOCUMENT ME!
364: */
365: private void renderImage(Graphics2D graphics, double x, double y,
366: Image image, double rotation, float opacity) {
367: if (LOGGER.isLoggable(Level.FINEST)) {
368: LOGGER.finest("drawing Image @" + x + "," + y);
369: }
370:
371: AffineTransform temp = graphics.getTransform();
372: AffineTransform markAT = new AffineTransform();
373: Point2D mapCentre = new java.awt.geom.Point2D.Double(x, y);
374: Point2D graphicCentre = new java.awt.geom.Point2D.Double();
375: temp.transform(mapCentre, graphicCentre);
376: markAT.translate(graphicCentre.getX(), graphicCentre.getY());
377:
378: double shearY = temp.getShearY();
379: double scaleY = temp.getScaleY();
380:
381: double originalRotation = Math.atan(shearY / scaleY);
382:
383: if (LOGGER.isLoggable(Level.FINER)) {
384: LOGGER.finer("originalRotation " + originalRotation);
385: }
386:
387: markAT.rotate(rotation);
388: graphics.setTransform(markAT);
389: graphics.setComposite(AlphaComposite.getInstance(
390: AlphaComposite.SRC_OVER, opacity));
391:
392: // we moved the origin to the centre of the image.
393: graphics.drawImage(image, -image.getWidth(imgObserver) / 2,
394: -image.getHeight(imgObserver) / 2, imgObserver);
395:
396: graphics.setTransform(temp);
397:
398: return;
399: }
400: }
|