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 }