001: //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/graphics/displayelements/PolygonDisplayElement.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.Rectangle;
052: import java.awt.TexturePaint;
053: import java.awt.geom.AffineTransform;
054: import java.awt.geom.GeneralPath;
055: import java.awt.image.BufferedImage;
056: import java.io.Serializable;
057: import java.util.ArrayList;
058: import java.util.Iterator;
059: import java.util.List;
060:
061: import org.deegree.framework.log.ILogger;
062: import org.deegree.framework.log.LoggerFactory;
063: import org.deegree.graphics.sld.GraphicFill;
064: import org.deegree.graphics.sld.PolygonSymbolizer;
065: import org.deegree.graphics.sld.Symbolizer;
066: import org.deegree.graphics.transformation.GeoTransform;
067: import org.deegree.model.feature.Feature;
068: import org.deegree.model.filterencoding.FilterEvaluationException;
069: import org.deegree.model.spatialschema.Envelope;
070: import org.deegree.model.spatialschema.Geometry;
071: import org.deegree.model.spatialschema.GeometryFactory;
072: import org.deegree.model.spatialschema.MultiPrimitive;
073: import org.deegree.model.spatialschema.MultiSurface;
074: import org.deegree.model.spatialschema.Position;
075: import org.deegree.model.spatialschema.Primitive;
076: import org.deegree.model.spatialschema.Surface;
077: import org.deegree.model.spatialschema.SurfacePatch;
078:
079: /**
080: * {@link DisplayElement} that encapsulates a {@link Surface} or {@link MultiSurface} geometry and a
081: * {@link PolygonSymbolizer}.
082: *
083: * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
084: * @author last edited by: $Author: aschmitz $
085: *
086: * @version $Revision: 10203 $, $Date: 2008-02-20 07:18:40 -0800 (Wed, 20 Feb 2008) $
087: */
088: public class PolygonDisplayElement extends GeometryDisplayElement
089: implements DisplayElement, Serializable {
090:
091: private static final ILogger LOG = LoggerFactory
092: .getLogger(PolygonDisplayElement.class);
093:
094: /** Use serialVersionUID for interoperability. */
095: private final static long serialVersionUID = -2980154437699081214L;
096:
097: private List<int[][]> pathes = new ArrayList<int[][]>(1000);
098:
099: /**
100: * Creates a new PolygonDisplayElement object.
101: *
102: * @param feature
103: * @param geometry
104: */
105: public PolygonDisplayElement(Feature feature, Surface geometry) {
106: super (feature, geometry, null);
107:
108: Symbolizer defaultSymbolizer = new PolygonSymbolizer();
109: this .setSymbolizer(defaultSymbolizer);
110: }
111:
112: /**
113: * Creates a new PolygonDisplayElement object.
114: *
115: * @param feature
116: * @param geometry
117: * @param symbolizer
118: */
119: public PolygonDisplayElement(Feature feature, Surface geometry,
120: PolygonSymbolizer symbolizer) {
121: super (feature, geometry, symbolizer);
122: }
123:
124: /**
125: * Creates a new PolygonDisplayElement object.
126: *
127: * @param feature
128: * @param geometry
129: */
130: public PolygonDisplayElement(Feature feature, MultiSurface geometry) {
131: super (feature, geometry, null);
132:
133: Symbolizer defaultSymbolizer = new PolygonSymbolizer();
134: this .setSymbolizer(defaultSymbolizer);
135: }
136:
137: /**
138: * Creates a new PolygonDisplayElement object.
139: *
140: * @param feature
141: * @param geometry
142: * @param symbolizer
143: */
144: public PolygonDisplayElement(Feature feature,
145: MultiSurface geometry, PolygonSymbolizer symbolizer) {
146: super (feature, geometry, symbolizer);
147: }
148:
149: /**
150: * renders the DisplayElement to the submitted graphic context
151: *
152: * @param g
153: * @param projection
154: * @param scale
155: */
156: public void paint(Graphics g, GeoTransform projection, double scale) {
157: synchronized (symbolizer) {
158: if (feature != null) {
159: ((ScaledFeature) feature).setScale(scale);
160: }
161: try {
162: // a local instance must be used because following statement may
163: // changes the original geometry
164: Geometry geom = geometry;
165: if (geom == null) {
166: LOG.logInfo("null geometry in "
167: + this .getClass().getName());
168: return;
169: }
170: Envelope env = growEnvelope(projection.getSourceRect(),
171: 0.05f);
172: Surface tmp = GeometryFactory.createSurface(env, geom
173: .getCoordinateSystem());
174:
175: if (!geom.intersects(tmp)) {
176: return;
177: }
178: try {
179: Geometry gg = geom.intersection(tmp);
180: if (gg != null) {
181: geom = gg;
182: }
183: } catch (Exception e) {
184: LOG.logWarning(e.getMessage());
185: }
186:
187: if (geom instanceof Surface) {
188: GeneralPath path = calcPolygonPath(projection,
189: (Surface) geom);
190: if (path != null) {
191: drawPolygon(g, path);
192: } else {
193: LOG.logWarning("null path in "
194: + this .getClass().getName());
195: }
196: } else {
197: MultiPrimitive msurface = (MultiPrimitive) geom;
198: for (int i = 0; i < msurface.getSize(); i++) {
199: Primitive prim = msurface.getPrimitiveAt(i);
200: if (prim instanceof Surface) {
201: GeneralPath path = calcPolygonPath(
202: projection, (Surface) prim);
203: if (path != null) {
204: drawPolygon(g, path);
205: } else {
206: LOG.logWarning("null path in "
207: + this .getClass().getName());
208: }
209: } else {
210: System.out.println(prim.getClass()
211: .getName());
212: }
213: }
214: }
215: } catch (FilterEvaluationException e) {
216: LOG
217: .logError(
218: "FilterEvaluationException caught evaluating an Expression!",
219: e);
220: } catch (Exception ex) {
221: LOG.logError(
222: "Exception caught evaluating an Expression!",
223: ex);
224: }
225: }
226: }
227:
228: private double distance(Position p1, Position p2) {
229: double x1 = p1.getX();
230: double y1 = p1.getY();
231: double x2 = p2.getX();
232: double y2 = p2.getY();
233: return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
234: }
235:
236: private GeneralPath calcPolygonPath(GeoTransform projection,
237: Surface surface) throws Exception {
238: GeneralPath path = new GeneralPath();
239:
240: SurfacePatch patch = surface.getSurfacePatchAt(0);
241: if (patch == null)
242: return null;
243: appendRingToPath(path, patch.getExteriorRing(), projection);
244: Position[][] inner = patch.getInteriorRings();
245: if (inner != null) {
246: for (int i = 0; i < inner.length; i++) {
247: appendRingToPath(path, inner[i], projection);
248: }
249: }
250:
251: return path;
252: }
253:
254: private void appendRingToPath(GeneralPath path, Position[] ring,
255: GeoTransform projection) {
256: if (ring.length == 0)
257: return;
258:
259: int[] x = new int[ring.length];
260: int[] y = new int[ring.length];
261: int k = 0;
262:
263: Position p = projection.getDestPoint(ring[0]);
264: Position pp = p;
265: path.moveTo((float) p.getX(), (float) p.getY());
266: for (int i = 1; i < ring.length; i++) {
267: p = projection.getDestPoint(ring[i]);
268: if (distance(p, pp) > 1) {
269: path.lineTo((float) p.getX(), (float) p.getY());
270: pp = p;
271: x[k] = (int) p.getX();
272: y[k++] = (int) p.getY();
273: }
274: }
275: int[][] tmp = new int[3][];
276: tmp[0] = x;
277: tmp[1] = y;
278: tmp[2] = new int[] { k };
279: pathes.add(tmp);
280: }
281:
282: private void drawPolygon(Graphics g, GeneralPath path)
283: throws FilterEvaluationException {
284: Graphics2D g2 = (Graphics2D) g;
285:
286: PolygonSymbolizer sym = (PolygonSymbolizer) symbolizer;
287: org.deegree.graphics.sld.Fill fill = sym.getFill();
288: org.deegree.graphics.sld.Stroke stroke = sym.getStroke();
289:
290: if (fill != null) {
291: double opacity = fill.getOpacity(feature);
292:
293: // is completly transparent
294: // if not fill polygon
295: if (opacity > 0.01) {
296: Color color = fill.getFill(feature);
297: int alpha = (int) Math.round(opacity * 255);
298: int red = color.getRed();
299: int green = color.getGreen();
300: int blue = color.getBlue();
301: color = new Color(red, green, blue, alpha);
302: g2.setColor(color);
303: GraphicFill gFill = fill.getGraphicFill();
304:
305: if (gFill != null) {
306: BufferedImage texture = gFill.getGraphic()
307: .getAsImage(feature);
308: if (texture != null) {
309: Rectangle anchor = new Rectangle(0, 0, texture
310: .getWidth(null), texture
311: .getHeight(null));
312: g2.setPaint(new TexturePaint(texture, anchor));
313: }
314: }
315: try {
316: g2.fill(path);
317: } catch (Exception e) {
318: // why are all exceptions catched here?
319: }
320: }
321: }
322:
323: // only stroke outline, if Stroke-Element is given
324: if (stroke != null) {
325: if (stroke.getOpacity(feature) > 0.001) {
326: // do not paint if feature is completly transparent
327: drawLine(g2, path, stroke);
328: }
329: if (stroke.getGraphicStroke() != null) {
330: try {
331: Image image = stroke.getGraphicStroke()
332: .getGraphic().getAsImage(feature);
333: CurveWalker walker = new CurveWalker(g
334: .getClipBounds());
335:
336: int[][] pos = null;
337: for (int i = 0; i < pathes.size(); i++) {
338: pos = pathes.get(i);
339: ArrayList<double[]> positions = walker
340: .createPositions(pos, image
341: .getWidth(null), true);
342: Iterator<double[]> it = positions.iterator();
343: while (it.hasNext()) {
344: double[] label = it.next();
345: int x = (int) (label[0] + 0.5);
346: int y = (int) (label[1] + 0.5);
347: paintImage(image, g2, x, y, Math
348: .toRadians(label[2]));
349: }
350: }
351: } catch (Exception e) {
352: LOG.logError(e.getMessage(), e);
353: }
354: }
355:
356: }
357: pathes.clear();
358: }
359:
360: /**
361: * Renders a curve to the submitted graphic context.
362: *
363: * TODO: Calculate miterlimit.
364: */
365: private void drawLine(Graphics g, GeneralPath path,
366: org.deegree.graphics.sld.Stroke stroke)
367: throws FilterEvaluationException {
368:
369: // Color & Opacity
370: Graphics2D g2 = (Graphics2D) g;
371: setColor(g2, stroke.getStroke(feature), stroke
372: .getOpacity(feature));
373:
374: float[] dash = stroke.getDashArray(feature);
375:
376: // use a simple Stroke if dash == null or its length < 2
377: // that's faster
378: float width = (float) stroke.getWidth(feature);
379: int cap = stroke.getLineCap(feature);
380: int join = stroke.getLineJoin(feature);
381: BasicStroke bs2 = null;
382:
383: if ((dash == null) || (dash.length < 2)) {
384: bs2 = new BasicStroke(width, cap, join);
385: } else {
386: bs2 = new BasicStroke(width, cap, join, 10.0f, dash, stroke
387: .getDashOffset(feature));
388: }
389:
390: g2.setStroke(bs2);
391: g2.draw(path);
392:
393: }
394:
395: /**
396: *
397: *
398: * @param g2
399: * @param color
400: * @param opacity
401: *
402: * @return the graphics object
403: */
404: private Graphics2D setColor(Graphics2D g2, Color color,
405: double opacity) {
406: if (opacity < 0.999) {
407: // just use a color having an alpha channel if a significant
408: // level of transparency has been defined
409: final int alpha = (int) Math.round(opacity * 255);
410: final int red = color.getRed();
411: final int green = color.getGreen();
412: final int blue = color.getBlue();
413: color = new Color(red, green, blue, alpha);
414: }
415:
416: g2.setColor(color);
417: return g2;
418: }
419:
420: /**
421: *
422: * @param image
423: * @param g
424: * @param x
425: * @param y
426: * @param rotation
427: */
428: private void paintImage(Image image, Graphics2D g, int x, int y,
429: double rotation) {
430:
431: // get the current transform
432: AffineTransform saveAT = g.getTransform();
433:
434: // translation parameters (rotation)
435: AffineTransform transform = new AffineTransform();
436: transform.rotate(rotation, x, y);
437: transform.translate(-image.getWidth(null), -image
438: .getHeight(null) / 2.0);
439: g.setTransform(transform);
440:
441: // render the image
442: g.drawImage(image, x, y, null);
443:
444: // restore original transform
445: g.setTransform(saveAT);
446: }
447: }
|