Newer
Older
weather-servlet / src / main / webapp / charts.js
var CIRCLE_RADIUS = 0.75, REAR_ARROW_WIDTH = 0.18,
    FRONT_ARROW_WIDTH = 0.15, BEARING_ARROW_WIDTH = 0.08,
    BEARING_ARROW_LENGTH = 0.65, LETTER_SIZE = 0.1,
    LETTER_RADIUS = 0.88, global_angle = 200, COOKIE = "ressie_angle",
    spin_initialized = 0, dragging = 0, start_drag_angle = 0, old_global_angle = 0,
    WIND_STEPS = 20, WIND_TIME = 1,
    wind_start = 0, wind_end = 0, wind_step = 0, wind_timer = null,
    last_angle = null, cache_ctx = null, cache_canvas = null, minWidthHeight = 1, current_angle = null;

function getContext() {
  if (cache_ctx) {
    return cache_ctx;
  }
  if (!cache_canvas) {
    cache_canvas = document.getElementById("compass");
    if (typeof G_vmlCanvasManager != "undefined") {
      cache_canvas = G_vmlCanvasManager.initElement(cache_canvas);
    }
    minWidthHeight = Math.min(cache_canvas.width, cache_canvas.height);
  }
  if (cache_canvas) {
    cache_ctx = cache_canvas.getContext("2d");
  }
  return cache_ctx;
}

function svgPathMap(svg_path, move_fun, line_fun, curve_fun, close_fun) {
  var i = 0;
  while (i < svg_path.length) {
    if (svg_path[i] == "M") {
      var move_xy = svg_path[i+1].split(",");
      move_fun(move_xy[0], move_xy[1]);
      i = i + 2;
    } else if (svg_path[i] == "L") {
      var line_xy = svg_path[i+1].split(",");
      line_fun(line_xy[0], line_xy[1]);
      i = i + 2;
    } else if (svg_path[i] == "C") {
      var xy1 = svg_path[i+1].split(","), xy2 = svg_path[i+2].split(","), xy3 = svg_path[i+3].split(",");
      curve_fun(xy1[0], xy1[1], xy2[0], xy2[1], xy3[0], xy3[1]);
      i = i + 4;
    } else if (svg_path[i] == "z") {
      close_fun();
      i++;
    } else {
      alert("Unknown path element: " + svg_path[i]);
    }
  }
}

function drawPath(svg_path, ctx, fill, scale) {
  if (fill) {
    ctx.beginPath();
    svgPathMap(svg_path, function(x, y) {ctx.moveTo(x*scale, y*scale);},
                         function(x, y) {ctx.lineTo(x*scale, y*scale);},
                         function(x1, y1, x2, y2, x, y) {ctx.bezierCurveTo(x1*scale, y1*scale, x2*scale,
                                                                           y2*scale, x*scale, y*scale);},
                         function() {ctx.fill();});
  } else {
    ctx.beginPath();
    svgPathMap(svg_path, function(x, y) {ctx.moveTo(x*scale, y*scale);},
                         function(x, y) {ctx.lineTo(x*scale, y*scale);},
                         function(x1, y1, x2, y2, x, y) {ctx.bezierCurveTo(x1*scale, y1*scale, x2*scale,
                                                                           y2*scale, x*scale, y*scale);},
                         function() {ctx.stroke();});
  }
}

