/* * Copyright 2010, 2011, 2012 mapsforge.org * * This program is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see <http://www.gnu.org/licenses/>. */ package eu.geopaparazzi.mapsforge.databasehandlers.core; import java.io.File; import java.io.IOException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import org.mapsforge.android.maps.mapgenerator.MapGeneratorJob; import org.mapsforge.android.maps.mapgenerator.tiledownloader.TileDownloader; import org.mapsforge.core.model.GeoPoint; import org.mapsforge.core.model.Tile; import android.content.Context; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; import android.preference.PreferenceManager; import eu.geopaparazzi.library.GPApplication; import eu.geopaparazzi.library.database.GPLog; import eu.geopaparazzi.library.network.NetworkUtilities; import eu.geopaparazzi.library.util.FileUtilities; import eu.geopaparazzi.library.util.MercatorUtils; import eu.geopaparazzi.spatialite.database.spatial.core.databasehandlers.MbtilesDatabaseHandler; /** * A MapGenerator that downloads tiles from the Mapnik server at OpenStreetMap. */ @SuppressWarnings("nls") public class CustomTileDownloader extends TileDownloader { private static final int ZOOM_LEVEL_DIFF = 1; private static final String YYY_STR = "YYY"; private static final String XXX_STR = "XXX"; private static final String ZZZ_STR = "ZZZ"; private static final String SSS_STR = "SSS"; private static final String URL_STR = "url"; private static final String GEOPAPARAZZI_STR = "Geopaparazzi"; private static final String USER_AGENT_STR = "User-Agent"; private static final String HTTP_STR = "http"; private static final String HTTPS_STR = "https"; //private static final String FILE_PROTOCOL_STR = "file:"; private static final String REQUEST_PROTOCOL_STR = "request_protocol"; private static final String REQUEST_Y_TYPE_STR = "request_y_type"; private static final String REQUEST_ZOOM_LEVELS_URL_STR = "request_zoom_levels_url"; private static final String REQUEST_BOUNDS_URL_STR = "request_bounds_url"; private static final String REQUEST_URL_STR = "request_url"; private static final String FILE_STR = "file"; private static final String LOAD_STR = "load"; private static final String DELETE_STR = "delete"; private static final String RESET_METADATA_STR = "reset_metadata"; private static final String UPDATE_BOUNDS_STR = "update_bounds"; private static final String VACUUM_STR = "vacuum"; private static final String DROP_STR = "drop"; private static final String REPLACE_STR = "replace"; private static final String FILL_STR = "fill"; private static final String RUN_STR = "run"; private static final String REQUEST_ZOOM_LEVELS_STR = "request_zoom_levels"; private static final String REQUEST_BOUNDS_STR = "request_bounds"; private static final String REQUEST_TYPE_STR = "request_type"; private static final String FORCE_UNIQUE_STR = "force_unique"; private static final String DEFAULTZOOM_STR = "defaultzoom"; private static final String TILE_ROW_TYPE_STR = "tile_row_type"; private static final String MBTILES_STR = "mbtiles"; private static final String TYPE_STR = "type"; private static final String MAXZOOM_STR = "maxzoom"; private static final String MINZOOM_STR = "minzoom"; private static final String CENTER_STR = "center"; private static final String BOUNDS_STR = "bounds"; private static final String FORMAT_STR = "format"; private static final String NAME_STR = "name"; private static final String DESCRIPTION_STR = "description"; /** * Possible schemas */ private enum TILESCHEMA { tms, google, wms, osm } // no wonder this was causing problems, must must NOT be static with a manager private String HOST_NAME = ""; private String PROTOCOL = HTTP_STR; //$NON-NLS-1$ private byte ZOOM_MIN = 0; private byte ZOOM_MAX = 22; private byte ZOOM_DEFAULT = 14; // mbtiles specific private final int minZoom; private final int maxZoom; private final int defaultZoom; // mbtiles specific private final double centerX; // wsg84 private final double centerY; // wsg84 private final double boundsWest; // wsg84 private final double boundsEast; // wsg84 private final double boundsNorth; // wsg84 private final double boundsSouth; // wsg84 private String name; // mbtiles specific private String description; // mbtiles specific private String format; // mbtiles specific private int i_force_unique = 0; private MbtilesDatabaseHandler mbtilesDatabase = null; private int i_tile_server = 0; // if no 'SSS' is found, server logic will not be called private GeoPoint centerPoint = new GeoPoint(0, 0); private String tilePart = ""; private boolean isFile = false; private TILESCHEMA type = TILESCHEMA.google; private boolean isConnectedToInternet; private boolean doScaleTiles; private SharedPreferences preferences; /** * Constructor. * * @param sourceFile the source file to use as tile source definition. * @throws IOException if something goes wrong. */ public CustomTileDownloader(File sourceFile) throws IOException { super(); String parentPath = sourceFile.getParentFile().getAbsolutePath(); Context context = GPApplication.getInstance(); preferences = PreferenceManager.getDefaultSharedPreferences(context); this.name = sourceFile.getName().substring(0, sourceFile.getName().lastIndexOf(".")); List<String> fileLines = new ArrayList<>(); try { fileLines = FileUtilities.readfileToList(sourceFile); } catch (IOException e) { GPLog.error(this, getClass().getSimpleName() + "[CustomTileDownloader.FileUtilities.readfileToList]", e); } // parentPath = '/mnt/sdcard/maps' : this will be appended to all pathis given in the // 'mapurl' file double[] bounds = new double[]{-180.0, -85.05113, 180, 85.05113}; double[] center = {0.0, 0.0}; double[] request_bounds = new double[]{0.0, 0.0, 0.0, 0.0}; String mbtilesFilePath = ""; HashMap<String, String> mbtilesMetadataMap = new LinkedHashMap<>(); HashMap<String, String> mbtilesRequestUrl = new LinkedHashMap<>(); if (GPLog.LOG_ABSURD) { StringBuilder sb = new StringBuilder(); sb.append("CustomTileDownloader called with:\n"); sb.append("parentPath: "); sb.append(parentPath); sb.append("\n"); for (String fileLine : fileLines) { sb.append("-> " + fileLine); sb.append("\n"); } GPLog.addLogEntry(sb.toString()); } File mbtilesFile = null; String tileRowType = "tms"; String requestType = ""; String requestUrl = ""; String requestBounds = ""; String requestedZoomLevels = ""; for (String line : fileLines) { line = line.trim(); if (line.length() == 0) { continue; } int split = line.indexOf('='); if (split != -1) { String parm = line.substring(0, split); String value = line.substring(split + 1).trim(); if (line.startsWith(URL_STR)) { requestUrl = value; int indexOfS = value.indexOf(SSS_STR); if (indexOfS != -1) { i_tile_server = 1; // Server logic will not be called [1,2] } int indexOfZ = value.indexOf(ZZZ_STR); if (indexOfZ != -1) { // tile_servers and local files [order of ZZZ,XXX,YY is no longer inportant] // url=http://mt1.google.com/vt/lyrs=s,h&x=XXX&y=YYY&z=ZZZ // url=mytilesfolder/ZZZ/XXX/YYY.png // url=http://tile.openstreetmap.org/ZZZ/XXX/YYY.png String s_work = value; if (value.startsWith(HTTPS_STR)) { PROTOCOL = HTTPS_STR; s_work = value.substring(8); } else if (value.startsWith(HTTP_STR)) { PROTOCOL = HTTP_STR; s_work = value.substring(7); } int indexOfSeperator = s_work.indexOf("/"); // tms_servers and local files will always have a '/' in them HOST_NAME = s_work.substring(0, indexOfSeperator); tilePart = s_work.substring(indexOfSeperator); if (!value.startsWith(HTTP_STR)) { // local files PROTOCOL = FILE_STR; HOST_NAME = parentPath + File.separator + HOST_NAME; isFile = true; } } else { int indexOfParms = value.indexOf("?"); // wms server should always have a '?' in them if (value.startsWith(HTTPS_STR)) { HOST_NAME = value.substring(8, indexOfParms); // removed: 'https://' PROTOCOL = HTTPS_STR; } else if (value.startsWith(HTTP_STR)) { HOST_NAME = value.substring(7, indexOfParms); // removed: 'http://' PROTOCOL = HTTP_STR; } tilePart = value.substring(indexOfParms); } } if (line.startsWith(MINZOOM_STR)) { try { byte b_zoom = Byte.valueOf(value); if ((b_zoom >= 0) && (b_zoom <= 22)) { ZOOM_MIN = b_zoom; } } catch (Exception e) { GPLog.error(this, null, e); } } if (line.startsWith(MAXZOOM_STR)) { try { byte b_zoom = Byte.valueOf(value); if ((b_zoom >= 0) && (b_zoom <= 22)) { ZOOM_MAX = b_zoom; } } catch (Exception e) { GPLog.error(this, null, e); } } if (line.startsWith(CENTER_STR)) { try { String[] coord = value.split("\\s+"); //$NON-NLS-1$ double x = Double.parseDouble(coord[0]); double y = Double.parseDouble(coord[1]); center[0] = x; center[1] = y; centerPoint = new GeoPoint(y, x); } catch (NumberFormatException e) { GPLog.error(this, null, e); } } if (line.startsWith(TYPE_STR)) { if (value.equals(TILESCHEMA.tms.toString())) { type = TILESCHEMA.tms; } if (value.equals(TILESCHEMA.wms.toString())) { type = TILESCHEMA.wms; } } if (line.startsWith(MBTILES_STR)) { // HOST_NAME = parentPath + File.separator + HOST_NAME; if (value.startsWith(File.separator)) { value = value.substring(1, value.length() - 2); } mbtilesFilePath = parentPath + File.separator + value; // GPLog.androidLog(-1,"CustomTileDownloader[mbtiles] s_mbtiles_file["+s_mbtiles_file+"]"); if (mbtilesFilePath.length() > 0) { mbtilesFile = new File(mbtilesFilePath); } } if (line.startsWith(BOUNDS_STR)) { try { String[] coord = value.split("\\s+"); //$NON-NLS-1$ bounds[0] = Double.parseDouble(coord[0]); bounds[1] = Double.parseDouble(coord[1]); bounds[2] = Double.parseDouble(coord[2]); bounds[3] = Double.parseDouble(coord[3]); } catch (NumberFormatException e) { bounds = new double[]{-180.0, -85.05113, 180, 85.05113}; } } if (line.startsWith(NAME_STR)) { this.name = value; } if (line.startsWith(DESCRIPTION_STR)) { this.description = value; } if (line.startsWith(FORMAT_STR)) { this.format = value; } if (line.startsWith(TILE_ROW_TYPE_STR)) { if (value.equals(TILESCHEMA.tms.toString()) || value.equals(TILESCHEMA.osm.toString())) { tileRowType = value; } } if (line.startsWith(DEFAULTZOOM_STR)) { try { byte b_zoom = Byte.valueOf(value); if ((b_zoom >= 0) && (b_zoom <= 22)) { ZOOM_DEFAULT = b_zoom; } } catch (Exception e) { GPLog.error(this, null, e); } } if (line.startsWith(FORCE_UNIQUE_STR)) { // will force mbtiles to check image is // unique per insert [blank images are // already determined and not checked] try { i_force_unique = Integer.parseInt(value); if ((i_force_unique < 0) || (i_force_unique > 1)) i_force_unique = 0; } catch (Exception e) { i_force_unique = 0; } } if (line.startsWith(REQUEST_TYPE_STR)) { if (!value.equals("off")) requestType = value; } if (line.startsWith(REQUEST_BOUNDS_STR)) { requestBounds = value; try { String[] coord = value.split("\\s+"); //$NON-NLS-1$ request_bounds[0] = Double.parseDouble(coord[0]); request_bounds[1] = Double.parseDouble(coord[1]); request_bounds[2] = Double.parseDouble(coord[2]); request_bounds[3] = Double.parseDouble(coord[3]); } catch (NumberFormatException e) { GPLog.error(this, null, e); requestBounds = ""; } } if (line.startsWith(REQUEST_ZOOM_LEVELS_STR)) { requestedZoomLevels = value; } } } this.centerX = center[0]; this.centerY = center[1]; this.boundsWest = bounds[0]; this.boundsSouth = bounds[1]; this.boundsEast = bounds[2]; this.boundsNorth = bounds[3]; if (ZOOM_MIN > ZOOM_MAX) { byte b_zoom = ZOOM_MIN; ZOOM_MIN = ZOOM_MAX; ZOOM_MAX = b_zoom; } this.minZoom = ZOOM_MIN; this.maxZoom = ZOOM_MAX; if (ZOOM_MIN > ZOOM_DEFAULT) ZOOM_DEFAULT = ZOOM_MIN; this.defaultZoom = ZOOM_DEFAULT; setDescription(this.description); if (mbtilesFilePath.length() > 0) { boolean doResetMetadata = false; if (!requestType.equals("")) { int run = 0; int create = 0; int indexOfS = requestType.indexOf(","); if (indexOfS != -1) { String[] requestTypeTokens = requestType.split(","); requestType = ""; String comma = ""; for (int i = 0; i < requestTypeTokens.length; i++) { if (requestTypeTokens[i].equals(RUN_STR)) { // no 'run', no fun [ignore all commands] run++; } if (requestTypeTokens[i].equals(FILL_STR)) { // will request missing tiles only if (!requestType.equals("")) comma = ","; requestType += comma + requestTypeTokens[i].trim(); if (requestedZoomLevels.equals("")) { // if not set, do all requestedZoomLevels = Integer.toString(this.minZoom) + "-" + Integer.toString(this.maxZoom); } create = 1; } if (requestTypeTokens[i].equals(REPLACE_STR)) { // will replace existing tiles if (!requestType.equals("")) comma = ","; requestType += comma + requestTypeTokens[i].trim(); create = 1; // if both 'fill' and 'replace' are given: 'fill' will // be used } if (requestTypeTokens[i].equals(DROP_STR)) { // will delete the requested tiles, // retaining the already downloaded // tiles if (!requestType.equals("")) comma = ","; requestType += comma + requestTypeTokens[i].trim(); } if (requestTypeTokens[i].equals(VACUUM_STR)) { if (!requestType.equals("")) comma = ","; requestType += comma + requestTypeTokens[i].trim(); } if (requestTypeTokens[i].equals(UPDATE_BOUNDS_STR)) { if (!requestType.equals("")) comma = ","; requestType += comma + requestTypeTokens[i].trim(); } if (requestTypeTokens[i].equals(RESET_METADATA_STR)) { doResetMetadata = true; } if (requestTypeTokens[i].equals(DELETE_STR)) { // planned for future if (!requestType.equals("")) comma = ","; requestType += comma + requestTypeTokens[i].trim(); } if (requestTypeTokens[i].equals(LOAD_STR)) { if (!requestType.equals("")) comma = ","; requestType += comma + requestTypeTokens[i].trim(); } // GPLog.androidLog(-1, "CustomTileDownloader sa_string[" + i + "].[" + // sa_string[i] + "] ["+ s_request_type + "]"); } if (create != 1) { requestBounds = ""; requestedZoomLevels = ""; requestUrl = ""; } } if (run > 0) { mbtilesRequestUrl.put(REQUEST_TYPE_STR, requestType); } if ((!requestedZoomLevels.equals("")) && (!requestUrl.equals(""))) { String s_bbox = this.boundsWest + "," + this.boundsSouth + "," + this.boundsEast + "," + this.boundsNorth; if (!requestBounds.equals("")) { requestBounds = request_bounds[0] + "," + request_bounds[1] + "," + request_bounds[2] + "," + request_bounds[3]; } else { // simplify filling of upper zoom-levels, fill supported area requestBounds = s_bbox; request_bounds = bounds; } if ((request_bounds[0] >= bounds[0]) && (request_bounds[2] <= bounds[2]) && (request_bounds[1] >= bounds[1]) && (request_bounds[3] <= bounds[3])) { if (PROTOCOL.equals(FILE_STR)) { // this must be the absolute path ; 'file:' // will be added later after checking if the // file exists requestUrl = parentPath + File.separator + requestUrl; } mbtilesRequestUrl.put(REQUEST_URL_STR, requestUrl); mbtilesRequestUrl.put(REQUEST_BOUNDS_STR, requestBounds); mbtilesRequestUrl.put(REQUEST_BOUNDS_URL_STR, s_bbox); s_bbox = Integer.toString(this.minZoom) + "-" + Integer.toString(this.maxZoom); mbtilesRequestUrl.put(REQUEST_ZOOM_LEVELS_URL_STR, s_bbox); mbtilesRequestUrl.put(REQUEST_ZOOM_LEVELS_STR, requestedZoomLevels); String requestYType = TILESCHEMA.wms.toString(); // 0=osm ; 1=tms ; 2=wms if (type == TILESCHEMA.google) { requestYType = TILESCHEMA.osm.toString(); } else if (type == TILESCHEMA.tms) { requestYType = TILESCHEMA.tms.toString(); } mbtilesRequestUrl.put(REQUEST_Y_TYPE_STR, requestYType); mbtilesRequestUrl.put(REQUEST_PROTOCOL_STR, PROTOCOL); // GPLog.androidLog(-1, "CustomTileDownloader [" + PROTOCOL + "] ["+ // s_request_url + "]"); } } } if (!mbtilesFile.exists() || doResetMetadata) { mbtilesMetadataMap.put(NAME_STR, this.name); mbtilesMetadataMap.put(DESCRIPTION_STR, this.description); if (!mbtilesFile.exists()) { mbtilesMetadataMap.put(FORMAT_STR, this.format); mbtilesMetadataMap.put(TILE_ROW_TYPE_STR, tileRowType); } // 'reset_metadata': will manually reset the mbtiles-metadata entries: // 'name','description','bounds','center','minzoom','maxzoom' ; NOT: // 'format','tile_row_type' String s_bbox = this.boundsWest + "," + this.boundsSouth + "," + this.boundsEast + "," + this.boundsNorth; mbtilesMetadataMap.put(BOUNDS_STR, s_bbox); s_bbox = this.centerX + "," + this.centerY + "," + this.defaultZoom; mbtilesMetadataMap.put(CENTER_STR, s_bbox); mbtilesMetadataMap.put(MINZOOM_STR, Integer.toString(this.minZoom)); mbtilesMetadataMap.put(MAXZOOM_STR, Integer.toString(this.maxZoom)); } if (mbtilesFile.exists()) { // this will open an existing mbtiles_db mbtilesDatabase = new MbtilesDatabaseHandler(mbtilesFile.getAbsolutePath(), null); } else { // this will create the mbtiles_db and set default values mbtilesDatabase = new MbtilesDatabaseHandler(mbtilesFile.getAbsolutePath(), mbtilesMetadataMap); } if (mbtilesRequestUrl.size() > 0) { mbtilesDatabase.runRetrieveUrl(mbtilesRequestUrl, mbtilesMetadataMap); } } // GPLog.androidLog(-1,"CustomTileDownloader parentPath[" + parentPath+ "]"); } public String getHostName() { return HOST_NAME; } /** * @return the tile related part of the request. */ public String getTilePart() { return tilePart; } public String getProtocol() { return PROTOCOL; } @Override public GeoPoint getStartPoint() { return centerPoint; } @Override public Byte getStartZoomLevel() { return ZOOM_MIN; } /** * Return Min Zoom * <p> * <p>default : 0 * <p>mbtiles : taken from value of metadata 'minzoom' * <p>map : value is given in 'StartZoomLevel' * * @return integer minzoom */ public int getMinZoom() { return minZoom; } /** * Return Max Zoom * <p> * <p>default : 22 * <p>mbtiles : taken from value of metadata 'maxzoom' * <p>map : value not defined, seems to calculate bitmap from vector data [18] * * @return integer maxzoom */ public int getMaxZoom() { return maxZoom; } /** * Return West X Value [Longitude] * <p> * <p>default : -180.0 [if not otherwise set] * <p>mbtiles : taken from 1st value of metadata 'bounds' * * @return double of West X Value [Longitude] */ public double getMinLongitude() { return boundsWest; } /** * Return South Y Value [Latitude] * <p> * <p>default : -85.05113 [if not otherwise set] * <p>mbtiles : taken from 2nd value of metadata 'bounds' * * @return double of South Y Value [Latitude] */ public double getMinLatitude() { return boundsSouth; } /** * Return East X Value [Longitude] * <p> * <p>default : 180.0 [if not otherwise set] * <p>mbtiles : taken from 3th value of metadata 'bounds' * * @return double of East X Value [Longitude] */ public double getMaxLongitude() { return boundsEast; } /** * Return North Y Value [Latitude] * <p> * <p>default : 85.05113 [if not otherwise set] * <p>mbtiles : taken from 4th value of metadata 'bounds' * * @return double of North Y Value [Latitude] */ public double getMaxLatitude() { return boundsNorth; } /** * Return Center X Value [Longitude] * <p> * <p>default : center of bounds * <p>mbtiles : taken from 1st value of metadata 'center' * * @return double of X Value [Longitude] */ public double getCenterX() { return centerX; } /** * Return Center Y Value [Latitude] * <p> * <p>default : center of bounds * <p>mbtiles : taken from 2nd value of metadata 'center' * * @return double of Y Value [Latitude] */ public double getCenterY() { return centerY; } /** * Retrieve Zoom level * <p> * <p>default : minZoom * <p>mbtiles : taken from 3rd value of metadata 'center' * * @return defaultZoom */ public int getDefaultZoom() { return defaultZoom; } /** * Return short name of map/file * <p> * <p>default: file name without path and extention * <p>mbtiles : metadata 'name' * <p>map : will be value of 'comment', if not null * * @return s_name as short name of map/file */ public String getName() { return name; } /** * Return String of bounds [wms-format] * <p> * <p>x_min,y_min,x_max,y_max * * @return bounds formatted using wms format */ public String getBounds_toString() { return boundsWest + "," + boundsSouth + "," + boundsEast + "," + boundsNorth; } /** * Return String of Map-Center with default Zoom * <p> * <p>x_position,y_position,default_zoom * * @return center formatted using mbtiles format */ public String getCenter_toString() { return centerX + "," + centerY + "," + defaultZoom; } /** * Return long description of map/file * <p> * <p>default: s_name with bounds and center * <p>mbtiles : metadata description' * <p>map : will be value of 'comment', if not null * * @return s_description long description of map/file */ public String getDescription() { if ((this.description == null) || (this.description.length() == 0) || (this.description.equals(this.name))) setDescription(getName()); // will set default values with bounds and center if it is // the same as 's_name' or empty return this.description; } /** * Set long description of map/file * <p> * <p>default: s_name with bounds and center * <p>mbtiles : metadata description' * <p>map : will be value of 'comment', if not null * * @param s_description long description of map/file */ public void setDescription(String s_description) { if ((s_description == null) || (s_description.length() == 0) || (s_description.equals(this.name))) { this.description = getName() + " bounds[" + getBounds_toString() + "] center[" + getCenter_toString() + "]"; } else this.description = s_description; } /** * @return the mbtiles database handler. */ public MbtilesDatabaseHandler getMBTilesDatabase() { return mbtilesDatabase; } public String getTilePath(Tile tile) { int zoomLevel = tile.zoomLevel; int tileX = (int) tile.tileX; int tileY = (int) tile.tileY; doScaleTiles = preferences.getBoolean("PREFS_KEY_RETINA", false); if (type != TILESCHEMA.wms && doScaleTiles) { tileX = tileX / (2 * ZOOM_LEVEL_DIFF); tileY = tileY / (2 * ZOOM_LEVEL_DIFF); zoomLevel = zoomLevel - ZOOM_LEVEL_DIFF; } if (type == TILESCHEMA.tms) { int[] tmsTiles = MercatorUtils.googleTile2TmsTile(tileX, tileY, zoomLevel); tileX = tmsTiles[0]; tileY = tmsTiles[1]; } if (type == TILESCHEMA.tms || type == TILESCHEMA.google) { String tmpTilePart = tilePart.replaceFirst(ZZZ_STR, String.valueOf(zoomLevel)); //$NON-NLS-1$ tmpTilePart = tmpTilePart.replaceFirst(XXX_STR, String.valueOf(tileX)); //$NON-NLS-1$ tmpTilePart = tmpTilePart.replaceFirst(YYY_STR, String.valueOf(tileY)); //$NON-NLS-1$ return tmpTilePart; } else if (type == TILESCHEMA.wms) { // minx, miny, maxx, maxy double[] tileBounds = MercatorUtils.tileLatLonBounds(tileX, tileY, zoomLevel, Tile.TILE_SIZE); String tmpTilePart = tilePart.replaceFirst(XXX_STR, String.valueOf(tileBounds[0])); //$NON-NLS-1$ tmpTilePart = tmpTilePart.replaceFirst(YYY_STR, String.valueOf(tileBounds[1])); //$NON-NLS-1$ tmpTilePart = tmpTilePart.replaceFirst(XXX_STR, String.valueOf(tileBounds[2])); //$NON-NLS-1$ tmpTilePart = tmpTilePart.replaceFirst(YYY_STR, String.valueOf(tileBounds[3])); //$NON-NLS-1$ return tmpTilePart; } return ""; //$NON-NLS-1$ } @Override public boolean executeJob(MapGeneratorJob mapGeneratorJob, Bitmap bitmap) { try { Tile tile = mapGeneratorJob.tile; int tileSize = Tile.TILE_SIZE; String tilePath = getTilePath(tile); int zoom = tile.zoomLevel; int tileX = (int) tile.tileX; int tileYOsm = (int) tile.tileY; if (mbtilesDatabase != null) { // try to retrieve this tile from the active mbtiles.db if (mbtilesDatabase.getBitmapTile(tileX, tileYOsm, zoom, tileSize, bitmap)) { // tile was found and the bitmap filled, return // GPLog.androidLog(-1,"CustomTileDownloader.executeJob: name["+getName() // +"] mbtiles_db["+mbtiles_db.getFileName()+"] tilePath["+i_zoom+"/"+i_tile_x+"/"+i_tile_y_osm+"] "); return true; } } StringBuilder sb = new StringBuilder(); if (!isFile) { sb.append(PROTOCOL); sb.append("://"); } String s_host_name = HOST_NAME; if (i_tile_server > 0) { s_host_name = s_host_name.replaceFirst(SSS_STR, String.valueOf(i_tile_server++)); //$NON-NLS-1$ if (i_tile_server > 2) i_tile_server = 1; } sb.append(s_host_name); sb.append(tilePath); Bitmap decodedBitmap = null; Context context = GPApplication.getInstance(); if (context != null) { isConnectedToInternet = NetworkUtilities.isNetworkAvailable(context); } if (isConnectedToInternet || isFile) { try { String urlString = sb.toString(); if (GPLog.LOG_HEAVY) GPLog.addLogEntry(this, "CustomTileDownloader.executeJob: " + sb.toString()); if (isFile) { File bitmapFile = new File(urlString); if (bitmapFile.exists()) decodedBitmap = BitmapFactory.decodeFile(urlString); } else { decodedBitmap = NetworkUtilities.downloadBitmap(urlString); } if (doScaleTiles && type != TILESCHEMA.wms) decodedBitmap = resize(decodedBitmap, tileX, tileYOsm, ZOOM_LEVEL_DIFF, tileSize); } catch (Exception e) { // ignore and set the image as empty if (GPLog.LOG_HEAVY) GPLog.addLogEntry(this, "Could not find image: " + sb.toString()); //$NON-NLS-1$ return false; } } // check if the input stream could be decoded into a bitmap if (decodedBitmap != null) { if (mbtilesDatabase != null) { // we have a valid image, store this to the active mbtiles.db // [this must be done before recycle() is called] // decodedBitmap == ARGB_8888 ; bitmap == RGB_565 mbtilesDatabase.insertBitmapTile(tileX, tileYOsm, zoom, decodedBitmap, i_force_unique); } // copy all pixels from the decoded bitmap to the color array decodedBitmap.getPixels(this.pixels, 0, tileSize, 0, 0, tileSize, tileSize); // GPLog.androidLog(-1,"CustomTileDownloader.executeJob: retrieved["+i_zoom+"/"+i_tile_x+"/"+i_tile_y_osm+"] "); decodedBitmap.recycle(); } else { for (int i = 0; i < pixels.length; i++) { pixels[i] = Color.WHITE; } } // copy all pixels from the color array to the tile bitmap bitmap.setPixels(this.pixels, 0, tileSize, 0, 0, tileSize, tileSize); return true; } catch (UnknownHostException e) { GPLog.error(this, null, e); return false; } catch (IOException e) { GPLog.error(this, null, e); return false; } } private static Bitmap resize(Bitmap bitmap, final int rx, final int ry, final int zoomLevelDiff, int mTileSizePixels) { int px = rx % (2 * zoomLevelDiff); int py = ry % (2 * zoomLevelDiff); Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, // (mTileSizePixels * 2) * zoomLevelDiff, // (mTileSizePixels * 2) * zoomLevelDiff, false); int x = (px == 0) ? 0 : mTileSizePixels * px; int y = (py == 0) ? 0 : mTileSizePixels * py; Bitmap bitmapResized = Bitmap.createBitmap(scaledBitmap, x, y, mTileSizePixels, mTileSizePixels); return bitmapResized; } public void cleanup() { if (mbtilesDatabase != null) { try { mbtilesDatabase.close(); mbtilesDatabase = null; } catch (Exception e) { GPLog.error(this, null, e); } } } public byte getZoomLevelMax() { return ZOOM_MAX; } /** * Function to check and correct bounds / zoom level [for 'CustomDownloader'] * <p> * <p>i_y_osm must be in is Open-Street-Map 'Slippy Map' notation * [will be converted to 'tms' notation if needed] * * @param mapCenterLocation [point/zoom to check] result of PositionUtilities.getMapCenterFromPreferences(preferences,true,true); * @param doCorrectIfOutOfRange if <code>true</code>, change mapCenterLocation values if out of range * @return 0=inside valid area/zoom ; i_rc > 0 outside area or zoom ; * i_parm=0 no corrections ; 1= correct tileBounds values. */ public int checkCenterLocation(double[] mapCenterLocation, boolean doCorrectIfOutOfRange) { int i_rc = 0; // inside area if (((mapCenterLocation[0] < boundsWest) || (mapCenterLocation[0] > boundsEast)) || ((mapCenterLocation[1] < boundsSouth) || (mapCenterLocation[1] > boundsNorth)) || ((mapCenterLocation[2] < minZoom) || (mapCenterLocation[2] > maxZoom))) { if (((mapCenterLocation[0] >= boundsWest) && (mapCenterLocation[0] <= boundsEast)) && ((mapCenterLocation[1] >= boundsSouth) && (mapCenterLocation[1] <= boundsNorth))) { // We are inside the Map-Area, but Zoom is not correct if (mapCenterLocation[2] < minZoom) { i_rc = 1; if (doCorrectIfOutOfRange) { mapCenterLocation[2] = minZoom; } } if (mapCenterLocation[2] > maxZoom) { i_rc = 2; if (doCorrectIfOutOfRange) { mapCenterLocation[2] = maxZoom; } } } else { if (mapCenterLocation[2] < minZoom) { i_rc = 11; if (doCorrectIfOutOfRange) { mapCenterLocation[2] = minZoom; } } if (mapCenterLocation[2] > maxZoom) { i_rc = 12; if (doCorrectIfOutOfRange) { mapCenterLocation[2] = maxZoom; } } if ((mapCenterLocation[0] < boundsWest) || (mapCenterLocation[0] > boundsEast)) { i_rc = 13; if (doCorrectIfOutOfRange) { mapCenterLocation[0] = centerX; } } if ((mapCenterLocation[1] < boundsSouth) || (mapCenterLocation[1] > boundsNorth)) { i_rc = 14; if (doCorrectIfOutOfRange) { mapCenterLocation[1] = centerY; } } } } return i_rc; } }