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

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

global use of osm website url and new url scheme

  • Property svn:eol-style set to native
File size: 10.2 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 String coordPart = url.substring(startIndex+5, endIndex);
96 String[] parts = coordPart.split("/");
97 Bounds b = positionToBounds(Double.parseDouble(parts[1]),
98 Double.parseDouble(parts[2]),
99 Integer.parseInt(parts[0]));
100 return b;
101 } catch (Exception ex) {
102 Main.debug(ex.getMessage());
103 return null;
104 }
105 }
106
107 private static double parseDouble(Map<String, String> map, String key) {
108 if (map.containsKey(key))
109 return Double.parseDouble(map.get(key));
110 return Double.parseDouble(map.get("m"+key));
111 }
112
113 private static final char[] SHORTLINK_CHARS = {
114 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
115 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
116 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
117 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
118 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
119 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
120 'w', 'x', 'y', 'z', '0', '1', '2', '3',
121 '4', '5', '6', '7', '8', '9', '_', '@'
122 };
123
124 /**
125 * Parse OSM short link
126 *
127 * @param url string for parsing
128 * @return Bounds if shortlink, null otherwise
129 * @see <a href="http://trac.openstreetmap.org/browser/sites/rails_port/lib/short_link.rb">short_link.rb</a>
130 */
131 private static Bounds parseShortLink(final String url) {
132 if (!url.startsWith(SHORTLINK_PREFIX))
133 return null;
134 final String shortLink = url.substring(SHORTLINK_PREFIX.length());
135
136 final Map<Character, Integer> array = new HashMap<Character, Integer>();
137
138 for (int i=0; i<SHORTLINK_CHARS.length; ++i) {
139 array.put(SHORTLINK_CHARS[i], i);
140 }
141
142 // long is necessary (need 32 bit positive value is needed)
143 long x = 0;
144 long y = 0;
145 int zoom = 0;
146 int zoomOffset = 0;
147
148 for (final char ch : shortLink.toCharArray()) {
149 if (array.containsKey(ch)) {
150 int val = array.get(ch);
151 for (int i=0; i<3; ++i) {
152 x <<= 1;
153 if ((val & 32) != 0) {
154 x |= 1;
155 }
156 val <<= 1;
157
158 y <<= 1;
159 if ((val & 32) != 0) {
160 y |= 1;
161 }
162 val <<= 1;
163 }
164 zoom += 3;
165 } else {
166 zoomOffset--;
167 }
168 }
169
170 x <<= 32 - zoom;
171 y <<= 32 - zoom;
172
173 // 2**32 == 4294967296
174 return positionToBounds(y * 180.0 / 4294967296.0 - 90.0,
175 x * 360.0 / 4294967296.0 - 180.0,
176 // TODO: -2 was not in ruby code
177 zoom - 8 - (zoomOffset % 3) - 2);
178 }
179
180 /** radius of the earth */
181 public static final double R = 6378137.0;
182
183 public static Bounds positionToBounds(final double lat, final double lon, final int zoom) {
184 int tileSizeInPixels = 256;
185 int height;
186 int width;
187 try {
188 height = Toolkit.getDefaultToolkit().getScreenSize().height;
189 width = Toolkit.getDefaultToolkit().getScreenSize().width;
190 if (Main.isDisplayingMapView()) {
191 height = Main.map.mapView.getHeight();
192 width = Main.map.mapView.getWidth();
193 }
194 } catch (HeadlessException he) {
195 // in headless mode, when running tests
196 height = 480;
197 width = 640;
198 }
199 double scale = (1 << zoom) * tileSizeInPixels / (2 * Math.PI * R);
200 double deltaX = width / 2.0 / scale;
201 double deltaY = height / 2.0 / scale;
202 double x = Math.toRadians(lon) * R;
203 double y = mercatorY(lat);
204 return new Bounds(invMercatorY(y - deltaY), Math.toDegrees(x - deltaX) / R, invMercatorY(y + deltaY), Math.toDegrees(x + deltaX) / R);
205 }
206
207 public static double mercatorY(double lat) {
208 return Math.log(Math.tan(Math.PI/4 + Math.toRadians(lat)/2)) * R;
209 }
210
211 public static double invMercatorY(double north) {
212 return Math.toDegrees(Math.atan(Math.sinh(north / R)));
213 }
214
215 public static Pair<Double, Double> getTileOfLatLon(double lat, double lon, double zoom) {
216 double x = Math.floor((lon + 180) / 360 * Math.pow(2.0, zoom));
217 double y = Math.floor((1 - Math.log(Math.tan(Math.toRadians(lat)) + 1 / Math.cos(Math.toRadians(lat))) / Math.PI)
218 / 2 * Math.pow(2.0, zoom));
219 return new Pair<Double, Double>(x, y);
220 }
221
222 public static LatLon getLatLonOfTile(double x, double y, double zoom) {
223 double lon = x / Math.pow(2.0, zoom) * 360.0 - 180;
224 double lat = Math.toDegrees(Math.atan(Math.sinh(Math.PI - (2.0 * Math.PI * y) / Math.pow(2.0, zoom))));
225 return new LatLon(lat, lon);
226 }
227
228 /**
229 * Return OSM Zoom level for a given area
230 *
231 * @param b bounds of the area
232 * @return matching zoom level for area
233 */
234 static public int getZoom(Bounds b) {
235 // convert to mercator (for calculation of zoom only)
236 double latMin = Math.log(Math.tan(Math.PI/4.0+b.getMinLat()/180.0*Math.PI/2.0))*180.0/Math.PI;
237 double latMax = Math.log(Math.tan(Math.PI/4.0+b.getMaxLat()/180.0*Math.PI/2.0))*180.0/Math.PI;
238 double size = Math.max(Math.abs(latMax-latMin), Math.abs(b.getMaxLon()-b.getMinLon()));
239 int zoom = 0;
240 while (zoom <= 20) {
241 if (size >= 180) {
242 break;
243 }
244 size *= 2;
245 zoom++;
246 }
247 return zoom;
248 }
249
250 /**
251 * Return OSM URL for given area.
252 *
253 * @param b bounds of the area
254 * @return link to display that area in OSM map
255 */
256 public static String getURL(Bounds b) {
257 return getURL(b.getCenter(), getZoom(b));
258 }
259
260 /**
261 * Return OSM URL for given position and zoom.
262 *
263 * @param pos center position of area
264 * @param zoom zoom depth of display
265 * @return link to display that area in OSM map
266 */
267 public static String getURL(LatLon pos, int zoom) {
268 return getURL(pos.lat(), pos.lon(), zoom);
269 }
270
271 /**
272 * Return OSM URL for given lat/lon and zoom.
273 *
274 * @param dlat center latitude of area
275 * @param dlon center longitude of area
276 * @param zoom zoom depth of display
277 * @return link to display that area in OSM map
278 *
279 * @since 6453
280 */
281 public static String getURL(double dlat, double dlon, int zoom) {
282 // Truncate lat and lon to something more sensible
283 int decimals = (int) Math.pow(10, (zoom / 3));
284 double lat = (Math.round(dlat * decimals));
285 lat /= decimals;
286 double lon = (Math.round(dlon * decimals));
287 lon /= decimals;
288 return Main.OSM_WEBSITE + "/#map="+zoom+"/"+lat+"/"+lon;
289 }
290}
Note: See TracBrowser for help on using the repository browser.