import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit;
import org.simantics.g2d.participant.MouseUtil;
import org.simantics.maps.sg.MapAttributionNode;
-import org.simantics.maps.sg.MapLocationZoomInfoNode;
+import org.simantics.maps.sg.MapInfoNode;
+import org.simantics.maps.sg.MapLocationInfoNode;
import org.simantics.maps.sg.MapNode;
import org.simantics.maps.sg.MapScaleNode;
import org.simantics.maps.sg.commands.MapCommands;
}
};
- protected MapNode node = null;
- protected MapScaleNode scaleNode = null;
+ private MapNode mapNode;
+ private MapLocationInfoNode locationInfoNode;
+ private MapScaleNode scaleNode;
+ private MapInfoNode attributionNode;
private AffineTransform transform;
- private MapLocationZoomInfoNode locationZoomInfoNode;
-
private ScheduledFuture<?> schedule;
public MapPainter(AffineTransform transform) {
public boolean handleEvent(Event e) {
if (e instanceof MouseMovedEvent) {
// here we should somehow re-render ?
- if (locationZoomInfoNode.isEnabled()) {
+ if (locationInfoNode != null && locationInfoNode.isEnabled()) {
if (schedule == null || schedule.isDone()) {
LOGGER.debug("current setDirty time" + System.currentTimeMillis());
schedule = ThreadUtils.getNonBlockingWorkExecutor().schedule(() -> {
@SGInit
public void initSG(G2DParentNode parent) {
- node = parent.addNode("map", MapNode.class);
- node.setTransform(transform);
- node.setEnabled(true);
- node.setZIndex(Integer.MIN_VALUE + 999); // Just under the grid
-
+ // Just under the grid
+ mapNode = parent.addNode("map", MapNode.class);
+ mapNode.setTransform(transform);
+ mapNode.setEnabled(true);
+ mapNode.setZIndex(Integer.MIN_VALUE + 999);
+
+ // On top of pretty much everything
+ attributionNode = parent.addNode("mapAttribution", MapAttributionNode.class);
+ attributionNode.setTransform(transform);
+ attributionNode.setZIndex(Integer.MAX_VALUE - 999);
+ attributionNode.setEnabled(true);
+
scaleNode = parent.addNode("mapScale", MapScaleNode.class);
scaleNode.setTransform(transform);
+ scaleNode.setZIndex(Integer.MAX_VALUE - 998);
scaleNode.setEnabled(true);
- scaleNode.setZIndex(Integer.MAX_VALUE - 999); // Just under the grid
-
- locationZoomInfoNode = parent.addNode("locationZoomInfo", MapLocationZoomInfoNode.class);
- locationZoomInfoNode.setTransform(transform);
- locationZoomInfoNode.setEnabled(true);
- MouseUtil mouseUtil = getContext().getAtMostOneItemOfClass(MouseUtil.class);
- locationZoomInfoNode.setMouseUtil(mouseUtil);
- locationZoomInfoNode.setZIndex(Integer.MAX_VALUE - 999); // Just under the grid
-
- MapAttributionNode addNode = parent.addNode("mapAttribution", MapAttributionNode.class);
- addNode.setTransform(transform);
- addNode.setEnabled(true);
- addNode.setZIndex(Integer.MAX_VALUE - 999);
+
+ locationInfoNode = parent.addNode("mapLocationInfo", MapLocationInfoNode.class);
+ locationInfoNode.setTransform(transform);
+ locationInfoNode.setZIndex(Integer.MAX_VALUE - 997);
+ locationInfoNode.setEnabled(true);
+ locationInfoNode.setMouseUtil(getContext().getAtMostOneItemOfClass(MouseUtil.class));
}
@SGCleanup
public void cleanupSG() {
- node.remove();
+ mapNode.remove();
+ attributionNode.remove();
+ scaleNode.remove();
+ locationInfoNode.remove();
}
protected void updateNode() {
- node.setEnabled(isPaintingEnabled());
- node.setEnabled(isMapEnabled());
- node.setBackgroundColor(getBackgroundColor());
+ mapNode.setEnabled(isPaintingEnabled());
+ mapNode.setEnabled(isMapEnabled());
+ mapNode.setBackgroundColor(getBackgroundColor());
}
boolean isPaintingEnabled() {
--- /dev/null
+package org.simantics.maps.sg;
+
+import java.util.Locale;
+
+/**
+ * @author Tuukka Lehtonen
+ */
+class Formatting {
+
+ private static final transient double EPSILON = 0.01;
+
+ private static final transient double TRIM_THRESHOLD_MAX_VALUE = Math.pow(10, 4);
+
+ private static final transient String[] SI_UNIT_LARGE_PREFIXES = {
+ "k", "M", "G", "T", "P", "E", "Z", "Y"
+ };
+
+ public static enum FormatMode {
+ LIMIT_DIGITS,
+ LIMIT_DECIMALS,
+ }
+
+ public static String formatValue(double value, int max, boolean removeTrailingZeros, FormatMode formatMode) {
+ return formatValue(value, max, removeTrailingZeros, formatMode, TRIM_THRESHOLD_MAX_VALUE);
+ }
+
+ public static String formatValue(double value, int max, boolean removeTrailingZeros, FormatMode formatMode, double trimThresholdMaxValue) {
+ return formatValue(value, max, removeTrailingZeros, formatMode, trimThresholdMaxValue, true);
+ }
+
+ public static String formatValue(double value, int max, boolean removeTrailingZeros, FormatMode formatMode, double trimThresholdMaxValue, boolean trimLargeValues) {
+ //System.out.println("formatValue(" + value + ", " + max + ", " + removeTrailingZeros + ", " + formatMode + ", " + trimThresholdMaxValue + ", " + trimLargeValues + ")");
+ int allowedDecimals = max;
+ if (formatMode == FormatMode.LIMIT_DIGITS) {
+ int magnitude = (int) Math.ceil(Math.log10(Math.abs(value)));
+ //System.out.println("magnitude(" + value + "): " + magnitude);
+ allowedDecimals -= Math.abs(magnitude);
+ if (allowedDecimals < 0)
+ allowedDecimals = 0;
+ //System.out.println("allowedDecimals: " + allowedDecimals);
+ }
+
+ String valueStr = String.format(Locale.US, "%." + allowedDecimals + "f", value);
+ if (allowedDecimals > 0) {
+ if (removeTrailingZeros) {
+ for (int trunc = valueStr.length() - 1; trunc > 0; --trunc) {
+ char ch = valueStr.charAt(trunc);
+ if (ch == '.') {
+ valueStr = valueStr.substring(0, trunc);
+ break;
+ }
+ if (ch != '0') {
+ valueStr = valueStr.substring(0, trunc + 1);
+ break;
+ }
+ }
+ }
+ if (Math.abs(value) + EPSILON > trimThresholdMaxValue) {
+ // Cut anything beyond a possible decimal dot out since they
+ // should not show anyway. This is a complete hack that tries to
+ // circumvent floating-point inaccuracy problems.
+ int dotIndex = valueStr.lastIndexOf('.');
+ if (dotIndex > -1) {
+ valueStr = valueStr.substring(0, dotIndex);
+ }
+ }
+ }
+
+ if (valueStr.equals("-0"))
+ valueStr = "0";
+
+ if (trimLargeValues) {
+ double trimValue = value;
+ if (Math.abs(value) + EPSILON >= trimThresholdMaxValue) {
+ for (int i = 0; Math.abs(trimValue) + EPSILON >= trimThresholdMaxValue; ++i) {
+ double trim = trimValue / 1000;
+ if (Math.abs(trim) - EPSILON < trimThresholdMaxValue) {
+ valueStr = valueStr.substring(0, valueStr.length() - (i + 1) * 3);
+ valueStr += SI_UNIT_LARGE_PREFIXES[i];
+ break;
+ }
+ trimValue = trim;
+ }
+ }
+ }
+
+ //System.out.println("formatted result: " + valueStr);
+
+ return valueStr;
+ }
+
+}
package org.simantics.maps.sg;
-import java.awt.AlphaComposite;
-import java.awt.BasicStroke;
import java.awt.Color;
-import java.awt.Font;
+import java.awt.Composite;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
-import org.simantics.scenegraph.g2d.G2DNode;
-import org.simantics.scenegraph.utils.DPIUtil;
-
-public class MapAttributionNode extends G2DNode {
+public class MapAttributionNode extends MapInfoNode {
private static final long serialVersionUID = 7994492218791569147L;
- private static final Color GRAY = new Color(100, 100, 100);
-
- protected boolean enabled = true;
-
@Override
public void render(Graphics2D g2d) {
if (!enabled)
return;
-
+
AffineTransform ot = g2d.getTransform();
- Color originalColor = g2d.getColor();
- g2d.transform(transform);
-
g2d.setTransform(new AffineTransform());
- // do the rendering magic
-
- Font rulerFont = new Font("Tahoma", Font.PLAIN, DPIUtil.upscale(9));
-
- //g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
- g2d.setStroke(new BasicStroke(1));
- g2d.setColor(new Color(0.9f, 0.9f, 0.9f, 0.75f));
-
- Rectangle2D bounds = g2d.getClipBounds();
- if (bounds == null)
+
+ Rectangle2D controlBounds = g2d.getClipBounds();
+ if (controlBounds == null)
return; // FIXME
- String str = "Map data \u00A9 OpenStreetMap contributors";
-
- g2d.setFont(rulerFont);
+ g2d.setFont(MapInfoConstants.getInfoFont());
+
FontMetrics fm = g2d.getFontMetrics();
- Rectangle2D r = fm.getStringBounds(str, g2d);
+ Rectangle2D r = fm.getStringBounds(MapInfoConstants.ATTRIBUTION, g2d);
+
+ double yOffsetFromBottom = getMapInfoNextY(g2d);
+ double frameMaxX = controlBounds.getMaxX();
+ double frameMaxY = controlBounds.getMaxY() - yOffsetFromBottom;
+ int frameWidth = (int) Math.ceil(r.getWidth()) + MapInfoConstants.TEXT_HORIZONTAL_MARGIN * 2;
+ int frameHeight = (int) Math.ceil(r.getHeight()) + MapInfoConstants.TEXT_VERTICAL_MARGIN * 2;
+
+ Composite oc = g2d.getComposite();
+ g2d.setComposite(MapInfoConstants.INFO_COMPOSITE);
+ g2d.setStroke(MapInfoConstants.INFO_STROKE);
+
+ g2d.setColor(MapInfoConstants.TEXT_BG_COLOR);
+ rect.setFrameFromDiagonal(frameMaxX - frameWidth, frameMaxY - frameHeight, frameMaxX, frameMaxY);
+ g2d.fill(rect);
- double pixels = r.getWidth();
- double scaleRight = bounds.getMaxX();
- double newScaleLeft = scaleRight - pixels;
- double y = bounds.getMaxY();
- g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f));
-
-
- Rectangle2D vertical = new Rectangle2D.Double(newScaleLeft - 10, y - 15, pixels + 10, 15);
- g2d.fill(vertical);
-
- g2d.setColor(GRAY);
- g2d.setFont(rulerFont);
-
-
g2d.setColor(Color.BLACK);
- g2d.drawString(str, (int)newScaleLeft - 5, (int)y - 5);
-
- g2d.setColor(originalColor);
- g2d.setTransform(ot);
- }
+ g2d.drawString(MapInfoConstants.ATTRIBUTION,
+ (int) rect.getMinX() + MapInfoConstants.TEXT_HORIZONTAL_MARGIN,
+ (int) frameMaxY - fm.getMaxDescent() - MapInfoConstants.TEXT_VERTICAL_MARGIN);
- @Override
- public Rectangle2D getBoundsInLocal() {
- return null;
- }
+ g2d.setComposite(oc);
+ g2d.setTransform(ot);
- public boolean isEnabled() {
- return enabled;
+ setMapInfoNextY(g2d, yOffsetFromBottom + rect.getHeight() + MapInfoConstants.INFO_ROW_SPACING);
}
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
}
--- /dev/null
+package org.simantics.maps.sg;
+
+import java.awt.AlphaComposite;
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.RenderingHints;
+
+import org.simantics.scenegraph.utils.DPIUtil;
+
+/**
+ * @author Tuukka Lehtonen
+ */
+class MapInfoConstants {
+
+ public static final String ATTRIBUTION = "Map data \u00A9 OpenStreetMap contributors"; //$NON-NLS-1$
+
+ public static final AlphaComposite INFO_COMPOSITE = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f);
+
+ public static final BasicStroke INFO_STROKE = new BasicStroke(1f);
+
+ public static final Color TEXT_BG_COLOR = new Color(0.9f, 0.9f, 0.9f, 0.75f);
+
+ public static final int FONT_SIZE = 9;
+
+ public static final int TEXT_VERTICAL_MARGIN = 2;
+
+ public static final int TEXT_HORIZONTAL_MARGIN = 10;
+
+ public static final int INFO_ROW_SPACING = 4;
+
+ public static int scaledFontSize() {
+ return DPIUtil.upscale(FONT_SIZE);
+ }
+
+ public static Font getInfoFont() {
+ int fontSize = MapInfoConstants.scaledFontSize();
+ return new Font("Tahoma", Font.PLAIN, fontSize);
+ }
+
+ public static final RenderingHints.Key KEY_MAP_INFO_Y_COORDINATE = new RenderingHints.Key(4000) {
+ @Override
+ public boolean isCompatibleValue(Object val) {
+ return val instanceof Double;
+ }
+ };
+
+}
--- /dev/null
+package org.simantics.maps.sg;
+
+import java.awt.Graphics2D;
+import java.awt.geom.Rectangle2D;
+
+import org.simantics.scenegraph.g2d.G2DNode;
+
+/**
+ * @author Tuukka Lehtonen
+ */
+public abstract class MapInfoNode extends G2DNode {
+
+ private static final long serialVersionUID = 5514354427232700777L;
+
+ protected boolean enabled = true;
+ protected Rectangle2D rect = new Rectangle2D.Double();
+
+ public MapInfoNode() {
+ super();
+ }
+
+ @Override
+ public Rectangle2D getBoundsInLocal() {
+ return null;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ protected void setMapInfoNextY(Graphics2D g, double y) {
+ g.setRenderingHint(MapInfoConstants.KEY_MAP_INFO_Y_COORDINATE, y);
+ }
+
+ protected double getMapInfoNextY(Graphics2D g) {
+ Double d = (Double) g.getRenderingHint(MapInfoConstants.KEY_MAP_INFO_Y_COORDINATE);
+ return d != null ? d : 0.0;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package org.simantics.maps.sg;
+
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+
+import org.simantics.g2d.participant.MouseUtil;
+import org.simantics.g2d.participant.MouseUtil.MouseInfo;
+import org.simantics.maps.elevation.server.SingletonTiffTileInterface;
+import org.simantics.maps.sg.Formatting.FormatMode;
+
+public class MapLocationInfoNode extends MapInfoNode {
+
+ private static final long serialVersionUID = 7994492218791569147L;
+
+ private static final transient int MAX_DIGITS = 7;
+ private static final transient int MAX_Z_DECIMALS = 2;
+ private static final transient double TRIM_THRESHOLD_MAX_VALUE = Math.pow(10, 4);
+ private static final transient double TEXT_RIGHT_MARGIN = 20;
+
+ private MouseUtil mouse;
+
+ @Override
+ public void render(Graphics2D g2d) {
+ if (!enabled || mouse == null)
+ return;
+
+ AffineTransform ot = g2d.getTransform();
+ g2d.setTransform(new AffineTransform());
+
+ Rectangle2D bounds = g2d.getClipBounds();
+ if (bounds == null)
+ return; // FIXME
+
+ MouseInfo mouseInfo = mouse.getMouseInfo(0);
+
+ double startLat;
+ double startLon;
+ if (mouseInfo != null && mouseInfo.canvasPosition != null) {
+ Point2D canvasPosition = mouseInfo.canvasPosition;
+ double cx = canvasPosition.getX();
+ double cy = canvasPosition.getY();
+ startLat = yToLatitude(-cy / transform.getScaleY());
+ startLon = xToLongitude(cx / transform.getScaleX());
+ } else {
+ startLat = 0;
+ startLon = 0;
+ }
+
+ Number z = SingletonTiffTileInterface.lookup(startLat, startLon);
+
+ String str = "X: " + Formatting.formatValue(startLon, MAX_DIGITS, false, FormatMode.LIMIT_DIGITS, TRIM_THRESHOLD_MAX_VALUE) +
+ " Y: " + Formatting.formatValue(startLat, MAX_DIGITS, false, FormatMode.LIMIT_DIGITS, TRIM_THRESHOLD_MAX_VALUE) +
+ " Z: " + Formatting.formatValue(z.doubleValue(), MAX_Z_DECIMALS, false, FormatMode.LIMIT_DECIMALS, TRIM_THRESHOLD_MAX_VALUE);
+
+ Font font = MapInfoConstants.getInfoFont();
+ g2d.setFont(font);
+ FontMetrics fm = g2d.getFontMetrics();
+
+ Rectangle2D r = fm.getStringBounds(str, g2d);
+
+ double yOffsetFromBottom = getMapInfoNextY(g2d);
+ double frameMaxY = bounds.getMaxY() - yOffsetFromBottom;
+ double frameMinY = frameMaxY - font.getSize() - 2 * MapInfoConstants.TEXT_VERTICAL_MARGIN;
+ double frameWidth = r.getWidth() + MapInfoConstants.TEXT_HORIZONTAL_MARGIN * 2;
+ double frameMaxX = bounds.getMaxX() - TEXT_RIGHT_MARGIN;
+ double frameMinX = frameMaxX - frameWidth;
+ double textY = frameMinY + MapInfoConstants.TEXT_VERTICAL_MARGIN + fm.getMaxAscent();
+
+ Composite oc = g2d.getComposite();
+ g2d.setComposite(MapInfoConstants.INFO_COMPOSITE);
+ g2d.setStroke(MapInfoConstants.INFO_STROKE);
+
+ g2d.setColor(MapInfoConstants.TEXT_BG_COLOR);
+ rect.setFrame(frameMinX, frameMinY, frameWidth, frameMaxY - frameMinY);
+ g2d.fill(rect);
+
+ g2d.setColor(Color.BLACK);
+ g2d.drawString(str, (int) frameMinX + MapInfoConstants.TEXT_HORIZONTAL_MARGIN, (int) textY);
+
+ g2d.setComposite(oc);
+ g2d.setTransform(ot);
+
+ setMapInfoNextY(g2d, yOffsetFromBottom + rect.getHeight() + MapInfoConstants.INFO_ROW_SPACING);
+ }
+
+ public void setMouseUtil(MouseUtil util) {
+ this.mouse = util;
+ }
+
+ // TODO: these only work with Spherical Mercator
+ private static double xToLongitude(double x) {
+ return x;
+ }
+
+ private static double yToLatitude(double y) {
+ double rad = Math.toRadians(y);
+ double sinh = Math.sinh(rad);
+ double atan = Math.atan(sinh);
+ double finald = Math.toDegrees(atan);
+ return finald;
+ }
+
+}
+++ /dev/null
-package org.simantics.maps.sg;
-
-import java.awt.AlphaComposite;
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.Font;
-import java.awt.FontMetrics;
-import java.awt.Graphics2D;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Point2D;
-import java.awt.geom.Rectangle2D;
-import java.util.Locale;
-
-import org.simantics.g2d.participant.MouseUtil;
-import org.simantics.g2d.participant.MouseUtil.MouseInfo;
-import org.simantics.maps.elevation.server.SingletonTiffTileInterface;
-import org.simantics.scenegraph.g2d.G2DNode;
-import org.simantics.scenegraph.utils.DPIUtil;
-
-public class MapLocationZoomInfoNode extends G2DNode {
-
- private static final long serialVersionUID = 7994492218791569147L;
-
- private static final Color GRAY = new Color(100, 100, 100);
-
- protected boolean enabled = true;
-
- private MouseUtil util;
-
- @Override
- public void render(Graphics2D g2d) {
- if (!enabled)
- return;
-
- AffineTransform ot = g2d.getTransform();
- Color originalColor = g2d.getColor();
- g2d.transform(transform);
-
- g2d.setTransform(new AffineTransform());
- // do the rendering magic
-
- Font rulerFont = new Font("Tahoma", Font.PLAIN, DPIUtil.upscale(9));
-
- //g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
- g2d.setStroke(new BasicStroke(1));
- g2d.setColor(new Color(0.9f, 0.9f, 0.9f, 0.75f));
-
- Rectangle2D bounds = g2d.getClipBounds();
- if (bounds == null)
- return; // FIXME
-
- MouseInfo mouseInfo = util.getMouseInfo(0);
-
- double startLat;
- double startLon;
- if (mouseInfo != null && mouseInfo.canvasPosition != null) {
- Point2D canvasPosition = mouseInfo.canvasPosition;
- double cx = canvasPosition.getX();
- double cy = canvasPosition.getY();
-
- startLat = yToLatitude(-cy / transform.getScaleY());
- startLon = xToLongitude(cx / transform.getScaleX());
- } else {
- startLat = 0;
- startLon = 0;
- }
- Number zoomLevel = SingletonTiffTileInterface.lookup(startLat, startLon);
- String str = "X: " + formatValue(startLon, MAX_DIGITS) + ", Y: " + formatValue(startLat, MAX_DIGITS) + ", Z: " + zoomLevel;
- g2d.setFont(rulerFont);
- FontMetrics fm = g2d.getFontMetrics();
- Rectangle2D r = fm.getStringBounds(str, g2d);
-
- double pixels = r.getWidth() + 10;
- double scaleRight = bounds.getMaxX() - 20;
- double newScaleLeft = scaleRight - pixels;
- double y = bounds.getMaxY() - 65;
- g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f));
-
-
- Rectangle2D vertical = new Rectangle2D.Double(newScaleLeft, y, pixels, 20);
- g2d.fill(vertical);
-
- g2d.setColor(GRAY);
- g2d.setFont(rulerFont);
-
-
- g2d.setColor(Color.BLACK);
- g2d.drawString(str, (int)newScaleLeft + 5, (int)y + 15);
-
- g2d.setColor(originalColor);
- g2d.setTransform(ot);
- }
-
- @Override
- public Rectangle2D getBoundsInLocal() {
- return null;
- }
-
- public boolean isEnabled() {
- return enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
- public void setMouseUtil(MouseUtil util) {
- this.util = util;
- }
-
- private static final transient int MAX_DIGITS = 7;
- private static final transient double EPSILON = 0.01;
- private static final transient double TRIM_THRESHOLD_MAX_VALUE = Math.pow(10, 4);
- private static final transient String[] SI_UNIT_LARGE_PREFIXES = {
- "k", "M", "G", "T", "P", "E", "Z", "Y"
- };
-
- private static String formatValue(double value, int maxDigits) {
- int magnitude = (int) Math.round(Math.log10(value));
- //System.out.println("magnitude: " + magnitude + ", " + value);
- int allowedDecimals = maxDigits;
- allowedDecimals -= Math.abs(magnitude);
- if (allowedDecimals < 0)
- allowedDecimals = 0;
-
- String valueStr = String.format(Locale.US, "%." + allowedDecimals + "f", value);
- if (allowedDecimals > 0) {
- for (int trunc = valueStr.length() - 1; trunc > 0; --trunc) {
- char ch = valueStr.charAt(trunc);
- if (ch == '.') {
- valueStr = valueStr.substring(0, trunc);
- break;
- }
- if (valueStr.charAt(trunc) != '0') {
- valueStr = valueStr.substring(0, trunc + 1);
- break;
- }
- }
- if (Math.abs(value) + EPSILON > TRIM_THRESHOLD_MAX_VALUE) {
- // Cut anything beyond a possible decimal dot out since they
- // should not show anyway. This is a complete hack that tries to
- // circumvent floating-point inaccuracy problems.
- int dotIndex = valueStr.lastIndexOf('.');
- if (dotIndex > -1) {
- valueStr = valueStr.substring(0, dotIndex);
- }
- }
- }
-
- double trimValue = value;
- if (Math.abs(value)+EPSILON >= TRIM_THRESHOLD_MAX_VALUE) {
- for (int i = 0; Math.abs(trimValue)+EPSILON >= TRIM_THRESHOLD_MAX_VALUE; ++i) {
- double trim = trimValue / 1000;
- if (Math.abs(trim)-EPSILON < TRIM_THRESHOLD_MAX_VALUE) {
- valueStr = valueStr.substring(0, valueStr.length() - (i + 1) * 3);
- valueStr += SI_UNIT_LARGE_PREFIXES[i];
- break;
- }
- trimValue = trim;
- }
- }
-
- if (valueStr.equals("-0"))
- valueStr = "0";
-
- return valueStr;
- }
-
- // TODO: these only work with Spherical Mercator
- private static double xToLongitude(double x) {
- return x;
- }
-
- private static double yToLatitude(double y) {
- double rad = Math.toRadians(y);
- double sinh = Math.sinh(rad);
- double atan = Math.atan(sinh);
- double finald = Math.toDegrees(atan);
- return finald;
- }
-}
package org.simantics.maps.sg;
import java.awt.AlphaComposite;
-import java.awt.BasicStroke;
import java.awt.Color;
+import java.awt.Composite;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
-import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
-import java.util.Locale;
import org.geotools.referencing.CRS;
import org.geotools.referencing.GeodeticCalculator;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.simantics.scenegraph.g2d.G2DNode;
+import org.simantics.maps.sg.Formatting.FormatMode;
import org.simantics.scenegraph.utils.DPIUtil;
-import org.simantics.scenegraph.utils.GridUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-public class MapScaleNode extends G2DNode {
+public class MapScaleNode extends MapInfoNode {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(MapScaleNode.class);
private static final long serialVersionUID = -2738682328944298290L;
-
- private static final Color GRAY = new Color(100, 100, 100);
- protected Boolean enabled = true;
+ private static final Color GRAY = new Color(50, 50, 50);
+
+ private static final transient int SCALE_VERTICAL_MARGIN = 4;
+ private static final transient int SCALE_MAJOR_TICK_HEIGHT = 8;
+ private static final transient int SCALE_MINOR_TICK_HEIGHT = 5;
+ private static final transient int SCALE_MICRO_TICK_HEIGHT = 3;
+ private static final transient double SCALE_RIGHT_MARGIN = 20;
+
+ private static final transient int MAX_DIGITS = 0;
+ private static final transient double TRIM_THRESHOLD_MAX_VALUE = Math.pow(10, 2);
+ private static final transient String[] SI_LENGTH_UNIT = { "m", "km" };
- protected double gridSize = 1.0;
+ private static final transient double[] SCALE_VALUES = { 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000 };
+
+ private GeodeticCalculator calculator;
@Override
public void render(Graphics2D g) {
- if (!enabled)
+ if (!enabled || calculator == null)
return;
AffineTransform ot = g.getTransform();
g.transform(transform);
-
+
AffineTransform tr = g.getTransform();
double scaleX = Math.abs(tr.getScaleX());
double scaleY = Math.abs(tr.getScaleY());
double offsetY = tr.getTranslateY();
g.setTransform(new AffineTransform());
- Font rulerFont = new Font("Tahoma", Font.PLAIN, DPIUtil.upscale(9));;
-
- //g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
- g.setStroke(new BasicStroke(1));
- g.setColor(new Color(0.9f, 0.9f, 0.9f, 0.75f));
-
- Rectangle2D bounds = g.getClipBounds();
- if(bounds == null) return; // FIXME
-
- double previousText = -100;
-
- double minY = bounds.getMaxY() - 40;
-
- double scaleRight = bounds.getMaxX() - 20;
-
- double meterPerPixel = getMeterPerPixel(scaleRight - offsetX, minY - offsetY, scaleX, scaleY);
-
- double pixels = 0;
- double value = 0;
- for (int i = 0; i < SCALE_VALUES.length; i++) {
- value = SCALE_VALUES[i];
- pixels = value / meterPerPixel;
- if (pixels > 100) {
- break;
- }
- }
-
-
- double newScaleLeft = scaleRight - pixels;
- g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f));
- Rectangle2D vertical = new Rectangle2D.Double(newScaleLeft, bounds.getMaxY() - 40, pixels, 20);
- g.fill(vertical);
-
- g.setColor(GRAY);
- g.setFont(rulerFont);
-
-
- // stepX and stepY should be something between 50 and 100
- double stepX = 50;
-
- stepX = GridUtils.limitedEvenGridSpacing(stepX, scaleX, 100, gridSize, true);
- //while(stepX * scaleX > 100) stepX /= 2;
- //while(stepY * scaleY > 100) stepY /= 2;
-
- while(stepX * scaleX < 50) stepX *= 2;
-
- stepX *= scaleX;
-
- double gap = scaleRight -newScaleLeft;
-
- stepX = gap / 2 - 0.00001;
-
- // Horizontal ruler
- double label = 0;
+ Rectangle2D controlBounds = g.getClipBounds();
+ if (controlBounds == null)
+ return; // FIXME
+
+ Font font = MapInfoConstants.getInfoFont();
+ g.setFont(font);
FontMetrics fm = g.getFontMetrics();
- for(double x = newScaleLeft; x < scaleRight; x += stepX) {
- String str = formatValue(label * meterPerPixel);
- Rectangle2D r = fm.getStringBounds(str, g);
- if((x - r.getWidth() / 2) > previousText) {
- g.setColor(Color.BLACK);
- g.drawString(str, (int)(x-r.getWidth()/2), (int)(minY+1+r.getHeight()));
- previousText = x+r.getWidth()/2+stepX/4;
- }
- g.setColor(GRAY);
- g.drawLine((int)x, (int)minY+12, (int)x, (int)minY+19);
- if (x + 0.1 < scaleRight) {
- if(stepX/5 > 2) {
- for(double x2 = x+stepX/5; x2 < x+stepX; x2+=stepX/5) {
- if(x2 > 20) {
- g.drawLine((int)x2, (int)minY+15, (int)x2, (int)minY+19);
- }
- }
- for(double x2 = x+stepX/10; x2 < x+stepX; x2+=stepX/5) {
- if(x2 > 20) {
- g.drawLine((int)x2, (int)minY+17, (int)x2, (int)minY+19);
- }
- }
- }
- }
- label += stepX;
- }
+ int majorTickHeight = DPIUtil.upscale(SCALE_MAJOR_TICK_HEIGHT);
+ int minorTickHeight = DPIUtil.upscale(SCALE_MINOR_TICK_HEIGHT);
+ int microTickHeight = DPIUtil.upscale(SCALE_MICRO_TICK_HEIGHT);
+ double yOffsetFromBottom = getMapInfoNextY(g);
- g.setTransform(ot);
- }
+ double scaleTotalHeight = Math.max(majorTickHeight + SCALE_VERTICAL_MARGIN * 2 , font.getSize() + MapInfoConstants.TEXT_VERTICAL_MARGIN * 2);
+ double scaleMaxX = controlBounds.getMaxX() - SCALE_RIGHT_MARGIN;
+ double scaleMaxY = controlBounds.getMaxY() - yOffsetFromBottom;
+ double scaleMinY = scaleMaxY - scaleTotalHeight;
+ int textY = (int) scaleMinY + MapInfoConstants.TEXT_VERTICAL_MARGIN + fm.getMaxAscent();
+ double meterPerPixel = getMeterPerPixel(scaleMaxX - offsetX, scaleMinY - offsetY, scaleX, scaleY);
- @Override
- public Rectangle2D getBoundsInLocal() {
- return null;
- }
-
- private static final transient int MAX_DIGITS = 0;
- private static final transient double EPSILON = 0.01;
- private static final transient double TRIM_THRESHOLD_MAX_VALUE = Math.pow(10, 2);
- //private static final transient String[] SI_UNIT_LARGE_PREFIXES = { "m", "km" };
-
- private static final transient double[] SCALE_VALUES = { 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 50000000 };
-
- public static String formatValue(double value) {
- int magnitude = (int) Math.round(Math.log10(value));
- //System.out.println("magnitude: " + magnitude + ", " + value);
- int allowedDecimals = MAX_DIGITS;
- allowedDecimals -= Math.abs(magnitude);
- if (allowedDecimals < 0)
- allowedDecimals = 0;
-
- String valueStr = String.format(Locale.US, "%." + allowedDecimals + "f", value);
- if (allowedDecimals > 0) {
- for (int trunc = valueStr.length() - 1; trunc > 0; --trunc) {
- char ch = valueStr.charAt(trunc);
- if (ch == '.') {
- valueStr = valueStr.substring(0, trunc);
- break;
- }
- if (valueStr.charAt(trunc) != '0') {
- valueStr = valueStr.substring(0, trunc + 1);
- break;
+ // Prevent the scale from getting too long in the X-direction
+ if (meterPerPixel < 0.005)
+ return;
+
+ double scaleWidth = findScaleWidth(meterPerPixel);
+ double scaleMinX = scaleMaxX - scaleWidth;
+ double value = scaleWidth * meterPerPixel;
+ String formattedValue = formatValue(value);
+ Rectangle2D textBounds = fm.getStringBounds(formattedValue, g);
+ double addedTextWidth = textBounds.getWidth() + MapInfoConstants.TEXT_HORIZONTAL_MARGIN * 2;
+ scaleMinX -= addedTextWidth;
+ scaleMaxX -= addedTextWidth;
+ rect.setFrame(scaleMinX, scaleMinY, scaleWidth + addedTextWidth, scaleTotalHeight);
+
+// System.out.println("----");
+// System.out.println("scale: " + scaleX + ", " + scaleY);
+// System.out.println("scaleMaxX: " + scaleMaxX + ", offsetX: " + offsetX);
+// System.out.println("scaleMinY: " + scaleMinY + ", offsetY: " + offsetY);
+// System.out.println("meterPerPixel: " + meterPerPixel);
+
+ Composite oc = g.getComposite();
+ g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f));
+ g.setStroke(MapInfoConstants.INFO_STROKE);
+
+ g.setColor(MapInfoConstants.TEXT_BG_COLOR);
+ g.fill(rect);
+
+ g.setColor(Color.BLACK);
+ g.drawString(formattedValue, (int) (scaleMinX + scaleWidth) + MapInfoConstants.TEXT_HORIZONTAL_MARGIN, textY);
+
+ double stepX = scaleWidth;
+
+ g.setColor(GRAY);
+ int yOffset = -SCALE_VERTICAL_MARGIN;
+ g.drawLine((int) scaleMinX, (int) scaleMaxY + yOffset + 1, (int) scaleMaxX, (int) scaleMaxY + yOffset + 1);
+ g.drawLine((int) scaleMinX, (int) scaleMaxY - majorTickHeight + yOffset, (int) scaleMinX, (int) scaleMaxY + yOffset);
+ g.drawLine((int) scaleMaxX, (int) scaleMaxY - majorTickHeight + yOffset, (int) scaleMaxX, (int) scaleMaxY + yOffset);
+ double x = scaleMinX;
+ if (stepX / 5 > 2) {
+ for (double x2 = x + stepX / 5; x2 < x + stepX; x2 += stepX / 5) {
+ if (x2 > 20) {
+ g.drawLine((int) x2, (int) scaleMaxY - minorTickHeight + yOffset, (int) x2, (int) scaleMaxY + yOffset);
}
}
- if (Math.abs(value) + EPSILON > TRIM_THRESHOLD_MAX_VALUE) {
- // Cut anything beyond a possible decimal dot out since they
- // should not show anyway. This is a complete hack that tries to
- // circumvent floating-point inaccuracy problems.
- int dotIndex = valueStr.lastIndexOf('.');
- if (dotIndex > -1) {
- valueStr = valueStr.substring(0, dotIndex);
+ for (double x2 = x + stepX / 10; x2 < x + stepX; x2 += stepX / 5) {
+ if (x2 > 20) {
+ g.drawLine((int) x2, (int) scaleMaxY - microTickHeight + yOffset, (int) x2, (int) scaleMaxY + yOffset);
}
}
}
- if (valueStr.equals("-0"))
- valueStr = "0";
-
-// if (!valueStr.equals("0")) {
-// double trimValue = value;
-// for (int i = 0; i < SI_UNIT_LARGE_PREFIXES.length; i++) {
-// double trim = trimValue / 1000;
-// if (Math.abs(trim)-EPSILON < TRIM_THRESHOLD_MAX_VALUE) {
-// valueStr = valueStr.substring(0, valueStr.length() - (i + 1) * 3);
-// valueStr += SI_UNIT_LARGE_PREFIXES[i];
-// break;
-// }
-// trimValue = trim;
-// }
-// }
-
- return valueStr;
+ g.setComposite(oc);
+ g.setTransform(ot);
+
+ setMapInfoNextY(g, yOffsetFromBottom + scaleTotalHeight + MapInfoConstants.INFO_ROW_SPACING);
}
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
+ private String formatValue(double value) {
+ int lengthUnit = 0;
+ if (value > 1000.0) {
+ value /= 1000.0;
+ lengthUnit = 1;
+ }
+ return Formatting.formatValue(value, MAX_DIGITS, true, FormatMode.LIMIT_DECIMALS, TRIM_THRESHOLD_MAX_VALUE, false)
+ + " " + SI_LENGTH_UNIT[lengthUnit];
}
@Override
public void init() {
try {
- EPSG4326 = CRS.decode("EPSG:4326");
+ CoordinateReferenceSystem EPSG4326 = CRS.decode("EPSG:4326");
calculator = new GeodeticCalculator(EPSG4326);
} catch (FactoryException e) {
- e.printStackTrace();
+ LOGGER.error("Failed to initialize GeodeticCalculator with coordinate reference system EPSG:4326", e);
}
super.init();
}
-
- GeodeticCalculator calculator;
- CoordinateReferenceSystem EPSG4326;
-
- public Point2D scaleLeftmostPoint(double startLon, double startLat, double distance, double azimuth) {
- GeodeticCalculator calculator = new GeodeticCalculator();
- calculator.setStartingGeographicPoint(startLon, startLat);
- calculator.setDirection(azimuth, distance);
- return calculator.getDestinationGeographicPoint();
- }
-
- public double getMeterPerPixel(double screenX, double screenY, double scaleX, double scaleY) {
+
+ private double getMeterPerPixel(double screenX, double screenY, double scaleX, double scaleY) {
double startLon = (screenX / scaleX);
double val = (screenY / scaleY);
val = Math.toDegrees(Math.atan(Math.sinh(Math.toRadians(val))));
double startLat = val;
double endLon = ((screenX + 1) / scaleX);
double endLat = val;
-
+
calculator.setStartingGeographicPoint(startLon, startLat);
calculator.setDestinationGeographicPoint(endLon, endLat);
double distance = calculator.getOrthodromicDistance();
-
+
return distance;
}
+ private double findScaleWidth(double meterPerPixel) {
+ double scaleWidth = 0;
+ for (int i = 0; i < SCALE_VALUES.length; i++) {
+ scaleWidth = SCALE_VALUES[i] / meterPerPixel;
+ if (scaleWidth > 100)
+ break;
+ }
+ return scaleWidth;
+ }
+
}
import org.simantics.diagram.ui.DiagramModelHints;
import org.simantics.district.network.DistrictNetworkUtil;
import org.simantics.district.network.ontology.DistrictNetworkResource;
+import org.simantics.district.network.ui.nodes.DistrictRenderingPreparationNode;
import org.simantics.district.network.ui.participants.DNPointerInteractor;
import org.simantics.district.network.ui.participants.DynamicVisualisationContributionsParticipant;
import org.simantics.district.network.ui.participants.ElevationServerParticipant;
import org.simantics.g2d.participant.RenderingQualityInteractor;
import org.simantics.g2d.participant.TransformUtil;
import org.simantics.g2d.participant.ZoomToAreaHandler;
+import org.simantics.g2d.scenegraph.SceneGraphConstants;
import org.simantics.maps.MapScalingTransform;
import org.simantics.maps.eclipse.MapPainter;
import org.simantics.maps.sg.commands.MapCommands;
import org.simantics.modeling.ui.diagramEditor.DiagramViewer;
+import org.simantics.scenegraph.g2d.G2DParentNode;
import org.simantics.scenegraph.g2d.events.command.Command;
import org.simantics.scenegraph.g2d.events.command.CommandEvent;
import org.simantics.scenegraph.g2d.events.command.Commands;
ctx.add(new NetworkDrawingParticipant(tr));
ctx.add(new ElevationServerParticipant(tr));
ctx.add(new DynamicVisualisationContributionsParticipant(tr));
+
+ // Optimize AffineTransform memory allocations during district diagram rendering
+ G2DParentNode spatialRoot = (G2DParentNode) ctx.getSceneGraph().lookupNode(SceneGraphConstants.SPATIAL_ROOT_NODE_ID);
+ DistrictRenderingPreparationNode prepNode = new DistrictRenderingPreparationNode();
+ prepNode.setZIndex(Integer.MIN_VALUE / 2);
+ spatialRoot.addNode("districtRenderingPrepareNode", prepNode);
}
protected String getPopupId() {
public boolean pickHoveredElement(Point2D currentMousePos, boolean isConnectionTool) {
PickRequest req = new PickRequest(new Rectangle2D.Double(currentMousePos.getX(), currentMousePos.getY(), 1e-8, 1e-8)).context(getContext());
- List<IElement> pickables = new ArrayList<IElement>();
+ List<IElement> pickables = new ArrayList<>();
pick.pick(diagram, req, pickables);
-
- List<IElement> snap = new ArrayList<>(diagram.getSnapshot());
-
- // snap.removeAll(pickables);
-
+
+ List<IElement> snap = diagram.getSnapshot();
+
boolean changed = false;
- changed = hoverVertexNodes(snap, false, isConnectionTool, changed, currentMousePos);
- changed = hoverEdgeNodes(snap, false, isConnectionTool, changed, currentMousePos);
- changed = hoverVertexNodes(pickables, true, isConnectionTool, changed, currentMousePos);
- changed = hoverEdgeNodes(pickables, true, isConnectionTool, changed, currentMousePos);
+ changed |= hoverNodes(snap, false, isConnectionTool, currentMousePos);
+ changed |= hoverNodes(pickables, true, isConnectionTool, currentMousePos);
return changed;
}
- private boolean hoverVertexNodes(List<IElement> elements, boolean hover, boolean isConnectionTool, boolean changed, Point2D p) {
+ private boolean hoverNodes(List<IElement> elements, boolean hover, boolean isConnectionTool, Point2D p) {
+ boolean changed = false;
for (IElement elem : elements) {
Node node = elem.getHint(DistrictNetworkVertexElement.KEY_DN_VERTEX_NODE);
if (node instanceof DistrictNetworkVertexNode) {
- changed = ((DistrictNetworkVertexNode) node).hover(hover, isConnectionTool) || changed;
+ changed |= ((DistrictNetworkVertexNode) node).hover(hover, isConnectionTool);
if (hover)
((DistrictNetworkVertexNode) node).setMousePosition(p);
- }
- }
- return changed;
- }
-
- private boolean hoverEdgeNodes(List<IElement> elements, boolean hover, boolean isConnectionTool, boolean changed, Point2D p) {
- for (IElement elem : elements) {
- Node node = elem.getHint(DistrictNetworkEdgeElement.KEY_DN_EDGE_NODE);
- if (node instanceof DistrictNetworkEdgeNode) {
- for (IG2DNode n : ((DistrictNetworkEdgeNode) node).getNodes()) {
- if (n instanceof HoverSensitiveNode) {
- changed = ((HoverSensitiveNode)n).hover(hover, isConnectionTool) || changed;
- if (hover)
- ((HoverSensitiveNode)n).setMousePosition(p);
+ } else {
+ node = elem.getHint(DistrictNetworkEdgeElement.KEY_DN_EDGE_NODE);
+ if (node instanceof DistrictNetworkEdgeNode) {
+ for (IG2DNode n : ((DistrictNetworkEdgeNode) node).getNodes()) {
+ if (n instanceof HoverSensitiveNode) {
+ changed |= ((HoverSensitiveNode)n).hover(hover, isConnectionTool);
+ if (hover)
+ ((HoverSensitiveNode)n).setMousePosition(p);
+ }
}
}
}
}
return changed;
}
-
+
public boolean isHoveringOverNode(Point2D currentMousePos) {
PickRequest req = new PickRequest(currentMousePos).context(getContext());
List<IElement> pickables = new ArrayList<IElement>();
import org.simantics.g2d.diagram.DiagramClass;
import org.simantics.g2d.diagram.IDiagram;
import org.simantics.g2d.diagram.handler.SubstituteElementClass;
+import org.simantics.g2d.diagram.handler.impl.LockingTransactionContext;
+import org.simantics.g2d.diagram.handler.impl.PickContextImpl;
import org.simantics.g2d.element.ElementClass;
import org.simantics.g2d.element.ElementHints;
import org.simantics.g2d.element.IElement;
import org.simantics.g2d.element.handler.ElementLayerListener;
+import org.simantics.g2d.elementclass.connection.ConnectionValidator;
import org.simantics.g2d.layers.ILayer;
public class DistrictDiagramClassAdapter extends DiagramClassAdapter {
- static final SubstituteElementClass DINSTANCE = new DistrictDiagramSubstituteElementClass();
+ static final DiagramClass INSTANCE = DiagramClass.compile(
+ new PickContextImpl(true),
+ LockingTransactionContext.INSTANCE,
+ ConnectionValidator.INSTANCE,
+ DistrictDiagramSubstituteElementClass.INSTANCE
+ );
@Override
public void adapt(AsyncReadGraph g, Resource source, Resource r, AsyncProcedure<DiagramClass> procedure) {
- procedure.execute(g, INSTANCE.newClassWith(DINSTANCE));
+ procedure.execute(g, INSTANCE);
}
static class DistrictDiagramElementLayerListenerImpl implements ElementLayerListener {
static class DistrictDiagramSubstituteElementClass implements SubstituteElementClass {
- static final ElementLayerListener LAYER_LISTENER = new DistrictDiagramElementLayerListenerImpl();
+ static final SubstituteElementClass INSTANCE = new DistrictDiagramSubstituteElementClass();
+
+ final ElementLayerListener LAYER_LISTENER = new DistrictDiagramElementLayerListenerImpl();
@Override
public ElementClass substitute(IDiagram d, ElementClass ec) {
import org.simantics.district.network.ModelledCRS;
import org.simantics.district.network.ui.DistrictNetworkEdge;
import org.simantics.district.network.ui.adapters.DistrictNetworkEdgeElementFactory;
-import org.simantics.maps.MapScalingTransform;
import org.simantics.scenegraph.INode;
import org.simantics.scenegraph.ISelectionPainterNode;
-import org.simantics.scenegraph.INode.PropertySetter;
import org.simantics.scenegraph.g2d.G2DNode;
import org.simantics.scenegraph.g2d.G2DParentNode;
+import org.simantics.scenegraph.g2d.G2DRenderingHints;
import org.simantics.scenegraph.g2d.nodes.SVGNode;
import org.simantics.scenegraph.utils.GeometryUtils;
import org.simantics.scenegraph.utils.NodeUtil;
private Rectangle2D bounds;
private transient Path2D path;
- private boolean scaleStroke = true;
+ private static boolean scaleStroke = true;
private Color color;
private Double stroke;
private transient Color dynamicColor = null;
public void render(Graphics2D g2d) {
AffineTransform ot = null;
AffineTransform t = getTransform();
+ double scale = scaleStroke ? (Double) g2d.getRenderingHint(DistrictRenderingHints.KEY_VIEW_SCALE_UNDER_SPATIAL_ROOT) : 1.0;
if (t != null && !t.isIdentity()) {
- ot = g2d.getTransform();
- g2d.transform(getTransform());
+ //ot = g2d.getTransform();
+ ot = (AffineTransform) g2d.getRenderingHint(G2DRenderingHints.KEY_TRANSFORM_UNDER_SPATIAL_ROOT);
+ g2d.transform(t);
+ if (scaleStroke) {
+ AffineTransform work = DistrictNetworkNodeUtils.sharedTransform.get();
+ work.setTransform(ot);
+ work.concatenate(t);
+ scale = DistrictNetworkNodeUtils.getScale(work);
+ }
}
- double scale = 1.0;
- if (scaleStroke) {
- AffineTransform tr = g2d.getTransform();
- scale = DistrictNetworkNodeUtils.getScale(tr);
- }
-
if (!hidden) {
Object aaHint = g2d.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
} else {
bs = STROKE;
}
-
- int zoomLevel = MapScalingTransform.zoomLevel(ot);
+
+ int zoomLevel = (Integer) g2d.getRenderingHint(DistrictRenderingHints.KEY_VIEW_ZOOM_LEVEL);
path = calculatePath(edge, path, zoomLevel > 15);
if (isSelected()) {
g2d.setColor(oldColor);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, aaHint);
}
-
+
for (INode nn : getNodes()) {
G2DNode g2dNode = (G2DNode)nn;
if (g2dNode instanceof SVGNode) {
if (scaleStroke) {
viewScaleRecip /= scale;
}
-
+
+ // If the node is hidden from the start, then the center point cannot be calculated yet.
Point2D p = getCenterPoint();
+ if (p == null)
+ break;
+
symbolRect = DistrictNetworkNodeUtils.calculateDrawnGeometry(p, NORMAL, symbolRect, viewScaleRecip);
symbolTransform = DistrictNetworkNodeUtils.getTransformToRectangle(symbolRect, symbolTransform);
-
+
g2dNode.setTransform(symbolTransform);
}
g2dNode.render(g2d);
}
-
+
if (ot != null)
g2d.setTransform(ot);
}
}
private Point2D getCenterPoint() {
+ if (path == null)
+ return null;
if (centerPoint == null)
centerPoint = new Point2D.Double();
-
Rectangle2D bounds = path.getBounds2D();
centerPoint.setLocation(bounds.getCenterX(), bounds.getCenterY());
return centerPoint;
}
@PropertySetter(value = "arrowLength")
- public void setArroLength(Double length) {
+ public void setArrowLength(Double length) {
arrowLength = length;
}
public class DistrictNetworkNodeUtils {
+ public static ThreadLocal<AffineTransform> sharedTransform = new ThreadLocal<AffineTransform>() {
+ protected AffineTransform initialValue() {
+ return new AffineTransform();
+ }
+ };
+
public static Rectangle2D calculateDrawnGeometry(Point2D p, Rectangle2D margin, Rectangle2D result, double scaleRecip) {
if (result == null)
result = new Rectangle2D.Double();
import org.simantics.scenegraph.ISelectionPainterNode;
import org.simantics.scenegraph.g2d.G2DNode;
import org.simantics.scenegraph.g2d.G2DParentNode;
+import org.simantics.scenegraph.g2d.G2DRenderingHints;
import org.simantics.scenegraph.g2d.IG2DNode;
import org.simantics.scenegraph.g2d.nodes.SVGNode;
import org.simantics.scenegraph.utils.GeometryUtils;
private DistrictNetworkVertex vertex;
- private boolean scaleStroke = true;
+ private static boolean scaleStroke = true;
private boolean hover;
private Color color;
public void render(Graphics2D g2d) {
AffineTransform ot = null;
AffineTransform t = getTransform();
+ double viewScaleRecip = scaleStroke ? (Double) g2d.getRenderingHint(DistrictRenderingHints.KEY_VIEW_SCALE_RECIPROCAL_UNDER_SPATIAL_ROOT) : 1.0;
if (t != null && !t.isIdentity()) {
- ot = g2d.getTransform();
+ //ot = g2d.getTransform();
+ ot = (AffineTransform) g2d.getRenderingHint(G2DRenderingHints.KEY_TRANSFORM_UNDER_SPATIAL_ROOT);
g2d.transform(t);
+
+ if (scaleStroke) {
+ AffineTransform work = DistrictNetworkNodeUtils.sharedTransform.get();
+ work.setTransform(ot);
+ work.concatenate(t);
+ viewScaleRecip = DistrictNetworkNodeUtils.calculateScaleRecip(work);
+ }
}
// Translate lat and lon to X and Y
Point2D p = point = DistrictNetworkNodeUtils.calculatePoint2D(vertex.getPoint(), point);
-
- double viewScaleRecip = 1;
- if (scaleStroke) {
- viewScaleRecip *= DistrictNetworkNodeUtils.calculateScaleRecip(g2d.getTransform());
- }
-
+
if (!hidden && nodeSize > 0.0) {
Object oaaHint = null;
Object aaHint = g2d.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
updateBounds();
}
- @Override
- public AffineTransform getTransform() {
- return super.getTransform();
- }
-
private Rectangle2D calculateBounds(Rectangle2D rect) {
Point2D calcPoint = DistrictNetworkNodeUtils.calculatePoint2D(vertex.getPoint(), point);
AffineTransform at = NodeUtil.getLocalToGlobalTransform(this);
--- /dev/null
+package org.simantics.district.network.ui.nodes;
+
+import java.awt.RenderingHints.Key;
+
+import org.simantics.scenegraph.g2d.G2DRenderingHints;
+
+/**
+ * @author Tuukka Lehtonen
+ * See {@link G2DRenderingHints}
+ */
+public final class DistrictRenderingHints {
+
+ public static final Key KEY_VIEW_SCALE_UNDER_SPATIAL_ROOT = new Key(3001) {
+ @Override
+ public boolean isCompatibleValue(Object val) {
+ return val instanceof Double;
+ }
+ };
+
+ public static final Key KEY_VIEW_SCALE_RECIPROCAL_UNDER_SPATIAL_ROOT = new Key(3002) {
+ @Override
+ public boolean isCompatibleValue(Object val) {
+ return val instanceof Double;
+ }
+ };
+
+ public static final Key KEY_VIEW_ZOOM_LEVEL = new Key(3003) {
+ @Override
+ public boolean isCompatibleValue(Object val) {
+ return val instanceof Integer;
+ }
+ };
+
+}
\ No newline at end of file
--- /dev/null
+package org.simantics.district.network.ui.nodes;
+
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+
+import org.simantics.maps.MapScalingTransform;
+import org.simantics.scenegraph.g2d.nodes.UnboundedNode;
+
+public class DistrictRenderingPreparationNode extends UnboundedNode {
+
+ private static final long serialVersionUID = 6891204351705486193L;
+
+ @Override
+ public boolean validate() {
+ // This allows render() to be invoked always.
+ return true;
+ }
+
+ @Override
+ public void render(Graphics2D g2d) {
+ AffineTransform ot = g2d.getTransform();
+ double scale = DistrictNetworkNodeUtils.getScale(ot);
+ double scaleRecip = DistrictNetworkNodeUtils.calculateScaleRecip(ot);
+ int zoomLevel = MapScalingTransform.zoomLevel(ot);
+ g2d.setRenderingHint(DistrictRenderingHints.KEY_VIEW_SCALE_UNDER_SPATIAL_ROOT, scale);
+ g2d.setRenderingHint(DistrictRenderingHints.KEY_VIEW_SCALE_RECIPROCAL_UNDER_SPATIAL_ROOT, scaleRecip);
+ g2d.setRenderingHint(DistrictRenderingHints.KEY_VIEW_ZOOM_LEVEL, zoomLevel);
+ }
+
+}
}
});
- // get currentyl active workbench part
+ // get currently active workbench part
IWorkbenchPart beforePerformingDefaultAction = WorkbenchUtils.getActiveWorkbenchPart();
IEditorPart activeEditor = WorkbenchUtils.getActiveEditor();
LOGGER.info("activeWorkbenchPart {}", beforePerformingDefaultAction);
return (T)element;
}
else if (contentType instanceof AnyVariable) {
+ AnyVariable type = (AnyVariable) contentType;
try {
- return (T) Simantics.getSession().syncRequest(new ResourceRead<Variable>(element) {
+ return (T) type.processor.syncRequest(new ResourceRead<Variable>(element) {
public Variable perform(ReadGraph graph) throws DatabaseException {
return ElementSelector.getVariableForElement(graph, resource);
}