3505 |
23 Sep 15 |
olle |
1 |
/** |
3505 |
23 Sep 15 |
olle |
* Draws box plot in a canvas context of percentile data |
3505 |
23 Sep 15 |
olle |
* supplied as a JSON object. |
3505 |
23 Sep 15 |
olle |
4 |
*/ |
3505 |
23 Sep 15 |
olle |
function createBoxPlot(boxPlotJsonObject, canvas, draw_area_wdt, draw_area_hgt, draw_scale_factor) |
3505 |
23 Sep 15 |
olle |
6 |
{ |
3505 |
23 Sep 15 |
olle |
var ctx = canvas.getContext('2d'); |
3505 |
23 Sep 15 |
olle |
8 |
|
3505 |
23 Sep 15 |
olle |
draw_area_wdt *= draw_scale_factor; |
3505 |
23 Sep 15 |
olle |
draw_area_hgt *= draw_scale_factor; |
3505 |
23 Sep 15 |
olle |
11 |
|
3505 |
23 Sep 15 |
olle |
ctx.clearRect(0, 0, draw_area_wdt, draw_area_hgt); |
3505 |
23 Sep 15 |
olle |
// Reserve area for scale and titles |
3505 |
23 Sep 15 |
olle |
var headerTitleHgt = 25 * draw_scale_factor; |
3505 |
23 Sep 15 |
olle |
var drect_xoff = 60 * draw_scale_factor; |
3505 |
23 Sep 15 |
olle |
var drect_yoff = 70 * draw_scale_factor + headerTitleHgt; |
3505 |
23 Sep 15 |
olle |
var header_text_top_off = 5 * draw_scale_factor; |
3505 |
23 Sep 15 |
olle |
var text_axis_off = 40 * draw_scale_factor; |
3505 |
23 Sep 15 |
olle |
// Calculate remaining plot area |
3505 |
23 Sep 15 |
olle |
var drect_w = draw_area_wdt - 2*drect_xoff; |
3505 |
23 Sep 15 |
olle |
var drect_h = draw_area_hgt - 2*drect_yoff; |
3505 |
23 Sep 15 |
olle |
22 |
|
3505 |
23 Sep 15 |
olle |
// Font sizes |
3505 |
23 Sep 15 |
olle |
var base_font_size = 11 * draw_scale_factor; // 11px scaled |
3505 |
23 Sep 15 |
olle |
var header1_font_size = base_font_size + 5 * draw_scale_factor; // 16px scaled |
3505 |
23 Sep 15 |
olle |
var header2_font_size = base_font_size + 4 * draw_scale_factor; // 15px scaled |
3505 |
23 Sep 15 |
olle |
var top_right_font_size = base_font_size + 1 * draw_scale_factor; // 12px scaled |
3505 |
23 Sep 15 |
olle |
var top_left_font_size = base_font_size; // 11px scaled |
3505 |
23 Sep 15 |
olle |
var y_title_font_size = header2_font_size; // 15px scaled |
3505 |
23 Sep 15 |
olle |
var y_scale_font_size = base_font_size; // 11px scaled |
3505 |
23 Sep 15 |
olle |
var x_scale_font_size = base_font_size; // 11px scaled |
3505 |
23 Sep 15 |
olle |
32 |
|
3505 |
23 Sep 15 |
olle |
// Draw rectangle enclosing plot area |
3505 |
23 Sep 15 |
olle |
ctx.strokeStyle = "#000000"; |
3505 |
23 Sep 15 |
olle |
ctx.strokeRect(fixCoordinate(ctx, drect_xoff), fixCoordinate(ctx, drect_yoff), Math.floor(drect_w), Math.floor(drect_h)); |
3505 |
23 Sep 15 |
olle |
// Add top header title |
3505 |
23 Sep 15 |
olle |
var headerTitleTop = boxPlotJsonObject.headerTitleTop; |
3505 |
23 Sep 15 |
olle |
drawHeaderTitleTopCenter(headerTitleTop, header1_font_size, header_text_top_off, ctx, drect_xoff, drect_yoff, drect_w, drect_h); |
3505 |
23 Sep 15 |
olle |
// Add top plot title |
3505 |
23 Sep 15 |
olle |
var titleTop = boxPlotJsonObject.titleTop; |
3505 |
23 Sep 15 |
olle |
drawTitleTopCenter(titleTop, header2_font_size, text_axis_off, ctx, drect_xoff, drect_yoff, drect_w, drect_h); |
3505 |
23 Sep 15 |
olle |
// Add bottom plot title |
3505 |
23 Sep 15 |
olle |
var titleBottom = boxPlotJsonObject.titleBottom; |
3505 |
23 Sep 15 |
olle |
drawTitleBottomCenter(titleBottom, header2_font_size, text_axis_off, ctx, drect_xoff, drect_yoff, drect_w, drect_h); |
3505 |
23 Sep 15 |
olle |
// Add plot top right sub-title |
3505 |
23 Sep 15 |
olle |
var subTitleRight = boxPlotJsonObject.subTitleRight; |
3505 |
23 Sep 15 |
olle |
drawSubTitleTopRight(subTitleRight, top_right_font_size, text_axis_off, ctx, drect_xoff, drect_yoff, drect_w, drect_h); |
3505 |
23 Sep 15 |
olle |
// Add plot top left sub-titles |
3505 |
23 Sep 15 |
olle |
var subTitleLeft01 = boxPlotJsonObject.subTitleLeft01; |
3505 |
23 Sep 15 |
olle |
var subTitleLeft02 = boxPlotJsonObject.subTitleLeft02; |
3505 |
23 Sep 15 |
olle |
var subTitleLeft03 = boxPlotJsonObject.subTitleLeft03; |
3505 |
23 Sep 15 |
olle |
drawSubTitleTopLeft(subTitleLeft01, 0, top_left_font_size, text_axis_off, ctx, drect_xoff, drect_yoff, drect_w, drect_h); |
3505 |
23 Sep 15 |
olle |
drawSubTitleTopLeft(subTitleLeft02, 1, top_left_font_size, text_axis_off, ctx, drect_xoff, drect_yoff, drect_w, drect_h); |
3505 |
23 Sep 15 |
olle |
drawSubTitleTopLeft(subTitleLeft03, 2, top_left_font_size, text_axis_off, ctx, drect_xoff, drect_yoff, drect_w, drect_h); |
3505 |
23 Sep 15 |
olle |
// Add y-axis left title |
3505 |
23 Sep 15 |
olle |
var yAxisTitleLeft = boxPlotJsonObject.yAxisTitleLeft; |
3505 |
23 Sep 15 |
olle |
drawTitleYLeft(yAxisTitleLeft, y_title_font_size, ctx, drect_xoff-35*draw_scale_factor, drect_yoff, drect_w, drect_h); |
3505 |
23 Sep 15 |
olle |
// Add y-axis right title |
3505 |
23 Sep 15 |
olle |
var yAxisTitleRight = boxPlotJsonObject.yAxisTitleRight; |
3505 |
23 Sep 15 |
olle |
drawTitleYRight(yAxisTitleRight, y_title_font_size, ctx, drect_xoff+35*draw_scale_factor, drect_yoff, drect_w, drect_h); |
3505 |
23 Sep 15 |
olle |
// Calculate y-axis data value limits |
3505 |
23 Sep 15 |
olle |
var minDataValue = calculateMinDataValue(boxPlotJsonObject); |
3505 |
23 Sep 15 |
olle |
var maxDataValue = calculateMaxDataValue(boxPlotJsonObject); |
3505 |
23 Sep 15 |
olle |
// Calculate scale marker array |
3505 |
23 Sep 15 |
olle |
var numTicks = 7; |
3505 |
23 Sep 15 |
olle |
var scaleMarkerArr = calculateScaleMarkerArray(minDataValue, maxDataValue, numTicks); |
3505 |
23 Sep 15 |
olle |
// Add 2% buffer below min value and 5% buffer above max value |
3505 |
23 Sep 15 |
olle |
var dataValueRange = maxDataValue - minDataValue; |
3505 |
23 Sep 15 |
olle |
var min_y = minDataValue - 0.02*dataValueRange; |
3505 |
23 Sep 15 |
olle |
var max_y = maxDataValue + 0.05*dataValueRange; |
3505 |
23 Sep 15 |
olle |
// Trim scale marker array to only include values inside range |
3505 |
23 Sep 15 |
olle |
var trimmedScaleMarkerArr = trimScaleMarkerArray(scaleMarkerArr, min_y, max_y); |
3505 |
23 Sep 15 |
olle |
// Add small spacing buffer in pixels below and above value area |
3505 |
23 Sep 15 |
olle |
var min_yoff = 5 * draw_scale_factor; |
3505 |
23 Sep 15 |
olle |
var max_yoff = 5 * draw_scale_factor; |
3505 |
23 Sep 15 |
olle |
// Select number of decimals for scale marker text |
3505 |
23 Sep 15 |
olle |
// Select number of decimals to get one decimal digit different from 0, up to 3 decimals |
3505 |
23 Sep 15 |
olle |
var numberOfDecimals = 0; |
3505 |
23 Sep 15 |
olle |
for (i=0; i < trimmedScaleMarkerArr.length; i++) |
3505 |
23 Sep 15 |
olle |
80 |
{ |
3505 |
23 Sep 15 |
olle |
var scaleValueTmp = trimmedScaleMarkerArr[i]; |
3505 |
23 Sep 15 |
olle |
// Round scale value to 3 decimals (to make e.g. 2.99999993 -> 3.000) |
3505 |
23 Sep 15 |
olle |
scaleValueTmp = scaleValueTmp.toFixed(3); |
3505 |
23 Sep 15 |
olle |
var decimalPart = scaleValueTmp - Math.floor(scaleValueTmp); |
3505 |
23 Sep 15 |
olle |
if (decimalPart >= 0.05) |
3505 |
23 Sep 15 |
olle |
86 |
{ |
3505 |
23 Sep 15 |
olle |
if (numberOfDecimals == 0 || numberOfDecimals > 1) |
3505 |
23 Sep 15 |
olle |
88 |
{ |
3505 |
23 Sep 15 |
olle |
numberOfDecimals = 1; |
3505 |
23 Sep 15 |
olle |
90 |
} |
3505 |
23 Sep 15 |
olle |
91 |
} |
3505 |
23 Sep 15 |
olle |
else if (decimalPart >= 0.005) |
3505 |
23 Sep 15 |
olle |
93 |
{ |
3505 |
23 Sep 15 |
olle |
if (numberOfDecimals == 0 || numberOfDecimals > 2) |
3505 |
23 Sep 15 |
olle |
95 |
{ |
3505 |
23 Sep 15 |
olle |
numberOfDecimals = 2; |
3505 |
23 Sep 15 |
olle |
97 |
} |
3505 |
23 Sep 15 |
olle |
98 |
} |
3505 |
23 Sep 15 |
olle |
else if (decimalPart >= 0.0005) |
3505 |
23 Sep 15 |
olle |
100 |
{ |
3505 |
23 Sep 15 |
olle |
if (numberOfDecimals == 0 || numberOfDecimals > 3) |
3505 |
23 Sep 15 |
olle |
102 |
{ |
3505 |
23 Sep 15 |
olle |
numberOfDecimals = 3; |
3505 |
23 Sep 15 |
olle |
104 |
} |
3505 |
23 Sep 15 |
olle |
105 |
} |
3505 |
23 Sep 15 |
olle |
106 |
} |
3505 |
23 Sep 15 |
olle |
// Draw y-axis left scale markers with values |
3505 |
23 Sep 15 |
olle |
var scaleValue; |
3505 |
23 Sep 15 |
olle |
var markerText; |
3505 |
23 Sep 15 |
olle |
for (i=0; i < trimmedScaleMarkerArr.length; i++) |
3505 |
23 Sep 15 |
olle |
111 |
{ |
3505 |
23 Sep 15 |
olle |
scaleValue = trimmedScaleMarkerArr[i]; |
3505 |
23 Sep 15 |
olle |
markerText = scaleValue.toFixed(numberOfDecimals); |
3505 |
23 Sep 15 |
olle |
drawScaleMarkerYLeft(scaleValue, y_scale_font_size, markerText, min_y, max_y, min_yoff, max_yoff, ctx, drect_xoff, drect_yoff, drect_w, drect_h); |
3505 |
23 Sep 15 |
olle |
115 |
} |
3505 |
23 Sep 15 |
olle |
// Find number of boxes in box plot |
3505 |
23 Sep 15 |
olle |
var boxNumTot = 0; |
3505 |
23 Sep 15 |
olle |
for (var i in boxPlotJsonObject.percentileData) |
3505 |
23 Sep 15 |
olle |
119 |
{ |
3505 |
23 Sep 15 |
olle |
boxNumTot++; |
3505 |
23 Sep 15 |
olle |
121 |
} |
3505 |
23 Sep 15 |
olle |
// Draw box plot boxes |
3505 |
23 Sep 15 |
olle |
for (var i in boxPlotJsonObject.percentileData) |
3505 |
23 Sep 15 |
olle |
124 |
{ |
3505 |
23 Sep 15 |
olle |
var boxNum = i; |
3505 |
23 Sep 15 |
olle |
var name = boxPlotJsonObject.percentileData[i].name; |
3505 |
23 Sep 15 |
olle |
var numItems = boxPlotJsonObject.percentileData[i].numItems; |
3505 |
23 Sep 15 |
olle |
var v1 = boxPlotJsonObject.percentileData[i].v1; |
3505 |
23 Sep 15 |
olle |
var v2 = boxPlotJsonObject.percentileData[i].v2; |
3505 |
23 Sep 15 |
olle |
var v3 = boxPlotJsonObject.percentileData[i].v3; |
3505 |
23 Sep 15 |
olle |
var v4 = boxPlotJsonObject.percentileData[i].v4; |
3505 |
23 Sep 15 |
olle |
var v5 = boxPlotJsonObject.percentileData[i].v5; |
3505 |
23 Sep 15 |
olle |
// Draw box plot box |
3505 |
23 Sep 15 |
olle |
var x_pad = 10 * draw_scale_factor; |
3505 |
23 Sep 15 |
olle |
drawBoxPlotBoxY(boxNum, boxNumTot, v1, v2, v3, v4, v5, min_y, max_y, min_yoff, max_yoff, x_pad, ctx, drect_xoff, drect_yoff, drect_w, drect_h, draw_scale_factor); |
3505 |
23 Sep 15 |
olle |
drawBoxPlotBoxScaleMarkerXBottom(boxNum, boxNumTot, name, x_scale_font_size, x_pad, ctx, drect_xoff, drect_yoff, drect_w, drect_h); |
3505 |
23 Sep 15 |
olle |
drawBoxPlotBoxScaleMarkerXTop(boxNum, boxNumTot, numItems, x_scale_font_size, x_pad, ctx, drect_xoff, drect_yoff, drect_w, drect_h); |
3505 |
23 Sep 15 |
olle |
138 |
} |
3505 |
23 Sep 15 |
olle |
// Draw y-axis right scale markers with values and horizontal dotted guide lines |
3505 |
23 Sep 15 |
olle |
for (var i in boxPlotJsonObject.valueGuideLinesY) |
3505 |
23 Sep 15 |
olle |
141 |
{ |
3505 |
23 Sep 15 |
olle |
if (boxPlotJsonObject.valueGuideLinesY[i] != null) |
3505 |
23 Sep 15 |
olle |
143 |
{ |
3505 |
23 Sep 15 |
olle |
var value = boxPlotJsonObject.valueGuideLinesY[i].value; |
3505 |
23 Sep 15 |
olle |
var valueText = boxPlotJsonObject.valueGuideLinesY[i].text; |
3505 |
23 Sep 15 |
olle |
// Draw y-axis right scale markers with values |
3505 |
23 Sep 15 |
olle |
drawScaleMarkerYRight(value, valueText, y_scale_font_size, min_y, max_y, min_yoff, max_yoff, ctx, drect_xoff, drect_yoff, drect_w, drect_h); |
3505 |
23 Sep 15 |
olle |
// Draw dotted horizontal lines at y axis right scale marker values |
3505 |
23 Sep 15 |
olle |
drawDottedLineY(value, min_y, max_y, min_yoff, max_yoff, ctx, drect_xoff, drect_yoff, drect_w, drect_h); |
3505 |
23 Sep 15 |
olle |
150 |
} |
3505 |
23 Sep 15 |
olle |
151 |
} |
3505 |
23 Sep 15 |
olle |
152 |
} |
3505 |
23 Sep 15 |
olle |
153 |
|
3505 |
23 Sep 15 |
olle |
154 |
/** |
3505 |
23 Sep 15 |
olle |
* Calculate min data value for percentiles in box plot JSON object. |
3505 |
23 Sep 15 |
olle |
156 |
*/ |
3505 |
23 Sep 15 |
olle |
function calculateMinDataValue(boxPlotJsonObject) |
3505 |
23 Sep 15 |
olle |
158 |
{ |
3505 |
23 Sep 15 |
olle |
// Find number of boxes in box plot |
3505 |
23 Sep 15 |
olle |
var boxNumTot = 0; |
3505 |
23 Sep 15 |
olle |
for (var i in boxPlotJsonObject.percentileData) |
3505 |
23 Sep 15 |
olle |
162 |
{ |
3505 |
23 Sep 15 |
olle |
boxNumTot++; |
3505 |
23 Sep 15 |
olle |
164 |
} |
3505 |
23 Sep 15 |
olle |
// Calculate min percentile value |
3505 |
23 Sep 15 |
olle |
var minValue = 1000000000; |
3505 |
23 Sep 15 |
olle |
for (var i in boxPlotJsonObject.percentileData) |
3505 |
23 Sep 15 |
olle |
168 |
{ |
3505 |
23 Sep 15 |
olle |
var boxNum = i; |
3505 |
23 Sep 15 |
olle |
var v1 = boxPlotJsonObject.percentileData[i].v1; |
3505 |
23 Sep 15 |
olle |
var v5 = boxPlotJsonObject.percentileData[i].v5; |
3505 |
23 Sep 15 |
olle |
if (v1 < minValue) |
3505 |
23 Sep 15 |
olle |
173 |
{ |
3505 |
23 Sep 15 |
olle |
minValue = v1; |
3505 |
23 Sep 15 |
olle |
175 |
} |
3505 |
23 Sep 15 |
olle |
if (v5 < minValue) |
3505 |
23 Sep 15 |
olle |
177 |
{ |
3505 |
23 Sep 15 |
olle |
minValue = v5; |
3505 |
23 Sep 15 |
olle |
179 |
} |
3505 |
23 Sep 15 |
olle |
180 |
} |
3505 |
23 Sep 15 |
olle |
return minValue; |
3505 |
23 Sep 15 |
olle |
182 |
} |
3505 |
23 Sep 15 |
olle |
183 |
|
3505 |
23 Sep 15 |
olle |
184 |
/** |
3505 |
23 Sep 15 |
olle |
* Calculate max data value for percentiles in box plot JSON object. |
3505 |
23 Sep 15 |
olle |
186 |
*/ |
3505 |
23 Sep 15 |
olle |
function calculateMaxDataValue(boxPlotJsonObject) |
3505 |
23 Sep 15 |
olle |
188 |
{ |
3505 |
23 Sep 15 |
olle |
// Find number of boxes in box plot |
3505 |
23 Sep 15 |
olle |
var boxNumTot = 0; |
3505 |
23 Sep 15 |
olle |
for (var i in boxPlotJsonObject.percentileData) |
3505 |
23 Sep 15 |
olle |
192 |
{ |
3505 |
23 Sep 15 |
olle |
boxNumTot++; |
3505 |
23 Sep 15 |
olle |
194 |
} |
3505 |
23 Sep 15 |
olle |
// Calculate max percentile value |
3505 |
23 Sep 15 |
olle |
var maxValue = -1; |
3505 |
23 Sep 15 |
olle |
for (var i in boxPlotJsonObject.percentileData) |
3505 |
23 Sep 15 |
olle |
198 |
{ |
3505 |
23 Sep 15 |
olle |
var boxNum = i; |
3505 |
23 Sep 15 |
olle |
var v1 = boxPlotJsonObject.percentileData[i].v1; |
3505 |
23 Sep 15 |
olle |
var v5 = boxPlotJsonObject.percentileData[i].v5; |
3505 |
23 Sep 15 |
olle |
if (v1 > maxValue) |
3505 |
23 Sep 15 |
olle |
203 |
{ |
3505 |
23 Sep 15 |
olle |
maxValue = v1; |
3505 |
23 Sep 15 |
olle |
205 |
} |
3505 |
23 Sep 15 |
olle |
if (v5 > maxValue) |
3505 |
23 Sep 15 |
olle |
207 |
{ |
3505 |
23 Sep 15 |
olle |
maxValue = v5; |
3505 |
23 Sep 15 |
olle |
209 |
} |
3505 |
23 Sep 15 |
olle |
210 |
} |
3505 |
23 Sep 15 |
olle |
return maxValue; |
3505 |
23 Sep 15 |
olle |
212 |
} |
3505 |
23 Sep 15 |
olle |
213 |
|
3505 |
23 Sep 15 |
olle |
214 |
/** |
3505 |
23 Sep 15 |
olle |
* Draws centered top header title in a canvas context, using coordinates |
3505 |
23 Sep 15 |
olle |
* where x increases to the right and y increases upwards, |
3505 |
23 Sep 15 |
olle |
* and where the offsets are added automatically. |
3505 |
23 Sep 15 |
olle |
218 |
*/ |
3505 |
23 Sep 15 |
olle |
function drawHeaderTitleTopCenter(text, font_size, text_top_off, context, xoff, yoff, wdt, hgt) |
3505 |
23 Sep 15 |
olle |
220 |
{ |
3505 |
23 Sep 15 |
olle |
// Add centered top header title |
3505 |
23 Sep 15 |
olle |
context.font = font_size + 'px sans-serif'; |
3505 |
23 Sep 15 |
olle |
context.textBaseline = 'top'; |
3505 |
23 Sep 15 |
olle |
context.textAlign="center"; |
3505 |
23 Sep 15 |
olle |
context.fillText(text, xoff + wdt/2, text_top_off); |
3505 |
23 Sep 15 |
olle |
226 |
} |
3505 |
23 Sep 15 |
olle |
227 |
|
3505 |
23 Sep 15 |
olle |
228 |
/** |
3505 |
23 Sep 15 |
olle |
* Draws centered top title in a canvas context, using coordinates |
3505 |
23 Sep 15 |
olle |
* where x increases to the right and y increases upwards, |
3505 |
23 Sep 15 |
olle |
* and where the offsets are added automatically. |
3505 |
23 Sep 15 |
olle |
232 |
*/ |
3505 |
23 Sep 15 |
olle |
function drawTitleTopCenter(text, font_size, text_axis_off, context, xoff, yoff, wdt, hgt) |
3505 |
23 Sep 15 |
olle |
234 |
{ |
3505 |
23 Sep 15 |
olle |
// Add centered top title |
3505 |
23 Sep 15 |
olle |
var text_axis_yoff = -text_axis_off; |
3505 |
23 Sep 15 |
olle |
context.font = font_size + 'px sans-serif'; |
3505 |
23 Sep 15 |
olle |
context.textBaseline = 'bottom'; |
3505 |
23 Sep 15 |
olle |
context.textAlign="center"; |
3505 |
23 Sep 15 |
olle |
context.fillText(text, xoff + wdt/2, yoff + text_axis_yoff); |
3505 |
23 Sep 15 |
olle |
241 |
} |
3505 |
23 Sep 15 |
olle |
242 |
|
3505 |
23 Sep 15 |
olle |
243 |
/** |
3505 |
23 Sep 15 |
olle |
* Draws centered bottom title in a canvas context, using coordinates |
3505 |
23 Sep 15 |
olle |
* where x increases to the right and y increases upwards, |
3505 |
23 Sep 15 |
olle |
* and where the offsets are added automatically. |
3505 |
23 Sep 15 |
olle |
247 |
*/ |
3505 |
23 Sep 15 |
olle |
function drawTitleBottomCenter(text, font_size, text_axis_off, context, xoff, yoff, wdt, hgt) |
3505 |
23 Sep 15 |
olle |
249 |
{ |
3505 |
23 Sep 15 |
olle |
// Add centered bottom title |
3505 |
23 Sep 15 |
olle |
var text_axis_yoff = text_axis_off; |
3505 |
23 Sep 15 |
olle |
context.font = font_size + 'px sans-serif'; |
3505 |
23 Sep 15 |
olle |
context.textBaseline = 'top'; |
3505 |
23 Sep 15 |
olle |
context.textAlign="center"; |
3505 |
23 Sep 15 |
olle |
context.fillText(text, xoff + wdt/2, yoff + hgt + text_axis_yoff); |
3505 |
23 Sep 15 |
olle |
256 |
} |
3505 |
23 Sep 15 |
olle |
257 |
|
3505 |
23 Sep 15 |
olle |
258 |
/** |
3505 |
23 Sep 15 |
olle |
* Draws right-aligned top sub-title in a canvas context, using coordinates |
3505 |
23 Sep 15 |
olle |
* where x increases to the right and y increases upwards, |
3505 |
23 Sep 15 |
olle |
* and where the offsets are added automatically. |
3505 |
23 Sep 15 |
olle |
262 |
*/ |
3505 |
23 Sep 15 |
olle |
function drawSubTitleTopRight(text, font_size, text_axis_off, context, xoff, yoff, wdt, hgt) |
3505 |
23 Sep 15 |
olle |
264 |
{ |
3505 |
23 Sep 15 |
olle |
// Add right-aligned top title |
3505 |
23 Sep 15 |
olle |
var text_axis_yoff = -text_axis_off; |
3505 |
23 Sep 15 |
olle |
context.font = font_size + 'px sans-serif'; |
3505 |
23 Sep 15 |
olle |
context.textBaseline = 'bottom'; |
3505 |
23 Sep 15 |
olle |
context.textAlign="right"; |
3505 |
23 Sep 15 |
olle |
context.fillText(text, xoff + wdt, yoff + text_axis_yoff); |
3505 |
23 Sep 15 |
olle |
271 |
} |
3505 |
23 Sep 15 |
olle |
272 |
|
3505 |
23 Sep 15 |
olle |
273 |
/** |
3505 |
23 Sep 15 |
olle |
* Draws left-aligned top sub-title in a canvas context, using coordinates |
3505 |
23 Sep 15 |
olle |
* where x increases to the right and y increases upwards, |
3505 |
23 Sep 15 |
olle |
* and where the offsets are added automatically. |
3505 |
23 Sep 15 |
olle |
277 |
*/ |
3505 |
23 Sep 15 |
olle |
function drawSubTitleTopLeft(text, sub_title_num, font_size, text_axis_off, context, xoff, yoff, wdt, hgt) |
3505 |
23 Sep 15 |
olle |
279 |
{ |
3505 |
23 Sep 15 |
olle |
// Add left-aligned top title |
3505 |
23 Sep 15 |
olle |
var text_axis_yoff = -text_axis_off; |
3505 |
23 Sep 15 |
olle |
//var text_yoff = 35 - 11*(2 - sub_title_num); |
3505 |
23 Sep 15 |
olle |
var text_axis_yoff = -text_axis_off; |
3505 |
23 Sep 15 |
olle |
var text_yoff = yoff + text_axis_yoff - (font_size+1)*(2 - sub_title_num); |
3505 |
23 Sep 15 |
olle |
context.font = font_size + 'px sans-serif'; |
3505 |
23 Sep 15 |
olle |
context.textBaseline = 'bottom'; |
3505 |
23 Sep 15 |
olle |
context.textAlign="left"; |
3505 |
23 Sep 15 |
olle |
context.fillText(text, xoff, text_yoff); |
3505 |
23 Sep 15 |
olle |
289 |
} |
3505 |
23 Sep 15 |
olle |
290 |
|
3505 |
23 Sep 15 |
olle |
291 |
/** |
3505 |
23 Sep 15 |
olle |
* Draws vertical box plot box with five percentiles. |
3505 |
23 Sep 15 |
olle |
* Box numbers starts from 0 at the left. |
3505 |
23 Sep 15 |
olle |
294 |
*/ |
3505 |
23 Sep 15 |
olle |
function drawBoxPlotBoxY(boxNum, boxNumTot, v1, v2, v3, v4, v5, min, max, min_off, max_off, x_pad, context, xoff, yoff, wdt, hgt, draw_scale_factor) |
3505 |
23 Sep 15 |
olle |
296 |
{ |
3505 |
23 Sep 15 |
olle |
// Calculate value offsets |
3505 |
23 Sep 15 |
olle |
var eff_hgt = hgt - min_off - max_off; |
3505 |
23 Sep 15 |
olle |
var value_range = max - min; |
3505 |
23 Sep 15 |
olle |
var v1_yoff = min_off + eff_hgt*((v1 - min)/value_range); |
3505 |
23 Sep 15 |
olle |
var v2_yoff = min_off + eff_hgt*((v2 - min)/value_range); |
3505 |
23 Sep 15 |
olle |
var v3_yoff = min_off + eff_hgt*((v3 - min)/value_range); |
3505 |
23 Sep 15 |
olle |
var v4_yoff = min_off + eff_hgt*((v4 - min)/value_range); |
3505 |
23 Sep 15 |
olle |
var v5_yoff = min_off + eff_hgt*((v5 - min)/value_range); |
3505 |
23 Sep 15 |
olle |
// Calculate box width |
3505 |
23 Sep 15 |
olle |
var width_per_box = (wdt - 2*x_pad)/boxNumTot; |
3505 |
23 Sep 15 |
olle |
// Reserve 20% as space between boxes |
3505 |
23 Sep 15 |
olle |
var box_pad = width_per_box/10; |
3505 |
23 Sep 15 |
olle |
var box_wdt = width_per_box*4/5; |
3505 |
23 Sep 15 |
olle |
// Calculate box min, center, and max offsets |
3505 |
23 Sep 15 |
olle |
var box_xmin = x_pad + boxNum*width_per_box + box_pad; |
3505 |
23 Sep 15 |
olle |
var box_xcenter = box_xmin + box_wdt/2; |
3505 |
23 Sep 15 |
olle |
var box_xmax = box_xmin + box_wdt; |
3505 |
23 Sep 15 |
olle |
// Draw box rectangle |
3505 |
23 Sep 15 |
olle |
drawRect(box_xmin, v2_yoff, box_wdt, v4_yoff - v2_yoff, context, xoff, yoff, wdt, hgt); |
3505 |
23 Sep 15 |
olle |
// Draw median line (thick) |
3505 |
23 Sep 15 |
olle |
context.lineWidth = 3; |
3505 |
23 Sep 15 |
olle |
context.beginPath(); |
3505 |
23 Sep 15 |
olle |
drawLine(box_xmin, v3_yoff, box_xmax, v3_yoff, context, xoff, yoff, wdt, hgt); |
3505 |
23 Sep 15 |
olle |
context.stroke(); |
3505 |
23 Sep 15 |
olle |
// Draw line between first percentile and box (dashed line) |
3505 |
23 Sep 15 |
olle |
context.lineWidth = 1; |
3505 |
23 Sep 15 |
olle |
context.beginPath(); |
3505 |
23 Sep 15 |
olle |
//drawLine(box_xcenter, v1_yoff, box_xcenter, v2_yoff, context, xoff, yoff, wdt, hgt); |
3505 |
23 Sep 15 |
olle |
var dash_size = 5 * draw_scale_factor; |
3505 |
23 Sep 15 |
olle |
drawDashedLineVertical(box_xcenter, v1_yoff, v2_yoff, context, xoff, yoff, wdt, hgt, dash_size, dash_size); |
3505 |
23 Sep 15 |
olle |
// Draw short horizontal line at end of line |
3505 |
23 Sep 15 |
olle |
drawLine(box_xcenter - box_wdt/4, v1_yoff, box_xcenter + box_wdt/4, v1_yoff, context, xoff, yoff, wdt, hgt); |
3505 |
23 Sep 15 |
olle |
context.stroke(); |
3505 |
23 Sep 15 |
olle |
// Draw line between box and fifth percentile (dashed line) |
3505 |
23 Sep 15 |
olle |
context.lineWidth = 1; |
3505 |
23 Sep 15 |
olle |
context.beginPath(); |
3505 |
23 Sep 15 |
olle |
//drawLine(box_xcenter, v4_yoff, box_xcenter, v5_yoff, context, xoff, yoff, wdt, hgt); |
3505 |
23 Sep 15 |
olle |
drawDashedLineVertical(box_xcenter, v4_yoff, v5_yoff, context, xoff, yoff, wdt, hgt, dash_size, dash_size); |
3505 |
23 Sep 15 |
olle |
// Draw short horizontal line at end of line |
3505 |
23 Sep 15 |
olle |
drawLine(box_xcenter - box_wdt/4, v5_yoff, box_xcenter + box_wdt/4, v5_yoff, context, xoff, yoff, wdt, hgt); |
3505 |
23 Sep 15 |
olle |
context.stroke(); |
3505 |
23 Sep 15 |
olle |
338 |
} |
3505 |
23 Sep 15 |
olle |
339 |
|
3505 |
23 Sep 15 |
olle |
340 |
/** |
3505 |
23 Sep 15 |
olle |
* Draws x-axis bottom scale marker with text in a canvas context |
3505 |
23 Sep 15 |
olle |
* for a box plot box, using coordinates where x increases to the right |
3505 |
23 Sep 15 |
olle |
* and y increases upwards, and where the offsets are added automatically. |
3505 |
23 Sep 15 |
olle |
344 |
*/ |
3505 |
23 Sep 15 |
olle |
function drawBoxPlotBoxScaleMarkerXBottom(boxNum, boxNumTot, value, font_size, x_pad, context, xoff, yoff, wdt, hgt) |
3505 |
23 Sep 15 |
olle |
346 |
{ |
3505 |
23 Sep 15 |
olle |
// Calculate box width |
3505 |
23 Sep 15 |
olle |
var width_per_box = (wdt - 2*x_pad)/boxNumTot; |
3505 |
23 Sep 15 |
olle |
// Reserve 20% as space between boxes |
3505 |
23 Sep 15 |
olle |
var box_pad = width_per_box/10; |
3505 |
23 Sep 15 |
olle |
var box_wdt = width_per_box*4/5; |
3505 |
23 Sep 15 |
olle |
// Calculate box min, center, and max offsets |
3505 |
23 Sep 15 |
olle |
var box_xmin = x_pad + boxNum*width_per_box + box_pad; |
3505 |
23 Sep 15 |
olle |
var box_xcenter = box_xmin + box_wdt/2; |
3505 |
23 Sep 15 |
olle |
var box_xmax = box_xmin + box_wdt; |
3505 |
23 Sep 15 |
olle |
// Draw scale marker |
3505 |
23 Sep 15 |
olle |
var marker_len = font_size; |
3505 |
23 Sep 15 |
olle |
context.lineWidth = 1; |
3505 |
23 Sep 15 |
olle |
context.beginPath(); |
3505 |
23 Sep 15 |
olle |
drawLine(box_xcenter, 0, box_xcenter, -marker_len, context, xoff, yoff, wdt, hgt); |
3505 |
23 Sep 15 |
olle |
context.stroke(); |
3505 |
23 Sep 15 |
olle |
// Draw scale marker value |
3505 |
23 Sep 15 |
olle |
var value_text = '' + value; |
3505 |
23 Sep 15 |
olle |
var text_axis_yoff = 1.5*font_size; |
3505 |
23 Sep 15 |
olle |
var text_marker_xoff = font_size / 2; |
3505 |
23 Sep 15 |
olle |
context.save(); |
3505 |
23 Sep 15 |
olle |
context.font = font_size + 'px sans-serif'; |
3505 |
23 Sep 15 |
olle |
context.textBaseline = 'bottom'; |
3505 |
23 Sep 15 |
olle |
context.translate(xoff + box_xcenter + text_marker_xoff, yoff + hgt + text_axis_yoff); |
3505 |
23 Sep 15 |
olle |
context.rotate(-Math.PI/2); |
3505 |
23 Sep 15 |
olle |
context.textAlign = "right"; |
3505 |
23 Sep 15 |
olle |
context.fillText(value_text, 0, 0); |
3505 |
23 Sep 15 |
olle |
context.restore(); |
3505 |
23 Sep 15 |
olle |
374 |
} |
3505 |
23 Sep 15 |
olle |
375 |
|
3505 |
23 Sep 15 |
olle |
376 |
/** |
3505 |
23 Sep 15 |
olle |
* Draws x-axis top scale marker with text in a canvas context |
3505 |
23 Sep 15 |
olle |
* for a box plot box, using coordinates where x increases to the right |
3505 |
23 Sep 15 |
olle |
* and y increases upwards, and where the offsets are added automatically. |
3505 |
23 Sep 15 |
olle |
380 |
*/ |
3505 |
23 Sep 15 |
olle |
function drawBoxPlotBoxScaleMarkerXTop(boxNum, boxNumTot, value, font_size, x_pad, context, xoff, yoff, wdt, hgt) |
3505 |
23 Sep 15 |
olle |
382 |
{ |
3505 |
23 Sep 15 |
olle |
// Calculate box width |
3505 |
23 Sep 15 |
olle |
var width_per_box = (wdt - 2*x_pad)/boxNumTot; |
3505 |
23 Sep 15 |
olle |
// Reserve 20% as space between boxes |
3505 |
23 Sep 15 |
olle |
var box_pad = width_per_box/10; |
3505 |
23 Sep 15 |
olle |
var box_wdt = width_per_box*4/5; |
3505 |
23 Sep 15 |
olle |
// Calculate box min, center, and max offsets |
3505 |
23 Sep 15 |
olle |
var box_xmin = x_pad + boxNum*width_per_box + box_pad; |
3505 |
23 Sep 15 |
olle |
var box_xcenter = box_xmin + box_wdt/2; |
3505 |
23 Sep 15 |
olle |
var box_xmax = box_xmin + box_wdt; |
3505 |
23 Sep 15 |
olle |
// Draw scale marker |
3505 |
23 Sep 15 |
olle |
var marker_len = font_size; |
3505 |
23 Sep 15 |
olle |
context.lineWidth = 1; |
3505 |
23 Sep 15 |
olle |
context.beginPath(); |
3505 |
23 Sep 15 |
olle |
drawLine(box_xcenter, hgt, box_xcenter, hgt + marker_len, context, xoff, yoff, wdt, hgt); |
3505 |
23 Sep 15 |
olle |
context.stroke(); |
3505 |
23 Sep 15 |
olle |
// Draw scale marker value |
3505 |
23 Sep 15 |
olle |
var value_text = '' + value; |
3505 |
23 Sep 15 |
olle |
var text_axis_yoff = 1.5*font_size; |
3505 |
23 Sep 15 |
olle |
var text_marker_xoff = font_size / 2; |
3505 |
23 Sep 15 |
olle |
context.save(); |
3505 |
23 Sep 15 |
olle |
context.font = font_size + 'px sans-serif'; |
3505 |
23 Sep 15 |
olle |
context.textBaseline = 'bottom'; |
3505 |
23 Sep 15 |
olle |
context.translate(xoff + box_xcenter + text_marker_xoff, yoff - text_axis_yoff); |
3505 |
23 Sep 15 |
olle |
context.rotate(-Math.PI/2); |
3505 |
23 Sep 15 |
olle |
context.textAlign = "left"; |
3505 |
23 Sep 15 |
olle |
context.fillText(value_text, 0, 0); |
3505 |
23 Sep 15 |
olle |
context.restore(); |
3505 |
23 Sep 15 |
olle |
410 |
} |
3505 |
23 Sep 15 |
olle |
411 |
|
3505 |
23 Sep 15 |
olle |
412 |
/** |
3505 |
23 Sep 15 |
olle |
* Draws y-axis left scale marker with text in a canvas context, using coordinates |
3505 |
23 Sep 15 |
olle |
* where x increases to the right and y increases upwards, |
3505 |
23 Sep 15 |
olle |
* and where the offsets are added automatically. |
3505 |
23 Sep 15 |
olle |
416 |
*/ |
3505 |
23 Sep 15 |
olle |
function drawScaleMarkerYLeft(value, font_size, markerText, min, max, min_off, max_off, context, xoff, yoff, wdt, hgt) |
3505 |
23 Sep 15 |
olle |
418 |
{ |
3505 |
23 Sep 15 |
olle |
var eff_hgt = hgt - min_off - max_off; |
3505 |
23 Sep 15 |
olle |
var value_range = max - min; |
3505 |
23 Sep 15 |
olle |
var marker_yoff = min_off + eff_hgt*((value - min)/value_range); |
3505 |
23 Sep 15 |
olle |
var marker_len = font_size; |
3505 |
23 Sep 15 |
olle |
// Draw scale marker |
3505 |
23 Sep 15 |
olle |
context.lineWidth = 1; |
3505 |
23 Sep 15 |
olle |
context.beginPath(); |
3505 |
23 Sep 15 |
olle |
drawLine(0, marker_yoff, -marker_len, marker_yoff, context, xoff, yoff, wdt, hgt); |
3505 |
23 Sep 15 |
olle |
context.stroke(); |
3505 |
23 Sep 15 |
olle |
// Draw scale marker value |
3505 |
23 Sep 15 |
olle |
var value_text = '' + markerText; |
3505 |
23 Sep 15 |
olle |
var text_axis_xoff = -1.5*font_size; |
3505 |
23 Sep 15 |
olle |
var text_marker_yoff = -font_size / 2; |
3505 |
23 Sep 15 |
olle |
context.font = font_size+'px sans-serif'; |
3505 |
23 Sep 15 |
olle |
context.textBaseline = 'bottom'; |
3505 |
23 Sep 15 |
olle |
context.textAlign="right"; |
3505 |
23 Sep 15 |
olle |
drawText(value_text, text_axis_xoff, marker_yoff + text_marker_yoff, context, xoff, yoff, wdt, hgt); |
3505 |
23 Sep 15 |
olle |
436 |
} |
3505 |
23 Sep 15 |
olle |
437 |
|
3505 |
23 Sep 15 |
olle |
438 |
/** |
3505 |
23 Sep 15 |
olle |
* Draws y-axis right scale marker with text in a canvas context, using coordinates |
3505 |
23 Sep 15 |
olle |
* where x increases to the right and y increases upwards, |
3505 |
23 Sep 15 |
olle |
* and where the offsets are added automatically. |
3505 |
23 Sep 15 |
olle |
442 |
*/ |
3505 |
23 Sep 15 |
olle |
function drawScaleMarkerYRight(value, markerText, font_size, min, max, min_off, max_off, context, xoff, yoff, wdt, hgt) |
3505 |
23 Sep 15 |
olle |
444 |
{ |
3505 |
23 Sep 15 |
olle |
var eff_hgt = hgt - min_off - max_off; |
3505 |
23 Sep 15 |
olle |
var value_range = max - min; |
3505 |
23 Sep 15 |
olle |
var marker_yoff = min_off + eff_hgt*((value - min)/value_range); |
3505 |
23 Sep 15 |
olle |
var marker_len = font_size; |
3505 |
23 Sep 15 |
olle |
// Draw scale marker |
3505 |
23 Sep 15 |
olle |
context.lineWidth = 1; |
3505 |
23 Sep 15 |
olle |
context.beginPath(); |
3505 |
23 Sep 15 |
olle |
drawLine(wdt, marker_yoff, wdt + marker_len, marker_yoff, context, xoff, yoff, wdt, hgt); |
3505 |
23 Sep 15 |
olle |
context.stroke(); |
3505 |
23 Sep 15 |
olle |
// Draw scale marker value |
3505 |
23 Sep 15 |
olle |
var value_text = '' + markerText; |
3505 |
23 Sep 15 |
olle |
var text_axis_xoff = font_size * 1.5; |
3505 |
23 Sep 15 |
olle |
var text_marker_yoff = -font_size / 2; |
3505 |
23 Sep 15 |
olle |
context.font = font_size + 'px sans-serif'; |
3505 |
23 Sep 15 |
olle |
context.textBaseline = 'bottom'; |
3505 |
23 Sep 15 |
olle |
context.textAlign="left"; |
3505 |
23 Sep 15 |
olle |
drawText(value_text, wdt + text_axis_xoff, marker_yoff + text_marker_yoff, context, xoff, yoff, wdt, hgt); |
3505 |
23 Sep 15 |
olle |
462 |
} |
3505 |
23 Sep 15 |
olle |
463 |
|
3505 |
23 Sep 15 |
olle |
464 |
/** |
3505 |
23 Sep 15 |
olle |
* Draws dotted line for y-value in a canvas context, using coordinates |
3505 |
23 Sep 15 |
olle |
* where x increases to the right and y increases upwards, |
3505 |
23 Sep 15 |
olle |
* and where the offsets are added automatically. |
3505 |
23 Sep 15 |
olle |
468 |
*/ |
3505 |
23 Sep 15 |
olle |
function drawDottedLineY(value, min, max, min_off, max_off, context, xoff, yoff, wdt, hgt) |
3505 |
23 Sep 15 |
olle |
470 |
{ |
3505 |
23 Sep 15 |
olle |
var eff_hgt = hgt - min_off - max_off; |
3505 |
23 Sep 15 |
olle |
var value_range = max - min; |
3505 |
23 Sep 15 |
olle |
var line_yoff = min_off + eff_hgt*((value - min)/value_range); |
3505 |
23 Sep 15 |
olle |
// Draw dotted line for y-value |
3505 |
23 Sep 15 |
olle |
context.lineWidth = 1; |
3505 |
23 Sep 15 |
olle |
context.beginPath(); |
3505 |
23 Sep 15 |
olle |
drawDottedLineHorizontal(0, wdt, line_yoff, context, xoff, yoff, wdt, hgt); |
3505 |
23 Sep 15 |
olle |
context.stroke(); |
3505 |
23 Sep 15 |
olle |
479 |
} |
3505 |
23 Sep 15 |
olle |
480 |
|
3505 |
23 Sep 15 |
olle |
481 |
/** |
3505 |
23 Sep 15 |
olle |
* Draws y-axis left scale title in a canvas context, using coordinates |
3505 |
23 Sep 15 |
olle |
* where x increases to the right and y increases upwards, |
3505 |
23 Sep 15 |
olle |
* and where the offsets are added automatically. |
3505 |
23 Sep 15 |
olle |
485 |
*/ |
3505 |
23 Sep 15 |
olle |
function drawTitleYLeft(title, font_size, context, xoff, yoff, wdt, hgt) |
3505 |
23 Sep 15 |
olle |
487 |
{ |
3505 |
23 Sep 15 |
olle |
// Add y axis title |
3505 |
23 Sep 15 |
olle |
context.save(); |
3505 |
23 Sep 15 |
olle |
context.font = font_size + 'px sans-serif'; |
3505 |
23 Sep 15 |
olle |
context.textBaseline = 'bottom'; |
3505 |
23 Sep 15 |
olle |
context.translate(xoff, yoff + hgt/2); |
3505 |
23 Sep 15 |
olle |
context.rotate(-Math.PI/2); |
3505 |
23 Sep 15 |
olle |
context.textAlign = "center"; |
3505 |
23 Sep 15 |
olle |
context.fillText(title, 0, 0); |
3505 |
23 Sep 15 |
olle |
context.restore(); |
3505 |
23 Sep 15 |
olle |
497 |
} |
3505 |
23 Sep 15 |
olle |
498 |
|
3505 |
23 Sep 15 |
olle |
499 |
/** |
3505 |
23 Sep 15 |
olle |
* Draws y-axis right scale title in a canvas context, using coordinates |
3505 |
23 Sep 15 |
olle |
* where x increases to the right and y increases upwards, |
3505 |
23 Sep 15 |
olle |
* and where the offsets are added automatically. |
3505 |
23 Sep 15 |
olle |
503 |
*/ |
3505 |
23 Sep 15 |
olle |
function drawTitleYRight(title, font_size, context, xoff, yoff, wdt, hgt) |
3505 |
23 Sep 15 |
olle |
505 |
{ |
3505 |
23 Sep 15 |
olle |
// Add y axis title |
3505 |
23 Sep 15 |
olle |
context.save(); |
3505 |
23 Sep 15 |
olle |
context.font = font_size + 'px sans-serif'; |
3505 |
23 Sep 15 |
olle |
context.textBaseline = 'top'; |
3505 |
23 Sep 15 |
olle |
context.translate(xoff + wdt, yoff + hgt/2); |
3505 |
23 Sep 15 |
olle |
context.rotate(-Math.PI/2); |
3505 |
23 Sep 15 |
olle |
context.textAlign = "center"; |
3505 |
23 Sep 15 |
olle |
context.fillText(title, 0, 0); |
3505 |
23 Sep 15 |
olle |
context.restore(); |
3505 |
23 Sep 15 |
olle |
515 |
} |
3505 |
23 Sep 15 |
olle |
516 |
|
3505 |
23 Sep 15 |
olle |
517 |
/** |
3505 |
23 Sep 15 |
olle |
* Draws a rectangle in a canvas context, using coordinates |
3505 |
23 Sep 15 |
olle |
* where x increases to the right and y increases upwards, |
3505 |
23 Sep 15 |
olle |
* and where the offsets are added automatically. |
3505 |
23 Sep 15 |
olle |
* Rectangle input is given as coordinates for lower left corner |
3505 |
23 Sep 15 |
olle |
* and rectangle width and height. |
3505 |
23 Sep 15 |
olle |
523 |
*/ |
3505 |
23 Sep 15 |
olle |
function drawRect(x, y, rect_wdt, rect_hgt, context, xoff, yoff, wdt, hgt) |
3505 |
23 Sep 15 |
olle |
525 |
{ |
3505 |
23 Sep 15 |
olle |
var x_with_offset = fixCoordinate(context, x + xoff); |
3505 |
23 Sep 15 |
olle |
var y_with_offset = fixCoordinate(context, yoff + hgt - y - rect_hgt); |
3505 |
23 Sep 15 |
olle |
// Draw box rectangle |
3505 |
23 Sep 15 |
olle |
context.strokeStyle = "#000000"; |
3505 |
23 Sep 15 |
olle |
context.strokeRect(x_with_offset, y_with_offset, Math.floor(rect_wdt), Math.floor(rect_hgt)); |
3505 |
23 Sep 15 |
olle |
531 |
} |
3505 |
23 Sep 15 |
olle |
532 |
|
3505 |
23 Sep 15 |
olle |
533 |
/** |
3505 |
23 Sep 15 |
olle |
* Draws a line in a canvas context, using coordinates |
3505 |
23 Sep 15 |
olle |
* where x increases to the right and y increases upwards, |
3505 |
23 Sep 15 |
olle |
* and where the offsets are added automatically. |
3505 |
23 Sep 15 |
olle |
537 |
*/ |
3505 |
23 Sep 15 |
olle |
function drawLine(x1, y1, x2, y2, context, xoff, yoff, wdt, hgt) |
3505 |
23 Sep 15 |
olle |
539 |
{ |
3505 |
23 Sep 15 |
olle |
var x1_with_offset = fixCoordinate(context, x1 + xoff); |
3505 |
23 Sep 15 |
olle |
var x2_with_offset = fixCoordinate(context, x2 + xoff); |
3505 |
23 Sep 15 |
olle |
var y1_with_offset = fixCoordinate(context, yoff + hgt - y1); |
3505 |
23 Sep 15 |
olle |
var y2_with_offset = fixCoordinate(context, yoff + hgt - y2); |
3505 |
23 Sep 15 |
olle |
context.moveTo(x1_with_offset, y1_with_offset); |
3505 |
23 Sep 15 |
olle |
context.lineTo(x2_with_offset, y2_with_offset); |
3505 |
23 Sep 15 |
olle |
546 |
} |
3505 |
23 Sep 15 |
olle |
547 |
|
3505 |
23 Sep 15 |
olle |
548 |
/** |
3505 |
23 Sep 15 |
olle |
* Draws a dashed vertical line in a canvas context, using coordinates |
3505 |
23 Sep 15 |
olle |
* where x increases to the right and y increases upwards, |
3505 |
23 Sep 15 |
olle |
* and where the offsets are added automatically. |
3505 |
23 Sep 15 |
olle |
552 |
*/ |
3505 |
23 Sep 15 |
olle |
function drawDashedLineVertical(x, y1, y2, context, xoff, yoff, wdt, hgt, dash_size, space_size) |
3505 |
23 Sep 15 |
olle |
554 |
{ |
3505 |
23 Sep 15 |
olle |
var sign = 1; |
3505 |
23 Sep 15 |
olle |
if (y1 > y2) |
3505 |
23 Sep 15 |
olle |
557 |
{ |
3505 |
23 Sep 15 |
olle |
sign = -1; |
3505 |
23 Sep 15 |
olle |
559 |
} |
3505 |
23 Sep 15 |
olle |
var y = y1; |
3505 |
23 Sep 15 |
olle |
var y_new; |
3505 |
23 Sep 15 |
olle |
while (sign*(y2 - y) > 0) |
3505 |
23 Sep 15 |
olle |
563 |
{ |
3505 |
23 Sep 15 |
olle |
y_new = y + sign*dash_size; |
3505 |
23 Sep 15 |
olle |
if (sign*y_new > sign*y2) |
3505 |
23 Sep 15 |
olle |
566 |
{ |
3505 |
23 Sep 15 |
olle |
y_new = y2; |
3505 |
23 Sep 15 |
olle |
568 |
} |
3505 |
23 Sep 15 |
olle |
drawLine(x, y, x, y_new, context, xoff, yoff, wdt, hgt); |
3505 |
23 Sep 15 |
olle |
y = y_new + sign*space_size; |
3505 |
23 Sep 15 |
olle |
571 |
} |
3505 |
23 Sep 15 |
olle |
572 |
} |
3505 |
23 Sep 15 |
olle |
573 |
|
3505 |
23 Sep 15 |
olle |
574 |
/** |
3505 |
23 Sep 15 |
olle |
* Draws a dotted horizontal line in a canvas context, using coordinates |
3505 |
23 Sep 15 |
olle |
* where x increases to the right and y increases upwards, |
3505 |
23 Sep 15 |
olle |
* and where the offsets are added automatically. |
3505 |
23 Sep 15 |
olle |
578 |
*/ |
3505 |
23 Sep 15 |
olle |
function drawDottedLineHorizontal(x1, x2, y, context, xoff, yoff, wdt, hgt) |
3505 |
23 Sep 15 |
olle |
580 |
{ |
3505 |
23 Sep 15 |
olle |
var dash_step1 = 2; |
3505 |
23 Sep 15 |
olle |
var space_step1 = 4; |
3505 |
23 Sep 15 |
olle |
var sign = 1; |
3505 |
23 Sep 15 |
olle |
if (x1 > x2) |
3505 |
23 Sep 15 |
olle |
585 |
{ |
3505 |
23 Sep 15 |
olle |
sign = -1; |
3505 |
23 Sep 15 |
olle |
587 |
} |
3505 |
23 Sep 15 |
olle |
var x = x1; |
3505 |
23 Sep 15 |
olle |
var x_new; |
3505 |
23 Sep 15 |
olle |
while (sign*(x2 - x) > 0) |
3505 |
23 Sep 15 |
olle |
591 |
{ |
3505 |
23 Sep 15 |
olle |
x_new = x + sign*dash_step1; |
3505 |
23 Sep 15 |
olle |
if (sign*x_new > sign*x2) |
3505 |
23 Sep 15 |
olle |
594 |
{ |
3505 |
23 Sep 15 |
olle |
x_new = x2; |
3505 |
23 Sep 15 |
olle |
596 |
} |
3505 |
23 Sep 15 |
olle |
drawLine(x, y, x_new, y, context, xoff, yoff, wdt, hgt); |
3505 |
23 Sep 15 |
olle |
x = x_new + sign*space_step1; |
3505 |
23 Sep 15 |
olle |
599 |
} |
3505 |
23 Sep 15 |
olle |
600 |
} |
3505 |
23 Sep 15 |
olle |
601 |
|
3505 |
23 Sep 15 |
olle |
602 |
/** |
3505 |
23 Sep 15 |
olle |
* Draws text in a canvas context, using coordinates |
3505 |
23 Sep 15 |
olle |
* where x increases to the right and y increases upwards, |
3505 |
23 Sep 15 |
olle |
* and where the offsets are added automatically. |
3505 |
23 Sep 15 |
olle |
606 |
*/ |
3505 |
23 Sep 15 |
olle |
function drawText(text, x1, y1, context, xoff, yoff, wdt, hgt) |
3505 |
23 Sep 15 |
olle |
608 |
{ |
3505 |
23 Sep 15 |
olle |
var x1_with_offset = fixCoordinate(context, x1 + xoff); |
3505 |
23 Sep 15 |
olle |
var y1_with_offset = fixCoordinate(context, yoff + hgt - y1); |
3505 |
23 Sep 15 |
olle |
context.fillText(text, x1_with_offset, y1_with_offset); |
3505 |
23 Sep 15 |
olle |
612 |
} |
3505 |
23 Sep 15 |
olle |
613 |
|
3505 |
23 Sep 15 |
olle |
614 |
/** |
3505 |
23 Sep 15 |
olle |
* Calculates an array of values for y-axis scale markers (ticks), |
3505 |
23 Sep 15 |
olle |
* given the min and max values of the data, and the numbers |
3505 |
23 Sep 15 |
olle |
* of markers (optional). |
3505 |
23 Sep 15 |
olle |
618 |
* |
3505 |
23 Sep 15 |
olle |
* Based on PHP example on |
3505 |
23 Sep 15 |
olle |
* http://stackoverflow.com/questions/326679/choosing-an-attractive-linear-scale-for-a-graphs-y-axis |
3505 |
23 Sep 15 |
olle |
* but with modified behavior for step size < 1. |
3505 |
23 Sep 15 |
olle |
622 |
*/ |
3505 |
23 Sep 15 |
olle |
function calculateScaleMarkerArray(minDataValue, maxDataValue, numTicks) |
3505 |
23 Sep 15 |
olle |
624 |
{ |
3505 |
23 Sep 15 |
olle |
if (numTicks == null || numTicks <= 0) |
3505 |
23 Sep 15 |
olle |
626 |
{ |
3505 |
23 Sep 15 |
olle |
numTicks = 10; |
3505 |
23 Sep 15 |
olle |
628 |
} |
3505 |
23 Sep 15 |
olle |
// Adjust ticks if needed |
3505 |
23 Sep 15 |
olle |
if (numTicks < 2) |
3505 |
23 Sep 15 |
olle |
631 |
{ |
3505 |
23 Sep 15 |
olle |
numTicks = 2; |
3505 |
23 Sep 15 |
olle |
633 |
} |
3505 |
23 Sep 15 |
olle |
else if (numTicks > 2) |
3505 |
23 Sep 15 |
olle |
635 |
{ |
3505 |
23 Sep 15 |
olle |
numTicks -= 2; |
3505 |
23 Sep 15 |
olle |
637 |
} |
3505 |
23 Sep 15 |
olle |
638 |
|
3505 |
23 Sep 15 |
olle |
// If min and max are identical, then adjust these values. |
3505 |
23 Sep 15 |
olle |
if (minDataValue == maxDataValue) |
3505 |
23 Sep 15 |
olle |
641 |
{ |
3505 |
23 Sep 15 |
olle |
minDataValue = minDataValue - 10; |
3505 |
23 Sep 15 |
olle |
maxDataValue = maxDataValue + 10; |
3505 |
23 Sep 15 |
olle |
644 |
} |
3505 |
23 Sep 15 |
olle |
645 |
|
3505 |
23 Sep 15 |
olle |
// Determine range |
3505 |
23 Sep 15 |
olle |
var range = maxDataValue - minDataValue; |
3505 |
23 Sep 15 |
olle |
// Get raw step value |
3505 |
23 Sep 15 |
olle |
var tempStep = range/numTicks; |
3505 |
23 Sep 15 |
olle |
// Calculate pretty step value |
3505 |
23 Sep 15 |
olle |
var mag = Math.floor(Math.log(tempStep)/Math.log(10) + 0.001); |
3505 |
23 Sep 15 |
olle |
var magPow = Math.pow(10, mag); |
3505 |
23 Sep 15 |
olle |
if (magPow >= 1) |
3505 |
23 Sep 15 |
olle |
654 |
{ |
3505 |
23 Sep 15 |
olle |
magPow = Math.floor(magPow + 0.001); |
3505 |
23 Sep 15 |
olle |
656 |
} |
3505 |
23 Sep 15 |
olle |
var magMsd = Math.floor(tempStep/magPow + 0.5); |
3505 |
23 Sep 15 |
olle |
var stepSize = magMsd*magPow; |
3505 |
23 Sep 15 |
olle |
// Correction for stepSize < 1000 |
3505 |
23 Sep 15 |
olle |
if (stepSize < 1000) |
3505 |
23 Sep 15 |
olle |
661 |
{ |
3505 |
23 Sep 15 |
olle |
if (stepSize >= 750) |
3505 |
23 Sep 15 |
olle |
663 |
{ |
3505 |
23 Sep 15 |
olle |
stepSize = 1000; |
3505 |
23 Sep 15 |
olle |
665 |
} |
3505 |
23 Sep 15 |
olle |
else if (stepSize >= 250) |
3505 |
23 Sep 15 |
olle |
667 |
{ |
3505 |
23 Sep 15 |
olle |
stepSize = 500; |
3505 |
23 Sep 15 |
olle |
669 |
} |
3505 |
23 Sep 15 |
olle |
else if (stepSize >= 75) |
3505 |
23 Sep 15 |
olle |
671 |
{ |
3505 |
23 Sep 15 |
olle |
stepSize = 100; |
3505 |
23 Sep 15 |
olle |
673 |
} |
3505 |
23 Sep 15 |
olle |
else if (stepSize >= 25) |
3505 |
23 Sep 15 |
olle |
675 |
{ |
3505 |
23 Sep 15 |
olle |
stepSize = 50; |
3505 |
23 Sep 15 |
olle |
677 |
} |
3505 |
23 Sep 15 |
olle |
else if (stepSize >= 7.5) |
3505 |
23 Sep 15 |
olle |
679 |
{ |
3505 |
23 Sep 15 |
olle |
stepSize = 10; |
3505 |
23 Sep 15 |
olle |
681 |
} |
3505 |
23 Sep 15 |
olle |
else if (stepSize >= 2.5) |
3505 |
23 Sep 15 |
olle |
683 |
{ |
3505 |
23 Sep 15 |
olle |
stepSize = 5; |
3505 |
23 Sep 15 |
olle |
685 |
} |
3505 |
23 Sep 15 |
olle |
else if (stepSize >= 0.75) |
3505 |
23 Sep 15 |
olle |
687 |
{ |
3505 |
23 Sep 15 |
olle |
stepSize = 1; |
3505 |
23 Sep 15 |
olle |
689 |
} |
3505 |
23 Sep 15 |
olle |
else if (stepSize >= 0.25) |
3505 |
23 Sep 15 |
olle |
691 |
{ |
3505 |
23 Sep 15 |
olle |
stepSize = 0.5; |
3505 |
23 Sep 15 |
olle |
693 |
} |
3505 |
23 Sep 15 |
olle |
else if (stepSize >= 0.075) |
3505 |
23 Sep 15 |
olle |
695 |
{ |
3505 |
23 Sep 15 |
olle |
stepSize = 0.1; |
3505 |
23 Sep 15 |
olle |
697 |
} |
3505 |
23 Sep 15 |
olle |
else if (stepSize >= 0.025) |
3505 |
23 Sep 15 |
olle |
699 |
{ |
3505 |
23 Sep 15 |
olle |
stepSize = 0.05; |
3505 |
23 Sep 15 |
olle |
701 |
} |
3505 |
23 Sep 15 |
olle |
else if (stepSize >= 0.0075) |
3505 |
23 Sep 15 |
olle |
703 |
{ |
3505 |
23 Sep 15 |
olle |
stepSize = 0.01; |
3505 |
23 Sep 15 |
olle |
705 |
} |
3505 |
23 Sep 15 |
olle |
else if (stepSize >= 0.0025) |
3505 |
23 Sep 15 |
olle |
707 |
{ |
3505 |
23 Sep 15 |
olle |
stepSize = 0.005; |
3505 |
23 Sep 15 |
olle |
709 |
} |
3505 |
23 Sep 15 |
olle |
else if (stepSize >= 0.00075) |
3505 |
23 Sep 15 |
olle |
711 |
{ |
3505 |
23 Sep 15 |
olle |
stepSize = 0.001; |
3505 |
23 Sep 15 |
olle |
713 |
} |
3505 |
23 Sep 15 |
olle |
else if (stepSize >= 0.00025) |
3505 |
23 Sep 15 |
olle |
715 |
{ |
3505 |
23 Sep 15 |
olle |
stepSize = 0.0005; |
3505 |
23 Sep 15 |
olle |
717 |
} |
3505 |
23 Sep 15 |
olle |
else if (stepSize >= 0.000075) |
3505 |
23 Sep 15 |
olle |
719 |
{ |
3505 |
23 Sep 15 |
olle |
stepSize = 0.0001; |
3505 |
23 Sep 15 |
olle |
721 |
} |
3505 |
23 Sep 15 |
olle |
722 |
} |
3505 |
23 Sep 15 |
olle |
723 |
|
3505 |
23 Sep 15 |
olle |
// Build y label array |
3505 |
23 Sep 15 |
olle |
// Lower and upper bounds calculations |
3505 |
23 Sep 15 |
olle |
var lowerBound = stepSize*Math.floor(minDataValue/stepSize); |
3505 |
23 Sep 15 |
olle |
var upperBound = stepSize*Math.ceil(maxDataValue/stepSize); |
3505 |
23 Sep 15 |
olle |
// Build array |
3505 |
23 Sep 15 |
olle |
var tickValuesArr = []; |
3505 |
23 Sep 15 |
olle |
var value = lowerBound; |
3505 |
23 Sep 15 |
olle |
var i = 0; |
3505 |
23 Sep 15 |
olle |
tickValuesArr[i] = value; |
3505 |
23 Sep 15 |
olle |
value += stepSize; |
3505 |
23 Sep 15 |
olle |
while(value <= upperBound) |
3505 |
23 Sep 15 |
olle |
735 |
{ |
3505 |
23 Sep 15 |
olle |
i++; |
3505 |
23 Sep 15 |
olle |
tickValuesArr[i] = value; |
3505 |
23 Sep 15 |
olle |
value += stepSize; |
3505 |
23 Sep 15 |
olle |
739 |
} |
3505 |
23 Sep 15 |
olle |
return tickValuesArr; |
3505 |
23 Sep 15 |
olle |
741 |
} |
3505 |
23 Sep 15 |
olle |
742 |
|
3505 |
23 Sep 15 |
olle |
743 |
/** |
3505 |
23 Sep 15 |
olle |
* Trims an array of values for y-axis scale markers (ticks), |
3505 |
23 Sep 15 |
olle |
* to only include values in given value range. |
3505 |
23 Sep 15 |
olle |
746 |
*/ |
3505 |
23 Sep 15 |
olle |
function trimScaleMarkerArray(origTickValuesArr, minValue, maxValue) |
3505 |
23 Sep 15 |
olle |
748 |
{ |
3505 |
23 Sep 15 |
olle |
// Build trimmed y label array |
3505 |
23 Sep 15 |
olle |
var tickValuesArr = []; |
3505 |
23 Sep 15 |
olle |
var value; |
3505 |
23 Sep 15 |
olle |
var k = 0; |
3505 |
23 Sep 15 |
olle |
for (i=0; i < origTickValuesArr.length; i++) |
3505 |
23 Sep 15 |
olle |
754 |
{ |
3505 |
23 Sep 15 |
olle |
value = origTickValuesArr[i]; |
3505 |
23 Sep 15 |
olle |
if (value >= minValue && value <= maxValue) |
3505 |
23 Sep 15 |
olle |
757 |
{ |
3505 |
23 Sep 15 |
olle |
// Add value to trimmed array |
3505 |
23 Sep 15 |
olle |
tickValuesArr[k] = value; |
3505 |
23 Sep 15 |
olle |
k++; |
3505 |
23 Sep 15 |
olle |
761 |
} |
3505 |
23 Sep 15 |
olle |
762 |
} |
3505 |
23 Sep 15 |
olle |
return tickValuesArr; |
3505 |
23 Sep 15 |
olle |
764 |
} |
3505 |
23 Sep 15 |
olle |
765 |
|
3505 |
23 Sep 15 |
olle |
766 |
/* |
3505 |
23 Sep 15 |
olle |
Fixes a plotting coordinate by aligning it so that anti-aliasing |
3505 |
23 Sep 15 |
olle |
is avoided. For 1-pixel line widths the coordate should alway be a |
3505 |
23 Sep 15 |
olle |
half-value, eg. 1.5, 10.5, etc. |
3505 |
23 Sep 15 |
olle |
770 |
*/ |
3505 |
23 Sep 15 |
olle |
function fixCoordinate(context, value) |
3505 |
23 Sep 15 |
olle |
772 |
{ |
3505 |
23 Sep 15 |
olle |
return Math.floor(value) + (context.lineWidth % 2) / 2; |
3505 |
23 Sep 15 |
olle |
774 |
} |
3505 |
23 Sep 15 |
olle |
775 |
|