From 0fe5a22bccca801290e613f408bc97fa410c08af Mon Sep 17 00:00:00 2001 From: Victor Shcherb Date: Wed, 9 Sep 2020 13:33:38 +0200 Subject: [PATCH] Fix #9777 --- .../main/java/net/osmand/osm/io/Base64.java | 153 ++++++++++-------- .../net/osmand/util/GeoPointParserUtil.java | 56 ++++++- .../osmand/util/GeoPointParserUtilTest.java | 15 +- 3 files changed, 148 insertions(+), 76 deletions(-) diff --git a/OsmAnd-java/src/main/java/net/osmand/osm/io/Base64.java b/OsmAnd-java/src/main/java/net/osmand/osm/io/Base64.java index 0a3da9bee2..0def1af562 100644 --- a/OsmAnd-java/src/main/java/net/osmand/osm/io/Base64.java +++ b/OsmAnd-java/src/main/java/net/osmand/osm/io/Base64.java @@ -1,93 +1,104 @@ package net.osmand.osm.io; + /** * @source http://www.javaworld.com/javaworld/javatips/jw-javatip47.html -- 24.11.2008, (mb) */ public class Base64 { - /******************************************************************************************************************* - * BASE 64 encoding of a String or an array of bytes. See also RFC 1421. - * - * @author Unknown - * @author David W. Croft - * @version 1998-06-08 - ******************************************************************************************************************/ + /******************************************************************************************************************* + * BASE 64 encoding of a String or an array of bytes. See also RFC 1421. + * + * @author Unknown + * @author David W. Croft + * @version 1998-06-08 + ******************************************************************************************************************/ - ////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////// - public static final char[] alphabet = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0 to 7 - 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 8 to 15 - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 16 to 23 - 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 24 to 31 - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 32 to 39 - 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 40 to 47 - 'w', 'x', 'y', 'z', '0', '1', '2', '3', // 48 to 55 - '4', '5', '6', '7', '8', '9', '+', '/' }; // 56 to 63 + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + public static final char[] alphabet = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0 to 7 + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 8 to 15 + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 16 to 23 + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 24 to 31 + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 32 to 39 + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 40 to 47 + 'w', 'x', 'y', 'z', '0', '1', '2', '3', // 48 to 55 + '4', '5', '6', '7', '8', '9', '+', '/' }; // 56 to 63 - ////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// - public static String encode(final String s) - ////////////////////////////////////////////////////////////////////// - { - return encode(s.getBytes()); - } + public static int indexOf(char c) { + for (int i = 0; i < alphabet.length; i++) { + if (alphabet[i] == c) { + return i; + } + } + return -1; + } - public static String encode(final byte[] octetString) - ////////////////////////////////////////////////////////////////////// - { - int bits24; - int bits6; - final char[] out = new char[((octetString.length - 1) / 3 + 1) * 4]; + public static String encode(final String s) + ////////////////////////////////////////////////////////////////////// + { + return encode(s.getBytes()); + } - int outIndex = 0; - int i = 0; + public static String encode(final byte[] octetString) + ////////////////////////////////////////////////////////////////////// + { + int bits24; + int bits6; - while ((i + 3) <= octetString.length) { - // store the octets - bits24 = (octetString[i++] & 0xFF) << 16; - bits24 |= (octetString[i++] & 0xFF) << 8; - bits24 |= (octetString[i++] & 0xFF); + final char[] out = new char[((octetString.length - 1) / 3 + 1) * 4]; - bits6 = (bits24 & 0x00FC0000) >> 18; - out[outIndex++] = alphabet[bits6]; - bits6 = (bits24 & 0x0003F000) >> 12; - out[outIndex++] = alphabet[bits6]; - bits6 = (bits24 & 0x00000FC0) >> 6; - out[outIndex++] = alphabet[bits6]; - bits6 = (bits24 & 0x0000003F); - out[outIndex++] = alphabet[bits6]; - } + int outIndex = 0; + int i = 0; - if (octetString.length - i == 2) { - // store the octets - bits24 = (octetString[i] & 0xFF) << 16; - bits24 |= (octetString[i + 1] & 0xFF) << 8; + while ((i + 3) <= octetString.length) { + // store the octets + bits24 = (octetString[i++] & 0xFF) << 16; + bits24 |= (octetString[i++] & 0xFF) << 8; + bits24 |= (octetString[i++] & 0xFF); - bits6 = (bits24 & 0x00FC0000) >> 18; - out[outIndex++] = alphabet[bits6]; - bits6 = (bits24 & 0x0003F000) >> 12; - out[outIndex++] = alphabet[bits6]; - bits6 = (bits24 & 0x00000FC0) >> 6; - out[outIndex++] = alphabet[bits6]; + bits6 = (bits24 & 0x00FC0000) >> 18; + out[outIndex++] = alphabet[bits6]; + bits6 = (bits24 & 0x0003F000) >> 12; + out[outIndex++] = alphabet[bits6]; + bits6 = (bits24 & 0x00000FC0) >> 6; + out[outIndex++] = alphabet[bits6]; + bits6 = (bits24 & 0x0000003F); + out[outIndex++] = alphabet[bits6]; + } - // padding - out[outIndex++] = '='; - } else if (octetString.length - i == 1) { - // store the octets - bits24 = (octetString[i] & 0xFF) << 16; + if (octetString.length - i == 2) { + // store the octets + bits24 = (octetString[i] & 0xFF) << 16; + bits24 |= (octetString[i + 1] & 0xFF) << 8; - bits6 = (bits24 & 0x00FC0000) >> 18; - out[outIndex++] = alphabet[bits6]; - bits6 = (bits24 & 0x0003F000) >> 12; - out[outIndex++] = alphabet[bits6]; + bits6 = (bits24 & 0x00FC0000) >> 18; + out[outIndex++] = alphabet[bits6]; + bits6 = (bits24 & 0x0003F000) >> 12; + out[outIndex++] = alphabet[bits6]; + bits6 = (bits24 & 0x00000FC0) >> 6; + out[outIndex++] = alphabet[bits6]; - // padding - out[outIndex++] = '='; - out[outIndex++] = '='; - } + // padding + out[outIndex++] = '='; + } else if (octetString.length - i == 1) { + // store the octets + bits24 = (octetString[i] & 0xFF) << 16; - return new String(out); - } + bits6 = (bits24 & 0x00FC0000) >> 18; + out[outIndex++] = alphabet[bits6]; + bits6 = (bits24 & 0x0003F000) >> 12; + out[outIndex++] = alphabet[bits6]; + + // padding + out[outIndex++] = '='; + out[outIndex++] = '='; + } + + return new String(out); + } } \ No newline at end of file diff --git a/OsmAnd-java/src/main/java/net/osmand/util/GeoPointParserUtil.java b/OsmAnd-java/src/main/java/net/osmand/util/GeoPointParserUtil.java index 9b2ce834b5..1383f4ae4a 100644 --- a/OsmAnd-java/src/main/java/net/osmand/util/GeoPointParserUtil.java +++ b/OsmAnd-java/src/main/java/net/osmand/util/GeoPointParserUtil.java @@ -3,6 +3,8 @@ package net.osmand.util; import java.net.URI; import java.net.URLDecoder; import java.net.URLEncoder; +import java.util.Arrays; +import java.util.Base64; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Locale; @@ -11,6 +13,9 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import net.osmand.data.LatLon; +import net.osmand.util.GeoPointParserUtil.GeoParsedPoint; + public class GeoPointParserUtil { @@ -69,6 +74,31 @@ public class GeoPointParserUtil { } return map; } + + private static int kMaxPointBytes = 10; + private static int kMaxCoordBits = kMaxPointBytes * 3; + public static LatLon decodeMapsMeLatLonToInt(String s) { + // 44TvlEGXf- + int lat = 0, lon = 0; + int shift = kMaxCoordBits - 3; + for (int i = 0; i < s.length(); ++i, shift -= 3) { + int a = net.osmand.osm.io.Base64.indexOf(s.charAt(i)); + if (a < 0) + return null; + + int lat1 = (((a >> 5) & 1) << 2 | ((a >> 3) & 1) << 1 | ((a >> 1) & 1)); + int lon1 = (((a >> 4) & 1) << 2 | ((a >> 2) & 1) << 1 | (a & 1)); + lat |= lat1 << shift; + lon |= lon1 << shift; + } + double middleOfSquare = 1 << (3 * (kMaxPointBytes - s.length()) - 1); + lat += middleOfSquare; + lon += middleOfSquare; + + double dlat = ((double) lat) / ((1 << kMaxCoordBits) - 1) * 180 - 90; + double dlon = ((double) lon) / ((1 << kMaxCoordBits) - 1 + 1) * 360.0 - 180; + return new LatLon(dlat, dlon); + } /** * Parses geo and map intents: @@ -190,6 +220,31 @@ public class GeoPointParserUtil { int zoom = parseZoom(zm); return new GeoParsedPoint(lat, lon, zoom); } + } else if (host.equals("ge0.me")) { + // http:///44TvlEGXf-/Kyiv + if (path.startsWith("/")) { + path = path.substring(1); + } + String[] pms = path.split("/"); + String label = ""; + if (pms.length > 1) { + label = pms[1]; + } + String qry = pms[0]; + if (qry.length() < 10) { + return null; + } + int indZoom = net.osmand.osm.io.Base64.indexOf(qry.charAt(0)); + int zoom = 15; + if (indZoom >= 0) { + zoom = indZoom / 4 + 4; + } + LatLon l = decodeMapsMeLatLonToInt(qry.substring(1).replace('-', '/')); + if (l == null) { + return null; + } + return new GeoParsedPoint(l.getLatitude(), l.getLongitude(), zoom, label); + } else if (simpleDomains.contains(host)) { if (uri.getQuery() == null && params.size() == 0) { // DOUBLE check this may be wrong test of openstreetmap.de (looks very weird url and server doesn't respond) @@ -221,7 +276,6 @@ public class GeoPointParserUtil { String z = String.valueOf(GeoParsedPoint.NO_ZOOM); if (params.containsKey("q")) { - System.out.println("q=" + params.get("q")); Matcher matcher = commaSeparatedPairPattern.matcher(params.get("q")); if (matcher.matches()) { latString = matcher.group(1); diff --git a/OsmAnd-java/src/test/java/net/osmand/util/GeoPointParserUtilTest.java b/OsmAnd-java/src/test/java/net/osmand/util/GeoPointParserUtilTest.java index c869779c4d..2131bebcfd 100644 --- a/OsmAnd-java/src/test/java/net/osmand/util/GeoPointParserUtilTest.java +++ b/OsmAnd-java/src/test/java/net/osmand/util/GeoPointParserUtilTest.java @@ -43,6 +43,13 @@ public class GeoPointParserUtilTest { "https://www.google.co.in/maps/place/data=!3m2!1e3!4b1!4m6!3m5!1s0x0:0x0!7e2!8m2!3d10.1213237!4d76.348392?shorturl=1"); assertGeoPoint(actual, new GeoParsedPoint(10.1213237, 76.348392)); } + + @Test + public void testMapsMeParser() { + GeoParsedPoint actual = GeoPointParserUtil.parse( + "http://ge0.me/44TvlEGXf-/Kyiv"); + assertGeoPoint(actual, new GeoParsedPoint(50.45003, 30.52414, 18, "Kyiv")); + } @Test public void testGeoPoint() { @@ -449,21 +456,21 @@ public class GeoPointParserUtilTest { actual = GeoPointParserUtil.parse(url); assertGeoPoint(actual, new GeoParsedPoint("paris")); - // TODO this URL does not work, where is it used? + // LEGACY this URL does not work, where is it used? // http://maps.google.com/maps/q=loc:34,-106&z=11 url = "http://maps.google.com/maps/q=loc:" + ilat + "," + ilon + "&z=" + z; System.out.println("url: " + url); actual = GeoPointParserUtil.parse(url); assertGeoPoint(actual, new GeoParsedPoint(ilat, ilon, z)); - // TODO this URL does not work, where is it used? + // LEGACY this URL does not work, where is it used? // http://maps.google.com/maps/q=loc:34.99393,-106.61568&z=11 url = "http://maps.google.com/maps/q=loc:" + dlat + "," + dlon + "&z=" + z; System.out.println("url: " + url); actual = GeoPointParserUtil.parse(url); assertGeoPoint(actual, new GeoParsedPoint(dlat, dlon, z)); - // TODO this URL does not work, where is it used? + // LEGACY this URL does not work, where is it used? // whatsapp // http://maps.google.com/maps/q=loc:34,-106 (You) z = GeoParsedPoint.NO_ZOOM; @@ -472,7 +479,7 @@ public class GeoPointParserUtilTest { actual = GeoPointParserUtil.parse(url); assertGeoPoint(actual, new GeoParsedPoint(ilat, ilon, z)); - // TODO this URL does not work, where is it used? + // LEGACY this URL does not work, where is it used? // whatsapp // http://maps.google.com/maps/q=loc:34.99393,-106.61568 (You) z = GeoParsedPoint.NO_ZOOM;