Ticket #6869: OsmMercator.java

File OsmMercator.java, 6.3 KB (added by jhuntley, 15 years ago)
Line 
1package org.openstreetmap.gui.jmapviewer;
2
3// License: GPL. Copyright 2007 by Tim Haussmann
4
5/**
6 * This class implements the Mercator Projection as it is used by Openstreetmap
7 * (and google). It provides methods to translate coordinates from 'map space'
8 * into latitude and longitude (on the WGS84 ellipsoid) and vice versa. Map
9 * space is measured in pixels. The origin of the map space is the top left
10 * corner. The map space origin (0,0) has latitude ~85 and longitude -180
11 *
12 * @author Tim Haussmann
13 *
14 */
15
16public class OsmMercator {
17
18 private static int TILE_SIZE = 256;
19 public static final double MAX_LAT = 85.05112877980659;
20 public static final double MIN_LAT = -85.05112877980659;
21 private static double EARTH_RADIUS_KM = 6371;
22
23 private static double KMTOM=1000;
24 private static double MTOKM=1/1000;
25
26 public static double kmToMeters(double kmeters) {
27 return kmeters*KMTOM;
28 }
29
30 public static double metersToKm(double meters) {
31 return meters*MTOKM;
32 }
33
34 public static double radius(int aZoomlevel) {
35 return (TILE_SIZE * (1 << aZoomlevel)) / (2.0 * Math.PI);
36 }
37
38 /**
39 * Returns the absolut number of pixels in y or x, defined as: 2^Zoomlevel *
40 * TILE_WIDTH where TILE_WIDTH is the width of a tile in pixels
41 *
42 * @param aZoomlevel
43 * @return
44 */
45 public static int getMaxPixels(int aZoomlevel) {
46 return TILE_SIZE * (1 << aZoomlevel);
47 }
48
49 public static int falseEasting(int aZoomlevel) {
50 return getMaxPixels(aZoomlevel) / 2;
51 }
52
53 public static int falseNorthing(int aZoomlevel) {
54 return (-1 * getMaxPixels(aZoomlevel) / 2);
55 }
56
57 /**
58 * Transform pixelspace to coordinates and get the distance.
59 *
60 * @param x1 the first x coordinate
61 * @param y1 the first y coordinate
62 * @param x2 the second x coordinate
63 * @param y2 the second y coordinate
64 *
65 * @param zoomLevel the zoom level
66 * @return the distance
67 * @author Jason Huntley
68 */
69 public static double getDistance(int x1, int y1, int x2, int y2, int zoomLevel) {
70 double la1 = YToLat(y1, zoomLevel);
71 double lo1 = XToLon(x1, zoomLevel);
72 double la2 = YToLat(y2, zoomLevel);
73 double lo2 = XToLon(x2, zoomLevel);
74
75 return getDistance(la1, lo1, la2, lo2);
76 }
77
78 /**
79 * Gets the distance using Spherical law of cosines.
80 *
81 * @param la1 the Latitude in degrees
82 * @param lo1 the Longitude in degrees
83 * @param la2 the Latitude from 2nd coordinate in degrees
84 * @param lo2 the Longitude from 2nd coordinate in degrees
85 * @return the distance
86 * @author Jason Huntley
87 */
88 public static double getDistance(double la1, double lo1, double la2, double lo2) {
89 double aStartLat = Math.toRadians(la1);
90 double aStartLong = Math.toRadians(lo1);
91 double aEndLat =Math.toRadians(la2);
92 double aEndLong = Math.toRadians(lo2);
93
94 double distance = Math.acos(Math.sin(aStartLat) * Math.sin(aEndLat)
95 + Math.cos(aStartLat) * Math.cos(aEndLat)
96 * Math.cos(aEndLong - aStartLong));
97
98 return (EARTH_RADIUS_KM * distance);
99 }
100
101 /**
102 * Transform longitude to pixelspace
103 *
104 * <p>
105 * Mathematical optimization<br>
106 * <code>
107 * x = radius(aZoomlevel) * toRadians(aLongitude) + falseEasting(aZoomLevel)<br>
108 * x = getMaxPixels(aZoomlevel) / (2 * PI) * (aLongitude * PI) / 180 + getMaxPixels(aZoomlevel) / 2<br>
109 * x = getMaxPixels(aZoomlevel) * aLongitude / 360 + 180 * getMaxPixels(aZoomlevel) / 360<br>
110 * x = getMaxPixels(aZoomlevel) * (aLongitude + 180) / 360<br>
111 * </code>
112 * </p>
113 *
114 * @param aLongitude
115 * [-180..180]
116 * @return [0..2^Zoomlevel*TILE_SIZE[
117 * @author Jan Peter Stotz
118 */
119 public static int LonToX(double aLongitude, int aZoomlevel) {
120 int mp = getMaxPixels(aZoomlevel);
121 int x = (int) ((mp * (aLongitude + 180l)) / 360l);
122 x = Math.min(x, mp - 1);
123 return x;
124 }
125
126 /**
127 * Transforms latitude to pixelspace
128 * <p>
129 * Mathematical optimization<br>
130 * <code>
131 * log(u) := log((1.0 + sin(toRadians(aLat))) / (1.0 - sin(toRadians(aLat))<br>
132 *
133 * y = -1 * (radius(aZoomlevel) / 2 * log(u)))) - falseNorthing(aZoomlevel))<br>
134 * y = -1 * (getMaxPixel(aZoomlevel) / 2 * PI / 2 * log(u)) - -1 * getMaxPixel(aZoomLevel) / 2<br>
135 * y = getMaxPixel(aZoomlevel) / (-4 * PI) * log(u)) + getMaxPixel(aZoomLevel) / 2<br>
136 * y = getMaxPixel(aZoomlevel) * ((log(u) / (-4 * PI)) + 1/2)<br>
137 * </code>
138 * </p>
139 * @param aLat
140 * [-90...90]
141 * @return [0..2^Zoomlevel*TILE_SIZE[
142 * @author Jan Peter Stotz
143 */
144 public static int LatToY(double aLat, int aZoomlevel) {
145 if (aLat < MIN_LAT)
146 aLat = MIN_LAT;
147 else if (aLat > MAX_LAT)
148 aLat = MAX_LAT;
149 double sinLat = Math.sin(Math.toRadians(aLat));
150 double log = Math.log((1.0 + sinLat) / (1.0 - sinLat));
151 int mp = getMaxPixels(aZoomlevel);
152 int y = (int) (mp * (0.5 - (log / (4.0 * Math.PI))));
153 y = Math.min(y, mp - 1);
154 return y;
155 }
156
157 /**
158 * Transforms pixel coordinate X to longitude
159 *
160 * <p>
161 * Mathematical optimization<br>
162 * <code>
163 * lon = toDegree((aX - falseEasting(aZoomlevel)) / radius(aZoomlevel))<br>
164 * lon = 180 / PI * ((aX - getMaxPixels(aZoomlevel) / 2) / getMaxPixels(aZoomlevel) / (2 * PI)<br>
165 * lon = 180 * ((aX - getMaxPixels(aZoomlevel) / 2) / getMaxPixels(aZoomlevel))<br>
166 * lon = 360 / getMaxPixels(aZoomlevel) * (aX - getMaxPixels(aZoomlevel) / 2)<br>
167 * lon = 360 * aX / getMaxPixels(aZoomlevel) - 180<br>
168 * </code>
169 * </p>
170 * @param aX
171 * [0..2^Zoomlevel*TILE_WIDTH[
172 * @return ]-180..180[
173 * @author Jan Peter Stotz
174 */
175 public static double XToLon(int aX, int aZoomlevel) {
176 return ((360d * aX) / getMaxPixels(aZoomlevel)) - 180.0;
177 }
178
179 /**
180 * Transforms pixel coordinate Y to latitude
181 *
182 * @param aY
183 * [0..2^Zoomlevel*TILE_WIDTH[
184 * @return [MIN_LAT..MAX_LAT] is about [-85..85]
185 */
186 public static double YToLat(int aY, int aZoomlevel) {
187 aY += falseNorthing(aZoomlevel);
188 double latitude = (Math.PI / 2) - (2 * Math.atan(Math.exp(-1.0 * aY / radius(aZoomlevel))));
189 return -1 * Math.toDegrees(latitude);
190 }
191
192}