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