001: //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/graphics/MapView.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.awt.Graphics2D;
048: import java.util.ArrayList;
049: import java.util.Collections;
050: import java.util.HashMap;
051: import java.util.Iterator;
052: import java.util.List;
053:
054: import org.deegree.framework.log.ILogger;
055: import org.deegree.framework.log.LoggerFactory;
056: import org.deegree.framework.util.MapUtils;
057: import org.deegree.framework.util.StringTools;
058: import org.deegree.graphics.displayelements.DisplayElement;
059: import org.deegree.graphics.optimizers.AbstractOptimizer;
060: import org.deegree.graphics.optimizers.Optimizer;
061: import org.deegree.graphics.transformation.GeoTransform;
062: import org.deegree.graphics.transformation.WorldToScreenTransform;
063: import org.deegree.model.crs.CRSFactory;
064: import org.deegree.model.crs.CoordinateSystem;
065: import org.deegree.model.crs.UnknownCRSException;
066: import org.deegree.model.spatialschema.Envelope;
067:
068: /**
069: * This interface describes the data model of the map itself. It is built from themes containing
070: * {@link DisplayElement}s to be rendered. Themes can be added and removed. Existing themes can be
071: * re-arragned by changing their order.
072: *
073: * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
074: * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider</a>
075: * @author last edited by: $Author: apoth $
076: *
077: * @version $Revision: 9340 $, $Date: 2007-12-27 04:32:12 -0800 (Thu, 27 Dec 2007) $
078: */
079: public class MapView {
080:
081: private static final ILogger LOG = LoggerFactory
082: .getLogger(MapView.class);
083:
084: private String name = null;
085:
086: private HashMap<String, Theme> themes = null;
087:
088: private HashMap<String, Boolean> enabled = null;
089:
090: private List<Theme> themesL = null;
091:
092: private Theme activatedTh = null;
093:
094: private Envelope boundingbox = null;
095:
096: private CoordinateSystem crs = null;
097:
098: private List<EventController> eventCntr = Collections
099: .synchronizedList(new ArrayList<EventController>());
100:
101: private double scale;
102:
103: private double pixelsize = 0.00028;
104:
105: private GeoTransform projection = new WorldToScreenTransform();
106:
107: // list of Optimizers that are processed at the beginning of the paint ()-call
108: private List<Optimizer> optimizers = new ArrayList<Optimizer>();
109:
110: /**
111: *
112: * @param name
113: * @param boundingbox
114: * @param pixelsize
115: * @throws UnknownCRSException
116: */
117: MapView(String name, Envelope boundingbox, double pixelsize)
118: throws UnknownCRSException {
119: this .name = name;
120: this .pixelsize = pixelsize;
121: themes = new HashMap<String, Theme>();
122: themesL = new ArrayList<Theme>();
123: enabled = new HashMap<String, Boolean>();
124: setBoundingBox(boundingbox);
125: crs = CRSFactory.create("EPSG:4326");
126: }
127:
128: /**
129: *
130: * @param name
131: * @param boundingbox
132: * @param crs
133: * @param pixelsize
134: */
135: MapView(String name, Envelope boundingbox, CoordinateSystem crs,
136: double pixelsize) {
137: this .name = name;
138: this .pixelsize = pixelsize;
139: themes = new HashMap<String, Theme>();
140: themesL = new ArrayList<Theme>();
141: enabled = new HashMap<String, Boolean>();
142: setBoundingBox(boundingbox);
143: this .crs = crs;
144: }
145:
146: /**
147: * returns the name of the map
148: *
149: */
150: public String getName() {
151: return name;
152: }
153:
154: /**
155: * returns the Theme that matches the submitted name
156: */
157: public Theme getTheme(String name) {
158: return themes.get(name);
159: }
160:
161: /**
162: * returns the Theme that matches the submitted index
163: */
164: public Theme getTheme(int index) {
165: return themesL.get(index);
166: }
167:
168: /**
169: * returns the Themes in correct order. The first Theme (index == 0) shall be rendered at first
170: * (bottom most).
171: */
172: public Theme[] getAllThemes() {
173: return themesL.toArray(new Theme[themesL.size()]);
174: }
175:
176: /**
177: * Returns the current scale of the MapView.
178: *
179: */
180: public double getScale() {
181: return scale;
182: }
183:
184: /**
185: * Returns the current scale of the MapView.
186: */
187: public double getScale(Graphics g) throws Exception {
188: return MapUtils.calcScale(g.getClipBounds().width, g
189: .getClipBounds().height, getBoundingBox(),
190: getCoordinatesSystem(), pixelsize);
191: }
192:
193: /**
194: * adds a theme to the MapView
195: */
196: public void addTheme(Theme theme) throws Exception {
197: themes.put(theme.getName(), theme);
198: themesL.add(theme);
199: enabled.put(theme.getName(), Boolean.TRUE);
200: activatedTh = theme;
201: theme.setParent(this );
202: theme.getLayer().setCoordinatesSystem(crs);
203: }
204:
205: /**
206: * removes a theme from the MapView
207: */
208: public void removeTheme(Theme theme) {
209: if (theme != null) {
210: enabled.remove(theme.getName());
211: themesL.remove(themesL.indexOf(theme));
212: themes.remove(theme.getName());
213: }
214: }
215:
216: /**
217: * removes the theme that matches the submitted name from the MapView
218: */
219: public void removeTheme(String name) {
220: removeTheme(getTheme(name));
221: }
222:
223: /**
224: * removes the theme that matches the submitted index from the MapView
225: */
226: public void removeTheme(int index) {
227: removeTheme(themesL.get(index));
228: }
229:
230: /**
231: * removes all themes from the MapView.
232: */
233: public void clear() {
234: themes.clear();
235: themesL.clear();
236: enabled.clear();
237: activatedTh = null;
238: }
239:
240: /**
241: * swaps the positions of the submitted themes
242: */
243: public void swapThemes(Theme first, Theme second) {
244:
245: if (themesL.contains(first) && themesL.contains(second)) {
246: int i1 = themesL.indexOf(first);
247: int i2 = themesL.indexOf(second);
248: themesL.set(i1, second);
249: themesL.set(i2, first);
250: }
251:
252: }
253:
254: /**
255: * move a theme up for one index position (index = oldindex + 1)
256: */
257: public void moveUp(Theme theme) {
258:
259: int idx = themesL.indexOf(theme);
260: if (idx < themesL.size() - 1) {
261: Theme th = themesL.get(idx + 1);
262: swapThemes(theme, th);
263: }
264:
265: }
266:
267: /**
268: * move a theme down for one index position (index = oldindex - 1)
269: */
270: public void moveDown(Theme theme) {
271:
272: int idx = themesL.indexOf(theme);
273: if (idx > 0) {
274: Theme th = themesL.get(idx - 1);
275: swapThemes(theme, th);
276: }
277:
278: }
279:
280: /**
281: * enables or disables a theme that is part of the MapView. A theme that has been disabled won't
282: * be rendered and usually doesn't react to events targeted to the MapView, but still is part of
283: * the MapView.
284: */
285: public void enableTheme(Theme theme, boolean enable) {
286: enabled.put(theme.getName(), enable ? Boolean.TRUE
287: : Boolean.FALSE);
288: }
289:
290: /**
291: * returns true if the passed theme is set to be enabled
292: */
293: public boolean isThemeEnabled(Theme theme) {
294: return enabled.get(theme.getName()).booleanValue();
295: }
296:
297: /**
298: * activates a theme. Usually the activated theme is perferred to react to events (this doesn't
299: * mean that other themes are not allowed to react to events).
300: */
301: public void activateTheme(Theme theme) {
302: activatedTh = theme;
303: }
304:
305: /**
306: * returns true if the passed theme is the one that is set to be activated
307: */
308: public boolean isThemeActivated(Theme theme) {
309: return activatedTh.getName().equals(theme.getName());
310: }
311:
312: /**
313: * returns the amount of themes within the MapView.
314: */
315: public int getSize() {
316: return themes.size();
317: }
318:
319: /**
320: * adds an eventcontroller to the MapView that's reponsible for handling events that targets the
321: * map. E.g.: zooming, panning, selecting a feature etc.
322: */
323: public void addEventController(MapEventController obj) {
324: eventCntr.add(obj);
325: obj.addMapView(this );
326: }
327:
328: /**
329: * @see org.deegree.graphics.MapView#addEventController(MapEventController)
330: */
331: public void removeEventController(MapEventController obj) {
332: eventCntr.remove(obj);
333: obj.removeMapView(this );
334: }
335:
336: /**
337: * A selector is a class that offers methods for selecting and deselecting single
338: * DisplayElements or groups of DisplayElements. A selector may offers methods like 'select all
339: * DisplayElements within a specified bounding box' or 'select all DisplayElements thats area is
340: * larger than 120 km�' etc.
341: */
342: public void addSelector(Selector obj) {
343: for (int i = 0; i < themesL.size(); i++) {
344: getTheme(i).addSelector(obj);
345: }
346: }
347:
348: /**
349: * @see org.deegree.graphics.MapView#addSelector(Selector)
350: */
351: public void removeSelector(Selector obj) {
352: for (int i = 0; i < themesL.size(); i++) {
353: getTheme(i).removeSelector(obj);
354: }
355: }
356:
357: /**
358: * returns the BoundingBox (Envelope) of the MapView. This isn't nessecary the BoundingBox of
359: * the data that will be rendered. It's the boundingBox of the the visible area of the map
360: * measured in its coordinate reference system.
361: */
362: public Envelope getBoundingBox() {
363: return boundingbox;
364: }
365:
366: /**
367: * @see org.deegree.graphics.MapView#getBoundingBox() this method may be used for zooming and
368: * panning the map
369: */
370: public void setBoundingBox(Envelope boundingbox) {
371: this .boundingbox = boundingbox;
372: projection.setSourceRect(boundingbox);
373: }
374:
375: /**
376: * returns the coordinate reference system of the MapView
377: */
378: public CoordinateSystem getCoordinatesSystem() {
379: return crs;
380: }
381:
382: /**
383: * sets the coordinate reference system of the map;
384: */
385: public void setCoordinateSystem(CoordinateSystem crs)
386: throws Exception {
387: this .crs = crs;
388: for (int i = 0; i < themesL.size(); i++) {
389: Layer lay = getTheme(i).getLayer();
390: lay.setCoordinatesSystem(crs);
391: }
392: }
393:
394: /**
395: * renders the map to the passed graphic context
396: *
397: * @param g
398: * @throws RenderException
399: * thrown if the passed <tt>Graphic<tt> haven't
400: * clipbounds. use g.setClip( .. );
401: */
402: public void paint(Graphics g) throws RenderException {
403:
404: if (g.getClipBounds() == null) {
405: throw new RenderException(
406: "no clip bounds defined for graphic context");
407: }
408:
409: int x = g.getClipBounds().x;
410: int y = g.getClipBounds().y;
411: int w = g.getClipBounds().width;
412: int h = g.getClipBounds().height;
413: projection.setDestRect(x, y, w + x, h + y);
414:
415: try {
416: double sc = getScale(g);
417: LOG.logInfo("OGC SLD scale denominator ", sc);
418: scale = sc;
419: // call all Optimizers
420: optimize(g);
421: } catch (Exception e) {
422: e.printStackTrace();
423: throw new RenderException(StringTools.stackTraceToString(e));
424: }
425:
426: // paint all Themes
427: for (int i = 0; i < themesL.size(); i++) {
428: if (isThemeEnabled(getTheme(i))) {
429: getTheme(i).paint(g);
430: }
431: }
432:
433: }
434:
435: /**
436: * renders the features marked as selected of all themes contained within the MapView
437: *
438: * @param g
439: * graphic context to render the map too
440: * @throws RenderException
441: * thrown if the passed <tt>Graphic<tt> haven't
442: * clipbounds. use g.setClip( .. );
443: */
444: public void paintSelected(Graphics g) throws RenderException {
445:
446: if (g.getClipBounds() == null) {
447: throw new RenderException(
448: "no clip bounds defined for graphic context");
449: }
450:
451: int x = g.getClipBounds().x;
452: int y = g.getClipBounds().y;
453: int width = g.getClipBounds().width;
454: int height = g.getClipBounds().height;
455: projection.setDestRect(x - 2, y - 2, width + x, height + y);
456:
457: try {
458: // call all Optimizers
459: optimize(g);
460: } catch (Exception e) {
461: throw new RenderException(StringTools.stackTraceToString(e));
462: }
463:
464: // paint all Themes
465: for (int i = 0; i < themesL.size(); i++) {
466: if (isThemeEnabled(getTheme(i))) {
467: getTheme(i).paintSelected(g);
468: }
469: }
470:
471: }
472:
473: /**
474: * renders the features marked as highlighted of all themes contained within the MapView
475: *
476: * @param g
477: * graphic context to render the map too
478: * @throws RenderException
479: * thrown if the passed <tt>Graphic<tt> haven't
480: * clipbounds. use g.setClip( .. );
481: */
482: public void paintHighlighted(Graphics g) throws RenderException {
483:
484: if (g.getClipBounds() == null) {
485: throw new RenderException(
486: "no clip bounds defined for graphic context");
487: }
488:
489: int x = g.getClipBounds().x;
490: int y = g.getClipBounds().y;
491: int width = g.getClipBounds().width;
492: int height = g.getClipBounds().height;
493: projection.setDestRect(x - 2, y - 2, width + x, height + y);
494:
495: try {
496: // call all Optimizers
497: optimize(g);
498: } catch (Exception e) {
499: throw new RenderException(StringTools.stackTraceToString(e));
500: }
501:
502: // paint all Themes
503: for (int i = 0; i < themesL.size(); i++) {
504: if (isThemeEnabled(getTheme(i))) {
505: getTheme(i).paintHighlighted(g);
506: }
507: }
508:
509: }
510:
511: /**
512: * A Highlighter is a class that is responsible for managing the highlight capabilities for one
513: * or more Themes.
514: */
515: public void addHighlighter(Highlighter highlighter) {
516: for (int i = 0; i < themesL.size(); i++) {
517: getTheme(i).addHighlighter(highlighter);
518: }
519: }
520:
521: /**
522: * @see org.deegree.graphics.MapView#addHighlighter(Highlighter)
523: */
524: public void removeHighlighter(Highlighter highlighter) {
525: for (int i = 0; i < themesL.size(); i++) {
526: getTheme(i).removeHighlighter(highlighter);
527: }
528: }
529:
530: /**
531: * Returns the <tt>GeoTransform</tt> that is associated to this MapView.
532: * <p>
533: *
534: * @return the associated <tt>GeoTransform</tt>-instance
535: *
536: */
537: public GeoTransform getProjection() {
538: return projection;
539: }
540:
541: /**
542: * Calls all registered <tt>Optimizer</tt> subsequently.
543: *
544: * @param g
545: */
546: private void optimize(Graphics g) throws Exception {
547: Graphics2D g2 = (Graphics2D) g;
548: Iterator it = optimizers.iterator();
549: while (it.hasNext()) {
550: AbstractOptimizer optimizer = (AbstractOptimizer) it.next();
551: optimizer.optimize(g2);
552: }
553: }
554:
555: /**
556: * Adds an <tt>Optimizer</tt>.
557: *
558: * @param optimizer
559: */
560: public void addOptimizer(Optimizer optimizer) {
561: optimizers.add(optimizer);
562: optimizer.setMapView(this );
563: }
564:
565: /**
566: * Returns the <tt>Optimizer</tt>s.
567: *
568: * @return the <tt>Optimizer</tt>s.
569: *
570: */
571: public Optimizer[] getOptimizers() {
572: return optimizers.toArray(new Optimizer[0]);
573: }
574:
575: /**
576: * Sets the <tt>Optimizer<tt>s.
577: * @param optimizers
578: */
579: public void setOptimizers(Optimizer[] optimizers) {
580: this .optimizers.clear();
581: for (int i = 0; i < optimizers.length; i++) {
582: addOptimizer(optimizers[i]);
583: }
584: }
585:
586: }
|