001: //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/trunk/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: mschneider $
086: *
087: * @version $Revision: 10547 $, $Date: 2008-03-11 01:40:28 -0700 (Tue, 11 Mar 2008) $
088: */
089: public class Theme {
090:
091: private static final ILogger LOG = LoggerFactory
092: .getLogger(Theme.class);
093:
094: private String name = null;
095:
096: private Layer layer = null;
097:
098: private UserStyle[] styles = null;
099:
100: private List<DisplayElement> displayElements = null;
101:
102: /**
103: * the MapView (map) the theme is associated to
104: *
105: */
106: private MapView parent = null;
107:
108: /**
109: * this ArrayList contains all DisplayElements (and so the features) that are marked as
110: * selected.
111: */
112: private List<Selector> selector = Collections
113: .synchronizedList(new ArrayList<Selector>());
114:
115: private List<Highlighter> highlighter = Collections
116: .synchronizedList(new ArrayList<Highlighter>());
117:
118: private List<EventController> eventController = Collections
119: .synchronizedList(new ArrayList<EventController>());
120:
121: /**
122: *
123: * @param name
124: * @param layer
125: * @param styles
126: */
127: protected Theme(String name, Layer layer, UserStyle[] styles) {
128: this .layer = layer;
129: this .name = name;
130: displayElements = new ArrayList<DisplayElement>(1000);
131: setStyles(styles);
132: }
133:
134: /**
135: * sets the parent MapView of the Theme.
136: *
137: */
138: public void setParent(MapView parent) {
139: this .parent = parent;
140: }
141:
142: /**
143: * returns the name of the layer
144: *
145: */
146: public String getName() {
147: return name;
148: }
149:
150: /**
151: * renders the layer to the submitted graphic context
152: */
153: public void paint(Graphics g) {
154:
155: double scale = parent.getScale();
156:
157: if (layer instanceof LazyRasterLayer) {
158: // re-create raster displayelements to adapt current
159: // current boundingbox
160: createLazyRasterDisplayElements();
161: } else if (layer instanceof OWSRasterLayer) {
162: createOWSRasterDisplayElements();
163: }
164: for (int i = 0; i < displayElements.size(); i++) {
165: DisplayElement de = displayElements.get(i);
166:
167: if (de.doesScaleConstraintApply(scale)) {
168: de.paint(g, parent.getProjection(), scale);
169: }
170: }
171:
172: }
173:
174: /**
175: * renders the display elements matching the submitted ids
176: */
177: public void paint(Graphics g, String[] ids) {
178:
179: double scale = parent.getScale();
180:
181: if (layer instanceof LazyRasterLayer) {
182: // re-create raster displayelements to adapt current
183: // current boundingbox
184: createLazyRasterDisplayElements();
185: }
186:
187: for (int k = 0; k < displayElements.size(); k++) {
188: DisplayElement de = displayElements.get(k);
189: for (int i = 0; i < ids.length; i++) {
190: if (de.getAssociateFeatureId().equals(ids[i])) {
191: de.paint(g, parent.getProjection(), scale);
192: break;
193: }
194: }
195: }
196: }
197:
198: /**
199: * renders the selected display elements of the layer
200: */
201: public void paintSelected(Graphics g) {
202:
203: double scale = parent.getScale();
204:
205: if (layer instanceof LazyRasterLayer) {
206: // re-create raster displayelements to adapt current
207: // current boundingbox
208: createLazyRasterDisplayElements();
209: }
210:
211: if (layer instanceof OWSRasterLayer) {
212:
213: }
214:
215: for (int i = 0; i < displayElements.size(); i++) {
216: DisplayElement de = displayElements.get(i);
217: if (de.isSelected()) {
218: de.paint(g, parent.getProjection(), scale);
219: }
220: }
221:
222: }
223:
224: /**
225: * renders the highlighted display elements of the layer
226: */
227: public void paintHighlighted(Graphics g) {
228:
229: double scale = parent.getScale();
230:
231: if (layer instanceof LazyRasterLayer) {
232: // re-create raster displayelements to adapt current
233: // current boundingbox
234: createLazyRasterDisplayElements();
235: }
236:
237: for (int i = 0; i < displayElements.size(); i++) {
238: DisplayElement de = displayElements.get(i);
239: if (de.isHighlighted()) {
240: de.paint(g, parent.getProjection(), scale);
241: }
242: }
243:
244: }
245:
246: /**
247: * A selector is a class that offers methods for selecting and deselecting single
248: * DisplayElements or groups of DisplayElements. A selector may offers methods like 'select all
249: * DisplayElements within a specified bounding box' or 'select all DisplayElements thats area is
250: * larger than 120 km�' etc.
251: */
252: public void addSelector(Selector selector) {
253: this .selector.add(selector);
254: selector.addTheme(this );
255: }
256:
257: /**
258: * @see org.deegree.graphics.Theme#addSelector(Selector)
259: */
260: public void removeSelector(Selector selector) {
261: this .selector.remove(selector);
262: selector.removeTheme(this );
263: }
264:
265: /**
266: * A Highlighter is a class that is responsible for managing the highlight capabilities for one
267: * or more Themes.
268: */
269: public void addHighlighter(Highlighter highlighter) {
270: this .highlighter.add(highlighter);
271: highlighter.addTheme(this );
272: }
273:
274: /**
275: * @see org.deegree.graphics.Theme#addHighlighter(Highlighter)
276: */
277: public void removeHighlighter(Highlighter highlighter) {
278: this .highlighter.remove(highlighter);
279: highlighter.removeTheme(this );
280: }
281:
282: /**
283: * adds an eventcontroller to the theme that's reponsible for handling events that targets the
284: * theme.
285: */
286: public void addEventController(ThemeEventController controller) {
287: eventController.add(controller);
288: controller.addTheme(this );
289: }
290:
291: /**
292: * @see org.deegree.graphics.Theme#addEventController(ThemeEventController)
293: */
294: public void removeEventController(ThemeEventController controller) {
295: eventController.remove(controller);
296: controller.removeTheme(this );
297: }
298:
299: /**
300: * Sets the styles used for this <tt>Theme</tt>. If this method will be called all
301: * <tt>DisplayElement</tt>s will be recreated to consider the new style definitions.
302: *
303: */
304: public void setStyles(UserStyle[] styles) {
305:
306: this .styles = styles;
307: displayElements.clear();
308: if (layer instanceof FeatureLayer) {
309: createFeatureDisplayElements();
310: } else if (layer instanceof RasterLayer) {
311: createRasterDisplayElements();
312: } else {
313: createLazyRasterDisplayElements();
314: }
315:
316: }
317:
318: /**
319: * creates <code>DisplayElement</code>s for <code>Feature</code> instances
320: */
321: private void createFeatureDisplayElements() {
322: displayElements.clear();
323: DisplayElementFactory fac = new DisplayElementFactory();
324: // keep LabelDisplayElements separate from the other elements
325: // and append them to the end of the DisplayElement-list
326: List<DisplayElement> labelDisplayElements = new ArrayList<DisplayElement>(
327: 100);
328: try {
329: // instance of FeatureLayer
330: for (int i = 0; i < ((FeatureLayer) layer).getSize(); i++) {
331: Feature feature = ((FeatureLayer) layer).getFeature(i);
332: featureToDisplayElement(styles, fac,
333: labelDisplayElements, feature);
334: }
335: } catch (Exception e) {
336: LOG.logError(e.getMessage(), e);
337: }
338: displayElements.addAll(labelDisplayElements);
339: }
340:
341: /**
342: * creates <code>DisplayElement</code>s for <code>GridCoverage</code> instances
343: */
344: private void createRasterDisplayElements() {
345: displayElements.clear();
346: DisplayElementFactory fac = new DisplayElementFactory();
347: try {
348: // instance of RasterLayer
349: RasterLayer rl = (RasterLayer) layer;
350: DisplayElement[] de = fac.createDisplayElement(rl
351: .getRaster(), styles);
352: for (int k = 0; k < de.length; k++) {
353: displayElements.add(de[k]);
354: }
355: } catch (Exception e) {
356: LOG.logError(e.getMessage(), e);
357: }
358: }
359:
360: /**
361: * creates <code>DisplayElement</code>s for <code>GridCoverage</code> instances that are
362: * loaded depending on current boundingbox.
363: */
364: private void createLazyRasterDisplayElements() {
365: displayElements.clear();
366: DisplayElementFactory fac = new DisplayElementFactory();
367: try {
368: if (parent != null) {
369: LazyRasterLayer rl = (LazyRasterLayer) layer;
370: double w = parent.getProjection().getDestRect()
371: .getWidth();
372: double d = parent.getBoundingBox().getWidth() / w;
373: GridCoverage gc = rl.getRaster(parent.getBoundingBox(),
374: d);
375: // gc can be null if e.g. the area covered by the raster
376: // layer is outside the visible area.
377: if (gc != null) {
378: DisplayElement[] de = fac.createDisplayElement(gc,
379: styles);
380: for (int k = 0; k < de.length; k++) {
381: displayElements.add(de[k]);
382: }
383: }
384: }
385: } catch (Exception e) {
386: LOG.logError(e.getMessage(), e);
387: }
388: }
389:
390: private void createOWSRasterDisplayElements() {
391: displayElements.clear();
392:
393: DisplayElementFactory fac = new DisplayElementFactory();
394: try {
395: if (parent != null) {
396: OWSRasterLayer rl = (OWSRasterLayer) layer;
397: double w = parent.getProjection().getDestRect()
398: .getWidth();
399: double h = parent.getProjection().getDestRect()
400: .getHeight();
401: GridCoverage gc = rl.getRaster(parent.getBoundingBox(),
402: w, h);
403: if (gc != null) {
404: DisplayElement[] de = fac.createDisplayElement(gc,
405: styles);
406: for (int k = 0; k < de.length; k++) {
407: displayElements.add(de[k]);
408: }
409: }
410: }
411: } catch (Exception e) {
412: LOG.logError(e.getMessage(), e);
413: }
414: }
415:
416: /**
417: *
418: * @param styles
419: * @param fac
420: * @param labelDisplayElements
421: * @param feature
422: * @throws ClassNotFoundException
423: * @throws IllegalAccessException
424: * @throws InstantiationException
425: * @throws NoSuchMethodException
426: * @throws InvocationTargetException
427: * @throws GeometryException
428: * @throws PropertyPathResolvingException
429: */
430: private void featureToDisplayElement(UserStyle[] styles,
431: DisplayElementFactory fac,
432: List<DisplayElement> labelDisplayElements, Feature feature)
433: throws ClassNotFoundException, IllegalAccessException,
434: InstantiationException, NoSuchMethodException,
435: InvocationTargetException, GeometryException,
436: PropertyPathResolvingException {
437: DisplayElement[] de = fac.createDisplayElement(feature, styles);
438: for (int k = 0; k < de.length; k++) {
439: if (de[k] instanceof LabelDisplayElement) {
440: labelDisplayElements.add(de[k]);
441: } else {
442: displayElements.add(de[k]);
443: }
444: }
445: FeatureProperty[] fp = feature.getProperties();
446: for (int i = 0; i < fp.length; i++) {
447: if (fp[i].getValue() != null
448: && fp[i].getValue() instanceof Feature) {
449: featureToDisplayElement(styles, fac,
450: labelDisplayElements, (Feature) fp[i]
451: .getValue());
452: }
453: }
454: }
455:
456: /**
457: * returns the styles used for this <tt>Theme</tt>.
458: *
459: */
460: public UserStyle[] getStyles() {
461: return styles;
462: }
463:
464: /**
465: * returns the layer that holds the data of the theme
466: *
467: */
468: public Layer getLayer() {
469: return layer;
470: }
471:
472: /**
473: * Returns all <tt>DisplayElements</tt> that this <tt>Theme</tt> contains.
474: * <p>
475: *
476: * @return <tt>ArrayList</tt> containing <tt>DisplayElements</tt>
477: *
478: */
479: public List<DisplayElement> getDisplayElements() {
480: return displayElements;
481: }
482:
483: /**
484: * returns the <tt>DisplayElements</tt> of the Theme
485: *
486: */
487: public void setDisplayElements(List<DisplayElement> de) {
488: this.displayElements = de;
489: }
490:
491: }
|