001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/graphics/Theme.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001-2006 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 066 * a portrayal model for their graphical representation. Considering the OGC 067 * Styled Layer Descriptor specification this is not nessecary the case. In 068 * confirmation with the SLD a theme can be build from a lot of thematic 069 * completly different feature types.<p></p> 070 * From a theoretical point of view this isn't very satisfying. But it will 071 * be supported by the <tt>Theme</tt> class.<p></p> 072 * Assigned to the Theme are: 073 * <ul> 074 * <li>a Layer that contains the data (features) 075 * <li>a Portrayal model that determines how the features shall be rendered 076 * <li>a Selector that offers method for selection and de-selection of 077 * features 078 * <li>a event listener that handles event occuring on a theme that's 079 * for usual part of a map. 080 * </ul> 081 * 082 * <p>------------------------------------------------------------------------</p> 083 * 084 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 085 * @version $Revision: 6259 $ $Date: 2007-03-20 10:15:15 +0100 (Di, 20 Mär 2007) $ 086 */ 087 088 public class Theme { 089 090 private static final ILogger LOG = LoggerFactory.getLogger( Theme.class ); 091 092 private String name = null; 093 094 private Layer layer = null; 095 096 private UserStyle[] styles = null; 097 098 private ArrayList displayElements = null; 099 100 /** 101 * the MapView (map) the theme is associated to 102 * 103 */ 104 private MapView parent = null; 105 106 /** 107 * this ArrayList contains all DisplayElements (and so the features) that 108 * are marked as selected. 109 */ 110 private List selector = Collections.synchronizedList( new ArrayList() ); 111 112 private List highlighter = Collections.synchronizedList( new ArrayList() ); 113 114 private List eventController = Collections.synchronizedList( new ArrayList() ); 115 116 /** 117 * 118 * @param name 119 * @param layer 120 * @param styles 121 */ 122 protected Theme( String name, Layer layer, UserStyle[] styles ) { 123 this.layer = layer; 124 this.name = name; 125 displayElements = new ArrayList( 1000 ); 126 setStyles( styles ); 127 } 128 129 /** 130 * sets the parent MapView of the Theme. 131 * 132 */ 133 public void setParent( MapView parent ) { 134 this.parent = parent; 135 } 136 137 /** 138 * returns the name of the layer 139 * 140 */ 141 public String getName() { 142 return name; 143 } 144 145 /** 146 * renders the layer to the submitted graphic context 147 */ 148 public void paint( Graphics g ) { 149 150 double scale = parent.getScale(); 151 152 if ( layer instanceof LazyRasterLayer ) { 153 // re-create raster displayelements to adapt current 154 // current boundingbox 155 createLazyRasterDisplayElements(); 156 } else if ( layer instanceof OWSRasterLayer ) { 157 createOWSRasterDisplayElements(); 158 } 159 for ( int i = 0; i < displayElements.size(); i++ ) { 160 DisplayElement de = (DisplayElement) displayElements.get( i ); 161 162 if ( de.doesScaleConstraintApply( scale ) ) { 163 de.paint( g, parent.getProjection(), scale ); 164 } 165 } 166 167 } 168 169 /** 170 * renders the display elements matching the submitted ids 171 */ 172 public void paint( Graphics g, String[] ids ) { 173 174 double scale = parent.getScale(); 175 176 if ( layer instanceof LazyRasterLayer ) { 177 // re-create raster displayelements to adapt current 178 // current boundingbox 179 createLazyRasterDisplayElements(); 180 } 181 182 for ( int k = 0; k < displayElements.size(); k++ ) { 183 DisplayElement de = (DisplayElement) displayElements.get( k ); 184 for ( int i = 0; i < ids.length; i++ ) { 185 if ( de.getAssociateFeatureId().equals( ids[i] ) ) { 186 de.paint( g, parent.getProjection(), scale ); 187 break; 188 } 189 } 190 } 191 } 192 193 /** 194 * renders the selected display elements of the layer 195 */ 196 public void paintSelected( Graphics g ) { 197 198 double scale = parent.getScale(); 199 200 if ( layer instanceof LazyRasterLayer ) { 201 // re-create raster displayelements to adapt current 202 // current boundingbox 203 createLazyRasterDisplayElements(); 204 } 205 206 if ( layer instanceof OWSRasterLayer ) { 207 208 } 209 210 for ( int i = 0; i < displayElements.size(); i++ ) { 211 DisplayElement de = ( (DisplayElement) displayElements.get( i ) ); 212 if ( de.isSelected() ) { 213 de.paint( g, parent.getProjection(), scale ); 214 } 215 } 216 217 } 218 219 /** 220 * renders the highlighted display elements of the layer 221 */ 222 public void paintHighlighted( Graphics g ) { 223 224 double scale = parent.getScale(); 225 226 if ( layer instanceof LazyRasterLayer ) { 227 // re-create raster displayelements to adapt current 228 // current boundingbox 229 createLazyRasterDisplayElements(); 230 } 231 232 for ( int i = 0; i < displayElements.size(); i++ ) { 233 DisplayElement de = ( (DisplayElement) displayElements.get( i ) ); 234 if ( de.isHighlighted() ) { 235 de.paint( g, parent.getProjection(), scale ); 236 } 237 } 238 239 } 240 241 /** 242 * A selector is a class that offers methods for selecting and 243 * deselecting single DisplayElements or groups of DisplayElements. 244 * A selector may offers methods like 'select all DisplayElements 245 * within a specified bounding box' or 'select all DisplayElements 246 * thats area is 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 263 * capabilities for one 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 280 * events that targets the 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 297 * called all <tt>DisplayElement</tt>s will be recreated to consider the 298 * new style definitions. 299 * 300 */ 301 public void setStyles( UserStyle[] styles ) { 302 303 this.styles = styles; 304 displayElements.clear(); 305 if ( layer instanceof FeatureLayer ) { 306 createFeatureDisplayElements(); 307 } else if ( layer instanceof RasterLayer ) { 308 createRasterDisplayElements( ); 309 } else { 310 createLazyRasterDisplayElements( ); 311 } 312 313 } 314 315 /** 316 * creates <code>DisplayElement</code>s for <code>Feature</code> instances 317 */ 318 private void createFeatureDisplayElements( ) { 319 displayElements.clear(); 320 DisplayElementFactory fac = new DisplayElementFactory(); 321 // keep LabelDisplayElements separate from the other elements 322 // and append them to the end of the DisplayElement-list 323 ArrayList labelDisplayElements = new ArrayList( 100 ); 324 try { 325 // instance of FeatureLayer 326 for ( int i = 0; i < ( (FeatureLayer) layer ).getSize(); i++ ) { 327 Feature feature = ( (FeatureLayer) layer ).getFeature( i ); 328 featureToDisplayElement( styles, fac, labelDisplayElements, feature ); 329 } 330 } catch ( Exception e ) { 331 LOG.logError( e.getMessage(), e ); 332 } 333 displayElements.addAll( labelDisplayElements ); 334 } 335 336 /** 337 * creates <code>DisplayElement</code>s for <code>GridCoverage</code> instances 338 */ 339 private void createRasterDisplayElements( ) { 340 displayElements.clear(); 341 DisplayElementFactory fac = new DisplayElementFactory(); 342 try { 343 // instance of RasterLayer 344 RasterLayer rl = (RasterLayer) layer; 345 DisplayElement[] de = fac.createDisplayElement( rl.getRaster(), styles ); 346 for ( int k = 0; k < de.length; k++ ) { 347 displayElements.add( de[k] ); 348 } 349 } catch ( Exception e ) { 350 LOG.logError( e.getMessage(), e ); 351 } 352 } 353 354 /** 355 * creates <code>DisplayElement</code>s for <code>GridCoverage</code> instances that 356 * are loaded depending on current boundingbox. 357 */ 358 private void createLazyRasterDisplayElements( ) { 359 displayElements.clear(); 360 DisplayElementFactory fac = new DisplayElementFactory(); 361 try { 362 if ( parent != null ) { 363 LazyRasterLayer rl = (LazyRasterLayer) layer; 364 double w = parent.getProjection().getDestRect().getWidth(); 365 double d = parent.getBoundingBox().getWidth()/w; 366 GridCoverage gc = rl.getRaster( parent.getBoundingBox(), d ); 367 //gc can be null if e.g. the area covered by the raster 368 // layer is outside the visible area. 369 if ( gc != null ) { 370 DisplayElement[] de = fac.createDisplayElement( gc, styles ); 371 for ( int k = 0; k < de.length; k++ ) { 372 displayElements.add( de[k] ); 373 } 374 } 375 } 376 } catch ( Exception e ) { 377 LOG.logError( e.getMessage(), e ); 378 } 379 } 380 381 private void createOWSRasterDisplayElements( ) { 382 displayElements.clear(); 383 384 DisplayElementFactory fac = new DisplayElementFactory(); 385 try { 386 if ( parent != null ) { 387 OWSRasterLayer rl = (OWSRasterLayer) layer; 388 double w = parent.getProjection().getDestRect().getWidth(); 389 double h = parent.getProjection().getDestRect().getHeight(); 390 GridCoverage gc = rl.getRaster( parent.getBoundingBox(), w, h ); 391 if ( gc != null ){ 392 DisplayElement[] de = 393 fac.createDisplayElement( gc, styles ); 394 for ( int k = 0; k < de.length; k++ ) { 395 displayElements.add( de[k] ); 396 } 397 } 398 } 399 } catch ( Exception e ) { 400 LOG.logError( e.getMessage(), e ); 401 } 402 } 403 /** 404 * 405 * @param styles 406 * @param fac 407 * @param labelDisplayElements 408 * @param feature 409 * @throws ClassNotFoundException 410 * @throws IllegalAccessException 411 * @throws InstantiationException 412 * @throws NoSuchMethodException 413 * @throws InvocationTargetException 414 * @throws GeometryException 415 * @throws PropertyPathResolvingException 416 */ 417 private void featureToDisplayElement( UserStyle[] styles, DisplayElementFactory fac, 418 ArrayList labelDisplayElements, Feature feature ) 419 throws ClassNotFoundException, IllegalAccessException, 420 InstantiationException, NoSuchMethodException, 421 InvocationTargetException, GeometryException, 422 PropertyPathResolvingException { 423 DisplayElement[] de = fac.createDisplayElement( feature, styles ); 424 for ( int k = 0; k < de.length; k++ ) { 425 if ( de[k] instanceof LabelDisplayElement ) { 426 labelDisplayElements.add( de[k] ); 427 } else { 428 displayElements.add( de[k] ); 429 } 430 } 431 FeatureProperty[] fp = feature.getProperties(); 432 for ( int i = 0; i < fp.length; i++ ) { 433 if ( fp[i].getValue() != null && fp[i].getValue() instanceof Feature ) { 434 featureToDisplayElement( styles, fac, labelDisplayElements, 435 (Feature) fp[i].getValue() ); 436 } 437 } 438 } 439 440 /** 441 * returns the styles used for this <tt>Theme</tt>. 442 * 443 */ 444 public UserStyle[] getStyles() { 445 return styles; 446 } 447 448 /** 449 * returns the layer that holds the data of the theme 450 * 451 */ 452 public Layer getLayer() { 453 return layer; 454 } 455 456 /** 457 * Returns all <tt>DisplayElements</tt> that this <tt>Theme</tt> contains. 458 * <p> 459 * @return <tt>ArrayList</tt> containing <tt>DisplayElements</tt> 460 * 461 */ 462 public ArrayList getDisplayElements() { 463 return displayElements; 464 } 465 466 /** 467 * returns the <tt>DisplayElements</tt> of the Theme 468 * 469 */ 470 public void setDisplayElements( ArrayList de ) { 471 this.displayElements = de; 472 } 473 474 } 475 /* ******************************************************************** 476 Changes to this class. What the people have been up to: 477 $Log$ 478 Revision 1.24 2007/02/09 17:28:00 poth 479 *** empty log message *** 480 481 Revision 1.23 2006/09/28 15:39:01 poth 482 bug fix - using LazyRasterLayer 483 484 Revision 1.22 2006/09/22 12:16:28 taddei 485 made constructor protected 486 487 Revision 1.21 2006/09/07 13:26:20 taddei 488 handling/ignoring exceptions in OWSRasterlayer; and using img dimension 489 490 Revision 1.20 2006/08/25 14:11:39 taddei 491 added code for OWSRasterLayer 492 493 Revision 1.19 2006/08/24 09:58:19 poth 494 support for OWSRasterLayer added 495 496 Revision 1.18 2006/05/31 17:53:33 poth 497 bug fix 498 499 Revision 1.17 2006/05/31 17:23:59 poth 500 first implementation of LazyRasterLayer 501 502 Revision 1.16 2006/05/26 06:43:23 poth 503 bug fix creating display elements 504 505 Revision 1.15 2006/05/25 16:16:46 poth 506 bug fix in all paint methods 507 508 Revision 1.14 2006/05/24 08:05:23 poth 509 support for LazyRasterLayer added 510 511 512 ********************************************************************** */