package jeql.command.plot;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.text.Bidi;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.imageio.ImageIO;
import jeql.api.row.Row;
import jeql.api.row.RowIterator;
import jeql.api.row.RowSchema;
import jeql.api.row.RowUtil;
import jeql.api.row.SchemaUtil;
import jeql.api.table.Table;
import jeql.awt.geom.Java2DConverter;
import jeql.style.StyleConstants;
import jeql.util.ColorUtil;
import jeql.util.ImageUtil;
import jeql.util.TypeUtil;
import com.vividsolutions.jts.awt.FontGlyphReader;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.Point;
public class LabelPlotter
{
public static void plot(Plot plot, List data)
{
for (Iterator i = data.iterator(); i.hasNext(); ) {
Table tbl = (Table) i.next();
LabelPlotter plotter = new LabelPlotter(plot);
plotter.plot(tbl);
}
}
private Plot plot;
private int geomIndex = -1;
private int labelIndex = -1;
private int labelColorIndex = -1;
private int labelSizeIndex = -1;
private int labelHaloSizeIndex = -1;
private int labelHaloColorIndex = -1;
private int labelRotateIndex = -1;
private int labelOffsetXIndex = -1;
private int labelOffsetYIndex = -1;
private Geometry labelPoint = null;
private int labelPointIndex;
public LabelPlotter(Plot plot) {
this.plot = plot;
}
public void plot(Table tbl)
{
RowIterator ri = tbl.getRows().iterator();
RowSchema schema = ri.getSchema();
geomIndex = SchemaUtil.getColumnWithType(schema, Geometry.class);
labelIndex = SchemaUtil.getColumnIndex(schema, StyleConstants.LABEL);
labelColorIndex = SchemaUtil.getColumnIndex(schema, StyleConstants.LABEL_COLOR);
labelSizeIndex = SchemaUtil.getColumnIndex(schema, StyleConstants.FONT_SIZE);
labelHaloSizeIndex = SchemaUtil.getColumnIndex(schema, StyleConstants.HALO_RADIUS);
labelHaloColorIndex = SchemaUtil.getColumnIndex(schema, StyleConstants.HALO_COLOR);
labelOffsetXIndex = SchemaUtil.getColumnIndex(schema, StyleConstants.LABEL_OFFSET_X);
labelOffsetYIndex = SchemaUtil.getColumnIndex(schema, StyleConstants.LABEL_OFFSET_Y);
labelRotateIndex = SchemaUtil.getColumnIndex(schema, StyleConstants.LABEL_ROTATE);
labelPointIndex = SchemaUtil.getColumnIndex(schema, StyleConstants.LABEL_POINT);
if (geomIndex < 0) return;
if (labelIndex < 0) return;
Graphics2D graphics = plot.getGraphics();
while (true) {
Row row = ri.next();
if (row == null)
break;
plot(row, graphics);
}
}
private static final String DEFAULT_LABEL_CLR = "000000"; //"80ff80";
private static final String DEFAULT_HALO_CLR = null; //"ff6060";
private Font font;
private void plot(Row row, Graphics2D graphics)
{
Geometry geom = (Geometry) row.getValue(geomIndex);
if (geom == null) return;
// Shape shp = plot.getConverter().toShape(centroid);
boolean hasColor = labelColorIndex >= 0;
// only use default colours if none are specified
String labelClrStr = null;
if (! hasColor) {
labelClrStr = DEFAULT_LABEL_CLR;
}
if (labelColorIndex >= 0)
labelClrStr = (String) row.getValue(labelColorIndex);
int labelSize = RowUtil.getInt(labelSizeIndex, row, 12);
if (font == null) {
font = new Font(FontGlyphReader.FONT_SANSERIF, Font.PLAIN, labelSize);
graphics.setFont(font);
}
int haloSize = RowUtil.getInt(labelHaloSizeIndex, row, 0);
String haloClrStr = RowUtil.getString(labelHaloColorIndex, row, DEFAULT_HALO_CLR);
// if halo clr is specified, make sure halo is visible even if size was omitted
if (haloClrStr != null && labelHaloSizeIndex < 0) {
haloSize = 1;
}
double rotate = RowUtil.getInt(labelRotateIndex, row, 0);
double offsetX = RowUtil.getInt(labelOffsetXIndex, row, 0);
double offsetY = RowUtil.getInt(labelOffsetYIndex, row, 0);
Geometry lblPoint = RowUtil.getGeometry(labelPointIndex, row);
String label = row.getValue(labelIndex).toString();
if (lblPoint == null) {
lblPoint = geom.getCentroid();
}
if (labelClrStr != null && labelClrStr.length() > 0) {
Coordinate centroidPt = lblPoint.getCoordinate();
Coordinate labelPt = new Coordinate(centroidPt.x + offsetX, centroidPt.y + offsetY);
Point2D p = plot.convert(labelPt);
//AffineTransform oldTransform = graphics.getTransform();
AffineTransform trans = AffineTransform.getTranslateInstance(
p.getX(), p.getY());
graphics.setTransform(trans);
if (rotate > 0) {
graphics.rotate(-Math.toRadians(rotate));
}
//System.out.println(label + " " + p);
if (haloSize > 0) {
graphics.setColor(ColorUtil.RGBAtoColor(haloClrStr));
drawHalo(label, haloSize, p, graphics);
}
Color clr = ColorUtil.RGBAtoColor(labelClrStr);
graphics.setColor(clr);
graphics.drawString(label, 0, 0);
//graphics.drawString(label, (int) p.getX() + 5, (int) p.getY());
}
}
private void drawHalo(String label, double haloRadius, Point2D p, Graphics2D graphics)
{
graphics.setStroke(new BasicStroke(2f * (float) haloRadius, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND));
Shape halo = getHaloShape(label, haloRadius, graphics);
graphics.draw(halo);
}
private Shape getHaloShape(String label, double haloRadius, Graphics2D graphics)
{
GlyphVector gv = getTextGlyphVector(label, graphics);
Shape haloShape = new BasicStroke(2f * (float) haloRadius,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND).createStrokedShape(gv
.getOutline());
return haloShape;
}
private GlyphVector getTextGlyphVector(String label, Graphics2D graphics) {
// arabic and hebrew are scripted and right to left, they do require full layout
// whilst western chars are easier to deal with. Find out which case we're dealing with,
// and create the glyph vector with the appropriate call
final char[] chars = label.toCharArray();
final int length = label.length();
GlyphVector textGlyphVector;
if(Bidi.requiresBidi(chars, 0, length) &&
new Bidi(label, Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT).isRightToLeft())
textGlyphVector = font.layoutGlyphVector(graphics.getFontRenderContext(), chars,
0, length, Font.LAYOUT_RIGHT_TO_LEFT);
else
textGlyphVector = font.createGlyphVector(graphics.getFontRenderContext(), chars);
return textGlyphVector;
}
}