package aimax.osm.viewer; import aimax.osm.data.BoundingBox; import aimax.osm.data.Position; /** * Class which is responsible for transformations between world * and view coordinates. It implements an equirectangular projection. * World coordinates are specified in latitude and longitude, view coordinates * in pixel coordinates (x and y). The actual screen resolution is maintained * for scale computation. Additionally, a unit-per-inch factor is maintained * for transforming logical symbol size into pixels. * @author Ruediger Lunde * */ public class CoordTransformer { /** Longitude of left upper corner. */ private float originLon; /** Latitude of left upper corner. */ private float originLat; /** Correction factor for longitude values. */ private float lonCorr; /** Number of pixels corresponding to one degree of latitude. */ private float dotsPerDeg; /** * Factor for scaling symbols (widths of lines, size of icons etc.). * We use logical units for defining the size of different kinds of symbols * and compute the actual number of pixels for a symbol by multiplying * the symbol's logical size with <code>dotsPerInch/unitsPerInch</code>. * Default value is 92. This means, that a symbol with a specified size * of 92 units should have the size of one inch on the screen. */ public static float unitsPerInch = 92f; /** * Number of pixels per inch on the screen (100 per default). The value * maintains the actual screen resolution (true size of a pixel) and is * used for scale computation. Default value is {@link #unitsPerInch}. */ private float dotsPerInch = unitsPerInch; /** Sets the screen resolution (used for scale computation). */ public void setScreenResolution(int dotsPerInch) { this.dotsPerInch = dotsPerInch; } /** * Adjusts the transformation with respect to a given world coordinate * bounding box. */ public void adjustTransformation(BoundingBox bb, int viewWidth, int viewHeight) { if (bb != null) { lonCorr = (float) Math.cos((bb.getLatMax() + bb.getLatMin()) / 360.0 * Math.PI); // adjust coordinates relative to the left upper corner of the graph area float scaleX = 1f; float scaleY = 1f; if (bb.getLonMax() > bb.getLonMin()) scaleX = viewWidth / ((bb.getLonMax()-bb.getLonMin()) * lonCorr); if (bb.getLatMax() > bb.getLatMin()) scaleY = viewHeight / (bb.getLatMax()-bb.getLatMin()); dotsPerDeg = Math.max(scaleX, scaleY); originLon = bb.getLonMin(); originLat = bb.getLatMax(); originLon += (bb.getLonMax() - lon(viewWidth)) / 2.0; originLat += (bb.getLatMin() - lat(viewHeight)) / 2.0; } else { lonCorr = 1.0f; dotsPerDeg = 100; originLon = 0; originLat = 0; } } /** * Multiples the current scale with the specified factor and * adjusts the view so that the objects shown at the * specified view focus keep at their position. */ public void zoom(float factor, int focusX, int focusY) { float focusLon = lon(focusX); float focusLat = lat(focusY); dotsPerDeg *= factor; int focusXNew = x(focusLon); int focusYNew = y(focusLat); adjust(focusX-focusXNew, focusY-focusYNew); } /** * Adjusts the transformation. * @param dx Number of pixels for horizontal shift. * @param dy Number of pixels for vertical shift. */ public void adjust(double dx, double dy) { this.originLon -= dx / (dotsPerDeg * lonCorr); this.originLat += dy / dotsPerDeg; } /** Returns the x_view of a given longitude value in world coordinates. */ public int x(double lon) { return (int) Math.round(dotsPerDeg * (lon - originLon) * lonCorr); } /** Returns the y_view of a given latitude value in world coordinates. */ public int y(double lat) { return (int) Math.round(dotsPerDeg * (originLat - lat)); } /** Computes the corresponding longitude for a given view x coordinate. */ public float lon(int x) { return x / (dotsPerDeg * lonCorr) + originLon; } /** Computes the corresponding latitude for a given view y coordinate. */ public float lat(int y) { return originLat - y / dotsPerDeg; } /** Returns the current {@link #dotsPerDeg} value. */ public float getDotsPerDeg() { return dotsPerDeg; } /** * Returns the scale. 1 / 100 000 means one cm on the screen corresponds to * 1 km in real world. */ public float computeScale() { //System.out.println(dotsPerInch + " ... " + Toolkit.getDefaultToolkit().getScreenResolution()); //System.out.println(Toolkit.getDefaultToolkit().getScreenSize().getWidth()); double kmPerInch = 0.0254e-3; double kmPerDeg = Position.EARTH_RADIUS * Math.toRadians(1.0); return (float) (dotsPerDeg / dotsPerInch * kmPerInch / kmPerDeg); } /** Returns a factor for converting logical symbol size units into screen dots. */ public float getDotsPerUnit() { return dotsPerInch / unitsPerInch; } }