001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/graphics/displayelements/DisplayElementFactory.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 ---------------------------------------------------------------------------*/
044 package org.deegree.graphics.displayelements;
045
046 import java.lang.reflect.Constructor;
047 import java.lang.reflect.InvocationTargetException;
048 import java.util.ArrayList;
049 import java.util.List;
050
051 import org.deegree.framework.log.ILogger;
052 import org.deegree.framework.log.LoggerFactory;
053 import org.deegree.graphics.sld.FeatureTypeStyle;
054 import org.deegree.graphics.sld.Geometry;
055 import org.deegree.graphics.sld.LineSymbolizer;
056 import org.deegree.graphics.sld.PointSymbolizer;
057 import org.deegree.graphics.sld.PolygonSymbolizer;
058 import org.deegree.graphics.sld.RasterSymbolizer;
059 import org.deegree.graphics.sld.Rule;
060 import org.deegree.graphics.sld.Symbolizer;
061 import org.deegree.graphics.sld.TextSymbolizer;
062 import org.deegree.graphics.sld.UserStyle;
063 import org.deegree.io.datastore.PropertyPathResolvingException;
064 import org.deegree.model.coverage.grid.GridCoverage;
065 import org.deegree.model.feature.Feature;
066 import org.deegree.model.feature.FeatureProperty;
067 import org.deegree.model.filterencoding.Filter;
068 import org.deegree.model.filterencoding.FilterEvaluationException;
069 import org.deegree.model.spatialschema.Curve;
070 import org.deegree.model.spatialschema.GeometryException;
071 import org.deegree.model.spatialschema.GeometryFactory;
072 import org.deegree.model.spatialschema.MultiCurve;
073 import org.deegree.model.spatialschema.MultiPoint;
074 import org.deegree.model.spatialschema.MultiPrimitive;
075 import org.deegree.model.spatialschema.MultiSurface;
076 import org.deegree.model.spatialschema.Point;
077 import org.deegree.model.spatialschema.Position;
078 import org.deegree.model.spatialschema.Surface;
079
080 /**
081 * Factory class for the different kinds of {@link DisplayElement}s.
082 *
083 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
084 * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider</a>
085 * @author last edited by: $Author: apoth $
086 *
087 * @version $Revision: 9340 $, $Date: 2007-12-27 13:32:12 +0100 (Do, 27 Dez 2007) $
088 */
089 public class DisplayElementFactory {
090
091 private static final ILogger LOG = LoggerFactory.getLogger( DisplayElementFactory.class );
092
093 /**
094 * Returns the display elements for a {@link Feature} or {@link GridCoverage}.
095 *
096 * @param o
097 * @param styles
098 * @return the display elements
099 * @throws ClassNotFoundException
100 * @throws IllegalAccessException
101 * @throws InstantiationException
102 * @throws NoSuchMethodException
103 * @throws InvocationTargetException
104 * @throws GeometryException
105 *
106 * @throws PropertyPathResolvingException
107 */
108 public DisplayElement[] createDisplayElement( Object o, UserStyle[] styles )
109 throws ClassNotFoundException, IllegalAccessException, InstantiationException,
110 NoSuchMethodException, InvocationTargetException, GeometryException,
111 PropertyPathResolvingException {
112
113 ArrayList<DisplayElement> list = new ArrayList<DisplayElement>( 20 );
114
115 if ( o instanceof Feature ) {
116 Feature feature = (Feature) o;
117
118 try {
119 String featureTypeName = feature.getFeatureType().getName().getPrefixedName();
120 String qfeatureTypeName = feature.getFeatureType().getName().getFormattedString();
121
122 for ( int i = 0; i < styles.length; i++ ) {
123
124 if ( styles[i] == null ) {
125 // create display element from default style
126 DisplayElement de = buildDisplayElement( feature );
127 if ( de != null ) {
128 list.add( de );
129 }
130 } else {
131 FeatureTypeStyle[] fts = styles[i].getFeatureTypeStyles();
132 for ( int k = 0; k < fts.length; k++ ) {
133 if ( fts[k].getFeatureTypeName() == null
134 || featureTypeName.equals( fts[k].getFeatureTypeName() )
135 || qfeatureTypeName.equals( fts[k].getFeatureTypeName() ) ) {
136 Rule[] rules = fts[k].getRules();
137 for ( int n = 0; n < rules.length; n++ ) {
138 // does the filter rule apply?
139 Filter filter = rules[n].getFilter();
140 if ( filter != null ) {
141 try {
142 if ( !filter.evaluate( feature ) ) {
143 continue;
144 }
145 } catch ( FilterEvaluationException e ) {
146 LOG.logDebug( "Error evaluating filter: ", e );
147 continue;
148 }
149 }
150
151 // Filter expression is true for this feature, so a
152 // corresponding DisplayElement has to be added to the
153 // list
154 Symbolizer[] symbolizers = rules[n].getSymbolizers();
155
156 for ( int u = 0; u < symbolizers.length; u++ ) {
157 DisplayElement displayElement = DisplayElementFactory.buildDisplayElement(
158 feature,
159 symbolizers[u] );
160 if ( displayElement != null ) {
161 list.add( displayElement );
162 }
163 }
164 }
165 }
166 }
167 }
168 }
169 } catch ( IncompatibleGeometryTypeException e ) {
170 e.printStackTrace();
171 }
172 } else {
173 for ( UserStyle style : styles ) {
174 if ( style == null ) {
175 list.add( buildRasterDisplayElement( (GridCoverage) o, new RasterSymbolizer() ) );
176 continue;
177 }
178 for ( FeatureTypeStyle fts : style.getFeatureTypeStyles() ) {
179 for ( Rule rule : fts.getRules() ) {
180 for ( Symbolizer symbolizer : rule.getSymbolizers() ) {
181 list.add( buildRasterDisplayElement( (GridCoverage) o, (RasterSymbolizer) symbolizer ) );
182 }
183 }
184 }
185 }
186 }
187
188 DisplayElement[] de = new DisplayElement[list.size()];
189 return list.toArray( de );
190 }
191
192 /**
193 * Builds a {@link DisplayElement} using the given {@link Feature} or {@link GridCoverage} and
194 * {@link Symbolizer}.
195 *
196 * @param o
197 * contains the geometry or raster information (Feature or GridCoverage)
198 * @param symbolizer
199 * contains the drawing (style) information and selects the geometry property of the
200 * <code>Feature</code> to be drawn
201 * @throws IncompatibleGeometryTypeException
202 * if the selected geometry of the <code>Feature</code> is not compatible with the
203 * <code>Symbolizer</code>
204 * @return constructed <code>DisplayElement</code>
205 * @throws ClassNotFoundException
206 * @throws IllegalAccessException
207 * @throws InstantiationException
208 * @throws NoSuchMethodException
209 * @throws InvocationTargetException
210 * @throws GeometryException
211 * @throws PropertyPathResolvingException
212 */
213 public static DisplayElement buildDisplayElement( Object o, Symbolizer symbolizer )
214 throws IncompatibleGeometryTypeException, ClassNotFoundException, IllegalAccessException,
215 InstantiationException, NoSuchMethodException, InvocationTargetException,
216 GeometryException, PropertyPathResolvingException {
217 DisplayElement displayElement = null;
218
219 if ( o instanceof Feature ) {
220 Feature feature = (Feature) o;
221
222 // determine the geometry property to be used
223 org.deegree.model.spatialschema.Geometry geometry = null;
224 Geometry symbolizerGeometry = symbolizer.getGeometry();
225
226 if ( symbolizerGeometry != null ) {
227 FeatureProperty property = feature.getDefaultProperty( symbolizerGeometry.getPropertyPath() );
228 if ( property != null ) {
229 geometry = (org.deegree.model.spatialschema.Geometry) property.getValue();
230 }
231 } else {
232 geometry = feature.getDefaultGeometryPropertyValue();
233 }
234
235 // if the geometry is null, do not build a DisplayElement
236 if ( geometry == null ) {
237 return null;
238 }
239 if ( symbolizer instanceof PointSymbolizer ) {
240 displayElement = buildPointDisplayElement( feature, geometry, (PointSymbolizer) symbolizer );
241 } else if ( symbolizer instanceof LineSymbolizer ) {
242 displayElement = buildLineStringDisplayElement( feature, geometry, (LineSymbolizer) symbolizer );
243 } else if ( symbolizer instanceof PolygonSymbolizer ) {
244 displayElement = buildPolygonDisplayElement( feature, geometry, (PolygonSymbolizer) symbolizer );
245 } else if ( symbolizer instanceof TextSymbolizer ) {
246 displayElement = buildLabelDisplayElement( feature, geometry, (TextSymbolizer) symbolizer );
247 }
248 } else {
249 if ( symbolizer instanceof RasterSymbolizer ) {
250 LOG.logDebug( "Building RasterDisplayElement" );
251 displayElement = buildRasterDisplayElement( (GridCoverage) o, (RasterSymbolizer) symbolizer );
252 }
253 }
254
255 return displayElement;
256 }
257
258 /**
259 * Builds a {@link DisplayElement} using the given {@link Feature} or {@link GridCoverage} and
260 * the default {@link Symbolizer}.
261 *
262 * @param o
263 * contains the geometry or raster information (Feature or GridCoverage)
264 * @throws IncompatibleGeometryTypeException
265 * if the selected geometry of the <code>Feature</code> is not compatible with the
266 * <code>Symbolizer</code>
267 * @return constructed <code>DisplayElement</code>
268 * @throws ClassNotFoundException
269 * @throws IllegalAccessException
270 * @throws InstantiationException
271 * @throws NoSuchMethodException
272 * @throws InvocationTargetException
273 * @throws GeometryException
274 */
275 public static DisplayElement buildDisplayElement( Object o )
276 throws IncompatibleGeometryTypeException, ClassNotFoundException, IllegalAccessException,
277 InstantiationException, NoSuchMethodException, InvocationTargetException, GeometryException {
278
279 DisplayElement displayElement = null;
280
281 if ( o instanceof GridCoverage ) {
282 RasterSymbolizer symbolizer = new RasterSymbolizer();
283 displayElement = buildRasterDisplayElement( (GridCoverage) o, symbolizer );
284 } else {
285 Feature feature = (Feature) o;
286 // determine the geometry property to be used
287 org.deegree.model.spatialschema.Geometry geoProperty = feature.getDefaultGeometryPropertyValue();
288
289 // if the geometry property is null, do not build a DisplayElement
290 if ( geoProperty == null ) {
291 return null;
292 }
293 // PointSymbolizer
294 if ( geoProperty instanceof Point || geoProperty instanceof MultiPoint ) {
295 PointSymbolizer symbolizer = new PointSymbolizer();
296 displayElement = buildPointDisplayElement( feature, geoProperty, symbolizer );
297 } // LineSymbolizer
298 else if ( geoProperty instanceof Curve || geoProperty instanceof MultiCurve ) {
299 LineSymbolizer symbolizer = new LineSymbolizer();
300 displayElement = buildLineStringDisplayElement( feature, geoProperty, symbolizer );
301 } // PolygonSymbolizer
302 else if ( geoProperty instanceof Surface || geoProperty instanceof MultiSurface ) {
303 PolygonSymbolizer symbolizer = new PolygonSymbolizer();
304 displayElement = buildPolygonDisplayElement( feature, geoProperty, symbolizer );
305 } else {
306 throw new IncompatibleGeometryTypeException( "not a valid geometry type" );
307 }
308 }
309
310 return displayElement;
311 }
312
313 /**
314 * Creates a {@link PointDisplayElement} using the given geometry and style information.
315 *
316 * @param feature
317 * associated feature (source of the geometry information)
318 * @param geom
319 * geometry information
320 * @param sym
321 * style information
322 * @return constructed <code>PointDisplayElement</code>
323 * @throws ClassNotFoundException
324 * @throws IllegalAccessException
325 * @throws InstantiationException
326 * @throws NoSuchMethodException
327 * @throws InvocationTargetException
328 */
329 public static PointDisplayElement buildPointDisplayElement( Feature feature,
330 org.deegree.model.spatialschema.Geometry geom,
331 PointSymbolizer sym )
332 throws ClassNotFoundException, IllegalAccessException, InstantiationException,
333 NoSuchMethodException, InvocationTargetException {
334
335 // if the geometry is not a point geometry, the centroid(s) of the
336 // geometry will be used
337 PointDisplayElement displayElement = null;
338 String className = sym.getResponsibleClass();
339 Class clss = Class.forName( className );
340 Object[] values = new Object[] { feature, geom, sym };
341 if ( geom instanceof Point ) {
342 Class[] param = new Class[] { Feature.class, Point.class, PointSymbolizer.class };
343 Constructor constructor = clss.getConstructor( param );
344 displayElement = (PointDisplayElement) constructor.newInstance( values );
345 } else if ( geom instanceof MultiPoint ) {
346 Class[] param = new Class[] { Feature.class, MultiPoint.class, PointSymbolizer.class };
347 Constructor constructor = clss.getConstructor( param );
348 displayElement = (PointDisplayElement) constructor.newInstance( values );
349 } else if ( geom instanceof MultiPrimitive ) {
350 // Primitive[] primitives = ( (MultiPrimitive) geom ).getAllPrimitives();
351 // Point[] centroids = new Point[primitives.length];
352 Point[] centroids = new Point[1];
353 centroids[0] = geom.getCentroid();
354
355 // for ( int i = 0; i < primitives.length; i++ ) {
356 // centroids[i] = primitives[i].getCentroid();
357 // }
358
359 try {
360 Class[] param = new Class[] { Feature.class, MultiPoint.class, PointSymbolizer.class };
361 Constructor constructor = clss.getConstructor( param );
362 values[1] = GeometryFactory.createMultiPoint( centroids );
363 displayElement = (PointDisplayElement) constructor.newInstance( values );
364 } catch ( Exception e ) {
365 e.printStackTrace();
366 }
367 } else {
368 Class[] param = new Class[] { Feature.class, Point.class, PointSymbolizer.class };
369 Constructor constructor = clss.getConstructor( param );
370 values[1] = geom.getCentroid();
371 displayElement = (PointDisplayElement) constructor.newInstance( values );
372 }
373
374 return displayElement;
375 }
376
377 /**
378 * Creates a {@link LineStringDisplayElement} using the given geometry and style information.
379 *
380 * @param feature
381 * associated feature (source of the geometry information)
382 * @param geom
383 * geometry information
384 * @param sym
385 * style information
386 * @throws IncompatibleGeometryTypeException
387 * if the geometry property is not a <code>Curve</code> or <code>MultiCurve</code>
388 * @return constructed <code>LineStringDisplayElement</code>
389 * @throws ClassNotFoundException
390 * @throws IllegalAccessException
391 * @throws InstantiationException
392 * @throws NoSuchMethodException
393 * @throws InvocationTargetException
394 * @throws GeometryException
395 */
396 public static LineStringDisplayElement buildLineStringDisplayElement(
397 Feature feature,
398 org.deegree.model.spatialschema.Geometry geom,
399 LineSymbolizer sym )
400 throws IncompatibleGeometryTypeException, ClassNotFoundException, IllegalAccessException,
401 InstantiationException, NoSuchMethodException, InvocationTargetException, GeometryException {
402 LineStringDisplayElement displayElement = null;
403
404 String className = sym.getResponsibleClass();
405 Class clss = Class.forName( className );
406 Object[] values = new Object[] { feature, geom, sym };
407
408 if ( geom instanceof Curve ) {
409 Class[] param = new Class[] { Feature.class, Curve.class, LineSymbolizer.class };
410 Constructor constructor = clss.getConstructor( param );
411 displayElement = (LineStringDisplayElement) constructor.newInstance( values );
412 } else if ( geom instanceof MultiCurve ) {
413 Class[] param = new Class[] { Feature.class, MultiCurve.class, LineSymbolizer.class };
414 Constructor constructor = clss.getConstructor( param );
415 displayElement = (LineStringDisplayElement) constructor.newInstance( values );
416 } else if ( geom instanceof Surface ) {
417 // according to OGC SLD specification it is possible to assign a
418 // LineSymbolizer to a polygon. To handle this the border of the
419 // polygon will be transformed into a lines (curves)
420 MultiCurve mc = surfaceToCurve( (Surface) geom );
421 displayElement = buildLineStringDisplayElement( feature, mc, sym );
422 } else if ( geom instanceof MultiSurface ) {
423 // according to OGC SLD specification it is possible to assign a
424 // LineSymbolizer to a multipolygon. To handle this the borders of the
425 // multipolygons will be transformed into a lines (curves)
426 MultiSurface ms = (MultiSurface) geom;
427 List<Curve> list = new ArrayList<Curve>( 500 );
428 for ( int i = 0; i < ms.getSize(); i++ ) {
429 MultiCurve mc = surfaceToCurve( ms.getSurfaceAt( i ) );
430 for ( int j = 0; j < mc.getSize(); j++ ) {
431 list.add( mc.getCurveAt( j ) );
432 }
433 }
434 Curve[] curves = list.toArray( new Curve[list.size()] );
435 MultiCurve mc = GeometryFactory.createMultiCurve( curves );
436 displayElement = buildLineStringDisplayElement( feature, mc, sym );
437 } else {
438 throw new IncompatibleGeometryTypeException( "Tried to create a LineStringDisplayElement from a geometry "
439 + "with an incompatible / unsupported type: '"
440 + geom.getClass().getName() + "'!" );
441 }
442
443 return displayElement;
444 }
445
446 /**
447 * Transforms a {@link Surface} into a {@link MultiCurve}.
448 *
449 * @param geom
450 * @return MultiCurve
451 * @throws Exception
452 */
453 private static MultiCurve surfaceToCurve( Surface geom )
454 throws GeometryException {
455 List<Curve> list = new ArrayList<Curve>( 100 );
456 int num = geom.getNumberOfSurfacePatches();
457 for ( int i = 0; i < num; i++ ) {
458 Position[] pos = geom.getSurfacePatchAt( i ).getExteriorRing();
459 Curve curve = GeometryFactory.createCurve( pos, geom.getCoordinateSystem() );
460 list.add( curve );
461 Position[][] inn = geom.getSurfacePatchAt( i ).getInteriorRings();
462 if ( inn != null ) {
463 for ( int j = 0; j < inn.length; j++ ) {
464 curve = GeometryFactory.createCurve( inn[j], geom.getCoordinateSystem() );
465 list.add( curve );
466 }
467 }
468 }
469 Curve[] curves = list.toArray( new Curve[list.size()] );
470 MultiCurve mc = GeometryFactory.createMultiCurve( curves );
471 return mc;
472 }
473
474 /**
475 * Creates a {@link PolygonDisplayElement} using the given geometry and style information.
476 *
477 * @param feature
478 * associated feature (source of the geometry information)
479 * @param geom
480 * geometry information
481 * @param sym
482 * style information
483 * @throws IncompatibleGeometryTypeException
484 * if the geometry property is not a <code>Surface</code> or
485 * <code>MultiSurface</code>
486 * @return constructed <code>PolygonDisplayElement</code>
487 * @throws ClassNotFoundException
488 * @throws IllegalAccessException
489 * @throws InstantiationException
490 * @throws NoSuchMethodException
491 * @throws InvocationTargetException
492 */
493 public static PolygonDisplayElement buildPolygonDisplayElement( Feature feature,
494 org.deegree.model.spatialschema.Geometry geom,
495 PolygonSymbolizer sym )
496 throws IncompatibleGeometryTypeException, ClassNotFoundException, IllegalAccessException,
497 InstantiationException, NoSuchMethodException, InvocationTargetException {
498 PolygonDisplayElement displayElement = null;
499
500 String className = sym.getResponsibleClass();
501 Class clss = Class.forName( className );
502 Object[] values = new Object[] { feature, geom, sym };
503 if ( geom instanceof Surface ) {
504 Class[] param = new Class[] { Feature.class, Surface.class, PolygonSymbolizer.class };
505 Constructor constructor = clss.getConstructor( param );
506 displayElement = (PolygonDisplayElement) constructor.newInstance( values );
507 } else if ( geom instanceof MultiSurface ) {
508 Class[] param = new Class[] { Feature.class, MultiSurface.class, PolygonSymbolizer.class };
509 Constructor constructor = clss.getConstructor( param );
510 displayElement = (PolygonDisplayElement) constructor.newInstance( values );
511 } else {
512 throw new IncompatibleGeometryTypeException(
513 "Tried to create a LineStringDisplayElement from a geometry with "
514 + "an incompatible / unsupported type: '"
515 + geom.getClass().getName() + "'!" );
516 }
517
518 return displayElement;
519 }
520
521 /**
522 * Creates a {@link LabelDisplayElement} using the given geometry and style information.
523 *
524 * @param feature
525 * associated feature (source of the geometry information and label caption)
526 * @param geom
527 * geometry information
528 * @param sym
529 * style information
530 * @throws IncompatibleGeometryTypeException
531 * if the geometry property is not a <code>Point</code>, a <code>Surface</code>
532 * or <code>MultiSurface</code>
533 * @return constructed <code>LabelDisplayElement</code>
534 * @throws ClassNotFoundException
535 * @throws IllegalAccessException
536 * @throws InstantiationException
537 * @throws NoSuchMethodException
538 * @throws InvocationTargetException
539 */
540 public static LabelDisplayElement buildLabelDisplayElement( Feature feature,
541 org.deegree.model.spatialschema.Geometry geom,
542 TextSymbolizer sym )
543 throws IncompatibleGeometryTypeException, ClassNotFoundException, IllegalAccessException,
544 InstantiationException, NoSuchMethodException, InvocationTargetException {
545
546 LabelDisplayElement displayElement = null;
547
548 if ( geom instanceof Point || geom instanceof MultiPoint || geom instanceof Surface
549 || geom instanceof MultiSurface || geom instanceof Curve || geom instanceof MultiCurve ) {
550 String className = sym.getResponsibleClass();
551 Class clss = Class.forName( className );
552 Class[] param = new Class[] { Feature.class, org.deegree.model.spatialschema.Geometry.class,
553 TextSymbolizer.class };
554 Object[] values = new Object[] { feature, geom, sym };
555 Constructor constructor = clss.getConstructor( param );
556 displayElement = (LabelDisplayElement) constructor.newInstance( values );
557 } else {
558 throw new IncompatibleGeometryTypeException( "Tried to create a LabelDisplayElement from a geometry with "
559 + "an incompatible / unsupported type: '"
560 + geom.getClass().getName() + "'!" );
561 }
562
563 return displayElement;
564 }
565
566 /**
567 * Creates a {@link RasterDisplayElement} from the given {@link GridCoverage}.
568 *
569 * @param gc
570 * grid coverage (source of the raster data)
571 * @param sym
572 * raster symbolizer
573 *
574 * @return RasterDisplayElement
575 */
576 public static RasterDisplayElement buildRasterDisplayElement( GridCoverage gc, RasterSymbolizer sym ) {
577 return new RasterDisplayElement( gc, sym );
578 }
579 }