001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/model/feature/AbstractFeature.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001-2008 by: 006 EXSE, Department of Geography, University of Bonn 007 http://www.giub.uni-bonn.de/deegree/ 008 lat/lon GmbH 009 http://www.lat-lon.de 010 011 This library is free software; you can redistribute it and/or 012 modify it under the terms of the GNU Lesser General Public 013 License as published by the Free Software Foundation; either 014 version 2.1 of the License, or (at your option) any later version. 015 016 This library is distributed in the hope that it will be useful, 017 but WITHOUT ANY WARRANTY; without even the implied warranty of 018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 019 Lesser General Public License for more details. 020 021 You should have received a copy of the GNU Lesser General Public 022 License along with this library; if not, write to the Free Software 023 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 024 025 Contact: 026 027 Andreas Poth 028 lat/lon GmbH 029 Aennchenstr. 19 030 53115 Bonn 031 Germany 032 E-Mail: poth@lat-lon.de 033 034 Prof. Dr. Klaus Greve 035 Department of Geography 036 University of Bonn 037 Meckenheimer Allee 166 038 53115 Bonn 039 Germany 040 E-Mail: greve@giub.uni-bonn.de 041 042 ---------------------------------------------------------------------------*/ 043 package org.deegree.model.feature; 044 045 import java.util.HashMap; 046 import java.util.HashSet; 047 import java.util.Map; 048 import java.util.Set; 049 050 import org.deegree.datatypes.QualifiedName; 051 import org.deegree.model.feature.schema.FeatureType; 052 import org.deegree.model.spatialschema.Envelope; 053 import org.deegree.model.spatialschema.Geometry; 054 import org.deegree.model.spatialschema.GeometryException; 055 import org.deegree.model.spatialschema.GeometryFactory; 056 import org.deegree.model.spatialschema.Point; 057 058 /** 059 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 060 * @author last edited by: $Author: apoth $ 061 * 062 * @version 1.0. $Revision: 9343 $, $Date: 2007-12-27 14:30:32 +0100 (Do, 27 Dez 2007) $ 063 */ 064 abstract class AbstractFeature implements Feature { 065 066 private String id; 067 068 protected FeatureType featureType; 069 070 protected Envelope envelope; 071 072 protected boolean envelopeCalculated; 073 074 protected String description; 075 076 protected FeatureProperty owner; 077 078 private Map<String, String> attributeMap = new HashMap<String, String>(); 079 080 /** 081 * @param id 082 * @param featureType 083 */ 084 AbstractFeature( String id, FeatureType featureType ) { 085 this.id = id; 086 this.featureType = featureType; 087 } 088 089 /** 090 * @param id 091 * @param featureType 092 */ 093 AbstractFeature( String id, FeatureType featureType, FeatureProperty owner ) { 094 this.id = id; 095 this.featureType = featureType; 096 this.owner = owner; 097 } 098 099 public String getDescription() { 100 return description; 101 } 102 103 public QualifiedName getName() { 104 return featureType.getName(); 105 } 106 107 /** 108 * Returns the envelope / boundingbox of the feature. The bounding box will be created in a 109 * recursion. That means if a property of the feature (a) contains another feature (b) the 110 * bounding box of b will be merged with the bounding box calculated from the geometry 111 * properties of a. A feature has no geometry properties and no properties that are features 112 * (and having a bounding box theirself) <code>null</code> will be returned. 113 */ 114 public Envelope getBoundedBy() 115 throws GeometryException { 116 if ( !this.envelopeCalculated ) { 117 getBoundedBy( new HashSet<Feature>() ); 118 } 119 return this.envelope; 120 } 121 122 /** 123 * Signals that the envelopes of the geometry properties have been updated. 124 */ 125 public void setEnvelopesUpdated() { 126 envelopeCalculated = false; 127 } 128 129 public FeatureProperty getOwner() { 130 return this.owner; 131 } 132 133 /** 134 * returns the id of the Feature. the id has to be a name space that must be unique for each 135 * feature. use the adress of the datasource in addition to a number for example . 136 */ 137 public String getId() { 138 return id; 139 } 140 141 public void setId( String fid ) { 142 this.id = fid; 143 } 144 145 /** 146 * returns the FeatureType of this Feature 147 * 148 */ 149 public FeatureType getFeatureType() { 150 return featureType; 151 } 152 153 /** 154 * Sets the feature type of this feature. 155 * 156 * @param ft 157 * feature type to set 158 */ 159 public void setFeatureType( FeatureType ft ) { 160 this.featureType = ft; 161 } 162 163 /** 164 * resets the envelope of the feature so the next call of getBounds() will force a 165 * re-calculation 166 * 167 */ 168 protected void resetBounds() { 169 envelope = null; 170 } 171 172 /** 173 * Returns the attribute value of the attribute with the specified name. 174 * 175 * @param name 176 * name of the attribute 177 * @return the attribute value 178 */ 179 public String getAttribute( String name ) { 180 return this.attributeMap.get( name ); 181 } 182 183 /** 184 * Returns all attributes of the feature. 185 * 186 * @return all attributes, keys are names, values are attribute values 187 */ 188 public Map<String, String> getAttributes() { 189 return this.attributeMap; 190 } 191 192 /** 193 * Sets the value of the attribute with the given name. 194 * 195 * @param name 196 * name of the attribute 197 * @param value 198 * value to set 199 */ 200 public void setAttribute( String name, String value ) { 201 this.attributeMap.put( name, value ); 202 } 203 204 /** 205 * Returns the {@link Envelope} of the feature. If the envelope has not been calculated yet, it 206 * is calculated recursively, using the given feature set to detected cycles in the feature 207 * structure. 208 * 209 * @param features 210 * used to detected cycles in feature structure and prevent endless recursion 211 * @return envelope of the feature 212 * @throws GeometryException 213 */ 214 private Envelope getBoundedBy( Set<Feature> features ) 215 throws GeometryException { 216 if ( !this.envelopeCalculated ) { 217 this.envelope = calcEnvelope( features ); 218 this.envelopeCalculated = true; 219 } 220 return this.envelope; 221 } 222 223 /** 224 * Calculates the envelope of the feature recursively. Respects all geometry properties and sub 225 * features of the feature. 226 * 227 * @param features 228 * used to detected cycles in feature structure and prevent endless recursion 229 * @return envelope of the feature 230 * @throws GeometryException 231 */ 232 private Envelope calcEnvelope( Set<Feature> features ) 233 throws GeometryException { 234 235 Envelope combinedEnvelope = null; 236 if ( features.contains( this ) ) { 237 return combinedEnvelope; 238 } 239 features.add( this ); 240 241 FeatureProperty[] props = getProperties(); 242 for ( FeatureProperty prop : props ) { 243 if ( prop != null ) { 244 Object propValue = prop.getValue(); 245 if ( propValue instanceof Geometry ) { 246 Geometry geom = (Geometry) propValue; 247 Envelope env = null; 248 if ( geom instanceof Point ) { 249 env = GeometryFactory.createEnvelope( ( (Point) geom ).getPosition(), 250 ( (Point) geom ).getPosition(), 251 geom.getCoordinateSystem() ); 252 } else { 253 env = geom.getEnvelope(); 254 } 255 if ( combinedEnvelope == null ) { 256 combinedEnvelope = env; 257 } else { 258 combinedEnvelope = combinedEnvelope.merge( env ); 259 } 260 } else if ( propValue instanceof AbstractFeature ) { 261 Envelope subEnvelope = ( (AbstractFeature) propValue ).getBoundedBy( features ); 262 if ( combinedEnvelope == null ) { 263 combinedEnvelope = subEnvelope; 264 } else if ( subEnvelope != null ) { 265 combinedEnvelope = combinedEnvelope.merge( subEnvelope ); 266 } 267 } 268 } 269 } 270 return combinedEnvelope; 271 } 272 }