/* * 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 java.util.Collection; import java.util.Iterator; import java.util.List; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader; import org.geotools.data.FeatureSource; import org.geotools.data.memory.CollectionSource; import org.geotools.feature.FeatureCollection; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.map.event.MapLayerListEvent; import org.geotools.referencing.crs.DefaultGeographicCRS; import org.geotools.styling.SLD; import org.geotools.styling.Style; import org.opengis.coverage.grid.GridCoverage; import org.opengis.feature.type.FeatureType; import org.opengis.referencing.crs.CoordinateReferenceSystem; /** * A kinder gentler implementation of {@linkplain org.geotools.map.MapContext} that produces defaults * as needed. * <p> * This implementation produces defaults as needed for: * <ul> * <li>Map Bounds are generated from the first layer if needed</li> * <li>Default Styles are generated if needed for display</li> * </ul> * * @author Andrea Aime * * @source $URL$ * http://svn.osgeo.org/geotools/trunk/modules/library/render/src/main/java/org/geotools * /map/DefaultMapContext.java $ * * @deprecated Use {@link MapContent} instead */ public class DefaultMapContext extends MapContext { /** * Creates a default empty map context. The coordinate reference system for the map context * should be set explicitly, or implicitly via {@code addLayer} prior to using the context. */ public DefaultMapContext() { super((CoordinateReferenceSystem) null); } /** * Creates a default empty map context * * @param crs * the coordindate reference system to be used with this context (may be null and set * later) */ public DefaultMapContext(final CoordinateReferenceSystem crs) { super(null, null, null, null, null, crs); } /** * Creates a map context with the provided layers. * <p> * Note, the coordinate reference system for the context will be set from that of the first * layer with an available CRS. * * @param layers * an array of MapLayer objects (may be empty or null) to be added to this context */ public DefaultMapContext(MapLayer[] layers) { super(layers, DefaultGeographicCRS.WGS84); } /** * Creates a map context with the provided layers and coordinate reference system * * @param layers * an array of MapLayer objects (may be empty or null) to be added to this context * * @param crs * the coordindate reference system to be used with this context (may be null and set * later) */ public DefaultMapContext(MapLayer[] layers, final CoordinateReferenceSystem crs) { super(layers, null, null, null, null, crs); } /** * Creates a map context * <p> * Note, the coordinate reference system for the context will be set from that of the first * layer with an available CRS. * * @param layers * an array of MapLayer objects (may be empty or null) to be added to this context * * @param title * a title for this context (e.g. might be used by client-code that is displaying the * context's layers); may be null or an empty string * * @param contextAbstract * a short description of the context and its contents; may be null or an empty * string * * @param contactInformation * can be used, for example, to record the creators or custodians of the data that * are, or will be, held by this context; may be null or an empty string * * @param keywords * an optional array of key words pertaining to the data that are, or will be, held * by this context; may be null or a zero-length String array * */ public DefaultMapContext(MapLayer[] layers, String title, String contextAbstract, String contactInformation, String[] keywords) { super(layers, title, contextAbstract, contactInformation, keywords, null); } /** * Creates a new map context * * @param layers * an array of MapLayer objects (may be empty or null) to be added to this context * * @param title * a title for this context (e.g. might be used by client-code that is displaying the * context's layers); may be null or an empty string * * @param contextAbstract * a short description of the context and its contents; may be null or an empty * string * * @param contactInformation * can be used, for example, to record the creators or custodians of the data that * are, or will be, held by this context; may be null or an empty string * * @param keywords * an optional array of key words pertaining to the data that are, or will be, held * by this context; may be null or a zero-length String array * * @param crs * the coordindate reference system to be used with this context (may be null and set * later) */ public DefaultMapContext(MapLayer[] layers, String title, String contextAbstract, String contactInformation, String[] keywords, final CoordinateReferenceSystem crs) { super(layers, title, contextAbstract, contactInformation, keywords, crs); } /** * Add a new layer if not already present and trigger a {@linkplain MapLayerListEvent}. If a * coordinate reference system has not been set for the context an attempt is made to retrieve * one from the new layer and use that as the context's CRS. * * @param index * the position at which to insert the layer in the list of layers held by this * context * * @param layer * the map layer to add * * @return true if the layer was added; false otherwise (layer was already present) */ @SuppressWarnings("deprecation") public boolean addLayer(int index, MapLayer mapLayer) { // checkCRS(mapLayer); Layer layer = mapLayer.toLayer(); layers().add(index, layer); return true; } /** * Add a new layer, if not already present, to the end of the list of layers held by this * context and trigger a {@linkplain MapLayerListEvent} If a coordinate reference system has not * been set for the context an attempt is made to retrieve one from the new layer and use that * as the context's CRS. * * @param layer * the map layer to add * * @return true if the layer was added; false otherwise (layer was already present) */ @SuppressWarnings("deprecation") public boolean addLayer(MapLayer mapLayer) { //checkCRS(mapLayer); layers().add(mapLayer.toLayer()); return true; } /** * Add the given feature source as a new layer to the end of the list of layers held by this * context and trigger a {@linkplain MapLayerListEvent}. This is a convenience method equivalent * to {@linkplain #addLayer}(new DefaultMapLayer(featureSource, style). * <p> * If a coordinate reference system has not been set for the context an attempt is made to * retrieve one from the new layer and use that as the context's CRS. * <p> * If {@code style} is null, a default style is created using * {@linkplain SLD#createSimpleStyle(org.opengis.feature.simple.SimpleFeatureType)}. * * @param featureSource * the source of the features for the new layer * * @param style * a Style object to be used in rendering this layer. */ public void addLayer(FeatureSource featureSource, Style style) { //checkCRS(featureSource); Style layerStyle = checkStyle(style, featureSource.getSchema()); addLayer(new DefaultMapLayer(featureSource, layerStyle, "")); } /** * Add the given collection source as a new layer to the end of the list of layers held by this * context and trigger a {@linkplain MapLayerListEvent}. This is a convenience method equivalent * to {@linkplain #addLayer}(new DefaultMapLayer(source, style). * <p> * If a coordinate reference system has not been set for the context an attempt is made to * retrieve one from the new layer and use that as the context's CRS. * * @param source * the source of the features for the new layer * @param style * a Style object to be used in rendering this layer */ public void addLayer(CollectionSource source, Style style) { throw new UnsupportedOperationException("FeatureSource required"); } /** * Add a grid coverage as a new layer to the end of the list of layers held by this context. * <p> * If a coordinate reference system has not been set for the context an attempt is made to * retrieve one from the grid coverage and use that as the context's CRS. * * @param gc * the grid coverage * @param style * a Style to be used when rendering the new layer */ public void addLayer(GridCoverage gc, Style style) { if (style == null) { throw new IllegalArgumentException("style cannot be null"); } // checkCRS(gc.getCoordinateReferenceSystem()); if (gc instanceof GridCoverage2D) { Layer layer = new GridCoverageLayer((GridCoverage2D) gc, style); layers().add(layer); } else { throw new UnsupportedOperationException("GridCoverage2D required"); } } /** * Add a grid coverage data to be supplied by the given reader as a new layer to the end of the * list of layers held by this context. * <p> * If a coordinate reference system has not been set for the context an attempt is made to * retrieve one from the reader and use that as the context's CRS. * * @param reader * the grid coverage reader * @param style * a Style to be used when rendering the new layer */ public void addLayer(AbstractGridCoverage2DReader reader, Style style) { if (style == null) { throw new IllegalArgumentException("Style cannot be null"); } // checkCRS(reader.getCrs()); Layer layer = new GridReaderLayer(reader, style); layers().add(layer); } /** * Add the given feature collection as a new layer to the end of the list of layers held by this * context and trigger a {@linkplain MapLayerListEvent}. This is a convenience method equivalent * to {@linkplain #addLayer}(new DefaultMapLayer(collection, style). * * @param collection * the collection of features for the new layer * @param style * a Style object to be used in rendering this layer */ public void addLayer(FeatureCollection featureCollection, Style style) { FeatureType schema = featureCollection.getSchema(); Style layerStyle = checkStyle(style, schema); // checkCRS(schema.getCoordinateReferenceSystem()); // Jody: added for consistency Layer layer = new FeatureLayer(featureCollection, layerStyle); this.layers().add(layer); } /** * Add the given collection as a new layer to the end of the list of layers held by this context * and trigger a {@linkplain MapLayerListEvent}. This is a convenience method equivalent to * {@linkplain #addLayer}(new DefaultMapLayer(collection, style). * * @param collection * the collection of features for the new layer * @param style * a Style object to be used in rendering this layer */ @SuppressWarnings("unchecked") public void addLayer(Collection collection, Style style) { if (collection instanceof FeatureCollection) { FeatureCollection featureCollection = (FeatureCollection) collection; addLayer( featureCollection, style); } else { throw new IllegalArgumentException("FeatureCollection required"); } } /** * If a CRS has not been defined for this context, attempt to get one from this map layer and * set it as the context's CRS. Invoked by addLayer methods. * * @param layer * a map layer being added to the context */ // @SuppressWarnings("deprecation") // private void checkCRS(MapLayer layer) { // FeatureSource<? extends FeatureType, ? extends Feature> featureSource = layer // .getFeatureSource(); // if (featureSource != null) { // checkCRS(featureSource); // } else { // CollectionSource source = layer.getSource(); // if (source != null) { // checkCRS(source.getCRS()); // } // } // } /** * If a CRS has not been defined for this context, attempt to get one from this featureSource * and set it as the context's CRS. Invoked by addLayer. * * @param featureSource * a feature source being added in a new layer */ // private void checkCRS(FeatureSource<? extends FeatureType, ? extends Feature> featureSource) { // if (featureSource != null) { // checkCRS(featureSource.getSchema().getCoordinateReferenceSystem()); // } // } /** * Sets the viewport CRS if needed. * <p> * This amounts to the viewport taking its default CRS from the first provided layer. */ // private void checkCRS(CoordinateReferenceSystem crs) { // if( viewport != null && viewport.getCoordianteReferenceSystem() != null ){ // if (crs != null) { // getViewport().setCoordinateReferenceSystem(crs); // } // } // } /** * Helper for some addLayer methods that take a Style argument. Checks if the style is null and, * if so, attepts to create a default Style. * * @param style * style argument that was passed to addLayer * @param featureType * feature type for which a default style be will made if required * * @return the input Style object if not {@code null}, or a Style instance for a default style */ private Style checkStyle(Style style, FeatureType featureType) { if (style != null) { return style; } Style defaultStyle = SLD.createSimpleStyle(featureType); if (defaultStyle == null) { throw new IllegalStateException("Failed to creaate a default style for the layer"); } return defaultStyle; } /** * Remove the given layer from this context, if present, and trigger a * {@linkplain MapLayerListEvent} * * @param layer * the layer to be removed * * @return true if the layer was present; false otherwise */ public boolean removeLayer(MapLayer layer) { int index = indexOf(layer); // getLayerBounds(); if (index == -1) { return false; } else { removeLayer(index); return true; } } /** * Remove the layer at the given position in the list of layers held by this context. The * position must be valid or an IndexOutOfBoundsException will result. CAlling this method * triggers a {@linkplain MapLayerListEvent}. * * @param index * the position of the layer in this context's list of layers * * @return the layer that was removed */ public MapLayer removeLayer(int index) { Layer removed = layers().remove(index); return new DefaultMapLayer(removed); } /** * Add an array of new layers to this context and trigger a {@link MapLayerListEvent}. * * @param layers * the new layers that are to be added. * * @return the number of new layers actually added (will be less than the length of the layers * array if some layers were already present) */ @SuppressWarnings("deprecation") public int addLayers(MapLayer[] array) { if ((array == null) || (array.length == 0)) { return 0; } List<Layer> layersToAdd = toLayerList(array); int count = layers().addAllAbsent(layersToAdd); return count; } /** * Remove an array of layers, if present, and trigger a {@link MapLayerListEvent}. * * @param layers * The layers that are to be removed. */ public void removeLayers(MapLayer[] array) { if ((array == null) || (array.length == 0) || layers().isEmpty()) { return; } List<Layer> layersToRemove = toLayerList(array); layers().removeAll(layersToRemove); } /** * Return this model's list of layers. If no layers are present, then an empty array is * returned. * * @return This model's list of layers. */ @SuppressWarnings("deprecation") public MapLayer[] getLayers() { MapLayer[] array = new MapLayer[layers().size()]; int index = 0; for (Iterator<Layer> iter = layers().iterator(); iter.hasNext(); index++) { Layer layer = iter.next(); array[index] = new DefaultMapLayer(layer); } return array; } /** * Return the requested layer. * * @param index * index of layer to return. * * @return the layer at the specified position * * @throws IndexOutOfBoundsException * if the index is out of range */ @SuppressWarnings("deprecation") public MapLayer getLayer(int index) throws IndexOutOfBoundsException { Layer layer = layers().get(index); return new DefaultMapLayer(layer); } /** * Remove all of the map layers from this context. This triggers a MapLayerListEvent. * */ public void clearLayerList() { layers().clear(); } @Override public synchronized MapViewport getViewport() { if(viewport == null) { viewport = new MapViewport(); try { ReferencedEnvelope layerBounds = getLayerBounds(); if(layerBounds != null) { viewport.setBounds( layerBounds ); viewport.setCoordinateReferenceSystem(layerBounds.getCoordinateReferenceSystem()); } } catch (IOException e) { } } return viewport; } }