move debug output to console.log; make DB upgrade work more reasonably
[lantea.git] / js / map.js
index b35a89cce8d62e3946ba670daf56f1998fda5004..a6988181b437e3c6af92f91d0dedcf817994d94d 100644 (file)
--- a/js/map.js
+++ b/js/map.js
@@ -122,7 +122,7 @@ function initMap() {
       gWaitCounter++;
       gTrackStore.getList(function(aTPoints) {
         if (gDebug)
       gWaitCounter++;
       gTrackStore.getList(function(aTPoints) {
         if (gDebug)
-          document.getElementById("debug").textContent = aTPoints.length + " points loaded.";
+          console.log(aTPoints.length + " points loaded.");
         if (aTPoints.length) {
           gTrack = aTPoints;
         }
         if (aTPoints.length) {
           gTrack = aTPoints;
         }
@@ -133,7 +133,7 @@ function initMap() {
       setTimeout(getPersistentPrefs, 100);
     loopCnt++;
     if (loopCnt > 50) {
       setTimeout(getPersistentPrefs, 100);
     loopCnt++;
     if (loopCnt > 50) {
-      document.getElementById("debug").textContent = "Loading prefs failed.";
+      console.log("Loading prefs failed.");
     }
   };
   getPersistentPrefs();
     }
   };
   getPersistentPrefs();
@@ -165,13 +165,14 @@ function initMap() {
 function resizeAndDraw() {
   var viewportWidth = Math.min(window.innerWidth, window.outerWidth);
   var viewportHeight = Math.min(window.innerHeight, window.outerHeight);
 function resizeAndDraw() {
   var viewportWidth = Math.min(window.innerWidth, window.outerWidth);
   var viewportHeight = Math.min(window.innerHeight, window.outerHeight);
-
-  gMapCanvas.width = viewportWidth;
-  gMapCanvas.height = viewportHeight;
-  gTrackCanvas.width = viewportWidth;
-  gTrackCanvas.height = viewportHeight;
-  drawMap();
-  showUI();
+  if (gMapCanvas && gTrackCanvas) {
+    gMapCanvas.width = viewportWidth;
+    gMapCanvas.height = viewportHeight;
+    gTrackCanvas.width = viewportWidth;
+    gTrackCanvas.height = viewportHeight;
+    drawMap();
+    showUI();
+  }
 }
 
 function zoomIn() {
 }
 
 function zoomIn() {
@@ -224,21 +225,11 @@ function mod(a, b) {
   return ((a % b) + b) % b;
 }
 
   return ((a % b) + b) % b;
 }
 
-function normaliseIndices(x, y, z) {
-  var zoomFactor = Math.pow(2, z);
-  return {x: mod(x, zoomFactor),
-          y: mod(y, zoomFactor),
-          z: z};
-}
-
-function tileURL(x, y, z) {
-  var norm = normaliseIndices(x, y, z);
-  return gMapStyles[gActiveMap].url
-         .replace("{x}", norm.x)
-         .replace("{y}", norm.y)
-         .replace("{z}", norm.z)
-         .replace("[a-c]", String.fromCharCode(97 + Math.floor(Math.random() * 2)))
-         .replace("[1-4]", 1 + Math.floor(Math.random() * 3));
+function normalizeCoords(aCoords) {
+  var zoomFactor = Math.pow(2, aCoords.z);
+  return {x: mod(aCoords.x, zoomFactor),
+          y: mod(aCoords.y, zoomFactor),
+          z: aCoords.z};
 }
 
 // Returns true if the tile is outside the current view.
 }
 
 // Returns true if the tile is outside the current view.
@@ -260,7 +251,7 @@ function isOutsideWindow(t) {
 }
 
 function encodeIndex(x, y, z) {
 }
 
 function encodeIndex(x, y, z) {
-  var norm = normaliseIndices(x, y, z);
+  var norm = normalizeCoords({x: x, y: y, z: z});
   return norm.x + "," + norm.y + "," + norm.z;
 }
 
   return norm.x + "," + norm.y + "," + norm.z;
 }
 
@@ -297,32 +288,28 @@ function drawMap() {
       // and the performance sucks (more than expected).
       var xoff = Math.round((x * size - xMin) / gZoomFactor);
       var yoff = Math.round((y * size - yMin) / gZoomFactor);
       // and the performance sucks (more than expected).
       var xoff = Math.round((x * size - xMin) / gZoomFactor);
       var yoff = Math.round((y * size - yMin) / gZoomFactor);
-      var tileKey = encodeIndex(x, y, gPos.z);
-      if (gTiles[tileKey] && gTiles[tileKey].complete) {
-        gMapContext.drawImage(gTiles[tileKey], xoff, yoff);
-      }
-      else {
-        // Draw placeholder, and then initiate loading/drawing of real one.
-        gMapContext.drawImage(gLoadingTile, xoff, yoff);
-        if (!gTiles[tileKey]) {
-          gTiles[tileKey] = new Image();
-          // Use alt field to communicate info to .onload.
-          gTiles[tileKey].alt = gPos.z + ":" + xoff + ":" + yoff;
-          gTiles[tileKey].src = tileURL(x, y, gPos.z);
-          gTiles[tileKey].onload = function() {
-            var tdata = this.alt.split(":", 3);
-            // Draw the tile if we're still looking at the same zoom.
-            if (tdata[0] == gPos.z) {
-              gMapContext.drawImage(this, tdata[1], tdata[2]);
-            }
-            this.alt = "";
+      // Draw placeholder, and then initiate loading/drawing of real one.
+      gMapContext.drawImage(gLoadingTile, xoff, yoff);
+
+      gTileService.get(gActiveMap, {x: x, y: y, z: gPos.z}, function(aImage, aStyle, aCoords) {
+        // Only draw if this applies for the current view.
+        if ((aStyle == gActiveMap) && (aCoords.z == gPos.z)) {
+          var ixMin = gPos.x - wid / 2;
+          var iyMin = gPos.y - ht / 2;
+          var ixoff = Math.round((aCoords.x * size - ixMin) / gZoomFactor);
+          var iyoff = Math.round((aCoords.y * size - iyMin) / gZoomFactor);
+          // Would be nice to draw directly from the blob, but that crashes:
+          // gMapContext.drawImage(aImage, ixoff, iyoff);
+          var URL = window.URL;
+          var imgURL = URL.createObjectURL(aImage);
+          var imgObj = new Image();
+          imgObj.src = imgURL;
+          imgObj.onload = function() {
+            gMapContext.drawImage(imgObj, ixoff, iyoff);
+            URL.revokeObjectURL(imgURL);
           }
         }
           }
         }
-        else {
-          // Update position to draw this image to when loaded.
-          gTiles[tileKey].alt = gPos.z + ":" + xoff + ":" + yoff;
-        }
-      }
+      });
     }
   }
   gLastDrawnPoint = null;
     }
   }
   gLastDrawnPoint = null;
@@ -489,10 +476,9 @@ var mapEvHandler = {
                        y: gPos.y + (x - gMapCanvas.height / 2) * gZoomFactor};
         var gpsCoord = xy2gps(ptCoord.x, ptCoord.y);
         var pt2Coord = gps2xy(gpsCoord.latitude, gpsCoord.longitude);
                        y: gPos.y + (x - gMapCanvas.height / 2) * gZoomFactor};
         var gpsCoord = xy2gps(ptCoord.x, ptCoord.y);
         var pt2Coord = gps2xy(gpsCoord.latitude, gpsCoord.longitude);
-        document.getElementById("debug").textContent =
-            ptCoord.x + "/" + ptCoord.y + " - " +
-            gpsCoord.latitude + "/" + gpsCoord.longitude + " - " +
-            pt2Coord.x + "/" + pt2Coord.y;
+        console.log(ptCoord.x + "/" + ptCoord.y + " - " +
+                    gpsCoord.latitude + "/" + gpsCoord.longitude + " - " +
+                    pt2Coord.x + "/" + pt2Coord.y);
         */
 
         var newZoomLevel = gPos.z + (delta > 0 ? 1 : -1);
         */
 
         var newZoomLevel = gPos.z + (delta > 0 ? 1 : -1);
@@ -605,7 +591,8 @@ function startTracking() {
       function(error) {
         // Ignore erros for the moment, but this is good for debugging.
         // See https://developer.mozilla.org/en/Using_geolocation#Handling_errors
       function(error) {
         // Ignore erros for the moment, but this is good for debugging.
         // See https://developer.mozilla.org/en/Using_geolocation#Handling_errors
-        document.getElementById("debug").textContent = error.message;
+        if (gDebug)
+          console.log(error.message);
       },
       {enableHighAccuracy: true}
     );
       },
       {enableHighAccuracy: true}
     );
