001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/graphics/Theme.java $
002 /*---------------- FILE HEADER ------------------------------------------
003
004 This file is part of deegree.
005 Copyright (C) 2001-2008 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 a portrayal model for
066 * their graphical representation. Considering the OGC Styled Layer Descriptor specification this is
067 * not nessecary the case. In confirmation with the SLD a theme can be build from a lot of thematic
068 * completly different feature types.
069 * <p>
070 * </p>
071 * From a theoretical point of view this isn't very satisfying. But it will be supported by the
072 * <tt>Theme</tt> class.
073 * <p>
074 * </p>
075 * Assigned to the Theme are:
076 * <ul>
077 * <li>a Layer that contains the data (features)
078 * <li>a Portrayal model that determines how the features shall be rendered
079 * <li>a Selector that offers method for selection and de-selection of features
080 * <li>a event listener that handles event occuring on a theme that's for usual part of a map.
081 * </ul>
082 *
083 *
084 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
085 * @author last edited by: $Author: apoth $
086 *
087 * @version. $Revision: 9340 $, $Date: 2007-12-27 13:32:12 +0100 (Do, 27 Dez 2007) $
088 */
089 public class Theme {
090
091 private static final ILogger LOG = LoggerFactory.getLogger( Theme.class );
092
093 private String name = null;
094
095 private Layer layer = null;
096
097 private UserStyle[] styles = null;
098
099 private List<DisplayElement> displayElements = null;
100
101 /**
102 * the MapView (map) the theme is associated to
103 *
104 */
105 private MapView parent = null;
106
107 /**
108 * this ArrayList contains all DisplayElements (and so the features) that are marked as
109 * selected.
110 */
111 private List<Selector> selector = Collections.synchronizedList( new ArrayList<Selector>() );
112
113 private List<Highlighter> highlighter = Collections.synchronizedList( new ArrayList<Highlighter>() );
114
115 private List<EventController> eventController = Collections.synchronizedList( new ArrayList<EventController>() );
116
117 /**
118 *
119 * @param name
120 * @param layer
121 * @param styles
122 */
123 protected Theme( String name, Layer layer, UserStyle[] styles ) {
124 this.layer = layer;
125 this.name = name;
126 displayElements = new ArrayList<DisplayElement>( 1000 );
127 setStyles( styles );
128 }
129
130 /**
131 * sets the parent MapView of the Theme.
132 *
133 */
134 public void setParent( MapView parent ) {
135 this.parent = parent;
136 }
137
138 /**
139 * returns the name of the layer
140 *
141 */
142 public String getName() {
143 return name;
144 }
145
146 /**
147 * renders the layer to the submitted graphic context
148 */
149 public void paint( Graphics g ) {
150
151 double scale = parent.getScale();
152
153 if ( layer instanceof LazyRasterLayer ) {
154 // re-create raster displayelements to adapt current
155 // current boundingbox
156 createLazyRasterDisplayElements();
157 } else if ( layer instanceof OWSRasterLayer ) {
158 createOWSRasterDisplayElements();
159 }
160 for ( int i = 0; i < displayElements.size(); i++ ) {
161 DisplayElement de = displayElements.get( i );
162
163 if ( de.doesScaleConstraintApply( scale ) ) {
164 de.paint( g, parent.getProjection(), scale );
165 }
166 }
167
168 }
169
170 /**
171 * renders the display elements matching the submitted ids
172 */
173 public void paint( Graphics g, String[] ids ) {
174
175 double scale = parent.getScale();
176
177 if ( layer instanceof LazyRasterLayer ) {
178 // re-create raster displayelements to adapt current
179 // current boundingbox
180 createLazyRasterDisplayElements();
181 }
182
183 for ( int k = 0; k < displayElements.size(); k++ ) {
184 DisplayElement de = displayElements.get( k );
185 for ( int i = 0; i < ids.length; i++ ) {
186 if ( de.getAssociateFeatureId().equals( ids[i] ) ) {
187 de.paint( g, parent.getProjection(), scale );
188 break;
189 }
190 }
191 }
192 }
193
194 /**
195 * renders the selected display elements of the layer
196 */
197 public void paintSelected( Graphics g ) {
198
199 double scale = parent.getScale();
200
201 if ( layer instanceof LazyRasterLayer ) {
202 // re-create raster displayelements to adapt current
203 // current boundingbox
204 createLazyRasterDisplayElements();
205 }
206
207 if ( layer instanceof OWSRasterLayer ) {
208
209 }
210
211 for ( int i = 0; i < displayElements.size(); i++ ) {
212 DisplayElement de = displayElements.get( i );
213 if ( de.isSelected() ) {
214 de.paint( g, parent.getProjection(), scale );
215 }
216 }
217
218 }
219
220 /**
221 * renders the highlighted display elements of the layer
222 */
223 public void paintHighlighted( Graphics g ) {
224
225 double scale = parent.getScale();
226
227 if ( layer instanceof LazyRasterLayer ) {
228 // re-create raster displayelements to adapt current
229 // current boundingbox
230 createLazyRasterDisplayElements();
231 }
232
233 for ( int i = 0; i < displayElements.size(); i++ ) {
234 DisplayElement de = displayElements.get( i );
235 if ( de.isHighlighted() ) {
236 de.paint( g, parent.getProjection(), scale );
237 }
238 }
239
240 }
241
242 /**
243 * A selector is a class that offers methods for selecting and deselecting single
244 * DisplayElements or groups of DisplayElements. A selector may offers methods like 'select all
245 * DisplayElements within a specified bounding box' or 'select all DisplayElements thats area is
246 * 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 capabilities for one
263 * 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 events that targets the
280 * 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 called all
297 * <tt>DisplayElement</tt>s will be recreated to consider the new style definitions.
298 *
299 */
300 public void setStyles( UserStyle[] styles ) {
301
302 this.styles = styles;
303 displayElements.clear();
304 if ( layer instanceof FeatureLayer ) {
305 createFeatureDisplayElements();
306 } else if ( layer instanceof RasterLayer ) {
307 createRasterDisplayElements();
308 } else {
309 createLazyRasterDisplayElements();
310 }
311
312 }
313
314 /**
315 * creates <code>DisplayElement</code>s for <code>Feature</code> instances
316 */
317 private void createFeatureDisplayElements() {
318 displayElements.clear();
319 DisplayElementFactory fac = new DisplayElementFactory();
320 // keep LabelDisplayElements separate from the other elements
321 // and append them to the end of the DisplayElement-list
322 List<DisplayElement> labelDisplayElements = new ArrayList<DisplayElement>( 100 );
323 try {
324 // instance of FeatureLayer
325 for ( int i = 0; i < ( (FeatureLayer) layer ).getSize(); i++ ) {
326 Feature feature = ( (FeatureLayer) layer ).getFeature( i );
327 featureToDisplayElement( styles, fac, labelDisplayElements, feature );
328 }
329 } catch ( Exception e ) {
330 LOG.logError( e.getMessage(), e );
331 }
332 displayElements.addAll( labelDisplayElements );
333 }
334
335 /**
336 * creates <code>DisplayElement</code>s for <code>GridCoverage</code> instances
337 */
338 private void createRasterDisplayElements() {
339 displayElements.clear();
340 DisplayElementFactory fac = new DisplayElementFactory();
341 try {
342 // instance of RasterLayer
343 RasterLayer rl = (RasterLayer) layer;
344 DisplayElement[] de = fac.createDisplayElement( rl.getRaster(), styles );
345 for ( int k = 0; k < de.length; k++ ) {
346 displayElements.add( de[k] );
347 }
348 } catch ( Exception e ) {
349 LOG.logError( e.getMessage(), e );
350 }
351 }
352
353 /**
354 * creates <code>DisplayElement</code>s for <code>GridCoverage</code> instances that are
355 * loaded depending on current boundingbox.
356 */
357 private void createLazyRasterDisplayElements() {
358 displayElements.clear();
359 DisplayElementFactory fac = new DisplayElementFactory();
360 try {
361 if ( parent != null ) {
362 LazyRasterLayer rl = (LazyRasterLayer) layer;
363 double w = parent.getProjection().getDestRect().getWidth();
364 double d = parent.getBoundingBox().getWidth() / w;
365 GridCoverage gc = rl.getRaster( parent.getBoundingBox(), d );
366 // gc can be null if e.g. the area covered by the raster
367 // layer is outside the visible area.
368 if ( gc != null ) {
369 DisplayElement[] de = fac.createDisplayElement( gc, styles );
370 for ( int k = 0; k < de.length; k++ ) {
371 displayElements.add( de[k] );
372 }
373 }
374 }
375 } catch ( Exception e ) {
376 LOG.logError( e.getMessage(), e );
377 }
378 }
379
380 private void createOWSRasterDisplayElements() {
381 displayElements.clear();
382
383 DisplayElementFactory fac = new DisplayElementFactory();
384 try {
385 if ( parent != null ) {
386 OWSRasterLayer rl = (OWSRasterLayer) layer;
387 double w = parent.getProjection().getDestRect().getWidth();
388 double h = parent.getProjection().getDestRect().getHeight();
389 GridCoverage gc = rl.getRaster( parent.getBoundingBox(), w, h );
390 if ( gc != null ) {
391 DisplayElement[] de = fac.createDisplayElement( gc, styles );
392 for ( int k = 0; k < de.length; k++ ) {
393 displayElements.add( de[k] );
394 }
395 }
396 }
397 } catch ( Exception e ) {
398 LOG.logError( e.getMessage(), e );
399 }
400 }
401
402 /**
403 *
404 * @param styles
405 * @param fac
406 * @param labelDisplayElements
407 * @param feature
408 * @throws ClassNotFoundException
409 * @throws IllegalAccessException
410 * @throws InstantiationException
411 * @throws NoSuchMethodException
412 * @throws InvocationTargetException
413 * @throws GeometryException
414 * @throws PropertyPathResolvingException
415 */
416 private void featureToDisplayElement( UserStyle[] styles, DisplayElementFactory fac,
417 List<DisplayElement> labelDisplayElements, Feature feature )
418 throws ClassNotFoundException, IllegalAccessException, InstantiationException,
419 NoSuchMethodException, InvocationTargetException, GeometryException,
420 PropertyPathResolvingException {
421 DisplayElement[] de = fac.createDisplayElement( feature, styles );
422 for ( int k = 0; k < de.length; k++ ) {
423 if ( de[k] instanceof LabelDisplayElement ) {
424 labelDisplayElements.add( de[k] );
425 } else {
426 displayElements.add( de[k] );
427 }
428 }
429 FeatureProperty[] fp = feature.getProperties();
430 for ( int i = 0; i < fp.length; i++ ) {
431 if ( fp[i].getValue() != null && fp[i].getValue() instanceof Feature ) {
432 featureToDisplayElement( styles, fac, labelDisplayElements, (Feature) fp[i].getValue() );
433 }
434 }
435 }
436
437 /**
438 * returns the styles used for this <tt>Theme</tt>.
439 *
440 */
441 public UserStyle[] getStyles() {
442 return styles;
443 }
444
445 /**
446 * returns the layer that holds the data of the theme
447 *
448 */
449 public Layer getLayer() {
450 return layer;
451 }
452
453 /**
454 * Returns all <tt>DisplayElements</tt> that this <tt>Theme</tt> contains.
455 * <p>
456 *
457 * @return <tt>ArrayList</tt> containing <tt>DisplayElements</tt>
458 *
459 */
460 public List<DisplayElement> getDisplayElements() {
461 return displayElements;
462 }
463
464 /**
465 * returns the <tt>DisplayElements</tt> of the Theme
466 *
467 */
468 public void setDisplayElements( List<DisplayElement> de ) {
469 this.displayElements = de;
470 }
471
472 }