Skip to content
Snippets Groups Projects

Resolve "automatic scale generator for data overlays"

Merged Piotr Gawron requested to merge 205-automatic-scale-generator-for-data-overlays into master
14 files
+ 7074
114
Compare changes
  • Side-by-side
  • Inline
Files
14
package lcsb.mapviewer.converter.graphics;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.imageio.ImageIO;
import lcsb.mapviewer.commands.ColorExtractor;
import lcsb.mapviewer.common.exception.NotImplementedException;
import lcsb.mapviewer.model.overlay.DataOverlay;
import lcsb.mapviewer.model.overlay.DataOverlayEntry;
import lcsb.mapviewer.model.overlay.GeneVariantDataOverlayEntry;
import lcsb.mapviewer.model.overlay.GenericDataOverlayEntry;
public class LegendGenerator {
private static final int WIDTH = 800;
private static final int HEIGHT = 300;
private static final int COLOR_HEIGHT = 100;
private static final int MARGIN = 100;
BufferedImage getImage(final DataOverlay overlay, final ColorExtractor colorExtractor) {
BufferedImage result = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = result.createGraphics();
Set<Class<? extends DataOverlayEntry>> entryTypes = new HashSet<>();
for (DataOverlayEntry entry : overlay.getEntries()) {
entryTypes.add(entry.getClass());
}
if (entryTypes.size() != 1) {
if (entryTypes.size() < 1) {
generateMessageLegend("No entries found", graphics);
} else {
generateMessageLegend("Legend not available for mixed entry types", graphics);
}
} else {
Class<? extends DataOverlayEntry> type = entryTypes.iterator().next();
if (type == GenericDataOverlayEntry.class) {
generateGenericLegend(graphics, overlay, colorExtractor);
} else if (type == GeneVariantDataOverlayEntry.class) {
generateGeneticLegend(graphics, overlay, colorExtractor);
} else {
generateMessageLegend("Legend not available for overlay type: " + type.getSimpleName(), graphics);
}
}
return result;
}
void generateMessageLegend(final String message, final Graphics2D graphics) {
Color oldColor = graphics.getColor();
Font oldFont = graphics.getFont();
graphics.setColor(Color.BLACK);
graphics.setFont(new Font(Font.SANS_SERIF, 0, 24));
graphics.drawString(message, MARGIN + COLOR_HEIGHT + 50, MARGIN + COLOR_HEIGHT / 2);
graphics.setColor(oldColor);
graphics.setFont(oldFont);
}
void generateGeneticLegend(final Graphics2D graphics, final DataOverlay overlay, final ColorExtractor colorExtractor) {
Color oldColor = graphics.getColor();
Font oldFont = graphics.getFont();
Map<Integer, Color> colors = new HashMap<>();
for (DataOverlayEntry entry : overlay.getEntries()) {
colors.put(((GeneVariantDataOverlayEntry) entry).getGeneVariants().size(), colorExtractor.getNormalizedColor(entry));
}
int width = WIDTH - 2 * MARGIN;
double elementWidth = ((double) width) / (double) (colors.size());
List<Integer> counters = new ArrayList<>(colors.keySet());
Collections.sort(counters);
for (int i = 0; i < counters.size(); i++) {
int count = counters.get(i);
Rectangle2D rectangle = new Rectangle2D.Double(MARGIN + i * elementWidth, MARGIN, elementWidth, COLOR_HEIGHT);
graphics.setColor(colors.get(count));
graphics.fill(rectangle);
}
graphics.setFont(new Font(Font.SANS_SERIF, 0, 18));
graphics.setColor(Color.BLACK);
Rectangle2D rectangle = new Rectangle2D.Double(MARGIN, MARGIN, WIDTH - 2 * MARGIN, COLOR_HEIGHT);
graphics.draw(rectangle);
graphics.drawString("Number of gene variants:", MARGIN, MARGIN / 2);
int labelCount = Math.min(10, counters.size());
for (int i = 0; i <= labelCount; i++) {
int selected = i * (counters.size() - 1) / labelCount;
int x = (int) (MARGIN + (selected + 0.5) * elementWidth);
graphics.drawLine(x, MARGIN - 10, x, MARGIN);
graphics.drawString(String.format("%d", counters.get(selected)), x, MARGIN - 20);
}
graphics.setColor(oldColor);
graphics.setFont(oldFont);
}
void generateGenericLegend(final Graphics2D graphics, final DataOverlay overlay, final ColorExtractor colorExtractor) {
boolean byValue = false;
boolean byColor = false;
for (DataOverlayEntry entry : overlay.getEntries()) {
GenericDataOverlayEntry genericDataOverlayEntry = (GenericDataOverlayEntry) entry;
if (genericDataOverlayEntry.getValue() != null) {
byValue = true;
}
if (genericDataOverlayEntry.getColor() != null) {
byColor = true;
}
}
if (byValue && byColor) {
throw new NotImplementedException();
} else if (byValue) {
generateGenericLegendByValue(graphics, overlay, colorExtractor);
} else if (byColor) {
generateGenericLegendByColor(graphics, overlay, colorExtractor);
} else {
generateGenericLegendBySimple(graphics, overlay, colorExtractor);
}
}
void generateGenericLegendBySimple(final Graphics2D graphics, final DataOverlay overlay, final ColorExtractor colorExtractor) {
Color oldColor = graphics.getColor();
Font oldFont = graphics.getFont();
Rectangle2D rectangle = new Rectangle2D.Double(MARGIN, MARGIN, COLOR_HEIGHT, COLOR_HEIGHT);
graphics.setColor(colorExtractor.getSimpleColor());
graphics.fill(rectangle);
graphics.setColor(Color.BLACK);
graphics.draw(rectangle);
graphics.setFont(new Font(Font.SANS_SERIF, 0, 24));
graphics.drawString("Element present on the map", MARGIN + COLOR_HEIGHT + 50, MARGIN + COLOR_HEIGHT / 2);
graphics.setColor(oldColor);
graphics.setFont(oldFont);
}
void generateGenericLegendByColor(final Graphics2D graphics, final DataOverlay overlay, final ColorExtractor colorExtractor) {
Color oldColor = graphics.getColor();
Font oldFont = graphics.getFont();
Set<Color> colorSet = new HashSet<>();
for (DataOverlayEntry entry : overlay.getEntries()) {
colorSet.add(((GenericDataOverlayEntry) entry).getColor());
}
List<Color> colors = new ArrayList<>(colorSet);
colors.sort(new Comparator<Color>() {
@Override
public int compare(final Color o1, final Color o2) {
return o1.getRGB() - o2.getRGB();
}
});
double elementWidth = ((double) (WIDTH - 2 * MARGIN)) / (double) colors.size();
for (int i = 0; i < colors.size(); i++) {
Rectangle2D rectangle = new Rectangle2D.Double(MARGIN + i * elementWidth, MARGIN, elementWidth, COLOR_HEIGHT);
graphics.setColor(colors.get(i));
graphics.fill(rectangle);
}
Rectangle2D rectangle = new Rectangle2D.Double(MARGIN, MARGIN, WIDTH - 2 * MARGIN, COLOR_HEIGHT);
graphics.setColor(Color.BLACK);
graphics.draw(rectangle);
graphics.setFont(new Font(Font.SANS_SERIF, 0, 24));
graphics.drawString("Entries have custom colors defined:", MARGIN, MARGIN - 10);
graphics.setColor(oldColor);
graphics.setFont(oldFont);
}
void generateGenericLegendByValue(final Graphics2D graphics, final DataOverlay overlay, final ColorExtractor colorExtractor) {
Color oldColor = graphics.getColor();
Font oldFont = graphics.getFont();
int width = WIDTH - 2 * MARGIN;
for (int i = 0; i < width; i++) {
Rectangle2D rectangle = new Rectangle2D.Double(MARGIN + i, MARGIN, 1, COLOR_HEIGHT);
GenericDataOverlayEntry entry = new GenericDataOverlayEntry();
entry.setValue(((double) (i - width / 2)) / (double) (width / 2.0));
graphics.setColor(colorExtractor.getNormalizedColor(entry));
graphics.fill(rectangle);
}
graphics.setColor(Color.BLACK);
graphics.setFont(new Font(Font.SANS_SERIF, 0, 18));
for (double val = -1.0; val <= 1.0; val += 0.2) {
int x = (int) (WIDTH / 2 + val * width / 2);
graphics.drawLine(x, MARGIN - 10, x, MARGIN);
graphics.drawString(String.format("%.1f", val), x, MARGIN - 20);
}
Rectangle2D rectangle = new Rectangle2D.Double(MARGIN, MARGIN, WIDTH - 2 * MARGIN, COLOR_HEIGHT);
graphics.draw(rectangle);
graphics.setColor(oldColor);
graphics.setFont(oldFont);
}
public byte[] generate(final DataOverlay overlay, final ColorExtractor colorExtractor) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(getImage(overlay, colorExtractor), "PNG", baos);
return baos.toByteArray();
}
public void savetToPng(final DataOverlay overlay, final ColorExtractor extractor, final String filename) throws IOException {
FileOutputStream fos = new FileOutputStream(new File(filename));
ImageIO.write(getImage(overlay, extractor), "PNG", fos);
}
}
Loading