LegendElement.java
/*
* Copyright (c) 2020 Martin Davis.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
*
* http://www.eclipse.org/org/documents/edl-v10.php.
*/
package org.locationtech.jtstest.testbuilder.ui.render;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.geom.Line2D;
import java.util.List;
import org.locationtech.jts.awt.FontGlyphReader;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jtstest.testbuilder.geom.GeometryUtil;
import org.locationtech.jtstest.testbuilder.model.Layer;
import org.locationtech.jtstest.testbuilder.ui.Viewport;
public class LegendElement {
private static final Color NAME_CLR = Color.BLACK;
private static final int BOX_OFFSET = 4;
private static final int BOX_MARGIN = 8;
private static final int SWATCH_SIZE = 10;
private static final int SWATCH_MARGIN = 6;
private static final int DEFAULT_FONT_SIZE = 12;
private static final int STAT_FONT_SIZE = 10;
private static final int DESC_INDENT = 6;
private Viewport viewport;
private Font font = new Font(FontGlyphReader.FONT_SANSERIF, Font.BOLD, DEFAULT_FONT_SIZE);
private Font fontDesc = new Font(FontGlyphReader.FONT_SANSERIF, Font.ITALIC, STAT_FONT_SIZE);
private int borderSize = 1;
private boolean isBorderEnabled;
private boolean isStatsEnabled = false;
private boolean isMetricsEnabled = false;
private Color borderColor;
private Color fillClr = Color.WHITE;
public LegendElement(Viewport viewport) {
this.viewport = viewport;
}
public void setBorderEnabled(boolean isBorderEnabled) {
this.isBorderEnabled = isBorderEnabled;
}
public void setStatsEnabled(boolean isEnabled) {
this.isStatsEnabled = isEnabled;
}
public void setMetricsEnabled(boolean isEnabled) {
this.isMetricsEnabled = isEnabled;
}
public void setBorder(int borderSize) {
this.borderSize = borderSize;
}
public void setBorderColor(Color clr) {
borderColor = clr;
}
public void setFill(Color clr) {
this.fillClr = clr;
}
public void paint(List<Layer> layerList, Graphics2D g) {
if (layerList.size() <= 0) return;
g.setFont(font);
Rectangle box = computeBox(layerList, g);
drawBox(box, g);
drawEntries(layerList, box, g);
}
private boolean hasDesc() {
return isStatsEnabled || isMetricsEnabled;
}
private int lineHeight() {
return DEFAULT_FONT_SIZE + 6 + (hasDesc() ? DEFAULT_FONT_SIZE : 0 );
}
private void drawEntries(List<Layer> layerList, Rectangle box, Graphics2D g) {
g.setFont(font);
int nameX = box.x + BOX_MARGIN + SWATCH_SIZE + SWATCH_MARGIN;
// have to account for width of border
int topY = box.y + BOX_MARGIN + borderSize;
int n = layerList.size();
for (int i = 0; i < n; i++) {
// draw layer name
int entryTopY = topY + i * lineHeight();
drawEntry(layerList.get(i), nameX, entryTopY, g);
}
}
private void drawEntry(Layer layer, int nameX, int topY, Graphics2D g) {
g.setPaint(NAME_CLR);
g.setFont(font);
g.drawString(getName(layer), nameX, topY + DEFAULT_FONT_SIZE );
if (hasDesc()) {
g.setFont(fontDesc);
g.drawString(getDescription(layer), nameX + DESC_INDENT, topY + DEFAULT_FONT_SIZE + STAT_FONT_SIZE + 3);
}
int swatchX = nameX - SWATCH_SIZE - SWATCH_MARGIN;
int swatchY = topY + 2;
drawSwatch(layer, swatchX, swatchY, g);
}
private String getDescription(Layer layer) {
String desc = "";
if (isStatsEnabled) {
desc += GeometryUtil.structureSummary(layer.getGeometry());
}
if (isMetricsEnabled) {
if (desc.length() > 0) desc += " / ";
desc += GeometryUtil.metricsSummary(layer.getGeometry());
}
return desc;
}
private String getName(Layer layer) {
return layer.getName();
}
private void drawSwatch(Layer layer, int x, int y, Graphics2D g) {
Geometry geom = layer.getGeometry();
switch (geom.getDimension()) {
case 2:
drawSwatchBox(layer, x, y, g);
break;
case 1:
drawSwatchLine(layer, x, y, g);
break;
case 0:
drawSwatchPoint(layer, x, y, g);
break;
}
}
private void drawSwatchBox(Layer layer, int x, int y, Graphics2D g) {
Rectangle box = new Rectangle(
x, y,
SWATCH_SIZE, SWATCH_SIZE);
//--- paint Fill
Color fillClr = Color.WHITE;
if (layer.getGeometryStyle().isFilled())
fillClr = layer.getGeometryStyle().getFillColor();
g.setPaint(fillClr);
g.fill(box);
//--- paint Line
if (layer.getGeometryStyle().isStroked()) {
float lineWidth = layer.getGeometryStyle().getStrokeWidth();
if (layer.getGeometryStyle().getStrokeWidth() > 3)
lineWidth = 3;
Stroke strokeBox = new BasicStroke(lineWidth, // Width of stroke
BasicStroke.CAP_BUTT, // End cap style
BasicStroke.JOIN_MITER, // Join style
10, // Miter limit
null, // Dash pattern
0); // Dash phase
g.setStroke(strokeBox);
Color lineClr = layer.getGeometryStyle().getLineColor();
g.setPaint(lineClr);
g.draw(box);
}
}
private void drawSwatchLine(Layer layer, int x, int y, Graphics2D g) {
Line2D line = new Line2D.Float(
x, y + SWATCH_SIZE,
x + SWATCH_SIZE, y );
//--- paint Line
float lineWidth = layer.getGeometryStyle().getStrokeWidth();
if (layer.getGeometryStyle().getStrokeWidth() > 3)
lineWidth = 3;
Stroke strokeBox = new BasicStroke(lineWidth, // Width of stroke
BasicStroke.CAP_BUTT, // End cap style
BasicStroke.JOIN_MITER, // Join style
10, // Miter limit
null, // Dash pattern
0); // Dash phase
g.setStroke(strokeBox);
Color lineClr = layer.getGeometryStyle().getLineColor();
g.setPaint(lineClr);
g.draw(line);
}
private void drawSwatchPoint(Layer layer, int x, int y, Graphics2D g) {
int size = layer.getLayerStyle().getVertexSize();
if (size > SWATCH_SIZE) size = SWATCH_SIZE;
int margin = (SWATCH_SIZE - size) / 2;
Rectangle box = new Rectangle(
x + margin, y + margin,
size, size);
Color clr = layer.getLayerStyle().getVertexColor();
g.setPaint(clr);
g.fill(box);
}
private void drawBox(Rectangle box, Graphics2D g) {
g.setPaint(fillClr);
g.fill(box);
if (isBorderEnabled && borderSize > 0) {
Stroke strokeBox = new BasicStroke(borderSize, // Width of stroke
BasicStroke.CAP_BUTT, // End cap style
BasicStroke.JOIN_MITER, // Join style
10, // Miter limit
null, // Dash pattern
0); // Dash phase
g.setStroke(strokeBox);
g.setPaint(borderColor);
g.draw(box);
}
}
private Rectangle computeBox(List<Layer> layerList, Graphics2D g) {
int width = entryWidth(layerList, g) + 2 * BOX_MARGIN + SWATCH_SIZE + SWATCH_MARGIN;
int height = layerList.size() * lineHeight() + 2 * BOX_MARGIN;
int viewHeight = (int) viewport.getHeightInView();
int viewWidth = (int) viewport.getWidthInView();
Rectangle box = new Rectangle(
viewWidth - BOX_OFFSET - width,
viewHeight - BOX_OFFSET - height,
width, height);
return box;
}
private int entryWidth(List<Layer> layerList, Graphics2D g2) {
int width = 0;
for (Layer layer : layerList) {
String s = getName(layer);
int nameWidth = (int) g2.getFontMetrics().getStringBounds(s, g2).getWidth();
if (nameWidth > width)
width = nameWidth;
if (hasDesc()) {
String s2 = getDescription(layer);
int statWidth = DESC_INDENT + (int) fontDesc.getStringBounds(s2, g2.getFontRenderContext()).getWidth();
if (statWidth > width)
width = statWidth;
}
}
return width;
}
}