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; } }