001    //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/graphics/sld/Mark.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 static org.deegree.framework.xml.XMLTools.escape;
039    
040    import java.awt.BasicStroke;
041    import java.awt.Color;
042    import java.awt.FontMetrics;
043    import java.awt.Graphics2D;
044    import java.awt.Polygon;
045    import java.awt.image.BufferedImage;
046    import java.net.MalformedURLException;
047    import java.net.URL;
048    
049    import org.deegree.framework.util.StringTools;
050    import org.deegree.framework.xml.Marshallable;
051    import org.deegree.model.feature.Feature;
052    import org.deegree.model.filterencoding.FilterEvaluationException;
053    import org.jfree.util.Log;
054    
055    /**
056     * A Mark takes a "shape" and applies coloring to it. The shape can be derived either from a well-known name (such as
057     * "square"), an external URL in various formats (such as, perhaps GIF), or from a glyph of a font. Multiple external
058     * formats may be used with the semantic that they all contain the equivalent shape in different formats. If an image
059     * format is used that has inherent coloring, the coloring is discarded and only the opacity channel (or equivalent) is
060     * used. A Halo, Fill, and/or Stroke is applied as appropriate for the shape's source format.
061     * <p>
062     * 
063     * @author <a href="mailto:k.lupp@web.de">Katharina Lupp </a>
064     * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a>
065     * @version $Revision: 27181 $ $Date: 2010-10-05 15:24:30 +0200 (Di, 05 Okt 2010) $
066     */
067    
068    public class Mark implements Marshallable {
069    
070        private BufferedImage image = null;
071    
072        private Fill fill = null;
073    
074        private String wellKnownName = null;
075    
076        private Stroke stroke = null;
077    
078        /**
079         * Constructor for the default <tt>Mark</tt>.
080         */
081        Mark() {
082            // nothing to do
083        }
084    
085        /**
086         * constructor initializing the class with the <Mark>
087         * 
088         * @param wellKnownName
089         * @param stroke
090         * @param fill
091         */
092        Mark( String wellKnownName, Stroke stroke, Fill fill ) {
093            setWellKnownName( wellKnownName );
094            setStroke( stroke );
095            setFill( fill );
096        }
097    
098        /**
099         * Gives the well known name of a Mark's shape. Allowed values include at least "square", "circle", "triangle",
100         * "star", "cross", and "x", though map servers may draw a different symbol instead if they don't have a shape for
101         * all of these. Renderings of these marks may be made solid or hollow depending on Fill and Stroke parameters. The
102         * default value is "square".
103         * 
104         * @return the WK-Name of the mark
105         * 
106         */
107        public String getWellKnownName() {
108            return wellKnownName;
109        }
110    
111        /**
112         * Sets the well known name of a Mark's shape. Allowed values include at least "square", "circle", "triangle",
113         * "star", "cross", and "x", though map servers may draw a different symbol instead if they don't have a shape for
114         * all of these. Renderings of these marks may be made solid or hollow depending on Fill and Stroke parameters. The
115         * default value is "square".
116         * 
117         * @param wellKnownName
118         *            the WK-Name of the mark
119         * 
120         */
121        public void setWellKnownName( String wellKnownName ) {
122            this.wellKnownName = wellKnownName;
123        }
124    
125        /**
126         * A Fill allows area geometries to be filled. There are two types of fills: solid-color and repeated GraphicFill.
127         * In general, if a Fill element is omitted in its containing element, no fill will be rendered. The default is a
128         * solid 50%-gray (color "#808080") opaque fill.
129         * 
130         * @return the fill of the mark
131         */
132        public Fill getFill() {
133            return fill;
134        }
135    
136        /**
137         * sets the <Fill>
138         * 
139         * @param fill
140         *            the fill of the mark
141         */
142        public void setFill( Fill fill ) {
143            this.fill = fill;
144        }
145    
146        /**
147         * A Stroke allows a string of line segments (or any linear geometry) to be rendered. There are three basic types of
148         * strokes: solid Color, GraphicFill (stipple), and repeated GraphicStroke. A repeated graphic is plotted linearly
149         * and has its graphic symbol bended around the curves of the line string. The default is a solid black line (Color
150         * "#000000").
151         * 
152         * @return the stroke of the mark
153         */
154        public Stroke getStroke() {
155            return stroke;
156        }
157    
158        /**
159         * sets <Stroke>
160         * 
161         * @param stroke
162         *            the stroke of the mark
163         */
164        public void setStroke( Stroke stroke ) {
165            this.stroke = stroke;
166        }
167    
168        /**
169         * Draws the given feature on the buffered image. The drawing values are used from the 'Fill' object (if set). or
170         * white as a default fill color and black as a stroke color. If the stroke is null the BasicStroke values
171         * (CAP_ROUND, JOIN_ROUND) will be used.
172         * <p>
173         * Be careful to set the buffered image first!.
174         * 
175         * @param feature
176         * 
177         * @param size
178         *            DOCUMENT ME!
179         * 
180         * @return The feature as a buffered image with a the 'Fill' values. or white as a default fill color and black as a
181         *         stroke color.
182         * @throws FilterEvaluationException
183         */
184        public BufferedImage getAsImage( Feature feature, int size )
185                                throws FilterEvaluationException {
186            double fillOpacity = 1.0;
187            double strokeOpacity = 1.0;
188            float[] dash = null;
189            float dashOffset = 0;
190            int cap = BasicStroke.CAP_ROUND;
191            int join = BasicStroke.JOIN_ROUND;
192            float width = 1;
193            Color fillColor = new Color( 128, 128, 128 );
194            Color strokeColor = new Color( 0, 0, 0 );
195            String symbol = null;
196    
197            if ( fill != null ) {
198                fillOpacity = fill.getOpacity( feature );
199                fillColor = fill.getFill( feature );
200                symbol = fill.getSymbol( feature );
201            }
202    
203            if ( stroke != null ) {
204                strokeOpacity = stroke.getOpacity( feature );
205                strokeColor = stroke.getStroke( feature );
206                dash = stroke.getDashArray( feature );
207                cap = stroke.getLineCap( feature );
208                join = stroke.getLineJoin( feature );
209                width = (float) stroke.getWidth( feature );
210                dashOffset = stroke.getDashOffset( feature );
211            }
212            if ( wellKnownName == null ) {
213                wellKnownName = "square";
214            }
215    
216            if ( symbol != null ) {
217                try {
218                    ExternalGraphic eg = new ExternalGraphic( null, new URL( symbol ) );
219                    return eg.getAsImage( size, size, feature );
220                } catch ( MalformedURLException e ) {
221                    Log.debug( "Could not create an image out of the given symbol with url " + symbol );
222                }
223            } else if ( wellKnownName.equalsIgnoreCase( "circle" ) ) {
224                image = drawCircle( size, fillOpacity, fillColor, strokeOpacity, strokeColor, dash, dashOffset, width, cap,
225                                    join );
226            } else if ( wellKnownName.equalsIgnoreCase( "triangle" ) ) {
227                image = drawTriangle( size, fillOpacity, fillColor, strokeOpacity, strokeColor, dash, dashOffset, width,
228                                      cap, join );
229            } else if ( wellKnownName.equalsIgnoreCase( "cross" ) ) {
230                image = drawCross1( size, strokeOpacity, strokeColor, dash, dashOffset, width, cap, join );
231            } else if ( wellKnownName.equalsIgnoreCase( "x" ) ) {
232                image = drawCross2( size, strokeOpacity, strokeColor, dash, dashOffset, width, cap, join );
233            } else if ( wellKnownName.startsWith( "CHAR" ) ) {
234                image = drawCharacter( size, fillOpacity, fillColor, strokeOpacity, strokeColor, wellKnownName );
235            } else if ( wellKnownName.equalsIgnoreCase( "star" ) ) {
236                image = drawStar( size, fillOpacity, fillColor, strokeOpacity, strokeColor, dash, dashOffset, width, cap,
237                                  join );
238            } else {
239                image = drawSquare( size, fillOpacity, fillColor, strokeOpacity, strokeColor, dash, dashOffset, width, cap,
240                                    join );
241            }
242    
243            return image;
244        }
245    
246        /**
247         * Sets the mark as an image. RThis method is not part of the sld specifications but it is added to speed up
248         * applications.
249         * 
250         * @param bufferedImage
251         *            the bufferedImage to be set for the mark
252         */
253        public void setAsImage( BufferedImage bufferedImage ) {
254            this.image = bufferedImage;
255        }
256    
257        /**
258         * 
259         * @param dash
260         * @param dashOffset
261         * @param width
262         * @param cap
263         * @param join
264         * @return the basic stroke
265         */
266        private BasicStroke createBasicStroke( float[] dash, float dashOffset, float width, int cap, int join ) {
267            BasicStroke bs2 = null;
268            if ( ( dash == null ) || ( dash.length < 2 ) ) {
269                bs2 = new BasicStroke( width, cap, join );
270            } else {
271                bs2 = new BasicStroke( width, cap, join, 10.0f, dash, dashOffset );
272            }
273            return bs2;
274        }
275    
276        /**
277         * Draws a scaled instance of a triangle mark according to the given parameters.
278         * 
279         * @param size
280         *            resulting image's height and width
281         * @param fillOpacity
282         *            opacity value for the filled parts of the image
283         * @param fillColor
284         *            <tt>Color</tt> to be used for the fill
285         * @param strokeOpacity
286         *            opacity value for the stroked parts of the image
287         * @param strokeColor
288         *            <tt>Color</tt> to be used for the strokes
289         * @param dash
290         *            dash array for rendering boundary line
291         * @param width
292         *            of the boundary line
293         * @param cap
294         *            of the boundary line
295         * @param join
296         *            of the boundary line
297         * @param dashOffset
298         * 
299         * @return image displaying a triangle
300         */
301        public BufferedImage drawTriangle( int size, double fillOpacity, Color fillColor, double strokeOpacity,
302                                           Color strokeColor, float[] dash, float dashOffset, float width, int cap, int join ) {
303    
304            int offset = (int) ( width * 2 + 1 ) / 2;
305            BufferedImage image = new BufferedImage( size + offset * 2, size + offset * 2, BufferedImage.TYPE_INT_ARGB );
306    
307            int[] x_ = new int[3];
308            int[] y_ = new int[3];
309            x_[0] = offset;
310            y_[0] = offset;
311            x_[1] = size / 2 + offset;
312            y_[1] = size - 1 + offset;
313            x_[2] = size - 1 + offset;
314            y_[2] = offset;
315    
316            Graphics2D g2D = (Graphics2D) image.getGraphics();
317            BasicStroke bs = createBasicStroke( dash, dashOffset, width, cap, join );
318            g2D.setStroke( bs );
319            setColor( g2D, fillColor, fillOpacity );
320            g2D.fillPolygon( x_, y_, 3 );
321            setColor( g2D, strokeColor, strokeOpacity );
322            g2D.drawPolygon( x_, y_, 3 );
323            g2D.dispose();
324    
325            return image;
326        }
327    
328        /**
329         * Draws a five-pointed star (pentagram) according to the given parameters.
330         * 
331         * @param size
332         *            resulting image's height and width
333         * @param fillOpacity
334         *            opacity value for the filled parts of the image
335         * @param fillColor
336         *            <tt>Color</tt> to be used for the fill
337         * @param strokeOpacity
338         *            opacity value for the stroked parts of the image
339         * @param strokeColor
340         *            <tt>Color</tt> to be used for the strokes
341         * @param dash
342         *            dash array for rendering boundary line
343         * @param width
344         *            of the boundary line
345         * @param cap
346         *            of the boundary line
347         * @param join
348         *            of the boundary line
349         * @param dashOffset
350         * 
351         * @return an image of a pentagram
352         */
353        public BufferedImage drawStar( int size, double fillOpacity, Color fillColor, double strokeOpacity,
354                                       Color strokeColor, float[] dash, float dashOffset, float width, int cap, int join ) {
355            int offset = (int) ( width * 2 + 1 ) / 2;
356            BufferedImage image = new BufferedImage( size + offset * 2, size + offset * 2, BufferedImage.TYPE_INT_ARGB );
357    
358            Graphics2D g2D = image.createGraphics();
359            BasicStroke bs = createBasicStroke( dash, dashOffset, width, cap, join );
360            g2D.setStroke( bs );
361            int s = size / 2;
362            int x0 = s;
363            int y0 = s;
364            double sin36 = Math.sin( Math.toRadians( 36 ) );
365            double cos36 = Math.cos( Math.toRadians( 36 ) );
366            double sin18 = Math.sin( Math.toRadians( 18 ) );
367            double cos18 = Math.cos( Math.toRadians( 18 ) );
368            int smallRadius = (int) ( s * sin18 / Math.sin( Math.toRadians( 54 ) ) );
369    
370            int p0X = x0;
371            int p0Y = y0 - s;
372            int p1X = x0 + (int) ( smallRadius * sin36 );
373            int p1Y = y0 - (int) ( smallRadius * cos36 );
374            int p2X = x0 + (int) ( s * cos18 );
375            int p2Y = y0 - (int) ( s * sin18 );
376            int p3X = x0 + (int) ( smallRadius * cos18 );
377            int p3Y = y0 + (int) ( smallRadius * sin18 );
378            int p4X = x0 + (int) ( s * sin36 );
379            int p4Y = y0 + (int) ( s * cos36 );
380            int p5Y = y0 + smallRadius;
381            int p6X = x0 - (int) ( s * sin36 );
382            int p7X = x0 - (int) ( smallRadius * cos18 );
383            int p8X = x0 - (int) ( s * cos18 );
384            int p9X = x0 - (int) ( smallRadius * sin36 );
385    
386            int[] x = new int[] { p0X, p1X, p2X, p3X, p4X, p0X, p6X, p7X, p8X, p9X };
387            int[] y = new int[] { p0Y, p1Y, p2Y, p3Y, p4Y, p5Y, p4Y, p3Y, p2Y, p1Y };
388            for ( int i = 0; i < y.length; i++ ) {
389                x[i] = x[i] + offset;
390                y[i] = y[i] + offset;
391            }
392            Polygon shape = new Polygon( x, y, 10 );
393    
394            setColor( g2D, fillColor, fillOpacity );
395            g2D.fill( shape );
396            setColor( g2D, strokeColor, strokeOpacity );
397            g2D.draw( shape );
398    
399            g2D.dispose();
400    
401            return image;
402        }
403    
404        /**
405         * Draws a scaled instance of a circle mark according to the given parameters.
406         * 
407         * @param size
408         *            resulting image's height and widthh
409         * @param fillOpacity
410         *            opacity value for the filled parts of the image
411         * @param fillColor
412         *            <tt>Color</tt> to be used for the fill
413         * @param strokeOpacity
414         *            opacity value for the stroked parts of the image
415         * @param strokeColor
416         *            <tt>Color</tt> to be used for the strokes
417         * @param dash
418         *            dash array for rendering boundary line
419         * @param width
420         *            of the boundary line
421         * @param cap
422         *            of the boundary line
423         * @param join
424         *            of the boundary line
425         * @param dashOffset
426         * 
427         * @return image displaying a circle
428         */
429        public BufferedImage drawCircle( int size, double fillOpacity, Color fillColor, double strokeOpacity,
430                                         Color strokeColor, float[] dash, float dashOffset, float width, int cap, int join ) {
431            int offset = (int) ( width * 2 + 1 ) / 2;
432            BufferedImage image = new BufferedImage( size + offset * 2, size + offset * 2, BufferedImage.TYPE_INT_ARGB );
433    
434            Graphics2D g2D = (Graphics2D) image.getGraphics();
435            BasicStroke bs = createBasicStroke( dash, dashOffset, width, cap, join );
436            g2D.setStroke( bs );
437            setColor( g2D, fillColor, fillOpacity );
438            g2D.fillOval( offset, offset, size, size );
439    
440            setColor( g2D, strokeColor, strokeOpacity );
441            g2D.drawOval( offset, offset, size, size );
442            g2D.dispose();
443    
444            return image;
445        }
446    
447        /**
448         * Draws a scaled instance of a square mark according to the given parameters.
449         * 
450         * @param size
451         *            resulting image's height and widthh
452         * @param fillOpacity
453         *            opacity value for the filled parts of the image
454         * @param fillColor
455         *            <tt>Color</tt> to be used for the fill
456         * @param strokeOpacity
457         *            opacity value for the stroked parts of the image
458         * @param strokeColor
459         *            <tt>Color</tt> to be used for the strokes
460         * @param dash
461         *            dash array for rendering boundary line
462         * @param width
463         *            of the boundary line
464         * @param cap
465         *            of the boundary line
466         * @param join
467         *            of the boundary line
468         * @param dashOffset
469         * 
470         * @return image displaying a square
471         */
472        public BufferedImage drawSquare( int size, double fillOpacity, Color fillColor, double strokeOpacity,
473                                         Color strokeColor, float[] dash, float dashOffset, float width, int cap, int join ) {
474            int offset = (int) ( width * 2 + 1 ) / 2;
475            BufferedImage image = new BufferedImage( size + offset * 2, size + offset * 2, BufferedImage.TYPE_INT_ARGB );
476    
477            Graphics2D g2D = (Graphics2D) image.getGraphics();
478            BasicStroke bs = createBasicStroke( dash, dashOffset, width, cap, join );
479            g2D.setStroke( bs );
480            setColor( g2D, fillColor, fillOpacity );
481            g2D.fillRect( offset, offset, size, size );
482    
483            setColor( g2D, strokeColor, strokeOpacity );
484            g2D.drawRect( offset, offset, size - 1, size - 1 );
485            g2D.dispose();
486    
487            return image;
488        }
489    
490        /**
491         * Draws a scaled instance of a cross mark (a "+") according to the given parameters.
492         * 
493         * @param size
494         *            resulting image's height and widthh
495         * @param strokeOpacity
496         *            opacity value for the stroked parts of the image
497         * @param strokeColor
498         *            <tt>Color</tt> to be used for the strokes
499         * @param dash
500         *            dash array for rendering boundary line
501         * @param width
502         *            of the boundary line
503         * @param cap
504         *            of the boundary line
505         * @param join
506         *            of the boundary line
507         * @param dashOffset
508         * 
509         * @return image displaying a cross (a "+")
510         */
511        public BufferedImage drawCross1( int size, double strokeOpacity, Color strokeColor, float[] dash, float dashOffset,
512                                         float width, int cap, int join ) {
513    
514            int offset = (int) ( width * 2 + 1 ) / 2;
515            BufferedImage image = new BufferedImage( size + offset * 2, size + offset * 2, BufferedImage.TYPE_INT_ARGB );
516    
517            Graphics2D g2D = (Graphics2D) image.getGraphics();
518    
519            BasicStroke bs = createBasicStroke( dash, dashOffset, width, cap, join );
520            g2D.setStroke( bs );
521    
522            setColor( g2D, strokeColor, strokeOpacity );
523            g2D.drawLine( offset, size / 2 + offset, size - 1 + offset, size / 2 + offset );
524            g2D.drawLine( size / 2 + offset, offset, size / 2 + offset, size - 1 + offset );
525            g2D.dispose();
526            return image;
527        }
528    
529        /**
530         * Draws a scaled instance of a cross mark (an "X") according to the given parameters.
531         * 
532         * @param size
533         *            resulting image's height and widthh
534         * @param strokeOpacity
535         *            opacity value for the stroked parts of the image
536         * @param strokeColor
537         *            <tt>Color</tt> to be used for the strokes
538         * @param dash
539         *            dash array for rendering boundary line
540         * @param width
541         *            of the boundary line
542         * @param cap
543         *            of the boundary line
544         * @param join
545         *            of the boundary line
546         * @param dashOffset
547         * 
548         * @return image displaying a cross (a "X")
549         */
550        public BufferedImage drawCross2( int size, double strokeOpacity, Color strokeColor, float[] dash, float dashOffset,
551                                         float width, int cap, int join ) {
552    
553            int offset = (int) ( width * 2 + 1 ) / 2;
554            BufferedImage image = new BufferedImage( size + offset * 2, size + offset * 2, BufferedImage.TYPE_INT_ARGB );
555    
556            Graphics2D g2D = (Graphics2D) image.getGraphics();
557    
558            BasicStroke bs = createBasicStroke( dash, dashOffset, width, cap, join );
559            g2D.setStroke( bs );
560    
561            setColor( g2D, strokeColor, strokeOpacity );
562            g2D.drawLine( offset, offset, size - 1 + offset, size - 1 + offset );
563            g2D.drawLine( offset, size - 1 + offset, size - 1 + offset, offset );
564            g2D.dispose();
565    
566            return image;
567        }
568    
569        /**
570         * 
571         * @param size
572         * @param fillOpacity
573         * @param fillColor
574         * @param strokeOpacity
575         * @param strokeColor
576         * @param charDesc
577         *            e.g. CHAR:Times New Roman:45
578         */
579        private BufferedImage drawCharacter( int size, double fillOpacity, Color fillColor, double strokeOpacity,
580                                             Color strokeColor, String charDesc ) {
581    
582            String[] tmp = StringTools.toArray( charDesc, ":", false );
583    
584            BufferedImage image = new BufferedImage( size, size, BufferedImage.TYPE_INT_ARGB );
585    
586            Graphics2D g2 = (Graphics2D) image.getGraphics();
587            setColor( g2, fillColor, fillOpacity );
588            g2.fillRect( 0, 0, size, size );
589    
590            java.awt.Font font = new java.awt.Font( tmp[1], java.awt.Font.PLAIN, size );
591            g2.setFont( font );
592            FontMetrics fm = g2.getFontMetrics();
593    
594            char c = (char) Integer.parseInt( tmp[2] );
595            int w = fm.charWidth( c );
596            int h = fm.getHeight();
597    
598            String s = "" + c;
599            setColor( g2, strokeColor, strokeOpacity );
600            g2.drawString( s, size / 2 - w / 2, size / 2 + h / 2 - fm.getDescent() );
601            g2.dispose();
602            return image;
603        }
604    
605        /**
606         * @param g2D
607         * @param color
608         * @param opacity
609         */
610        private void setColor( Graphics2D g2D, Color color, double opacity ) {
611            if ( opacity < 0.999 ) {
612                final int alpha = (int) Math.round( opacity * 255 );
613                final int red = color.getRed();
614                final int green = color.getGreen();
615                final int blue = color.getBlue();
616                color = new Color( red, green, blue, alpha );
617            }
618    
619            g2D.setColor( color );
620        }
621    
622        /**
623         * exports the content of the Mark as XML formated String
624         * 
625         * @return xml representation of the Mark
626         */
627        public String exportAsXML() {
628    
629            StringBuffer sb = new StringBuffer( 1000 );
630            sb.append( "<Mark>" );
631            if ( wellKnownName != null && !wellKnownName.equals( "" ) ) {
632                sb.append( "<WellKnownName>" ).append( escape( wellKnownName ) );
633                sb.append( "</WellKnownName>" );
634            }
635            if ( fill != null ) {
636                sb.append( ( (Marshallable) fill ).exportAsXML() );
637            }
638            if ( stroke != null ) {
639                sb.append( ( (Marshallable) stroke ).exportAsXML() );
640            }
641    
642            sb.append( "</Mark>" );
643    
644            return sb.toString();
645        }
646    
647        // private void drawUnicode(Graphics2D g2, int x, int y, double rotation,
648        // double size, String m, Mark mark) {
649        // int sz = (int)size;
650        // double fo = mark.getFill().getOpacity();
651        // double so = mark.getStroke().getOpacity();
652        //
653        // java.awt.Font font = new java.awt.Font("sans serif", java.awt.Font.PLAIN,
654        // sz);
655        // g2.setFont( font );
656        // FontMetrics fm = g2.getFontMetrics();
657        //
658        // char c = (char)m.charAt(0);
659        // int w = fm.charWidth(c);
660        // int h = fm.getHeight();
661        //
662        // g2 = setColor( g2, mark.getFill().getFill(), fo );
663        // g2.fillRect( x-w/2, y-h/2, w, h);
664        // g2 = setColor( g2, mark.getStroke().getStroke(), so );
665        //
666        // String s = "" + c;
667        // g2.drawString( s, x-w/2, y+h/2-fm.getDescent());
668        // }
669        // else {
670        //
671        // Mark[] marks = sym.getGraphic().getMarks();
672        // double rotation = sym.getGraphic().getRotation();
673        // double size = sym.getGraphic().getSize();
674        // if (marks != null) {
675        //
676        // for (int k = 0; k > marks.length; k++) {
677        //
678        // float w = (float)marks[k].getStroke().getWidth();
679        // g2.setStroke( new BasicStroke( w ) );
680        //
681        // if (marks[k].getWellKnownName().equalsIgnoreCase("triangle") ) {
682        // drawTriangle( g2, x, y, rotation, size, marks[k] );
683        // }
684        // else
685        // if (marks[k].getWellKnownName().equalsIgnoreCase("circle") ) {
686        // drawCircle( g2, x, y, rotation, size, marks[k] );
687        // }
688        // else
689        // if (marks[k].getWellKnownName().equalsIgnoreCase("square") ) {
690        // drawSquare( g2, x, y, rotation, size, marks[k] );
691        // }
692        // else
693        // if (marks[k].getWellKnownName().equalsIgnoreCase("cross") ) {
694        // drawCross1( g2, x, y, rotation, size, marks[k] );
695        // }
696        // else
697        // if (marks[k].getWellKnownName().equalsIgnoreCase("x") ) {
698        // drawCross2( g2, x, y, rotation, size, marks[k] );
699        // }
700        // else
701        // if (marks[k].getWellKnownName().length() == 0 ) {
702        // drawSquare( g2, x, y, rotation, size, marks[k] );
703        // }
704        // else {
705        // drawUnicode( g2, x, y, rotation, size,
706        // marks[k].getWellKnownName(), marks[k] );
707        // }
708        // }
709        // }
710        // }
711    }