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 }