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