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    }