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