diff options
| author | blafoo <github@blafoo.de> | 2012-02-21 21:40:52 +0100 |
|---|---|---|
| committer | blafoo <github@blafoo.de> | 2012-02-23 23:21:04 +0100 |
| commit | 36d8df2e25e5f2b6551dd00fd579aa5c0d092c62 (patch) | |
| tree | 262924c2e3c3b4a6ab5034e69b5eae14e0facb83 | |
| parent | 473d76b8a064549094c5c777acf013d205b0f720 (diff) | |
| download | cgeo-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.java | 8 | ||||
| -rw-r--r-- | main/src/cgeo/geocaching/cgBase.java | 31 | ||||
| -rw-r--r-- | main/src/cgeo/geocaching/connector/gc/GCBase.java | 172 | ||||
| -rw-r--r-- | main/src/cgeo/geocaching/connector/gc/Tile.java | 24 | ||||
| -rw-r--r-- | main/src/cgeo/geocaching/connector/gc/UTFGrid.java | 13 | ||||
| -rw-r--r-- | main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java | 6 | ||||
| -rw-r--r-- | main/src/cgeo/geocaching/enumerations/CacheType.java | 4 | ||||
| -rw-r--r-- | main/src/cgeo/geocaching/maps/CGeoMap.java | 7 | ||||
| -rw-r--r-- | tests/src/cgeo/geocaching/connector/GCConnectorTest.java | 54 |
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 + } + } + |
