001: //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/graphics/displayelements/LineStringDisplayElement.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: 53177 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: ---------------------------------------------------------------------------*/
044: package org.deegree.graphics.displayelements;
045:
046: import java.awt.BasicStroke;
047: import java.awt.Color;
048: import java.awt.Graphics;
049: import java.awt.Graphics2D;
050: import java.awt.Image;
051: import java.awt.geom.AffineTransform;
052: import java.io.Serializable;
053: import java.util.ArrayList;
054: import java.util.Iterator;
055:
056: import org.deegree.framework.log.ILogger;
057: import org.deegree.framework.log.LoggerFactory;
058: import org.deegree.graphics.sld.LineSymbolizer;
059: import org.deegree.graphics.sld.Symbolizer;
060: import org.deegree.graphics.transformation.GeoTransform;
061: import org.deegree.model.feature.Feature;
062: import org.deegree.model.filterencoding.FilterEvaluationException;
063: import org.deegree.model.spatialschema.Curve;
064: import org.deegree.model.spatialschema.Envelope;
065: import org.deegree.model.spatialschema.Geometry;
066: import org.deegree.model.spatialschema.GeometryFactory;
067: import org.deegree.model.spatialschema.LineString;
068: import org.deegree.model.spatialschema.MultiCurve;
069: import org.deegree.model.spatialschema.Position;
070: import org.deegree.model.spatialschema.Surface;
071:
072: /**
073: * {@link DisplayElement} that encapsulates a linestring or multi-linestring geometry and a
074: * {@link LineSymbolizer}.
075: * <p>
076: * It can be rendered using a solid stroke or a graphics stroke.
077: *
078: * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
079: * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider</a>
080: * @author last edited by: $Author: apoth $
081: *
082: * @version $Revision: 9340 $, $Date: 2007-12-27 04:32:12 -0800 (Thu, 27 Dec 2007) $
083: */
084: class LineStringDisplayElement extends GeometryDisplayElement implements
085: DisplayElement, Serializable {
086:
087: private static final ILogger LOG = LoggerFactory
088: .getLogger(LineStringDisplayElement.class);
089:
090: /** Use serialVersionUID for interoperability. */
091: private final static long serialVersionUID = -4657962592230618248L;
092:
093: /**
094: * Creates a new {@link LineStringDisplayElement} object.
095: *
096: * @param feature
097: * @param geometry
098: */
099: public LineStringDisplayElement(Feature feature, Curve geometry) {
100: super (feature, geometry, null);
101:
102: Symbolizer defaultSymbolizer = new LineSymbolizer();
103: this .setSymbolizer(defaultSymbolizer);
104: }
105:
106: /**
107: * Creates a new {@link LineStringDisplayElement} object.
108: *
109: * @param feature
110: * @param geometry
111: * @param symbolizer
112: */
113: public LineStringDisplayElement(Feature feature, Curve geometry,
114: LineSymbolizer symbolizer) {
115: super (feature, geometry, symbolizer);
116: }
117:
118: /**
119: * Creates a new {@link LineStringDisplayElement} object.
120: *
121: * @param feature
122: * @param geometry
123: */
124: public LineStringDisplayElement(Feature feature, MultiCurve geometry) {
125: super (feature, geometry, null);
126:
127: Symbolizer defaultSymbolizer = new LineSymbolizer();
128: this .setSymbolizer(defaultSymbolizer);
129: }
130:
131: /**
132: * Creates a new {@link LineStringDisplayElement} object.
133: *
134: * @param feature
135: * @param geometry
136: * @param symbolizer
137: */
138: public LineStringDisplayElement(Feature feature,
139: MultiCurve geometry, LineSymbolizer symbolizer) {
140: super (feature, geometry, symbolizer);
141: }
142:
143: /**
144: * Draws a graphics symbol (image) onto a defined position on the line.
145: *
146: * @param image
147: * @param g
148: * @param x
149: * @param y
150: * @param rotation
151: */
152: private void paintImage(Image image, Graphics2D g, int x, int y,
153: double rotation) {
154:
155: // get the current transform
156: AffineTransform saveAT = g.getTransform();
157:
158: // translation parameters (rotation)
159: AffineTransform transform = new AffineTransform();
160: transform.rotate(rotation, x, y);
161: transform.translate(-image.getWidth(null), -image
162: .getHeight(null) / 2.0);
163: g.setTransform(transform);
164:
165: // render the image
166: g.drawImage(image, x, y, null);
167:
168: // restore original transform
169: g.setTransform(saveAT);
170: }
171:
172: /**
173: * Renders the DisplayElement to the submitted graphic context.
174: *
175: * @param g
176: * @param projection
177: * @param scale
178: */
179: public void paint(Graphics g, GeoTransform projection, double scale) {
180: synchronized (symbolizer) {
181: if (geometry == null) {
182: return;
183: }
184:
185: // a local instance must be used because the following intersection operation may change
186: // the
187: // geometry type
188: Geometry geom = geometry;
189: try {
190: Envelope screenRect = projection.getSourceRect();
191: // grow by 5 percent to cope with partly overlapping stroke graphics
192: screenRect = growEnvelope(screenRect, 0.05f);
193: Surface sur = GeometryFactory.createSurface(screenRect,
194: null);
195: geom = sur.intersection(geometry);
196: } catch (Exception e) {
197: // use original geometry
198: }
199: if (geom == null) {
200: return;
201: }
202:
203: LOG
204: .logDebug("After clipping: "
205: + geom.getClass().getName());
206:
207: ((ScaledFeature) feature).setScale(scale);
208:
209: LineSymbolizer sym = (LineSymbolizer) symbolizer;
210: org.deegree.graphics.sld.Stroke stroke = sym.getStroke();
211:
212: // no stroke defined -> don't draw anything
213: if (stroke == null) {
214: return;
215: }
216:
217: try {
218: if (stroke.getOpacity(feature) > 0.001) {
219: // do not paint if feature is completly transparent
220: int[][] pos = null;
221: Graphics2D g2 = (Graphics2D) g;
222:
223: if (geom instanceof Curve) {
224: pos = calcTargetCoordinates(projection,
225: (Curve) geom);
226: drawLine(g2, pos, stroke);
227: } else {
228: MultiCurve mc = (MultiCurve) geom;
229: for (int i = 0; i < mc.getSize(); i++) {
230: pos = calcTargetCoordinates(projection, mc
231: .getCurveAt(i));
232: drawLine(g2, pos, stroke);
233: }
234: }
235: }
236: } catch (Exception e) {
237: LOG.logError(e.getMessage(), e);
238: }
239:
240: // GraphicStroke label
241: if (stroke.getGraphicStroke() != null) {
242: try {
243: Image image = stroke.getGraphicStroke()
244: .getGraphic().getAsImage(feature);
245: CurveWalker walker = new CurveWalker(g
246: .getClipBounds());
247:
248: if (geom instanceof Curve) {
249: int[][] pos = LabelFactory
250: .calcScreenCoordinates(projection,
251: (Curve) geom);
252: ArrayList<double[]> positions = walker
253: .createPositions(pos, image
254: .getWidth(null), false);
255: Iterator<double[]> it = positions.iterator();
256: while (it.hasNext()) {
257: double[] label = it.next();
258: int x = (int) (label[0] + 0.5);
259: int y = (int) (label[1] + 0.5);
260: paintImage(image, (Graphics2D) g, x, y,
261: Math.toRadians(label[2]));
262: }
263: } else {
264: MultiCurve mc = (MultiCurve) geom;
265: for (int i = 0; i < mc.getSize(); i++) {
266: int[][] pos = LabelFactory
267: .calcScreenCoordinates(projection,
268: mc.getCurveAt(i));
269: ArrayList<double[]> positions = walker
270: .createPositions(pos, image
271: .getWidth(null), false);
272: Iterator<double[]> it = positions
273: .iterator();
274: while (it.hasNext()) {
275: double[] label = it.next();
276: int x = (int) (label[0] + 0.5);
277: int y = (int) (label[1] + 0.5);
278: paintImage(image, (Graphics2D) g, x, y,
279: Math.toRadians(label[2]));
280: }
281: }
282: }
283: } catch (Exception e) {
284: LOG.logError(e.getMessage(), e);
285: }
286: }
287: }
288: }
289:
290: /**
291: * Calculates the screen coordinates of the curve.
292: */
293: private int[][] calcTargetCoordinates(GeoTransform projection,
294: Curve curve) throws Exception {
295: LineString lineString = curve.getAsLineString();
296: int count = lineString.getNumberOfPoints();
297: int[][] pos = new int[3][];
298: pos[0] = new int[count];
299: pos[1] = new int[count];
300: pos[2] = new int[1];
301:
302: int k = 0;
303: for (int i = 0; i < count; i++) {
304: Position position = lineString.getPositionAt(i);
305: double tx = projection.getDestX(position.getX());
306: double ty = projection.getDestY(position.getY());
307:
308: if (i > 0) {
309: if (distance(tx, ty, pos[0][k - 1], pos[1][k - 1]) > 1) {
310: pos[0][k] = (int) (tx + 0.5);
311: pos[1][k] = (int) (ty + 0.5);
312: k++;
313: }
314: } else {
315: pos[0][k] = (int) (tx + 0.5);
316: pos[1][k] = (int) (ty + 0.5);
317: k++;
318: }
319: }
320: pos[2][0] = k;
321:
322: return pos;
323: }
324:
325: /**
326: * Renders a curve to the submitted graphic context.
327: *
328: * TODO: Calculate miterlimit.
329: */
330: private void drawLine(Graphics g, int[][] pos,
331: org.deegree.graphics.sld.Stroke stroke)
332: throws FilterEvaluationException {
333:
334: // Color & Opacity
335: Graphics2D g2 = (Graphics2D) g;
336: setColor(g2, stroke.getStroke(feature), stroke
337: .getOpacity(feature));
338:
339: float[] dash = stroke.getDashArray(feature);
340:
341: // use a simple Stroke if dash == null or its length < 2
342: // that's faster
343: float width = (float) stroke.getWidth(feature);
344: int cap = stroke.getLineCap(feature);
345: int join = stroke.getLineJoin(feature);
346: BasicStroke bs2 = null;
347:
348: if ((dash == null) || (dash.length < 2)) {
349: bs2 = new BasicStroke(width, cap, join);
350: } else {
351: bs2 = new BasicStroke(width, cap, join, 10.0f, dash, stroke
352: .getDashOffset(feature));
353: }
354:
355: g2.setStroke(bs2);
356:
357: g2.drawPolyline(pos[0], pos[1], pos[2][0]);
358:
359: }
360:
361: private double distance(double x1, double y1, double x2, double y2) {
362: return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
363: }
364:
365: private Graphics2D setColor(Graphics2D g2, Color color,
366: double opacity) {
367: if (opacity < 0.999) {
368: // just use a color having an alpha channel if a significant
369: // level of transparency has been defined
370: final int alpha = (int) Math.round(opacity * 255);
371: final int red = color.getRed();
372: final int green = color.getGreen();
373: final int blue = color.getBlue();
374: color = new Color(red, green, blue, alpha);
375: }
376:
377: g2.setColor(color);
378: return g2;
379: }
380: }
|