function drawCompass(ctx, scale) {
  // draw a compass, centred at (0, 0), bounded by (-scale, -scale), (scale, scale)
  ctx.save();
  // draw background semi transparent filled circle
  //ctx.lineWidth = 0.75;
  ctx.strokeStyle = "rgb(136,136,136)";
  ctx.fillStyle="rgba(255,255,255,0.5)";
  ctx.beginPath();
  ctx.arc(0, 0, CIRCLE_RADIUS * scale, 0, Math.PI * 2, true);
  ctx.closePath();
  ctx.fill();
  ctx.beginPath();
  ctx.arc(0, 0, CIRCLE_RADIUS * scale, 0, Math.PI * 2, true);
  ctx.closePath();
  ctx.stroke();
  // draw background N, S, E, W arrows in blue
  var QUART = Math.sqrt(2)/2 * CIRCLE_RADIUS,
      SVG_BACK_ARROWS = ("M 0,-" + REAR_ARROW_WIDTH + " L " + QUART + ",-" + QUART +
                    " L " + REAR_ARROW_WIDTH + ",0 L " + QUART + "," + QUART +
                    " L 0," + REAR_ARROW_WIDTH + " L -" + QUART + "," + QUART +
                    " L -" + REAR_ARROW_WIDTH + ",0 L -" + QUART + ",-" + QUART +
                    " L 0,-" + REAR_ARROW_WIDTH + " z").split(" ");
  ctx.fillStyle = "rgb(0, 0, 255)";
  drawPath(SVG_BACK_ARROWS, ctx, 1, scale);
  drawPath(SVG_BACK_ARROWS, ctx, 0, scale);

  var FRONT_45 = QUART * FRONT_ARROW_WIDTH * scale;
  for (var i = 0; i < 2; i++) {
    ctx.save();
    for(var j=0; j<4; j++) {
      ctx.beginPath();
      ctx.moveTo(0, 0);
      ctx.lineTo(FRONT_45, -FRONT_45);
      ctx.lineTo(0, -scale);
      ctx.lineTo(0, 0);
      ctx.closePath();
      if (i === 0) {
        ctx.fillStyle = "rgb(255,0,0)";
        ctx.fill();
      } else {
        ctx.stroke();
      }
      ctx.beginPath();
      ctx.moveTo(0, 0);
      ctx.lineTo(-FRONT_45, -FRONT_45);
      ctx.lineTo(0, -scale);
      ctx.lineTo(0, 0);
      ctx.closePath();
      if (i === 0) {
        ctx.fillStyle = "rgb(255,255,255)";
        ctx.fill();
      } else {
        ctx.stroke();
      }
      ctx.rotate(Math.PI / 2);
    }
    ctx.restore();
  }
  var NORTH_PATH = ("M -" + (LETTER_SIZE / 1.4) + ",-" + (LETTER_RADIUS - LETTER_SIZE) +
                " L -" + (LETTER_SIZE / 1.4) + ",-" + (LETTER_RADIUS + LETTER_SIZE) +
                " L " + (LETTER_SIZE / 1.4) + ",-" + (LETTER_RADIUS - LETTER_SIZE) +
                " L " + (LETTER_SIZE / 1.4) + ",-" + (LETTER_RADIUS + LETTER_SIZE) +
                " z").split(" ");
  ctx.lineWidth = 3.0;
  ctx.lineJoin = 'round';
  ctx.strokeStyle = "rgba(255,255,255,0.9)";
  drawPath(NORTH_PATH, ctx, 0, scale);
  ctx.lineWidth = 1.0;
  ctx.strokeStyle = "rgb(0,0,0)";
  drawPath(NORTH_PATH, ctx, 0, scale);
  ctx.restore();
}

function drawArrow(ctx, scale) {
  ctx.save();
  var ARROW_PATH = ("M -0.05,-0.15 L 0.05,-0.15 L 0.05,0.25 L 0.1,0.25 " +
                    "L 0,0.4 L -0.1,0.25 L -0.05,0.25 L -0.05,-0.15 z").split(" ");
  ctx.lineWidth = 1.0;
  ctx.strokeStyle = "rgb(0,0,0)";
  ctx.fillStyle = "rgb(238, 238, 0)";
  ctx.lineJoin = "round";
  drawPath(ARROW_PATH, ctx, 1, scale);
  drawPath(ARROW_PATH, ctx, 0, scale);
  ctx.restore();
}

function drawRessie(ctx, scale) {
  var SVG_PATH=("M 242.14286,620.93361 C 242.14286,620.93361 307.14286,540.93361 318.57143,526.6479 "+
                "C 330,512.36218 309.28571,498.07647 309.28571,498.07647 C 309.28571,498.07647 291.19688,481.65613 287.85714,477.36218 "+
                "C 282.85715,470.93361 290.78801,462.50108 298.57143,463.79075 C 310.72728,465.80491 334.28571,463.79075 347.85714,456.6479 "+
                "C 361.42857,449.50504 360.71429,440.21933 370,435.21933 C 379.28571,430.21933 390,425.93361 407.14286,423.07647 "+
                "C 424.05247,420.2582 430.71429,413.07647 437.14286,409.50504 C 443.57143,405.93361 450,398.07647 451.42857,405.21933 "+
                "C 452.85714,412.36218 480,520.93361 480,520.93361 C 480,520.93361 447.14286,551.6479 440,555.93361 "+
                "C 432.85714,560.21933 427.85714,567.36218 423.57143,567.36218 C 419.28571,567.36218 409.28571,574.50504 409.28571,574.50504 "+
                "L 408.57143,594.50504 C 408.57143,594.50504 407.85714,615.21933 405,615.93361 "+
                "C 402.14286,616.6479 392.14286,625.93361 375,613.79075 C 357.85714,601.6479 337.14286,593.07647 327.14286,595.93361 "+
                "C 317.14286,598.79075 298.57143,603.07647 298.57143,603.07647 C 298.57143,603.07647 265,641.6479 259.28571,641.6479 "+
                "C 253.57143,641.6479 243.57143,643.07647 240.71429,635.93361 C 237.85714,628.79076 242.14286,621.6479 242.14286,620.93361 z").split(" ");
  var max_x = 0, max_y = 0, min_x = 2000, min_y = 2000;
  function bound(x, y) {
    max_x = Math.max(max_x, x);
    max_y = Math.max(max_y, y);
    min_x = Math.min(min_x, x);
    min_y = Math.min(min_y, y);
  }
  svgPathMap(SVG_PATH, bound, bound, function(cx1, cy1, cx2, cy2, x, y) {bound(x, y);}, function() {});
  ctx.save();
  //ctx.translate(minWidthHeight / 2, minWidthHeight / 2);
//  cCtx.rotate(Math.PI / 180 * ressie_angle);
  var thisScale = scale / Math.max(max_x - min_x, max_y - min_y) * 2 / Math.sqrt(2);
  ctx.scale(thisScale, thisScale);
  ctx.translate(-(min_x + max_x) / 2, -(min_y + max_y) / 2);
  ctx.beginPath();
  ctx.strokeStyle = "rgb(136, 136, 136)";
  ctx.fillStyle = "rgb(34, 170, 238)";
  svgPathMap(SVG_PATH, function(x, y) {ctx.moveTo(x, y);},
                       function(x, y) {ctx.lineTo(x, y);},
                       function(x1, y1, x2, y2, x, y) {ctx.bezierCurveTo(x1, y1, x2, y2, x, y);},
                       function() {ctx.closePath(); ctx.fill();});
  svgPathMap(SVG_PATH, function(x, y) {ctx.moveTo(x, y);},
                       function(x, y) {ctx.lineTo(x, y);},
                       function(x1, y1, x2, y2, x, y) {ctx.bezierCurveTo(x1, y1, x2, y2, x, y);},
                       function() {ctx.closePath(); ctx.stroke();});
  ctx.restore();
}

