001    //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/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: mschneider $
054     *
055     * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18 Jun 2009) $
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 String description;
068    
069        protected FeatureProperty owner;
070    
071        private Map<String, String> attributeMap = new HashMap<String, String>();
072    
073        /**
074         * @param id
075         * @param featureType
076         */
077        AbstractFeature( String id, FeatureType featureType ) {
078            this.id = id;
079            this.featureType = featureType;
080        }
081    
082        /**
083         * @param id
084         * @param featureType
085         */
086        AbstractFeature( String id, FeatureType featureType, FeatureProperty owner ) {
087            this.id = id;
088            this.featureType = featureType;
089            this.owner = owner;
090        }
091    
092        public String getDescription() {
093            return description;
094        }
095    
096        public QualifiedName getName() {
097            return featureType.getName();
098        }
099    
100        /**
101         * Returns the envelope / boundingbox of the feature. The bounding box will be created in a recursion. That means if
102         * a property of the feature (a) contains another feature (b) the bounding box of b will be merged with the bounding
103         * box calculated from the geometry properties of a. A feature has no geometry properties and no properties that are
104         * features (and having a bounding box theirself) <code>null</code> will be returned.
105         */
106        public Envelope getBoundedBy()
107                                throws GeometryException {
108            if ( !this.envelopeCalculated ) {
109                getBoundedBy( new HashSet<Feature>() );
110            }
111            return this.envelope;
112        }
113    
114        /**
115         * Signals that the envelopes of the geometry properties have been updated.
116         */
117        public void setEnvelopesUpdated() {
118            envelopeCalculated = false;
119        }
120    
121        public FeatureProperty getOwner() {
122            return this.owner;
123        }
124    
125        /**
126         * returns the id of the Feature. the id has to be a name space that must be unique for each feature. use the adress
127         * of the datasource in addition to a number for example .
128         */
129        public String getId() {
130            return id;
131        }
132    
133        public void setId( String fid ) {
134            this.id = fid;
135        }
136    
137        /**
138         * returns the FeatureType of this Feature
139         *
140         */
141        public FeatureType getFeatureType() {
142            return featureType;
143        }
144    
145        /**
146         * Sets the feature type of this feature.
147         *
148         * @param ft
149         *            feature type to set
150         */
151        public void setFeatureType( FeatureType ft ) {
152            this.featureType = ft;
153        }
154    
155        /**
156         * resets the envelope of the feature so the next call of getBounds() will force a re-calculation
157         *
158         */
159        protected void resetBounds() {
160            envelope = null;
161        }
162    
163        /**
164         * Returns the attribute value of the attribute with the specified name.
165         *
166         * @param name
167         *            name of the attribute
168         * @return the attribute value
169         */
170        public String getAttribute( String name ) {
171            return this.attributeMap.get( name );
172        }
173    
174        /**
175         * Returns all attributes of the feature.
176         *
177         * @return all attributes, keys are names, values are attribute values
178         */
179        public Map<String, String> getAttributes() {
180            return this.attributeMap;
181        }
182    
183        /**
184         * Sets the value of the attribute with the given name.
185         *
186         * @param name
187         *            name of the attribute
188         * @param value
189         *            value to set
190         */
191        public void setAttribute( String name, String value ) {
192            this.attributeMap.put( name, value );
193        }
194    
195        /**
196         * Returns the {@link Envelope} of the feature. If the envelope has not been calculated yet, it is calculated
197         * recursively, using the given feature set to detected cycles in the feature structure.
198         *
199         * @param features
200         *            used to detected cycles in feature structure and prevent endless recursion
201         * @return envelope of the feature
202         * @throws GeometryException
203         */
204        private Envelope getBoundedBy( Set<Feature> features )
205                                throws GeometryException {
206            if ( !this.envelopeCalculated ) {
207                this.envelope = calcEnvelope( features );
208                this.envelopeCalculated = true;
209            }
210            return this.envelope;
211        }
212    
213        /**
214         * Calculates the envelope of the feature recursively. Respects all geometry properties and sub features of the
215         * feature.
216         *
217         * @param features
218         *            used to detected cycles in feature structure and prevent endless recursion
219         * @return envelope of the feature
220         * @throws GeometryException
221         */
222        private Envelope calcEnvelope( Set<Feature> features )
223                                throws GeometryException {
224    
225            Envelope combinedEnvelope = null;
226            if ( features.contains( this ) ) {
227                return combinedEnvelope;
228            }
229            features.add( this );
230    
231            FeatureProperty[] props = getProperties();
232            for ( FeatureProperty prop : props ) {
233                if ( prop != null ) {
234                    Object propValue = prop.getValue();
235                    if ( propValue instanceof Geometry ) {
236                        Geometry geom = (Geometry) propValue;
237                        Envelope env = null;
238                        if ( geom instanceof Point ) {
239                            env = GeometryFactory.createEnvelope( ( (Point) geom ).getPosition(),
240                                                                  ( (Point) geom ).getPosition(),
241                                                                  geom.getCoordinateSystem() );
242                        } else {
243                            env = geom.getEnvelope();
244                        }
245                        if ( combinedEnvelope == null ) {
246                            combinedEnvelope = env;
247                        } else {
248                            combinedEnvelope = combinedEnvelope.merge( env );
249                        }
250                    } else if ( propValue instanceof AbstractFeature ) {
251                        Envelope subEnvelope = ( (AbstractFeature) propValue ).getBoundedBy( features );
252                        if ( combinedEnvelope == null ) {
253                            combinedEnvelope = subEnvelope;
254                        } else if ( subEnvelope != null ) {
255                            combinedEnvelope = combinedEnvelope.merge( subEnvelope );
256                        }
257                    }
258                }
259            }
260            return combinedEnvelope;
261        }
262    
263        @Override
264        public Object clone()
265                                throws CloneNotSupportedException {
266            throw new CloneNotSupportedException();
267        }
268    
269    }