001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/graphics/sld/PointPlacement.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.sld;
037    
038    import org.deegree.framework.log.ILogger;
039    import org.deegree.framework.log.LoggerFactory;
040    import org.deegree.framework.xml.Marshallable;
041    import org.deegree.model.feature.Feature;
042    import org.deegree.model.filterencoding.FilterEvaluationException;
043    import org.deegree.model.filterencoding.PropertyName;
044    
045    /**
046     * Incarnation of a sld:PointPlacement-element. For a PointPlacement, the anchor point of the label
047     * and a linear displacement from the point can be specified, to allow a graphic symbol to be
048     * plotted directly at the point. This might be useful to label a city, for example. For a
049     * LinePlacement, a perpendicular offset can be specified, to allow the line itself to be plotted
050     * also. This might be useful for labelling a road or a river, for example.
051     * <p>
052     *
053     *
054     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
055     * @author last edited by: $Author: mschneider $
056     *
057     * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
058     */
059    public class PointPlacement implements Marshallable {
060    
061        private static final ILogger LOG = LoggerFactory.getLogger( PointPlacement.class );
062    
063        private ParameterValueType rotation = null;
064    
065        private ParameterValueType[] anchorPoint = null;
066    
067        private ParameterValueType[] displacement = null;
068    
069        // should the placement be optimized?
070        private boolean auto = false;
071    
072        /**
073         * package protected default constructor
074         *
075         */
076        PointPlacement() {
077            //again nothing.
078        }
079    
080        /**
081         * Creates a new PointPlacement object.
082         *
083         * @param anchorPoint
084         * @param displacement
085         * @param rotation
086         * @param auto
087         */
088        public PointPlacement( ParameterValueType[] anchorPoint, ParameterValueType[] displacement,
089                               ParameterValueType rotation, boolean auto ) {
090            this.anchorPoint = anchorPoint;
091            this.displacement = displacement;
092            this.rotation = rotation;
093            this.auto = auto;
094        }
095    
096        /**
097         * returns the anchor points (x and y) as array of
098         *
099         * @see ParameterValueType
100         * @return anchor point as
101         * @see ParameterValueType
102         */
103        public ParameterValueType[] getAnchorPoint() {
104            return anchorPoint;
105        }
106    
107        /**
108         * returns the displacements (x and y) as array of
109         *
110         * @see ParameterValueType
111         * @return displacements (x and y) as array of
112         * @see ParameterValueType
113         */
114        public ParameterValueType[] getDisplacement() {
115            return displacement;
116        }
117    
118        /**
119         * returns the rotation of ParameterValueType
120         * @return the rotation
121         */
122        public ParameterValueType getRotation() {
123            return rotation;
124        }
125    
126        /**
127         * The AnchorPoint element of a PointPlacement gives the location inside of a label to use for
128         * anchoring the label to the main-geometry point.
129         * <p>
130         * </p>
131         * The coordinates are given as two floating-point numbers in the AnchorPointX and AnchorPointY
132         * elements each with values between 0.0 and 1.0 inclusive. The bounding box of the label to be
133         * rendered is considered to be in a coorindate space from 0.0 (lower-left corner) to 1.0
134         * (upper-right corner), and the anchor position is specified as a point in this space. The
135         * default point is X=0, Y=0.5, which is at the middle height of the left-hand side of the
136         * label. A system may choose different anchor points to de-conflict labels.
137         * <p>
138         *
139         * @param feature
140         *            specifies the <tt>Feature</tt> to be used for evaluation of the underlying
141         *            'sld:ParameterValueType'
142         * @return 2 double values: x ([0]) and y ([0])
143         * @throws FilterEvaluationException
144         *             if the evaluation fails
145         */
146        public double[] getAnchorPoint( Feature feature )
147                                throws FilterEvaluationException {
148            double[] anchorPointVal = { 0.0, 0.5 };
149    
150            if ( anchorPoint != null ) {
151                anchorPointVal[0] = Double.parseDouble( anchorPoint[0].evaluate( feature ) );
152                anchorPointVal[1] = Double.parseDouble( anchorPoint[1].evaluate( feature ) );
153            }
154    
155            return anchorPointVal;
156        }
157    
158        /**
159         * @see PointPlacement#getAnchorPoint(Feature)
160         *      <p>
161         * @param anchorPoint
162         *            anchorPoint for the PointPlacement
163         */
164        public void setAnchorPoint( double[] anchorPoint ) {
165            ParameterValueType pvt = null;
166            ParameterValueType[] pvtArray = new ParameterValueType[anchorPoint.length];
167            for ( int i = 0; i < anchorPoint.length; i++ ) {
168                pvt = StyleFactory.createParameterValueType( "" + anchorPoint[i] );
169                pvtArray[i] = pvt;
170            }
171            this.anchorPoint = pvtArray;
172        }
173    
174        /**
175         * The Displacement element of a PointPlacement gives the X and Y displacements from the
176         * main-geometry point to render a text label.
177         * <p>
178         * </p>
179         * This will often be used to avoid over-plotting a graphic symbol marking a city or some such
180         * feature. The displacements are in units of pixels above and to the right of the point. A
181         * system may reflect this displacement about the X and/or Y axes to de-conflict labels. The
182         * default displacement is X=0, Y=0.
183         * <p>
184         *
185         * @param feature
186         *            specifies the <tt>Feature</tt> to be used for evaluation of the underlying
187         *            'sld:ParameterValueType'
188         * @return 2 double values: x ([0]) and y ([0])
189         * @throws FilterEvaluationException
190         *             if the evaluation fails*
191         */
192        public double[] getDisplacement( Feature feature )
193                                throws FilterEvaluationException {
194            double[] displacementVal = { 0.0, 0.0 };
195    
196            if ( displacement != null ) {
197                displacementVal[0] = Double.parseDouble( displacement[0].evaluate( feature ) );
198                displacementVal[1] = Double.parseDouble( displacement[1].evaluate( feature ) );
199            }
200    
201            return displacementVal;
202        }
203    
204        /**
205         * @see PointPlacement#getDisplacement(Feature)
206         *      <p>
207         * @param displacement
208         */
209        public void setDisplacement( double[] displacement ) {
210            ParameterValueType pvt = null;
211            ParameterValueType[] pvtArray = new ParameterValueType[displacement.length];
212            for ( int i = 0; i < displacement.length; i++ ) {
213                pvt = StyleFactory.createParameterValueType( "" + displacement[i] );
214                pvtArray[i] = pvt;
215            }
216            this.displacement = pvtArray;
217        }
218    
219        /**
220         * The Rotation of a PointPlacement gives the clockwise rotation of the label in degrees from
221         * the normal direction for a font (left-to-right for Latin- derived human languages at least).
222         * <p>
223         *
224         * @param feature
225         *            specifies the <tt>Feature</tt> to be used for evaluation of the underlying
226         *            'sld:ParameterValueType'
227         * @return double value describing the rotation parameter
228         * @throws FilterEvaluationException
229         *             if the evaluation fails*
230         */
231        public double getRotation( Feature feature )
232                                throws FilterEvaluationException {
233            double rot = 0.0;
234    
235            if ( rotation != null ) {
236                String tmp = rotation.evaluate( feature );
237                if ( tmp != null ) {
238                    try {
239                        rot = Double.parseDouble( tmp );
240                    } catch ( NumberFormatException e ) {
241                        LOG.logInfo( "could not parse rotation value as float, use 0° as default: ", tmp );
242                    }
243                }
244            }
245    
246            return rot;
247        }
248    
249    
250        /**
251         * Returns the property name of the rotation if available
252         *
253         * @return the property name or null, if no property name is set
254         */
255        public PropertyName getRotationPropertyName() {
256            if ( rotation != null ) {
257                Object[] o = rotation.getComponents();
258                for ( int i = 0; i < o.length; i++ ) {
259                    if ( o[i] instanceof PropertyName ) {
260                        return (PropertyName) o[i];
261                    }
262                }
263            }
264            return null;
265        }
266    
267        /**
268         * @see PointPlacement#getRotation(Feature)
269         * @param rotation
270         *            the rotation to be set for the PointPlacement
271         */
272        public void setRotation( double rotation ) {
273            ParameterValueType pvt = null;
274            pvt = StyleFactory.createParameterValueType( "" + rotation );
275            this.rotation = pvt;
276        }
277    
278        /**
279         * Returns whether the placement should be optimized or not.
280         * <p>
281         *
282         * @return true, if it should be optimized
283         *
284         */
285        public boolean isAuto() {
286            return auto;
287        }
288    
289        /**
290         * <p>
291         *
292         * @param auto
293         *
294         */
295        public void setAuto( boolean auto ) {
296            this.auto = auto;
297        }
298    
299        /**
300         * exports the content of the PointPlacement as XML formated String
301         *
302         * @return xml representation of the PointPlacement
303         */
304        public String exportAsXML() {
305    
306            StringBuffer sb = new StringBuffer( 1000 );
307            sb.append( "<PointPlacement" );
308            if ( auto ) {
309                sb.append( " auto='true'" );
310            }
311            sb.append( ">" );
312            if ( anchorPoint != null && anchorPoint.length > 1 ) {
313                sb.append( "<AnchorPoint>" ).append( "<AnchorPointX>" );
314                sb.append( ( (Marshallable) anchorPoint[0] ).exportAsXML() );
315                sb.append( "</AnchorPointX>" ).append( "<AnchorPointY>" );
316                sb.append( ( (Marshallable) anchorPoint[1] ).exportAsXML() );
317                sb.append( "</AnchorPointY>" ).append( "</AnchorPoint>" );
318            }
319            if ( displacement != null && displacement.length > 1 ) {
320                sb.append( "<Displacement>" ).append( "<DisplacementX>" );
321                sb.append( ( (Marshallable) displacement[0] ).exportAsXML() );
322                sb.append( "</DisplacementX>" ).append( "<DisplacementY>" );
323                sb.append( ( (Marshallable) displacement[1] ).exportAsXML() );
324                sb.append( "</DisplacementY>" ).append( "</Displacement>" );
325            }
326            if ( rotation != null ) {
327                sb.append( "<Rotation>" );
328                sb.append( ( (Marshallable) rotation ).exportAsXML() );
329                sb.append( "</Rotation>" );
330            }
331    
332            sb.append( "</PointPlacement>" );
333    
334            return sb.toString();
335        }
336    }