function drawAll(ctx, wind_angle) {
  ctx.clearRect(0, 0, cache_canvas.width, cache_canvas.height);
  ctx.save();
  ctx.translate(minWidthHeight / 2, minWidthHeight / 2);
  ctx.rotate(Math.PI * global_angle / 180);
  drawRessie(ctx, minWidthHeight / 2);
  ctx.restore();
  ctx.save();
  ctx.translate(minWidthHeight / 4, minWidthHeight / 4);
  ctx.rotate(Math.PI * global_angle / 180);
  drawCompass(ctx, minWidthHeight / 4);
  ctx.restore();
  ctx.save();
  ctx.translate(minWidthHeight / 2, minWidthHeight / 2);
  ctx.rotate(Math.PI * (global_angle) / 180 + wind_angle);
  drawArrow(ctx, minWidthHeight / 2);
  ctx.restore();
}

function initSpin(canvas) {
  var start, end, value;
  if (document.cookie.length > 0) {
    start = document.cookie.indexOf(COOKIE + "=");
    if ((start != -1) && (document.cookie.length >= start + COOKIE.length + 1)) {
      end = document.cookie.indexOf(";", start + COOKIE.length + 1);
      if (end == -1) {
        end = document.cookie.length;
      }
      global_angle = parseInt(unescape(document.cookie.substring(start + COOKIE.length + 1, end)), 10);
    }
  }
  $('#compass').mousedown(function(event){
    if (dragging === 0) {
        var x = event.clientX - (canvas.width / 2),
            y = event.clientY - (canvas.height / 2),
            elem = canvas;
        while (elem) {
            x -= elem.offsetLeft;
            y -= elem.offsetTop;
            elem = elem.offsetParent;
        }
        start_drag_angle = Math.atan2(y, x);
        old_global_angle = global_angle;
        dragging = 1;
    }
  }).mousemove(function(event){
    if (dragging == 1) {
        var x = event.clientX - (canvas.width / 2),
            y = event.clientY - (canvas.height / 2),
            elem = canvas;
        while (elem) {
            x -= elem.offsetLeft;
            y -= elem.offsetTop;
            elem = elem.offsetParent;
        }
        var current = Math.atan2(y, x);
        global_angle = old_global_angle + (current - start_drag_angle) * 180 / Math.PI;
        drawAll(cache_ctx, current_angle);
    }
  }).mouseup(function(event){
    if (dragging == 1) {
      dragging = 0;
      if (global_angle != old_global_angle) {
        drawAll(cache_ctx, current_angle);
        var date = new Date();
        date.setTime(date.getTime() + (10*365.25*60*60*1000));
        var expires = "; expires="+date.toGMTString();
        document.cookie = COOKIE + "=" + Math.floor(global_angle) + expires + "path=/";
      }
    }
  }).mouseout(function(event){
    if (dragging == 1) {
      dragging = 0;
      global_angle = old_global_angle;
      drawAll(cache_ctx, current_angle);
    }
  });
  spin_initialized = 1;
}

function drawStep() {
  var ctx = getContext();
  if (ctx) {
    if (spin_initialized === 0) {
      initSpin(cache_canvas);
    }
    var ratio = (1-Math.cos(wind_step / WIND_STEPS * Math.PI))/2;
    current_angle = wind_start + (wind_end - wind_start) * ratio;
    drawAll(ctx, current_angle);
  }
  if (wind_step < WIND_STEPS) {
    wind_step = wind_step + 1;
  } else {
    clearInterval(wind_timer);
    wind_timer = null;
    wind_start = wind_end;
  }
}

function drawWind(angle) {
  if (last_angle && (last_angle === angle)) {
    return;
  }
  last_angle = angle;
  wind_end = angle / 180 * Math.PI;
  if ((wind_end - wind_start) > Math.PI) {
    wind_end = wind_end - 2 * Math.PI;
  }
  wind_step = 0;
  if (wind_timer !== null) {
    clearInterval(wind_timer);
    wind_start = current_angle;
  }
  wind_timer = setInterval(drawStep, WIND_TIME * 1000 / WIND_STEPS);
}