| 63 | private static final char[] SHORTLINK_CHARS = { |
| 64 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', |
| 65 | 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', |
| 66 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', |
| 67 | 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', |
| 68 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', |
| 69 | 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', |
| 70 | 'w', 'x', 'y', 'z', '0', '1', '2', '3', |
| 71 | '4', '5', '6', '7', '8', '9', '_', '@' |
| 72 | }; |
| 73 | |
| 74 | /** |
| 75 | * p |
| 76 | * |
| 77 | * @param url string for parsing |
| 78 | * |
| 79 | * @return Bounds if shortlink, null otherwise |
| 80 | * |
| 81 | * @see http://trac.openstreetmap.org/browser/sites/rails_port/lib/short_link.rb |
| 82 | */ |
| 83 | private static Bounds parseShortLink(final String url) { |
| 84 | if (!url.startsWith(SHORTLINK_PREFIX)) { |
| 85 | return null; |
| 86 | } |
| 87 | final String shortLink = url.substring(SHORTLINK_PREFIX.length()); |
| 88 | |
| 89 | final Map<Character, Integer> array = new HashMap<Character, Integer>(); |
| 90 | |
| 91 | for (int i=0; i<SHORTLINK_CHARS.length; ++i) { |
| 92 | array.put(SHORTLINK_CHARS[i], i); |
| 93 | } |
| 94 | |
| 95 | // long is necessary (need 32 bit positive value is needed) |
| 96 | long x = 0; |
| 97 | long y = 0; |
| 98 | int zoom = 0; |
| 99 | int zoomOffset = 0; |
| 100 | |
| 101 | for (final char ch : shortLink.toCharArray()) { |
| 102 | if (array.containsKey(ch)) { |
| 103 | int val = array.get(ch); |
| 104 | for (int i=0; i<3; ++i) { |
| 105 | x <<= 1; |
| 106 | if ((val & 32) != 0) { |
| 107 | x |= 1; |
| 108 | } |
| 109 | val <<= 1; |
| 110 | |
| 111 | y <<= 1; |
| 112 | if ((val & 32) != 0) { |
| 113 | y |= 1; |
| 114 | } |
| 115 | val <<= 1; |
| 116 | } |
| 117 | zoom += 3; |
| 118 | } else { |
| 119 | zoomOffset--; |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | x <<= 32 - zoom; |
| 124 | y <<= 32 - zoom; |
| 125 | |
| 126 | // 2**32 == 4294967296 |
| 127 | return positionToBounds(y * 180.0 / 4294967296.0 - 90.0, |
| 128 | x * 360.0 / 4294967296.0 - 180.0, |
| 129 | // TODO: -2 was not in ruby code |
| 130 | zoom - 8 - (zoomOffset % 3) - 2); |
| 131 | } |
| 132 | |
| 133 | private static Bounds positionToBounds(final double lat, final double lon, final int zoom) { |
| 134 | final double size = 180.0 / Math.pow(2, zoom); |
| 135 | return new Bounds( |
| 136 | new LatLon(lat - size/2, lon - size), |
| 137 | new LatLon(lat + size/2, lon + size)); |
| 138 | } |
| 139 | |
| 140 | |