/* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, availible at the root
* application directory.
*/
package org.geoserver.wms;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.geoserver.catalog.CoverageInfo;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.catalog.ResourcePool;
import org.geoserver.catalog.StyleInfo;
import org.geotools.data.FeatureSource;
import org.geotools.factory.Hints;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.styling.FeatureTypeConstraint;
import org.geotools.styling.Style;
import org.opengis.coverage.grid.GridCoverageReader;
import org.opengis.feature.Feature;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.FeatureType;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
/**
* DOCUMENT ME!
*
* @author $Author: Alessio Fabiani (alessio.fabiani@gmail.com) $ (last modification)
* @author $Author: Simone Giannecchini (simboss1@gmail.com) $ (last modification)
* @author Gabriel Roldan
*/
public final class MapLayerInfo {
public static int TYPE_VECTOR = LayerInfo.Type.VECTOR.getCode();
public static int TYPE_RASTER = LayerInfo.Type.RASTER.getCode();
public static int TYPE_REMOTE_VECTOR = LayerInfo.Type.REMOTE.getCode();
/**
* The feature source for the remote WFS layer (see REMOVE_OWS_TYPE/URL in the SLD spec)
*/
private final FeatureSource<SimpleFeatureType, SimpleFeature> remoteFeatureSource;
/**
*
* @uml.property name="type" multiplicity="(0 1)"
*/
private final int type;
/**
*
* @uml.property name="name" multiplicity="(0 1)"
*/
private final String name;
/**
*
* @uml.property name="label" multiplicity="(0 1)"
*/
private final String label;
/**
*
* @uml.property name="description" multiplicity="(0 1)"
*/
private final String description;
private final LayerInfo layerInfo;
private Style style;
/**
* The extra constraints that can be set when an external SLD is used
*/
private FeatureTypeConstraint[] layerFeatureConstraints;
public MapLayerInfo(FeatureSource<SimpleFeatureType, SimpleFeature> remoteSource) {
this.remoteFeatureSource = remoteSource;
this.layerInfo = null;
name = remoteFeatureSource.getSchema().getTypeName();
label = name;
description = "Remote WFS";
type = TYPE_REMOTE_VECTOR;
}
public MapLayerInfo(LayerInfo layerInfo) {
this.layerInfo = layerInfo;
this.remoteFeatureSource = null;
ResourceInfo resource = layerInfo.getResource();
// handle InlineFeatureStuff
this.name = resource.getPrefixedName();
this.label = resource.getTitle();
this.description = resource.getAbstract();
this.type = layerInfo.getType().getCode();
}
public Style getStyle() {
return style;
}
public void setStyle(Style style) {
this.style = style;
}
/**
* <p>
* The feature source bounds. Mind, it might be null, in that case, grab the lat/lon bounding
* box and reproject to the native bounds.
* </p>
*
* @return Envelope the feature source bounds.
* @throws Exception
*/
public ReferencedEnvelope getBoundingBox() throws Exception {
if (layerInfo != null) {
ResourceInfo resource = layerInfo.getResource();
ReferencedEnvelope bbox = resource.boundingBox();
// if(bbox == null){
// bbox = resource.getLatLonBoundingBox();
// }
return bbox;
} else if (this.type == TYPE_REMOTE_VECTOR) {
return remoteFeatureSource.getBounds();
}
return null;
}
/**
* Get the bounding box in latitude and longitude for this layer.
*
* @return Envelope the feature source bounds.
*
* @throws IOException
* when an error occurs
*/
public ReferencedEnvelope getLatLongBoundingBox() throws IOException {
if (layerInfo != null) {
ResourceInfo resource = layerInfo.getResource();
return resource.getLatLonBoundingBox();
}
throw new UnsupportedOperationException("getLatLongBoundingBox not "
+ "implemented for remote sources");
}
/**
*
* @uml.property name="coverage"
*/
public CoverageInfo getCoverage() {
return (CoverageInfo) layerInfo.getResource();
}
/**
*
* @uml.property name="description"
*/
public String getDescription() {
return description;
}
/**
*
* @uml.property name="feature"
*/
public FeatureTypeInfo getFeature() {
return (FeatureTypeInfo) layerInfo.getResource();
}
/**
*
* @uml.property name="label"
*/
public String getLabel() {
return label;
}
/**
*
* @uml.property name="name"
*/
public String getName() {
return name;
}
/**
*
* @uml.property name="type"
*/
public int getType() {
return type;
}
public Style getDefaultStyle() {
if (layerInfo != null) {
try {
return layerInfo.getDefaultStyle().getStyle();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return null;
}
/**
* Returns the remote feature source in case this layer is a remote WFS layer
*
* @return
*/
public FeatureSource<SimpleFeatureType, SimpleFeature> getRemoteFeatureSource() {
return remoteFeatureSource;
}
/**
* @return the resource SRS name or {@code null} if the underlying resource is not a registered
* one
*/
public String getSRS() {
if (layerInfo != null) {
return layerInfo.getResource().getSRS();
}
return null;
}
/**
* @return the list of the alternate style names registered for this layer
*/
public List<String> getOtherStyleNames() {
if (layerInfo == null) {
return Collections.emptyList();
}
final List<String> styleNames = new ArrayList<String>();
for (StyleInfo si : layerInfo.getStyles()) {
styleNames.add(si.getName());
}
return styleNames;
}
/**
* Should we add the cache-control: max-age header to maps containing this layer?
*
* @return true if we should, false if we should omit the header
*/
public boolean isCachingEnabled() {
if (layerInfo == null) {
return false;
}
ResourceInfo resource = layerInfo.getResource();
Boolean cachingEnabled = resource.getMetadata().get("cachingEnabled",Boolean.class);
return cachingEnabled == null ? false : cachingEnabled.booleanValue();
}
/**
* This value is added the headers of generated maps, marking them as being both "cache-able"
* and designating the time for which they are to remain valid. The specific header added is
* "Cache-Control: max-age="
*
* @return the number of seconds to be added to the "Cache-Control: max-age=" header, or {@code
* 0} if not set
*/
public int getCacheMaxAge() {
if (layerInfo == null) {
return 0;
}
ResourceInfo resource = layerInfo.getResource();
Integer val = resource.getMetadata().get("cacheAgeMax",Integer.class);
return val == null ? 0 : val;
}
/**
* If this layers has been setup to reproject data, skipReproject = true will disable
* reprojection. This method is build especially for the rendering subsystem that should be able
* to perform a full reprojection on its own, and do generalization before reprojection (thus
* avoid to reproject all of the original coordinates)
*/
public FeatureSource<? extends FeatureType, ? extends Feature> getFeatureSource(
boolean skipReproject) throws IOException {
if (type != TYPE_VECTOR) {
throw new IllegalArgumentException("Layer type is not vector");
}
// ask for enabled() instead of isEnabled() to account for disabled resource/store
if (!layerInfo.enabled()) {
throw new IOException("featureType: " + getName()
+ " does not have a properly configured " + "datastore");
}
FeatureTypeInfo resource = (FeatureTypeInfo) layerInfo.getResource();
if (resource.getStore() == null || resource.getStore().getDataStore(null) == null) {
throw new IOException("featureType: " + getName()
+ " does not have a properly configured " + "datastore");
}
Hints hints = new Hints(ResourcePool.REPROJECT, Boolean.valueOf(!skipReproject));
return resource.getFeatureSource(null, hints);
}
public GridCoverageReader getCoverageReader() throws IOException {
if (type != TYPE_RASTER) {
throw new IllegalArgumentException("Layer type is not raster");
}
CoverageInfo resource = (CoverageInfo) layerInfo.getResource();
GridCoverageReader coverageReader = resource.getGridCoverageReader(null, null);
return coverageReader;
}
public CoordinateReferenceSystem getCoordinateReferenceSystem() {
if (layerInfo != null) {
return layerInfo.getResource().getCRS();
}
if (remoteFeatureSource != null) {
SimpleFeatureType schema = remoteFeatureSource.getSchema();
return schema.getCoordinateReferenceSystem();
}
throw new IllegalStateException();
}
public static String getRegionateAttribute(FeatureTypeInfo layerInfo) {
return layerInfo.getMetadata().get("kml.regionateAttribute",String.class);
}
public void setLayerFeatureConstraints(FeatureTypeConstraint[] layerFeatureConstraints) {
this.layerFeatureConstraints = layerFeatureConstraints;
}
public FeatureTypeConstraint[] getLayerFeatureConstraints() {
return layerFeatureConstraints;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + type;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MapLayerInfo other = (MapLayerInfo) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (type != other.type)
return false;
return true;
}
}