add actual maps to Lantea
authorRobert Kaiser <kairo@kairo.at>
Sun, 18 Dec 2011 15:18:54 +0000 (16:18 +0100)
committerRobert Kaiser <kairo@kairo.at>
Sun, 18 Dec 2011 15:18:54 +0000 (16:18 +0100)
index.html
js/map.js [new file with mode: 0644]
js/ui.js [new file with mode: 0644]
manifest.appcache
style/lantea.css [new file with mode: 0644]

index a84b858..5b4cfe6 100644 (file)
@@ -1,13 +1,59 @@
+<!-- ***** BEGIN LICENSE BLOCK *****
+   - Version: MPL 1.1/GPL 2.0/LGPL 2.1
+   -
+   - The contents of this file are subject to the Mozilla Public License Version
+   - 1.1 (the "License"); you may not use this file except in compliance with
+   - the License. You may obtain a copy of the License at
+   - http://www.mozilla.org/MPL/
+   -
+   - Software distributed under the License is distributed on an "AS IS" basis,
+   - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+   - for the specific language governing rights and limitations under the
+   - License.
+   -
+   - The Original Code is Lantea mapping/tracking web app.
+   -
+   - The Initial Developer of the Original Code is
+   - Robert Kaiser <kairo@kairo.at>.
+   - Portions created by the Initial Developer are Copyright (C) 2011
+   - the Initial Developer. All Rights Reserved.
+   -
+   - Contributor(s):
+   -   Robert Kaiser <kairo@kairo.at> (original author)
+   -
+   - Alternatively, the contents of this file may be used under the terms of
+   - either the GNU General Public License Version 2 or later (the "GPL"), or
+   - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+   - in which case the provisions of the GPL or the LGPL are applicable instead
+   - of those above. If you wish to allow use of your version of this file only
+   - under the terms of either the GPL or the LGPL, and not to allow others to
+   - use your version of this file under the terms of the MPL, indicate your
+   - decision by deleting the provisions above and replace them with the notice
+   - and other provisions required by the LGPL or the GPL. If you do not delete
+   - the provisions above, a recipient may use your version of this file under
+   - the terms of any one of the MPL, the GPL or the LGPL.
+   -
+   - ***** END LICENSE BLOCK ***** -->
+
 <!DOCTYPE html>
 <html manifest="manifest.appcache">
 <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
   <title>Lantea</title>
-  <!-- script src="js/lantea.js"></script -->
-  <!-- link rel="stylesheet" href="style/lantea.css" -->
+  <script src="js/map.js"></script>
+  <script src="js/ui.js"></script>
+  <link rel="stylesheet" href="style/lantea.css">
 </head>
 <body>
 <h1>Lantea</h1>
 
+<canvas id="map" width="500" height="500">
+  Please use a browser that supports &lt;canvas&gt; elements.
+</canvas>
+
+<p id="copyright">Maps &copy;
+<a href="http://www.openstreetmap.org/">OpenStreetMap</a> contributors,
+<a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a></p>
 
