]> gerrit.simantics Code Review - simantics/district.git/blobdiff - org.simantics.district.maps/src/org/simantics/maps/sg/MapScaleNode.java
Lots of changes to district stuff
[simantics/district.git] / org.simantics.district.maps / src / org / simantics / maps / sg / MapScaleNode.java
diff --git a/org.simantics.district.maps/src/org/simantics/maps/sg/MapScaleNode.java b/org.simantics.district.maps/src/org/simantics/maps/sg/MapScaleNode.java
new file mode 100644 (file)
index 0000000..a0242c1
--- /dev/null
@@ -0,0 +1,242 @@
+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.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.scenegraph.utils.GridUtils;
+
+public class MapScaleNode extends G2DNode {
+
+    private static final long serialVersionUID = -2738682328944298290L;
+    
+    private static final Color GRAY              = new Color(100, 100, 100);
+
+    protected Boolean          enabled           = true;
+
+    protected double           gridSize          = 1.0;
+
+    private double scale;
+
+    @Override
+    public void render(Graphics2D g) {
+        if (!enabled)
+            return;
+
+        AffineTransform tr = g.getTransform();
+        double scaleX = Math.abs(tr.getScaleX());
+        double scaleY = Math.abs(tr.getScaleY());
+        if (scaleX <= 0 || scaleY <= 0) {
+            // Make sure that we don't end up in an eternal loop below.
+            return;
+        }
+        double offsetX = tr.getTranslateX();
+        double offsetY = tr.getTranslateY();
+        g.setTransform(new AffineTransform());
+
+        Font rulerFont = new Font("Tahoma", Font.PLAIN, 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() - 30;
+        
+        double scaleRight = bounds.getMaxX() - 30;
+        
+        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() - 30, 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;
+        for(double x = newScaleLeft; x < scaleRight; x += stepX) {
+            String str = formatValue(label * meterPerPixel);
+            FontMetrics fm = g.getFontMetrics();
+            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;
+        }
+
+        g.setTransform(tr);
+    }
+
+    @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;
+                }
+            }
+            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);
+                }
+            }
+        }
+
+        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;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    public void setScale(double scale) {
+        this.scale = scale;
+    }
+    
+    @Override
+    public void init() {
+        try {
+            EPSG4326 = CRS.decode("EPSG:4326");
+            calculator = new GeodeticCalculator(EPSG4326);
+        } catch (FactoryException e) {
+            e.printStackTrace();
+        }
+        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) {
+        double startLon = (screenX / scaleX) / scale;
+        double val = (screenY / scaleY) / scale;
+        val = Math.toDegrees(Math.atan(Math.sinh(Math.toRadians(val))));
+        double startLat = val;
+        double endLon = ((screenX + 1) / scaleX) / scale;
+        double endLat = val;
+        
+        calculator.setStartingGeographicPoint(startLon, startLat);
+        calculator.setDestinationGeographicPoint(endLon, endLat);
+        double distance = calculator.getOrthodromicDistance();
+        
+        return distance;
+    }
+
+}