/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2004-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.data.ows;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import org.geotools.data.wms.xml.Dimension;
import org.geotools.data.wms.xml.Extent;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.opengis.geometry.Envelope;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.TransformException;
/**
* Nested list of zero or more map Layers offered by this server. It contains only fields for
* information that we currently find interesting. Feel free to add your own.
*
* @author rgould
* @source $URL:
* http://svn.osgeo.org/geotools/branches/2.6.x/modules/extension/wms/src/main/java/org
* /geotools/data/ows/Layer.java $
*/
public class Layer implements Comparable<Layer> {
/** A machine-readable (typically one word) identifier */
private String name;
/**
* Parent Layer may be null if this is the root Layer for a WMS Service
*/
private Layer parent;
/**
* Child Layers (Note we should clear the children cache if any of setter methods are called)
*/
private List<Layer> children = new ArrayList<Layer>();
/** The title is for informative display to a human. */
private String title;
private String _abstract;
private String[] keywords;
/**
* A set of Strings representing SRSs. These are the SRSs contributed by this layer. For the
* complete list you need to consider these values and those defined by its parent.
*/
private Set<String> srs = null;
/**
* The bounding boxes on each layer; usually this matches the actual data coordinate reference
* system (compare with latLonBoundingBox) near as I can tell multiple boundingBoxes are here to
* account for either data that moves over time; or compound layers that combine several data
* sets.
*/
private List<CRSEnvelope> boundingBoxes = new ArrayList<CRSEnvelope>();
/**
* A BoundingBox containing the minimum rectangle of the map data in EPSG:4326
*/
private CRSEnvelope latLonBoundingBox = null;
/** A list of type org.opengis.layer.Style */
private List<StyleImpl> styles;
/** Indicates if this layer responds to a GetFeatureInfo query */
private Boolean queryable = null;
private double scaleHintMin = Double.NaN;
private double scaleHintMax = Double.NaN;
private double scaleDenominatorMin = Double.NaN;
private double scaleDenominatorMax = Double.NaN;
private List<Dimension> dimensions = new ArrayList<Dimension>();
private List<Extent> extents = new ArrayList<Extent>();
// Cache
// These cache data structures are used to store the union of this Layers definition
// with those inherited from the parent layer.
//
// These are all null by default; and populated as needed to respond to get methods;
// clearCache() reset's these caches to null
//
/**
* The union of the layers's SRSs and the parent's SRSs. This cache is used to check if the SRS
* is known to work for this layer
*/
private Set<String> allSRSCache = null;
/**
* The union of the layer's boundingBoxes and the parent's bounding boxes.
*/
private Map<String, CRSEnvelope> allBoundingBoxesCache = null;
/**
* A HashMap recording the dimensions on each layer. The Key is the name of the dimension (case
* insensitive). The Value is the Dimension object itself. Chances are you need to check the
* parent Dimensions as well.
*/
private HashMap<String, Dimension> allDimensionsCache = null;
/**
* A HashMap recoding the extents on each layer. An Extent is not valid unless there is a
* Dimension with the same name. The Key is the name of the dimension (case insensitive). The
* Value is the Extent object itself.
*/
private HashMap<String, Extent> allExtentsCache = null;
/**
* This is where we try and go from our rather lame CRSEnvelope data structure to an actual
* RefernecedEnvelope with a real CoordianteReferenceSystem.
*/
private Map<CoordinateReferenceSystem, Envelope> envelopeCache = Collections
.synchronizedMap(new WeakHashMap<CoordinateReferenceSystem, Envelope>());
/**
* Called to clear the internal cache of this layer; and any children.
*/
public void clearCache() {
allSRSCache = null;
allExtentsCache = null;
allDimensionsCache = null;
allBoundingBoxesCache = null;
envelopeCache.clear();
for( Layer child : children ){
child.clearCache();
}
}
/**
* Crate a layer with no human readable title.
* <p>
* These layers are simply for organization and storage of common settings (like SRS or style
* settings). These settings will be valid for all children.
*/
public Layer() {
this(null);
}
/**
* Create a layer with an optional title
*
* @param title
*/
public Layer(String title) {
this.title = title;
}
/**
* Get the BoundingBoxes associated with this layer.
* <p>
* If you modify the contents of this List please call clearCache() so that the
* getBoundingBoxes() method can return the correct combination of this list and the parent
* bounding boxes.
*/
public List<CRSEnvelope> getLayerBoundingBoxes() {
return boundingBoxes;
}
/**
* Returns every BoundingBox associated with this layer. The <code>HashMap</code> returned has
* each bounding box's SRS Name (usually an EPSG code) value as the key, and the value is the
* <code>BoundingBox</code> object itself.
*
* Implements inheritance: if this layer's bounding box is null, query ancestors until the first
* bounding box is found or no more ancestors
*
* @return a HashMap of all of this layer's bounding boxes or null if no bounding boxes found
*/
public synchronized Map<String, CRSEnvelope> getBoundingBoxes() {
if (allBoundingBoxesCache == null) {
allBoundingBoxesCache = new HashMap<String, CRSEnvelope>();
Layer parent = this.getParent();
while (parent != null) {
for( CRSEnvelope bbox : getLayerBoundingBoxes() ){
allBoundingBoxesCache.put( bbox.getSRSName(), bbox );
}
parent = parent.getParent();
}
}
// May return empty. But that is OK since spec says 0 or more may be specified
return allBoundingBoxesCache;
}
public void setBoundingBoxes(CRSEnvelope boundingBox) {
this.boundingBoxes.clear();
this.boundingBoxes.add( boundingBox );
}
/**
* Sets this layer's bounding boxes. The HashMap must have each BoundingBox's CRS/SRS value as
* its key, and the <code>BoundingBox</code> object as its value.
*
* @param boundingBoxes
* a HashMap containing bounding boxes
*/
public void setBoundingBoxes(Map<String, CRSEnvelope> boundingBoxes) {
this.boundingBoxes.clear();
this.boundingBoxes.addAll(boundingBoxes.values());
}
/**
* Direct access to the dimensions contributed by this Layer.
* For the complete list of Dimensions applicable to the layer
* this value must be combined with any Dimensions supplied by
* the parent - this work is done for you using the getDimensions() method.
* @see getDimensions()
* @return List of Dimensions contributed by this Layer definition
*/
public List<Dimension> getLayerDimensions(){
return dimensions;
}
/**
* The dimensions valid for this layer.
* Includes both getLauerDimensions() and all Dimensions contributed
* by parent layers. The result is an unmodifiable map indexed by Dimension
* name.
* @return Map of valid dimensions for this layer indexed by Dimension name.
*/
public synchronized Map<String, Dimension> getDimensions() {
if( allDimensionsCache == null ){
Layer layer = this;
allDimensionsCache = new HashMap<String, Dimension>();
while (layer != null) {
for( Dimension dimension : layer.getLayerDimensions() ){
allDimensionsCache.put(dimension.getName(), dimension );
}
layer = layer.getParent();
}
}
return Collections.unmodifiableMap( allDimensionsCache );
}
public void setDimensions(Map<String, Dimension> dimensionMap) {
dimensions.clear();
if( dimensionMap != null ){
dimensions.addAll( dimensionMap.values() );
}
clearCache();
}
public void setDimensions(Collection<Dimension> dimensionList) {
dimensions.clear();
if( dimensionList != null ){
dimensions.addAll( dimensionList );
}
clearCache();
}
public void setDimensions( Dimension dimension) {
dimensions.clear();
if( dimension != null ){
dimensions.add( dimension );
}
clearCache();
}
/** Look up a Dimension; note this looks up any parent supplied definitions as well */
public Dimension getDimension(String name) {
return getDimensions().get(name);
}
/**
* The Extents contributed by this Layer.
* <p>
* Please note that for the complete list of Extents valid for this layer
* you should use the getExtents() mehtod which will consider extents defined
* as part of a Dimension and all those contributed by Parent layers.
* <p>
* This is an accessor; if you modify the provided list please call clearCache().
* </p>
* @return Extents directly defined by this layer
*/
public List<Extent> getLayerExtents(){
return extents;
}
/**
* The Extents valid for this layer; this includes both extents defined by this layer
* and all extents contributed by parent layers.
* <p>
* In keeping with the WMS 1.3.0 specification some extents may be defined as part of
* a Dimension definition.
* @return All extents valid for this layer.
*/
public synchronized Map<String, Extent> getExtents() {
if( allExtentsCache == null ){
Layer layer = this;
allExtentsCache = new HashMap<String, Extent>();
while (layer != null) {
for( Extent extent : layer.getLayerExtents() ){
allExtentsCache.put(extent.getName(), extent );
}
for( Dimension dimension : layer.getLayerDimensions() ){
Extent extent = dimension.getExtent(); // only for WMS 1.3.0
if( extent == null || extent.isEmpty() ){
continue;
}
allExtentsCache.put(extent.getName(), extent );
}
layer = layer.getParent();
}
}
return Collections.unmodifiableMap( allExtentsCache );
}
/**
* Look up an extent by name; search includes all parent extent definitions.
* @param name
* @return Extent or null if not found
*/
public Extent getExtent(String name) {
return getExtents().get(name);
}
public void setExtents(Map<String, Extent> extentMap) {
extents.clear();
if( extentMap != null ){
extents.addAll( extentMap.values() );
}
clearCache();
}
public void setExtents(Collection<Extent> extentList) {
extents.clear();
if( extentList != null ){
extents.addAll( extentList );
}
clearCache();
}
public void setExtents(Extent extent) {
extents.clear();
if( extent != null ){
extents.add( extent );
}
clearCache();
}
/**
* Gets the name of the <code>Layer</code>. It is designed to be machine readable, and if it is
* present, this layer is determined to be drawable and is a valid candidate for use in a GetMap
* or GetFeatureInfo request.
*
* @return the machine-readable name of the layer
*/
public String getName() {
return name;
}
/**
* Sets the name of this layer. Giving the layer name indicates that it can be drawn during a
* GetMap or GetFeatureInfo request.
*
* @param name
* the layer's new name
*/
public void setName(String name) {
this.name = name;
}
/**
* Accumulates all of the srs/crs specified for this layer and all srs/crs inherited from its
* ancestors. No duplicates are returned.
*
* @return Set of all srs/crs for this layer and its ancestors
*/
public Set<String> getSrs() {
synchronized (this) {
if (allSRSCache == null) {
allSRSCache = new HashSet<String>(srs);
// Get my ancestor's srs/crs
Layer parent = this.getParent();
if (parent != null) {
Set<String> parentSrs = parent.getSrs();
if (parentSrs != null) // got something, add to accumulation
allSRSCache.addAll(parentSrs);
}
}
// May return an empty list, but spec says at least one must be specified. Perhaps, need
// to check and throw exception if set is empty. I'm leaving that out for now since
// it changes the method signature and would potentially break existing users of this
// class
return allSRSCache;
}
}
public void setSrs(Set<String> srs) {
this.srs = srs;
}
/**
* Accumulates all of the styles specified for this layer and all styles inherited from its
* ancestors. No duplicates are returned.
*
* The List that is returned is of type List<org.opengis.layer.Style>. Before 2.2-RC0 it was of
* type List<java.lang.String>.
*
* @return List of all styles for this layer and its ancestors
*/
public List<StyleImpl> getStyles() {
ArrayList<StyleImpl> allStyles = new ArrayList<StyleImpl>();
// Get my ancestor's styles
Layer parent = this.getParent();
if (parent != null) {
List<StyleImpl> parentStyles = parent.getStyles();
if (parentStyles != null) // got something, add to accumulation
allStyles.addAll(parentStyles);
}
// Now add my styles, if any
// Brute force check for duplicates. The spec says duplicates are not allowed:
// (para 7.1.4.5.4) "A child shall not redefine a Style with the same Name as one
// inherited from a parent. A child may define a new Style with a new Name that is
// not available for the parent Layer."
if ((styles != null) && !styles.isEmpty()) {
for (Iterator<StyleImpl> iter = styles.iterator(); iter.hasNext();) {
StyleImpl style = iter.next();
if (!allStyles.contains(style))
allStyles.add(style);
}
}
// May return an empty list, but that is OK since spec says 0 or more styles may be
// specified
return allStyles;
}
public void setStyles(List<StyleImpl> styles) {
this.styles = styles;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
/**
* Determines if this layer is queryable. Implements inheritance: if this layer's Queryable
* attribute is null, check ancestors until the first Queryable attribute is found or no more
* ancestors. If a Queryable attribute is not found for this layer, it will return the default
* value of false.
*
* @return true is this layer is Queryable
*/
public boolean isQueryable() {
if (queryable == null) {
Layer parent = this.getParent();
while (parent != null) {
Boolean q = parent.getQueryable();
if (q != null)
return q.booleanValue();
else
parent = parent.getParent();
}
// At this point a attribute was not found so return default
return false;
}
return queryable.booleanValue();
}
private Boolean getQueryable() {
return queryable;
}
public void setQueryable(boolean queryable) {
this.queryable = new Boolean(queryable);
}
/*
* (non-Javadoc)
*
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(Layer layer) {
if ((this.getName() != null) && (layer.getName() != null)) {
return this.getName().compareTo(layer.getName());
}
return this.getTitle().compareTo(layer.getTitle());
}
/**
* DOCUMENT ME!
*
* @return Returns the parent.
*/
public Layer getParent() {
return parent;
}
/**
* Set the parent; child will be added to the parents list of children (if it is not already).
*
* @param parent
* The parent to set.
*/
public void setParent(Layer parentLayer) {
this.parent = parentLayer;
if( !parentLayer.children.contains( this )){
parentLayer.children.add( this );
}
}
/**
* Returns the LatLonBoundingBox for this layer. Implements inheritance: if this layer's
* bounding box is null, query ancestors until the first bounding box is found or no more
* ancestors.
*
* @return the LatLonBoundingBox for this layer or null if no lat/lon bounding box is found
*/
public CRSEnvelope getLatLonBoundingBox() {
if (latLonBoundingBox == null) {
Layer parent = this.getParent();
while (parent != null) {
CRSEnvelope llbb = parent.getLatLonBoundingBox();
if (llbb != null)
return llbb;
else
parent = parent.getParent();
}
// We should never get to falling out of the while loop w/o a LatLonBoundingBox
// being found. The WMS spec says one is required. So perhaps if we don't find one,
// then throw an exception. I'm leaving that out for now since it changes the method
// signature
// and would potentially break existing users of this class
}
// May return null!
return latLonBoundingBox;
}
public void setLatLonBoundingBox(CRSEnvelope latLonBoundingBox) {
this.latLonBoundingBox = latLonBoundingBox;
}
/**
* List of children.
* @return list of children
*/
public List<Layer> getLayerChildren(){
return new AbstractList<Layer>(){
@Override
public Layer get(int index) {
return children.get(index);
}
@Override
public int size() {
return children.size();
}
@Override
public Layer set(int index, Layer element) {
Layer replaced = children.set(index, element );
replaced.parent = null;
element.parent = Layer.this;
return replaced;
}
@Override
public void add(int index, Layer element) {
children.add( index, element );
element.parent = Layer.this;
}
@Override
public Layer remove(int index) {
Layer removed = children.remove(index);
if( removed != null ){
removed.parent = null;
}
return removed;
}
};
}
public Layer[] getChildren() {
return children.toArray(new Layer[ children.size()]);
}
public void setChildren(Layer[] childrenArray) {
children.clear();
for( Layer child : childrenArray ){
if( child == null || children.contains( child )) {
continue; // skip
}
child.parent = this;
this.children.add( child );
}
}
public void addChildren( Layer child ){
child.parent = this;
children.add( child );
}
/**
* The abstract contains human-readable information about this layer
*
* @return Returns the _abstract.
*/
public String get_abstract() {
return _abstract;
}
/**
* @param _abstract
* The _abstract to set.
*/
public void set_abstract(String _abstract) {
this._abstract = _abstract;
}
/**
* Keywords are Strings to be used in searches
*
* @return Returns the keywords.
*/
public String[] getKeywords() {
return keywords;
}
/**
* @param keywords
* The keywords to set.
*/
public void setKeywords(String[] keywords) {
this.keywords = keywords;
}
/**
* Max scale denominator for which it is appropriate to draw this layer.
* <p>
* Scale denominator is calculated based on the bounding box of the central pixel in a request
* (ie not a scale based on real world size of the entire layer).
*
* @param Max
* scale denominator for which it is approprirate to draw this layer
*/
public void setScaleDenominatorMax(double scaleDenominatorMax) {
this.scaleDenominatorMax = scaleDenominatorMax;
}
/**
* Max scale denominator for which it is appropriate to draw this layer.
* <p>
* Scale denominator is calculated based on the bounding box of the central pixel in a request
* (ie not a scale based on real world size of the entire layer).
* <p>
* Some web map servers will refuse to render images at a scale greater than the value provided
* here.
* <p>
* return Max scale denominator for which it is appropriate to draw this layer.
*/
public double getScaleDenominatorMax() {
return scaleDenominatorMax;
}
/**
* Min scale denominator for which it is appropriate to draw this layer.
* <p>
* Scale denominator is calculated based on the bounding box of the central pixel in a request
* (ie not a scale based on real world size of the entire layer).
*
* @param Min
* scale denominator for which it is appropriate to draw this layer
*/
public void setScaleDenominatorMin(double scaleDenominatorMin) {
this.scaleDenominatorMin = scaleDenominatorMin;
}
/**
* Min scale denominator for which it is appropriate to draw this layer.
* <p>
* Scale denominator is calculated based on the bounding box of the central pixel in a request
* (ie not a scale based on real world size of the entire layer).
* <p>
* Some web map servers will refuse to render images at a scale less than the value provided
* here.
* <p>
* return Min scale denominator for which it is appropriate to draw this layer
*/
public double getScaleDenominatorMin() {
return scaleDenominatorMin;
}
/**
* Maximum scale for which this layer is considered good.
* <p>
* We assume this calculation is done in a similar manner to getScaleDenominatorMax(); but a
* look at common web services such as JPL show this not to be the case.
* <p>
*
* @return The second scale hint value (understood to mean the max value)
* @deprecated Use getScaleDenomiatorMax() as there is less confusion over meaning
*/
public double getScaleHintMax() {
return scaleHintMax;
}
/**
* Maximum scale for which this layer is considered good.
* <p>
* We assume this calculation is done in a similar manner to setScaleDenominatorMax(); but a
* look at common web services such as JPL show this not to be the case.
* <p>
*
* @param The
* second scale hint value (understood to mean the max value)
* @deprecated Use setScaleDenomiatorMax() as there is less confusion over meaning
*/
public void setScaleHintMax(double scaleHintMax) {
this.scaleHintMax = scaleHintMax;
}
/**
* Minimum scale for which this layer is considered good.
* <p>
* We assume this calculation is done in a similar manner to getScaleDenominatorMin(); but a
* look at common web services such as JPL show this not to be the case.
* <p>
*
* @return The first scale hint value (understood to mean the min value)
* @deprecated Use getScaleDenomiatorMin() as there is less confusion over meaning
*/
public double getScaleHintMin() {
return scaleHintMin;
}
/**
* Minimum scale for which this layer is considered good.
* <p>
* We assume this calculation is done in a similar manner to setScaleDenominatorMin(); but a
* look at common web services such as JPL show this not to be the case.
* <p>
* param The first scale hint value (understood to mean the min value)
*
* @deprecated Use setScaleDenomiatorMin() as there is less confusion over meaning
*/
public void setScaleHintMin(double scaleHintMin) {
this.scaleHintMin = scaleHintMin;
}
/**
* Look up an envelope for the provided CoordianteReferenceSystem.
* <p>
* Please note that the lookup is preformed based on the SRS Name of the provided
* CRS which is assumed to be one of its identifiers.
* </p>
* This method returns the first envelope found; this may not be valid for
* sparse data sets that indicate data location using multiple envelopes for
* a provided CRS.
*
* @param crs
* @return GeneralEnvelope matching the provided crs; or null if unavaialble.
*/
public GeneralEnvelope getEnvelope(CoordinateReferenceSystem crs) {
if( crs == null ){
return null;
}
// Check the cache!
GeneralEnvelope found = (GeneralEnvelope) envelopeCache.get(crs);
if (found != null){
return found;
}
Collection<Object> identifiers = new ArrayList<Object>();
identifiers.addAll( crs.getIdentifiers() );
if (crs == DefaultGeographicCRS.WGS84 || crs == DefaultGeographicCRS.WGS84_3D) {
identifiers.add( "EPSG:4326" );
}
for (Object identifier : identifiers ) {
String srsName = identifier.toString();
CRSEnvelope tempBBox = null;
Layer layer = this;
// Locate an exact bounding box if we can
Map<String, CRSEnvelope> boxes = layer.getBoundingBoxes(); // extents for layer and parents
tempBBox = (CRSEnvelope) boxes.get(srsName);
// Otherwise, locate a LatLon bounding box ... if applicable
if (tempBBox == null && ("EPSG:4326".equals(srsName.toUpperCase()))) {
CRSEnvelope latLonBBox = null;
layer = this;
while (latLonBBox == null && layer != null) {
latLonBBox = layer.getLatLonBoundingBox();
if (latLonBBox != null) {
try {
new GeneralEnvelope(new double[] { latLonBBox.getMinX(),
latLonBBox.getMinY() }, new double[] { latLonBBox.getMaxX(),
latLonBBox.getMaxY() });
break;
} catch (IllegalArgumentException e) {
// TODO LOG here
// log("Layer "+layer.getName()+" has invalid bbox declared: "+tempBbox.toString());
latLonBBox = null;
}
}
layer = layer.getParent();
}
if (latLonBBox == null) {
// TODO could convert another bbox to latlon?
tempBBox = new CRSEnvelope("EPSG:4326", -180, -90, 180, 90);
} else {
tempBBox = new CRSEnvelope("EPSG:4326", latLonBBox.getMinX(), latLonBBox
.getMinY(), latLonBBox.getMaxX(), latLonBBox.getMaxY());
}
}
if (tempBBox == null) {
// Haven't found a bbox in the requested CRS. Attempt to transform another bbox
String epsg = null;
if (getLatLonBoundingBox() != null) {
CRSEnvelope latLonBBox = getLatLonBoundingBox();
tempBBox = new CRSEnvelope("EPSG:4326", latLonBBox.getMinX(), latLonBBox
.getMinY(), latLonBBox.getMaxX(), latLonBBox.getMaxY());
epsg = "EPSG:4326";
}
if (tempBBox == null && getBoundingBoxes() != null && getBoundingBoxes().size() > 0) {
tempBBox = (CRSEnvelope) getBoundingBoxes().values().iterator().next();
epsg = tempBBox.getEPSGCode();
}
if (tempBBox == null) {
continue;
}
GeneralEnvelope env = new GeneralEnvelope(new double[] { tempBBox.getMinX(),
tempBBox.getMinY() },
new double[] { tempBBox.getMaxX(), tempBBox.getMaxY() });
CoordinateReferenceSystem fromCRS = null;
try {
fromCRS = CRS.decode(epsg);
ReferencedEnvelope oldEnv = new ReferencedEnvelope(env.getMinimum(0), env
.getMaximum(0), env.getMinimum(1), env.getMaximum(1), fromCRS);
ReferencedEnvelope newEnv = oldEnv.transform(crs, true);
env = new GeneralEnvelope(new double[] { newEnv.getMinimum(0),
newEnv.getMinimum(1) }, new double[] { newEnv.getMaximum(0),
newEnv.getMaximum(1) });
env.setCoordinateReferenceSystem(crs);
// success!!
envelopeCache.put(crs, env);
return env;
} catch (NoSuchAuthorityCodeException e) {
// TODO Catch e
} catch (FactoryException e) {
// TODO Catch e
} catch (MismatchedDimensionException e) {
// TODO Catch e
} catch (TransformException e) {
// TODO Catch e
}
}
// TODO Attempt to figure out the valid area of the CRS and use that.
if (tempBBox != null) {
GeneralEnvelope env = new GeneralEnvelope(new double[] { tempBBox.getMinX(),
tempBBox.getMinY() },
new double[] { tempBBox.getMaxX(), tempBBox.getMaxY() });
env.setCoordinateReferenceSystem(crs);
return env;
}
}
return null;
}
@Override
public String toString() {
if (this.title != null) {
return title;
}
return name;
}
}