/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2005-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.legend;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.image.BufferedImage;
import org.geotools.styling.Rule;
import org.opengis.feature.simple.SimpleFeatureType;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
/**
* Renderers a small Glyph used to represent a Map Layer in a legend.
* <p>
* Because GeoTools is used both with Swing and SWT applications this class
* is set up to produce BufferedImage.
*
* @author Johann Sorel (AlterSIG)
*
*
* @source $URL$
*/
public class Glyph {
/** Utility class for working with Images, Features and Styles */
private static Drawer drawer = Drawer.create();
private final static int DEFAULT_WIDTH = 16;
private final static int DEFAULT_HEIGHT = 16;
private static final int DEFAULT_DEPTH = BufferedImage.TYPE_INT_ARGB;
private static final Color DEFAULT_BORDER = new Color(0,0,0);
private static final Color DEFAULT_FILL = new Color(27,158,119, 255);
/**
* Create a transparent image, this is a *real* resource against the
* provided display.
*
* @return
*/
public static BufferedImage image() {
BufferedImage bi = new BufferedImage(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_DEPTH);
return bi;
}
/**
* Render a icon based on the current style.
* <p>
* Simple render of point in the center of the screen.
* </p>
* @param style
* @return Icon representing style applyed to an image
*/
public static BufferedImage point( final Rule rule ) {
BufferedImage bi = new BufferedImage(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_DEPTH);
drawer.drawDirect( bi, drawer.feature(drawer.point(7,7)), rule );
return bi;
}
/**
* Icon for point data in the provided color
* <p>
* XXX: Suggest point( SLD style ) at a later time.
* </p>
* @param color
* @param fill
* @return ImageDescriptor
*/
public static BufferedImage point( final Color color, final Color fill ) {
BufferedImage bi = new BufferedImage(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_DEPTH);
Graphics2D gc = (Graphics2D) bi.getGraphics();
gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
Stroke stroke = new BasicStroke(1);
gc.setStroke(stroke);
Color c = color;
Color f = fill;
if( c == null ) c = Color.BLACK;
if( f == null ) f = Color.LIGHT_GRAY;
gc.setColor(f);
gc.fillRect( 8,7, 5, 5 );
gc.setColor(c);
gc.drawRect( 8,7, 5, 5 );
return bi;
}
/**
* Complex render of Geometry allowing presentation of point, line and polygon styles.
* <p>
* Layout:<pre><code>
* 1 2 3 4 5 6 7 8 9101112131415
* 0
* 1 LL L
* 2 L L L
* 3 L L L
* 4 L L L
* 5 L L L
* 6 L L L
* 7 L L L
* 8 L L L
* 9 L L L
* 10 L L L
* 11 L L L
* 12 L L L
* 13 L L L
* 14 L LL
* 15
* </code><pre>
* </p>
* @param style
* @return Icon representing geometry style
*/
public static BufferedImage line( final Rule rule ) {
BufferedImage bi = new BufferedImage(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_DEPTH);
drawer.drawDirect( bi, drawer.feature(drawer.line(new int[]{1,14, 6,0, 11,14, 15,1})), rule );
return bi;
}
/**
* Icon for linestring in the provided color and width.
* <p>
* XXX: Suggest line( SLD style ) at a later time.
* </p>
* @param black
* @return Icon
*/
public static BufferedImage line( Color color, int width ) {
BufferedImage bi = new BufferedImage(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_DEPTH);
Graphics2D gc = (Graphics2D) bi.getGraphics();
gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
Color color2 = color;
int width2 = width;
if (color2 == null) color2 = Color.BLACK;
if (width2 <= 0) width2 = 1;
final int finalWidth = width2;
final Color finalColor = color2;
Stroke stroke = new BasicStroke(finalWidth);
gc.setStroke(stroke);
gc.setColor(finalColor);
gc.drawLine(1, 13, 6, 2);
gc.drawLine(6, 2, 9, 13);
gc.drawLine(9, 13, 14, 2);
return bi;
}
/**
* Complex render of Geometry allowing presentation of point, line and polygon styles.
* <p>
* Layout:<pre><code>
* 1 2 3 4 5 6 7 8 9101112131415
* 0
* 1
* 2
* 3 L L
* 4 p L L PPPPPP
* 5 L L PPPPP L p
* 6 L LPPPP L p
* 7 L PPPL L p
* 8 L PP L L p
* 9 L P L L P
* 10 L P L L P
* 11 L P L L P
* 12 L P L P
* 13 p P
* 14 PPPPPPPPPPPPPPPPPPPPPPPP
* 15
* </code><pre>
* </p>
* @param style
* @return Icon representing geometry style
*/
public static BufferedImage geometry( final Rule rule ) {
BufferedImage bi = new BufferedImage(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_DEPTH);
drawer.drawDirect( bi, drawer.feature(drawer.polygon(new int[]{1,14, 3,9, 4,6, 6,4, 9,3, 14,1, 14,14})), rule );
drawer.drawDirect( bi,drawer.feature(drawer.line(new int[]{0,12, 6,3, 11,12, 15,3})),rule );
drawer.drawDirect( bi, drawer.feature(drawer.point(4,4)), rule );
return bi;
}
/**
* Icon for generic Geometry or Geometry Collection.
* @param color
* @param fill
*
* @return Icon
*/
public static BufferedImage geometry( final Color color, final Color fill ) {
BufferedImage bi = new BufferedImage(DEFAULT_WIDTH, DEFAULT_HEIGHT, BufferedImage.TYPE_INT_ARGB);
Graphics2D gc = (Graphics2D) bi.getGraphics();
gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
Stroke stroke = new BasicStroke(1);
gc.setStroke(stroke);
Color c = color;
Color f = fill;
if( c == null ) c = Color.BLACK;
if( f == null ) f = Color.LIGHT_GRAY;
gc.setColor(f);
gc.fillRoundRect( 2,1, 13, 13, 2, 2 );
gc.setColor(c);
gc.drawRoundRect( 2,1, 13, 13, 2, 2 );
return bi;
}
/**
* Render of a polygon allowing style.
* <p>
* Layout:<pre><code>
* 1 2 3 4 5 6 7 8 9101112131415
* 0
* 1
* 2 PPPPPPPP
* 3 PPPPPP P
* 4 PPPPPP P
* 5 PPP p
* 6 PP p
* 7 P p
* 8 P p
* 9 P P
* 10 P P
* 11 P P
* 12 P P
* 13 P P
* 14 PPPPPPPPPPPPPPPPPPPPPPPPPPPP
* 15
* </code><pre>
* </p>
* @param style
* @return Icon representing geometry style
*/
public static BufferedImage Polygon( final Rule rule ){
BufferedImage bi = new BufferedImage(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_DEPTH);
drawer.drawDirect( bi, drawer.feature(drawer.polygon(new int[]{1,14, 3,9, 4,6, 6,4, 9,3, 14,1, 14,14})), rule );
return bi;
}
/**
* Icon for polygon in provided border, fill and width
*
* @param black
* @param gray
* @param i
* @return
*/
public static BufferedImage polygon( final Color color, final Color fill, final int width ) {
BufferedImage bi = new BufferedImage(DEFAULT_WIDTH, DEFAULT_HEIGHT, BufferedImage.TYPE_INT_ARGB);
Graphics2D gc = (Graphics2D) bi.getGraphics();
gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
Color c = color;
Color f = fill;
int w = (width > 0) ? width : 1;
if( c == null ) c = Color.BLACK;
if( f == null ) f = Color.LIGHT_GRAY;
Stroke stroke = new BasicStroke(w);
gc.setStroke(stroke);
int[] xs = new int[]{1,3,4,6,9,14,14};
int[] ys = new int[]{14,9,6,4,3,1,14};
int nb = 7;
gc.setColor(c);
gc.fillPolygon( xs, ys, nb);
gc.setColor(f);
gc.drawPolygon( xs, ys, nb);
return bi;
}
/**
* Icon for grid data, small grid made up of provided colors.
* <p>
* Layout:<pre><code>
* 0 1 2 3 4 5 6 7 8 9 101112131415
* 0
* 1 AAAAAAAAAAAAABBBBBBBBBBBBBB
* 2 AAAAAAAAAAAAABBBBBBBBBBBBBB
* 3 AAAAAAAAAAAAABBBBBBBBBBBBBB
* 4 AAAAAAAAAAAAABBBBBBBBBBBBBB
* 5 AAAAAAAAAAAAABBBBBBBBBBBBBB
* 6 AAAAAAAAAAAAABBBBBBBBBBBBBB
* 7 AAAAAAAAAAAAABBBBBBBBBBBBBB
* 8 CCCCCCCCCCCCCDDDDDDDDDDDDDD
* 9 CCCCCCCCCCCCCDDDDDDDDDDDDDD
* 10 CCCCCCCCCCCCCDDDDDDDDDDDDDD
* 11 CCCCCCCCCCCCCDDDDDDDDDDDDDD
* 12 CCCCCCCCCCCCCDDDDDDDDDDDDDD
* 13 CCCCCCCCCCCCCDDDDDDDDDDDDDD
* 14 CCCCCCCCCCCCCDDDDDDDDDDDDDD
* 15
* </code><pre>
* </p>
* @param a
* @param b
* @param c
* @param d1
* @return Icon representing a grid
*
*/
public static BufferedImage grid( Color a, Color b, Color c, Color d) {
BufferedImage bi = new BufferedImage(DEFAULT_WIDTH, DEFAULT_HEIGHT, BufferedImage.TYPE_INT_ARGB);
Graphics2D gc = (Graphics2D) bi.getGraphics();
gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
if (a == null) a = Color.BLACK;
if (b == null) b = Color.DARK_GRAY;
if (c == null) c = Color.LIGHT_GRAY;
if (d == null) d = Color.WHITE;
gc.setColor( a );
gc.fillRect( 0, 0, 7, 7);
gc.setColor( b );
gc.fillRect( 7, 0, 15, 7 );
gc.setColor( c );
gc.fillRect( 0, 7, 7, 15 );
gc.setColor( d );
gc.fillRect( 7, 7, 15, 15 );
gc.setColor( Color.BLACK );
gc.drawRect( 0, 0, 7, 7 );
gc.drawRect( 0, 0, 15, 7 );
gc.drawRect( 0, 7, 7, 15 );
gc.drawRect( 0, 7, 15, 15 );
return bi;
}
/**
* Render of a color swatch allowing style.
* <p>
* Layout:<pre><code>
* 0 1 2 3 4 5 6 7 8 9 101112131415
* 0
* 1 dddddddddddddddddddddddddddd
* 2 dCCCCCCCCCCCCCCCCCCCCCcCCCCCCd
* 3 dCCCCCCCCCCCCCCCCCCCCCCcCCCCCd
* 4 dCCCCCCCCCCCCCCCCCCCCCCCcCCCCd
* 5 dCCCCCCCCCCCCCCCCCCCCCCCCcCCCd
* 6 dCCCCCCCCCCCCCCCCCCCCCCCCCcCCd
* 7 dCCCCCCCCCCCCCCCCCCCCCCCCCCcCd
* 8 dCcCCCCCCCCCCCCCCCCCCCCCCCCCCd
* 9 dCCcCCCCCCCCCCCCCCCCCCCCCCCCCd
* 10 dCCCcCCCCCCCCCCCCCCCCCCCCCCCCd
* 11 dCCCCcCCCCCCCCCCCCCCCCCCCCCCCd
* 12 dCCCCCcCCCCCCCCCCCCCCCCCCCCCCd
* 13 ddCCCCCcCCCCCCCCCCCCCCCCCCCCdd
* 14 ddddddddddddddddddddddddddd
* 15
* </code><pre>
* </p>
* @param style
* @return Icon representing geometry style
*/
public static BufferedImage swatch( Color c ) {
BufferedImage bi = new BufferedImage(DEFAULT_WIDTH, DEFAULT_HEIGHT, BufferedImage.TYPE_INT_ARGB);
Graphics2D gc = (Graphics2D) bi.getGraphics();
gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
Color c2=c;
if( c == null ) c2=Color.GRAY;
else c2=c;
final Color color=c2;
int saturation = color.getRed() + color.getGreen() + color.getBlue();
final Color contrast = saturation < 384 ? c.brighter() : c.darker();
gc.setColor( color );
gc.fillRoundRect( 0, 0, 14, 14, 2, 2);
gc.setColor( contrast );
gc.drawRoundRect( 0, 0, 14, 14, 2, 2 );
return bi;
}
/**
* Icon for grid data, small grid made up of provided colors.
* Layout:<pre><code>
* 0 1 2 3 4 5 6 7 8 9 101112131415
* 0
* 1 AABBCDEEFfGgHhIiJjKkllmmnnoopp
* 2 AABBCDEEFfGgHhIiJjKkllmmnnoopp
* 3 AABBCDEEFfGgHhIiJjKkllmmnnoopp
* 4 AABBCDEEFfGgHhIiJjKkllmmnnoopp
* 5 AABBCDEEFfGgHhIiJjKkllmmnnoopp
* 6 AABBCDEEFfGgHhIiJjKkllmmnnoopp
* 7 AABBCDEEFfGgHhIiJjKkllmmnnoopp
* 8 AABBCDEEFfGgHhIiJjKkllmmnnoopp
* 9 AABBCDEEFfGgHhIiJjKkllmmnnoopp
* 10 AABBCDEEFfGgHhIiJjKkllmmnnoopp
* 11 AABBCDEEFfGgHhIiJjKkllmmnnoopp
* 12 AABBCDEEFfGgHhIiJjKkllmmnnoopp
* 14 AABBCDEEFfGgHhIiJjKkllmmnnoopp
* 15
* </code><pre>
* </p>
* @param c palette of colors
* @return Icon representing a palette
*
*/
public static BufferedImage palette( Color c[]) {
BufferedImage bi = new BufferedImage(DEFAULT_WIDTH, DEFAULT_HEIGHT, BufferedImage.TYPE_INT_ARGB);
Graphics2D gc = (Graphics2D) bi.getGraphics();
gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
final Color[] colors = new Color[16];
Color color = Color.GRAY;
if( c == null ){
for( int i=0; i<16; i++) color = Color.GRAY;
} else {
for( int i=0; i<16; i++) {
int lookup = (i*c.length)/16;
if( c[ lookup ] != null ) color = c[ lookup ];
colors[i] = color;
}
}
for( int i=0; i<16;i++){
gc.setColor( colors[i] );
gc.drawLine(i,0,i,15);
}
gc.setColor( Color.GRAY );
gc.drawRoundRect( 0, 0, 14, 14, 2, 2 );
return bi;
}
public static BufferedImage icon( SimpleFeatureType ft ) {
if( ft==null || ft.getGeometryDescriptor()==null )
return null;
Class binding = ft.getGeometryDescriptor().getType().getBinding();
if( Point.class.isAssignableFrom(binding)
|| MultiPoint.class.isAssignableFrom(binding) ){
return point(DEFAULT_BORDER, DEFAULT_FILL);
}
if( LineString.class.isAssignableFrom(binding)
|| MultiLineString.class.isAssignableFrom(binding)
|| LinearRing.class.isAssignableFrom(binding)){
return line(DEFAULT_BORDER, 1);
}
if( Polygon.class.isAssignableFrom(binding)
|| MultiPolygon.class.isAssignableFrom(binding) ){
return polygon(DEFAULT_BORDER, DEFAULT_FILL, 1);
}
return geometry(DEFAULT_BORDER, DEFAULT_FILL);
}
}