001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, GeoTools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * Created on 23 novembre 2003, 10.53
017: */
018: package org.geotools.map;
019:
020: import java.io.IOException;
021: import java.util.Collection;
022:
023: import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
024: import org.geotools.data.DataUtilities;
025: import org.geotools.data.DefaultQuery;
026: import org.geotools.data.FeatureEvent;
027: import org.geotools.data.FeatureListener;
028: import org.geotools.data.FeatureSource;
029: import org.geotools.data.Query;
030: import org.geotools.data.memory.CollectionSource;
031: import org.geotools.factory.FactoryConfigurationError;
032: import org.geotools.feature.FeatureCollection;
033: import org.geotools.feature.IllegalAttributeException;
034: import org.geotools.feature.SchemaException;
035: import org.geotools.geometry.jts.ReferencedEnvelope;
036: import org.geotools.map.event.MapLayerEvent;
037: import org.geotools.resources.image.CoverageUtilities;
038: import org.geotools.styling.Style;
039: import org.opengis.coverage.grid.GridCoverage;
040: import org.opengis.geometry.MismatchedDimensionException;
041: import org.opengis.referencing.crs.CoordinateReferenceSystem;
042: import org.opengis.referencing.operation.TransformException;
043:
044: /**
045: * Default implementation of the MapLayer implementation
046: *
047: * @author wolf
048: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/render/src/main/java/org/geotools/map/DefaultMapLayer.java $
049: */
050: public class DefaultMapLayer implements MapLayer {
051:
052: /** Simple wrapper around a raw collection */
053: private CollectionSource source = null;
054:
055: /** Holds value of property FeatureSource. */
056: protected FeatureSource featureSource;
057:
058: /** The style to symbolize the features of this layer */
059: protected Style style;
060:
061: /** The query to limit the number of rendered features based on its filter */
062: protected Query query = Query.ALL;
063:
064: /** Holds value of property title. */
065: protected String title;
066:
067: /** Whether this layer is visible or not. */
068: protected boolean visible;
069:
070: /** Utility field used by event firing mechanism. */
071: protected javax.swing.event.EventListenerList listenerList = null;
072:
073: /** Listener to forward feature source events as layer events */
074: protected FeatureListener sourceListener = new FeatureListener() {
075: public void changed(FeatureEvent featureEvent) {
076: fireMapLayerListenerLayerChanged(new MapLayerEvent(
077: DefaultMapLayer.this , MapLayerEvent.DATA_CHANGED));
078: }
079: };
080:
081: /**
082: * Creates a new instance of DefaultMapLayer
083: *
084: * @param featureSource
085: * the data source for this layer
086: * @param style
087: * the style used to represent this layer
088: * @param title
089: * the layer title
090: *
091: * @throws NullPointerException
092: * DOCUMENT ME!
093: */
094: public DefaultMapLayer(FeatureSource featureSource, Style style,
095: String title) {
096: if ((featureSource == null) || (style == null)
097: || (title == null)) {
098: //throw new NullPointerException();
099: }
100:
101: // enable data source listening
102: // featureSource.addFeatureListener(sourceListener);
103:
104: this .featureSource = featureSource;
105: this .style = style;
106: this .title = title;
107: this .visible = true;
108: }
109:
110: public DefaultMapLayer(CollectionSource source, Style style,
111: String title) {
112:
113: if ((source == null) || (style == null) || (title == null)) {
114: throw new NullPointerException();
115: }
116: this .source = source;
117: this .style = style;
118: this .title = title;
119: this .visible = true;
120: }
121:
122: /**
123: * Creates a new instance of DefaultMapLayer
124: *
125: * @param featureSource
126: * the data source for this layer
127: * @param style
128: * the style used to represent this layer
129: */
130: public DefaultMapLayer(FeatureSource featureSource, Style style) {
131: this (featureSource, style, "");
132: }
133:
134: /**
135: * Creates a new instance of DefaultMapLayer using a non-emtpy feature
136: * collection as a parameter
137: *
138: * @param collection
139: * the source feature collection
140: * @param style
141: * the style used to represent this layer
142: * @param title
143: * DOCUMENT ME!
144: */
145: public DefaultMapLayer(FeatureCollection collection, Style style,
146: String title) {
147: this (DataUtilities.source(collection), style, title);
148: }
149:
150: public DefaultMapLayer(Collection collection, Style style,
151: String title) {
152: this (new CollectionSource(collection), style, title);
153: }
154:
155: /**
156: * Creates a new instance of DefaultMapLayer using a non-emtpy feature
157: * collection as a parameter
158: *
159: * @param collection
160: * the source feature collection
161: * @param style
162: * the style used to represent this layer
163: */
164: public DefaultMapLayer(FeatureCollection collection, Style style) {
165: this (DataUtilities.source(collection), style, "");
166: }
167:
168: public DefaultMapLayer(Collection collection, Style style) {
169: //this(DataUtilities.source(collection), style, "");
170: }
171:
172: /**
173: * * Add a new layer and trigger a {@link LayerListEvent}.
174: *
175: * @param coverage
176: * The new layer that has been added.
177: * @param style
178: * @throws IllegalAttributeException
179: * @throws SchemaException
180: * @throws FactoryConfigurationError
181: * @throws TransformException
182: */
183: public DefaultMapLayer(GridCoverage coverage, Style style)
184: throws TransformException, FactoryConfigurationError,
185: SchemaException, IllegalAttributeException {
186:
187: this (CoverageUtilities.wrapGc(coverage), style, "");
188:
189: }
190:
191: /**
192: * Constructor which adds a new layer and trigger a {@link LayerListEvent}.
193: *
194: * @param reader
195: * a reader with the new layer that will be added.
196: * @param style
197: * @param title
198: *
199: * @throws IllegalAttributeException
200: * @throws SchemaException
201: * @throws FactoryConfigurationError
202: * @throws TransformException
203: */
204: public DefaultMapLayer(AbstractGridCoverage2DReader reader,
205: Style style, String title) throws TransformException,
206: FactoryConfigurationError, SchemaException,
207: IllegalAttributeException {
208:
209: this (CoverageUtilities.wrapGcReader(reader), style, title);
210:
211: }
212:
213: /**
214: * Constructor which adds a new layer and triggers a {@link LayerListEvent}.
215: *
216: * @param reader
217: * a reader with the new layer that will be added
218: * @param style
219: *
220: * @throws IllegalAttributeException
221: * @throws SchemaException
222: * @throws FactoryConfigurationError
223: * @throws TransformException
224: */
225: public DefaultMapLayer(AbstractGridCoverage2DReader reader,
226: Style style) throws TransformException,
227: FactoryConfigurationError, SchemaException,
228: IllegalAttributeException {
229:
230: this (CoverageUtilities.wrapGcReader(reader), style, "");
231:
232: }
233:
234: /**
235: * * Add a new layer and trigger a {@link LayerListEvent}.
236: *
237: * @param coverage
238: * The new layer that has been added.
239: * @param style
240: * @param title
241: * @throws IllegalAttributeException
242: * @throws SchemaException
243: * @throws FactoryConfigurationError
244: * @throws TransformException
245: */
246: public DefaultMapLayer(GridCoverage coverage, Style style,
247: String title) throws TransformException,
248: FactoryConfigurationError, SchemaException,
249: IllegalAttributeException {
250:
251: this (CoverageUtilities.wrapGc(coverage), style, title);
252:
253: }
254:
255: /**
256: * Getter for property featureSource.
257: *
258: * @return Value of property featureSource.
259: */
260: public FeatureSource getFeatureSource() {
261: return this .featureSource;
262: }
263:
264: public CollectionSource getSource() {
265: return this .source;
266: }
267:
268: /**
269: * Getter for property style.
270: *
271: * @return Value of property style.
272: */
273: public Style getStyle() {
274: return this .style;
275: }
276:
277: /**
278: * Setter for property style.
279: *
280: * @param style
281: * New value of property style.
282: *
283: * @throws NullPointerException
284: * DOCUMENT ME!
285: */
286: public void setStyle(Style style) {
287: if (style == null) {
288: throw new NullPointerException();
289: }
290:
291: this .style = style;
292: fireMapLayerListenerLayerChanged(new MapLayerEvent(this ,
293: MapLayerEvent.STYLE_CHANGED));
294: }
295:
296: /**
297: * Getter for property title.
298: *
299: * @return Value of property title.
300: */
301: public String getTitle() {
302: return this .title;
303: }
304:
305: /**
306: * Setter for property title.
307: *
308: * @param title
309: * New value of property title.
310: *
311: * @throws NullPointerException
312: * DOCUMENT ME!
313: */
314: public void setTitle(String title) {
315: if (title == null) {
316: throw new NullPointerException();
317: }
318:
319: this .title = title;
320:
321: fireMapLayerListenerLayerChanged(new MapLayerEvent(this ,
322: MapLayerEvent.METADATA_CHANGED));
323: }
324:
325: /**
326: * Getter for property visible.
327: *
328: * @return Value of property visible.
329: */
330: public boolean isVisible() {
331: return this .visible;
332: }
333:
334: /**
335: * Setter for property visible.
336: *
337: * @param visible
338: * New value of property visible.
339: */
340: public void setVisible(boolean visible) {
341: if (this .visible == visible) {
342: return;
343: }
344:
345: // change visibility and fire events
346: this .visible = visible;
347:
348: MapLayerEvent event = new MapLayerEvent(this ,
349: MapLayerEvent.VISIBILITY_CHANGED);
350:
351: if (visible) {
352: fireMapLayerListenerLayerShown(event);
353: } else {
354: fireMapLayerListenerLayerHidden(event);
355: }
356: }
357:
358: /**
359: * Returns the definition query established for this layer.
360: *
361: * @return the definition query established for this layer. If not set, just
362: * returns {@link Query.ALL}, if set, returns a copy of the actual
363: * query object to avoid external modification
364: *
365: * @see org.geotools.map.MapLayer#getQuery()
366: */
367: public Query getQuery() {
368: return (query == Query.ALL) ? query : new DefaultQuery(query);
369: }
370:
371: /**
372: * Sets a definition query for this layer.
373: *
374: * <p>
375: * If present (other than <code>Query.ALL</code>, a renderer or consumer
376: * must use it to limit the number of returned features based on the filter
377: * it holds and the value of the maxFeatures attributes, and also can use it
378: * as a performance hto limit the number of requested attributes
379: * </p>
380: *
381: * @param query
382: * the full filter for this layer.
383: *
384: * @throws NullPointerException
385: * if no query is passed on. If you want to reset a definition
386: * query, pass it {@link Query.ALL} instead of <code>null</code>
387: *
388: * @task TODO: test that the query filter is siutable for the layer's
389: * <code>FeatureSource</code> schema
390: *
391: * @see org.geotools.map.MapLayer#setQuery(org.geotools.data.Query)
392: */
393: public void setQuery(final Query query) {
394: if (query == null) {
395: throw new NullPointerException(
396: "must provide a Query. Do you mean Query.ALL?");
397: }
398:
399: // be prudent
400: this .query = new DefaultQuery(query);
401: fireMapLayerListenerLayerChanged(new MapLayerEvent(this ,
402: MapLayerEvent.FILTER_CHANGED));
403: }
404:
405: public ReferencedEnvelope getBounds() {
406:
407: CoordinateReferenceSystem sourceCrs = featureSource.getSchema()
408: .getDefaultGeometry().getCoordinateSystem();
409: ReferencedEnvelope env;
410: try {
411: env = new ReferencedEnvelope(featureSource.getBounds(),
412: sourceCrs);
413: return env;
414: } catch (MismatchedDimensionException e) {
415: // TODO Auto-generated catch block
416: e.printStackTrace();
417: } catch (IOException e) {
418: // TODO Auto-generated catch block
419: e.printStackTrace();
420: }
421: return null;
422: }
423:
424: // ------------------------------------------------------------------------
425: // EVENT HANDLING CODE
426: // ------------------------------------------------------------------------
427:
428: /**
429: * Registers MapLayerListener to receive events.
430: *
431: * @param listener
432: * The listener to register.
433: */
434: public synchronized void addMapLayerListener(
435: org.geotools.map.event.MapLayerListener listener) {
436: if (listenerList == null) {
437: listenerList = new javax.swing.event.EventListenerList();
438: }
439:
440: if (listenerList.getListenerCount() == 0
441: && featureSource != null) {
442: // enable data source listening
443: featureSource.addFeatureListener(sourceListener);
444: }
445: listenerList.add(org.geotools.map.event.MapLayerListener.class,
446: listener);
447: }
448:
449: /**
450: * Removes MapLayerListener from the list of listeners.
451: *
452: * @param listener
453: * The listener to remove.
454: */
455: public synchronized void removeMapLayerListener(
456: org.geotools.map.event.MapLayerListener listener) {
457: listenerList
458: .remove(org.geotools.map.event.MapLayerListener.class,
459: listener);
460: if (listenerList.getListenerCount() == 0
461: && featureSource != null) {
462: featureSource.removeFeatureListener(sourceListener);
463: }
464: }
465:
466: /**
467: * Notifies all registered listeners about the event.
468: *
469: * @param event
470: * The event to be fired
471: */
472: protected void fireMapLayerListenerLayerChanged(
473: org.geotools.map.event.MapLayerEvent event) {
474: if (listenerList == null) {
475: return;
476: }
477:
478: Object[] listeners = listenerList.getListenerList();
479: final int length = listeners.length;
480: for (int i = length - 2; i >= 0; i -= 2) {
481: if (listeners[i] == org.geotools.map.event.MapLayerListener.class) {
482: ((org.geotools.map.event.MapLayerListener) listeners[i + 1])
483: .layerChanged(event);
484: }
485: }
486: }
487:
488: /**
489: * Notifies all registered listeners about the event.
490: *
491: * @param event
492: * The event to be fired
493: */
494: protected void fireMapLayerListenerLayerShown(
495: org.geotools.map.event.MapLayerEvent event) {
496: if (listenerList == null) {
497: return;
498: }
499:
500: Object[] listeners = listenerList.getListenerList();
501: final int length = listeners.length;
502: for (int i = length - 2; i >= 0; i -= 2) {
503: if (listeners[i] == org.geotools.map.event.MapLayerListener.class) {
504: ((org.geotools.map.event.MapLayerListener) listeners[i + 1])
505: .layerShown(event);
506: }
507: }
508: }
509:
510: /**
511: * Notifies all registered listeners about the event.
512: *
513: * @param event
514: * The event to be fired
515: */
516: protected void fireMapLayerListenerLayerHidden(
517: org.geotools.map.event.MapLayerEvent event) {
518: if (listenerList == null) {
519: return;
520: }
521:
522: Object[] listeners = listenerList.getListenerList();
523: final int length = listeners.length;
524: for (int i = length - 2; i >= 0; i -= 2) {
525: if (listeners[i] == org.geotools.map.event.MapLayerListener.class) {
526: ((org.geotools.map.event.MapLayerListener) listeners[i + 1])
527: .layerHidden(event);
528: }
529: }
530: }
531:
532: public String toString() {
533: StringBuffer buf = new StringBuffer();
534: buf.append("DefaultMapLayer[ ");
535: if (title == null || title.length() == 0) {
536: buf.append("UNNAMED");
537: } else {
538: buf.append(title);
539: }
540: if (visible) {
541: buf.append(", VISIBLE");
542: } else {
543: buf.append(", HIDDEN");
544: }
545: buf.append(", style=");
546: buf.append(style);
547: buf.append(", data=");
548: buf.append(featureSource);
549: if (query != Query.ALL) {
550: buf.append(", query=");
551: buf.append(query);
552: }
553: buf.append("]");
554: return buf.toString();
555: }
556: }
|