// License: GPL. Copyright 2007 by Immanuel Scholz and others
package org.openstreetmap.josm.tools;
import java.util.HashMap;
import java.util.Map;
import org.openstreetmap.josm.data.Bounds;
import org.openstreetmap.josm.data.coor.LatLon;
public class OsmUrlToBounds {
private static final String SHORTLINK_PREFIX = "http://osm.org/go/";
public static Bounds parse(String url) {
Bounds b = parseShortLink(url);
if (b != null)
return b;
int i = url.indexOf('?');
if (i == -1)
return null;
String[] args = url.substring(i+1).split("&");
HashMap<String, String> map = new HashMap<String, String>();
for (String arg : args) {
int eq = arg.indexOf('=');
if (eq != -1) {
map.put(arg.substring(0, eq), arg.substring(eq + 1));
}
}
try {
if (map.containsKey("bbox")) {
String bbox[] = map.get("bbox").split(",");
b = new Bounds(
new LatLon(Double.parseDouble(bbox[1]), Double.parseDouble(bbox[0])),
new LatLon(Double.parseDouble(bbox[3]), Double.parseDouble(bbox[2])));
} else if (map.containsKey("minlat")) {
String s = map.get("minlat");
Double minlat = Double.parseDouble(s);
s = map.get("minlon");
Double minlon = Double.parseDouble(s);
s = map.get("maxlat");
Double maxlat = Double.parseDouble(s);
s = map.get("maxlon");
Double maxlon = Double.parseDouble(s);
b = new Bounds(new LatLon(minlat, minlon), new LatLon(maxlat, maxlon));
} else {
b = positionToBounds(parseDouble(map, "lat"),
parseDouble(map, "lon"),
Integer.parseInt(map.get("zoom")));
}
} catch (NumberFormatException x) {
} catch (NullPointerException x) {
}
return b;
}
private static double parseDouble(HashMap<String, String> map, String key) {
if (map.containsKey(key))
return Double.parseDouble(map.get(key));
return Double.parseDouble(map.get("m"+key));
}
private static final char[] SHORTLINK_CHARS = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '_', '@'
};
/**
* p
*
* @param url string for parsing
*
* @return Bounds if shortlink, null otherwise
*
* @see http://trac.openstreetmap.org/browser/sites/rails_port/lib/short_link.rb
*/
private static Bounds parseShortLink(final String url) {
if (!url.startsWith(SHORTLINK_PREFIX))
return null;
final String shortLink = url.substring(SHORTLINK_PREFIX.length());
final Map<Character, Integer> array = new HashMap<Character, Integer>();
for (int i=0; i<SHORTLINK_CHARS.length; ++i) {
array.put(SHORTLINK_CHARS[i], i);
}
// long is necessary (need 32 bit positive value is needed)
long x = 0;
long y = 0;
int zoom = 0;
int zoomOffset = 0;
for (final char ch : shortLink.toCharArray()) {
if (array.containsKey(ch)) {
int val = array.get(ch);
for (int i=0; i<3; ++i) {
x <<= 1;
if ((val & 32) != 0) {
x |= 1;
}
val <<= 1;
y <<= 1;
if ((val & 32) != 0) {
y |= 1;
}
val <<= 1;
}
zoom += 3;
} else {
zoomOffset--;
}
}
x <<= 32 - zoom;
y <<= 32 - zoom;
// 2**32 == 4294967296
return positionToBounds(y * 180.0 / 4294967296.0 - 90.0,
x * 360.0 / 4294967296.0 - 180.0,
// TODO: -2 was not in ruby code
zoom - 8 - (zoomOffset % 3) - 2);
}
public static Bounds positionToBounds(final double lat, final double lon, final int zoom) {
final double size = 180.0 / Math.pow(2, zoom);
return new Bounds(
new LatLon(lat - size/2, lon - size),
new LatLon(lat + size/2, lon + size));
}
static public int getZoom(Bounds b) {
// convert to mercator (for calculation of zoom only)
double latMin = Math.log(Math.tan(Math.PI/4.0+b.getMin().lat()/180.0*Math.PI/2.0))*180.0/Math.PI;
double latMax = Math.log(Math.tan(Math.PI/4.0+b.getMax().lat()/180.0*Math.PI/2.0))*180.0/Math.PI;
double size = Math.max(Math.abs(latMax-latMin), Math.abs(b.getMax().lon()-b.getMin().lon()));
int zoom = 0;
while (zoom <= 20) {
if (size >= 180) {
break;
}
size *= 2;
zoom++;
}
return zoom;
}
static public String getURL(Bounds b) {
return getURL(b.getCenter(), getZoom(b));
}
static public String getURL(LatLon pos, int zoom) {
// Truncate lat and lon to something more sensible
int decimals = (int) Math.pow(10, (zoom / 3));
double lat = (Math.round(pos.lat() * decimals));
lat /= decimals;
double lon = (Math.round(pos.lon() * decimals));
lon /= decimals;
return "http://www.openstreetmap.org/?lat="+lat+"&lon="+lon+"&zoom="+zoom;
}
}