]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.district.maps/src/org/simantics/maps/sg/MapScaleNode.java
Reduction in size of hover label font to 9 pts.
[simantics/district.git] / org.simantics.district.maps / src / org / simantics / maps / sg / MapScaleNode.java
1 package org.simantics.maps.sg;
2
3 import java.awt.AlphaComposite;
4 import java.awt.BasicStroke;
5 import java.awt.Color;
6 import java.awt.Font;
7 import java.awt.FontMetrics;
8 import java.awt.Graphics2D;
9 import java.awt.geom.AffineTransform;
10 import java.awt.geom.Point2D;
11 import java.awt.geom.Rectangle2D;
12 import java.util.Locale;
13
14 import org.geotools.referencing.CRS;
15 import org.geotools.referencing.GeodeticCalculator;
16 import org.opengis.referencing.FactoryException;
17 import org.opengis.referencing.crs.CoordinateReferenceSystem;
18 import org.simantics.scenegraph.g2d.G2DNode;
19 import org.simantics.scenegraph.utils.DPIUtil;
20 import org.simantics.scenegraph.utils.GridUtils;
21
22 public class MapScaleNode extends G2DNode {
23
24     private static final long serialVersionUID = -2738682328944298290L;
25     
26     private static final Color GRAY              = new Color(100, 100, 100);
27
28     protected Boolean          enabled           = true;
29
30     protected double           gridSize          = 1.0;
31
32     @Override
33     public void render(Graphics2D g) {
34         if (!enabled)
35             return;
36
37         AffineTransform ot = g.getTransform();
38         g.transform(transform);
39         
40         AffineTransform tr = g.getTransform();
41         double scaleX = Math.abs(tr.getScaleX());
42         double scaleY = Math.abs(tr.getScaleY());
43         if (scaleX <= 0 || scaleY <= 0) {
44             // Make sure that we don't end up in an eternal loop below.
45             return;
46         }
47         double offsetX = tr.getTranslateX();
48         double offsetY = tr.getTranslateY();
49         g.setTransform(new AffineTransform());
50
51         Font rulerFont = new Font("Tahoma", Font.PLAIN, DPIUtil.upscale(9));;
52
53         //g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
54         g.setStroke(new BasicStroke(1));
55         g.setColor(new Color(0.9f, 0.9f, 0.9f, 0.75f));
56
57         Rectangle2D bounds = g.getClipBounds();
58         if(bounds == null) return; // FIXME
59
60         double previousText = -100;
61         
62         double minY = bounds.getMaxY() - 40;
63         
64         double scaleRight = bounds.getMaxX() - 20;
65         
66         double meterPerPixel = getMeterPerPixel(scaleRight - offsetX, minY - offsetY, scaleX, scaleY);
67         
68         double pixels = 0;
69         double value = 0;
70         for (int i = 0; i < SCALE_VALUES.length; i++) {
71             value = SCALE_VALUES[i];
72             pixels = value / meterPerPixel;
73             if (pixels > 100) {
74                 break;
75             }
76         }
77         
78         
79         double newScaleLeft = scaleRight - pixels;
80         g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f));
81         Rectangle2D vertical = new Rectangle2D.Double(newScaleLeft, bounds.getMaxY() - 40, pixels, 20);
82         g.fill(vertical);
83         
84         g.setColor(GRAY);
85         g.setFont(rulerFont);
86         
87         
88         // stepX and stepY should be something between 50 and 100
89         double stepX = 50;
90
91         stepX = GridUtils.limitedEvenGridSpacing(stepX, scaleX, 100, gridSize, true);
92         //while(stepX * scaleX > 100) stepX /= 2;
93         //while(stepY * scaleY > 100) stepY /= 2;
94
95         while(stepX * scaleX < 50) stepX *= 2;
96
97         stepX *= scaleX;
98         
99         double gap = scaleRight -newScaleLeft;
100         
101         stepX = gap / 2 - 0.00001;
102         
103         // Horizontal ruler
104         double label = 0;
105         FontMetrics fm = g.getFontMetrics();
106         for(double x = newScaleLeft; x < scaleRight; x += stepX) {
107             String str = formatValue(label * meterPerPixel);
108             Rectangle2D r = fm.getStringBounds(str, g);
109             if((x - r.getWidth() / 2) > previousText) {
110                 g.setColor(Color.BLACK);
111                 g.drawString(str, (int)(x-r.getWidth()/2), (int)(minY+1+r.getHeight()));
112                 previousText = x+r.getWidth()/2+stepX/4;
113             }
114
115             g.setColor(GRAY);
116             g.drawLine((int)x, (int)minY+12, (int)x, (int)minY+19);
117             if (x + 0.1 < scaleRight) {
118                 if(stepX/5 > 2) {
119                     for(double x2 = x+stepX/5; x2 < x+stepX; x2+=stepX/5) {
120                         if(x2 > 20) {
121                             g.drawLine((int)x2, (int)minY+15, (int)x2, (int)minY+19);
122                         }
123                     }
124                     for(double x2 = x+stepX/10; x2 < x+stepX; x2+=stepX/5) {
125                         if(x2 > 20) {
126                             g.drawLine((int)x2, (int)minY+17, (int)x2, (int)minY+19);
127                         }
128                     }
129                 }
130             }
131             label += stepX;
132         }
133
134         g.setTransform(ot);
135     }
136
137     @Override
138     public Rectangle2D getBoundsInLocal() {
139         return null;
140     }
141     
142     private static final transient int    MAX_DIGITS = 0;
143     private static final transient double EPSILON    = 0.01;
144     private static final transient double TRIM_THRESHOLD_MAX_VALUE = Math.pow(10, 2);
145     //private static final transient String[] SI_UNIT_LARGE_PREFIXES = { "m", "km" };
146     
147     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 };
148     
149     public static String formatValue(double value) {
150         int magnitude = (int) Math.round(Math.log10(value));
151         //System.out.println("magnitude: " + magnitude + ", " + value);
152         int allowedDecimals = MAX_DIGITS;
153         allowedDecimals -= Math.abs(magnitude);
154         if (allowedDecimals < 0)
155             allowedDecimals = 0;
156
157         String valueStr = String.format(Locale.US, "%." + allowedDecimals + "f", value);
158         if (allowedDecimals > 0) {
159             for (int trunc = valueStr.length() - 1; trunc > 0; --trunc) {
160                 char ch = valueStr.charAt(trunc);
161                 if (ch == '.') {
162                     valueStr = valueStr.substring(0, trunc);
163                     break;
164                 }
165                 if (valueStr.charAt(trunc) != '0') {
166                     valueStr = valueStr.substring(0, trunc + 1);
167                     break;
168                 }
169             }
170             if (Math.abs(value) + EPSILON > TRIM_THRESHOLD_MAX_VALUE) {
171                 // Cut anything beyond a possible decimal dot out since they
172                 // should not show anyway. This is a complete hack that tries to
173                 // circumvent floating-point inaccuracy problems.
174                 int dotIndex = valueStr.lastIndexOf('.');
175                 if (dotIndex > -1) {
176                     valueStr = valueStr.substring(0, dotIndex);
177                 }
178             }
179         }
180
181         if (valueStr.equals("-0"))
182             valueStr = "0";
183         
184 //        if (!valueStr.equals("0")) {
185 //            double trimValue = value;
186 //            for (int i = 0; i < SI_UNIT_LARGE_PREFIXES.length; i++) {
187 //                double trim = trimValue / 1000;
188 //                if (Math.abs(trim)-EPSILON < TRIM_THRESHOLD_MAX_VALUE) {
189 //                    valueStr = valueStr.substring(0, valueStr.length() - (i + 1) * 3);
190 //                    valueStr += SI_UNIT_LARGE_PREFIXES[i];
191 //                    break;
192 //                }
193 //                trimValue = trim;
194 //            }
195 //        }
196
197         return valueStr;
198     }
199
200     public void setEnabled(boolean enabled) {
201         this.enabled = enabled;
202     }
203
204     @Override
205     public void init() {
206         try {
207             EPSG4326 = CRS.decode("EPSG:4326");
208             calculator = new GeodeticCalculator(EPSG4326);
209         } catch (FactoryException e) {
210             e.printStackTrace();
211         }
212         super.init();
213     }
214     
215     GeodeticCalculator calculator; 
216     CoordinateReferenceSystem EPSG4326;
217     
218     public Point2D scaleLeftmostPoint(double startLon, double startLat, double distance, double azimuth) {
219         GeodeticCalculator calculator = new GeodeticCalculator();
220         calculator.setStartingGeographicPoint(startLon, startLat);
221         calculator.setDirection(azimuth, distance);
222         return calculator.getDestinationGeographicPoint();
223     }
224     
225     public double getMeterPerPixel(double screenX, double screenY, double scaleX, double scaleY) {
226         double startLon = (screenX / scaleX);
227         double val = (screenY / scaleY);
228         val = Math.toDegrees(Math.atan(Math.sinh(Math.toRadians(val))));
229         double startLat = val;
230         double endLon = ((screenX + 1) / scaleX);
231         double endLat = val;
232         
233         calculator.setStartingGeographicPoint(startLon, startLat);
234         calculator.setDestinationGeographicPoint(endLon, endLat);
235         double distance = calculator.getOrthodromicDistance();
236         
237         return distance;
238     }
239
240 }