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