001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/graphics/Theme.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; 045 046 import java.awt.Graphics; 047 import java.lang.reflect.InvocationTargetException; 048 import java.util.ArrayList; 049 import java.util.Collections; 050 import java.util.List; 051 052 import org.deegree.framework.log.ILogger; 053 import org.deegree.framework.log.LoggerFactory; 054 import org.deegree.graphics.displayelements.DisplayElement; 055 import org.deegree.graphics.displayelements.DisplayElementFactory; 056 import org.deegree.graphics.displayelements.LabelDisplayElement; 057 import org.deegree.graphics.sld.UserStyle; 058 import org.deegree.io.datastore.PropertyPathResolvingException; 059 import org.deegree.model.coverage.grid.GridCoverage; 060 import org.deegree.model.feature.Feature; 061 import org.deegree.model.feature.FeatureProperty; 062 import org.deegree.model.spatialschema.GeometryException; 063 064 /** 065 * A Theme is for usual a homogenious collection of Features coupled with a portrayal model for 066 * their graphical representation. Considering the OGC Styled Layer Descriptor specification this is 067 * not nessecary the case. In confirmation with the SLD a theme can be build from a lot of thematic 068 * completly different feature types. 069 * <p> 070 * </p> 071 * From a theoretical point of view this isn't very satisfying. But it will be supported by the 072 * <tt>Theme</tt> class. 073 * <p> 074 * </p> 075 * Assigned to the Theme are: 076 * <ul> 077 * <li>a Layer that contains the data (features) 078 * <li>a Portrayal model that determines how the features shall be rendered 079 * <li>a Selector that offers method for selection and de-selection of features 080 * <li>a event listener that handles event occuring on a theme that's for usual part of a map. 081 * </ul> 082 * 083 * 084 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</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 Theme { 090 091 private static final ILogger LOG = LoggerFactory.getLogger( Theme.class ); 092 093 private String name = null; 094 095 private Layer layer = null; 096 097 private UserStyle[] styles = null; 098 099 private List<DisplayElement> displayElements = null; 100 101 /** 102 * the MapView (map) the theme is associated to 103 * 104 */ 105 private MapView parent = null; 106 107 /** 108 * this ArrayList contains all DisplayElements (and so the features) that are marked as 109 * selected. 110 */ 111 private List<Selector> selector = Collections.synchronizedList( new ArrayList<Selector>() ); 112 113 private List<Highlighter> highlighter = Collections.synchronizedList( new ArrayList<Highlighter>() ); 114 115 private List<EventController> eventController = Collections.synchronizedList( new ArrayList<EventController>() ); 116 117 /** 118 * 119 * @param name 120 * @param layer 121 * @param styles 122 */ 123 protected Theme( String name, Layer layer, UserStyle[] styles ) { 124 this.layer = layer; 125 this.name = name; 126 displayElements = new ArrayList<DisplayElement>( 1000 ); 127 setStyles( styles ); 128 } 129 130 /** 131 * sets the parent MapView of the Theme. 132 * 133 */ 134 public void setParent( MapView parent ) { 135 this.parent = parent; 136 } 137 138 /** 139 * returns the name of the layer 140 * 141 */ 142 public String getName() { 143 return name; 144 } 145 146 /** 147 * renders the layer to the submitted graphic context 148 */ 149 public void paint( Graphics g ) { 150 151 double scale = parent.getScale(); 152 153 if ( layer instanceof LazyRasterLayer ) { 154 // re-create raster displayelements to adapt current 155 // current boundingbox 156 createLazyRasterDisplayElements(); 157 } else if ( layer instanceof OWSRasterLayer ) { 158 createOWSRasterDisplayElements(); 159 } 160 for ( int i = 0; i < displayElements.size(); i++ ) { 161 DisplayElement de = displayElements.get( i ); 162 163 if ( de.doesScaleConstraintApply( scale ) ) { 164 de.paint( g, parent.getProjection(), scale ); 165 } 166 } 167 168 } 169 170 /** 171 * renders the display elements matching the submitted ids 172 */ 173 public void paint( Graphics g, String[] ids ) { 174 175 double scale = parent.getScale(); 176 177 if ( layer instanceof LazyRasterLayer ) { 178 // re-create raster displayelements to adapt current 179 // current boundingbox 180 createLazyRasterDisplayElements(); 181 } 182 183 for ( int k = 0; k < displayElements.size(); k++ ) { 184 DisplayElement de = displayElements.get( k ); 185 for ( int i = 0; i < ids.length; i++ ) { 186 if ( de.getAssociateFeatureId().equals( ids[i] ) ) { 187 de.paint( g, parent.getProjection(), scale ); 188 break; 189 } 190 } 191 } 192 } 193 194 /** 195 * renders the selected display elements of the layer 196 */ 197 public void paintSelected( Graphics g ) { 198 199 double scale = parent.getScale(); 200 201 if ( layer instanceof LazyRasterLayer ) { 202 // re-create raster displayelements to adapt current 203 // current boundingbox 204 createLazyRasterDisplayElements(); 205 } 206 207 if ( layer instanceof OWSRasterLayer ) { 208 209 } 210 211 for ( int i = 0; i < displayElements.size(); i++ ) { 212 DisplayElement de = displayElements.get( i ); 213 if ( de.isSelected() ) { 214 de.paint( g, parent.getProjection(), scale ); 215 } 216 } 217 218 } 219 220 /** 221 * renders the highlighted display elements of the layer 222 */ 223 public void paintHighlighted( Graphics g ) { 224 225 double scale = parent.getScale(); 226 227 if ( layer instanceof LazyRasterLayer ) { 228 // re-create raster displayelements to adapt current 229 // current boundingbox 230 createLazyRasterDisplayElements(); 231 } 232 233 for ( int i = 0; i < displayElements.size(); i++ ) { 234 DisplayElement de = displayElements.get( i ); 235 if ( de.isHighlighted() ) { 236 de.paint( g, parent.getProjection(), scale ); 237 } 238 } 239 240 } 241 242 /** 243 * A selector is a class that offers methods for selecting and deselecting single 244 * DisplayElements or groups of DisplayElements. A selector may offers methods like 'select all 245 * DisplayElements within a specified bounding box' or 'select all DisplayElements thats area is 246 * larger than 120 km�' etc. 247 */ 248 public void addSelector( Selector selector ) { 249 this.selector.add( selector ); 250 selector.addTheme( this ); 251 } 252 253 /** 254 * @see org.deegree.graphics.Theme#addSelector(Selector) 255 */ 256 public void removeSelector( Selector selector ) { 257 this.selector.remove( selector ); 258 selector.removeTheme( this ); 259 } 260 261 /** 262 * A Highlighter is a class that is responsible for managing the highlight capabilities for one 263 * or more Themes. 264 */ 265 public void addHighlighter( Highlighter highlighter ) { 266 this.highlighter.add( highlighter ); 267 highlighter.addTheme( this ); 268 } 269 270 /** 271 * @see org.deegree.graphics.Theme#addHighlighter(Highlighter) 272 */ 273 public void removeHighlighter( Highlighter highlighter ) { 274 this.highlighter.remove( highlighter ); 275 highlighter.removeTheme( this ); 276 } 277 278 /** 279 * adds an eventcontroller to the theme that's reponsible for handling events that targets the 280 * theme. 281 */ 282 public void addEventController( ThemeEventController controller ) { 283 eventController.add( controller ); 284 controller.addTheme( this ); 285 } 286 287 /** 288 * @see org.deegree.graphics.Theme#addEventController(ThemeEventController) 289 */ 290 public void removeEventController( ThemeEventController controller ) { 291 eventController.remove( controller ); 292 controller.removeTheme( this ); 293 } 294 295 /** 296 * Sets the styles used for this <tt>Theme</tt>. If this method will be called all 297 * <tt>DisplayElement</tt>s will be recreated to consider the new style definitions. 298 * 299 */ 300 public void setStyles( UserStyle[] styles ) { 301 302 this.styles = styles; 303 displayElements.clear(); 304 if ( layer instanceof FeatureLayer ) { 305 createFeatureDisplayElements(); 306 } else if ( layer instanceof RasterLayer ) { 307 createRasterDisplayElements(); 308 } else { 309 createLazyRasterDisplayElements(); 310 } 311 312 } 313 314 /** 315 * creates <code>DisplayElement</code>s for <code>Feature</code> instances 316 */ 317 private void createFeatureDisplayElements() { 318 displayElements.clear(); 319 DisplayElementFactory fac = new DisplayElementFactory(); 320 // keep LabelDisplayElements separate from the other elements 321 // and append them to the end of the DisplayElement-list 322 List<DisplayElement> labelDisplayElements = new ArrayList<DisplayElement>( 100 ); 323 try { 324 // instance of FeatureLayer 325 for ( int i = 0; i < ( (FeatureLayer) layer ).getSize(); i++ ) { 326 Feature feature = ( (FeatureLayer) layer ).getFeature( i ); 327 featureToDisplayElement( styles, fac, labelDisplayElements, feature ); 328 } 329 } catch ( Exception e ) { 330 LOG.logError( e.getMessage(), e ); 331 } 332 displayElements.addAll( labelDisplayElements ); 333 } 334 335 /** 336 * creates <code>DisplayElement</code>s for <code>GridCoverage</code> instances 337 */ 338 private void createRasterDisplayElements() { 339 displayElements.clear(); 340 DisplayElementFactory fac = new DisplayElementFactory(); 341 try { 342 // instance of RasterLayer 343 RasterLayer rl = (RasterLayer) layer; 344 DisplayElement[] de = fac.createDisplayElement( rl.getRaster(), styles ); 345 for ( int k = 0; k < de.length; k++ ) { 346 displayElements.add( de[k] ); 347 } 348 } catch ( Exception e ) { 349 LOG.logError( e.getMessage(), e ); 350 } 351 } 352 353 /** 354 * creates <code>DisplayElement</code>s for <code>GridCoverage</code> instances that are 355 * loaded depending on current boundingbox. 356 */ 357 private void createLazyRasterDisplayElements() { 358 displayElements.clear(); 359 DisplayElementFactory fac = new DisplayElementFactory(); 360 try { 361 if ( parent != null ) { 362 LazyRasterLayer rl = (LazyRasterLayer) layer; 363 double w = parent.getProjection().getDestRect().getWidth(); 364 double d = parent.getBoundingBox().getWidth() / w; 365 GridCoverage gc = rl.getRaster( parent.getBoundingBox(), d ); 366 // gc can be null if e.g. the area covered by the raster 367 // layer is outside the visible area. 368 if ( gc != null ) { 369 DisplayElement[] de = fac.createDisplayElement( gc, styles ); 370 for ( int k = 0; k < de.length; k++ ) { 371 displayElements.add( de[k] ); 372 } 373 } 374 } 375 } catch ( Exception e ) { 376 LOG.logError( e.getMessage(), e ); 377 } 378 } 379 380 private void createOWSRasterDisplayElements() { 381 displayElements.clear(); 382 383 DisplayElementFactory fac = new DisplayElementFactory(); 384 try { 385 if ( parent != null ) { 386 OWSRasterLayer rl = (OWSRasterLayer) layer; 387 double w = parent.getProjection().getDestRect().getWidth(); 388 double h = parent.getProjection().getDestRect().getHeight(); 389 GridCoverage gc = rl.getRaster( parent.getBoundingBox(), w, h ); 390 if ( gc != null ) { 391 DisplayElement[] de = fac.createDisplayElement( gc, styles ); 392 for ( int k = 0; k < de.length; k++ ) { 393 displayElements.add( de[k] ); 394 } 395 } 396 } 397 } catch ( Exception e ) { 398 LOG.logError( e.getMessage(), e ); 399 } 400 } 401 402 /** 403 * 404 * @param styles 405 * @param fac 406 * @param labelDisplayElements 407 * @param feature 408 * @throws ClassNotFoundException 409 * @throws IllegalAccessException 410 * @throws InstantiationException 411 * @throws NoSuchMethodException 412 * @throws InvocationTargetException 413 * @throws GeometryException 414 * @throws PropertyPathResolvingException 415 */ 416 private void featureToDisplayElement( UserStyle[] styles, DisplayElementFactory fac, 417 List<DisplayElement> labelDisplayElements, Feature feature ) 418 throws ClassNotFoundException, IllegalAccessException, InstantiationException, 419 NoSuchMethodException, InvocationTargetException, GeometryException, 420 PropertyPathResolvingException { 421 DisplayElement[] de = fac.createDisplayElement( feature, styles ); 422 for ( int k = 0; k < de.length; k++ ) { 423 if ( de[k] instanceof LabelDisplayElement ) { 424 labelDisplayElements.add( de[k] ); 425 } else { 426 displayElements.add( de[k] ); 427 } 428 } 429 FeatureProperty[] fp = feature.getProperties(); 430 for ( int i = 0; i < fp.length; i++ ) { 431 if ( fp[i].getValue() != null && fp[i].getValue() instanceof Feature ) { 432 featureToDisplayElement( styles, fac, labelDisplayElements, (Feature) fp[i].getValue() ); 433 } 434 } 435 } 436 437 /** 438 * returns the styles used for this <tt>Theme</tt>. 439 * 440 */ 441 public UserStyle[] getStyles() { 442 return styles; 443 } 444 445 /** 446 * returns the layer that holds the data of the theme 447 * 448 */ 449 public Layer getLayer() { 450 return layer; 451 } 452 453 /** 454 * Returns all <tt>DisplayElements</tt> that this <tt>Theme</tt> contains. 455 * <p> 456 * 457 * @return <tt>ArrayList</tt> containing <tt>DisplayElements</tt> 458 * 459 */ 460 public List<DisplayElement> getDisplayElements() { 461 return displayElements; 462 } 463 464 /** 465 * returns the <tt>DisplayElements</tt> of the Theme 466 * 467 */ 468 public void setDisplayElements( List<DisplayElement> de ) { 469 this.displayElements = de; 470 } 471 472 }