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 }