001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/graphics/displayelements/LabelFactory.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 static 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.graphics.displayelements;
044    
045    import java.awt.Color;
046    import java.awt.Font;
047    import java.awt.Graphics2D;
048    import java.awt.font.FontRenderContext;
049    import java.awt.font.LineMetrics;
050    import java.awt.geom.Rectangle2D;
051    import java.util.ArrayList;
052    import java.util.Collections;
053    import java.util.Iterator;
054    import java.util.List;
055    
056    import org.deegree.graphics.sld.Halo;
057    import org.deegree.graphics.sld.LabelPlacement;
058    import org.deegree.graphics.sld.LinePlacement;
059    import org.deegree.graphics.sld.PointPlacement;
060    import org.deegree.graphics.sld.TextSymbolizer;
061    import org.deegree.graphics.transformation.GeoTransform;
062    import org.deegree.model.feature.Feature;
063    import org.deegree.model.filterencoding.FilterEvaluationException;
064    import org.deegree.model.spatialschema.Curve;
065    import org.deegree.model.spatialschema.Geometry;
066    import org.deegree.model.spatialschema.GeometryException;
067    import org.deegree.model.spatialschema.GeometryFactory;
068    import org.deegree.model.spatialschema.LineString;
069    import org.deegree.model.spatialschema.MultiCurve;
070    import org.deegree.model.spatialschema.MultiPoint;
071    import org.deegree.model.spatialschema.MultiSurface;
072    import org.deegree.model.spatialschema.Point;
073    import org.deegree.model.spatialschema.Position;
074    import org.deegree.model.spatialschema.Surface;
075    
076    /**
077     * Does the labeling, i.e. creates (screen) <tt>Label</tt> representations from
078     * <tt>LabelDisplayElement</tt>s.
079     * <p>
080     * Different geometry-types (of the LabelDisplayElement) imply different strategies concerning the
081     * way the <tt>Labels</tt> are generated.
082     * <p>
083     * 
084     * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a>
085     * @version $Revision: 9340 $ $Date: 2007-12-27 13:32:12 +0100 (Do, 27 Dez 2007) $
086     */
087    public class LabelFactory {
088    
089        /**
090         * @param caption
091         * @param font
092         * @param color
093         * @param metrics
094         * @param feature
095         * @param halo
096         * @param x
097         * @param y
098         * @param w
099         * @param h
100         * @param rotation
101         * @param anchorPointX
102         * @param anchorPointY
103         * @param displacementX
104         * @param displacementY
105         * @return label-representations
106         */
107        public static Label createLabel( String caption, Font font, Color color, LineMetrics metrics, Feature feature,
108                                         Halo halo, int x, int y, int w, int h, double rotation, double anchorPointX,
109                                         double anchorPointY, double displacementX, double displacementY ) {
110            if ( rotation == 0.0 ) {
111                return new HorizontalLabel( caption, font, color, metrics, feature, halo, x, y, w, h,
112                                            new double[] { anchorPointX, anchorPointY }, new double[] { displacementX,
113                                                                                                       displacementY } );
114            }
115            return new RotatedLabel( caption, font, color, metrics, feature, halo, x, y, w, h, rotation,
116                                     new double[] { anchorPointX, anchorPointY }, new double[] { displacementX,
117                                                                                                displacementY } );
118    
119        }
120    
121        /**
122         * @param caption
123         * @param font
124         * @param color
125         * @param metrics
126         * @param feature
127         * @param halo
128         * @param x
129         * @param y
130         * @param w
131         * @param h
132         * @param rotation
133         * @param anchorPoint
134         * @param displacement
135         * @return label-representations
136         */
137        public static Label createLabel( String caption, Font font, Color color, LineMetrics metrics, Feature feature,
138                                         Halo halo, int x, int y, int w, int h, double rotation, double[] anchorPoint,
139                                         double[] displacement ) {
140            if ( rotation == 0.0 ) {
141                return new HorizontalLabel( caption, font, color, metrics, feature, halo, x, y, w, h, anchorPoint,
142                                            displacement );
143            }
144            return new RotatedLabel( caption, font, color, metrics, feature, halo, x, y, w, h, rotation, anchorPoint,
145                                     displacement );
146    
147        }
148    
149        /**
150         * Generates label-representations for a given <tt>LabelDisplayElement</tt>.
151         * <p>
152         * 
153         * @param element
154         * @param projection
155         * @param g
156         * @return label-representations
157         * @throws Exception
158         */
159        public static Label[] createLabels( LabelDisplayElement element, GeoTransform projection, Graphics2D g )
160                                throws Exception {
161    
162            Label[] labels = new Label[0];
163            Feature feature = element.getFeature();
164            String caption = element.getLabel().evaluate( feature );
165    
166            // sanity check: empty labels are ignored
167            if ( caption == null || caption.trim().equals( "" ) ) {
168                return labels;
169            }
170    
171            Geometry geometry = element.getGeometry();
172            TextSymbolizer symbolizer = (TextSymbolizer) element.getSymbolizer();
173    
174            // gather font information
175            org.deegree.graphics.sld.Font sldFont = symbolizer.getFont();
176            java.awt.Font font = new java.awt.Font( sldFont.getFamily( feature ), sldFont.getStyle( feature )
177                                                                                  | sldFont.getWeight( feature ),
178                                                    sldFont.getSize( feature ) );
179            g.setFont( font );
180            FontRenderContext frc = g.getFontRenderContext();
181            Rectangle2D bounds = font.getStringBounds( caption, frc );
182            LineMetrics metrics = font.getLineMetrics( caption, frc );
183            int w = (int) bounds.getWidth();
184            int h = (int) bounds.getHeight();
185            // int descent = (int) metrics.getDescent ();
186    
187            if ( geometry instanceof Point || geometry instanceof MultiPoint ) {
188    
189                // get screen coordinates
190                int[] coords = calcScreenCoordinates( projection, geometry );
191                int x = coords[0];
192                int y = coords[1];
193    
194                // default placement information
195                double rotation = 0.0;
196                double[] anchorPoint = { 0.0, 0.5 };
197                double[] displacement = { 0.0, 0.0 };
198    
199                // use placement information from SLD
200                LabelPlacement lPlacement = symbolizer.getLabelPlacement();
201    
202                if ( lPlacement != null ) {
203                    PointPlacement pPlacement = lPlacement.getPointPlacement();
204                    anchorPoint = pPlacement.getAnchorPoint( feature );
205                    displacement = pPlacement.getDisplacement( feature );
206                    rotation = pPlacement.getRotation( feature );
207                }
208    
209                labels = new Label[] { createLabel( caption, font, sldFont.getColor( feature ), metrics, feature,
210                                                    symbolizer.getHalo(), x, y, w, h, rotation, anchorPoint, displacement ) };
211            } else if ( geometry instanceof Surface || geometry instanceof MultiSurface ) {
212    
213                // get screen coordinates
214                int[] coords = calcScreenCoordinates( projection, geometry );
215                int x = coords[0];
216                int y = coords[1];
217    
218                // default placement information
219                double rotation = 0.0;
220                double[] anchorPoint = { 0.5, 0.5 };
221                double[] displacement = { 0.0, 0.0 };
222    
223                // use placement information from SLD
224                LabelPlacement lPlacement = symbolizer.getLabelPlacement();
225    
226                if ( lPlacement != null ) {
227                    PointPlacement pPlacement = lPlacement.getPointPlacement();
228    
229                    // check if the label is to be centered within the intersection
230                    // of
231                    // the screen surface and the polygon geometry
232                    if ( pPlacement.isAuto() ) {
233                        Surface screenSurface = GeometryFactory.createSurface( projection.getSourceRect(), null );
234                        Geometry intersection = screenSurface.intersection( geometry );
235                        if ( intersection != null ) {
236                            Position source = intersection.getCentroid().getPosition();
237                            x = (int) ( projection.getDestX( source.getX() ) + 0.5 );
238                            y = (int) ( projection.getDestY( source.getY() ) + 0.5 );
239                        }
240                    }
241                    anchorPoint = pPlacement.getAnchorPoint( feature );
242                    displacement = pPlacement.getDisplacement( feature );
243                    rotation = pPlacement.getRotation( feature );
244                }
245    
246                labels = new Label[] { createLabel( caption, font, sldFont.getColor( feature ), metrics, feature,
247                                                    symbolizer.getHalo(), x, y, w, h, rotation, anchorPoint, displacement )
248    
249                };
250            } else if ( geometry instanceof Curve || geometry instanceof MultiCurve ) {
251                Surface screenSurface = GeometryFactory.createSurface( projection.getSourceRect(), null );
252                Geometry intersection = screenSurface.intersection( geometry );
253                if ( intersection != null ) {
254                    List list = null;
255                    if ( intersection instanceof Curve ) {
256                        list = createLabels( (Curve) intersection, element, g, projection );
257                    } else if ( intersection instanceof MultiCurve ) {
258                        list = createLabels( (MultiCurve) intersection, element, g, projection );
259                    } else {
260                        throw new Exception( "Intersection produced unexpected " + "geometry type: '"
261                                             + intersection.getClass().getName() + "'!" );
262                    }
263                    labels = new Label[list.size()];
264                    for ( int i = 0; i < labels.length; i++ ) {
265                        Label label = (Label) list.get( i );
266                        labels[i] = label;
267                    }
268                }
269            } else {
270                throw new Exception( "LabelFactory does not implement generation " + "of Labels from geometries of type: '"
271                                     + geometry.getClass().getName() + "'!" );
272            }
273            return labels;
274        }
275    
276        /**
277         * Determines positions on the given <tt>MultiCurve</tt> where a caption could be drawn. For
278         * each of this positons, three candidates are produced; one on the line, one above of it and
279         * one below.
280         * <p>
281         * 
282         * @param multiCurve
283         * @param element
284         * @param g
285         * @param projection
286         * @return ArrayList containing Arrays of Label-objects
287         * @throws FilterEvaluationException
288         */
289        public static List createLabels( MultiCurve multiCurve, LabelDisplayElement element, Graphics2D g,
290                                         GeoTransform projection )
291                                throws FilterEvaluationException {
292    
293            List<Label> placements = Collections.synchronizedList( new ArrayList<Label>( 10 ) );
294            for ( int i = 0; i < multiCurve.getSize(); i++ ) {
295                Curve curve = multiCurve.getCurveAt( i );
296                placements.addAll( createLabels( curve, element, g, projection ) );
297            }
298            return placements;
299        }
300    
301        /**
302         * Determines positions on the given <tt>Curve</tt> where a caption could be drawn. For each
303         * of this positons, three candidates are produced; one on the line, one above of it and one
304         * below.
305         * <p>
306         * 
307         * @param curve
308         * @param element
309         * @param g
310         * @param projection
311         * @return ArrayList containing Arrays of Label-objects
312         * @throws FilterEvaluationException
313         */
314        public static ArrayList<Label> createLabels( Curve curve, LabelDisplayElement element, Graphics2D g,
315                                                     GeoTransform projection )
316                                throws FilterEvaluationException {
317    
318            Feature feature = element.getFeature();
319    
320            // determine the placement type and parameters from the TextSymbolizer
321            double perpendicularOffset = 0.0;
322            int placementType = LinePlacement.TYPE_ABSOLUTE;
323            double lineWidth = 3.0;
324            int gap = 6;
325            TextSymbolizer symbolizer = ( (TextSymbolizer) element.getSymbolizer() );
326            if ( symbolizer.getLabelPlacement() != null ) {
327                LinePlacement linePlacement = symbolizer.getLabelPlacement().getLinePlacement();
328                if ( linePlacement != null ) {
329                    placementType = linePlacement.getPlacementType( element.getFeature() );
330                    perpendicularOffset = linePlacement.getPerpendicularOffset( element.getFeature() );
331                    lineWidth = linePlacement.getLineWidth( element.getFeature() );
332                    gap = linePlacement.getGap( element.getFeature() );
333                }
334            }
335    
336            // get width & height of the caption
337            String caption = element.getLabel().evaluate( element.getFeature() );
338            org.deegree.graphics.sld.Font sldFont = symbolizer.getFont();
339            java.awt.Font font = new java.awt.Font( sldFont.getFamily( element.getFeature() ),
340                                                    sldFont.getStyle( element.getFeature() )
341                                                                            | sldFont.getWeight( element.getFeature() ),
342                                                    sldFont.getSize( element.getFeature() ) );
343            g.setFont( font );
344            FontRenderContext frc = g.getFontRenderContext();
345            Rectangle2D bounds = font.getStringBounds( caption, frc );
346            LineMetrics metrics = font.getLineMetrics( caption, frc );
347            double width = bounds.getWidth();
348            double height = bounds.getHeight();
349    
350            // get screen coordinates of the line
351            int[][] pos = calcScreenCoordinates( projection, curve );
352    
353            // ideal distance from the line
354            double delta = height / 2.0 + lineWidth / 2.0;
355    
356            // walk along the linestring and "collect" possible placement positions
357            int w = (int) width;
358            int lastX = pos[0][0];
359            int lastY = pos[1][0];
360            int count = pos[2][0];
361            int boxStartX = lastX;
362            int boxStartY = lastY;
363    
364            ArrayList<Label> labels = new ArrayList<Label>( 100 );
365            List<int[]> eCandidates = Collections.synchronizedList( new ArrayList<int[]>( 100 ) );
366            int i = 0;
367            int kk = 0;
368            while ( i < count && kk < 100 ) {
369                kk++;
370                int x = pos[0][i];
371                int y = pos[1][i];
372    
373                // segment found where endpoint of box should be located?
374                if ( getDistance( boxStartX, boxStartY, x, y ) >= w ) {
375    
376                    int[] p0 = new int[] { boxStartX, boxStartY };
377                    int[] p1 = new int[] { lastX, lastY };
378                    int[] p2 = new int[] { x, y };
379    
380                    int[] p = findPointWithDistance( p0, p1, p2, w );
381                    x = p[0];
382                    y = p[1];
383    
384                    lastX = x;
385                    lastY = y;
386                    int boxEndX = x;
387                    int boxEndY = y;
388    
389                    // does the linesegment run from right to left?
390                    if ( x <= boxStartX ) {
391                        boxEndX = boxStartX;
392                        boxEndY = boxStartY;
393                        boxStartX = x;
394                        boxStartY = y;
395                        x = boxEndX;
396                        y = boxEndY;
397                    }
398    
399                    double rotation = getRotation( boxStartX, boxStartY, x, y );
400                    double[] deviation = calcDeviation( new int[] { boxStartX, boxStartY }, new int[] { boxEndX, boxEndY },
401                                                        eCandidates );
402    
403                    Label label = null;
404    
405                    switch ( placementType ) {
406                    case LinePlacement.TYPE_ABSOLUTE: {
407                        label = createLabel( caption, font, sldFont.getColor( feature ), metrics, feature,
408                                             symbolizer.getHalo(), boxStartX, boxStartY, (int) width, (int) height,
409                                             rotation, 0.0, 0.5, ( w - width ) / 2, perpendicularOffset );
410                        break;
411                    }
412                    case LinePlacement.TYPE_ABOVE: {
413                        label = createLabel( caption, font, sldFont.getColor( feature ), metrics, feature,
414                                             symbolizer.getHalo(), boxStartX, boxStartY, (int) width, (int) height,
415                                             rotation, 0.0, 0.5, ( w - width ) / 2, delta + deviation[0] );
416                        break;
417                    }
418                    case LinePlacement.TYPE_BELOW:
419                    case LinePlacement.TYPE_AUTO: {
420                        label = createLabel( caption, font, sldFont.getColor( feature ), metrics, feature,
421                                             symbolizer.getHalo(), boxStartX, boxStartY, (int) width, (int) height,
422                                             rotation, 0.0, 0.5, ( w - width ) / 2, -delta - deviation[1] );
423                        break;
424                    }
425                    case LinePlacement.TYPE_CENTER: {
426                        label = createLabel( caption, font, sldFont.getColor( feature ), metrics, feature,
427                                             symbolizer.getHalo(), boxStartX, boxStartY, (int) width, (int) height,
428                                             rotation, 0.0, 0.5, ( w - width ) / 2, 0.0 );
429                        break;
430                    }
431                    default: {
432                    }
433                    }
434                    labels.add( label );
435                    boxStartX = lastX;
436                    boxStartY = lastY;
437                    eCandidates.clear();
438                } else {
439                    eCandidates.add( new int[] { x, y } );
440                    lastX = x;
441                    lastY = y;
442                    i++;
443                }
444            }
445    
446            // pick lists of boxes on the linestring
447            ArrayList<Label> pick = new ArrayList<Label>( 100 );
448            int n = labels.size();
449            for ( int j = n / 2; j < labels.size(); j += ( gap + 1 ) ) {
450                pick.add( labels.get( j ) );
451            }
452            for ( int j = n / 2 - ( gap + 1 ); j > 0; j -= ( gap + 1 ) ) {
453                pick.add( labels.get( j ) );
454            }
455            return pick;
456        }
457    
458        /**
459         * Calculates the maximum deviation that points on a linestring have to the ideal line between
460         * the starting point and the end point.
461         * <p>
462         * The ideal line is thought to be running from left to right, the left deviation value
463         * generally is above the line, the right value is below.
464         * <p>
465         * 
466         * @param start
467         *            starting point of the linestring
468         * @param end
469         *            end point of the linestring
470         * @param points
471         *            points in between
472         * @return maximum deviation
473         */
474        public static double[] calcDeviation( int[] start, int[] end, List<int[]> points ) {
475    
476            // extreme deviation to the left
477            double d1 = 0.0;
478            // extreme deviation to the right
479            double d2 = 0.0;
480            Iterator it = points.iterator();
481    
482            // eventually swap start and end point
483            if ( start[0] > end[0] ) {
484                int[] tmp = start;
485                start = end;
486                end = tmp;
487            }
488    
489            if ( start[0] != end[0] ) {
490                // label orientation is not completly vertical
491                if ( start[1] != end[1] ) {
492                    // label orientation is not completly horizontal
493                    while ( it.hasNext() ) {
494                        int[] point = (int[]) it.next();
495                        double u = ( (double) end[1] - (double) start[1] ) / ( (double) end[0] - (double) start[0] );
496                        double x = ( u * u * start[0] - u * ( (double) start[1] - (double) point[1] ) + point[0] )
497                                   / ( 1.0 + u * u );
498                        double y = ( x - start[0] ) * u + start[1];
499                        double d = getDistance( point, new int[] { (int) ( x + 0.5 ), (int) ( y + 0.5 ) } );
500                        if ( y >= point[1] ) {
501                            // candidate for left extreme value
502                            if ( d > d1 ) {
503                                d1 = d;
504                            }
505                        } else if ( d > d2 ) {
506                            // candidate for right extreme value
507                            d2 = d;
508                        }
509                    }
510                } else {
511                    // label orientation is completly horizontal
512                    while ( it.hasNext() ) {
513                        int[] point = (int[]) it.next();
514                        double d = point[1] - start[1];
515                        if ( d < 0 ) {
516                            // candidate for left extreme value
517                            if ( -d > d1 ) {
518                                d1 = -d;
519                            }
520                        } else if ( d > d2 ) {
521                            // candidate for left extreme value
522                            d2 = d;
523                        }
524                    }
525                }
526            } else {
527                // label orientation is completly vertical
528                while ( it.hasNext() ) {
529                    int[] point = (int[]) it.next();
530                    double d = point[0] - start[0];
531                    if ( d < 0 ) {
532                        // candidate for left extreme value
533                        if ( -d > d1 ) {
534                            d1 = -d;
535                        }
536                    } else if ( d > d2 ) {
537                        // candidate for right extreme value
538                        d2 = d;
539                    }
540                }
541            }
542            return new double[] { d1, d2 };
543        }
544    
545        /**
546         * Finds a point on the line between p1 and p2 that has a certain distance from point p0
547         * (provided that there is such a point).
548         * <p>
549         * 
550         * @param p0
551         *            point that is used as reference point for the distance
552         * @param p1
553         *            starting point of the line
554         * @param p2
555         *            end point of the line
556         * @param d
557         *            distance
558         * @return point on the line between p1 and p2 that has a certain distance from point p0
559         */
560        public static int[] findPointWithDistance( int[] p0, int[] p1, int[] p2, int d ) {
561    
562            double x, y;
563            double x0 = p0[0];
564            double y0 = p0[1];
565            double x1 = p1[0];
566            double y1 = p1[1];
567            double x2 = p2[0];
568            double y2 = p2[1];
569    
570            if ( x1 != x2 ) {
571                // line segment does not run vertical
572                double u = ( y2 - y1 ) / ( x2 - x1 );
573                double p = -2 * ( x0 + u * u * x1 - u * ( y1 - y0 ) ) / ( u * u + 1 );
574                double q = ( ( y1 - y0 ) * ( y1 - y0 ) + u * u * x1 * x1 + x0 * x0 - 2 * u * x1 * ( y1 - y0 ) - d * d )
575                           / ( u * u + 1 );
576                double minX = x1;
577                double maxX = x2;
578                double minY = y1;
579                double maxY = y2;
580                if ( minX > maxX ) {
581                    minX = x2;
582                    maxX = x1;
583                }
584                if ( minY > maxY ) {
585                    minY = y2;
586                    maxY = y1;
587                }
588                x = -p / 2 - Math.sqrt( ( p / 2 ) * ( p / 2 ) - q );
589                if ( x < minX || x > maxX ) {
590                    x = -p / 2 + Math.sqrt( ( p / 2 ) * ( p / 2 ) - q );
591                }
592                y = ( x - x1 ) * u + y1;
593            } else {
594                // vertical line segment
595                x = x1;
596                double minY = y1;
597                double maxY = y2;
598    
599                if ( minY > maxY ) {
600                    minY = y2;
601                    maxY = y1;
602                }
603    
604                double p = -2 * y0;
605                double q = y0 * y0 + ( x1 - x0 ) * ( x1 - x0 ) - d * d;
606    
607                y = -p / 2 - Math.sqrt( ( p / 2 ) * ( p / 2 ) - q );
608                if ( y < minY || y > maxY ) {
609                    y = -p / 2 + Math.sqrt( ( p / 2 ) * ( p / 2 ) - q );
610                }
611            }
612            return new int[] { (int) ( x + 0.5 ), (int) ( y + 0.5 ) };
613        }
614    
615        /**
616         * @param x1
617         * @param y1
618         * @param x2
619         * @param y2
620         * @return rotation (degree) of the line between two passed coordinates
621         */
622        public static double getRotation( double x1, double y1, double x2, double y2 ) {
623            double dx = x2 - x1;
624            double dy = y2 - y1;
625    
626            return Math.toDegrees( Math.atan( dy / dx ) );
627        }
628    
629        /**
630         * @param p1
631         * @param p2
632         * @return distance between two passed coordinates
633         */
634        public static double getDistance( int[] p1, int[] p2 ) {
635            double dx = p1[0] - p2[0];
636            double dy = p1[1] - p2[1];
637            return Math.sqrt( dx * dx + dy * dy );
638        }
639    
640        /**
641         * @param x1
642         * @param y1
643         * @param x2
644         * @param y2
645         * @return distance between two passed coordinates
646         */
647        public static double getDistance( double x1, double y1, double x2, double y2 ) {
648            double dx = x2 - x1;
649            double dy = y2 - y1;
650            return Math.sqrt( dx * dx + dy * dy );
651        }
652    
653        /**
654         * Calculates the screen coordinates of the given <tt>Curve</tt>.
655         * physical screen coordinates
656         */
657        public static int[][] calcScreenCoordinates( GeoTransform projection, Curve curve ) {
658    
659            LineString lineString = null;
660            try {
661                lineString = curve.getAsLineString();
662            } catch ( GeometryException e ) {
663            }
664    
665            int count = lineString.getNumberOfPoints();
666            int[][] pos = new int[3][];
667            pos[0] = new int[count];
668            pos[1] = new int[count];
669            pos[2] = new int[1];
670    
671            int k = 0;
672            for ( int i = 0; i < count; i++ ) {
673                Position position = lineString.getPositionAt( i );
674                double tx = projection.getDestX( position.getX() );
675                double ty = projection.getDestY( position.getY() );
676                if ( i > 0 ) {
677                    if ( getDistance( tx, ty, pos[0][k - 1], pos[1][k - 1] ) > 1 ) {
678                        pos[0][k] = (int) ( tx + 0.5 );
679                        pos[1][k] = (int) ( ty + 0.5 );
680                        k++;
681                    }
682                } else {
683                    pos[0][k] = (int) ( tx + 0.5 );
684                    pos[1][k] = (int) ( ty + 0.5 );
685                    k++;
686                }
687            }
688            pos[2][0] = k;
689    
690            return pos;
691        }
692    
693        /**
694         * Returns the physical (screen) coordinates.
695         * @return physical screen coordinates
696         */
697        public static int[] calcScreenCoordinates( GeoTransform projection, Geometry geometry ) {
698    
699            int[] coords = new int[2];
700    
701            Position source = null;
702            if ( geometry instanceof Point ) {
703                source = ( (Point) geometry ).getPosition();
704            } else if ( geometry instanceof Curve || geometry instanceof MultiCurve ) {
705                source = geometry.getCentroid().getPosition();
706            } else {
707                source = geometry.getCentroid().getPosition();
708            }
709    
710            coords[0] = (int) ( projection.getDestX( source.getX() ) + 0.5 );
711            coords[1] = (int) ( projection.getDestY( source.getY() ) + 0.5 );
712            return coords;
713        }
714    }