@@ -623,3 +610,91 @@ function clearTrack() {
   gTrackStore.clear();
   drawMap();
 }
   gTrackStore.clear();
   drawMap();
 }
+
+var gTileService = {
+  objStore: "tilecache",
+
+  get: function(aStyle, aCoords, aCallback) {
+    var norm = normalizeCoords(aCoords);
+    var dbkey = aStyle + "::" + norm.x + "," + norm.y + "," + norm.z;
+    this.getDBCache(dbkey, function(aResult, aEvent) {
+      if (aResult) {
+        // We did get a cached object.
+        // TODO: Look at the timestamp and trigger a reload when it's too old.
+        aCallback(aResult.image, aStyle, aCoords);
+      }
+      else {
+        // Retrieve image from the web and store it in the cache.
+        var XHR = new XMLHttpRequest();
+        XHR.open("GET",
+                 gMapStyles[aStyle].url
+                   .replace("{x}", norm.x)
+                   .replace("{y}", norm.y)
+                   .replace("{z}", norm.z)
+                   .replace("[a-c]", String.fromCharCode(97 + Math.floor(Math.random() * 2)))
+                   .replace("[1-4]", 1 + Math.floor(Math.random() * 3)),
+                 true);
+        XHR.responseType = "blob";
+        XHR.addEventListener("load", function () {
+          if (XHR.status === 200) {
+            var blob = XHR.response;
+            gTileService.setDBCache(dbkey, {image: blob, timestamp: Date.now()});
+            aCallback(blob, aStyle, aCoords);
+          }
+        }, false);
+        XHR.send();
+      }
+    });
+  },
+
+  getDBCache: function(aKey, aCallback) {
+    if (!mainDB)
+      return;
+    var transaction = mainDB.transaction([this.objStore]);
+    var request = transaction.objectStore(this.objStore).get(aKey);
+    request.onsuccess = function(event) {
+      aCallback(request.result, event);
+    };
+    request.onerror = function(event) {
+      // Errors can be handled here.
+      aCallback(undefined, event);
+    };
+  },
+
+  setDBCache: function(aKey, aValue, aCallback) {
+    if (!mainDB)
+      return;
+    var success = false;
+    var transaction = mainDB.transaction([this.objStore], "readwrite");
+    var objStore = transaction.objectStore(this.objStore);
+    var request = objStore.put(aValue, aKey);
+    request.onsuccess = function(event) {
+      success = true;
+      if (aCallback)
+        aCallback(success, event);
+    };
+    request.onerror = function(event) {
+      // Errors can be handled here.
+      if (aCallback)
+        aCallback(success, event);
+    };
+  },
+
+  unsetDBCache: function(aKey, aCallback) {
+    if (!mainDB)
+      return;
+    var success = false;
+    var transaction = mainDB.transaction([this.objStore], "readwrite");
+    var request = transaction.objectStore(this.objStore).delete(aKey);
+    request.onsuccess = function(event) {
+      success = true;
+      if (aCallback)
+        aCallback(success, event);
+    };
+    request.onerror = function(event) {
+      // Errors can be handled here.
+      if (aCallback)
+        aCallback(success, event);
+    }
+  }
+};