package eu.geopaparazzi.spatialite.database.spatial.core.mbtiles;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import eu.geopaparazzi.library.GPApplication;
import eu.geopaparazzi.library.database.GPLog;
import eu.geopaparazzi.library.network.NetworkUtilities;
import eu.geopaparazzi.spatialite.database.spatial.core.databasehandlers.MbtilesDatabaseHandler;
/**
// http://www.vogella.com/articles/AndroidBackgroundProcessing/article.html
// - CursorLoader
// mbtiles_Async download_tiles= new mbtiles_Async(this);
// download_tiles.execute("");
// download_tiles.cancel(true);
// executeOnExecutor(java.util.concurrent.Executor, Object[])
// RUNNING
*/
@SuppressWarnings("nls")
public class MBtilesAsync extends AsyncTask<MbtilesDatabaseHandler.AsyncTasks, String, Integer> {
private MbtilesDatabaseHandler db_mbtiles;
private List<MbtilesDatabaseHandler.AsyncTasks> async_parms = null;
private HashMap<String, String> async_mbtiles_metadata = null;
private HashMap<String, String> mbtiles_request_url = null;
private String s_request_url_source = "";
private String s_request_protocol = ""; // 'file' or 'http'
private int i_tile_server = 0; // if no 'SSS' is found, server logic will not be called
private String s_request_type = ""; // 'fill','replace'
private String s_request_bounds = "";
private String s_request_bounds_url = "";
private int i_request_zoom_min = 22; // will be set properly on construction
private int i_request_zoom_max = 0; // will be set properly on construction
private int i_request_zoom_level = -1; // active request zoom_level
private int i_url_zoom_min = 22; // will be set properly on construction
private int i_url_zoom_max = 0; // will be set properly on construction
private String s_request_y_type = "osm"; // 0=osm ; 1=tms ; 2=wms
private List<Integer> zoom_levels = null;
private double[] request_bounds = null;
private String s_message = "";
private int i_http_code = -1;
private int i_http_not_usable = 0;
private int i_http_bad_requests = 0;
private String s_http_message = "";
private String s_http_result = "";
// -----------------------------------------------
/**
* Constructor
*
* <br>- Base values will be take from MbtilesDatabaseHandler:
* <br>-- an extreame amout of sanit-checks will be made
* <br>a--- Goal: attemt to avoid any server spins when rquesting tiles
*
* @param db_mbtiles MbtilesDatabaseHandler
*/
public MBtilesAsync( MbtilesDatabaseHandler db_mbtiles ) {
this.db_mbtiles = db_mbtiles;
this.async_parms = this.db_mbtiles.getAsyncTasks();
this.async_mbtiles_metadata = this.db_mbtiles.async_mbtiles_metadata;
for( int i = 0; i < async_parms.size(); i++ ) {
s_message += this.async_parms.get(i);
if (i < (async_parms.size() - 1))
s_message += ",";
}
if (!this.db_mbtiles.s_request_type.equals("")) {
this.s_request_type = this.db_mbtiles.s_request_type;
if (!s_message.equals(""))
s_message += ",";
s_message += this.s_request_type;
}
this.s_request_url_source = this.db_mbtiles.s_request_url_source;
this.s_request_protocol = this.db_mbtiles.s_request_protocol;
if (!this.s_request_url_source.equals("")) {
if (check_request_bounds(this.db_mbtiles.s_request_bounds, this.db_mbtiles.s_request_bounds_url) > 0) { // invalid
this.s_request_url_source = "";
} else { // correct
int indexOfS = this.s_request_url_source.indexOf("SSS");
if (indexOfS != -1) {
this.i_tile_server = 1; // Server logic will not be called [1,2]
}
if ((this.db_mbtiles.s_request_y_type.equals("tms")) || (this.db_mbtiles.s_request_y_type.equals("wms"))) {
this.s_request_y_type = this.db_mbtiles.s_request_y_type;
}
}
}
if (!this.s_request_bounds.equals("")) { // only if valid
if ((!this.db_mbtiles.s_request_zoom_levels.equals("")) && (!this.db_mbtiles.s_request_zoom_levels_url.equals(""))) {
create_zoom_levels(this.db_mbtiles.s_request_zoom_levels, this.db_mbtiles.s_request_zoom_levels_url);
}
}
}
protected void onPreExecute() {
if (GPLog.LOG_HEAVY)
GPLog.androidLog(-1, "mbtiles_Async.tasks[" + db_mbtiles.getName() + "][" + s_message + "]");
}
protected Integer doInBackground( MbtilesDatabaseHandler.AsyncTasks... async_values ) {
int i_rc = 0;
if (async_values[0] != MbtilesDatabaseHandler.AsyncTasks.ASYNC_PARMS) {
this.async_parms.clear();
for( int i = 0; i < async_values.length; i++ ) {
this.async_parms.add(async_values[i]);
}
}
if (GPLog.LOG_HEAVY)
GPLog.androidLog(-1, "mbtiles_Async.On doInBackground[" + db_mbtiles.getName() + "]");
for( int i = 0; i < this.async_parms.size(); i++ ) {
if (isCancelled()) {
i_rc = 1077;
s_message = "-W-> mbtiles_Async.On doInBackground[" + db_mbtiles.getName() + "] rc=" + i_rc;
break;
}
MbtilesDatabaseHandler.AsyncTasks async_task = this.async_parms.get(i);
switch( async_task ) {
case RESET_METADATA: {
if ((this.async_mbtiles_metadata != null) && (this.async_mbtiles_metadata.size() > 0)) {
// i_reload_metadata 1: reload values after update
try {
db_mbtiles.updateMetadata(this.async_mbtiles_metadata, 1);
publishProgress("-I-> update_metadata[" + db_mbtiles.getName() + "]: bounds["
+ db_mbtiles.getBoundsAsString() + "] zoom_levels[" + db_mbtiles.getMinMaxZoomLevelsAsString()
+ "] center_parms[" + db_mbtiles.getCenterParms() + "] rc=" + i_rc);
} catch (Exception e) {
GPLog.error(this, "Error", e);
}
}
}
break;
case ANALYZE_VACUUM: { // implemented, but not tested : 20131123: does not seem to work
// correctly
publishProgress("-I-> on_analyze_vacuum[" + db_mbtiles.getName() + "]: support discontinued ");
// publishProgress("-I-> on_analyze_vacuum[" + db_mbtiles.getName() +
// "]: starting ");
// i_rc = on_analyze_vacuum();
// publishProgress("-I-> on_analyze_vacuum[" + db_mbtiles.getName() +
// "]: i_rc="+i_rc);
}
break;
case UPDATE_BOUNDS: { // extensive checking of bounds with update of mbtiles.metadata
// table
publishProgress("-I-> on_update_bounds[" + db_mbtiles.getName() + "]: starting: bounds["
+ db_mbtiles.getBoundsAsString() + "] zoom_levels[" + db_mbtiles.getMinMaxZoomLevelsAsString()
+ "] center_parms[" + db_mbtiles.getCenterParms() + "]");
i_rc = on_update_bounds();
publishProgress("-I-> on_update_bounds[" + db_mbtiles.getName() + "]: end: bounds["
+ db_mbtiles.getBoundsAsString() + "] zoom_levels[" + db_mbtiles.getMinMaxZoomLevelsAsString()
+ "] center_parms[" + db_mbtiles.getCenterParms() + "] rc=" + i_rc);
}
break;
case REQUEST_URL: { // retrieve tiles found in 'request_url'
// TODO @mj10777 is all this debug and testing code necessary?.
//
// int i_test_downloads = 0;
// // i_test_downloads=1;
// if (i_test_downloads == 0) {
i_rc = on_request_url();
// this will update the metadata Table, if we are not being canceled
on_update_bounds();
// } else {
//
// try {
// // this returns a valid image
// String s_tile_url =
// "http://fbinter.stadt-berlin.de/fb/wms/senstadt/k_luftbild2011_20?LAYERS=0&FORMAT=image/jpeg&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=visual&SRS=EPSG:4326&BBOX=13.293457031249988,52.562995039558004,13.315429687500005,52.57634993749886&WIDTH=256&HEIGHT=256";
// Bitmap bm_test = on_download_tile_http(s_tile_url);
// // this returns blank [area not supported]
// s_tile_url =
// "http://fbinter.stadt-berlin.de/fb/wms/senstadt/k_luftbild2011_20?LAYERS=0&FORMAT=image/jpeg&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=visual&SRS=EPSG:4326&BBOX=13.20556640624999,52.6430634366589,13.227539062500007,52.656393941988014&WIDTH=256&HEIGHT=256";
// bm_test = on_download_tile_http(s_tile_url);
// // this returns an error [<ServiceException code="LayerNotDefined">theme
// // k_luftbild1938@senstadt access denied</ServiceException>]
// s_tile_url =
// "http://fbinter.stadt-berlin.de/fb/wms/senstadt/k_luftbild1938?LAYERS=0&FORMAT=image/jpeg&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=visual&SRS=EPSG:4326&BBOX=13.20556640624999,52.6430634366589,13.227539062500007,52.656393941988014&WIDTH=256&HEIGHT=256";
// bm_test = on_download_tile_http(s_tile_url);
// } catch (Exception e) {
// }
// }
}
break;
case REQUEST_CREATE: { // create and fill 'request_url' with tile-requests
if (zoom_levels != null) {
i_rc = on_request_create();
} else {
i_rc = 2000;
}
}
break;
case REQUEST_DELETE: { // todo: delete an area of the db
}
break;
case REQUEST_DROP: { // Drop the request table
i_rc = db_mbtiles.getRequestUrlCount(MBTilesDroidSpitter.i_request_url_count_drop);
}
break;
case REQUEST_PING: { // todo: check if internet is really working [i.e. a valid image
// can be downloaded]
}
break;
}
}
s_message = "-I-> mbtiles_Async.On doInBackground[" + db_mbtiles.getName() + "] all tasks compleated rc=" + i_rc;
return i_rc;
}
// TODO @mj10777 this seems to be unused.
//
// /**
// * House-keeping tasks for Database
// * The ANALYZE command gathers statistics fragment_about tables and indices
// * The VACUUM command rebuilds the entire database.
// * - A VACUUM will fail if there is an open transaction, or if there are one or more active
// SQL statements when it is run.
// * @return 0=correct ; 1=ANALYSE has failed ; 2=VACUUM has failed
// */
// private int on_analyze_vacuum() {
// return db_mbtiles.on_analyze_vacuum();
// }
/**
* Extensive checking of Bounds for all Zoom-Levels
* - results will be written to metadata-Table
* @return i_rc [ 0: task compleated; 1=task interupted ; 2: task is being canceled]
*/
private int on_update_bounds() {
if (!isCancelled())
return db_mbtiles.updateBounds(1);
else
return 2;
}
// -----------------------------------------------
/**
* will retrieve the list of requested tile-images
* - retrieves list from 'request_url' table [if any]
* - for each request 'on_request_tile_id_url' will be called
* -- isCancelled() is called before each request
* @return i_rc [ 0: task compleated; 1=task interupted]
*/
private int on_request_url() {
int i_rc = 0;
int i_count_tiles_total = db_mbtiles.getRequestUrlCount(1); // read the table and return
// the amount
int i_count_tiles_count = 0;
int i_count_tiles_left = 0;
int i_count_rest = 1;
int i_request_zoom_level_prev = -1;
if (i_count_tiles_total > 0) { // avoid divide by zero error
i_count_rest = i_count_tiles_total / 20; // 100=1% ; 80=1.25% ; 40=2.5% ; 20=5% ; 10=10%
// ; 5=20% ; 1=100%
if (i_count_tiles_total > 10000) {
i_count_rest = i_count_tiles_total / 100; // 100=1% ;
} else {
if (i_count_tiles_total > 5000) {
i_count_rest = i_count_tiles_total / 50; // 50=2% ;
} else {
if (i_count_tiles_total > 2500) {
i_count_rest = i_count_tiles_total / 25; // 25=2% ;
}
}
}
if (i_count_rest < 1)
i_count_rest = 1;
}
Context context = GPApplication.getInstance();
boolean networkAvailable = NetworkUtilities.isNetworkAvailable(context);
int i_limit = 100; // avoid excesive memory usage
mbtiles_request_url = db_mbtiles.getRequestUrlsMap(i_limit);
while( mbtiles_request_url.size() > 0 ) {
for( Map.Entry<String, String> request_url : mbtiles_request_url.entrySet() ) {
if (i_http_not_usable > 0) {
i_rc = 3775;
s_message = "-W-> on_request_url[" + s_http_result + "][" + db_mbtiles.getName() + "]: mbtiles_request_url["
+ i_count_tiles_total + "] rc=" + i_rc;
publishProgress(s_message);
return i_rc;
}
if (!networkAvailable) {
i_rc = 3776;
s_http_result = "No Internet Connection";
s_message = "-W-> on_request_url[" + s_http_result + "][" + db_mbtiles.getName() + "]: mbtiles_request_url["
+ i_count_tiles_total + "] rc=" + i_rc;
publishProgress(s_message);
return i_rc;
}
if (isCancelled()) {
i_rc = 3777;
s_message = "-W-> on_request_url[" + s_request_type + "][" + db_mbtiles.getName() + "]: mbtiles_request_url["
+ i_count_tiles_total + "] rc=" + i_rc;
return i_rc;
}
String s_tile_id = request_url.getKey();
String s_tile_url = request_url.getValue();
i_rc = on_request_tile_id_url(s_tile_id, s_tile_url);
i_count_tiles_count++;
i_count_tiles_left = i_count_tiles_total - i_count_tiles_count;
if (i_request_zoom_level > 0) { // i_request_zoom_level is set in
// 'on_request_tile_id_url'
if (i_request_zoom_level_prev < 0) { // The first message when starting - list
// the first tile that was downloaded
s_message = "-I-> on_request_url[" + s_request_type + "][" + db_mbtiles.getName()
+ "]: mbtiles_request_url[" + i_count_tiles_total + "] tile_id[" + s_tile_id + "] open["
+ i_count_tiles_left + "] rc=" + i_rc;
publishProgress(s_message);
}
if (i_request_zoom_level_prev != i_request_zoom_level) { // A new Zoom-level has
// been started this
// will update the
// metadata Table with
// true min/max
// zoom-levels and
// bounds
on_update_bounds();
publishProgress("-I-> on_update_bounds[" + i_request_zoom_level + "][" + db_mbtiles.getName()
+ "]: bounds[" + db_mbtiles.getBoundsAsString() + "] zoom_levels["
+ db_mbtiles.getMinMaxZoomLevelsAsString() + "] center_parms[" + db_mbtiles.getCenterParms()
+ "] ");
i_request_zoom_level_prev = i_request_zoom_level;
}
}
if ((i_count_tiles_count % i_count_rest) == 0) {
double d_procent = (double) i_count_tiles_left;
d_procent = 100 - ((d_procent / i_count_tiles_total) * 100);
s_message = "-I-> on_request_url[" + db_mbtiles.getName() + "][" + s_request_type + "]: tile_id[" + s_tile_id
+ "] retrieved[" + i_count_tiles_count + "] [" + String.format("%.4f", d_procent) + " %] open["
+ i_count_tiles_left + "] total[" + i_count_tiles_total + "]";
publishProgress(s_message);
}
}
int i_count_tiles_test = db_mbtiles.getRequestUrlCount(1);
if (i_count_tiles_test != i_count_tiles_total) {
// retrieve the next amount, avoiding excesive memory usage
mbtiles_request_url = db_mbtiles.getRequestUrlsMap(i_limit);
} else { // something is wrong, return to avoid loop
i_rc = 3778;
s_message = "-W-> on_request_url[" + s_request_type + "][" + db_mbtiles.getName() + "]: mbtiles_request_url["
+ i_count_tiles_total + "] rc=" + i_rc;
return i_rc;
}
}
i_count_tiles_total = db_mbtiles.getRequestUrlCount(1);
if (i_count_tiles_total < 1) { // when completed, call update_bounds
on_update_bounds();
publishProgress("-I-> on_update_bounds[" + i_request_zoom_level + "][" + db_mbtiles.getName() + "]: bounds["
+ db_mbtiles.getBoundsAsString() + "] zoom_levels[" + db_mbtiles.getMinMaxZoomLevelsAsString()
+ "] center_parms[" + db_mbtiles.getCenterParms() + "] ");
}
return i_rc;
}
// -----------------------------------------------
/**
* will retrieve requested tile-image [on_download_tile]
* - will store each retieved bitmap in the mbtiles-file [db_mbtiles.insertBitmapTile]
* - will remove the request [db_mbtiles.delete_request_url]
* @param s_tile_id tile_id to use
* @param s_tile_url full url to retrieve tile with
* @return i_rc [ 0: image retrieved and stored; 101=image not retrieved ; 100=invalid mbtiles ; otherwise some storing error]
*/
private int on_request_tile_id_url( String s_tile_id, String s_tile_url ) {
int i_rc = 0;
int[] zxy_osm_tms = MBTilesDroidSpitter.get_zxy_from_tile_id(s_tile_id);
if ((zxy_osm_tms != null) && (zxy_osm_tms.length == 4)) {
int i_zoom = zxy_osm_tms[0];
i_request_zoom_level = i_zoom;
int i_tile_x = zxy_osm_tms[1];
int i_tile_y_osm = zxy_osm_tms[2];
// int i_tile_y_tms = zxy_osm_tms[3];
try {
Bitmap tile_bitmap = on_download_tile_http(s_tile_url);
if (tile_bitmap != null) {
// GPLog.androidLog(-1, "mbtiles_Async on_request_tile_id_url[" +
// db_mbtiles.getName() + "][" + s_tile_id + "] ["+ s_tile_url +
// "] i_tile_y_tms=" + i_tile_y_tms);
i_rc = db_mbtiles.insertBitmapTile(i_tile_x, i_tile_y_osm, i_zoom, tile_bitmap, 0);
if (i_rc == 0) {
i_http_bad_requests = 0;
db_mbtiles.deleteRequestUrl(s_tile_id);
}
} else {
if ((i_http_bad_requests > 10) && (i_http_not_usable == 0)) {
// provider is not // sending // anything
s_http_result = "Internet Connection: recieved [" + i_http_bad_requests + "] bad requests";
i_http_not_usable = 1;
}
}
} catch (Throwable t) {
i_rc = 2;
StringBuilder sb = new StringBuilder();
sb.append("mbtiles_Async on_request_tile_id_url[");
sb.append(db_mbtiles.getName());
sb.append("][");
sb.append(s_tile_id);
sb.append("] [");
sb.append(s_tile_url);
sb.append("] rc=");
sb.append(i_rc);
GPLog.error(this, sb.toString(), t);
}
} else {
i_rc = 1;
}
return i_rc;
}
// -----------------------------------------------
/**
* will retrieve requested tile-image [s_tile_url]
* - goal is to determin whether we have a fake connection or a non-publich server
* -- provider will NOT return results - only requests for money - therefore connection not usable
* --- 302 HTTP_MOVED_TEMP
* -- even with 'access denied', HTTP_OK is returned
* Goal is to avoid unneeded / invalid requests assuming that a valid http-code is returned
* @param s_tile_url full url to retrieve tile with
* @return i_rc [ 0: image retrieved and stored; 101=image not retrieved ; 100=invalid mbtiles ; otherwise some storing error]
*/
private Bitmap on_download_tile_http( String s_tile_url ) throws Exception {
Bitmap tile_bitmap = null;
i_http_code = -1;
int i_image_null = 1;
int i_content_length = 0;
int i_http_code = 0;
String s_http_message = "";
try {
URL this_url = new URL(s_tile_url);
// GPLog.androidLog(-1, "mbtiles_Async on_request_tile_id_url[" + db_mbtiles.getName() +
// "][" + this_url.getProtocol() + "] ["+ s_tile_url + "] toExternalForm[" +
// this_url.toExternalForm()+"]");
InputStream input_stream = null;
HttpURLConnection this_http = null;
try {
if (this_url.getProtocol().equals("file")) {
input_stream = this_url.openStream();
s_http_message = "File:OK";
i_http_code = 200;
// GPLog.androidLog(-1, "mbtiles_Async on_request_tile_id_url[" +
// db_mbtiles.getName() + "][" + this_url.getProtocol() + "] ["+ s_tile_url +
// "] toExternalForm[" + this_url.toExternalForm()+"]");
} else {
this_http = (HttpURLConnection) this_url.openConnection();
if (this_http != null) {
this_http.setDoInput(true);
i_content_length = this_http.getContentLength(); // the size of the gziped
// value
// returned
input_stream = this_http.getInputStream();
s_http_message = this_http.getResponseMessage();
i_http_code = this_http.getResponseCode();
} else {
s_http_message = "this_url.openConnection() failed";
i_http_code = 778;
}
}
if (input_stream != null) {
tile_bitmap = BitmapFactory.decodeStream(input_stream);
input_stream.close();
if (tile_bitmap == null) { // possible 'access denied' - not a public server
// -should
// be considered an invalid server [returns HTTP_OK]
if (i_content_length > 0) { // is probely an error text similer to:
// <ServiceException
// code="LayerNotDefined">theme
// k_luftbild1938@senstadt access
// denied</ServiceException>
}
} else {
i_image_null = 0;
}
} else {
s_http_message = "input_stream is null";
i_http_code = 779;
}
} catch (IOException e) {
if (e.getMessage().contains("ETIMEDOUT")) { // failed to connect to
// fbinter.stadt-berlin.de/141.15.4.15
// (port 80): connect failed:
// ETIMEDOUT (Connection timed out)
i_http_code = 408;
s_http_message = e.getMessage();
tile_bitmap = null;
} else {
s_message = "mbtiles_Async.on_download_tile: http_code[" + i_http_code + "] [" + s_http_message + "] "
+ e.getMessage();
GPLog.error(this, s_message, e);
tile_bitmap = null;
}
} finally { // will set values, depending on values to determin if this task should be
// aborted
get_http_result(0, i_http_code, s_http_message, i_content_length, i_image_null);
}
} catch (Throwable t) {
s_message = "mbtiles_Async.on_download_tile: http_code[" + i_http_code + "] [" + s_http_message + "]";
GPLog.error(this, s_message, t);
}
return tile_bitmap;
}
// -----------------------------------------------
/**
* will interpate the result and attempt to deside if this is a usable Internet connection
* - goal is to determin whether we have a fake connection or a non-publich server
* -- provider will NOT return results - only requests for money - therefore connection not usable
* --- 302: HTTP_MOVED_TEMP
* -- even with 'access denied', HTTP_OK is returned, but with an error text being sent (i_content_length != -1)
* @param i_parm reservered
* @param i_http_code http code recieved
* @param i_content_length gziped length of data recieved [images always '-1']
* @param i_image_null 0= no valid imageg recieved ; 1= valid image recieved
* @return i_http_code
*/
private int get_http_result( int i_parm, int i_http_code, String s_http_message, int i_content_length, int i_image_null ) {
this.i_http_code = i_http_code;
this.s_http_message = s_http_message;
this.s_http_result = "[" + this.i_http_code + "][" + this.s_http_message + "]";
switch( this.i_http_code ) {
case HttpURLConnection.HTTP_OK: { // this is the desired result
if (i_image_null < 1) {
i_http_not_usable = 0; // is usable
i_http_bad_requests = 0;
} else { // possible 'access denied' - not a public server -should be considered an
// invalid server [returns never the less: HTTP_OK]
// after 10 attempts, will abort
i_http_bad_requests++;
// i_content_length=4;
}
// GPLog.androidLog(i_content_length,"mbtiles_Async.get_http_result: "+s_message);
}
break;
case HttpURLConnection.HTTP_MOVED_TEMP: { // this can be a connection that returns no
// results until you activate an account
s_http_result = "Internet Connection: recieved [" + this.s_http_result + "] - aborting";
i_http_not_usable = 1;
}
break;
case HttpURLConnection.HTTP_CLIENT_TIMEOUT:
case HttpURLConnection.HTTP_BAD_REQUEST: { // 400: Bad Request
// malformed url or tiles out of range
// after 10 attempts, will abort
i_http_bad_requests++;
}
break;
default:
if (GPLog.LOG_HEAVY)
GPLog.androidLog(-1, "mbtiles_Async.get_http_result: " + s_http_result);
break;
}
s_message = "mbtiles_Async.get_http_result: i_image_null[" + i_image_null + "] content_length[" + i_content_length
+ "] [" + s_http_result + "]";
// GPLog.androidLog(-1,"mbtiles_Async.get_http_result: "+s_message);
return this.i_http_code;
}
// TODO @mj10777 this seems to be unused.
//
// private Bitmap on_download_tile( String s_tile_url ) throws Exception {
// Bitmap tile_bitmap = null;
// try {
// URL this_url = new URL(s_tile_url);
// InputStream input_stream = null;
// HttpURLConnection this_http = null;
// try {
// input_stream = this_url.openStream();
// tile_bitmap = BitmapFactory.decodeStream(input_stream);
// input_stream.close();
// } catch (IOException e) {
// String s_message = e.getMessage();
// tile_bitmap = null;
// }
// } catch (Throwable t) {
// }
// return tile_bitmap;
// }
// -----------------------------------------------
/**
* Create list of 'request_url
* - Assumtions:
* -- we have a valid bounds
* -- we have valid zoom_levels
* -- we have a valid request url
* -- '5-3,7-8,6'
* -- order is not important, mixing up min/max will be corrected
* -- only unique values will be stored
* - valid zoom-levels: 0-22
* - result will sorted from min to max,West to East ; North to South
* @return zoom_levels.size() [ amount of valid,sorted zoom_levels found]
*/
private int on_request_create() {
int i_rc = 0;
int i_max_limit = 100;
int i_limit = 0;
int i_count_tiles_total = db_mbtiles.getRequestUrlCount(1);
int i_count_tiles_level = 0;
String s_url = "";
String s_request_y = "";
if (s_request_protocol.equals("file")) { // We are adding from an existing set of tiles, the
// tile must exist
s_url = s_request_url_source;
s_request_y = s_request_y_type;
}
mbtiles_request_url = new LinkedHashMap<String, String>();
s_message = "-I-> on_request_create[" + s_request_type + "," + s_request_protocol + "][" + db_mbtiles.getName()
+ "]: zoom_levels[" + zoom_levels.size() + "] file_url[" + s_url + "] file_y[" + s_request_y + "]";
publishProgress(s_message);
for( int i = 0; i < zoom_levels.size(); i++ ) { // for all selected zoom levels
int i_zoom_level = zoom_levels.get(i);
if (isCancelled()) {
i_rc = 2777;
s_message = "-W-> on_request_create[" + db_mbtiles.getName() + "]: zoom_level[" + i_zoom_level + "] tiles["
+ i_count_tiles_level + "] total[" + i_count_tiles_total + "] rc=" + i_rc;
return i_rc;
}
// retrieve list of missing[fill] or compleate list[replace] of tiles to retrieve
// This is still a full list (without limit) which could cause memory problems
//
String s_tile_id = "";
// TODO: build in better logic to avoid TimeOut situations
int[] tile_bounds = MBTilesDroidSpitter.LatLonBounds_to_TileBounds(request_bounds, i_zoom_level);
int i_min_x = tile_bounds[1];
int i_min_y_osm = tile_bounds[2];
int i_max_x = tile_bounds[3];
int i_max_y_osm = tile_bounds[4];
for( int x = i_min_x; x <= i_max_x; x++ ) { // collect for each y column ; we are
// hoping, that after each return from
// another thread will avoid a TimeOut
double[] x_request_bounds = MBTilesDroidSpitter.TileBounds_to_LatLonBounds(new int[]{x, i_min_y_osm, x + 1,
i_max_y_osm}, i_zoom_level);
int i_column_left = i_max_x - x;
List<String> list_tile_id = db_mbtiles.buildRequestList(x_request_bounds, i_zoom_level, s_request_type, s_url,
s_request_y);
s_message = "-I-> on_request_create[" + s_request_type + "," + s_request_protocol + "][" + db_mbtiles.getName()
+ "]: list_tile_id.size[" + list_tile_id.size() + "] x=" + x + " ; " + i_column_left + " columns left";
publishProgress(s_message);
for( int j = 0; j < list_tile_id.size(); j++ ) {
if (isCancelled()) {
i_rc = 2778;
s_message = "-W-> on_request_create[" + db_mbtiles.getName() + "]: zoom_level[" + i_zoom_level
+ "] tiles[" + i_count_tiles_level + "] total[" + i_count_tiles_total + "] rc=" + i_rc;
return i_rc;
}
s_tile_id = list_tile_id.get(j);
if (!s_tile_id.equals("")) { // Avoid error: code 19 constraint failed when
// inserting image more than once
// for each tile send tile_id[from which to
// position
// will be calculated[wms] or the tile-numbers set]
// - these values be set in the given url
// -- the tile_id and created url will be added to
// 'mbtiles_request_url'
on_request_create_url(s_tile_id, s_request_url_source);
i_limit++;
}
if (i_limit >= i_max_limit) { // save reguraly to avoid excess memory usage
if ((mbtiles_request_url.size()) > 0) {
i_count_tiles_total = db_mbtiles.bulkInsertFromUrlsTilesInTable(mbtiles_request_url);
// clear 'mbtiles_request_url' that have been stored
mbtiles_request_url.clear();
s_message = "-I-> on_request_create[" + db_mbtiles.getName() + "]: [" + j + "] tile_id[" + s_tile_id
+ "] zoom_level[" + i_zoom_level + "] tiles_level[" + i_count_tiles_level + "] total["
+ i_count_tiles_total + "]";
publishProgress(s_message);
}
i_limit = 0;
}
}
list_tile_id.clear();
if ((mbtiles_request_url.size()) > 0) { // nothing may have been found
// save 'mbtiles_request_url' to the
// database
i_count_tiles_level = db_mbtiles.getRequestUrlCount(0);
i_count_tiles_total = db_mbtiles.bulkInsertFromUrlsTilesInTable(mbtiles_request_url);
i_count_tiles_level = i_count_tiles_total - i_count_tiles_level;
// clear 'mbtiles_request_url' that have been stored
mbtiles_request_url.clear();
s_message = "-I-> on_request_create[" + db_mbtiles.getName() + "]: zoom_level[" + i_zoom_level + "] tile_id["
+ s_tile_id + "] total[" + i_count_tiles_total + "]";
publishProgress(s_message);
}
}
}
i_count_tiles_total = db_mbtiles.getRequestUrlCount(1);
s_message = "-I-> on_request_create[" + s_request_type + "][" + db_mbtiles.getName() + "]: requested tiles["
+ i_count_tiles_total + "] exist.";
publishProgress(s_message);
return i_rc;
}
// -----------------------------------------------
/**
* Parse s_request_bounds string
* - Sample of supported formats:
* -- '17'
* -- '15-17'
* -- '2,5,9-10,12'
* -- '5-3,7-8,6'
* -- order is not important, mixing up min/max will be corrected
* -- only unique values will be stored
* - valid zoom-levels: 0-22
* - result will sorted from min to max
* @return zoom_levels.size() [ amount of valid,sorted zoom_levels found]
*/
private int check_request_bounds( String s_request_bounds, String s_request_bounds_url ) {
int i_rc = 0;
if ((!s_request_bounds.equals("")) || (!s_request_bounds_url.equals(""))) {
double[] url_bounds = new double[]{0.0, 0.0, 0.0, 0.0};
double[] test_bounds = new double[]{0.0, 0.0, 0.0, 0.0};
String[] sa_string = null;
int indexOfS = s_request_bounds.indexOf(",");
if (indexOfS != -1) {
sa_string = s_request_bounds.split(",");
} else {
sa_string = s_request_bounds.split("\\s+");
}
if (sa_string.length == 4) {
try {
test_bounds[0] = Double.parseDouble(sa_string[0]);
test_bounds[1] = Double.parseDouble(sa_string[1]);
test_bounds[2] = Double.parseDouble(sa_string[2]);
test_bounds[3] = Double.parseDouble(sa_string[3]);
} catch (NumberFormatException e) {
i_rc = 3;
return i_rc;
}
} else {
i_rc = 2;
return i_rc;
}
indexOfS = s_request_bounds_url.indexOf(",");
if (indexOfS != -1) {
sa_string = s_request_bounds_url.split(",");
} else {
sa_string = s_request_bounds_url.split("\\s+");
}
if (sa_string.length == 4) {
try {
url_bounds[0] = Double.parseDouble(sa_string[0]);
url_bounds[1] = Double.parseDouble(sa_string[1]);
url_bounds[2] = Double.parseDouble(sa_string[2]);
url_bounds[3] = Double.parseDouble(sa_string[3]);
} catch (NumberFormatException e) {
i_rc = 5;
return i_rc;
}
} else {
i_rc = 4;
return i_rc;
}
if (test_bounds[0] < url_bounds[0])
test_bounds[0] = url_bounds[0];
if (test_bounds[0] > url_bounds[2])
test_bounds[0] = url_bounds[2];
if (test_bounds[1] < url_bounds[1])
test_bounds[1] = url_bounds[1];
if (test_bounds[1] > url_bounds[3])
test_bounds[1] = url_bounds[3];
if (test_bounds[2] < url_bounds[0])
test_bounds[2] = url_bounds[0];
if (test_bounds[2] > url_bounds[2])
test_bounds[2] = url_bounds[2];
if (test_bounds[3] < url_bounds[1])
test_bounds[3] = url_bounds[1];
if (test_bounds[3] > url_bounds[3])
test_bounds[3] = url_bounds[3];
if ((test_bounds[0] == test_bounds[2]) || (test_bounds[1] == test_bounds[3])) {
i_rc = 5;
return i_rc;
}
// end of sanity checks
this.request_bounds = new double[]{test_bounds[0], test_bounds[1], test_bounds[2], test_bounds[3]};
this.s_request_bounds = s_request_bounds;
this.s_request_bounds_url = s_request_bounds_url;
} else {
i_rc = 1;
}
return i_rc;
}
// -----------------------------------------------
/**
* Parse zoom-level string
* - Sample of supported formats:
* -- '17'
* -- '15-17'
* -- '2,5,9-10,12'
* -- '5-3,7-8,6'
* -- order is not important, mixing up min/max will be corrected
* -- only unique values will be stored
* - valid zoom-levels: 0-22
* - result will sorted from min to max
* @param s_zoom_levels list of zoom levels
* @return zoom_levels.size() [ amount of valid,sorted zoom_levels found]
*/
private int create_zoom_levels( String s_zoom_levels, String s_zoom_levels_url ) {
zoom_levels = new ArrayList<Integer>();
if (add_zoom_from_to(s_zoom_levels_url, 0) != 0)
return zoom_levels.size();
int indexOfS = s_zoom_levels.indexOf(",");
int i_zoom_level = 0;
if (indexOfS != -1) {
String[] sa_string = s_zoom_levels.split(",");
for (String s_zoom_level : sa_string) {
indexOfS = s_zoom_level.indexOf("-");
if (indexOfS != -1) {
if (add_zoom_from_to(s_zoom_levels, 1) != 0)
return zoom_levels.size();
} else {
try {
i_zoom_level = Integer.parseInt(s_zoom_levels);
} catch (NumberFormatException e) {
zoom_levels.clear();
return zoom_levels.size();
}
if (!zoom_levels.contains(i_zoom_level)) {
if ((i_zoom_level >= this.i_url_zoom_min) && (i_zoom_level <= this.i_url_zoom_max)) {
if (i_zoom_level < this.i_request_zoom_min)
this.i_request_zoom_min = i_zoom_level;
if (i_zoom_level > this.i_request_zoom_max)
this.i_request_zoom_max = i_zoom_level;
zoom_levels.add(i_zoom_level);
}
}
}
}
} else {
indexOfS = s_zoom_levels.indexOf("-");
if (indexOfS != -1) {
if (add_zoom_from_to(s_zoom_levels, 1) != 0)
return zoom_levels.size();
} else {
try {
i_zoom_level = Integer.parseInt(s_zoom_levels);
} catch (NumberFormatException e) {
zoom_levels.clear();
return zoom_levels.size();
}
if (!zoom_levels.contains(i_zoom_level)) {
if ((i_zoom_level >= this.i_url_zoom_min) && (i_zoom_level <= this.i_url_zoom_max)) {
if (i_zoom_level < this.i_request_zoom_min)
this.i_request_zoom_min = i_zoom_level;
if (i_zoom_level > this.i_request_zoom_max)
this.i_request_zoom_max = i_zoom_level;
zoom_levels.add(i_zoom_level);
}
}
}
}
Collections.sort(zoom_levels);
return zoom_levels.size();
}
// -----------------------------------------------
/**
* Parse zoom-level string [with from_to]
* - Sample of supported formats:
* -- '15-17'
* -- order is not important, mixing up min/max will be corrected
* -- only unique values will be stored
* - valid zoom-levels: 0-22
* @param s_zoom_levels list of zoom levels
* @return i_rc [ correct ; 1=no '-' found]
*/
private int add_zoom_from_to( String s_zoom_levels, int i_parm ) {
int i_rc = 1;
int indexOfS = s_zoom_levels.indexOf("-");
int i_request_zoom_min = 0;
int i_request_zoom_max = 0;
if (indexOfS != -1) {
String[] sa_string = s_zoom_levels.split("-");
if (sa_string.length == 2) {
try {
i_request_zoom_min = Integer.parseInt(sa_string[0]);
i_request_zoom_max = Integer.parseInt(sa_string[1]);
} catch (NumberFormatException e) {
zoom_levels.clear();
return i_rc;
}
if (i_request_zoom_min > i_request_zoom_max) {
indexOfS = i_request_zoom_min;
i_request_zoom_max = i_request_zoom_min;
i_request_zoom_min = indexOfS;
}
if (i_parm == 0) {
if ((i_request_zoom_max >= 0) && (i_request_zoom_max <= 22)) {
if ((i_request_zoom_min >= 0) && (i_request_zoom_min <= 22)) {
this.i_url_zoom_min = i_request_zoom_min; // will be set properly on
// construction
this.i_url_zoom_max = i_request_zoom_max; // will be set properly on
// construction
return 0;
}
}
return i_rc;
}
for( int i_zoom_level = i_request_zoom_min; i_zoom_level <= i_request_zoom_max; i_zoom_level++ ) {
if (!zoom_levels.contains(i_zoom_level)) {
if ((i_zoom_level >= this.i_url_zoom_min) && (i_zoom_level <= this.i_url_zoom_max)) {
if (i_zoom_level < this.i_request_zoom_min)
this.i_request_zoom_min = i_zoom_level;
if (i_zoom_level > this.i_request_zoom_max)
this.i_request_zoom_max = i_zoom_level;
zoom_levels.add(i_zoom_level);
i_rc = 0;
}
}
}
}
}
return i_rc;
}
// -----------------------------------------------
/**
* will fill source_url [with placeholder] with valid values to retrieve requested tile-image
* - adds result to 'mbtiles_request_url'
* @param s_tile_id tile_id to use
* @param s_url_source source url with placeholders for ZZZ,XXX,YYY and SSS
* @return i_rc [ -1: s_tile_id incorrectly formatted; 0=valid s_tile_id and s_tile_url were added to the list ; ]
*/
private int on_request_create_url( String s_tile_id, String s_url_source ) {
int i_rc = 0;
int indexOfZ = s_url_source.indexOf("ZZZ");
int[] zxy_osm_tms = MBTilesDroidSpitter.get_zxy_from_tile_id(s_tile_id);
if ((zxy_osm_tms != null) && (zxy_osm_tms.length == 4)) {
int i_z = zxy_osm_tms[0];
int i_x = zxy_osm_tms[1];
int i_y_osm = zxy_osm_tms[2];
int i_y_tms = zxy_osm_tms[3];
int i_y = i_y_osm;
if (s_request_y_type.equals("tms")) {
i_y = i_y_tms;
}
String s_tile_url = s_url_source;
if (i_tile_server > 0) { // ['http://otileSSS.mqcdn.com/'] replace
// 'http://otile1.mqcdn.com/' with ''http://otile2.mqcdn.com/'
s_tile_url = s_tile_url.replaceFirst("SSS", String.valueOf(i_tile_server++)); //$NON-NLS-1$
if (i_tile_server > 2)
i_tile_server = 1;
}
if (indexOfZ != -1) { // tile-server: replace ZZZ,XXX,YYY
s_tile_url = s_tile_url.replaceFirst("ZZZ", String.valueOf(i_z)); //$NON-NLS-1$
s_tile_url = s_tile_url.replaceFirst("XXX", String.valueOf(i_x)); //$NON-NLS-1$
s_tile_url = s_tile_url.replaceFirst("YYY", String.valueOf(i_y)); //$NON-NLS-1$
} else { // wms_server
double[] tileBounds = MBTilesDroidSpitter.tileLatLonBounds(i_x, i_y_osm, i_z, 256);
s_tile_url = s_tile_url.replaceFirst("XXX", String.valueOf(tileBounds[0])); //$NON-NLS-1$
s_tile_url = s_tile_url.replaceFirst("YYY", String.valueOf(tileBounds[1])); //$NON-NLS-1$
s_tile_url = s_tile_url.replaceFirst("XXX", String.valueOf(tileBounds[2])); //$NON-NLS-1$
s_tile_url = s_tile_url.replaceFirst("YYY", String.valueOf(tileBounds[3])); //$NON-NLS-1$
}
if (s_request_protocol.equals("file")) {
File file_tile = new File(s_tile_url);
if (file_tile.exists()) {
s_tile_url = "file:" + s_tile_url;
} else {
s_tile_url = "";
}
}
if (!s_tile_url.equals("")) {
mbtiles_request_url.put(s_tile_id, s_tile_url);
}
} else {
i_rc = -1;
}
// GPLog.androidLog(4,"on_request_create_url["+s_tile_id+"] rc="+i_rc);
return i_rc;
}
protected void onProgressUpdate( String... a ) {
if (GPLog.LOG_HEAVY)
GPLog.androidLog(-1, "mbtiles_Async.[" + a[0] + "]");
}
protected void onPostExecute( Integer i_rc ) {
if (GPLog.LOG_HEAVY)
GPLog.androidLog(-1, "mbtiles_Async.onPostExecute[" + i_rc + "] [" + s_message + "]");
}
}