diff --git a/src/main/scala/uk/org/floop/msc/comet/WeatherActor.scala b/src/main/scala/uk/org/floop/msc/comet/WeatherActor.scala index e97c5b1..779dfb9 100644 --- a/src/main/scala/uk/org/floop/msc/comet/WeatherActor.scala +++ b/src/main/scala/uk/org/floop/msc/comet/WeatherActor.scala @@ -10,19 +10,64 @@ class WeatherActor(info: CometActorInitInfo) extends CometActor(info) { def defaultPrefix = "weather" + def COMPASS_ROSE = List("N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", + "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW") var currentWeather: List[Pair[String, Any]] = Nil val currentWeatherMap = new HashMap[String, Any]() + def direction(d: Option[Any]) = d match { + case Some(angle: Short) => COMPASS_ROSE((angle / 22.5 + 0.5).toInt % 16) + case None => "unknown" + } + + def knots(s: Option[Any]): String = s match { + case Some(mph: Short) => + val k = (mph / 1.15077945 + 0.5).toInt + if (k == 1) { + "1 knot" + } else { + k + " knots" + } + case None => "unknown" + } + + def temp(t: Option[Any]): String = t match { + case Some(f: Float) => + val c = (f - 32) * 5 / 9; + c.toDouble.formatted("%.1f") + case None => "unknown" + } + + def pressure(p: Option[Any]): String = p match { + case Some(inchesMercury: Float) => + val millibar = (inchesMercury * 33.86389).toInt + millibar + " mb" + case None => "unknown" + } + + def humidity(h: Option[Any]): String = h match { + case Some(p: Short) => p + "%" + case None => "unknown" + } + + def rain(r: Option[Any]): String = r match { + case Some(inHr: Float) => (inHr * 25.4).toInt + " mm/hr" + case None => "unknown" + } + def render: RenderOut = { new RenderOut(bind("view" -> - {currentWeather.map(pair => - - )} + + + + + +
{pair._1}{pair._2}
Wind Direction:{direction(currentWeatherMap.get("windDir"))}
Wind Speed:{knots(currentWeatherMap.get("windSpeed"))}
Temperature:{temp(currentWeatherMap.get("outTemp"))} °C
Air Pressure:{pressure(currentWeatherMap.get("barometer"))}
Relative Humidity:{humidity(currentWeatherMap.get("outHumidity"))}
Rain Rate:{rain(currentWeatherMap.get("rainRate"))}
), currentWeatherMap.get("windDir") match { - case Some(angle) => Run("drawCompass(" + angle + ")") + case Some(angle) => Run("drawWind(" + angle + ")") case None => Noop } ) diff --git a/src/main/scala/uk/org/floop/msc/rrd/DataStore.scala b/src/main/scala/uk/org/floop/msc/rrd/DataStore.scala index 279fa90..e574f5a 100644 --- a/src/main/scala/uk/org/floop/msc/rrd/DataStore.scala +++ b/src/main/scala/uk/org/floop/msc/rrd/DataStore.scala @@ -114,6 +114,7 @@ } }) sample.update() + //val fetchRequest = rrdb.createFetchRequest(ConsolFun.AVERAGE, ) } catch { case ex: IOException => println("StorePacket, IOException: " + ex.toString) case ex: IllegalArgumentException => println("StorePacket, IllegalArgumentException: " + ex.toString) diff --git a/src/main/webapp/charts.js b/src/main/webapp/charts.js index 8b5edea..335ee72 100644 --- a/src/main/webapp/charts.js +++ b/src/main/webapp/charts.js @@ -1,113 +1,27 @@ -var cCanvas, cCtx, ressie_angle = 0; -var CIRCLE_RADIUS = 0.75; -var REAR_ARROW_WIDTH = 0.18; -var FRONT_ARROW_WIDTH = 0.15; -var BEARING_ARROW_WIDTH = 0.08; -var BEARING_ARROW_LENGTH = 0.65; +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, + 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; -function drawCompass(angle) { - cCanvas = document.getElementById("compass"); - if (!cCanvas) { - return; +function getContext() { + if (cache_ctx) { + return cache_ctx; } - cCtx = cCanvas.getContext("2d"); - if (!cCtx) { - return; - } - cCtx.clearRect(0, 0, cCanvas.width, cCanvas.height); - var minWidthHeight = Math.min(cCanvas.width, cCanvas.height); - var FRONT_45 = minWidthHeight / 2 * CIRCLE_RADIUS * Math.sin(Math.PI / 4) * FRONT_ARROW_WIDTH; - cCtx.save(); - cCtx.translate(cCanvas.width / 2, cCanvas.height / 2); - cCtx.strokeStyle = "rgb(136, 136, 136)"; - cCtx.lineWidth = 0.75; - for (var i = 0; i < 2; i++) { - cCtx.beginPath(); - cCtx.arc(0, 0, minWidthHeight / 2 * CIRCLE_RADIUS, 0, Math.PI * 2, true); - cCtx.closePath(); - if (i == 0) { - cCtx.fillStyle = "rgba(255,255,255,0.5)"; - cCtx.fill(); - } else { - cCtx.stroke(); + 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); } - for (var i = 0; i < 2; i++) { - cCtx.beginPath(); - cCtx.moveTo(0, -minWidthHeight / 2 * REAR_ARROW_WIDTH); - cCtx.lineTo(minWidthHeight / 2 * CIRCLE_RADIUS * Math.sin(Math.PI / 4), - -minWidthHeight / 2 * CIRCLE_RADIUS * Math.sin(Math.PI / 4)); - cCtx.lineTo(minWidthHeight / 2 * REAR_ARROW_WIDTH, 0); - cCtx.lineTo(minWidthHeight / 2 * CIRCLE_RADIUS * Math.sin(Math.PI / 4), - minWidthHeight / 2 * CIRCLE_RADIUS * Math.sin(Math.PI / 4)); - cCtx.lineTo(0, minWidthHeight / 2 * REAR_ARROW_WIDTH); - cCtx.lineTo(-minWidthHeight / 2 * CIRCLE_RADIUS * Math.sin(Math.PI / 4), - minWidthHeight / 2 * CIRCLE_RADIUS * Math.sin(Math.PI / 4)); - cCtx.lineTo(-minWidthHeight / 2 * REAR_ARROW_WIDTH, 0); - cCtx.lineTo(-minWidthHeight / 2 * CIRCLE_RADIUS * Math.sin(Math.PI / 4), - -minWidthHeight / 2 * CIRCLE_RADIUS * Math.sin(Math.PI / 4)); - cCtx.lineTo(0, -minWidthHeight / 2 * REAR_ARROW_WIDTH); - cCtx.closePath(); - if (i == 0) { - cCtx.fillStyle = "rgb(0, 0, 255)"; - cCtx.fill(); - } else { - cCtx.stroke(); - } + if (cache_canvas) { + cache_ctx = cache_canvas.getContext("2d"); } - for (var i = 0; i < 2; i++) { - cCtx.save(); - for(var j=0; j<4; j++) { - cCtx.beginPath(); - cCtx.moveTo(0, 0); - cCtx.lineTo(FRONT_45, -FRONT_45); - cCtx.lineTo(0, -minWidthHeight / 2); - cCtx.lineTo(0, 0); - cCtx.closePath(); - if (i == 0) { - cCtx.fillStyle = "rgb(255,0,0)"; - cCtx.fill(); - } else { - cCtx.stroke(); - } - cCtx.beginPath(); - cCtx.moveTo(0, 0); - cCtx.lineTo(-FRONT_45, -FRONT_45); - cCtx.lineTo(0, -minWidthHeight / 2); - cCtx.lineTo(0, 0); - cCtx.closePath(); - if (i == 0) { - cCtx.fillStyle = "rgb(255,255,255)"; - cCtx.fill(); - } else { - cCtx.stroke(); - } - cCtx.rotate(Math.PI / 2); - } - cCtx.restore(); - } - for (var i = 0; i < 2; i++) { - cCtx.save(); - cCtx.rotate(angle * Math.PI / 180.0); - cCtx.beginPath(); - cCtx.moveTo(0, BEARING_ARROW_WIDTH * minWidthHeight / 2); - cCtx.lineTo(BEARING_ARROW_WIDTH * minWidthHeight / 2, 0); - cCtx.lineTo(0, -minWidthHeight * BEARING_ARROW_LENGTH / 2); - cCtx.lineTo(-BEARING_ARROW_WIDTH * minWidthHeight / 2, 0); - cCtx.lineTo(0, BEARING_ARROW_WIDTH * minWidthHeight / 2); - cCtx.closePath(); - if (i == 0) { - cCtx.fillStyle = "rgb(238,238,0)"; - cCtx.fill(); - } else { - cCtx.lineWidth = 3; - cCtx.strokeStyle = "rgb(0, 0, 0)"; - cCtx.stroke(); - } - cCtx.restore(); - } - cCtx.restore(); -} + return cache_ctx; +} function svgPathMap(svg_path, move_fun, line_fun, curve_fun, close_fun) { var i = 0; @@ -127,20 +41,117 @@ } else if (svg_path[i] == "z") { close_fun(); i++; + } else { + alert("Unknown path element: " + svg_path[i]); } } } -function drawRessie() { - cCanvas = document.getElementById("compass"); - if (!cCanvas) { - return; +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();}); } - cCtx = cCanvas.getContext("2d"); - if (!cCtx) { - return; +} + +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(); } - cCtx.clearRect(0, 0, cCanvas.width, cCanvas.height); + 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(); + 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 "+ @@ -153,7 +164,6 @@ "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; - var minWidthHeight = Math.min(cCanvas.width, cCanvas.height); function bound(x, y) { max_x = Math.max(max_x, x); max_y = Math.max(max_y, y); @@ -161,27 +171,69 @@ min_y = Math.min(min_y, y); } svgPathMap(SVG_PATH, bound, bound, function(cx1, cy1, cx2, cy2, x, y) {bound(x, y);}, function() {}); - cCtx.save(); - if (ressie_angle < 360) { - ressie_angle = ressie_angle + 1; - } else { - ressie_angle = 0; - } - cCtx.translate(minWidthHeight / 2, minWidthHeight / 2); - cCtx.rotate(Math.PI / 180 * ressie_angle); - var scale = minWidthHeight / Math.max(max_x - min_x, max_y - min_y) / Math.sqrt(2); - cCtx.scale(scale, scale); - cCtx.translate(-(min_x + max_x) / 2, -(min_y + max_y) / 2); - cCtx.beginPath(); - cCtx.strokeStyle = "rgb(136, 136, 136)"; - cCtx.fillStyle = "rgb(34, 170, 238)"; - svgPathMap(SVG_PATH, function(x, y) {cCtx.moveTo(x, y);}, - function(x, y) {cCtx.lineTo(x, y);}, - function(x1, y1, x2, y2, x, y) {cCtx.bezierCurveTo(x1, y1, x2, y2, x, y);}, - function() {cCtx.closePath(); cCtx.fill();}); - svgPathMap(SVG_PATH, function(x, y) {cCtx.moveTo(x, y);}, - function(x, y) {cCtx.lineTo(x, y);}, - function(x1, y1, x2, y2, x, y) {cCtx.bezierCurveTo(x1, y1, x2, y2, x, y);}, - function() {cCtx.closePath(); cCtx.stroke();}); - cCtx.restore(); + 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 drawStep() { + var ctx = getContext(); + if (ctx) { + var ratio = (1-Math.cos(wind_step / WIND_STEPS * Math.PI))/2; + var angle = wind_start + (wind_end - wind_start) * ratio; + 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 + angle); + drawArrow(ctx, minWidthHeight / 2); + ctx.restore(); + } + 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_timer = setInterval(drawStep, WIND_TIME * 1000 / WIND_STEPS); +} \ No newline at end of file diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html index 4f4b8df..3e1976a 100644 --- a/src/main/webapp/index.html +++ b/src/main/webapp/index.html @@ -1,9 +1,14 @@

Current Weather Conditions

- - - - Loading... - + + + + + +
+ + Loading... + +
diff --git a/src/main/webapp/test.html b/src/main/webapp/test.html index a2cccb5..61dbd46 100644 --- a/src/main/webapp/test.html +++ b/src/main/webapp/test.html @@ -9,9 +9,9 @@ - +

Current Weather Conditions

- +