source: josm/trunk/src/org/openstreetmap/josm/tools/OsmUrlToBounds.java@ 6388

Last change on this file since 6388 was 6380, checked in by Don-vip, 10 years ago

update license/copyright information

  • Property svn:eol-style set to native
File size: 9.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3
4import java.awt.HeadlessException;
5import java.awt.Toolkit;
6import java.io.UnsupportedEncodingException;
7import java.net.URLDecoder;
8import java.util.HashMap;
9import java.util.Map;
10
11import org.openstreetmap.josm.Main;
12import org.openstreetmap.josm.data.Bounds;
13import org.openstreetmap.josm.data.coor.LatLon;
14
15public final class OsmUrlToBounds {
16 private static final String SHORTLINK_PREFIX = "http://osm.org/go/";
17
18 private OsmUrlToBounds() {
19 // Hide default constructor for utils classes
20 }
21
22 public static Bounds parse(String url) {
23 try {
24 // a percent sign indicates an encoded URL (RFC 1738).
25 if (url.contains("%")) {
26 url = URLDecoder.decode(url, "UTF-8");
27 }
28 } catch (UnsupportedEncodingException x) {
29 Main.error(x);
30 } catch (IllegalArgumentException x) {
31 Main.error(x);
32 }
33 Bounds b = parseShortLink(url);
34 if (b != null)
35 return b;
36 int i = url.indexOf("#map");
37 if (i >= 0) {
38 // probably it's a URL following the new scheme?
39 return parseHashURLs(url);
40 }
41 i = url.indexOf('?');
42 if (i == -1) {
43 return null;
44 }
45 String[] args = url.substring(i+1).split("&");
46 HashMap<String, String> map = new HashMap<String, String>();
47 for (String arg : args) {
48 int eq = arg.indexOf('=');
49 if (eq != -1) {
50 map.put(arg.substring(0, eq), arg.substring(eq + 1));
51 }
52 }
53
54 try {
55 if (map.containsKey("bbox")) {
56 String[] bbox = map.get("bbox").split(",");
57 b = new Bounds(
58 Double.parseDouble(bbox[1]), Double.parseDouble(bbox[0]),
59 Double.parseDouble(bbox[3]), Double.parseDouble(bbox[2]));
60 } else if (map.containsKey("minlat")) {
61 double minlat = Double.parseDouble(map.get("minlat"));
62 double minlon = Double.parseDouble(map.get("minlon"));
63 double maxlat = Double.parseDouble(map.get("maxlat"));
64 double maxlon = Double.parseDouble(map.get("maxlon"));
65 b = new Bounds(minlat, minlon, maxlat, maxlon);
66 } else {
67 String z = map.get("zoom");
68 b = positionToBounds(parseDouble(map, "lat"),
69 parseDouble(map, "lon"),
70 z == null ? 18 : Integer.parseInt(z));
71 }
72 } catch (NumberFormatException x) {
73 x.printStackTrace();
74 } catch (NullPointerException x) {
75 x.printStackTrace();
76 } catch (ArrayIndexOutOfBoundsException x) {
77 x.printStackTrace();
78 }
79 return b;
80 }
81
82 /**
83 * Openstreetmap.org changed it's URL scheme in August 2013, which breaks the URL parsing.
84 * The following function, called by the old parse function if necessary, provides parsing new URLs
85 * the new URLs follow the scheme http://www.openstreetmap.org/#map=18/51.71873/8.76164&layers=CN
86 * @param url string for parsing
87 * @return Bounds if hashurl, {@code null} otherwise
88 */
89 private static Bounds parseHashURLs(String url) {
90 int startIndex = url.indexOf("#map=");
91 if (startIndex == -1) return null;
92 int endIndex = url.indexOf('&', startIndex);
93 if (endIndex == -1) endIndex = url.length();
94 try
95 {
96 String coordPart = url.substring(startIndex+5, endIndex);
97 String[] parts = coordPart.split("/");
98 Bounds b = positionToBounds(Double.parseDouble(parts[1]),
99 Double.parseDouble(parts[2]),
100 Integer.parseInt(parts[0]));
101 return b;
102 }
103 catch(Exception ex)
104 {
105 return null;
106 }
107 }
108
109 private static double parseDouble(Map<String, String> map, String key) {
110 if (map.containsKey(key))
111 return Double.parseDouble(map.get(key));
112 return Double.parseDouble(map.get("m"+key));
113 }
114
115 private static final char[] SHORTLINK_CHARS = {
116 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
117 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
118 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
119 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
120 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
121 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
122 'w', 'x', 'y', 'z', '0', '1', '2', '3',
123 '4', '5', '6', '7', '8', '9', '_', '@'
124 };
125
126 /**
127 * Parse OSM short link
128 *
129 * @param url string for parsing
130 * @return Bounds if shortlink, null otherwise
131 * @see <a href="http://trac.openstreetmap.org/browser/sites/rails_port/lib/short_link.rb">short_link.rb</a>
132 */
133 private static Bounds parseShortLink(final String url) {
134 if (!url.startsWith(SHORTLINK_PREFIX))
135 return null;
136 final String shortLink = url.substring(SHORTLINK_PREFIX.length());
137
138 final Map<Character, Integer> array = new HashMap<Character, Integer>();
139
140 for (int i=0; i<SHORTLINK_CHARS.length; ++i) {
141 array.put(SHORTLINK_CHARS[i], i);
142 }
143
144 // long is necessary (need 32 bit positive value is needed)
145 long x = 0;
146 long y = 0;
147 int zoom = 0;
148 int zoomOffset = 0;
149
150 for (final char ch : shortLink.toCharArray()) {
151 if (array.containsKey(ch)) {
152 int val = array.get(ch);
153 for (int i=0; i<3; ++i) {
154 x <<= 1;
155 if ((val & 32) != 0) {
156 x |= 1;
157 }
158 val <<= 1;
159
160 y <<= 1;
161 if ((val & 32) != 0) {
162 y |= 1;
163 }
164 val <<= 1;
165 }
166 zoom += 3;
167 } else {
168 zoomOffset--;
169 }
170 }
171
172 x <<= 32 - zoom;
173 y <<= 32 - zoom;
174
175 // 2**32 == 4294967296
176 return positionToBounds(y * 180.0 / 4294967296.0 - 90.0,
177 x * 360.0 / 4294967296.0 - 180.0,
178 // TODO: -2 was not in ruby code
179 zoom - 8 - (zoomOffset % 3) - 2);
180 }
181
182 /** radius of the earth */
183 public static final double R = 6378137.0;
184
185 public static Bounds positionToBounds(final double lat, final double lon, final int zoom) {
186 int tileSizeInPixels = 256;
187 int height;
188 int width;
189 try {
190 height = Toolkit.getDefaultToolkit().getScreenSize().height;
191 width = Toolkit.getDefaultToolkit().getScreenSize().width;
192 if (Main.isDisplayingMapView()) {
193 height = Main.map.mapView.getHeight();
194 width = Main.map.mapView.getWidth();
195 }
196 } catch (HeadlessException he) {
197 // in headless mode, when running tests
198 height = 480;
199 width = 640;
200 }
201 double scale = (1 << zoom) * tileSizeInPixels / (2 * Math.PI * R);
202 double deltaX = width / 2.0 / scale;
203 double deltaY = height / 2.0 / scale;
204 double x = Math.toRadians(lon) * R;
205 double y = mercatorY(lat);
206 return new Bounds(invMercatorY(y - deltaY), Math.toDegrees(x - deltaX) / R, invMercatorY(y + deltaY), Math.toDegrees(x + deltaX) / R);
207 }
208
209 public static double mercatorY(double lat) {
210 return Math.log(Math.tan(Math.PI/4 + Math.toRadians(lat)/2)) * R;
211 }
212
213 public static double invMercatorY(double north) {
214 return Math.toDegrees(Math.atan(Math.sinh(north / R)));
215 }
216
217 public static Pair<Double, Double> getTileOfLatLon(double lat, double lon, double zoom) {
218 double x = Math.floor((lon + 180) / 360 * Math.pow(2.0, zoom));
219 double y = Math.floor((1 - Math.log(Math.tan(Math.toRadians(lat)) + 1 / Math.cos(Math.toRadians(lat))) / Math.PI)
220 / 2 * Math.pow(2.0, zoom));
221 return new Pair<Double, Double>(x, y);
222 }
223
224 public static LatLon getLatLonOfTile(double x, double y, double zoom) {
225 double lon = x / Math.pow(2.0, zoom) * 360.0 - 180;
226 double lat = Math.toDegrees(Math.atan(Math.sinh(Math.PI - (2.0 * Math.PI * y) / Math.pow(2.0, zoom))));
227 return new LatLon(lat, lon);
228 }
229
230 /**
231 * Return OSM Zoom level for a given area
232 *
233 * @param b bounds of the area
234 * @return matching zoom level for area
235 */
236 static public int getZoom(Bounds b) {
237 // convert to mercator (for calculation of zoom only)
238 double latMin = Math.log(Math.tan(Math.PI/4.0+b.getMinLat()/180.0*Math.PI/2.0))*180.0/Math.PI;
239 double latMax = Math.log(Math.tan(Math.PI/4.0+b.getMaxLat()/180.0*Math.PI/2.0))*180.0/Math.PI;
240 double size = Math.max(Math.abs(latMax-latMin), Math.abs(b.getMaxLon()-b.getMinLon()));
241 int zoom = 0;
242 while (zoom <= 20) {
243 if (size >= 180) {
244 break;
245 }
246 size *= 2;
247 zoom++;
248 }
249 return zoom;
250 }
251
252 /**
253 * Return OSM URL for given area
254 *
255 * @param b bounds of the area
256 * @return link to display that area in OSM map
257 */
258 static public String getURL(Bounds b) {
259 return getURL(b.getCenter(), getZoom(b));
260 }
261
262 /**
263 * Return OSM URL for given position and zoom
264 *
265 * @param pos center position of area
266 * @param zoom zoom depth of display
267 * @return link to display that area in OSM map
268 */
269 static public String getURL(LatLon pos, int zoom) {
270 // Truncate lat and lon to something more sensible
271 int decimals = (int) Math.pow(10, (zoom / 3));
272 double lat = (Math.round(pos.lat() * decimals));
273 lat /= decimals;
274 double lon = (Math.round(pos.lon() * decimals));
275 lon /= decimals;
276 return "http://www.openstreetmap.org/?lat="+lat+"&lon="+lon+"&zoom="+zoom;
277 }
278}
Note: See TracBrowser for help on using the repository browser.