/* * This file is part of the GeoLatte project. * * GeoLatte 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, either version 3 of the License, or * (at your option) any later version. * * GeoLatte 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. * * You should have received a copy of the GNU Lesser General Public License * along with GeoLatte. If not, see <http://www.gnu.org/licenses/>. * * Copyright (C) 2010 - 2011 and Ownership of code is shared by: * Qmino bvba - Esperantolaan 4 - 3001 Heverlee (http://www.qmino.com) * Geovise bvba - Generaal Eisenhowerlei 9 - 2140 Antwerpen (http://www.geovise.com) */ package org.geolatte.maprenderer.java2D; import org.geolatte.geom.Envelope; import org.geolatte.geom.crs.CrsId; import org.geolatte.maprenderer.map.MapGraphics; import java.awt.*; import java.awt.color.ColorSpace; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.awt.geom.AffineTransform; import java.awt.image.*; import java.awt.image.renderable.RenderableImage; import java.text.AttributedCharacterIterator; import java.util.Map; public class JAIMapGraphics extends MapGraphics { private static final boolean OPTIMIZE_FOR_QUALITY = true; private final static Color DEFAULT_BACKGROUND_COLOR = new Color(1.0f, 1.0f, 1.0f, 0.0f); private double mapUnitsPerPixel; private final int width; private final int height; private Graphics2D g2; private final CrsId spatialReference; private final BufferedImage image; private static ColorModel makeColorModel(boolean transparency) { if (transparency) { ColorSpace space = ColorSpace.getInstance(ColorSpace.CS_sRGB); return new DirectColorModel(space, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000, true, DataBuffer.TYPE_INT); } else { ColorSpace space = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB); return new DirectColorModel(space, 24, 0xff0000, 0x00ff00, 0x000ff, 0, false, DataBuffer.TYPE_INT); } } public JAIMapGraphics(Dimension dimension, Envelope extent, ColorModel colorModel) { this.spatialReference = extent.getCrsId(); this.width = (int) dimension.getWidth(); this.height = (int) dimension.getHeight(); WritableRaster raster = colorModel.createCompatibleWritableRaster(this.width, this.height); this.image = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null); initGraphics(); setToExtent(extent); } public JAIMapGraphics(Dimension dimension, Envelope extent, boolean transparency) { this(dimension, extent, makeColorModel(transparency)); } public JAIMapGraphics(Dimension dimension, Envelope extent) { this(dimension, extent, true); } public void initGraphics() { this.g2 = (Graphics2D) this.image.getGraphics(); setBackground(DEFAULT_BACKGROUND_COLOR); setComposite(AlphaComposite.SrcOver); this.clearRect(0, 0, this.width, this.height); RenderingHints hints = getDefaultHints(); setRenderingHints(hints); } protected RenderingHints getDefaultHints() { RenderingHints hints = new RenderingHints(null); if (OPTIMIZE_FOR_QUALITY) { getDefaultHintsForQuality(hints); } else { getDefaultHintsForSpeed(hints); } return hints; } private RenderingHints getDefaultHintsForSpeed(RenderingHints hints) { hints.put(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED); hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); hints.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED); hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE); hints.put(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF); hints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); return hints; } private RenderingHints getDefaultHintsForQuality(RenderingHints hints) { hints.put(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); hints.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED); hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); //hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); hints.put(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); hints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); return hints; } private void setToExtent(Envelope extent) { if (extent.getCrsId().getCode() != getSpatialReference().getCode()) throw new IllegalArgumentException("Spatial Reference of extent object must be EPSG: " + getSpatialReference().getCode()); AffineTransform atf = new AffineTransform(); double sx = width / extent.getWidth(); double sy = height / extent.getHeight(); this.mapUnitsPerPixel = Math.max(1 / sx, 1 / sy); //we don't maintain aspect-ratio here! atf.scale(sx, -sy); atf.translate(-extent.getMinX(), -extent.getMaxY()); setTransform(atf); } @Override public Dimension getDimension() { return new Dimension(width, height); } @Override public CrsId getSpatialReference() { return this.spatialReference; } @Override public double getMapUnitsPerPixel() { return this.mapUnitsPerPixel; } @Override public RenderedImage createRendering() { return this.image; } public Graphics create() { return g2.create(); } public Color getColor() { return g2.getColor(); } public void setColor(Color c) { g2.setColor(c); } public void setPaintMode() { g2.setPaintMode(); } public void setXORMode(Color c1) { g2.setXORMode(c1); } public Font getFont() { return g2.getFont(); } public void setFont(Font font) { g2.setFont(font); } public FontMetrics getFontMetrics(Font f) { return g2.getFontMetrics(f); } public Rectangle getClipBounds() { return g2.getClipBounds(); } public void clipRect(int x, int y, int width, int height) { g2.clipRect(x, y, width, height); } public void setClip(int x, int y, int width, int height) { g2.setClip(x, y, width, height); } public Shape getClip() { return g2.getClip(); } public void setClip(Shape clip) { g2.setClip(clip); } public void copyArea(int x, int y, int width, int height, int dx, int dy) { g2.copyArea(x, y, width, height, dx, dy); } public void drawLine(int x1, int y1, int x2, int y2) { g2.drawLine(x1, y1, x2, y2); } public void fillRect(int x, int y, int width, int height) { g2.fillRect(x, y, width, height); } public void clearRect(int x, int y, int width, int height) { g2.clearRect(x, y, width, height); } public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { g2.drawRoundRect(x, y, width, height, arcWidth, arcHeight); } public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { g2.fillRoundRect(x, y, width, height, arcWidth, arcHeight); } public void draw3DRect(int x, int y, int width, int height, boolean raised) { g2.draw3DRect(x, y, width, height, raised); } public void fill3DRect(int x, int y, int width, int height, boolean raised) { g2.fill3DRect(x, y, width, height, raised); } public void drawOval(int x, int y, int width, int height) { g2.drawOval(x, y, width, height); } public void fillOval(int x, int y, int width, int height) { g2.fillOval(x, y, width, height); } public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { g2.drawArc(x, y, width, height, startAngle, arcAngle); } public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) { g2.fillArc(x, y, width, height, startAngle, arcAngle); } public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) { g2.drawPolyline(xPoints, yPoints, nPoints); } public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) { g2.drawPolygon(xPoints, yPoints, nPoints); } public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) { g2.fillPolygon(xPoints, yPoints, nPoints); } public void drawString(String str, int x, int y) { g2.drawString(str, x, y); } public boolean drawImage(Image img, int x, int y, ImageObserver observer) { return g2.drawImage(img, x, y, observer); } public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { return g2.drawImage(img, x, y, width, height, observer); } public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) { return g2.drawImage(img, x, y, bgcolor, observer); } public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) { return g2.drawImage(img, x, y, width, height, bgcolor, observer); } public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) { return g2.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer); } public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer) { return g2.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer); } public void dispose() { g2.dispose(); } public void draw(Shape s) { g2.draw(s); } public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { return g2.drawImage(img, xform, obs); } public void drawRenderedImage(RenderedImage img, AffineTransform xform) { g2.drawRenderedImage(img, xform); } public void drawRenderableImage(RenderableImage img, AffineTransform xform) { g2.drawRenderableImage(img, xform); } public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) { g2.drawImage(img, op, x, y); } public void drawString(String s, float x, float y) { g2.drawString(s, x, y); } public void drawString(AttributedCharacterIterator iterator, int x, int y) { g2.drawString(iterator, x, y); } public void drawString(AttributedCharacterIterator iterator, float x, float y) { g2.drawString(iterator, x, y); } public void drawGlyphVector(GlyphVector v, float x, float y) { g2.drawGlyphVector(v, x, y); } public void fill(Shape s) { g2.fill(s); } public boolean hit(Rectangle rect, Shape s, boolean onStroke) { return g2.hit(rect, s, onStroke); } public GraphicsConfiguration getDeviceConfiguration() { return g2.getDeviceConfiguration(); } public FontRenderContext getFontRenderContext() { return g2.getFontRenderContext(); } public void setComposite(Composite comp) { g2.setComposite(comp); } public void setPaint(Paint paint) { g2.setPaint(paint); } public void setStroke(Stroke s) { Stroke scaledStroke = scaleStroke(s); g2.setStroke(scaledStroke); } private Stroke scaleStroke(Stroke s) { if (s instanceof PerpendicularOffsetStroke) { PerpendicularOffsetStroke orig = (PerpendicularOffsetStroke) s; float origOffset = orig.getPerpendicularOffset(); float scaledOffset = (float) (origOffset* getMapUnitsPerPixel()); return new PerpendicularOffsetStroke((float) (orig.getLineWidth() * getMapUnitsPerPixel()), scaledOffset, orig.getLineJoin(), orig.getEndCap(), scaleArray(orig.getDashArray()), (float) (orig.getDashPhase() * getMapUnitsPerPixel())); } if (s instanceof BasicStroke) { BasicStroke orig = (BasicStroke) s; return new BasicStroke((float) (orig.getLineWidth() * getMapUnitsPerPixel()), orig.getEndCap(), orig.getLineJoin(), orig.getMiterLimit(), scaleArray(orig.getDashArray()), (float) (orig.getDashPhase() * getMapUnitsPerPixel()) ); } throw new IllegalArgumentException("Can't scale stroke."); } private float[] scaleArray(float[] dashArray) { if (dashArray == null) return null; float[] result = new float[dashArray.length]; for (int i = 0; i < result.length; i++) { result[i] = (float) (dashArray[i] * mapUnitsPerPixel); } return result; } public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) { g2.setRenderingHint(hintKey, hintValue); } public Object getRenderingHint(RenderingHints.Key hintKey) { return g2.getRenderingHint(hintKey); } public RenderingHints getRenderingHints() { return g2.getRenderingHints(); } public void translate(int x, int y) { g2.translate(x, y); } public void translate(double x, double v) { g2.translate(x, v); } public void rotate(double theta) { g2.rotate(theta); } public void rotate(double theta, double v, double x) { g2.rotate(theta, v, x); } public void scale(double sx, double v) { g2.scale(sx, v); } public void shear(double shx, double v) { g2.shear(shx, v); } public void transform(AffineTransform Tx) { g2.transform(Tx); } public void setTransform(AffineTransform Tx) { g2.setTransform(Tx); } public AffineTransform getTransform() { return g2.getTransform(); } public Paint getPaint() { return g2.getPaint(); } public Composite getComposite() { return g2.getComposite(); } public void setBackground(Color color) { g2.setBackground(color); } public Color getBackground() { return g2.getBackground(); } public Stroke getStroke() { return g2.getStroke(); } public void clip(Shape s) { g2.clip(s); } public void setRenderingHints(Map<?, ?> hints) { g2.setRenderingHints(hints); } public void addRenderingHints(Map<?, ?> hints) { g2.addRenderingHints(hints); } public Graphics create(int x, int y, int width, int height) { return g2.create(x, y, width, height); } public FontMetrics getFontMetrics() { return g2.getFontMetrics(); } public void drawRect(int x, int y, int width, int height) { g2.drawRect(x, y, width, height); } public void drawPolygon(Polygon p) { g2.drawPolygon(p); } public void fillPolygon(Polygon p) { g2.fillPolygon(p); } public void drawChars(char[] data, int offset, int length, int x, int y) { g2.drawChars(data, offset, length, x, y); } public void drawBytes(byte[] data, int offset, int length, int x, int y) { g2.drawBytes(data, offset, length, x, y); } public void finalize() { g2.finalize(); } public String toString() { return g2.toString(); } @Deprecated public Rectangle getClipRect() { return g2.getClipRect(); } public boolean hitClip(int x, int y, int width, int height) { return g2.hitClip(x, y, width, height); } public Rectangle getClipBounds(Rectangle r) { return g2.getClipBounds(r); } }