Cache tiles in WebSQL instead of LocalStorage?

1215
7
01-27-2012 03:07 AM
EthanBodin
New Contributor
I have an html5 application caching tiles from a tiled map service layer in LocalStorage (originally from this sample: http://help.arcgis.com/en/webapi/javascript/arcgis/help/jssamples_start.htm), which works fine.  The problem is the size limit; I just can't cache enough tiles.

So now I'm trying to use WebSQL (in which you can increase the database size).  I can store the tiles in the database, but the problem arises when I try to retrieve them.  WebSQL transactions are asynchronous, so when I issue the statement to retrieve the tile from the database, the getTileUrl function continues and returns nothing (a gray tile) without waiting for the query to return.

Is there some way to make the process wait for the readTransaction function below to return a value?

    dojo.extend(esri.layers.ArcGISTiledMapServiceLayer, {
        getTileUrl: function (level, row, col) {
            var url = tiledLayerSource + "/tile/" + level + "/" + row + "/" + col;
            url = window.location.protocol + '//' + window.location.host + proxyPage + "?" + url;
            var result;
            if (usingWebSQL) {
                // the next statement is asynchronous, so this approach fails
                dbMapCache.readTransaction(function (t) {
                    var s = 'select IMAGE FROM TILES where url = ?';
                    t.executeSql(s, [url], function (t, rs) {
                        if (rs && rs.rows.length > 0) {
                            var r = rs.rows.item(0);
                            result = "data:image;base64," + r.IMAGE;
                        }
                        else {
                            console.log('query returned nothing, pass url and load tile');
                            ioWorker.postMessage([url]);
                            result = url;
                        }
                    });
                });
            }
            else {
                // this approach uses localStorage, and it works
                if (localStorage.getItem(url) !== null) {
                    console.log("in local storage");
                    result = "data:image;base64," + localStorage.getItem(url);
                } else {
                    if (keepCaching) {
                        console.log("not in local storage, pass url and load tile");
                        ioWorker.postMessage([url]);
                    }
                    result = url;
                }
            }
            return result;
        }
    });
0 Kudos
7 Replies
LewLadd
New Contributor III
Hi Ethan,
      I've run into the identical problem. Have you come up with a solution?
Thanks,
Lew.
0 Kudos
LewLadd
New Contributor III
Hi Ethan,
    I haven't tried it yet, but this looks promising:

    http://onilabs.com/blog/stratifying-asynchronous-storage

   From that page, the claim is you can do this:
var db = require("webdatabase").openDatabase("CandyDB", ...);
try {  
   var kids = db.executeSql("SELECT * FROM kids").rows;  
   db.executeSql("INSERT INTO kids (name) VALUES (:name);", [kids[0]]);  
   alert("done");
} 
catch(e) { 
 alert("something went wrong");
}
0 Kudos
EthanBodin
New Contributor
Lew, thanks for the lead, which I tried and did not pan out.  So I still have the same problem.  A co-worker is going to bring this to the developer summit next week to see if we can get any farther.  I'll post whatever we find out.
0 Kudos
MattMoyles
New Contributor III
I'd like to point out that WebSQL is depercated on most browsers, but will prob. be supported for backwards compatability reasons for some time.
0 Kudos
EthanBodin
New Contributor
Matt,

You are right on target, but I think the asynchronous issue is still relevant going forward.  Unfortunately, although WebSQL is being deprecated as a standard,  it is actually more widely implemented in current browsers than the newer alternatives like FileAPI and IndexedDB.  So we expect to be able to use those in the future.  We can also hope that the browser vendors implement the synchronous interfaces described in the standard, but given what happened with WebSQL (which also has asynchronous methods defined but not implemented in the browsers), in general it would be really nice to be able to use the asynchronous methods in the Esri API.

- Ethan
0 Kudos
by Anonymous User
Not applicable
Ethan,

This seems like a case for using dojo deferred objects.  Below is a link showing the implementation.

I might try these steps.

1) Create a defered object
2) Split out the conditions in getTileUrl into functions getTileUrlWebSQL AND getTileUrlLocal
3) Use the defered on getTileUrlWebSQL (b/ its an asynchronous function)
 
/* EXAMPLE OF USING A DEFERRED
http://dojotoolkit.org/reference-guide/1.7/dojo/Deferred.html
  
var deferred = someAsyncFunction();
deferred.then(function(value){
    // Do something on success.
},
function(error){
    // Do something on failure.
});

Give it a shot.  Conceptually I think it may work.

Regards,
Doug Carroll
Esri Support Services
0 Kudos
EthanBodin
New Contributor
Deferred objects were something we tried also, though the jQuery version and not the dojo version, though we would expect the results to be the same: not successful.

After trying all of the good ideas arising from this blog and discussing with Esri support, it appears the only way to handle this would be through an enhancement to the ArcGIS API for JavaScript itself, perhaps in the form of a new override-able method similar to getTileUrl method with a callback function that would give the tile to the map asynchronously.  This has been submitted as NIM079527.
0 Kudos