/*---------------- FILE HEADER ------------------------------------------ This file is part of deegree. Copyright (C) 2001-2006 by: EXSE, Department of Geography, University of Bonn http://www.giub.uni-bonn.de/deegree/ lat/lon GmbH http://www.lat-lon.de This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact: Andreas Poth lat/lon GmbH Aennchenstr. 19 53115 Bonn Germany E-Mail: poth@lat-lon.de Prof. Dr. Klaus Greve Department of Geography University of Bonn Meckenheimer Allee 166 53115 Bonn Germany E-Mail: greve@giub.uni-bonn.de ---------------------------------------------------------------------------*/ package org.deegree.graphics; import java.awt.Graphics; import java.awt.Graphics2D; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import org.deegree.framework.log.ILogger; import org.deegree.framework.log.LoggerFactory; import org.deegree.framework.util.MapUtils; import org.deegree.framework.util.StringTools; import org.deegree.graphics.optimizers.AbstractOptimizer; import org.deegree.graphics.optimizers.Optimizer; import org.deegree.graphics.transformation.GeoTransform; import org.deegree.graphics.transformation.WorldToScreenTransform; import org.deegree.model.crs.CRSFactory; import org.deegree.model.crs.CoordinateSystem; import org.deegree.model.crs.UnknownCRSException; import org.deegree.model.spatialschema.Envelope; /** * This interface describes the data modell of the map it self. It is build * from themes containing DisplayElements to be rendered. Themes can be added * and removed. Existing themes can be re-arragned by changing their order. * * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider</a> * @version $Revision: 1.18 $ $Date: 2006/11/27 09:07:52 $ */ public class MapView { private static final ILogger LOG = LoggerFactory.getLogger( MapView.class ); private String name = null; private HashMap themes = null; private HashMap enabled = null; private ArrayList themesL = null; private Theme activatedTh = null; private Envelope boundingbox = null; private CoordinateSystem crs = null; private List eventCntr = Collections.synchronizedList( new ArrayList() ); private double scale; private GeoTransform projection = new WorldToScreenTransform(); // list of Optimizers that are processed at the beginning of the paint ()-call private ArrayList optimizers = new ArrayList(); /** * * @param name * @param boundingbox * @throws UnknownCRSException */ MapView( String name, Envelope boundingbox ) throws UnknownCRSException { this.name = name; themes = new HashMap(); themesL = new ArrayList(); enabled = new HashMap(); setBoundingBox( boundingbox ); crs = CRSFactory.create( "EPSG:4326" ); } MapView( String name, Envelope boundingbox, CoordinateSystem crs ) { this.name = name; themes = new HashMap(); themesL = new ArrayList(); enabled = new HashMap(); setBoundingBox( boundingbox ); this.crs = crs; } /** * returns the name of the map * */ public String getName() { return name; } /** * returns the Theme that matches the submitted name */ public Theme getTheme( String name ) { return (Theme) themes.get( name ); } /** * returns the Theme that matches the submitted index */ public Theme getTheme( int index ) { return (Theme) themesL.get( index ); } /** * returns the Themes in correct order. The first Theme (index == 0) shall * be rendered at first (bottom most). */ public Theme[] getAllThemes() { return (Theme[]) themesL.toArray( new Theme[themesL.size()] ); } /** * Returns the current scale of the MapView. * */ public double getScale() { return scale; } /** * Returns the current scale of the MapView. */ public double getScale( Graphics g ) throws Exception { return MapUtils.calcScale( g.getClipBounds().width, g.getClipBounds().height, getBoundingBox(), getCoordinatesSystem(), 0.00028 ); } /** * adds a theme to the MapView */ public void addTheme( Theme theme ) throws Exception { themes.put( theme.getName(), theme ); themesL.add( theme ); enabled.put( theme.getName(), Boolean.TRUE ); activatedTh = theme; theme.setParent( this ); theme.getLayer().setCoordinatesSystem( crs ); } /** * removes a theme from the MapView */ public void removeTheme( Theme theme ) { enabled.remove( theme.getName() ); themesL.remove( themesL.indexOf( theme ) ); themes.remove( theme.getName() ); } /** * removes the theme that matches the submitted name from the MapView */ public void removeTheme( String name ) { removeTheme( getTheme( name ) ); } /** * removes the theme that matches the submitted index from the MapView */ public void removeTheme( int index ) { removeTheme( (Theme) themesL.get( index ) ); } /** * removes all themes from the MapView. */ public void clear() { themes.clear(); themesL.clear(); enabled.clear(); activatedTh = null; } /** * swaps the positions of the submitted themes */ public void swapThemes( Theme first, Theme second ) { if ( themesL.contains( first ) && themesL.contains( second ) ) { int i1 = themesL.indexOf( first ); int i2 = themesL.indexOf( second ); themesL.set( i1, second ); themesL.set( i2, first ); } } /** * move a theme up for one index position (index = oldindex + 1) */ public void moveUp( Theme theme ) { int idx = themesL.indexOf( theme ); if ( idx < themesL.size() - 1 ) { Theme th = (Theme) themesL.get( idx + 1 ); swapThemes( theme, th ); } } /** * move a theme down for one index position (index = oldindex - 1) */ public void moveDown( Theme theme ) { int idx = themesL.indexOf( theme ); if ( idx > 0 ) { Theme th = (Theme) themesL.get( idx - 1 ); swapThemes( theme, th ); } } /** * enables or disables a theme that is part of the MapView. A theme * that has been disabled won't be rendered and usually doesn't react to * events targeted to the MapView, but still is part of the MapView. */ public void enableTheme( Theme theme, boolean enable ) { enabled.put( theme.getName(), enable ? Boolean.TRUE : Boolean.FALSE ); } /** * returns true if the passed theme is set to be enabled */ public boolean isThemeEnabled( Theme theme ) { return ( (Boolean) enabled.get( theme.getName() ) ).booleanValue(); } /** * activates a theme. Usually the activated theme is perferred to react * to events (this doesn't mean that other themes are not allowed to * react to events). */ public void activateTheme( Theme theme ) { activatedTh = theme; } /** * returns true if the passed theme is the one that is set to be activated */ public boolean isThemeActivated( Theme theme ) { return activatedTh.getName().equals( theme.getName() ); } /** * returns the amount of themes within the MapView. */ public int getSize() { return themes.size(); } /** * adds an eventcontroller to the MapView that's reponsible for handling * events that targets the map. E.g.: zooming, panning, selecting a feature etc. */ public void addEventController( MapEventController obj ) { eventCntr.add( obj ); obj.addMapView( this ); } /** * @see org.deegree.graphics.MapView#addEventController(MapEventController) */ public void removeEventController( MapEventController obj ) { eventCntr.remove( obj ); obj.removeMapView( this ); } /** * A selector is a class that offers methods for selecting and * deselecting single DisplayElements or groups of DisplayElements. * A selector may offers methods like 'select all DisplayElements * within a specified bounding box' or 'select all DisplayElements * thats area is larger than 120 km�' etc. */ public void addSelector( Selector obj ) { for ( int i = 0; i < themesL.size(); i++ ) { getTheme( i ).addSelector( obj ); } } /** * @see org.deegree.graphics.MapView#addSelector(Selector) */ public void removeSelector( Selector obj ) { for ( int i = 0; i < themesL.size(); i++ ) { getTheme( i ).removeSelector( obj ); } } /** * returns the BoundingBox (Envelope) of the MapView. This isn't nessecary the * BoundingBox of the data that will be rendered. It's the boundingBox of the * the visible area of the map measured in its coordinate reference system. */ public Envelope getBoundingBox() { return boundingbox; } /** * @see org.deegree.graphics.MapView#getBoundingBox() * this method may be used for zooming and panning the map */ public void setBoundingBox( Envelope boundingbox ) { this.boundingbox = boundingbox; projection.setSourceRect( boundingbox ); } /** * returns the coordinate reference system of the MapView */ public CoordinateSystem getCoordinatesSystem() { return crs; } /** * sets the coordinate reference system of the map; */ public void setCoordinateSystem( CoordinateSystem crs ) throws Exception { this.crs = crs; for ( int i = 0; i < themesL.size(); i++ ) { Layer lay = getTheme( i ).getLayer(); lay.setCoordinatesSystem( crs ); } } /** renders the map to the passed graphic context * @param g * @throws RenderException thrown if the passed <tt>Graphic<tt> haven't * clipbounds. use g.setClip( .. ); */ public void paint( Graphics g ) throws RenderException { if ( g.getClipBounds() == null ) { throw new RenderException( "no clip bounds defined for graphic context" ); } int x = g.getClipBounds().x; int y = g.getClipBounds().y; int w = g.getClipBounds().width; int h = g.getClipBounds().height; projection.setDestRect( x , y , w + x, h + y ); try { LOG.logInfo( "OGC SLD scale denominator " + getScale( g ) ); scale = getScale( g ); // call all Optimizers optimize( g ); } catch ( Exception e ) { e.printStackTrace(); throw new RenderException( StringTools.stackTraceToString( e ) ); } // paint all Themes for ( int i = 0; i < themesL.size(); i++ ) { if ( isThemeEnabled( getTheme( i ) ) ) { getTheme( i ).paint( g ); } } } /** renders the features marked as selected of all themes contained within * the MapView * @param g graphic context to render the map too * @throws RenderException thrown if the passed <tt>Graphic<tt> haven't * clipbounds. use g.setClip( .. ); */ public void paintSelected( Graphics g ) throws RenderException { if ( g.getClipBounds() == null ) { throw new RenderException( "no clip bounds defined for graphic context" ); } int x = g.getClipBounds().x; int y = g.getClipBounds().y; int width = g.getClipBounds().width; int height = g.getClipBounds().height; projection.setDestRect( x - 2, y - 2, width + x, height + y ); try { // call all Optimizers optimize( g ); } catch ( Exception e ) { throw new RenderException( StringTools.stackTraceToString( e ) ); } // paint all Themes for ( int i = 0; i < themesL.size(); i++ ) { if ( isThemeEnabled( getTheme( i ) ) ) { getTheme( i ).paintSelected( g ); } } } /** renders the features marked as highlighted of all themes contained within * the MapView * @param g graphic context to render the map too * @throws RenderException thrown if the passed <tt>Graphic<tt> haven't * clipbounds. use g.setClip( .. ); */ public void paintHighlighted( Graphics g ) throws RenderException { if ( g.getClipBounds() == null ) { throw new RenderException( "no clip bounds defined for graphic context" ); } int x = g.getClipBounds().x; int y = g.getClipBounds().y; int width = g.getClipBounds().width; int height = g.getClipBounds().height; projection.setDestRect( x - 2, y - 2, width + x, height + y ); try { // call all Optimizers optimize( g ); } catch ( Exception e ) { throw new RenderException( StringTools.stackTraceToString( e ) ); } // paint all Themes for ( int i = 0; i < themesL.size(); i++ ) { if ( isThemeEnabled( getTheme( i ) ) ) { getTheme( i ).paintHighlighted( g ); } } } /** * A Highlighter is a class that is responsible for managing the highlight * capabilities for one or more Themes. */ public void addHighlighter( Highlighter highlighter ) { for ( int i = 0; i < themesL.size(); i++ ) { getTheme( i ).addHighlighter( highlighter ); } } /** * @see org.deegree.graphics.MapView#addHighlighter(Highlighter) */ public void removeHighlighter( Highlighter highlighter ) { for ( int i = 0; i < themesL.size(); i++ ) { getTheme( i ).removeHighlighter( highlighter ); } } /** * Returns the <tt>GeoTransform</tt> that is associated to this MapView. * <p> * @return the associated <tt>GeoTransform</tt>-instance * */ public GeoTransform getProjection() { return projection; } /** * Calls all registered <tt>Optimizer</tt> subsequently. * @param g */ private void optimize( Graphics g ) throws Exception { Graphics2D g2 = (Graphics2D) g; Iterator it = optimizers.iterator(); while ( it.hasNext() ) { AbstractOptimizer optimizer = (AbstractOptimizer) it.next(); optimizer.optimize( g2 ); } } /** * Adds an <tt>Optimizer</tt>. * @param optimizer */ public void addOptimizer( Optimizer optimizer ) { optimizers.add( optimizer ); optimizer.setMapView( this ); } /** * Returns the <tt>Optimizer</tt>s. * @return * */ public Optimizer[] getOptimizers() { return (Optimizer[]) optimizers.toArray( new Optimizer[0] ); } /** * Sets the <tt>Optimizer<tt>s. * @param optimizers */ public void setOptimizers( Optimizer[] optimizers ) { this.optimizers.clear(); for ( int i = 0; i < optimizers.length; i++ ) { addOptimizer( optimizers[i] ); } } } /* ******************************************************************** Changes to this class. What the people have been up to: $Log: MapView.java,v $ Revision 1.18 2006/11/27 09:07:52 poth JNI integration of proj4 has been removed. The CRS functionality now will be done by native deegree code. Revision 1.17 2006/10/17 20:31:19 poth *** empty log message *** Revision 1.16 2006/07/29 08:51:12 poth references to deprecated classes removed Revision 1.15 2006/07/25 06:22:19 poth code formatting Revision 1.14 2006/07/04 18:30:04 poth footer added ********************************************************************** */