001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/graphics/MapView.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.awt.Graphics2D; 048 import java.util.ArrayList; 049 import java.util.Collections; 050 import java.util.HashMap; 051 import java.util.Iterator; 052 import java.util.List; 053 054 import org.deegree.framework.log.ILogger; 055 import org.deegree.framework.log.LoggerFactory; 056 import org.deegree.framework.util.MapUtils; 057 import org.deegree.framework.util.StringTools; 058 import org.deegree.graphics.displayelements.DisplayElement; 059 import org.deegree.graphics.optimizers.AbstractOptimizer; 060 import org.deegree.graphics.optimizers.Optimizer; 061 import org.deegree.graphics.transformation.GeoTransform; 062 import org.deegree.graphics.transformation.WorldToScreenTransform; 063 import org.deegree.model.crs.CRSFactory; 064 import org.deegree.model.crs.CoordinateSystem; 065 import org.deegree.model.crs.UnknownCRSException; 066 import org.deegree.model.spatialschema.Envelope; 067 068 /** 069 * This interface describes the data model of the map itself. It is built from themes containing 070 * {@link DisplayElement}s to be rendered. Themes can be added and removed. Existing themes can be 071 * re-arragned by changing their order. 072 * 073 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 074 * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider</a> 075 * @author last edited by: $Author: bezema $ 076 * 077 * @version $Revision: 6259 $, $Date: 2007-03-20 10:15:15 +0100 (Di, 20 Mär 2007) $ 078 */ 079 public class MapView { 080 081 private static final ILogger LOG = LoggerFactory.getLogger( MapView.class ); 082 083 private String name = null; 084 085 private HashMap themes = null; 086 087 private HashMap enabled = null; 088 089 private ArrayList themesL = null; 090 091 private Theme activatedTh = null; 092 093 private Envelope boundingbox = null; 094 095 private CoordinateSystem crs = null; 096 097 private List eventCntr = Collections.synchronizedList( new ArrayList() ); 098 099 private double scale; 100 101 private GeoTransform projection = new WorldToScreenTransform(); 102 103 // list of Optimizers that are processed at the beginning of the paint ()-call 104 private ArrayList optimizers = new ArrayList(); 105 106 /** 107 * 108 * @param name 109 * @param boundingbox 110 * @throws UnknownCRSException 111 */ 112 MapView( String name, Envelope boundingbox ) throws UnknownCRSException { 113 this.name = name; 114 themes = new HashMap(); 115 themesL = new ArrayList(); 116 enabled = new HashMap(); 117 setBoundingBox( boundingbox ); 118 crs = CRSFactory.create( "EPSG:4326" ); 119 } 120 121 MapView( String name, Envelope boundingbox, CoordinateSystem crs ) { 122 this.name = name; 123 themes = new HashMap(); 124 themesL = new ArrayList(); 125 enabled = new HashMap(); 126 setBoundingBox( boundingbox ); 127 this.crs = crs; 128 } 129 130 /** 131 * returns the name of the map 132 * 133 */ 134 public String getName() { 135 return name; 136 } 137 138 /** 139 * returns the Theme that matches the submitted name 140 */ 141 public Theme getTheme( String name ) { 142 return (Theme) themes.get( name ); 143 } 144 145 /** 146 * returns the Theme that matches the submitted index 147 */ 148 public Theme getTheme( int index ) { 149 return (Theme) themesL.get( index ); 150 } 151 152 /** 153 * returns the Themes in correct order. The first Theme (index == 0) shall be rendered at first 154 * (bottom most). 155 */ 156 public Theme[] getAllThemes() { 157 return (Theme[]) themesL.toArray( new Theme[themesL.size()] ); 158 } 159 160 /** 161 * Returns the current scale of the MapView. 162 * 163 */ 164 public double getScale() { 165 return scale; 166 } 167 168 /** 169 * Returns the current scale of the MapView. 170 */ 171 public double getScale( Graphics g ) 172 throws Exception { 173 return MapUtils.calcScale( g.getClipBounds().width, g.getClipBounds().height, 174 getBoundingBox(), getCoordinatesSystem(), 0.00028 ); 175 } 176 177 /** 178 * adds a theme to the MapView 179 */ 180 public void addTheme( Theme theme ) 181 throws Exception { 182 themes.put( theme.getName(), theme ); 183 themesL.add( theme ); 184 enabled.put( theme.getName(), Boolean.TRUE ); 185 activatedTh = theme; 186 theme.setParent( this ); 187 theme.getLayer().setCoordinatesSystem( crs ); 188 } 189 190 /** 191 * removes a theme from the MapView 192 */ 193 public void removeTheme( Theme theme ) { 194 enabled.remove( theme.getName() ); 195 themesL.remove( themesL.indexOf( theme ) ); 196 themes.remove( theme.getName() ); 197 } 198 199 /** 200 * removes the theme that matches the submitted name from the MapView 201 */ 202 public void removeTheme( String name ) { 203 removeTheme( getTheme( name ) ); 204 } 205 206 /** 207 * removes the theme that matches the submitted index from the MapView 208 */ 209 public void removeTheme( int index ) { 210 removeTheme( (Theme) themesL.get( index ) ); 211 } 212 213 /** 214 * removes all themes from the MapView. 215 */ 216 public void clear() { 217 themes.clear(); 218 themesL.clear(); 219 enabled.clear(); 220 activatedTh = null; 221 } 222 223 /** 224 * swaps the positions of the submitted themes 225 */ 226 public void swapThemes( Theme first, Theme second ) { 227 228 if ( themesL.contains( first ) && themesL.contains( second ) ) { 229 int i1 = themesL.indexOf( first ); 230 int i2 = themesL.indexOf( second ); 231 themesL.set( i1, second ); 232 themesL.set( i2, first ); 233 } 234 235 } 236 237 /** 238 * move a theme up for one index position (index = oldindex + 1) 239 */ 240 public void moveUp( Theme theme ) { 241 242 int idx = themesL.indexOf( theme ); 243 if ( idx < themesL.size() - 1 ) { 244 Theme th = (Theme) themesL.get( idx + 1 ); 245 swapThemes( theme, th ); 246 } 247 248 } 249 250 /** 251 * move a theme down for one index position (index = oldindex - 1) 252 */ 253 public void moveDown( Theme theme ) { 254 255 int idx = themesL.indexOf( theme ); 256 if ( idx > 0 ) { 257 Theme th = (Theme) themesL.get( idx - 1 ); 258 swapThemes( theme, th ); 259 } 260 261 } 262 263 /** 264 * enables or disables a theme that is part of the MapView. A theme that has been disabled won't 265 * be rendered and usually doesn't react to events targeted to the MapView, but still is part of 266 * the MapView. 267 */ 268 public void enableTheme( Theme theme, boolean enable ) { 269 enabled.put( theme.getName(), enable ? Boolean.TRUE : Boolean.FALSE ); 270 } 271 272 /** 273 * returns true if the passed theme is set to be enabled 274 */ 275 public boolean isThemeEnabled( Theme theme ) { 276 return ( (Boolean) enabled.get( theme.getName() ) ).booleanValue(); 277 } 278 279 /** 280 * activates a theme. Usually the activated theme is perferred to react to events (this doesn't 281 * mean that other themes are not allowed to react to events). 282 */ 283 public void activateTheme( Theme theme ) { 284 activatedTh = theme; 285 } 286 287 /** 288 * returns true if the passed theme is the one that is set to be activated 289 */ 290 public boolean isThemeActivated( Theme theme ) { 291 return activatedTh.getName().equals( theme.getName() ); 292 } 293 294 /** 295 * returns the amount of themes within the MapView. 296 */ 297 public int getSize() { 298 return themes.size(); 299 } 300 301 /** 302 * adds an eventcontroller to the MapView that's reponsible for handling events that targets the 303 * map. E.g.: zooming, panning, selecting a feature etc. 304 */ 305 public void addEventController( MapEventController obj ) { 306 eventCntr.add( obj ); 307 obj.addMapView( this ); 308 } 309 310 /** 311 * @see org.deegree.graphics.MapView#addEventController(MapEventController) 312 */ 313 public void removeEventController( MapEventController obj ) { 314 eventCntr.remove( obj ); 315 obj.removeMapView( this ); 316 } 317 318 /** 319 * A selector is a class that offers methods for selecting and deselecting single 320 * DisplayElements or groups of DisplayElements. A selector may offers methods like 'select all 321 * DisplayElements within a specified bounding box' or 'select all DisplayElements thats area is 322 * larger than 120 km�' etc. 323 */ 324 public void addSelector( Selector obj ) { 325 for ( int i = 0; i < themesL.size(); i++ ) { 326 getTheme( i ).addSelector( obj ); 327 } 328 } 329 330 /** 331 * @see org.deegree.graphics.MapView#addSelector(Selector) 332 */ 333 public void removeSelector( Selector obj ) { 334 for ( int i = 0; i < themesL.size(); i++ ) { 335 getTheme( i ).removeSelector( obj ); 336 } 337 } 338 339 /** 340 * returns the BoundingBox (Envelope) of the MapView. This isn't nessecary the BoundingBox of 341 * the data that will be rendered. It's the boundingBox of the the visible area of the map 342 * measured in its coordinate reference system. 343 */ 344 public Envelope getBoundingBox() { 345 return boundingbox; 346 } 347 348 /** 349 * @see org.deegree.graphics.MapView#getBoundingBox() this method may be used for zooming and 350 * panning the map 351 */ 352 public void setBoundingBox( Envelope boundingbox ) { 353 this.boundingbox = boundingbox; 354 projection.setSourceRect( boundingbox ); 355 } 356 357 /** 358 * returns the coordinate reference system of the MapView 359 */ 360 public CoordinateSystem getCoordinatesSystem() { 361 return crs; 362 } 363 364 /** 365 * sets the coordinate reference system of the map; 366 */ 367 public void setCoordinateSystem( CoordinateSystem crs ) 368 throws Exception { 369 this.crs = crs; 370 for ( int i = 0; i < themesL.size(); i++ ) { 371 Layer lay = getTheme( i ).getLayer(); 372 lay.setCoordinatesSystem( crs ); 373 } 374 } 375 376 /** 377 * renders the map to the passed graphic context 378 * 379 * @param g 380 * @throws RenderException 381 * thrown if the passed <tt>Graphic<tt> haven't 382 * clipbounds. use g.setClip( .. ); 383 */ 384 public void paint( Graphics g ) 385 throws RenderException { 386 387 if ( g.getClipBounds() == null ) { 388 throw new RenderException( "no clip bounds defined for graphic context" ); 389 } 390 391 int x = g.getClipBounds().x; 392 int y = g.getClipBounds().y; 393 int w = g.getClipBounds().width; 394 int h = g.getClipBounds().height; 395 projection.setDestRect( x, y, w + x, h + y ); 396 397 try { 398 LOG.logInfo( "OGC SLD scale denominator " + getScale( g ) ); 399 scale = getScale( g ); 400 // call all Optimizers 401 optimize( g ); 402 } catch ( Exception e ) { 403 e.printStackTrace(); 404 throw new RenderException( StringTools.stackTraceToString( e ) ); 405 } 406 407 // paint all Themes 408 for ( int i = 0; i < themesL.size(); i++ ) { 409 if ( isThemeEnabled( getTheme( i ) ) ) { 410 getTheme( i ).paint( g ); 411 } 412 } 413 414 } 415 416 /** 417 * renders the features marked as selected of all themes contained within the MapView 418 * 419 * @param g 420 * graphic context to render the map too 421 * @throws RenderException 422 * thrown if the passed <tt>Graphic<tt> haven't 423 * clipbounds. use g.setClip( .. ); 424 */ 425 public void paintSelected( Graphics g ) 426 throws RenderException { 427 428 if ( g.getClipBounds() == null ) { 429 throw new RenderException( "no clip bounds defined for graphic context" ); 430 } 431 432 int x = g.getClipBounds().x; 433 int y = g.getClipBounds().y; 434 int width = g.getClipBounds().width; 435 int height = g.getClipBounds().height; 436 projection.setDestRect( x - 2, y - 2, width + x, height + y ); 437 438 try { 439 // call all Optimizers 440 optimize( g ); 441 } catch ( Exception e ) { 442 throw new RenderException( StringTools.stackTraceToString( e ) ); 443 } 444 445 // paint all Themes 446 for ( int i = 0; i < themesL.size(); i++ ) { 447 if ( isThemeEnabled( getTheme( i ) ) ) { 448 getTheme( i ).paintSelected( g ); 449 } 450 } 451 452 } 453 454 /** 455 * renders the features marked as highlighted of all themes contained within the MapView 456 * 457 * @param g 458 * graphic context to render the map too 459 * @throws RenderException 460 * thrown if the passed <tt>Graphic<tt> haven't 461 * clipbounds. use g.setClip( .. ); 462 */ 463 public void paintHighlighted( Graphics g ) 464 throws RenderException { 465 466 if ( g.getClipBounds() == null ) { 467 throw new RenderException( "no clip bounds defined for graphic context" ); 468 } 469 470 int x = g.getClipBounds().x; 471 int y = g.getClipBounds().y; 472 int width = g.getClipBounds().width; 473 int height = g.getClipBounds().height; 474 projection.setDestRect( x - 2, y - 2, width + x, height + y ); 475 476 try { 477 // call all Optimizers 478 optimize( g ); 479 } catch ( Exception e ) { 480 throw new RenderException( StringTools.stackTraceToString( e ) ); 481 } 482 483 // paint all Themes 484 for ( int i = 0; i < themesL.size(); i++ ) { 485 if ( isThemeEnabled( getTheme( i ) ) ) { 486 getTheme( i ).paintHighlighted( g ); 487 } 488 } 489 490 } 491 492 /** 493 * A Highlighter is a class that is responsible for managing the highlight capabilities for one 494 * or more Themes. 495 */ 496 public void addHighlighter( Highlighter highlighter ) { 497 for ( int i = 0; i < themesL.size(); i++ ) { 498 getTheme( i ).addHighlighter( highlighter ); 499 } 500 } 501 502 /** 503 * @see org.deegree.graphics.MapView#addHighlighter(Highlighter) 504 */ 505 public void removeHighlighter( Highlighter highlighter ) { 506 for ( int i = 0; i < themesL.size(); i++ ) { 507 getTheme( i ).removeHighlighter( highlighter ); 508 } 509 } 510 511 /** 512 * Returns the <tt>GeoTransform</tt> that is associated to this MapView. 513 * <p> 514 * 515 * @return the associated <tt>GeoTransform</tt>-instance 516 * 517 */ 518 public GeoTransform getProjection() { 519 return projection; 520 } 521 522 /** 523 * Calls all registered <tt>Optimizer</tt> subsequently. 524 * 525 * @param g 526 */ 527 private void optimize( Graphics g ) 528 throws Exception { 529 Graphics2D g2 = (Graphics2D) g; 530 Iterator it = optimizers.iterator(); 531 while ( it.hasNext() ) { 532 AbstractOptimizer optimizer = (AbstractOptimizer) it.next(); 533 optimizer.optimize( g2 ); 534 } 535 } 536 537 /** 538 * Adds an <tt>Optimizer</tt>. 539 * 540 * @param optimizer 541 */ 542 public void addOptimizer( Optimizer optimizer ) { 543 optimizers.add( optimizer ); 544 optimizer.setMapView( this ); 545 } 546 547 /** 548 * Returns the <tt>Optimizer</tt>s. 549 * 550 * @return the <tt>Optimizer</tt>s. 551 * 552 */ 553 public Optimizer[] getOptimizers() { 554 return (Optimizer[]) optimizers.toArray( new Optimizer[0] ); 555 } 556 557 /** 558 * Sets the <tt>Optimizer<tt>s. 559 * @param optimizers 560 */ 561 public void setOptimizers( Optimizer[] optimizers ) { 562 this.optimizers.clear(); 563 for ( int i = 0; i < optimizers.length; i++ ) { 564 addOptimizer( optimizers[i] ); 565 } 566 } 567 568 } 569 570 /*************************************************************************************************** 571 * <code> 572 Changes to this class. What the people have been up to: 573 574 $Log$ 575 Revision 1.20 2007/02/13 16:59:00 mschneider 576 Improved javadoc. 577 578 Revision 1.19 2007/01/26 14:20:45 wanhoff 579 fixed Javadoc @return tag and footer 580 581 Revision 1.18 2006/11/27 09:07:52 poth 582 JNI integration of proj4 has been removed. The CRS functionality now will be done by native deegree code. 583 584 Revision 1.17 2006/10/17 20:31:19 poth 585 *** empty log message *** 586 587 Revision 1.16 2006/07/29 08:51:12 poth 588 references to deprecated classes removed 589 590 Revision 1.15 2006/07/25 06:22:19 poth 591 code formatting 592 593 Revision 1.14 2006/07/04 18:30:04 poth 594 footer added 595 596 * </code> 597 **************************************************************************************************/