for a point
this.getTooltip = function (xd, yd, xp, yp) {
var that = this;
var resultMarkers = that.findToolTipMarkers(xd, yd, xp, yp);
var buildTooltip = function (markerInfo) {
var content = undefined;
for (var prop in markerInfo) {
if (markerInfo.hasOwnProperty(prop)) {
var propTitle = that.getTitle(prop);
if (content)
content += "
" + propTitle + ": " + markerInfo[prop];
else
content = "
" + propTitle + ": " + markerInfo[prop];
}
}
return "
" + content + "
";
};
if (resultMarkers.length > 0) {
var toolTip = "
" + that.name + "";
resultMarkers.forEach(function (markerInfo) {
toolTip += "
" + buildTooltip(markerInfo);
});
return "
" + toolTip + "
";
}
};
// Others
this.onDataTransformChanged = function (arg) {
this.invalidateLocalBounds();
InteractiveDataDisplay.Markers.prototype.onDataTransformChanged.call(this, arg);
};
Object.defineProperty(this, "color", {
get: function () { return _data.color; },
set: function (value) {
if (value == _data.color) return;
_data.color = value;
this.fireAppearanceChanged("color");
this.requestNextFrameOrUpdate();
},
configurable: false
});
Object.defineProperty(this, "colorPalette", {
get: function () { return _colorPalette; },
set: function (value) {
if (value == _colorPalette) return;
_colorPalette = value;
if (value.isNormalized) {
_colorRange = InteractiveDataDisplay.Utils.getMinMax(_data.color);
}
this.fireAppearanceChanged("colorPalette");
this.requestNextFrameOrUpdate();
},
configurable: false
});
Object.defineProperty(this, "size", {
get: function () { return _data.size; },
set: function (value) {
if (value == _data.size) return;
_data.size = value;
this.fireAppearanceChanged("size");
this.requestNextFrameOrUpdate();
},
configurable: false
});
Object.defineProperty(this, "sizePalette", {
get: function () { return _sizePalette; },
set: function (value) {
if (value == _sizePalette) return;
_sizePalette = value;
if (value.isNormalized) {
_sizeRange = InteractiveDataDisplay.Utils.getMinMax(_data.size);
}
this.fireAppearanceChanged("sizePalette");
this.requestNextFrameOrUpdate();
},
configurable: false
});
Object.defineProperty(this, "shape", {
get: function () { return _shape; },
set: function (value) {
if (value == _shape) return;
_shape = value;
this.fireAppearanceChanged("shape");
this.requestNextFrameOrUpdate();
},
configurable: false
});
Object.defineProperty(this, "border", {
get: function () { return _data.border; },
set: function (value) {
if (value == _data.border) return;
_data.border = value;
this.fireAppearanceChanged("border");
this.requestNextFrameOrUpdate();
},
configurable: false
});
Object.defineProperty(this, "preRender", {
get: function () { return _preRender; },
set: function (value) {
if (value == _preRender) return;
_preRender = value;
this.fireAppearanceChanged("preRender");
this.requestNextFrameOrUpdate();
},
configurable: false
});
this.getLegend = function () {
var that = this;
var div = $("
");
var itemDiv = $("
").appendTo(div);
var fontSize = 14;
if (document.defaultView && document.defaultView.getComputedStyle) {
fontSize = parseFloat(document.defaultView.getComputedStyle(div[0], null).getPropertyValue("font-size"));
}
if (isNaN(fontSize) || fontSize == 0) fontSize = 14;
var canvas = $("
").appendTo(itemDiv);
var canvasIsVisible = true;
var maxSize = fontSize * 1.5;
var x1 = maxSize / 2 + 1;
var y1 = x1;
canvas[0].width = canvas[0].height = maxSize + 2;
var canvasStyle = canvas[0].style;
var context = canvas.get(0).getContext("2d");
var nameDiv = $("
").appendTo(itemDiv);
var setName = function () {
nameDiv.text(that.name);
}
setName();
var item, itemDivStyle;
var itemIsVisible = 0;
var colorIsArray, color, border, drawBorder;
var colorDiv, colorDivStyle, colorControl;
var colorIsVisible = 0;
var sizeIsArray, size, halfSize;
var sizeDiv, sizeDivStyle, sizeControl;
var sizeIsVisible = 0;
var sizeTitle;
var refreshSize = function () {
sizeIsArray = InteractiveDataDisplay.Utils.isArray(_data.size);
if (sizeIsArray) {
size = maxSize;
if (_sizePalette) {
var szTitleText = that.getTitle("size");
if (sizeIsVisible == 0) {
sizeDiv = $("
").appendTo(div);
sizeTitle = $("
").text(szTitleText).appendTo(sizeDiv);
sizeDivStyle = sizeDiv[0].style;
var paletteDiv = $("
").appendTo(sizeDiv);
sizeControl = new InteractiveDataDisplay.SizePaletteViewer(paletteDiv);
sizeIsVisible = 2;
} else {
sizeTitle.text(szTitleText);
}
sizeControl.palette = _sizePalette;
if (_sizePalette.isNormalized) {
sizeControl.dataRange = _sizeRange;
}
if (sizeIsVisible == 1) {
sizeDivStyle.display = "block";
sizeIsVisible = 2;
}
}
}
else {
size = Math.min(_data.size, maxSize);
if (sizeIsVisible == 2) {
sizeDivStyle.display = "none";
sizeIsVisible = 1;
}
}
halfSize = size / 2;
};
var colorTitle;
var refreshColor = function () {
colorIsArray = InteractiveDataDisplay.Utils.isArray(_data.color);
drawBorder = false;
if (colorIsArray && _colorPalette) {
var clrTitleText = that.getTitle("color");
if (colorIsVisible == 0) {
colorDiv = $("
").appendTo(div);
colorTitle = $("
").text(clrTitleText).appendTo(colorDiv);
colorDivStyle = colorDiv[0].style;
var paletteDiv = $("
").appendTo(colorDiv);
colorControl = new InteractiveDataDisplay.ColorPaletteViewer(paletteDiv);
colorIsVisible = 2;
} else {
colorTitle.text(clrTitleText);
}
colorControl.palette = _colorPalette;
if (_colorPalette.isNormalized) {
colorControl.dataRange = _colorRange;
}
if (colorIsVisible == 1) {
colorDivStyle.display = "block";
colorIsVisible = 2;
}
}
else {
if (colorIsVisible == 2) {
colorDivStyle.display = "none";
colorIsVisible = 1;
}
}
if (colorIsArray) {
border = "#000000";
color = "#ffffff";
drawBorder = true;
}
else {
color = _data.color;
border = color;
if (_data.border) {
drawBorder = true;
border = _data.border;
}
}
};
var renderShape = function () {
if (typeof (_shape) == "object") {
if (_shape.getLegendItem) {
var drawData = { size: size, color: color, border: border };
for (var prop in _data) {
if (prop != "size" && prop != "color" && prop != "border") {
var v = _data[prop];
if (InteractiveDataDisplay.Utils.isArray(v)) drawData[prop] = v[i];
else drawData[prop] = v;
}
}
if (itemIsVisible == 0) {
item = _shape.getLegendItem(drawData);
itemDiv[0].insertBefore(item[0], name[0]);
}
else {
var newItem = _shape.getLegendItem(drawData);
item.replaceWith(newItem);
item = newItem;
}
itemDivStyle = item[0].style;
itemIsVisible = 2;
}
if (canvasIsVisible) {
canvasStyle.display = "none";
canvasIsVisible = false;
}
}
else {
if (itemIsVisible == 2) {
itemDivStyle.display = "none";
itemIsVisible = 1;
}
context.clearRect(0, 0, maxSize + 2, maxSize + 2);
context.strokeStyle = border;
context.fillStyle = color;
var invShape = isStandartShape(_shape);
switch (invShape) {
case 1: // box
context.fillRect(x1 - halfSize, y1 - halfSize, size, size);
if (drawBorder)
context.strokeRect(x1 - halfSize, y1 - halfSize, size, size);
break;
case 2: // circle
context.beginPath();
context.arc(x1, y1, halfSize, 0, 2 * Math.PI);
context.fill();
if (drawBorder)
context.stroke();
break;
case 3: // diamond
context.beginPath();
context.moveTo(x1 - halfSize, y1);
context.lineTo(x1, y1 - halfSize);
context.lineTo(x1 + halfSize, y1);
context.lineTo(x1, y1 + halfSize);
context.closePath();
context.fill();
if (drawBorder)
context.stroke();
break;
case 4: // cross
var thirdSize = size / 3;
var halfThirdSize = thirdSize / 2;
if (drawBorder) {
context.beginPath();
context.moveTo(x1 - halfSize, y1 - halfThirdSize);
context.lineTo(x1 - halfThirdSize, y1 - halfThirdSize);
context.lineTo(x1 - halfThirdSize, y1 - halfSize);
context.lineTo(x1 + halfThirdSize, y1 - halfSize);
context.lineTo(x1 + halfThirdSize, y1 - halfThirdSize);
context.lineTo(x1 + halfSize, y1 - halfThirdSize);
context.lineTo(x1 + halfSize, y1 + halfThirdSize);
context.lineTo(x1 + halfThirdSize, y1 + halfThirdSize);
context.lineTo(x1 + halfThirdSize, y1 + halfSize);
context.lineTo(x1 - halfThirdSize, y1 + halfSize);
context.lineTo(x1 - halfThirdSize, y1 + halfThirdSize);
context.lineTo(x1 - halfSize, y1 + halfThirdSize);
context.closePath();
context.fill();
context.stroke();
} else {
context.fillRect(x1 - halfThirdSize, y1 - halfSize, thirdSize, size);
context.fillRect(x1 - halfSize, y1 - halfThirdSize, size, thirdSize);
}
break;
case 5: // triangle
context.beginPath();
context.moveTo(x1 - halfSize, y1 + halfSize);
context.lineTo(x1, y1 - halfSize);
context.lineTo(x1 + halfSize, y1 + halfSize);
context.closePath();
context.fill();
if (drawBorder)
context.stroke();
break;
}
if (!canvasIsVisible) {
canvasStyle.display = "inline-block";
canvasIsVisible = true;
}
}
};
refreshColor();
refreshSize();
renderShape();
var that = this;
this.host.bind("appearanceChanged",
function (event, propertyName) {
if (!propertyName || propertyName == "name")
setName();
if (!propertyName || propertyName == "color" || propertyName == "colorPalette")
refreshColor();
if (!propertyName || propertyName == "size" || propertyName == "sizePalette")
refreshSize();
renderShape();
});
var onLegendRemove = function () {
that.host.unbind("appearanceChanged");
div[0].innerHTML = "";
div.removeClass("idd-legend-item");
};
return { div: div, onLegendRemove: onLegendRemove };
};
// Initialization
if (initialData && typeof initialData.y != 'undefined')
this.draw(initialData);
};
InteractiveDataDisplay.Markers.prototype = new InteractiveDataDisplay.CanvasPlot;
InteractiveDataDisplay.AdaptMarkerSize = function (markers, plotRect, screenSize, transform, context) {
var visibleMarkers = 0;
var x = markers.x;
var y = markers.y;
var xi, yi;
for (var i = 0, n = x.length; i < n; i++) {
xi = x[i];
yi = y[i];
if (xi >= plotRect.x && xi <= plotRect.x + plotRect.width && yi >= plotRect.y && yi <= plotRect.y + plotRect.height)
visibleMarkers++;
}
// constants
var areaConst = 0.2;
var minAdaptiveSize = 15;
var maxAdaptiveSize = 100;
// adaptive size in plot coordinates
var adaptiveSize = Math.sqrt(areaConst * plotRect.width * plotRect.height / visibleMarkers);
// transform to screen coordinates and check limits
adaptiveSize = transform.dataToScreenX(adaptiveSize) - transform.dataToScreenX(0);
if (adaptiveSize < minAdaptiveSize) adaptiveSize = minAdaptiveSize;
else if (adaptiveSize > maxAdaptiveSize) adaptiveSize = maxAdaptiveSize;
markers.size = adaptiveSize;
};
;// Area plot takes data with coordinates named 'x', 'y1', 'y2' and a fill colour named 'fill'.
InteractiveDataDisplay.Area = function (div, master) {
var that = this;
var defaultFill = "rgba(0,0,0,0.2)";
// Initialization (#1)
var initializer = InteractiveDataDisplay.Utils.getDataSourceFunction(div, InteractiveDataDisplay.readCsv);
var initialData = initializer(div);
this.base = InteractiveDataDisplay.CanvasPlot;
this.base(div, master);
var _x = []; // an array of horizontal axis coordinates
var _y1 = [];
var _y2 = []; // arrays of lower and upper limits of the band
var _fill = defaultFill;
// default styles:
if (initialData) {
_fill = typeof initialData.fill != "undefined" ? initialData.fill : defaultFill;
}
this.draw = function (data) {
var y1 = data.y1;
if (!y1) throw "Data series y1 is undefined";
var n = y1.length;
var y2 = data.y2;
if (!y2) throw "Data series y2 is undefined";
if (y2.length !== n)
throw "Data series y1 and y2 have different lengths";
var x = data.x;
if (!x) {
x = InteractiveDataDisplay.Utils.range(0, n - 1);
}
if (x.length !== n)
throw "Data series x and y1, y2 have different lengths";
_y1 = y1;
_y2 = y2;
_x = x;
// styles:
_fill = typeof data.fill != "undefined" ? data.fill : defaultFill;
this.invalidateLocalBounds();
this.requestNextFrameOrUpdate();
this.fireAppearanceChanged();
};
// Returns a rectangle in the plot plane.
this.computeLocalBounds = function () {
var dataToPlotX = this.xDataTransform && this.xDataTransform.dataToPlot;
var dataToPlotY = this.yDataTransform && this.yDataTransform.dataToPlot;
var y1 = InteractiveDataDisplay.Utils.getBoundingBoxForArrays(_x, _y1, dataToPlotX, dataToPlotY);
var y2 = InteractiveDataDisplay.Utils.getBoundingBoxForArrays(_x, _y2, dataToPlotX, dataToPlotY);
return InteractiveDataDisplay.Utils.unionRects(y1, y2);
};
// Returns 4 margins in the screen coordinate system
this.getLocalPadding = function () {
return { left: 0, right: 0, top: 0, bottom: 0 };
};
this.renderCore = function (plotRect, screenSize) {
InteractiveDataDisplay.Area.prototype.renderCore.call(this, plotRect, screenSize);
var context = that.getContext(true);
if (_x === undefined || _y1 == undefined || _y2 == undefined)
return;
var n = _y1.length;
if (n == 0) return;
var t = that.getTransform();
var dataToScreenX = t.dataToScreenX;
var dataToScreenY = t.dataToScreenY;
// size of the canvas
var w_s = screenSize.width;
var h_s = screenSize.height;
var xmin = 0, xmax = w_s;
var ymin = 0, ymax = h_s;
context.fillStyle = _fill;
//Drawing polygons
var polygons = [];
var curInd = undefined;
for (var i = 0; i < n; i++) {
if (isNaN(_x[i]) || isNaN(_y1[i]) || isNaN(_y2[i])) {
if (curInd === undefined) {
curInd = i;
}
else {
polygons.push([curInd, i]);
curInd = undefined;
}
} else {
if (curInd === undefined) {
curInd = i;
}
else {
if (i === n - 1) {
polygons.push([curInd, i]);
curInd = undefined;
}
}
}
}
var nPoly = polygons.length;
for (var i = 0; i < nPoly; i++) {
context.beginPath();
var curPoly = polygons[i];
context.moveTo(dataToScreenX(_x[curPoly[0]]), dataToScreenY(_y1[curPoly[0]]));
for (var j = curPoly[0] + 1; j <= curPoly[1]; j++) {
context.lineTo(dataToScreenX(_x[j]), dataToScreenY(_y1[j]));
}
for (var j = curPoly[1]; j >= curPoly[0]; j--) {
context.lineTo(dataToScreenX(_x[j]), dataToScreenY(_y2[j]));
}
context.fill();
}
};
// Clipping algorithms
var code = function (x, y, xmin, xmax, ymin, ymax) {
return (x < xmin) << 3 | (x > xmax) << 2 | (y < ymin) << 1 | (y > ymax);
};
// Others
this.onDataTransformChanged = function (arg) {
this.invalidateLocalBounds();
InteractiveDataDisplay.Area.prototype.onDataTransformChanged.call(this, arg);
};
// Initialization
if (initialData && initialData.x && initialData.y1 && initialData.y2)
this.draw(initialData);
}
InteractiveDataDisplay.Area.prototype = new InteractiveDataDisplay.CanvasPlot;
;// See http://jsperf.com/rendering-a-frame-in-image-data
InteractiveDataDisplay.heatmapBackgroundRenderer = new InteractiveDataDisplay.SharedRenderWorker(
function() {
var workerCodeUri;
if(typeof InteractiveDataDisplay.heatmapBackgroundRendererCodeBase64 === 'undefined' || /PhantomJS/.test(window.navigator.userAgent)) {
// Build process usually initializes the heatmapBackgroundRendererCodeBase64 with base64 encoded
// concatenation of idd.heatmapworker.js and idd.transforms.js.
workerCodeUri = "idd.heatmapworker.js";
}
else {
var workerBlob = new Blob([ window.atob(InteractiveDataDisplay.heatmapBackgroundRendererCodeBase64) ], { type: 'text/javascript' });
workerCodeUri = window.URL.createObjectURL(workerBlob);
}
return workerCodeUri
} (),
function (heatmapPlot, completedTask) {
heatmapPlot.onRenderTaskCompleted(completedTask);
});
// Renders a fuction f(x,y) on a regular grid (x,y) as a heat map using color palette
InteractiveDataDisplay.Heatmap = function (div, master) {
// Initialization (#1)
var initializer = InteractiveDataDisplay.Utils.getDataSourceFunction(div, InteractiveDataDisplay.readCsv2d);
var initialData = initializer(div);
if (initialData && typeof initialData.y !== 'undefined' && typeof initialData.f !== 'undefined') {
var y = initialData.y;
var f = initialData.f;
var n = y.length;
var m = f.length;
if (n > 1 && m > 0 && y[0] > y[1]) {
y.reverse();
for (var i = 0; i < n; i++)
f[i].reverse();
}
}
this.base = InteractiveDataDisplay.CanvasPlot;
this.base(div, master);
if (!div) return;
// default styles:
var loadPalette = function (palette) {
if (palette) {
try {
if (typeof palette == 'string')
_palette = InteractiveDataDisplay.ColorPalette.parse(palette);
else
_palette = palette;
_paletteColors = InteractiveDataDisplay.ColorPalette.toArray(_palette, 512);
} catch (exc) {
if (window.console) console.error("Failed to initialize the palette");
}
}
};
var loadOpacity = function (opacity) {
_opacity = Math.min(1.0, Math.max(0.0, opacity));
};
var _innerCanvas = document.createElement("canvas");
var _imageData;
var _y;
var _x;
var _f;
var _fmin, _fmax;
var _opacity; // 1 is opaque, 0 is transparent
var _mode; // gradient or matrix
var _palette;
var _dataChanged;
var _paletteColors;
loadOpacity((initialData && typeof (initialData.opacity) != 'undefined') ? parseFloat(initialData.opacity) : 1.0);
loadPalette((initialData && typeof (initialData.palette) != 'undefined') ? initialData.palette : InteractiveDataDisplay.palettes.grayscale);
var findFminmax = function () {
var n = _f.length;
if (n < 1) return;
var m = _f[0].length;
if (m < 1) return;
_fmin = _fmax = _f[0][0];
for (var i = 0; i < n; i++) {
var fi = _f[i];
for (var j = 0; j < m; j++) {
var v = fi[j];
if (v == v) {
if (v > _fmax) _fmax = v;
else if (v < _fmin) _fmin = v;
}
}
}
};
var lastCompletedTask;
var that = this;
this.onRenderTaskCompleted = function (completedTask) {
lastCompletedTask = completedTask;
if (_innerCanvas.width !== lastCompletedTask.width || _innerCanvas.height !== lastCompletedTask.height) {
_innerCanvas.width = lastCompletedTask.width;
_innerCanvas.height = lastCompletedTask.height;
}
var context = _innerCanvas.getContext("2d");
context.putImageData(lastCompletedTask.image, 0, 0);
//console.log("Complete render " + this.name);
that.requestNextFrame();
};
this.draw = function (data) {
var f = data.f;
if (!f) throw "Data series f is undefined";
var n = f.length;
var m = f[0].length;
var x = data.x;
if (!x) {
x = InteractiveDataDisplay.Utils.range(0, n);
} else {
if (x.length != n && x.length != n + 1) throw "Data series x must have length equal or one more than length of data series f by first dimension";
}
var y = data.y;
if (!y) {
y = InteractiveDataDisplay.Utils.range(0, m);
} else {
if (y.length != m && y.length != m + 1) throw "Data series y must have length equal or one more than length of data series f by second dimension";
}
_x = x;
_y = y;
_f = f;
if (x.length == n) {
if (y.length != m) throw "Data series y must have length equal to length of data series f by second dimension";
_mode = 'gradient';
} else {
if (y.length != m + 1) throw "Data series y must have length equal to one more than length of data series f by second dimension";
_mode = 'matrix';
}
if (_x.length < 2) throw "Data series x must have at least 2 elements by each dimension";
if (_y.length < 2) throw "Data series y must have at least 2 elements by each dimension";
// styles:
if (data && typeof (data.opacity) != 'undefined') {
loadOpacity(parseFloat(data.opacity));
}
if (data && typeof (data.palette) != 'undefined')
loadPalette(data.palette);
if (_palette.isNormalized) findFminmax();
_dataChanged = true;
var prevBB = this.invalidateLocalBounds();
var bb = this.getLocalBounds();
if (InteractiveDataDisplay.Utils.equalRect(prevBB, bb))
this.requestNextFrame();
else
this.requestNextFrameOrUpdate();
this.fireAppearanceChanged();
};
// Returns a rectangle in the plot plane.
this.computeLocalBounds = function () {
var _bbox;
if (_x && _y) { // todo: fix for matrix mode
var xmin, xmax, ymin, ymax;
var n = _x.length;
var m = _y.length;
var i;
for (i = 0; i < n; i++) {
xmin = _x[i];
if (xmin == xmin) break;
}
for (i = n; --i >= 0;) {
xmax = _x[i];
if (xmax == xmax) break;
}
for (i = 0; i < m; i++) {
ymin = _y[i];
if (ymin == ymin) break;
}
for (i = m; --i >= 0;) {
ymax = _y[i];
if (ymax == ymax) break;
}
var dataToPlotX = this.xDataTransform && this.xDataTransform.dataToPlot;
var dataToPlotY = this.yDataTransform && this.yDataTransform.dataToPlot;
if (dataToPlotX) {
xmin = dataToPlotX(xmin);
xmax = dataToPlotX(xmax);
}
if (dataToPlotY) {
ymin = dataToPlotY(ymin);
ymax = dataToPlotY(ymax);
}
_bbox = { x: Math.min(xmin, xmax), y: Math.min(ymin, ymax), width: Math.abs(xmax - xmin), height: Math.abs(ymax - ymin) };
}
return _bbox;
};
if (typeof (Modernizr) != 'undefined') {
if (div && (!Modernizr.webworkers || !Modernizr.postmessage)) {
var parent = div[0].parentElement;
if (parent) {
var hasText = false;
for (var i = 0; i < parent.childNodes.length; i++) {
if ($(parent.childNodes[i]).hasClass("nowebworkers")) {
hasText = true;
break;
}
}
div[0].removeAttribute("data-idd-plot");
div[0].innerText = "";
if (!hasText) {
div[0].innerText = ' Heatmap cannot be rendered: browser does not support web workers.';
div.addClass("nowebworkers");
}
else div[0].innerText = "";
}
return;
}
}
//Theess objects are used for renderfing on the map
var polygon = undefined;
var polygon2 = undefined;
// Updates output of this plot using the current coordinate transform and screen size.
// plotRect {x,y,width,height} Rectangle in the plot plane which is visible, (x,y) is left/bottom of the rectangle
// screenSize {width,height} Size of the output region to render inside
// Returns true, if the plot actually has rendered something; otherwise, returns false.
this.renderCore = function (plotRect, screenSize) {
InteractiveDataDisplay.Heatmap.prototype.renderCore.call(this, plotRect, screenSize);
var context = this.getContext(true);
if (_x == undefined || _y == undefined || _f == undefined)
return;
var ct = this.coordinateTransform;
var plotToScreenX = ct.plotToScreenX;
var plotToScreenY = ct.plotToScreenY;
var bb = this.getLocalBounds();
// this is a rectangle which we should fill:
var visibleRect = InteractiveDataDisplay.Utils.intersect(bb, plotRect);
if (!visibleRect) return;
var drawBasic = true;
if (master.mapControl !== undefined) {
var left = bb.x;
var middle = bb.x + bb.width / 2;
var right = bb.x + bb.width;
if (polygon === undefined) {
var backColor = 120;
var options = {
fillColor: new Microsoft.Maps.Color(backColor, backColor, backColor, backColor),
strokeColor: new Microsoft.Maps.Color(backColor, backColor, backColor, backColor),
strokeThickness: 0
};
polygon = new Microsoft.Maps.Polygon([
new Microsoft.Maps.Location(InteractiveDataDisplay.mercatorTransform.plotToData(bb.y), left),
new Microsoft.Maps.Location(InteractiveDataDisplay.mercatorTransform.plotToData(bb.y), middle),
new Microsoft.Maps.Location(InteractiveDataDisplay.mercatorTransform.plotToData(bb.y + bb.height), middle),
new Microsoft.Maps.Location(InteractiveDataDisplay.mercatorTransform.plotToData(bb.y + bb.height), left),
], options);
polygon2 = new Microsoft.Maps.Polygon([
new Microsoft.Maps.Location(InteractiveDataDisplay.mercatorTransform.plotToData(bb.y), middle),
new Microsoft.Maps.Location(InteractiveDataDisplay.mercatorTransform.plotToData(bb.y), right),
new Microsoft.Maps.Location(InteractiveDataDisplay.mercatorTransform.plotToData(bb.y + bb.height), right),
new Microsoft.Maps.Location(InteractiveDataDisplay.mercatorTransform.plotToData(bb.y + bb.height), middle),
], options);
master.mapControl.entities.push(polygon);
master.mapControl.entities.push(polygon2);
}
if (_dataChanged) {
polygon.setLocations([
new Microsoft.Maps.Location(InteractiveDataDisplay.mercatorTransform.plotToData(bb.y), left),
new Microsoft.Maps.Location(InteractiveDataDisplay.mercatorTransform.plotToData(bb.y), middle),
new Microsoft.Maps.Location(InteractiveDataDisplay.mercatorTransform.plotToData(bb.y + bb.height), middle),
new Microsoft.Maps.Location(InteractiveDataDisplay.mercatorTransform.plotToData(bb.y + bb.height), left),
]);
polygon2.setLocations([
new Microsoft.Maps.Location(InteractiveDataDisplay.mercatorTransform.plotToData(bb.y), middle),
new Microsoft.Maps.Location(InteractiveDataDisplay.mercatorTransform.plotToData(bb.y), right),
new Microsoft.Maps.Location(InteractiveDataDisplay.mercatorTransform.plotToData(bb.y + bb.height), right),
new Microsoft.Maps.Location(InteractiveDataDisplay.mercatorTransform.plotToData(bb.y + bb.height), middle),
]);
}
drawBasic = !master.isInAnimation;
polygon.setOptions({ visible: master.isInAnimation });
polygon2.setOptions({ visible: master.isInAnimation });
}
if (drawBasic) {
var visibleRect_s = {
left: Math.floor(plotToScreenX(visibleRect.x)),
width: Math.ceil(ct.plotToScreenWidth(visibleRect.width)),
top: Math.floor(plotToScreenY(visibleRect.y + visibleRect.height)),
height: Math.ceil(ct.plotToScreenHeight(visibleRect.height))
};
var scale = ct.getScale();
var offset = ct.getOffset();
// rendering a placeholder to indicate that here will be real heatmap
context.fillStyle = 'rgba(200,200,200,0.3)';
context.fillRect(visibleRect_s.left, visibleRect_s.top, visibleRect_s.width, visibleRect_s.height);
if (lastCompletedTask) {
var taskRect = InteractiveDataDisplay.Utils.intersect(lastCompletedTask.plotRect, plotRect);
// todo: draw bb here
if (taskRect) {
var left_s = plotToScreenX(lastCompletedTask.plotRect.x);
var top_s = plotToScreenY(lastCompletedTask.plotRect.y + lastCompletedTask.plotRect.height);
var alpha;
if (_opacity != 1) {
alpha = context.globalAlpha;
context.globalAlpha = _opacity;
}
if (scale.x != lastCompletedTask.scaleX || scale.y != lastCompletedTask.scaleY) {
var sx = scale.x / lastCompletedTask.scaleX;
var sy = scale.y / lastCompletedTask.scaleY;
context.drawImage(_innerCanvas, 0, 0, lastCompletedTask.image.width, lastCompletedTask.image.height,
left_s, top_s, sx * lastCompletedTask.image.width, sy * lastCompletedTask.image.height);
} else {
context.drawImage(_innerCanvas, left_s, top_s);
}
if (_opacity != 1) {
context.globalAlpha = alpha;
}
}
}
if (_dataChanged ||
!this.master.isInAnimation &&
(!lastCompletedTask || lastCompletedTask.scaleX != scale.x || lastCompletedTask.scaleY != scale.y || !InteractiveDataDisplay.Utils.includes(lastCompletedTask.plotRect, visibleRect))) {
if (!_imageData || _imageData.width !== visibleRect_s.width || _imageData.height !== visibleRect_s.height) {
// avoiding creating new image data,
// it is possible to reuse the image data since web worker marshalling makes a copy of it
_imageData = context.createImageData(visibleRect_s.width, visibleRect_s.height);
}
var task = {
image: _imageData,
width: _imageData.width,
height: _imageData.height,
x: _x,
y: _y,
f: _f,
fmin: _fmin,
fmax: _fmax,
plotRect: visibleRect,
scaleX: scale.x,
scaleY: scale.y,
offsetX: offset.x - visibleRect_s.left,
offsetY: offset.y - visibleRect_s.top,
palette: {
isNormalized: _palette.isNormalized,
range: _palette.range,
points: _palette.points,
colors: _paletteColors
},
xDataTransform: this.xDataTransform && this.xDataTransform.type,
yDataTransform: this.yDataTransform && this.yDataTransform.type
};
//console.log("Heatmap " + this.name + " enqueues a task (isInAnimation: " + this.master.isInAnimation + ")");
InteractiveDataDisplay.heatmapBackgroundRenderer.enqueue(task, this);
_dataChanged = false;
}
//}
}
};
this.onIsRenderedChanged = function () {
if (!this.isRendered) {
InteractiveDataDisplay.heatmapBackgroundRenderer.cancelPending(this);
}
};
// Others
this.onDataTransformChanged = function (arg) {
this.invalidateLocalBounds();
InteractiveDataDisplay.Heatmap.prototype.onDataTransformChanged.call(this, arg);
};
var getCellContaining = function (x_d, y_d) {
var n = _x.length;
var m = _y.length;
if (n == 0 || m == 0) return;
if (x_d < _x[0] || y_d < _y[0] ||
x_d > _x[n - 1] || y_d > _y[m - 1]) return;
var i;
for (i = 1; i < n; i++) {
if (x_d <= _x[i]) {
if (isNaN(_x[i - 1])) return NaN;
break;
}
}
var j;
for (j = 1; j < m; j++) {
if (y_d <= _y[j]) {
if (isNaN(_y[j - 1])) return NaN;
break;
}
}
if (i >= n || j >= m) return NaN;
return { iLeft: i - 1, jBottom: j - 1 };
};
/// Gets the value (probably, interpolated) for the heatmap
/// in the point (xd,yd) in data coordinates.
/// Depends on the heatmap mode.
/// Returns null, if the point is outside of the plot.
this.getValue = function (xd, yd) {
var n = _x.length;
var m = _y.length;
if (n == 0 || m == 0) return null;
var cell = getCellContaining(xd, yd);
if (cell == undefined) return null;
if (cell != cell) return "
" + (this.name || "heatmap") + ": (unknown value)
";
var value;
if (_mode === "gradient") {
var flb, flt, frt, frb;
flt = _f[cell.iLeft][cell.jBottom + 1];
flb = _f[cell.iLeft][cell.jBottom];
frt = _f[cell.iLeft + 1][cell.jBottom + 1];
frb = _f[cell.iLeft + 1][cell.jBottom];
if (isNaN(flt) || isNaN(flb) || isNaN(frt) || isNaN(frb)) {
value = NaN;
} else {
var y0 = _y[cell.jBottom];
var y1 = _y[cell.jBottom + 1];
var kyLeft = (flt - flb) / (y1 - y0);
var kyRight = (frt - frb) / (y1 - y0);
var fleft = kyLeft * (yd - y0) + flb;
var fright = kyRight * (yd - y0) + frb;
var x0 = _x[cell.iLeft];
var x1 = _x[cell.iLeft + 1];
var kx = (fright - fleft) / (x1 - x0);
value = kx * (xd - x0) + fleft;
}
} else {
value = _f[cell.iLeft][cell.jBottom];
}
return value;
};
this.getTooltip = function (xd, yd) {
if (_f === undefined)
return;
var value = this.getValue(xd, yd);
if (value == null) return;
return "
" + (this.name || "heatmap") +
": " + value + "
";
};
Object.defineProperty(this, "palette", {
get: function () { return _palette; },
set: function (value) {
if (value == _palette) return;
if (!value) throw "Heatmap palette is undefined";
if (_palette && value.isNormalized && !_palette.isNormalized && _f) {
findFminmax();
}
loadPalette(value);
lastCompletedTask = undefined;
this.fireAppearanceChanged("palette");
this.requestNextFrame();
},
configurable: false
});
Object.defineProperty(this, "opacity", {
get: function () { return _opacity; },
set: function (value) {
if (!value) throw "Heatmap opacity is undefined";
if (value == _opacity) return;
loadOpacity(value);
this.fireAppearanceChanged("opacity");
this.requestNextFrame();
},
configurable: false
});
Object.defineProperty(this, "mode", {
get: function () { return _mode; },
configurable: false
});
this.getLegend = function () {
var div = $("
");
var that = this;
var nameDiv = $("
").appendTo(div);
var setName = function () {
nameDiv.text(that.name);
}
setName();
var paletteDiv = $("
").appendTo(div);
var paletteControl = new InteractiveDataDisplay.ColorPaletteViewer(paletteDiv, _palette);
if (_palette && _palette.isNormalized) {
paletteControl.dataRange = { min: _fmin, max: _fmax };
}
this.host.bind("appearanceChanged",
function (event, propertyName) {
if (!propertyName || propertyName == "name")
setName();
if (!propertyName || propertyName == "palette") paletteControl.palette = _palette;
var oldRange = paletteControl.dataRange;
if (_palette && _palette.isNormalized && (oldRange == undefined || oldRange.min != _fmin || oldRange.max != _fmax)) {
paletteControl.dataRange = { min: _fmin, max: _fmax };
}
});
var onLegendRemove = function () {
that.host.unbind("appearanceChanged");
div[0].innerHTML = "";
div.removeClass("idd-legend-item");
};
return { div: div, onLegendRemove: onLegendRemove };
};
// Initialization
if (initialData && typeof initialData.f != 'undefined')
this.draw(initialData);
};
InteractiveDataDisplay.Heatmap.prototype = new InteractiveDataDisplay.CanvasPlot();
InteractiveDataDisplay.register("heatmap", function (jqDiv, master) {
return new InteractiveDataDisplay.Heatmap(jqDiv, master);
});
;InteractiveDataDisplay.BingMaps = InteractiveDataDisplay.BingMaps || {};
InteractiveDataDisplay.BingMaps.ESRI = InteractiveDataDisplay.BingMaps.ESRI || {};
InteractiveDataDisplay.BingMaps.ESRI.GetWorldTopo = function () {
function getTilePath(tile) {
return "http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/" + tile.levelOfDetail + "/" + tile.y + "/" + tile.x;
}
return new Microsoft.Maps.TileSource({ uriConstructor: getTilePath });
};
InteractiveDataDisplay.BingMaps.ESRI.GetDeLorme = function () { // DeLorme World Basemap
function getTilePath(tile) {
return "http://server.arcgisonline.com/ArcGIS/rest/services/Specialty/DeLorme_World_Base_Map/MapServer/tile/" + tile.levelOfDetail + "/" + tile.y + "/" + tile.x;
}
return new Microsoft.Maps.TileSource({ uriConstructor: getTilePath });
};
InteractiveDataDisplay.BingMaps.ESRI.GetWorldImagery = function () { // ESRI World Imagery
function getTilePath(tile) {
return "http://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/" + tile.levelOfDetail + "/" + tile.y + "/" + tile.x;
}
return new Microsoft.Maps.TileSource({ uriConstructor: getTilePath });
};
InteractiveDataDisplay.BingMaps.ESRI.GetOceanBasemap = function () { // Ocean Basemap
function getTilePath(tile) {
return "http://services.arcgisonline.com/ArcGIS/rest/services/Ocean_Basemap/MapServer/tile/" + tile.levelOfDetail + "/" + tile.y + "/" + tile.x;
}
return new Microsoft.Maps.TileSource({ uriConstructor: getTilePath });
};
InteractiveDataDisplay.BingMaps.ESRI.GetNationalGeographicMap = function () { // National Geographic World Map
function getTilePath(tile) {
return "http://services.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer/tile/" + tile.levelOfDetail + "/" + tile.y + "/" + tile.x;
}
return new Microsoft.Maps.TileSource({ uriConstructor: getTilePath });
};
InteractiveDataDisplay.BingMaps.ESRI.GetWorldShadedRelief = function () { // World Shaded Relief
function getTilePath(tile) {
return "http://services.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/" + tile.levelOfDetail + "/" + tile.y + "/" + tile.x;
}
return new Microsoft.Maps.TileSource({ uriConstructor: getTilePath });
};
InteractiveDataDisplay.BingMaps.ESRI.GetWorldTerrainBase = function () { // World Terrain Base
function getTilePath(tile) {
return "http://services.arcgisonline.com/ArcGIS/rest/services/World_Terrain_Base/MapServer/tile/" + tile.levelOfDetail + "/" + tile.y + "/" + tile.x;
}
return new Microsoft.Maps.TileSource({ uriConstructor: getTilePath });
};
InteractiveDataDisplay.BingMaps.OpenStreetMap = InteractiveDataDisplay.BingMaps.OpenStreet || {};
InteractiveDataDisplay.BingMaps.OpenStreetMap.GetTileSource = function () {
function getTilePath(tile) {
return "http://tile.openstreetmap.org/" + tile.levelOfDetail + "/" + tile.x + "/" + tile.y + ".png";
}
return new Microsoft.Maps.TileSource({ uriConstructor: getTilePath });
};
InteractiveDataDisplay.BingMapsPlot = function (div, master) {
if (!div) return;
var mapDiv = $('
').prependTo(div);
this.base = InteractiveDataDisplay.Plot;
this.base(div, master);
var that = this;
if (typeof Microsoft === 'undefined') {
//BingMaps script wasn't loaded
$("
").css("margin", 15).css("word-wrap", "break-word").text("BingMaps script is unavailable. Check your internet connection.").appendTo(div);
} else {
var navDiv = undefined;
var navCanvas = undefined;
if (that.children.length === 0) {
navDiv = $('
').appendTo(div);
navDiv.css("z-index", InteractiveDataDisplay.ZIndexNavigationLayer);
navCanvas = $('
').appendTo(navDiv);
}
var maxLat = 85.05112878;
this.mapKey = div.attr("data-idd-mapKey");
var _map = new Microsoft.Maps.Map(mapDiv[0], {
credentials: that.mapKey,
mapTypeId: Microsoft.Maps.MapTypeId.aerial,
enableClickableLogo: false,
enableSearchLogo: false,
showCopyright: false,
showDashboard: false,
showLogo: false,
disablePanning: true,
disableZooming: true,
width: div.width(),
height: div.height()
});
Object.defineProperty(this, "map", {
get: function () { return _map; },
configurable: false
});
var bingMapsAnimation = new InteractiveDataDisplay.BingMapsAnimation(_map);
this.arrange = function (finalRect) {
InteractiveDataDisplay.BingMapsPlot.prototype.arrange.call(this, finalRect);
_map.width = finalRect.width;
_map.height = finalRect.height;
};
// Sets the map provided as an argument which is either a tile source (Microsoft.Maps.TileSource, e.g. see InteractiveDataDisplay.BingMaps.OpenStreetMap.GetTileSource),
// or a map type of Bing Maps (Microsoft.Maps.MapTypeId).
this.setMap = function (map) {
_map.setMapType(Microsoft.Maps.MapTypeId.mercator);
_map.entities.clear();
if (!map) return;
if (map instanceof Microsoft.Maps.TileSource) {
// Construct the layer using the tile source
var tilelayer = new Microsoft.Maps.TileLayer({ mercator: map, opacity: 1 });
_map.entities.push(tilelayer);
} else {
_map.setMapType(map);
}
};
this.constraint = function (plotRect, screenSize) {
var mapWidth = _map.getWidth();
var mapHeight = _map.getHeight();
if (mapWidth <= 1 || mapHeight <= 1)
return plotRect;
bingMapsAnimation.setMapView(plotRect, screenSize);
mapRect = InteractiveDataDisplay.Utils.getPlotRectForMap(_map);
return mapRect;
}
this.arrange = function (finalRect) {
InteractiveDataDisplay.CanvasPlot.prototype.arrange.call(this, finalRect);
if (navDiv !== undefined) {
navDiv.width(finalRect.width);
navDiv.height(finalRect.height);
navCanvas[0].width = finalRect.width;
navCanvas[0].height = finalRect.height;
}
}
bingMapsAnimation.constraint = this.constraint;
that.navigation.animation = bingMapsAnimation;
this.selfMapRefresh();
}
}
InteractiveDataDisplay.BingMapsPlot.prototype = new InteractiveDataDisplay.Plot;;}
(function () {
if (window.define) {
define(['jquery', 'rx'],
function ($, Rx) {
IDD($, Rx);
return InteractiveDataDisplay;
});
} else {
IDD($, Rx);
window.InteractiveDataDisplay = InteractiveDataDisplay;
}
}());