-</body></html>
\ No newline at end of file
+</body>
+</html>
\ No newline at end of file
diff --git a/js/map.js b/js/map.js
new file mode 100644 (file)
index 0000000..2a65f78
--- /dev/null
+++ b/js/map.js
@@ -0,0 +1,276 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Lantea mapping/tracking web app.
+ *
+ * The Initial Developer of the Original Code is
+ * Robert Kaiser <kairo@kairo.at>.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Robert Kaiser <kairo@kairo.at>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+var gCanvas, gContext;
+
+var gTileSize = 256;
+var gMaxZoom = 18; // The minimum is 0.
+
+//drawMap();
+
+var gPos = {x: 35630000.0, // Current position in the map in pixels at the maximum zoom level (18)
+            y: 23670000.0, // The range is 0-67108864 (2^gMaxZoom * gTileSize)
+            z: 5.0}; // This can be fractional if we are between zoom levels.
+
+var gLastMouseX = 0;
+var gLastMouseY = 0;
+
+// Used as an assiciative array. They keys have to be strings, ours will be "xindex,yindex,zindex" e.g. "13,245,12".
+var gTiles = {};
+
+var gDragging = false;
+var gZoomTouchID;
+
+window.onload = function() {
+  gCanvas = document.getElementById("map");
+  gContext = gCanvas.getContext("2d");
+
+  gCanvas.addEventListener("mouseup", mapEvHandler, false);
+  gCanvas.addEventListener("mousemove", mapEvHandler, false);
+  gCanvas.addEventListener("mousedown", mapEvHandler, false);
+  gCanvas.addEventListener("mouseout", mapEvHandler, false);
+
+  gCanvas.addEventListener("touchstart", mapEvHandler, false);
+  gCanvas.addEventListener("touchmove", mapEvHandler, false);
+  gCanvas.addEventListener("touchend", mapEvHandler, false);
+  gCanvas.addEventListener("touchcancel", mapEvHandler, false);
+  gCanvas.addEventListener("touchleave", mapEvHandler, false);
+
+  gCanvas.addEventListener("DOMMouseScroll", mapEvHandler, false);
+  gCanvas.addEventListener("mousewheel", mapEvHandler, false);
+
+  resizeAndDraw();
+}
+
+window.onresize = function() {
+  resizeAndDraw();
+}
+
+function resizeAndDraw() {
+  var viewportWidth = window.innerWidth;
+  var viewportHeight = window.innerHeight;
+
+  var canvasWidth = viewportWidth * 0.98;
+  var canvasHeight = (viewportHeight-110) * 0.98;
+  gCanvas.style.position = "fixed";
+  gCanvas.width = canvasWidth;
+  gCanvas.height = canvasHeight;
+  drawMap();
+}
+
+function zoomIn() {
+  if (gPos.z < gMaxZoom) {
+    gPos.z++;
+    drawMap();
+  }
+}
+
+function zoomOut() {
+  if (gPos.z > 0) {
+    gPos.z--;
+    drawMap();
+  }
+}
+
+// A sane mod function that works for negative numbers.
+// Returns a % b.
+function mod(a, b) {
+  return ((a % b) + b) % b;
+}
+
+function normaliseIndices(x, y, z) {
+  return {x: mod(x, Math.pow(2, z)),
+          y: mod(y, Math.pow(2, z)),
+          z: z};
+}
+
+function tileURL(x, y, z) {
+  var norm = normaliseIndices(x, y, z);
+  var url = "http://tile.openstreetmap.org/" + norm.z + "/" + norm.x + "/" + norm.y + ".png";
+  return url;
+}
+
+// Returns true if the tile is outside the current view.
+function isOutsideWindow(t) {
+  var pos = decodeIndex(t);
+  var x = pos[0];
+  var y = pos[1];
+  var z = pos[2];
+
+  var wid = gCanvas.width * Math.pow(2, gMaxZoom - z);
+  var ht = gCanvas.height * Math.pow(2, gMaxZoom - z);
+
+  x *= Math.pow(2, gMaxZoom - z);
+  y *= Math.pow(2, gMaxZoom - z);
+
+  var sz = gTileSize * Math.pow(2, gMaxZoom - z);
+  if (x > gPos.x + wid / 2 || y > gPos.y + ht / 2 ||
+      x + sz < gPos.x - wid / 2 || y - sz < gPos.y - ht / 2)
+    return true;
+  return false;
+}
+
+function encodeIndex(x, y, z) {
+  var norm = normaliseIndices(x, y, z);
+  return norm.x + "," + norm.y + "," + norm.z;
+}
+
+function decodeIndex(encodedIdx) {
+  return encodedIdx.split(",", 3);
+}
+
+function drawMap() {
+  // Go through all the currently loaded tiles. If we don't want any of them remove them.
+  // for (t in gTiles) {
+  //   if (isOutsideWindow(t))
+  //     delete gTiles[t];
+  // }
+  var z = Math.round(gPos.z);
+  var wid = gCanvas.width * Math.pow(2, gMaxZoom - z); // Width in level 18 pixels.
+  var ht = gCanvas.height * Math.pow(2, gMaxZoom - z); // Height in level 18 pixels.
+  var sz = gTileSize * Math.pow(2, gMaxZoom - z); // Tile size in level 18 pixels.
+
+  var xMin = gPos.x - wid / 2; // Corners of the window in level 18 pixels.
+  var yMin = gPos.y - ht / 2;
+  var xMax = gPos.x + wid / 2;
+  var yMax = gPos.y + ht / 2;
+
+  // Go through all the tiles we want. If any of them aren't loaded or being loaded, do so.
+  for (var x = Math.floor(xMin / sz); x < Math.ceil(xMax / sz); ++x) {
+    for (var y = Math.floor(yMin / sz); y < Math.ceil(yMax / sz); ++y) {
+      var xoff = (x * sz - xMin) / Math.pow(2, gMaxZoom - z);
+      var yoff = (y * sz - yMin) / Math.pow(2, gMaxZoom - z);
+      var tileKey = encodeIndex(x, y, z);
+      if (gTiles[tileKey] && gTiles[tileKey].complete) {
+        // Round here is **CRUICIAL** otherwise the images are filtered and the performance sucks (more than expected).
+        gContext.drawImage(gTiles[tileKey], Math.round(xoff), Math.round(yoff));
+      }
+      else {
+        if (!gTiles[tileKey]) {
+          gTiles[tileKey] = new Image();
+          gTiles[tileKey].src = tileURL(x, y, gPos.z);
+          gTiles[tileKey].onload = function() {
+            // TODO: Just render this tile where it should be.
+            // context.drawImage(gTiles[tileKey], Math.round(xoff), Math.round(yoff)); // Doesn't work for some reason.
+            drawMap();
+          }
+        }
+        gContext.fillStyle = "#ffffff";
+        gContext.fillRect(Math.round(xoff), Math.round(yoff), gTileSize, gTileSize);
+      }
+    }
+  }
+}
+
+var mapEvHandler = {
+  handleEvent: function(aEvent) {
+    var touchEvent = aEvent.type.indexOf('touch') != -1;
+
+    // Bail out on unwanted map moves, but not mousewheel events.
+    if (aEvent.type != "DOMMouseScroll" && aEvent.type != "mousewheel") {
+      // Bail out if this is neither a touch nor left-click.
+      if (!touchEvent && aEvent.button != 0)
+        return;
+
+      // Bail out if the started touch can't be found.
+      if (touchEvent && zoomstart &&
+          !aEvent.changedTouches.identifiedTouch(gZoomTouchID))
+        return;
+    }
+
+    var coordObj = touchEvent ?
+                   aEvent.changedTouches.identifiedTouch(gZoomTouchID) :
+                   aEvent;
+
+    switch (aEvent.type) {
+      case "mousedown":
+      case "touchstart":
+        if (touchEvent) {
+          zoomTouchID = aEvent.changedTouches.item(0).identifier;
+          coordObj = aEvent.changedTouches.identifiedTouch(gZoomTouchID);
+        }
+        var x = coordObj.clientX - gCanvas.offsetLeft;
+        var y = coordObj.clientY - gCanvas.offsetTop;
+        if (touchEvent || aEvent.button === 0) {
+          gDragging = true;
+        }
+        gLastMouseX = x;
+        gLastMouseY = y;
+        break;
+      case "mousemove":
+      case "touchmove":
+        var x = coordObj.clientX - gCanvas.offsetLeft;
+        var y = coordObj.clientY - gCanvas.offsetTop;
+        if (gDragging === true) {
+          var dX = x - gLastMouseX;
+          var dY = y - gLastMouseY;
+          gPos.x -= dX * Math.pow(2, gMaxZoom - gPos.z);
+          gPos.y -= dY * Math.pow(2, gMaxZoom - gPos.z);
+          drawMap();
+        }
+        gLastMouseX = x;
+        gLastMouseY = y;
+        break;
+      case "mouseup":
+      case "touchend":
+        gDragging = false;
+        break;
+      case "mouseout":
+      case "touchcancel":
+      case "touchleave":
+        //gDragging = false;
+        break;
+      case "DOMMouseScroll":
+      case "mousewheel":
+        var delta = 0;
+        if (aEvent.wheelDelta) {
+          delta = aEvent.wheelDelta / 120;
+          if (window.opera)
+            delta = -delta;
+        }
+        else if (aEvent.detail) {
+          delta = -aEvent.detail / 3;
+        }
+
+        if (delta > 0)
+          zoomIn();
+        else if (delta < 0)
+          zoomOut();
+        break;
+    }
+  }
+};
diff --git a/js/ui.js b/js/ui.js
new file mode 100644 (file)
index 0000000..74ea521
--- /dev/null
+++ b/js/ui.js
@@ -0,0 +1,38 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Lantea mapping/tracking web app.
+ *
+ * The Initial Developer of the Original Code is
+ * Robert Kaiser <kairo@kairo.at>.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Robert Kaiser <kairo@kairo.at>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
index afede5d..6719f97 100644 (file)
@@ -2,6 +2,10 @@ CACHE MANIFEST
 
 # /
 index.html
+manifest.webapp
+js/map.js
+js/ui.js
+style/lantea.css
 
 NETWORK:
 
diff --git a/style/lantea.css b/style/lantea.css
new file mode 100644 (file)
index 0000000..47af3ca
--- /dev/null
@@ -0,0 +1,49 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Lantea mapping/tracking web app.
+ *
+ * The Initial Developer of the Original Code is
+ * Robert Kaiser <kairo@kairo.at>.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Robert Kaiser <kairo@kairo.at>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+body {
+  font-family: sans-serif;
+}
+
+#copyright {
+  position: absolute;
+  bottom: .5em;
+  right: .5em;
+  margin: 0;
+  font-size: small;
+  opacity: .5;
+}