package org.codemap.tasks; import java.util.ArrayList; import java.util.Collection; import org.codemap.CodemapCore; import org.codemap.DefaultLabelScheme; import org.codemap.Labeling; import org.codemap.Location; import org.codemap.MapInstance; import org.codemap.layers.Label; import org.codemap.layers.LabelOverlay; import org.codemap.util.MapScheme; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Display; import ch.akuhn.util.Get; import ch.akuhn.util.ProgressMonitor; import ch.akuhn.values.Arguments; import ch.akuhn.values.TaskValue; import ch.akuhn.values.Value; public class ComputeLabelingTask extends TaskValue<Labeling> { public ComputeLabelingTask(Value<MapInstance> mapInstance, Value<MapScheme<String>> labelScheme) { super("Label layout", mapInstance, labelScheme); } @Override protected Labeling computeValue(ProgressMonitor monitor, Arguments arguments) { MapInstance mapInstance = arguments.nextOrFail(); MapScheme<String> labelScheme = arguments.nextOrFail(); if (labelScheme == null) labelScheme = new DefaultLabelScheme(); return computeValue(monitor, mapInstance, labelScheme); } private Labeling computeValue(ProgressMonitor monitor, MapInstance mapInstance, MapScheme<String> labelScheme) { Device device = Display.getDefault(); Image image = new Image(device, 8, 8); // to get a GC GC gc = new GC(image); Iterable<Label> labels = makeLabels(monitor, gc, mapInstance, labelScheme); labels = new LayoutAlgorithm().layout(labels); gc.dispose(); image.dispose(); return new Labeling(labels); } private Iterable<Label> makeLabels(ProgressMonitor monitor, GC gc, MapInstance map, MapScheme<String> labelScheme) { Collection<Label> labels = new ArrayList<Label>(); Font basefont = new Font(gc.getDevice(), LabelOverlay.ARIAL_NARROW, 12, SWT.NORMAL); for (Location each: map.locations()) { String text = labelScheme.forLocation(each.getPoint()); if (text == null) continue; FontData[] fontData = basefont.getFontData(); int height = (int) (Math.sqrt(each.getElevation()) * CodemapCore.colorScheme().getLabelHeightFactor()); for (FontData fd: fontData) fd.setHeight(height); Font font = new Font(gc.getDevice(), fontData); gc.setFont(font); Point extent = gc.stringExtent(text); labels.add(new Label(each.px, each.py, extent, height, text, each)); font.dispose(); } basefont.dispose(); return labels; } private class LayoutAlgorithm { Collection<Label> layout; public Iterable<Label> layout(Iterable<Label> labels) { layout = new ArrayList<Label>(); for (Label each: Get.sorted(labels)) mayebAddToLayout(each); return layout; } private void mayebAddToLayout(Label label) { for (double[] each: ORIENTATION) { label.changeOrientation(each[0], each[1]); if (intersectsLabels(label)) continue; layout.add(label); return; } } private boolean intersectsLabels(Label label) { for (Label each: layout) if (label.intersects(each)) return true; return false; } } private static final double[][] ORIENTATION = new double[][] { {-.5d,-1d}, // north {-.5d,-.5d}, // center {-.5d,0d}, // south {-.25d,-1d}, {-.25d,-.5d}, {-.25d,0d}, {-.75d,-1d}, {-.75d,-.5d}, {-.75d,0d}, {0d,-.5d}, {-1d,-.5d}}; }