aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorblafoo <github@blafoo.de>2012-02-21 21:40:52 +0100
committerblafoo <github@blafoo.de>2012-02-23 23:21:04 +0100
commit36d8df2e25e5f2b6551dd00fd579aa5c0d092c62 (patch)
tree262924c2e3c3b4a6ab5034e69b5eae14e0facb83
parent473d76b8a064549094c5c777acf013d205b0f720 (diff)
downloadcgeo-36d8df2e25e5f2b6551dd00fd579aa5c0d092c62.zip
cgeo-36d8df2e25e5f2b6551dd00fd579aa5c0d092c62.tar.gz
cgeo-36d8df2e25e5f2b6551dd00fd579aa5c0d092c62.tar.bz2
First working version of new live map
-rw-r--r--main/src/cgeo/geocaching/GCConstants.java8
-rw-r--r--main/src/cgeo/geocaching/cgBase.java31
-rw-r--r--main/src/cgeo/geocaching/connector/gc/GCBase.java172
-rw-r--r--main/src/cgeo/geocaching/connector/gc/Tile.java24
-rw-r--r--main/src/cgeo/geocaching/connector/gc/UTFGrid.java13
-rw-r--r--main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java6
-rw-r--r--main/src/cgeo/geocaching/enumerations/CacheType.java4
-rw-r--r--main/src/cgeo/geocaching/maps/CGeoMap.java7
-rw-r--r--tests/src/cgeo/geocaching/connector/GCConnectorTest.java54
9 files changed, 239 insertions, 80 deletions
diff --git a/main/src/cgeo/geocaching/GCConstants.java b/main/src/cgeo/geocaching/GCConstants.java
index af717ee..c28cfcb 100644
--- a/main/src/cgeo/geocaching/GCConstants.java
+++ b/main/src/cgeo/geocaching/GCConstants.java
@@ -13,6 +13,11 @@ import java.util.regex.Pattern;
*/
public final class GCConstants {
+ /** Live Map */
+ public final static String URL_LIVE_MAP = "http://www.geocaching.com/map/default.aspx";
+ /** Caches in a tile */
+ public final static String URL_MAP_INFO = "http://www.geocaching.com/map/map.info";
+
/**
* Patterns for parsing the result of a (detailed) search
*/
@@ -132,4 +137,7 @@ public final class GCConstants {
public final static Pattern PATTERN_VIEWSTATEFIELDCOUNT = Pattern.compile("id=\"__VIEWSTATEFIELDCOUNT\"[^(value)]+value=\"(\\d+)\"[^>]+>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
public final static Pattern PATTERN_VIEWSTATES = Pattern.compile("id=\"__VIEWSTATE(\\d*)\"[^(value)]+value=\"([^\"]+)\"[^>]+>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
public final static Pattern PATTERN_USERTOKEN2 = Pattern.compile("userToken\\s*=\\s*'([^']+)'");
+
+ /** Live Map since 14.02.2012 */
+ public final static Pattern PATTERN_SESSIONTOKEN = Pattern.compile("sessionToken:'([^']+)'");
}
diff --git a/main/src/cgeo/geocaching/cgBase.java b/main/src/cgeo/geocaching/cgBase.java
index 5481da6..4f74881 100644
--- a/main/src/cgeo/geocaching/cgBase.java
+++ b/main/src/cgeo/geocaching/cgBase.java
@@ -271,7 +271,10 @@ public class cgBase {
return StatusCode.NO_LOGIN_INFO_STORED;
}
- cgBase.setActualStatus(res.getString(R.string.init_login_popup_working));
+ // res is null during the unit tests
+ if (res != null) {
+ cgBase.setActualStatus(res.getString(R.string.init_login_popup_working));
+ }
HttpResponse loginResponse = request("https://www.geocaching.com/login/default.aspx", null, false, false, false);
String loginData = getResponseData(loginResponse);
if (loginResponse != null && loginResponse.getStatusLine().getStatusCode() == 503 && BaseUtils.matches(loginData, GCConstants.PATTERN_MAINTENANCE)) {
@@ -357,7 +360,10 @@ public class cgBase {
return false;
}
- setActualStatus(res.getString(R.string.init_login_popup_ok));
+ // res is null during the unit tests
+ if (res != null) {
+ setActualStatus(res.getString(R.string.init_login_popup_ok));
+ }
// on every page except login page
setActualLoginStatus(BaseUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME));
@@ -377,7 +383,10 @@ public class cgBase {
return true;
}
- setActualStatus(res.getString(R.string.init_login_popup_failed));
+ // res is null during the unit tests
+ if (res != null) {
+ setActualStatus(res.getString(R.string.init_login_popup_failed));
+ }
return false;
}
@@ -1906,7 +1915,8 @@ public class cgBase {
request.addHeader("X-Requested-With", "XMLHttpRequest");
request.addHeader("Accept", "application/json, text/javascript, */*; q=0.01");
request.addHeader("Referer", referer);
- return getResponseData(request(request));
+
+ return getResponseData(request(request), false);
}
// TODO Valentine Remove with merge
@@ -2348,9 +2358,10 @@ public class cgBase {
return getViewstates(getResponseData(response));
}
- static public String getResponseDataOnError(final HttpResponse response) {
+ static public String getResponseDataOnError(final HttpResponse response, boolean replaceWhitespace) {
try {
- return BaseUtils.replaceWhitespace(EntityUtils.toString(response.getEntity(), HTTP.UTF_8));
+ String data = EntityUtils.toString(response.getEntity(), HTTP.UTF_8);
+ return replaceWhitespace ? BaseUtils.replaceWhitespace(data) : data;
} catch (Exception e) {
Log.e(Settings.tag, "getResponseData", e);
return null;
@@ -2358,10 +2369,14 @@ public class cgBase {
}
static public String getResponseData(final HttpResponse response) {
+ return getResponseData(response, true);
+ }
+
+ static public String getResponseData(final HttpResponse response, boolean replaceWhitespace) {
if (!isSuccess(response)) {
return null;
}
- return getResponseDataOnError(response);
+ return getResponseDataOnError(response, replaceWhitespace);
}
/**
@@ -2893,6 +2908,8 @@ public class cgBase {
return false;
}
+ // TODO Valentine Remove with merge
+ @Deprecated
public static String getMapUserToken(final Handler noTokenHandler) {
final HttpResponse response = request("http://www.geocaching.com/map/default.aspx", null, false);
final String data = getResponseData(response);
diff --git a/main/src/cgeo/geocaching/connector/gc/GCBase.java b/main/src/cgeo/geocaching/connector/gc/GCBase.java
index b037366..05ed9ec 100644
--- a/main/src/cgeo/geocaching/connector/gc/GCBase.java
+++ b/main/src/cgeo/geocaching/connector/gc/GCBase.java
@@ -1,5 +1,6 @@
package cgeo.geocaching.connector.gc;
+import cgeo.geocaching.GCConstants;
import cgeo.geocaching.SearchResult;
import cgeo.geocaching.Settings;
import cgeo.geocaching.StoredList;
@@ -8,10 +9,11 @@ import cgeo.geocaching.cgCache;
import cgeo.geocaching.enumerations.CacheType;
import cgeo.geocaching.geopoint.Geopoint;
import cgeo.geocaching.geopoint.Viewport;
+import cgeo.geocaching.utils.BaseUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.http.HttpResponse;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -39,19 +41,34 @@ public class GCBase {
// TODO Valentine remove before merge
/** go online or use mocked data ? */
- public static final boolean IS_ONLINE = false;
+ public static final boolean IS_ONLINE = true;
// TODO Valentine move to connector before merge
+ /**
+ * @param viewport
+ * @param zoomlevel
+ * initial zoomlevel
+ * @autoAdjust Auto-adjust zoomlevel
+ * @param sessionToken
+ * @return
+ */
@SuppressWarnings("null")
- public static SearchResult searchByViewport(final Viewport viewport) {
+ public static SearchResult searchByViewport(final Viewport viewport, final int zoomlevel, final boolean autoAdjust, final String sessionToken) {
+
+ assert zoomlevel >= Tile.ZOOMLEVEL_MIN && zoomlevel <= Tile.ZOOMLEVEL_MAX : "zoomlevel out of bounds.";
+
+ Geopoint centerOfViewport = new Geopoint((viewport.getLatitudeMin() + viewport.getLatitudeMax()) / 2, (viewport.getLongitudeMin() + viewport.getLongitudeMax()) / 2);
+ final String referer = GCConstants.URL_LIVE_MAP +
+ "?ll=" + centerOfViewport.getLatitude() +
+ "," + centerOfViewport.getLongitude();
final SearchResult searchResult = new SearchResult();
+ searchResult.setUrl(referer);
+
+ List<Tile> tiles = getTilesForViewport(viewport, zoomlevel, autoAdjust);
- List<ImmutablePair<Integer, Integer>> tiles = getTilesForViewport(viewport);
- // TODO Valentine Use the coords from the viewport for the referer
- final String referer = "http://www.geocaching.com/map/default.aspx?ll=52.4162,9.59412";
- for (ImmutablePair<Integer, Integer> tile : tiles) {
+ for (Tile tile : tiles) {
/*
* http://www.geocaching.com/map/ --- map-url
* map.info? --- request for JSON
@@ -60,18 +77,25 @@ public class GCBase {
* z=14 --- zoom
* _=1329484185663 --- token/filter, not required
*/
- final String url = "http://www.geocaching.com/map/map.info?x=" + tile.left + "&y=" + tile.right + "&z=14";
- String page = "";
+ String url = GCConstants.URL_MAP_INFO +
+ "?x=" + tile.getX() +
+ "&y=" + tile.getY() +
+ "&z=" + tile.getZoomlevel();
+ if (StringUtils.isNotEmpty(sessionToken)) {
+ url += "&st=" + sessionToken;
+ }
+
+ String data = "";
if (IS_ONLINE) {
- page = cgBase.requestJSON(url, referer);
+ data = cgBase.requestJSON(url, referer);
} else {
- page = "{\"grid\":[\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" 04$ \",\" /5' \",\" .6& \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" %:( \",\" #;, \",\" !<) \",\" \",\" \",\" \",\" \",\" 8-1 \",\" 9+2 \",\" 7*3 \",\" \"],\"keys\":[\"\",\"55_55\",\"55_54\",\"17_25\",\"55_53\",\"17_27\",\"17_26\",\"57_53\",\"57_55\",\"3_62\",\"3_61\",\"57_54\",\"3_60\",\"15_27\",\"15_26\",\"15_25\",\"4_60\",\"4_61\",\"4_62\",\"16_25\",\"16_26\",\"16_27\",\"2_62\",\"2_60\",\"2_61\",\"56_53\",\"56_54\",\"56_55\"],\"data\":{\"55_55\":[{\"i\":\"gEaR\",\"n\":\"Spiel & Sport\"}],\"" +
+ data = "{\"grid\":[\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" 04$ \",\" /5' \",\" .6& \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" \",\" %:( \",\" #;, \",\" !<) \",\" \",\" \",\" \",\" \",\" 8-1 \",\" 9+2 \",\" 7*3 \",\" \"],\"keys\":[\"\",\"55_55\",\"55_54\",\"17_25\",\"55_53\",\"17_27\",\"17_26\",\"57_53\",\"57_55\",\"3_62\",\"3_61\",\"57_54\",\"3_60\",\"15_27\",\"15_26\",\"15_25\",\"4_60\",\"4_61\",\"4_62\",\"16_25\",\"16_26\",\"16_27\",\"2_62\",\"2_60\",\"2_61\",\"56_53\",\"56_54\",\"56_55\"],\"data\":{\"55_55\":[{\"i\":\"gEaR\",\"n\":\"Spiel & Sport\"}],\"" +
"55_54\":[{\"i\":\"gEaR\",\"n\":\"Spiel & Sport\"}],\"17_25\":[{\"i\":\"Rkzt\",\"n\":\"EDSSW: Rathaus \"}],\"55_53\":[{\"i\":\"gEaR\",\"n\":\"Spiel & Sport\"}],\"17_27\":[{\"i\":\"Rkzt\",\"n\":\"EDSSW: Rathaus \"}],\"17_26\":[{\"i\":\"Rkzt\",\"n\":\"EDSSW: Rathaus \"}],\"57_53\":[{\"i\":\"gEaR\",\"n\":\"Spiel & Sport\"}],\"57_55\":[{\"i\":\"gEaR\",\"n\":\"Spiel & Sport\"}],\"3_62\":[{\"i\":\"gOWz\",\"n\":\"Baumarktserie - Wer Wo Was -\"}],\"3_61\":[{\"i\":\"gOWz\",\"n\":\"Baumarktserie - Wer Wo Was -\"}],\"57_54\":[{\"i\":\"gEaR\",\"n\":\"Spiel & Sport\"}],\"3_60\":[{\"i\":\"gOWz\",\"n\":\"Baumarktserie - Wer Wo Was -\"}],\"15_27\":[{\"i\":\"Rkzt\",\"n\":\"EDSSW: Rathaus \"}],\"15_26\":[{\"i\":\"Rkzt\",\"n\":\"EDSSW: Rathaus \"}],\"15_25\":[{\"i\":\"Rkzt\",\"n\":\"EDSSW: Rathaus \"}],\"4_60\":[{\"i\":\"gOWz\",\"n\":\"Baumarktserie - Wer Wo Was -\"}],\"4_61\":[{\"i\":\"gOWz\",\"n\":\"Baumarktserie - Wer Wo Was -\"}],\"4_62\":[{\"i\":\"gOWz\",\"n\":\"Baumarktserie - Wer Wo Was -\"}],\"16_25\":[{\"i\":\"Rkzt\",\"n\":\"EDSSW: Rathaus \"}],\"16_26\":[{\"i\":\"Rkzt\",\"n\":\"EDSSW: Rathaus \"}],\"16_27\":[{\"i\":\"Rkzt\",\"n\":\"EDSSW: Rathaus \"}],\"2_62\":[{\"i\":\"gOWz\",\"n\":\"Baumarktserie - Wer Wo Was -\"}],\"2_60\":[{\"i\":\"gOWz\",\"n\":\"Baumarktserie - Wer Wo Was -\"}],\"2_61\":[{\"i\":\"gOWz\",\"n\":\"Baumarktserie - Wer Wo Was -\"}],\"56_53\":[{\"i\":\"gEaR\",\"n\":\"Spiel & Sport\"}],\"56_54\":[{\"i\":\"gEaR\",\"n\":\"Spiel & Sport\"}],\"56_55\":[{\"i\":\"gEaR\",\"n\":\"Spiel & Sport\"}]}}";
}
- if (StringUtils.isBlank(page)) {
- Log.e(Settings.tag, "GCBase.searchByViewport: No data from server for tile (" + tile.left + "/" + tile.right + ")");
+ if (StringUtils.isEmpty(data)) {
+ Log.e(Settings.tag, "GCBase.searchByViewport: No data from server for tile (" + tile.getX() + "/" + tile.getY() + ")");
}
- final SearchResult search = parseMapJSON(url, page);
+ final SearchResult search = parseMapJSON(data, tile);
if (search == null || CollectionUtils.isEmpty(search.getGeocodes())) {
Log.e(Settings.tag, "GCBase.searchByViewport: No cache parsed for viewport " + viewport);
}
@@ -85,18 +109,17 @@ public class GCBase {
/**
* @param url
* URL used to retrieve data.
- * @param page
+ * @param data
* Retrieved data.
* @return SearchResult. Never null.
*/
- public static SearchResult parseMapJSON(final String url, final String page) {
+ public static SearchResult parseMapJSON(final String data, Tile tile) {
final SearchResult searchResult = new SearchResult();
- searchResult.setUrl(url);
try {
- if (StringUtils.isEmpty(page)) {
+ if (StringUtils.isEmpty(data)) {
throw new JSONException("No page given");
}
@@ -106,10 +129,10 @@ public class GCBase {
// "data":{"55_55":[{"i":"gEaR","n":"Spiel & Sport"}],"55_54":[{"i":"gEaR","n":"Spiel & Sport"}],"17_25":[{"i":"Rkzt","n":"EDSSW: Rathaus "}],"55_53":[{"i":"gEaR","n":"Spiel & Sport"}],"17_27":[{"i":"Rkzt","n":"EDSSW: Rathaus "}],"17_26":[{"i":"Rkzt","n":"EDSSW: Rathaus "}],"57_53":[{"i":"gEaR","n":"Spiel & Sport"}],"57_55":[{"i":"gEaR","n":"Spiel & Sport"}],"3_62":[{"i":"gOWz","n":"Baumarktserie - Wer Wo Was -"}],"3_61":[{"i":"gOWz","n":"Baumarktserie - Wer Wo Was -"}],"57_54":[{"i":"gEaR","n":"Spiel & Sport"}],"3_60":[{"i":"gOWz","n":"Baumarktserie - Wer Wo Was -"}],"15_27":[{"i":"Rkzt","n":"EDSSW: Rathaus "}],"15_26":[{"i":"Rkzt","n":"EDSSW: Rathaus "}],"15_25":[{"i":"Rkzt","n":"EDSSW: Rathaus "}],"4_60":[{"i":"gOWz","n":"Baumarktserie - Wer Wo Was -"}],"4_61":[{"i":"gOWz","n":"Baumarktserie - Wer Wo Was -"}],"4_62":[{"i":"gOWz","n":"Baumarktserie - Wer Wo Was -"}],"16_25":[{"i":"Rkzt","n":"EDSSW: Rathaus "}],"16_26":[{"i":"Rkzt","n":"EDSSW: Rathaus "}],"16_27":[{"i":"Rkzt","n":"EDSSW: Rathaus "}],"2_62":[{"i":"gOWz","n":"Baumarktserie - Wer Wo Was -"}],"2_60":[{"i":"gOWz","n":"Baumarktserie - Wer Wo Was -"}],"2_61":[{"i":"gOWz","n":"Baumarktserie - Wer Wo Was -"}],"56_53":[{"i":"gEaR","n":"Spiel & Sport"}],"56_54":[{"i":"gEaR","n":"Spiel & Sport"}],"56_55":[{"i":"gEaR","n":"Spiel & Sport"}]}
// }
- final JSONObject json = new JSONObject(page);
+ final JSONObject json = new JSONObject(data);
final JSONArray grid = json.getJSONArray("grid");
- if (grid == null || grid.length() != 64) {
+ if (grid == null || grid.length() != (UTFGrid.GRID_MAXY + 1)) {
throw new JSONException("No grid inside JSON");
}
final JSONArray keys = json.getJSONArray("keys");
@@ -122,21 +145,29 @@ public class GCBase {
}
// attach all keys with the cache positions in the tile
- Map<String, ImmutablePair<Integer, Integer>> keyPositions = new HashMap<String, ImmutablePair<Integer, Integer>>(); // JSON key, (x/y) in grid
+ Map<String, UTFGridPosition> keyPositions = new HashMap<String, UTFGridPosition>(); // JSON key, (x/y) in grid
for (int y = 0; y < grid.length(); y++) {
- byte[] row = grid.getString(y).getBytes();
- for (int x = 0; x < row.length; x++) {
- if (row[x] != 32) {
- byte id = UTFGrid.getUTFGridId(row[x]);
- keyPositions.put(keys.getString(id), new ImmutablePair<Integer, Integer>(x, y));
+ String rowUTF8 = grid.getString(y);
+ if (rowUTF8.length() != (UTFGrid.GRID_MAXX + 1)) {
+ throw new JSONException("Grid has wrong size");
+ }
+
+ for (int x = 0; x < UTFGrid.GRID_MAXX; x++) {
+ char c = rowUTF8.charAt(x);
+ if (c != ' ') {
+ short id = UTFGrid.getUTFGridId(c);
+ keyPositions.put(keys.getString(id), new UTFGridPosition(x, y));
}
}
}
+ // Optimization:
+ // the grid can get ignored. The keys are the grid position in the format x_y
+
// iterate over the data and construct all caches in this tile
Map<String, cgCache> caches = new HashMap<String, cgCache>(); // JSON id, cache
- Map<String, List<ImmutablePair<Integer, Integer>>> positions = new HashMap<String, List<ImmutablePair<Integer, Integer>>>(); // JSON id as key
- for (int i = 0; i < keys.length(); i++) {
+ Map<String, List<UTFGridPosition>> positions = new HashMap<String, List<UTFGridPosition>>(); // JSON id as key
+ for (int i = 1; i < keys.length(); i++) { // index 0 is empty
String key = keys.getString(i);
if (StringUtils.isNotBlank(key)) {
JSONArray dataForKey = dataObject.getJSONArray(key);
@@ -155,28 +186,31 @@ public class GCBase {
caches.put(id, cache);
}
- List<ImmutablePair<Integer, Integer>> pos = positions.get(id);
+ List<UTFGridPosition> listOfPositions = positions.get(id);
+ if (listOfPositions == null) {
+ listOfPositions = new ArrayList<UTFGridPosition>();
+ }
+ UTFGridPosition pos = keyPositions.get(key);
if (pos == null) {
- pos = new ArrayList<ImmutablePair<Integer, Integer>>();
+ Log.e(Settings.tag, "key " + key + " not found in keyPositions");
+ } else {
+ listOfPositions.add(pos);
}
- pos.add(keyPositions.get(key));
- positions.put(id, pos);
+ positions.put(id, listOfPositions);
}
}
}
for (String id : positions.keySet()) {
- List<ImmutablePair<Integer, Integer>> pos = positions.get(id);
+ List<UTFGridPosition> pos = positions.get(id);
cgCache cache = caches.get(id);
- cache.setCoords(getCoordsForUTFGrid(pos));
+ cache.setCoords(getCoordsForUTFGrid(tile, pos));
- Log.d(Settings.tag, "id= " + id + " geocode= " + cache.getGeocode());
- for (ImmutablePair<Integer, Integer> ImmutablePair : pos) {
- Log.d(Settings.tag, "(" + ImmutablePair.left + "," + ImmutablePair.right + ")");
- }
+ Log.d(Settings.tag, "id=" + id + " geocode=" + cache.getGeocode() + " coords=" + cache.getCoords().toString());
searchResult.addCache(cache);
}
+ Log.d(Settings.tag, "Retrieved " + searchResult.getCount() + " caches for tile " + tile.toString());
} catch (Exception e) {
Log.e(Settings.tag, "GCBase.parseMapJSON", e);
@@ -186,24 +220,58 @@ public class GCBase {
}
/**
- * Calculate tiles for the given viewport
+ * Calculate needed tiles for the given viewport
*
* @param viewport
* @return
*/
- protected static List<ImmutablePair<Integer, Integer>> getTilesForViewport(Viewport viewport) {
- // TODO Valentine Calculate tile number
- ImmutablePair<Integer, Integer> tile = new ImmutablePair<Integer, Integer>(8633, 5381); // = N 52° 24,516 E 009° 42,592
- List<ImmutablePair<Integer, Integer>> tiles = new ArrayList<ImmutablePair<Integer, Integer>>();
- tiles.add(tile);
- return tiles;
+ protected static List<Tile> getTilesForViewport(final Viewport viewport, final int zoomlevel, final boolean autoAdjust) {
+ Tile tileBottomLeft = new Tile(viewport.bottomLeft, zoomlevel);
+ Tile tileTopRight = new Tile(viewport.topRight, zoomlevel);
+
+ int minX = Math.min(tileBottomLeft.getX(), tileTopRight.getX());
+ int maxX = Math.max(tileBottomLeft.getX(), tileTopRight.getX());
+ int minY = Math.min(tileBottomLeft.getY(), tileTopRight.getY());
+ int maxY = Math.max(tileBottomLeft.getY(), tileTopRight.getY());
+
+ // The recursion is a compromise between number of requests and precision.
+ // The smaller the zoomlevel the smaller the number of requests the more inaccurate the coords are
+ // The bigger the zoomlevel the bigger the number of requests the more accurate the coords are
+ // For really usable coords a zoomlevel >= 13 is required
+ if (autoAdjust && zoomlevel >= Tile.ZOOMLEVEL_MIN && ((maxX - minX + 1) * (maxY - minY + 1) > 4)) {
+ return getTilesForViewport(viewport, zoomlevel - 1, autoAdjust);
+ }
+
+ List<Tile> tiles = new ArrayList<Tile>();
+
+ if (tileBottomLeft.getX() == tileTopRight.getX() &&
+ tileBottomLeft.getY() == tileTopRight.getY()) {
+ tiles.add(tileBottomLeft);
+ return tiles;
+ }
+ for (int x = minX; x <= maxX; x++) {
+ for (int y = minY; y <= maxY; y++) {
+ tiles.add(new Tile(x, y, zoomlevel));
+ }
+ }
+ Log.d(Settings.tag, "# tiles=" + tiles.size() + " " + minX + "/" + minY + " " + maxX + "/" + maxY);
+ return tiles;
}
/** Calculate from a list of positions (x/y) the coords */
- protected static Geopoint getCoordsForUTFGrid(List<ImmutablePair<Integer, Integer>> positions) {
- // TODO Valentine Calculate coords
- return new Geopoint("N 52° 24,516 E 009° 42,592");
+ protected static Geopoint getCoordsForUTFGrid(Tile tile, List<UTFGridPosition> positions) {
+ int minX = UTFGrid.GRID_MAXX;
+ int maxX = 0;
+ int minY = UTFGrid.GRID_MAXY;
+ int maxY = 0;
+ for (UTFGridPosition pos : positions) {
+ minX = Math.min(minX, pos.x);
+ maxX = Math.max(maxX, pos.x);
+ minY = Math.min(minY, pos.y);
+ maxY = Math.max(maxY, pos.y);
+ }
+ return tile.getCoord(new UTFGridPosition((minX + maxX) / 2, (minY + maxY) / 2));
}
/**
@@ -287,6 +355,7 @@ public class GCBase {
return GCBase.gcidToGCCode(gcid);
}
+ // TODO Valentine
/** Request further details in the live mapa for a given id */
public void requestDetailsFromMap(@SuppressWarnings("unused") String id) {
/**
@@ -301,4 +370,11 @@ public class GCBase {
*/
}
+ /** Get session token from the Live Map. Needed for following requests */
+ public static String getSessionToken() {
+ final HttpResponse response = cgBase.request(GCConstants.URL_LIVE_MAP, null, false);
+ final String data = cgBase.getResponseData(response);
+ return BaseUtils.getMatch(data, GCConstants.PATTERN_SESSIONTOKEN, "");
+ }
+
}
diff --git a/main/src/cgeo/geocaching/connector/gc/Tile.java b/main/src/cgeo/geocaching/connector/gc/Tile.java
index f036f59..40efcff 100644
--- a/main/src/cgeo/geocaching/connector/gc/Tile.java
+++ b/main/src/cgeo/geocaching/connector/gc/Tile.java
@@ -14,6 +14,8 @@ import cgeo.geocaching.geopoint.Geopoint;
public class Tile {
public static final int TILE_SIZE = 256;
+ public static final int ZOOMLEVEL_MAX = 18;
+ public static final int ZOOMLEVEL_MIN = 0;
private final int tileX;
private final int tileY;
@@ -66,21 +68,25 @@ public class Tile {
return (int) ((1 - (Math.log(Math.tan(lat_rad) + (1 / Math.cos(lat_rad))) / Math.PI)) / 2 * numberOfTiles);
}
- /** Calculate latitude/longitude for a given x/y position in this tile. */
+ /**
+ * Calculate latitude/longitude for a given x/y position in this tile.
+ *
+ * @see http://developers.cloudmade.com/projects/tiles/examples/convert-coordinates-to-tile-numbers
+ */
public Geopoint getCoord(UTFGridPosition pos) {
- long numberOfPixels = TILE_SIZE * numberOfTiles;
-
double pixX = tileX * TILE_SIZE + pos.x * 4;
double pixY = tileY * TILE_SIZE + pos.y * 4;
-
- pixY += -1 * numberOfPixels / 2;
- double radius = numberOfPixels / (2.0 * Math.PI);
- double lat = (Math.PI / 2) - (2 * Math.atan(Math.exp(-1.0 * pixY / radius)));
- lat = -1 * Math.toDegrees(lat);
+ long numberOfPixels = TILE_SIZE * numberOfTiles;
double lon = ((360.0 * pixX) / numberOfPixels) - 180.0;
- return new Geopoint(lat, lon);
+ double latRad = Math.atan(Math.sinh(Math.PI * (1 - 2 * pixY / numberOfPixels)));
+ return new Geopoint(latRad * Geopoint.rad2deg, lon);
}
+ @Override
+ public String toString() {
+ return String.format("(%d/%d), zoom=%d", tileX, tileY, zoomlevel).toString();
+
+ }
}
diff --git a/main/src/cgeo/geocaching/connector/gc/UTFGrid.java b/main/src/cgeo/geocaching/connector/gc/UTFGrid.java
index 9e635c9..17f61da 100644
--- a/main/src/cgeo/geocaching/connector/gc/UTFGrid.java
+++ b/main/src/cgeo/geocaching/connector/gc/UTFGrid.java
@@ -5,20 +5,23 @@ package cgeo.geocaching.connector.gc;
* @author blafoo
*
* @see https://github.com/mapbox/mbtiles-spec/blob/master/1.1/utfgrid.md
- *
+ *
*/
-public class UTFGrid {
+public final class UTFGrid {
+
+ public static final int GRID_MAXX = 63;
+ public static final int GRID_MAXY = 63;
/** Convert a value from a JSON grid object into an id that can be used as an index */
- public static byte getUTFGridId(final byte value) {
- byte result = value;
+ public static short getUTFGridId(final char value) {
+ short result = (short) value;
if (result >= 93) {
result--;
}
if (result >= 35) {
result--;
}
- return (byte) (result - 32);
+ return (short) (result - 32);
}
}
diff --git a/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java b/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java
index cbd7696..9d875c5 100644
--- a/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java
+++ b/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java
@@ -1,5 +1,9 @@
package cgeo.geocaching.connector.gc;
+import cgeo.geocaching.Settings;
+
+import android.util.Log;
+
/**
* Representation of a position inside an UTFGrid
*
@@ -13,9 +17,11 @@ public final class UTFGridPosition {
UTFGridPosition(final int x, final int y) {
if (x > UTFGrid.GRID_MAXX) {
+ Log.e(Settings.tag, "x outside grid");
throw new IllegalArgumentException("x outside grid");
}
if (y > UTFGrid.GRID_MAXY) {
+ Log.e(Settings.tag, "y outside grid");
throw new IllegalArgumentException("y outside grid");
}
this.x = x;
diff --git a/main/src/cgeo/geocaching/enumerations/CacheType.java b/main/src/cgeo/geocaching/enumerations/CacheType.java
index fd1b926..ea89135 100644
--- a/main/src/cgeo/geocaching/enumerations/CacheType.java
+++ b/main/src/cgeo/geocaching/enumerations/CacheType.java
@@ -29,8 +29,8 @@ public enum CacheType {
PROJECT_APE("ape", "project ape cache", "2555690d-b2bc-4b55-b5ac-0cb704c0b768", R.string.ape, R.drawable.type_ape),
GCHQ("gchq", "groundspeak hq", "416f2494-dc17-4b6a-9bab-1a29dd292d8c", R.string.gchq, R.drawable.type_hq),
GPS_EXHIBIT("gps", "gps cache exhibit", "72e69af2-7986-4990-afd9-bc16cbbb4ce3", R.string.gps, R.drawable.type_traditional), // icon missing
- // TODO Valentine create new drawable
- GC_LIVE_MAP("live map", "live map", "", R.string.unknown, R.drawable.type_mystery),
+ // GC Live Map
+ GC_LIVE_MAP("live map", "live map", "", R.string.unknown, R.drawable.type_unknown),
UNKNOWN("unknown", "unknown", "", R.string.unknown, R.drawable.type_mystery), // icon missing
/** No real cache type -> filter */
ALL("all", "display all caches", "9a79e6ce-3344-409c-bbe9-496530baf758", R.string.all_types, R.drawable.type_mystery);
diff --git a/main/src/cgeo/geocaching/maps/CGeoMap.java b/main/src/cgeo/geocaching/maps/CGeoMap.java
index b15d54c..8221e61 100644
--- a/main/src/cgeo/geocaching/maps/CGeoMap.java
+++ b/main/src/cgeo/geocaching/maps/CGeoMap.java
@@ -15,6 +15,7 @@ import cgeo.geocaching.cgWaypoint;
import cgeo.geocaching.cgeoapplication;
import cgeo.geocaching.cgeocaches;
import cgeo.geocaching.activity.ActivityMixin;
+import cgeo.geocaching.connector.gc.GCBase;
import cgeo.geocaching.enumerations.CacheType;
import cgeo.geocaching.enumerations.LoadFlags;
import cgeo.geocaching.enumerations.StatusCode;
@@ -1265,7 +1266,8 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto
}
if (token == null) {
- token = cgBase.getMapUserToken(noMapTokenHandler);
+ // token = cgBase.getMapUserToken(noMapTokenHandler);
+ token = GCBase.getSessionToken();
}
if (stop) {
@@ -1273,7 +1275,8 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto
}
final Viewport viewport = new Viewport(new Geopoint(latMin, lonMin), new Geopoint(latMax, lonMax));
- search = cgBase.searchByViewport(token, viewport);
+ // search = cgBase.searchByViewport(token, viewport);
+ search = GCBase.searchByViewport(viewport, 14, true, token);
if (search != null) {
downloaded = true;
if (search.error == StatusCode.NOT_LOGGED_IN) {
diff --git a/tests/src/cgeo/geocaching/connector/GCConnectorTest.java b/tests/src/cgeo/geocaching/connector/GCConnectorTest.java
index 072d163..93ba5d2 100644
--- a/tests/src/cgeo/geocaching/connector/GCConnectorTest.java
+++ b/tests/src/cgeo/geocaching/connector/GCConnectorTest.java
@@ -1,9 +1,11 @@
package cgeo.geocaching.connector;
import cgeo.geocaching.SearchResult;
+import cgeo.geocaching.cgBase;
import cgeo.geocaching.connector.gc.GCBase;
+import cgeo.geocaching.connector.gc.Tile;
+import cgeo.geocaching.geopoint.Geopoint;
import cgeo.geocaching.geopoint.Viewport;
-import cgeo.geocaching.test.mock.GC2JVEH;
import android.test.AndroidTestCase;
@@ -12,12 +14,31 @@ public class GCConnectorTest extends AndroidTestCase {
@SuppressWarnings("null")
public static void testGetViewport() {
- GC2JVEH cache = new GC2JVEH();
- final Viewport viewport = new Viewport(cache.getCoords(), 1.0, 1.0);
- SearchResult searchResult = GCBase.searchByViewport(viewport);
- assertTrue(searchResult != null);
- assertEquals(3, searchResult.getCount());
- assertTrue(searchResult.getGeocodes().contains("GC211WG"));
+ cgBase.login();
+
+ String sessionToken = GCBase.getSessionToken();
+
+ {
+ final Viewport viewport = new Viewport(new Geopoint("N 52° 25.369 E 9° 35.499"), new Geopoint("N 52° 25.371 E 9° 35.501"));
+ SearchResult searchResult = GCBase.searchByViewport(viewport, 14, false, sessionToken);
+ assertTrue(searchResult != null);
+ assertEquals(7, searchResult.getCount());
+ assertTrue(searchResult.getGeocodes().contains("GC211WG"));
+
+ /*
+ * Baumarktserie GC21F1W N 52° 25.370 E 009° 35.500
+ * Rathaus GC1J1CT N 52° 25.590 E 009° 35.636
+ * Spiel & Sport GC211WG N 52° 25.413 E 009° 36.049
+ */
+ }
+
+ {
+ final Viewport viewport = new Viewport(new Geopoint("N 52° 24.000 E 9° 34.500"), new Geopoint("N 52° 26.000 E 9° 38.500"));
+ SearchResult searchResult = GCBase.searchByViewport(viewport, 14, false, sessionToken);
+ assertTrue(searchResult != null);
+ assertTrue(searchResult.getGeocodes().contains("GC211WG"));
+ }
+
}
public static void testBaseCodings() {
@@ -29,4 +50,23 @@ public class GCConnectorTest extends AndroidTestCase {
assertEquals("GC211WG", GCBase.newidToGeocode("gEaR"));
}
+ public static void testTile() {
+ {
+ // http://coord.info/GC2CT8K = N 52° 30.462 E 013° 27.906
+ Tile tile = new Tile(new Geopoint(52.5077, 13.4651), 14);
+ assertEquals(8804, tile.getX());
+ assertEquals(5374, tile.getY());
+ }
+
+ {
+ // (8633, 5381); N 52° 24,516 E 009° 42,592
+ Tile tile = new Tile(new Geopoint("N 52° 24,516 E 009° 42,592"), 14);
+ assertEquals(8633, tile.getX());
+ assertEquals(5381, tile.getY());
+ }
+
+ // TODO Valentine zoomlevel != 14, Seatle, Rio, Sydney
+ }
+
}
+