001    //$HeadURL: http://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/graphics/displayelements/ScaledFeature.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.graphics.displayelements;
037    
038    import java.util.HashMap;
039    import java.util.Map;
040    
041    import org.deegree.datatypes.QualifiedName;
042    import org.deegree.datatypes.Types;
043    import org.deegree.io.datastore.PropertyPathResolvingException;
044    import org.deegree.model.feature.DefaultFeature;
045    import org.deegree.model.feature.Feature;
046    import org.deegree.model.feature.FeatureCollection;
047    import org.deegree.model.feature.FeatureFactory;
048    import org.deegree.model.feature.FeatureProperty;
049    import org.deegree.model.feature.schema.FeatureType;
050    import org.deegree.model.feature.schema.PropertyType;
051    import org.deegree.model.spatialschema.Envelope;
052    import org.deegree.model.spatialschema.Geometry;
053    import org.deegree.model.spatialschema.GeometryException;
054    import org.deegree.ogcbase.PropertyPath;
055    
056    /**
057     * This class is a wrapper for a Feature and a Feature itself.
058     * <p>
059     * It adds a special behavior/property to a feature that is required by deegree DisplayElements. This special behavior
060     * is an additional property named "$SCALE". In opposite to conventional properties this one can change its value during
061     * lifetime of a feature without changing the underlying feature itself. <br>
062     * The class is use to offer users the opportunity to use the scale of a map within expressions embedded in SLD
063     * rules/symbolizers, i.e. this enables a user to define that a symbol shall appear in 10m size independ of a map's
064     * scale.
065     *
066     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
067     * @author last edited by: $Author: apoth $
068     *
069     * @version $Revision: 29966 $, $Date: 2011-03-09 15:19:04 +0100 (Wed, 09 Mar 2011) $
070     */
071    public class ScaledFeature implements Feature {
072    
073        private Feature feature;
074    
075        private FeatureType ft;
076    
077        private FeatureProperty[] props;
078    
079        private Map<String, String> attributeMap = new HashMap<String, String>();
080    
081        /**
082         *
083         * @param feature
084         *            feature wrap
085         * @param scale
086         *            maps scale (should be -1 if not known)
087         */
088        public ScaledFeature( Feature feature, double scale ) {
089            this.feature = feature;
090            PropertyType[] ftp = feature.getFeatureType().getProperties();
091            PropertyType[] ftp2 = new PropertyType[ftp.length + 1];
092            for ( int i = 0; i < ftp.length; i++ ) {
093                ftp2[i] = ftp[i];
094            }
095            QualifiedName qn = new QualifiedName( feature.getName().getPrefix(), "$SCALE", feature.getName().getNamespace() );
096            ftp2[ftp2.length - 1] = FeatureFactory.createSimplePropertyType( qn, Types.DOUBLE, false );
097            FeatureProperty[] o = feature.getProperties();
098            props = new FeatureProperty[o.length + 1];
099            for ( int i = 0; i < o.length; i++ ) {
100                props[i] = o[i];
101            }
102            props[props.length - 1] = FeatureFactory.createFeatureProperty( qn, new Double( scale ) );
103            ft = FeatureFactory.createFeatureType( feature.getFeatureType().getName(), false, ftp2 );
104        }
105    
106        /**
107         * @return features owner
108         */
109        public FeatureProperty getOwner() {
110            return feature.getOwner();
111        }
112       
113        /**
114         * @return features name
115         */
116        public QualifiedName getName() {
117            return feature.getName();
118        }
119    
120        /**
121         * @see Feature#getDefaultGeometryPropertyValue()
122         */
123        public Geometry getDefaultGeometryPropertyValue() {
124            return feature.getDefaultGeometryPropertyValue();
125        }
126    
127        /**
128         * @return features envelope
129         */
130        public Envelope getBoundedBy()
131                                throws GeometryException {
132            return feature.getBoundedBy();
133        }
134    
135        /**
136         * @see Feature#getFeatureType() the returned feature type contains all properties of the wrapped feature plus a
137         *      property named '$SCALE'
138         */
139        public FeatureType getFeatureType() {
140            return ft;
141        }
142    
143        /**
144         * @see Feature#getGeometryPropertyValues()
145         */
146        public Geometry[] getGeometryPropertyValues() {
147            return feature.getGeometryPropertyValues();
148        }
149    
150        /**
151         * @see Feature#getId()
152         */
153        public String getId() {
154            return feature.getId();
155        }
156    
157        /**
158         * @see Feature#getProperties() the returned array contains all properties of the wrapped feature plus a property
159         *      named '$SCALE'
160         */
161        public FeatureProperty[] getProperties() {
162            return props;
163        }
164    
165        /**
166         * The property '$SCALE' has the highest valid index
167         */
168        public FeatureProperty[] getProperties( int index ) {
169            return new FeatureProperty[] { props[index] };
170        }
171    
172        /**
173         * use '$SCALE' to access the scale property value
174         */
175        public FeatureProperty getDefaultProperty( QualifiedName name ) {
176            QualifiedName qn = new QualifiedName( "$SCALE" );
177            if ( name.equals( qn ) ) {
178                return props[props.length - 1];
179            }
180            return feature.getDefaultProperty( name );
181        }
182    
183        /**
184         * @param name
185         * @return property array
186         */
187        public FeatureProperty[] getProperties( QualifiedName name ) {
188            if ( name.getLocalName().equalsIgnoreCase( "$SCALE" ) ) {
189                return new FeatureProperty[] { props[props.length - 1] };
190            }
191            return feature.getProperties( name );
192        }
193    
194        /**
195         * @param path
196         * @return property
197         */
198        public FeatureProperty getDefaultProperty( PropertyPath path )
199                                throws PropertyPathResolvingException {
200            if ( path.getStep( 0 ).getPropertyName().getLocalName().equalsIgnoreCase( "$SCALE" ) ) {
201                return props[props.length - 1];
202            }
203            return feature.getDefaultProperty( path );
204        }
205    
206        public void setProperty( FeatureProperty property, int index ) {
207            feature.setProperty( property, index );
208        }
209    
210        /**
211         * sets the features scale. Expected is the scale denominator as defined by OGC SLD specification
212         *
213         * @param scale
214         */
215        public void setScale( double scale ) {
216            // must be multiplied with pixel size to get scale as length
217            // of a pixels diagonal measured in meter
218            props[props.length - 1].setValue( scale * 0.00028 );
219        }
220    
221        /**
222         * returns the features scale
223         *
224         * @return the features scale
225         */
226        public double getScale() {
227            return ( (Double) props[props.length - 1].getValue() ).doubleValue();
228        }
229    
230        /**
231         * @param property
232         */
233        public void addProperty( FeatureProperty property ) {
234            this.feature.addProperty( property );
235        }
236    
237        /**
238         * @param propertyName
239         */
240        public void removeProperty( QualifiedName propertyName ) {
241            this.feature.removeProperty( propertyName );
242        }
243    
244        /**
245         * @param oldProperty
246         * @param newProperty
247         */
248        public void replaceProperty( FeatureProperty oldProperty, FeatureProperty newProperty ) {
249            this.feature.replaceProperty( oldProperty, newProperty );
250        }
251    
252        /**
253         * @param fid
254         */
255        public void setId( String fid ) {
256            feature.setId( fid );
257        }
258    
259        /**
260         * Returns the attribute value of the attribute with the specified name.
261         *
262         * @param name
263         *            name of the attribute
264         * @return the attribute value
265         */
266        public String getAttribute( String name ) {
267            return this.attributeMap.get( name );
268        }
269    
270        /**
271         * Returns all attributes of the feature.
272         *
273         * @return all attributes, keys are names, values are attribute values
274         */
275        public Map<String, String> getAttributes() {
276            return this.attributeMap;
277        }
278    
279        /**
280         * Sets the value of the attribute with the given name.
281         *
282         * @param name
283         *            name of the attribute
284         * @param value
285         *            value to set
286         */
287        public void setAttribute( String name, String value ) {
288            this.attributeMap.put( name, value );
289        }
290    
291        /**
292         * Sets the feature type of this feature.
293         *
294         * @param ft
295         *            feature type to set
296         */
297        public void setFeatureType( FeatureType ft ) {
298            feature.setFeatureType( ft );
299        }
300    
301        /*
302         * (non-Javadoc)
303         *
304         * @see org.deegree.model.feature.Feature#setEnvelopesUpdated()
305         */
306        public void setEnvelopesUpdated() {
307            feature.setEnvelopesUpdated();
308        }
309    
310        /*
311         * (non-Javadoc)
312         *
313         * @see org.deegree.model.feature.Feature#cloneDeep()
314         */
315        public Feature cloneDeep()
316                                throws CloneNotSupportedException {
317            Feature tmp = feature.cloneDeep();
318            return new ScaledFeature( tmp, getScale() );
319        }
320    
321        @Override
322        public Object clone()
323                                throws CloneNotSupportedException {
324            Feature tmp = (Feature) feature.clone();
325            return new ScaledFeature( tmp, getScale() );
326        }
327        
328        @Override
329        public String toString() {
330            String ret = getClass().getName();
331            ret = "";
332            for ( int i = 0; i < props.length; i++ ) {
333                if ( props[i].getValue() instanceof FeatureCollection ) {
334                    ret += ( "  " + props[i].getName() + ": ");
335                    ret += "\n";
336                    ret += ( "  " +(FeatureCollection) props[i].getValue() ).toString();
337                    ret += "\n";
338                } else if ( props[i].getValue() instanceof DefaultFeature ) {
339                    ret += ( "  " +props[i].getName() + ": ");
340                    ret += "\n";
341                    ret += ( "  " +(DefaultFeature) props[i].getValue() ).toString();
342                    ret += "\n";
343                } else  if ( props[i].getValue() instanceof Geometry ) {
344                    ret += props[i].getName();
345                    ret += "\n";
346                } else {
347                    String o = "null";
348                    if ( props[i].getValue()  != null ) {
349                        o = props[i].getValue().toString();
350                    }
351                    ret += (props[i].getName() + " = " + o );
352                    ret += "\n";
353                }
354            }
355            return ret;
356        }
357    
358    }