/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2003-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.map;
import java.io.IOException;
import org.geotools.data.DefaultQuery;
import org.geotools.data.FeatureEvent;
import org.geotools.data.FeatureListener;
import org.geotools.data.FeatureSource;
import org.geotools.data.Query;
import org.geotools.data.memory.CollectionSource;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.event.MapLayerEvent;
import org.geotools.styling.Style;
import org.opengis.feature.Feature;
import org.opengis.feature.type.FeatureType;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
/**
* Implementation of {@link MapLayer} without restricting the return type of {@link #getFeatureSource()}
* allows better support of the DataAccess API;
*
* <p>
* This implementation does not support a collection or grid coverage source.
* <p>
* This implementation was almost entirely stolen from that of {@link DefaultMapLayer}.
* <p>
*
* @author Ben Caradoc-Davies, CSIRO Exploration and Mining
* @source $URL$
*/
public class FeatureSourceMapLayer implements MapLayer {
/** Holds value of property FeatureSource. */
protected FeatureSource<? extends FeatureType, ? extends Feature> featureSource;
/** The style to symbolize the features of this layer */
protected Style style;
/** The query to limit the number of rendered features based on its filter */
protected Query query = Query.ALL;
/** Holds value of property title. */
protected String title;
/** Whether this layer is visible or not. */
protected boolean visible;
/** Whether this layer is selected or not. */
protected boolean selected;
/** Utility field used by event firing mechanism. */
protected javax.swing.event.EventListenerList listenerList = null;
/** Listener to forward feature source events as layer events */
protected FeatureListener sourceListener = new FeatureListener() {
public void changed(FeatureEvent featureEvent) {
fireMapLayerListenerLayerChanged(new MapLayerEvent(FeatureSourceMapLayer.this,
MapLayerEvent.DATA_CHANGED));
}
};
/**
* Constructor
*
* @param featureSource
* the data source for this layer
* @param style
* the style used to represent this layer
* @param title
* the layer title
*/
public FeatureSourceMapLayer(
FeatureSource<? extends FeatureType, ? extends Feature> featureSource, Style style,
String title) {
if (featureSource == null || style == null || title == null) {
throw new NullPointerException();
}
this.featureSource = featureSource;
this.style = style;
this.title = title;
this.visible = true;
this.selected = false;
}
/**
* Convenience constructor that sets title to the empty string.
*
* @param featureSource
* the data source for this layer
* @param style
* the style used to represent this layer
*/
public FeatureSourceMapLayer(
FeatureSource<? extends FeatureType, ? extends Feature> featureSource, Style style) {
this(featureSource, style, "");
}
/**
* Getter for property featureSource.
*
* @return Value of property featureSource.
*/
public FeatureSource<? extends FeatureType, ? extends Feature> getFeatureSource() {
return this.featureSource;
}
/**
* Returns null.
*
* @see org.geotools.map.MapLayer#getSource()
*/
public CollectionSource getSource() {
return null;
}
/**
* Getter for property style.
*
* @return Value of property style.
*/
public Style getStyle() {
return this.style;
}
/**
* Setter for property style.
*
* @param style
* New value of property style.
*
* @throws NullPointerException
* DOCUMENT ME!
*/
public void setStyle(Style style) {
if (style == null) {
throw new NullPointerException();
}
this.style = style;
fireMapLayerListenerLayerChanged(new MapLayerEvent(this, MapLayerEvent.STYLE_CHANGED));
}
/**
* Getter for property title.
*
* @return Value of property title.
*/
public String getTitle() {
return this.title;
}
/**
* Setter for property title.
*
* @param title
* New value of property title.
*
* @throws NullPointerException
* DOCUMENT ME!
*/
public void setTitle(String title) {
if (title == null) {
throw new NullPointerException();
}
this.title = title;
fireMapLayerListenerLayerChanged(new MapLayerEvent(this, MapLayerEvent.METADATA_CHANGED));
}
/**
* Getter for property visible.
*
* @return Value of property visible.
*/
public boolean isVisible() {
return this.visible;
}
/**
* Setter for property visible.
*
* @param visible
* New value of property visible.
*/
public void setVisible(boolean visible) {
if (this.visible == visible) {
return;
}
// change visibility and fire events
this.visible = visible;
MapLayerEvent event = new MapLayerEvent(this, MapLayerEvent.VISIBILITY_CHANGED);
if (visible) {
fireMapLayerListenerLayerShown(event);
} else {
fireMapLayerListenerLayerHidden(event);
}
}
/**
* Getter for property selected.
*
* @return Value of property selected.
*/
public boolean isSelected() {
return this.selected;
}
/**
* Setter for property selected.
*
* @param selected
* New value of property selected.
*/
public void setSelected(boolean selected) {
if (this.selected == selected) {
return;
}
// change visibility and fire events
this.selected = selected;
MapLayerEvent event = new MapLayerEvent(this, MapLayerEvent.SELECTION_CHANGED);
if (selected) {
fireMapLayerListenerLayerSelected(event);
} else {
fireMapLayerListenerLayerDeselected(event);
}
}
/**
* Returns the definition query established for this layer.
*
* @return the definition query established for this layer. If not set, just returns
* {@link Query.ALL}, if set, returns a copy of the actual query object to avoid
* external modification
*
* @see org.geotools.map.MapLayer#getQuery()
*/
public Query getQuery() {
return (query == Query.ALL) ? query : new DefaultQuery(query);
}
/**
* Sets a definition query for this layer.
*
* <p>
* If present (other than <code>Query.ALL</code>, a renderer or consumer must use it to limit
* the number of returned features based on the filter it holds and the value of the maxFeatures
* attributes, and also can use it as a performance hto limit the number of requested attributes
* </p>
*
* @param query
* the full filter for this layer.
*
* @throws NullPointerException
* if no query is passed on. If you want to reset a definition query, pass it
* {@link Query.ALL} instead of <code>null</code>
*
* @task TODO: test that the query filter is suitable for the layer's <code>FeatureSource</code>
* schema
*
* @see org.geotools.map.MapLayer#setQuery(org.geotools.data.Query)
*/
public void setQuery(final Query query) {
if (query == null) {
throw new NullPointerException("must provide a Query. Do you mean Query.ALL?");
}
// be prudent
this.query = new DefaultQuery(query);
fireMapLayerListenerLayerChanged(new MapLayerEvent(this, MapLayerEvent.FILTER_CHANGED));
}
public ReferencedEnvelope getBounds() {
CoordinateReferenceSystem sourceCrs = featureSource.getSchema()
.getCoordinateReferenceSystem();
ReferencedEnvelope env;
try {
env = new ReferencedEnvelope(featureSource.getBounds(), sourceCrs);
return env;
} catch (MismatchedDimensionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
// ------------------------------------------------------------------------
// EVENT HANDLING CODE
// ------------------------------------------------------------------------
/**
* Registers MapLayerListener to receive events.
*
* @param listener
* The listener to register.
*/
public synchronized void addMapLayerListener(org.geotools.map.event.MapLayerListener listener) {
if (listenerList == null) {
listenerList = new javax.swing.event.EventListenerList();
}
if (listenerList.getListenerCount() == 0 && featureSource != null) {
// enable data source listening
featureSource.addFeatureListener(sourceListener);
}
listenerList.add(org.geotools.map.event.MapLayerListener.class, listener);
}
/**
* Removes MapLayerListener from the list of listeners.
*
* @param listener
* The listener to remove.
*/
public synchronized void removeMapLayerListener(org.geotools.map.event.MapLayerListener listener) {
listenerList.remove(org.geotools.map.event.MapLayerListener.class, listener);
if (listenerList.getListenerCount() == 0 && featureSource != null) {
featureSource.removeFeatureListener(sourceListener);
}
}
/**
* Notifies all registered listeners about the event.
*
* @param event
* The event to be fired
*/
protected void fireMapLayerListenerLayerChanged(org.geotools.map.event.MapLayerEvent event) {
if (listenerList == null) {
return;
}
Object[] listeners = listenerList.getListenerList();
final int length = listeners.length;
for (int i = length - 2; i >= 0; i -= 2) {
if (listeners[i] == org.geotools.map.event.MapLayerListener.class) {
((org.geotools.map.event.MapLayerListener) listeners[i + 1]).layerChanged(event);
}
}
}
/**
* Notifies all registered listeners about the event.
*
* @param event
* The event to be fired
*/
protected void fireMapLayerListenerLayerShown(org.geotools.map.event.MapLayerEvent event) {
if (listenerList == null) {
return;
}
Object[] listeners = listenerList.getListenerList();
final int length = listeners.length;
for (int i = length - 2; i >= 0; i -= 2) {
if (listeners[i] == org.geotools.map.event.MapLayerListener.class) {
((org.geotools.map.event.MapLayerListener) listeners[i + 1]).layerShown(event);
}
}
}
/**
* Notifies all registered listeners about the event.
*
* @param event
* The event to be fired
*/
protected void fireMapLayerListenerLayerHidden(org.geotools.map.event.MapLayerEvent event) {
if (listenerList == null) {
return;
}
Object[] listeners = listenerList.getListenerList();
final int length = listeners.length;
for (int i = length - 2; i >= 0; i -= 2) {
if (listeners[i] == org.geotools.map.event.MapLayerListener.class) {
((org.geotools.map.event.MapLayerListener) listeners[i + 1]).layerHidden(event);
}
}
}
/**
* Notifies all registered listeners about the selection event.
*
* @param event
* The event to be fired
*/
protected void fireMapLayerListenerLayerSelected(org.geotools.map.event.MapLayerEvent event) {
if (listenerList == null) {
return;
}
Object[] listeners = listenerList.getListenerList();
final int length = listeners.length;
for (int i = length - 2; i >= 0; i -= 2) {
if (listeners[i] == org.geotools.map.event.MapLayerListener.class) {
((org.geotools.map.event.MapLayerListener) listeners[i + 1]).layerSelected(event);
}
}
}
/**
* Notifies all registered listeners about the deselection event.
*
* @param event
* The event to be fired
*/
protected void fireMapLayerListenerLayerDeselected(org.geotools.map.event.MapLayerEvent event) {
if (listenerList == null) {
return;
}
Object[] listeners = listenerList.getListenerList();
final int length = listeners.length;
for (int i = length - 2; i >= 0; i -= 2) {
if (listeners[i] == org.geotools.map.event.MapLayerListener.class) {
((org.geotools.map.event.MapLayerListener) listeners[i + 1]).layerDeselected(event);
}
}
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append("FeatureCollectionMapLayer[ ");
if (title == null || title.length() == 0) {
buf.append("UNNAMED");
} else {
buf.append(title);
}
if (visible) {
buf.append(", VISIBLE");
} else {
buf.append(", HIDDEN");
}
if (selected) {
buf.append(", SELECTED");
} else {
buf.append(", UNSELECTED");
}
buf.append(", style=");
buf.append(style);
buf.append(", data=");
buf.append(featureSource);
if (query != Query.ALL) {
buf.append(", query=");
buf.append(query);
}
buf.append("]");
return buf.toString();
}
}