Public
Snippet $6682 authored by Ian Turton

TextGraphicFactory

TextGraphicFactory.java
package com.ianturton.cookbook.styling;

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.util.logging.Logger;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.WindowConstants;

import org.geotools.factory.CommonFactoryFinder;
import org.geotools.renderer.style.ExternalGraphicFactory;
import org.geotools.renderer.style.FontCache;
import org.geotools.renderer.style.shape.ExplicitBoundsShape;
import org.geotools.util.SoftValueHashMap;
import org.opengis.feature.Feature;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.expression.Expression;

public class TextGraphicFactory implements ExternalGraphicFactory {
    /** The logger for the rendering module. */
    private static final Logger LOGGER = org.geotools.util.logging.Logging
	    .getLogger("org.geotools.styling.TextGraphicFactory");
    /** cache icons to save time */
    static Map<String, ShapeIcon> imageCache = Collections
	    .synchronizedMap(new SoftValueHashMap<String, ShapeIcon>());
    private static final String TEXT_PREFIX = "http://";
    private static final float SIZE = 20;
    private static final Font DEFAULT_FONT = new Font("Sans", Font.PLAIN,
	    (int) SIZE);

    private static final String TEXT_FORMAT = "image/text";

    private static FontRenderContext FONT_RENDER_CONTEXT = new FontRenderContext(
	    new AffineTransform(), false, false);

    private float fontSize = SIZE;

    @Override
    public Icon getIcon(Feature feature, Expression url, String format, int size)
	    throws Exception {
	if (format.equalsIgnoreCase(TEXT_FORMAT)) {
	    System.out.println("Trying to evaluate " + url);
	    String text = url.evaluate(feature, String.class);
	    String cacheurl = text;
	    if (!text.startsWith(TEXT_PREFIX)) {
		LOGGER.fine("failed to match " + url + " with expected "
			+ TEXT_PREFIX);
		return null;
	    }
	    if (imageCache.containsKey(text)) {
		return imageCache.get(text);
	    }
	    text = text.substring(TEXT_PREFIX.length());
	    Font font = DEFAULT_FONT;
	    Color background = null;
	    Color fill = null;
	    if (text.contains("#")) {
		String[] fontElements = text.split("#");
		int length = fontElements.length;
		text = fontElements[length - 1];

		for (int i = 0; i < length - 1; i++) {
		    System.out.println(fontElements[i]);
		    if (fontElements[i].startsWith("bg=")) {
			background = Color.decode("#"
				+ fontElements[i].substring(3));
			System.out.println("set bg " + background.getRGB());
		    } else if (fontElements[i].startsWith("fg=")) {
			fill = Color.decode("#" + fontElements[i].substring(3));
			System.out.println("set fg " + fill.getRGB());
		    } else if (fontElements[i].startsWith("size=")) {
			fontSize = Float.parseFloat(fontElements[i]
				.substring(5));
		    } else {
			// look up the font
			font = FontCache.getDefaultInstance().getFont(
				fontElements[0]);
			if (font == null) {
			    LOGGER.info("Could not find " + fontElements[0]);
			    font = DEFAULT_FONT;
			}

		    }
		}

		font = font.deriveFont(fontSize);
	    }

	    TextLayout textTl = new TextLayout(text, font, FONT_RENDER_CONTEXT);

	    Shape s = textTl.getOutline(null);
	    Rectangle bounds = s.getBounds();

	    ShapeIcon ret = new ShapeIcon(s, bounds, size, background, fill);
	    imageCache.put(cacheurl, ret);
	    return ret;
	}
	// else we can't process this URL
	return null;
    }

    public class ShapeIcon implements Icon {
	private final Shape shape;
	private int width;
	private int height;
	private Rectangle bounds;
	private Color fill = Color.black;

	private Color background = null;

