001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/graphics/Theme.java $ 002 /*---------------------------------------------------------------------------- 003 This file is part of deegree, http://deegree.org/ 004 Copyright (C) 2001-2009 by: 005 Department of Geography, University of Bonn 006 and 007 lat/lon GmbH 008 009 This library is free software; you can redistribute it and/or modify it under 010 the terms of the GNU Lesser General Public License as published by the Free 011 Software Foundation; either version 2.1 of the License, or (at your option) 012 any later version. 013 This library is distributed in the hope that it will be useful, but WITHOUT 014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 015 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 016 details. 017 You should have received a copy of the GNU Lesser General Public License 018 along with this library; if not, write to the Free Software Foundation, Inc., 019 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 020 021 Contact information: 022 023 lat/lon GmbH 024 Aennchenstr. 19, 53177 Bonn 025 Germany 026 http://lat-lon.de/ 027 028 Department of Geography, University of Bonn 029 Prof. Dr. Klaus Greve 030 Postfach 1147, 53001 Bonn 031 Germany 032 http://www.geographie.uni-bonn.de/deegree/ 033 034 e-mail: info@deegree.org 035 ----------------------------------------------------------------------------*/ 036 037 package org.deegree.graphics; 038 039 import java.awt.Graphics; 040 import java.lang.reflect.InvocationTargetException; 041 import java.util.ArrayList; 042 import java.util.Collections; 043 import java.util.List; 044 045 import org.deegree.framework.log.ILogger; 046 import org.deegree.framework.log.LoggerFactory; 047 import org.deegree.framework.util.StringTools; 048 import org.deegree.graphics.displayelements.DisplayElement; 049 import org.deegree.graphics.displayelements.DisplayElementFactory; 050 import org.deegree.graphics.displayelements.LabelDisplayElement; 051 import org.deegree.graphics.sld.UserStyle; 052 import org.deegree.io.datastore.PropertyPathResolvingException; 053 import org.deegree.model.coverage.grid.GridCoverage; 054 import org.deegree.model.feature.Feature; 055 import org.deegree.model.feature.FeatureProperty; 056 import org.deegree.model.spatialschema.GeometryException; 057 058 /** 059 * A Theme is for usual a homogenious collection of Features coupled with a portrayal model for 060 * their graphical representation. Considering the OGC Styled Layer Descriptor specification this is 061 * not nessecary the case. In confirmation with the SLD a theme can be build from a lot of thematic 062 * completly different feature types. 063 * <p> 064 * </p> 065 * From a theoretical point of view this isn't very satisfying. But it will be supported by the 066 * <tt>Theme</tt> class. 067 * <p> 068 * </p> 069 * Assigned to the Theme are: 070 * <ul> 071 * <li>a Layer that contains the data (features) 072 * <li>a Portrayal model that determines how the features shall be rendered 073 * <li>a Selector that offers method for selection and de-selection of features 074 * <li>a event listener that handles event occuring on a theme that's for usual part of a map. 075 * </ul> 076 * 077 * 078 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 079 * @author last edited by: $Author: apoth $ 080 * 081 * @version $Revision: 19921 $, $Date: 2009-10-02 11:20:56 +0200 (Fr, 02 Okt 2009) $ 082 */ 083 public class Theme { 084 085 private static final ILogger LOG = LoggerFactory.getLogger( Theme.class ); 086 087 private String name = null; 088 089 private Layer layer = null; 090 091 private UserStyle[] styles = null; 092 093 private List<DisplayElement> displayElements = null; 094 095 /** 096 * the MapView (map) the theme is associated to 097 * 098 */ 099 private MapView parent = null; 100 101 /** 102 * this ArrayList contains all DisplayElements (and so the features) that are marked as 103 * selected. 104 */ 105 private List<Selector> selector = Collections.synchronizedList( new ArrayList<Selector>() ); 106 107 private List<Highlighter> highlighter = Collections.synchronizedList( new ArrayList<Highlighter>() ); 108 109 private List<EventController> eventController = Collections.synchronizedList( new ArrayList<EventController>() ); 110 111 /** 112 * 113 * @param name 114 * @param layer 115 * @param styles 116 */ 117 protected Theme( String name, Layer layer, UserStyle[] styles ) { 118 this.layer = layer; 119 this.name = name; 120 displayElements = new ArrayList<DisplayElement>( 1000 ); 121 setStyles( styles ); 122 } 123 124 /** 125 * sets the parent MapView of the Theme. 126 * @param parent of this theme 127 * 128 */ 129 public void setParent( MapView parent ) { 130 this.parent = parent; 131 } 132 133 /** 134 * returns the name of the theme 135 * @return the name of the theme 136 * 137 */ 138 public String getName() { 139 return name; 140 } 141 142 /** 143 * renders the layer to the submitted graphic context 144 * @param g to draw upon 145 */ 146 public void paint( Graphics g ) { 147 148 double scale = parent.getScale(); 149 150 if ( layer instanceof LazyRasterLayer ) { 151 // re-create raster displayelements to adapt current 152 // current boundingbox 153 createLazyRasterDisplayElements(); 154 } else if ( layer instanceof OWSRasterLayer ) { 155 createOWSRasterDisplayElements(); 156 } else if ( layer instanceof LazyFeatureLayer ) { 157 createFeatureDisplayElements(); 158 } 159 for ( int i = 0; i < displayElements.size(); i++ ) { 160 DisplayElement de = 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 * @param g to draw upon 172 * @param ids of the id's to render 173 */ 174 public void paint( Graphics g, String[] ids ) { 175 176 double scale = parent.getScale(); 177 178 if ( layer instanceof LazyRasterLayer ) { 179 // re-create raster displayelements to adapt current 180 // current boundingbox 181 createLazyRasterDisplayElements(); 182 } else if ( layer instanceof LazyFeatureLayer ) { 183 createFeatureDisplayElements(); 184 } 185 for ( int k = 0; k < displayElements.size(); k++ ) { 186 DisplayElement de = displayElements.get( k ); 187 for ( int i = 0; i < ids.length; i++ ) { 188 if ( de.getAssociateFeatureId().equals( ids[i] ) ) { 189 de.paint( g, parent.getProjection(), scale ); 190 break; 191 } 192 } 193 } 194 } 195 196 /** 197 * renders the selected display elements of the layer 198 * @param g to draw upon 199 */ 200 public void paintSelected( Graphics g ) { 201 202 double scale = parent.getScale(); 203 204 if ( layer instanceof LazyRasterLayer ) { 205 // re-create raster displayelements to adapt current 206 // current boundingbox 207 createLazyRasterDisplayElements(); 208 } else if ( layer instanceof LazyFeatureLayer ) { 209 createFeatureDisplayElements(); 210 } 211 212 // if ( layer instanceof OWSRasterLayer ) { 213 // // 214 // 215 // } 216 217 for ( int i = 0; i < displayElements.size(); i++ ) { 218 DisplayElement de = displayElements.get( i ); 219 if ( de.isSelected() ) { 220 de.paint( g, parent.getProjection(), scale ); 221 } 222 } 223 224 } 225 226 /** 227 * renders the highlighted display elements of the layer 228 * @param g to draw upon 229 */ 230 public void paintHighlighted( Graphics g ) { 231 232 double scale = parent.getScale(); 233 234 if ( layer instanceof LazyRasterLayer ) { 235 // re-create raster displayelements to adapt current 236 // current boundingbox 237 createLazyRasterDisplayElements(); 238 } else if ( layer instanceof LazyFeatureLayer ) { 239 createFeatureDisplayElements(); 240 } 241 242 for ( int i = 0; i < displayElements.size(); i++ ) { 243 DisplayElement de = displayElements.get( i ); 244 if ( de.isHighlighted() ) { 245 de.paint( g, parent.getProjection(), scale ); 246 } 247 } 248 249 } 250 251 /** 252 * A selector is a class that offers methods for selecting and de-selecting single 253 * DisplayElements or groups of DisplayElements. A selector may offers methods like 'select all 254 * DisplayElements within a specified bounding box' or 'select all DisplayElements thats area is 255 * larger than 120 km' etc. 256 * @param selector to which this theme will be added and vice versa 257 */ 258 public void addSelector( Selector selector ) { 259 this.selector.add( selector ); 260 selector.addTheme( this ); 261 } 262 263 /** 264 * @param selector to remove this theme from (and viceversa) 265 * @see org.deegree.graphics.Theme#addSelector(Selector) 266 */ 267 public void removeSelector( Selector selector ) { 268 this.selector.remove( selector ); 269 selector.removeTheme( this ); 270 } 271 272 /** 273 * A Highlighter is a class that is responsible for managing the highlight capabilities for one 274 * or more Themes. 275 * @param highlighter to add this theme to, and vice-versa 276 */ 277 public void addHighlighter( Highlighter highlighter ) { 278 this.highlighter.add( highlighter ); 279 highlighter.addTheme( this ); 280 } 281 282 /** 283 * @param highlighter to remove this theme from and vice-versa 284 * @see org.deegree.graphics.Theme#addHighlighter(Highlighter) 285 */ 286 public void removeHighlighter( Highlighter highlighter ) { 287 this.highlighter.remove( highlighter ); 288 highlighter.removeTheme( this ); 289 } 290 291 /** 292 * adds an eventcontroller to the theme that's responsible for handling events that targets the 293 * theme. 294 * @param controller to add this theme to, and vice-versa 295 */ 296 public void addEventController( ThemeEventController controller ) { 297 eventController.add( controller ); 298 controller.addTheme( this ); 299 } 300 301 /** 302 * @param controller to remove this theme from and vice-versa 303 * @see org.deegree.graphics.Theme#addEventController(ThemeEventController) 304 */ 305 public void removeEventController( ThemeEventController controller ) { 306 eventController.remove( controller ); 307 controller.removeTheme( this ); 308 } 309 310 /** 311 * Sets the styles used for this <tt>Theme</tt>. If this method will be called all 312 * <tt>DisplayElement</tt>s will be recreated to consider the new style definitions. 313 * @param styles the style to set to this theme 314 * 315 */ 316 public void setStyles( UserStyle[] styles ) { 317 318 this.styles = styles; 319 displayElements.clear(); 320 if ( layer instanceof FeatureLayer ) { 321 createFeatureDisplayElements(); 322 } else if ( layer instanceof RasterLayer ) { 323 createRasterDisplayElements(); 324 } else { 325 createLazyRasterDisplayElements(); 326 } 327 328 } 329 330 /** 331 * creates <code>DisplayElement</code>s for <code>Feature</code> instances 332 */ 333 private void createFeatureDisplayElements() { 334 displayElements.clear(); 335 DisplayElementFactory fac = new DisplayElementFactory(); 336 // keep LabelDisplayElements separate from the other elements 337 // and append them to the end of the DisplayElement-list 338 List<DisplayElement> labelDisplayElements = new ArrayList<DisplayElement>( 100 ); 339 try { 340 // instance of FeatureLayer 341 int cnt = ( (FeatureLayer) layer ).getSize(); 342 for ( int i = 0; i < cnt; i++ ) { 343 Feature feature = ( (FeatureLayer) layer ).getFeature( i ); 344 featureToDisplayElement( styles, fac, labelDisplayElements, feature ); 345 } 346 } catch ( Exception e ) { 347 LOG.logError( e.getMessage(), e ); 348 } 349 displayElements.addAll( labelDisplayElements ); 350 } 351 352 /** 353 * creates <code>DisplayElement</code>s for <code>GridCoverage</code> instances 354 */ 355 private void createRasterDisplayElements() { 356 displayElements.clear(); 357 DisplayElementFactory fac = new DisplayElementFactory(); 358 try { 359 // instance of RasterLayer 360 RasterLayer rl = (RasterLayer) layer; 361 DisplayElement[] de = fac.createDisplayElement( rl.getRaster(), styles, rl.getRequest() ); 362 for ( int k = 0; k < de.length; k++ ) { 363 displayElements.add( de[k] ); 364 } 365 } catch ( Exception e ) { 366 LOG.logError( e.getMessage(), e ); 367 } 368 } 369 370 /** 371 * creates <code>DisplayElement</code>s for <code>GridCoverage</code> instances that are 372 * loaded depending on current boundingbox. 373 */ 374 private void createLazyRasterDisplayElements() { 375 displayElements.clear(); 376 DisplayElementFactory fac = new DisplayElementFactory(); 377 try { 378 if ( parent != null ) { 379 LazyRasterLayer rl = (LazyRasterLayer) layer; 380 double w = parent.getProjection().getDestRect().getWidth(); 381 double d = parent.getBoundingBox().getWidth() / w; 382 GridCoverage gc = rl.getRaster( parent.getBoundingBox(), d ); 383 // gc can be null if e.g. the area covered by the raster 384 // layer is outside the visible area. 385 if ( gc != null ) { 386 DisplayElement[] de = fac.createDisplayElement( gc, styles ); 387 for ( int k = 0; k < de.length; k++ ) { 388 displayElements.add( de[k] ); 389 } 390 } 391 } 392 } catch ( Exception e ) { 393 LOG.logError( e.getMessage(), e ); 394 throw new RuntimeException( StringTools.stackTraceToString( e ), e ); 395 } 396 } 397 398 private void createOWSRasterDisplayElements() { 399 displayElements.clear(); 400 401 DisplayElementFactory fac = new DisplayElementFactory(); 402 try { 403 if ( parent != null ) { 404 OWSRasterLayer rl = (OWSRasterLayer) layer; 405 double w = parent.getProjection().getDestRect().getWidth(); 406 double h = parent.getProjection().getDestRect().getHeight(); 407 GridCoverage gc = rl.getRaster( parent.getBoundingBox(), w, h ); 408 if ( gc != null ) { 409 DisplayElement[] de = fac.createDisplayElement( gc, styles ); 410 for ( int k = 0; k < de.length; k++ ) { 411 displayElements.add( de[k] ); 412 } 413 } 414 } 415 } catch ( Exception e ) { 416 LOG.logError( e.getMessage(), e ); 417 } 418 } 419 420 /** 421 * 422 * @param styles 423 * @param fac 424 * @param labelDisplayElements 425 * @param feature 426 * @throws ClassNotFoundException 427 * @throws IllegalAccessException 428 * @throws InstantiationException 429 * @throws NoSuchMethodException 430 * @throws InvocationTargetException 431 * @throws GeometryException 432 * @throws PropertyPathResolvingException 433 */ 434 private void featureToDisplayElement( UserStyle[] styles, DisplayElementFactory fac, 435 List<DisplayElement> labelDisplayElements, Feature feature ) 436 throws ClassNotFoundException, IllegalAccessException, InstantiationException, 437 NoSuchMethodException, InvocationTargetException, GeometryException, 438 PropertyPathResolvingException { 439 DisplayElement[] de = fac.createDisplayElement( feature, styles ); 440 for ( int k = 0; k < de.length; k++ ) { 441 if ( de[k] instanceof LabelDisplayElement ) { 442 labelDisplayElements.add( de[k] ); 443 } else { 444 displayElements.add( de[k] ); 445 } 446 } 447 FeatureProperty[] fp = feature.getProperties(); 448 for ( int i = 0; i < fp.length; i++ ) { 449 if ( fp[i].getValue() != null && fp[i].getValue() instanceof Feature ) { 450 featureToDisplayElement( styles, fac, labelDisplayElements, (Feature) fp[i].getValue() ); 451 } 452 } 453 } 454 455 /** 456 * returns the styles used for this <tt>Theme</tt>. 457 * @return the styles used for this <tt>Theme</tt>. 458 * 459 */ 460 public UserStyle[] getStyles() { 461 return styles; 462 } 463 464 /** 465 * returns the layer that holds the data of the theme 466 * @return the layer that holds the data of the theme 467 * 468 */ 469 public Layer getLayer() { 470 return layer; 471 } 472 473 /** 474 * Returns all <tt>DisplayElements</tt> that this <tt>Theme</tt> contains. 475 * <p> 476 * 477 * @return <tt>ArrayList</tt> containing <tt>DisplayElements</tt> 478 * 479 */ 480 public List<DisplayElement> getDisplayElements() { 481 return displayElements; 482 } 483 484 /** 485 * returns the <tt>DisplayElements</tt> of the Theme 486 * @param de to set to this theme. 487 * 488 */ 489 public void setDisplayElements( List<DisplayElement> de ) { 490 this.displayElements = de; 491 } 492 493 }