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 **************************************************************************************************/