	public ShapeIcon(Shape s, Rectangle b, int size, Color bg, Color fill) {
	    this.shape = s;
	    this.bounds = b;
	    this.background = bg;
	    if (fill != null) {
		this.fill = fill;
	    }

	    double targetWidth = bounds.getWidth();
	    double targetHeight = bounds.getHeight();

	    if (size > 0) {
		double scaleY = ((double) size) / targetHeight;
		double scaleX = scaleY;

		targetWidth = scaleX * targetWidth;
		targetHeight = scaleY * targetHeight;

	    }
	    this.width = (int) Math.ceil(targetWidth);
	    this.height = (int) Math.ceil(targetHeight);

	}

	@Override
	public void paintIcon(Component c, Graphics g, int x, int y) {
	    Graphics2D g2d = (Graphics2D) g;
	    RenderingHints oldHints = g2d.getRenderingHints();

	    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
		    RenderingHints.VALUE_ANTIALIAS_OFF);
	    // saves the old transform;
	    AffineTransform oldTransform = g2d.getTransform();
	    Color oldFill = g2d.getColor();
	    try {
		if (oldTransform == null)
		    oldTransform = new AffineTransform();

		AffineTransform transform = new AffineTransform(oldTransform);
		transform.translate(x - bounds.x, y - bounds.y);
		transform.scale(getIconWidth() / bounds.getWidth(),
			getIconHeight() / bounds.getHeight());

		g2d.setTransform(transform);

		if (background != null) {
		    g2d.setColor(background);

		    g2d.fill(bounds);
		    // g2d.setColor(Color.red);
		    g2d.draw(bounds);
		    g2d.drawRect(x - 1, y - 1, getIconWidth() + 2,
			    -getIconHeight() - 2);
		}
		g2d.setColor(fill);
		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
			RenderingHints.VALUE_ANTIALIAS_ON);
		g2d.fill(shape);

	    } finally {
		g2d.setTransform(oldTransform);
		g2d.setColor(oldFill);
		g2d.setRenderingHints(oldHints);
	    }

	}

	@Override
	public int getIconHeight() {
	    return height;
	}

	@Override
	public int getIconWidth() {
	    return width;
	}

    }

    public static void main(String[] args) throws Exception {
	FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
	TextGraphicFactory tgf = new TextGraphicFactory();
	Expression url = ff.literal("http://Pennsylvania");
	Icon icon = tgf.getIcon(null, url, "image/text", 0);
	Expression url2 = ff.literal("http://Arial#Texas");
	Icon icon2 = tgf.getIcon(null, url2, "image/text", 0);

	Expression url3 = ff.literal("http://Arial#fg=FFFFFF#Texas");
	Icon icon3 = tgf.getIcon(null, url3, "image/text", 0);
	Expression url4 = ff.literal("http://Arial#fg=FF0000#bg=00FF00#Texas");
	Icon icon4 = tgf.getIcon(null, url4, "image/text", 0);
	Expression url5 = ff
		.literal("http://Arial#fg=FF0000#bg=00FF00#size=10#Texas");
	Icon icon5 = tgf.getIcon(null, url5, "image/text", 0);
	Icon icon6 = tgf.getIcon(null, url5, "image/text", 50);
	BufferedImage image = new BufferedImage(300, 300,
		BufferedImage.TYPE_4BYTE_ABGR);
	Graphics2D g2d = (Graphics2D) image.getGraphics();
	g2d.setColor(Color.BLACK);

	g2d.translate(10, 100);
	g2d.setColor(Color.BLUE);
	icon.paintIcon(null, g2d, 0, 0);
	int h = icon.getIconHeight();
	int w = icon.getIconWidth();
	g2d.drawRect(0, 0, w, h);

	icon2.paintIcon(null, g2d, 150, 0);
	int h2 = icon2.getIconHeight();
	int w2 = icon2.getIconWidth();

	g2d.drawRect(150, 0, w2, h2);

	icon3.paintIcon(null, g2d, 0, 30);
	icon4.paintIcon(null, g2d, 70, 30);

	icon5.paintIcon(null, g2d, 0, 70);
	icon6.paintIcon(null, g2d, 70, 170);

	g2d.dispose();
	JFrame frame = new JFrame("Test");
	frame.setContentPane(new JLabel(new ImageIcon(image)));
	frame.pack();
	frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
	frame.setVisible(true);
    }
}