package org.mapfish.print.processor.map.scalebar;
import org.mapfish.print.attribute.ScalebarAttribute.ScalebarAttributeValues;
import java.awt.BasicStroke;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
/**
* Base class for drawing a scale bar.
*/
public abstract class ScalebarDrawer {
/**
* The graphics context.
*/
private final Graphics2D graphics2d;
/**
* Parameters for rendering the scalebar.
*/
private final ScaleBarRenderSettings settings;
/**
* Parameters for the scalebar.
*/
private final ScalebarAttributeValues params;
/**
* Constructor.
* @param graphics2d The graphics context.
* @param settings Parameters for rendering the scalebar.
*/
public ScalebarDrawer(final Graphics2D graphics2d, final ScaleBarRenderSettings settings) {
this.graphics2d = graphics2d;
this.settings = settings;
this.params = settings.getParams();
}
/**
* Start the rendering of the scalebar.
*/
public final void draw() {
final AffineTransform transform = getAlignmentTransform();
// draw the background box
this.graphics2d.setTransform(transform);
this.graphics2d.setColor(this.params.getBackgroundColor());
this.graphics2d.fillRect(0, 0, this.settings.getSize().width, this.settings.getSize().height);
//draw the labels
this.graphics2d.setColor(this.params.getFontColor());
drawLabels(transform, this.params.getOrientation(), this.params.getLabelRotation());
//sets the transformation for drawing the bar and do it
final AffineTransform lineTransform = new AffineTransform(transform);
setLineTranslate(lineTransform);
if (this.params.getOrientation() == Orientation.VERTICAL_LABELS_LEFT ||
this.params.getOrientation() == Orientation.VERTICAL_LABELS_RIGHT) {
final AffineTransform rotate = AffineTransform.getQuadrantRotateInstance(1);
lineTransform.concatenate(rotate);
}
this.graphics2d.setTransform(lineTransform);
this.graphics2d.setStroke(new BasicStroke(this.settings.getLineWidth()));
this.graphics2d.setColor(this.params.getColor());
drawBar();
}
/**
* Create a transformation which takes the alignment settings into account.
*/
private AffineTransform getAlignmentTransform() {
final int offsetX;
switch (this.settings.getParams().getAlign()) {
case LEFT:
offsetX = 0;
break;
case RIGHT:
offsetX = this.settings.getMaxSize().width - this.settings.getSize().width;
break;
case CENTER:
default:
offsetX = (int) Math.floor(this.settings.getMaxSize().width / 2.0 - this.settings.getSize().width / 2.0);
break;
}
final int offsetY;
switch (this.settings.getParams().getVerticalAlign()) {
case TOP:
offsetY = 0;
break;
case BOTTOM:
offsetY = this.settings.getMaxSize().height - this.settings.getSize().height;
break;
case MIDDLE:
default:
offsetY = (int) Math.floor(this.settings.getMaxSize().height / 2.0 - this.settings.getSize().height / 2.0);
break;
}
return AffineTransform.getTranslateInstance(Math.round(offsetX), Math.round(offsetY));
}
private void setLineTranslate(final AffineTransform lineTransform) {
final float x;
final float y;
if (this.params.getOrientation() == Orientation.HORIZONTAL_LABELS_BELOW) {
x = this.settings.getPadding() + this.settings.getLeftLabelMargin();
y = this.settings.getPadding() + this.settings.getBarSize();
} else if (this.params.getOrientation() == Orientation.HORIZONTAL_LABELS_ABOVE) {
x = this.settings.getPadding() + this.settings.getLeftLabelMargin();
y = this.settings.getPadding() + this.settings.getBarSize() + this.settings.getLabelDistance() +
Label.getRotatedHeight(this.settings.getMaxLabelSize(), this.params.getLabelRotation());
} else if (this.params.getOrientation() == Orientation.VERTICAL_LABELS_LEFT) {
x = this.settings.getPadding() +
Label.getRotatedWidth(this.settings.getMaxLabelSize(), this.params.getLabelRotation()) +
this.settings.getLabelDistance();
y = this.settings.getPadding() + this.settings.getTopLabelMargin();
} else { // if (this.params.getOrientation() == Orientation.VERTICAL_LABELS_RIGHT)
x = this.settings.getPadding();
y = this.settings.getPadding() + this.settings.getTopLabelMargin();
}
lineTransform.translate(Math.round(x), Math.round(y));
}
/**
* Sets 0,0 in the middle of the first tick mark at labelDistance away from it.
*/
private void setLabelTranslate(final AffineTransform labelTransform) {
final float x;
final float y;
if (this.params.getOrientation() == Orientation.HORIZONTAL_LABELS_BELOW) {
x = this.settings.getPadding() + this.settings.getLeftLabelMargin();
y = this.settings.getPadding() + this.settings.getBarSize() + this.settings.getLabelDistance();
} else if (this.params.getOrientation() == Orientation.HORIZONTAL_LABELS_ABOVE) {
x = this.settings.getPadding() + this.settings.getLeftLabelMargin();
y = this.settings.getPadding() +
Label.getRotatedHeight(this.settings.getMaxLabelSize(), this.params.getLabelRotation());
} else if (this.params.getOrientation() == Orientation.VERTICAL_LABELS_LEFT) {
x = this.settings.getPadding() +
Label.getRotatedWidth(this.settings.getMaxLabelSize(), this.params.getLabelRotation());
y = this.settings.getPadding() + this.settings.getTopLabelMargin();
} else { //if (this.params.getOrientation() == Orientation.VERTICAL_LABELS_RIGHT)
x = this.settings.getPadding() + this.settings.getBarSize() + this.settings.getLabelDistance();
y = this.settings.getPadding() + this.settings.getTopLabelMargin();
}
labelTransform.translate(Math.round(x), Math.round(y));
}
/**
* Draws the bar itself. The transformation is setup in a manner where the
* bar should be drawn into the rectangle (0, 0) (intervals*intervalWidth, -barSize).
*/
protected abstract void drawBar();
private void drawLabels(final AffineTransform transform, final Orientation orientation,
final float labelRotation) {
float prevMargin = orientation.isHorizontal() ? this.settings.getMaxSize().width :
this.settings.getMaxSize().height;
final AffineTransform firstTickTransform = new AffineTransform(transform);
setLabelTranslate(firstTickTransform); // 0,0 is the center of the first label
for (int i = this.settings.getLabels().size() - 1; i >= 0; i--) {
final Label label = this.settings.getLabels().get(i);
final float newMargin;
final boolean shouldSkipLabel;
final AffineTransform centerTransform = new AffineTransform(firstTickTransform);
final float halfRotatedWidth = label.getRotatedWidth(labelRotation) / 2.0f;
final float halfRotatedHeight = label.getRotatedHeight(labelRotation) / 2.0f;
if (orientation.isHorizontal()) {
centerTransform.concatenate(AffineTransform.getTranslateInstance(
Math.round(label.getGraphicOffset()),
Math.round(orientation == Orientation.HORIZONTAL_LABELS_BELOW ?
halfRotatedHeight : -halfRotatedHeight)));
shouldSkipLabel = label.getGraphicOffset() + halfRotatedWidth > prevMargin - 1;
newMargin = label.getGraphicOffset() - halfRotatedWidth;
} else {
centerTransform.concatenate(AffineTransform.getTranslateInstance(
Math.round(orientation == Orientation.VERTICAL_LABELS_RIGHT ?
halfRotatedWidth : -halfRotatedWidth),
Math.round(label.getGraphicOffset())));
shouldSkipLabel = label.getGraphicOffset() + halfRotatedHeight > prevMargin - 1;
newMargin = label.getGraphicOffset() - halfRotatedHeight;
}
if (labelRotation != 0.0) {
centerTransform.concatenate(AffineTransform.getRotateInstance(Math.toRadians(labelRotation)));
}
if (!shouldSkipLabel) {
this.graphics2d.setTransform(centerTransform);
//for some reason, we need to floor the coordinates for the text to be nicely centered
label.getLabelLayout().draw(this.graphics2d, (float) Math.floor(-label.getWidth() / 2.0f),
(float) Math.floor(label.getHeight() / 2.0f));
prevMargin = newMargin;
} else {
//the label would be written over the previous one => ignore it
}
}
}
public final Graphics2D getGraphics2d() {
return this.graphics2d;
}
public final ScaleBarRenderSettings getSettings() {
return this.settings;
}
public final ScalebarAttributeValues getParams() {
return this.params;
}
}