blob: 849863a7210fce25f147cf908ea80f487c6c3620 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.stem.loggers.imagewriter.logger.draw;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import javax.imageio.ImageIO;
import org.eclipse.stem.core.graph.DynamicLabel;
import org.eclipse.stem.core.graph.DynamicNodeLabel;
import org.eclipse.stem.core.model.Decorator;
import org.eclipse.stem.loggers.imagewriter.ProjectedMapImageLogger;
import org.eclipse.stem.loggers.imagewriter.logger.PolygonHandler;
import org.eclipse.stem.loggers.imagewriter.logger.PolygonHandler.ProjectedPolygons;
import org.eclipse.stem.loggers.imagewriter.logger.projections.IMapProjection;
import org.eclipse.stem.ui.adapters.color.ColorProviderAdapter;
import org.eclipse.stem.ui.adapters.color.STEMColor;
public class MapDrawer
{
private static final float MAX_ALPHA_VALUE = 0.75f;
private final ProjectedMapImageLogger logger;
private BufferedImage img;
private Graphics2D gc;
private AffineTransform tx;
private ColorProviderAdapter colorProvider;
private Decorator decorator;
private List<ProjectedPolygons> cachedProjectedPolygons = new LinkedList<ProjectedPolygons>();
private IMapProjection projection;
private Rectangle2D mapBounds;
// Image Size Parameters
private int imageWidth;
private int imageHeight;
// Projection parameters
//private boolean useProjectionBounds = false;
//private boolean forceAspectRatio = true;
// Canvas color parameters
private Color defaultBorderColor = Color.yellow;
private Color defaultBackgroundColor = Color.black;
private float borderTransparency = 0.25f;
//private boolean useTransparentBackground = false;
// Simulation color parameters
//private boolean useLogScaling = true;
private float gainFactor = 1.0f;
private AlphaComposite borderCompositeAlpha;
public MapDrawer(Decorator decorator, ProjectedMapImageLogger logger, IMapProjection projection)
{
this.projection = projection;
this.logger = logger;
this.decorator = decorator;
init();
}
private void prepareShapes()
{
for (DynamicLabel label : decorator.getLabelsToUpdate()) {
if (label instanceof DynamicNodeLabel) {
ProjectedPolygons pp = PolygonHandler.getPolygonsForProjection(projection, (DynamicNodeLabel)label);
if (pp != null) {
cachedProjectedPolygons.add(pp);
}
}
}
}
private Rectangle2D getBoundsForProjection()
{
return projection.getBounds();
}
private Rectangle2D getBoundsForPolygons()
{
Rectangle2D bounds = null;
for (ProjectedPolygons pp : cachedProjectedPolygons) {
Rectangle2D shapeBounds = pp.getBoundingBox();
if (shapeBounds != null) {
if (bounds == null) {
bounds = shapeBounds;
} else {
bounds = bounds.createUnion(shapeBounds);
}
}
}
if (bounds == null) {
return getBoundsForProjection();
}
return bounds;
}
private void init()
{
prepareShapes();
// Get the boundaries for the map space
if (logger.isFitToShapeBounds()) {
// Otherwise, we want to fit the bounds to the bounding box of the
// polygons to be drawn
mapBounds = getBoundsForPolygons();
} else {
// If configured to show the "whole earth", then we want to
// get the projection boundaries from the map projection
mapBounds = getBoundsForProjection();
}
imageWidth = logger.getWidth();
imageHeight = logger.getHeight();
// If configured to hold aspect, then re-calculate the image width/height based on the shortest
// edge of the map projection boundaries
if (logger.isForceAspectRatio()) {
double aspect = Math.min(imageWidth / mapBounds.getWidth(), imageHeight / mapBounds.getHeight());
imageWidth = (int)(mapBounds.getWidth() * aspect);
imageHeight = (int)(mapBounds.getHeight() * aspect);
}
// Calculate the transform from map (projected geographic) coordinates to image coordinates
tx = new AffineTransform();
tx.scale(imageWidth / mapBounds.getWidth(), imageHeight / mapBounds.getHeight());
tx.translate(-mapBounds.getX(), -mapBounds.getY());
// Create the buffered image to back the drawing
img = new BufferedImage(imageWidth,imageHeight, BufferedImage.TYPE_INT_ARGB);
gc = img.createGraphics();
// Set the border transparency level
borderCompositeAlpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, borderTransparency);
gainFactor = logger.getGain();
if (gainFactor <= 0f) {
gainFactor = 1.0f;
}
int borderTransparency = logger.getBorderTransparency();
if (borderTransparency >= 0 && borderTransparency <= 1) {
this.borderTransparency = (float)borderTransparency / 100.0f;
}
reset();
}
public void setColorProvider(ColorProviderAdapter cp)
{
this.colorProvider = cp;
}
public void reset()
{
gc.setBackground(Color.white);
gc.clearRect(0,0, img.getWidth(), img.getHeight());
// gc.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
// gc.setBackground(Color.black);
// gc.setPaint(Color.black);
// gc.fillRect(0, 0, imageWidth,imageHeight);
}
public void save(File imagePath, String imageFormat)
{
try {
ImageIO.write(img, imageFormat, imagePath);
} catch (IOException e) {
e.printStackTrace();
}
}
public void close()
{
img.flush();
gc.dispose();
gc = null;
img = null;
}
private void drawBackground()
{
if (logger.isTransparentBackground()) {
return;
}
Color backgroundColor;
STEMColor backgroundColorStem = colorProvider.getBackgroundFillColor();
if (backgroundColorStem != null) {
backgroundColor = backgroundColorStem.toAWTColor();
} else {
backgroundColor = defaultBackgroundColor;
}
gc.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
gc.setBackground(backgroundColor);
gc.setPaint(backgroundColor);
gc.fillRect(0, 0, imageWidth,imageHeight);
}
private Color getBorderColor()
{
Color borderColor;
STEMColor borderColorStem = colorProvider.getBorderColor();
if (borderColorStem != null) {
borderColor = borderColorStem.toAWTColor();
} else {
borderColor = defaultBorderColor;
}
return borderColor;
}
public void draw()
{
reset();
Composite heldComposite = gc.getComposite();
drawBackground();
Color borderColor = getBorderColor();
for (ProjectedPolygons node : cachedProjectedPolygons) {
for (Shape polygon : node.polygons) {
Shape projectedShape = tx.createTransformedShape(polygon);
colorProvider.setTarget(node.identifiable);
STEMColor color = colorProvider.getColor(gainFactor, logger.isLogScaling());
gc.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, color.getAlpha() * MAX_ALPHA_VALUE));
gc.setColor(color.toAWTColor());
gc.fill(projectedShape);
gc.setComposite(borderCompositeAlpha);
gc.setColor(borderColor);
gc.draw(projectedShape);
}
}
gc.setComposite(heldComposite);
}
}