/* 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.vfny.geoserver.config; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.GeometryFactory; import org.geotools.referencing.CRS; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.AttributeDescriptor; import org.opengis.feature.type.GeometryDescriptor; import org.opengis.filter.Filter; import org.opengis.referencing.FactoryException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.vfny.geoserver.global.FeatureTypeInfo; import org.vfny.geoserver.global.dto.AttributeTypeInfoDTO; import org.vfny.geoserver.global.dto.CloneLibrary; import org.vfny.geoserver.global.dto.FeatureTypeInfoDTO; import org.vfny.geoserver.global.dto.FeatureTypeInfoDTO; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.logging.Logger; /** * User interface SimpleFeatureType staging area. * * @author dzwiers, Refractions Research, Inc. * @version $Id$ */ public class FeatureTypeConfig { protected static Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.vfny.geoserver.config"); /** The Id of the datastore which should be used to get this featuretype. */ private String dataStoreId; /** An EPSG:4326 bounding box for this featuretype */ private Envelope latLongBBox; /** A native CRS bounding box for this featuretype */ private Envelope nativeBBox; /** native wich EPGS code for the FeatureTypeInfo */ private int SRS; /** * Either force declared or reproject from native to declared, see {@link FeatureTypeInfo#FORCE} * and {@link FeatureTypeInfo#REPROJECT} */ private int SRSHandling; /** * This is an ordered list of AttributeTypeInfoConfig. * <p> * These attribtue have been defined by the user (or schema.xml file). * Additional attribute may be assumed based on the schemaBase * </p> * <p> * If this is <code>null</code>, all Attribtue information * will be generated. An empty list is used to indicate that only * attribtues indicated by the schemaBase will be returned. * </p> */ private List schemaAttributes; /** Name (must match DataStore typeName). */ private String name; /** * The user facing alias for this feature type, if any */ private String alias; /** * */ private String wmsPath; /** * The schema name. * <p> * Usually name + "_Type" * </p> */ private String schemaName; /** * The schema base. * <p> * The schema base is used to indicate additional attribtues, not defined * by the user. These attribute are fixed -not be edited by the user. * </p> * <p> * This easiest is "AbstractFeatureType" * </p> */ private String schemaBase; /** * The featuretype directory name. * <p> * This is used to write to, and is stored because it may be longer than * the name, as this often includes information about the source of the * featuretype. * </p> * <p> * A common naming convention is: <code>dataStoreId + "_" + name</code> * </p> */ private String dirName; /** * The featuretype title. * <p> * Not sure what this is used for - usually name+"_Type" */ private String title; /** The feature type abstract, short explanation of this featuretype. */ private String _abstract; /** * A list of keywords to associate with this featuretype. * <p> * Keywords are destinct strings, often rendered surrounded by brackets * to aid search engines. * </p> */ private Set keywords; /** * A list of metadata links to associate with this featuretype. * <p> * Metadata URLs are distinct URLs. * </p> */ private Set metadataLinks; /** Configuration information used to specify numeric percision */ private int numDecimals; /** * Filter used to limit query. * <p> * TODO: Check the following comment - I don't belive it. * The list of exposed attributes. If the list is empty or not present at * all, all the FeatureTypeInfo's attributes are exposed, if is present, * only those oattributes in this list will be exposed by the services * </p> */ private Filter definitionQuery = null; /** * The default style name. */ private String defaultStyle; /** * Other WMS Styles */ private ArrayList styles; /** * A featureType-specific override for the defaultMaxAge defined in WMSConfig. 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 "CacheControl: max-age=" */ private String cacheMaxAge; /** * Should we be adding the CacheControl: max-age header to outgoing maps which include this layer? */ private boolean cachingEnabled; /** * Should we list this layer when crawlers request the sitemap? */ private boolean indexingEnabled; /** * The name of the property to use when regionating using the attribute strategy. */ private String regionateAttribute; private String regionateStrategy; private int regionateFeatureLimit; private String nameTemplate; /** * The maximum number of features to be served for this feature type (it's understood * it's less than the global maxFeatures). 0 is used as the "no limit" flag */ private int maxFeatures = 0; /** * Package visible constructor for test cases */ FeatureTypeConfig() { } /** * Creates a FeatureTypeInfo to represent an instance with default data. * * @param dataStoreId ID for data store in catalog * @param schema Geotools2 SimpleFeatureType * @param generate True to generate entries for all attribtues */ public FeatureTypeConfig(String dataStoreId, SimpleFeatureType schema, boolean generate) { if ((dataStoreId == null) || (dataStoreId.length() == 0)) { throw new IllegalArgumentException("dataStoreId is required for FeatureTypeConfig"); } if (schema == null) { throw new IllegalArgumentException("SimpleFeatureType is required for FeatureTypeConfig"); } this.dataStoreId = dataStoreId; latLongBBox = new Envelope(); nativeBBox = new Envelope(); SRS = lookupSRS(schema.getGeometryDescriptor()); if (generate) { this.schemaAttributes = new ArrayList(); for (int i = 0; i < schema.getAttributeCount(); i++) { AttributeDescriptor attrib = schema.getDescriptor(i); this.schemaAttributes.add(new AttributeTypeInfoConfig(attrib)); } } else { this.schemaAttributes = null; } defaultStyle = ""; styles = new ArrayList(); name = schema.getTypeName(); wmsPath = "/"; title = schema.getTypeName() + "_Type"; _abstract = "Generated from " + dataStoreId; keywords = new HashSet(); keywords.add(dataStoreId); keywords.add(name); metadataLinks = new HashSet(); numDecimals = 8; definitionQuery = null; dirName = dataStoreId + "_" + name; schemaName = name + "_Type"; schemaBase = "gml:AbstractFeatureType"; cachingEnabled = false; cacheMaxAge = null; indexingEnabled = false; regionateAttribute = ""; regionateStrategy = "best_guess"; regionateFeatureLimit = 15; nameTemplate = null; } /** * TODO: this method is duplicated with CoveragesEditorAction and should be replaced by * an equivalent method in CRS class. Once the methods is added, forward to the CRS class. * @param defaultGeometry * @return */ private int lookupSRS(GeometryDescriptor defaultGeometry) { // NPE avoidance if (defaultGeometry == null) { return -1; } // try the (deprecated) geometry factory, we don't want to break data stores that // do correctly set it //GeometryFactory geometryFactory = defaultGeometry.getGeometryFactory(); Integer epsgCode = null; try { if(defaultGeometry.getCoordinateReferenceSystem() != null) epsgCode = CRS.lookupEpsgCode(defaultGeometry.getCoordinateReferenceSystem(),true); } catch (FactoryException e) { //log this? } if ( epsgCode != null ) { return epsgCode.intValue(); } return 0; // // try to reverse engineer the SRID from the coordinate system // CoordinateReferenceSystem ref = defaultGeometry.getCoordinateSystem(); // String code = CRS.lookupIdentifier(ref, Collections.singleton("EPSG"), true); // if(code == null) // return 0; // if(code.startsWith("EPSG:")) { // code = code.substring(5); // } // try { // return Integer.parseInt(code); // } catch(NumberFormatException e) { // LOGGER.severe("Could not parse EPSG code: " + code); // return 0; // } } /** * FeatureTypeInfo constructor. * * <p> * Creates a copy of the FeatureTypeInfoDTO provided. All the data * structures are cloned. * </p> * * @param dto The FeatureTypeInfoDTO to copy. * * @throws NullPointerException DOCUMENT ME! */ public FeatureTypeConfig(FeatureTypeInfoDTO dto) { if (dto == null) { throw new NullPointerException("Non null FeatureTypeInfoDTO required"); } dataStoreId = dto.getDataStoreId(); latLongBBox = new Envelope(dto.getLatLongBBox()); nativeBBox = dto.getNativeBBox() != null ? new Envelope(dto.getNativeBBox()) : null; SRS = dto.getSRS(); SRSHandling = dto.getSRSHandling(); if (dto.getSchemaAttributes() == null) { schemaAttributes = null; } else { schemaAttributes = new LinkedList(); Iterator i = dto.getSchemaAttributes().iterator(); while (i.hasNext()) { schemaAttributes.add(new AttributeTypeInfoConfig((AttributeTypeInfoDTO) i.next())); } } name = dto.getName(); alias = dto.getAlias(); wmsPath = dto.getWmsPath(); title = dto.getTitle(); _abstract = dto.getAbstract(); numDecimals = dto.getNumDecimals(); definitionQuery = dto.getDefinitionQuery(); try { keywords = new HashSet(dto.getKeywords()); } catch (Exception e) { keywords = new HashSet(); } try { metadataLinks = new HashSet(dto.getMetadataLinks()); } catch (Exception e) { metadataLinks = new HashSet(); } defaultStyle = dto.getDefaultStyle(); styles = dto.getStyles(); dirName = dto.getDirName(); schemaName = dto.getSchemaName(); schemaBase = dto.getSchemaBase(); cachingEnabled = dto.isCachingEnabled(); cacheMaxAge = dto.getCacheMaxAge(); maxFeatures = dto.getMaxFeatures(); indexingEnabled = dto.isIndexingEnabled(); regionateAttribute = dto.getRegionateAttribute(); regionateStrategy = dto.getRegionateStrategy(); regionateFeatureLimit = dto.getRegionateFeatureLimit(); nameTemplate = null; } /** * Implement toDTO. * * <p> * Creates a represetation of this object as a FeatureTypeInfoDTO * </p> * * @return a representation of this object as a FeatureTypeInfoDTO * * @see org.vfny.geoserver.config.DataStructure#toDTO() */ public FeatureTypeInfoDTO toDTO() { FeatureTypeInfoDTO f = new FeatureTypeInfoDTO(); f.setDataStoreId(dataStoreId); f.setLatLongBBox(CloneLibrary.clone(latLongBBox)); f.setNativeBBox(CloneLibrary.clone(nativeBBox)); f.setSRS(SRS); f.setSRSHandling(SRSHandling); if (schemaAttributes == null) { // Use generated default attributes f.setSchemaAttributes(null); } else { // Use user provided attribtue + schemaBase attribtues List s = new ArrayList(); for (int i = 0; i < schemaAttributes.size(); i++) { s.add(((AttributeTypeInfoConfig) schemaAttributes.get(i)).toDTO()); } f.setSchemaAttributes(s); } f.setName(name); f.setAlias(alias); f.setWmsPath(wmsPath); f.setTitle(title); f.setAbstract(_abstract); f.setNumDecimals(numDecimals); f.setDefinitionQuery(definitionQuery); try { f.setKeywords(new ArrayList(keywords)); } catch (Exception e) { // do nothing, defaults already exist. } try { f.setMetadataLinks(new ArrayList(metadataLinks)); } catch (Exception e) { // do nothing, defaults already exist. } f.setDefaultStyle(defaultStyle); f.setStyles(styles); // override the dir name to make sure if(alias != null) f.setDirName(dataStoreId + "_" + alias); else if(dirName.endsWith(name)) f.setDirName(dirName); else f.setDirName(dataStoreId + "_" + name); f.setSchemaBase(schemaBase); f.setSchemaName(schemaName); f.setCachingEnabled(cachingEnabled); f.setCacheMaxAge(cacheMaxAge); f.setMaxFeatures(maxFeatures); f.setIndexingEnabled(indexingEnabled); f.setRegionateAttribute(regionateAttribute); f.setRegionateStrategy(regionateStrategy); f.setRegionateFeatureLimit(regionateFeatureLimit); f.setNameTemplate(nameTemplate); return f; } /** * Searches through the schema looking for an AttributeTypeInfoConfig that * matches the name passed in attributeTypeName * * @param attributeTypeName the name of the AttributeTypeInfo to search * for. * * @return AttributeTypeInfoConfig from the schema, if found */ public AttributeTypeInfoConfig getAttributeFromSchema(String attributeTypeName) { Iterator iter = schemaAttributes.iterator(); while (iter.hasNext()) { AttributeTypeInfoConfig atiConfig = (AttributeTypeInfoConfig) iter.next(); if (atiConfig.getName().equals(attributeTypeName)) { return atiConfig; } } return null; } /** * Convience method for dataStoreId.typeName. * * <p> * This key may be used to store this SimpleFeatureType in a Map for later. * </p> * * @return dataStoreId.typeName */ public String getKey() { return getDataStoreId() + DataConfig.SEPARATOR + getName(); } /** * Access _abstract property. * * @return Returns the _abstract. */ public String getAbstract() { return _abstract; } /** * Set _abstract to _abstract. * * @param _abstract The _abstract to set. */ public void setAbstract(String _abstract) { this._abstract = _abstract; } /** * Access dataStoreId property. * * @return Returns the dataStoreId. */ public String getDataStoreId() { return dataStoreId; } /** * Set dataStoreId to dataStoreId. * * @param dataStoreId The dataStoreId to set. */ public void setDataStoreId(String dataStoreId) { this.dataStoreId = dataStoreId; } /** * Access defaultStyle property. * * @return Returns the defaultStyle. */ public String getDefaultStyle() { return defaultStyle; } /** * Set defaultStyle to defaultStyle. * * @param defaultStyle The defaultStyle to set. */ public void setDefaultStyle(String defaultStyle) { this.defaultStyle = defaultStyle; } public ArrayList getStyles() { return styles; } public void setStyles(ArrayList styles) { this.styles = styles; } public void addStyle(String style) { if (!this.styles.contains(style)) { this.styles.add(style); } } /** * Access definitionQuery property. * * @return Returns the definitionQuery. */ public Filter getDefinitionQuery() { return definitionQuery; } /** * Set definitionQuery to definitionQuery. * * @param definitionQuery The definitionQuery to set. */ public void setDefinitionQuery(Filter definitionQuery) { this.definitionQuery = definitionQuery; } /** * Access dirName property. * * @return Returns the dirName. */ public String getDirName() { return dirName; } /** * Set dirName to dirName. * * @param dirName The dirName to set. */ public void setDirName(String dirName) { this.dirName = dirName; } /** * Access keywords property. * * @return Returns the keywords. */ public Set getKeywords() { return keywords; } /** * Set keywords to keywords. * * @param keywords The keywords to set. */ public void setKeywords(Set keywords) { this.keywords = keywords; } /** * Access metadataURLs property. * * @return Returns the metadataURLs. */ public Set getMetadataLinks() { return metadataLinks; } /** * Set metadataURLs to metadataURLs. * * @param metadataURLs The metadataURLs to set. */ public void setMetadataLinks(Set metadataURLs) { this.metadataLinks = metadataURLs; } /** * Access latLongBBox property. * * @return Returns the latLongBBox. */ public Envelope getLatLongBBox() { return latLongBBox; } /** * Set latLongBBox to latLongBBox. * * @param latLongBBox The latLongBBox to set. */ public void setLatLongBBox(Envelope latLongBBox) { this.latLongBBox = latLongBBox; } /** * Access nativeBBox property. * * @return Returns the nativeBBox. */ public Envelope getNativeBBox() { return nativeBBox; } /** * Set nativeBBox to nativeBBox. * * @param nativeBBox The nativeBBox to set. */ public void setNativeBBox(Envelope nativeBBox) { this.nativeBBox = nativeBBox; } /** * Access name property. * * @return Returns the name. */ public String getName() { return name; } /** * Set name to name. * * @param name The name to set. */ public void setName(String name) { this.name = name; } /** * Access numDecimals property. * * @return Returns the numDecimals. */ public int getNumDecimals() { return numDecimals; } /** * Set numDecimals to numDecimals. * * @param numDecimals The numDecimals to set. */ public void setNumDecimals(int numDecimals) { this.numDecimals = numDecimals; } /** * Access schemaAttributes property. * * @return Returns the schemaAttributes. */ public List getSchemaAttributes() { return schemaAttributes; } /** * Set schemaAttributes to schemaAttributes. * * @param schemaAttributes The schemaAttributes to set. */ public void setSchemaAttributes(List schemaAttributes) { this.schemaAttributes = schemaAttributes; } /** * Access schemaBase property. * * @return Returns the schemaBase. */ public String getSchemaBase() { return schemaBase; } /** * Set schemaBase to schemaBase. * * @param schemaBase The schemaBase to set. */ public void setSchemaBase(String schemaBase) { this.schemaBase = schemaBase; } /** * Access schemaName property. * * @return Returns the schemaName. */ public String getSchemaName() { return schemaName; } /** * Set schemaName to schemaName. * * @param schemaName The schemaName to set. */ public void setSchemaName(String schemaName) { this.schemaName = schemaName; } /** * Access sRS property. * * @return Returns the sRS. */ public int getSRS() { return SRS; } /** * Set sRS to srs. * * @param srs The sRS to set. */ public void setSRS(int srs) { SRS = srs; } public int getSRSHandling() { return SRSHandling; } public void setSRSHandling(int srsHandling) { this.SRSHandling = srsHandling; } /** * Access title property. * * @return Returns the title. */ public String getTitle() { return title; } /** * Set title to title. * * @param title The title to set. */ public void setTitle(String title) { this.title = title; } public String toString() { return "FeatureTypeConfig[name: " + name + " alias: " + alias + " schemaName: " + schemaName + " SRS: " + SRS + " schemaAttributes: " + schemaAttributes + " schemaBase " + schemaBase + "]"; } public String getWmsPath() { return wmsPath; } public void setWmsPath(String wmsPath) { this.wmsPath = wmsPath; } public boolean isCachingEnabled() { return cachingEnabled; } public boolean isIndexingEnabled() { return indexingEnabled; } /** * Which property should we use when regionating using the attribute strategy? * @return the name of the property */ public String getRegionateAttribute(){ return regionateAttribute; } public String getRegionateStrategy(){ return regionateStrategy; } public int getRegionateFeatureLimit(){ return regionateFeatureLimit; } public void setCachingEnabled(boolean cachingEnabled) { this.cachingEnabled = cachingEnabled; } public void setIndexingEnabled(boolean indexingEnabled){ this.indexingEnabled = indexingEnabled; } public void setNameTemplate(String name){ this.nameTemplate = name; } /** * Set which property to use when regionating using the attribute strategy. * @param attr the name of the property */ public void setRegionateAttribute(String attr){ this.regionateAttribute = attr; } public void setRegionateStrategy(String strategy){ this.regionateStrategy = strategy; } public void setRegionateFeatureLimit(int limit){ this.regionateFeatureLimit = limit; } public String getCacheMaxAge() { return cacheMaxAge; } public void setCacheMaxAge(String cacheMaxAge) { this.cacheMaxAge = cacheMaxAge; } public int getMaxFeatures() { return maxFeatures; } public void setMaxFeatures(int maxFeatures) { this.maxFeatures = maxFeatures; } public String getAlias() { return alias; } public void setAlias(String alias) { this.alias = alias; } }