How can I render heatmap on svg image? - javascript

Currently I am working on a web application using Angularjs. I need to show heat-map on a svg in order to show some analytics. Can anyone suggest me a JavaScript library?

You can create a custom directive for the same. I have used an angularjs minified file...
(URL: https://www.patrick-wied.at/static/heatmapjs/assets/js/heatmap.min.js)
I could not find anything more relevant on github or anywhere else! If you find something, please update the same here.
Anyway, I tried to create a small heatmap using this external file, hope this helps!
var myApp = angular.module('myapp', []);
myApp
.controller('HeatMapCtrl', function($scope) {
//random data
var points = [];
var max = 0;
var width = 840;
var height = 400;
var len = 200;
while (len--) {
var val = Math.floor(Math.random() * 100);
max = Math.max(max, val);
var point = {
x: Math.floor(Math.random() * width),
y: Math.floor(Math.random() * height),
value: val
};
points.push(point);
}
// heatmap data format
$scope.heat_data = {
max: max,
data: points
};
})
.directive('heatMap', function() {
return {
restrict: 'E',
scope: {
data: '='
},
template: '<div container></div>',
link: function(scope, ele, attr) {
scope.heatmapInstance = h337.create({
container: ele.find('div')[0]
});
scope.heatmapInstance.setData(scope.data);
}
};
});
heat-map {
width: 600px;
height: 300px;
display: block;
}
heat-map div {
height: 100%;
}
<script src="https://www.patrick-wied.at/static/heatmapjs/assets/js/heatmap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.1/angular.min.js"></script>
<div ng-app="myapp">
<div ng-controller="HeatMapCtrl">
<heat-map data="heat_data"></heat-map>
</div>
</div>

Related

Getting polyfill "vminpoly" working with internal CSS

On Saabi's github for vminpoly, a vw and vh unit polyfill (https://github.com/saabi/vminpoly), it says:
Only linked stylesheets are being parsed right now but it's very easy
to also parse 'style' elements.
How would vw and vh CSS inside style elements work with vminpoly?
You could refer to the code below to support vh, vw in particular:
(function($, window) {
var $win = $(window),
_css = $.fn.css;
function viewportToPixel(val) {
var percent = val.match(/\d+/)[0] / 100,
unit = val.match(/[vwh]+/)[0];
return (unit == 'vh' ? $win.height() : $win.width()) * percent + 'px';
}
function parseProps(props) {
var p, prop;
for (p in props) {
prop = props[p];
if (/[vwh]$/.test(prop)) {
props[p] = viewportToPixel(prop);
}
}
return props;
}
$.fn.css = function(props) {
var self = this,
update = function() {
return _css.call(self, parseProps($.extend({}, props)));
};
$win.resize(update);
return update();
};
}(jQuery, window));
//Usage:
$('div').css({
height: '50vh',
width: '50vw',
marginTop: '25vh',
marginLeft: '25vw',
fontSize: '10vw'
});
body {
margin: 0;
}
div {
background: #fa7098;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>hello</div>
The result in IE is like this:

Responsive, cover-fit image tagging

I am working on a project where I have a slideshow with images as follows:
img {
width:100vw;
height:100vh;
object-fit:cover;
}
This makes the images fullscreen and behave like background-size:cover, so they fill out the whole viewport on any screen size without distortion.
I would like to tag certain points with text tooltips on these images. For this purpose I have found Tim Severien's Taggd, which works great on responsive images, but in my case the object-fit:cover; property makes the tagged positions inaccurate.
I have tried everything from CSS hacks to improving Tim's code, but I am out of ideas. If you have any solution or workaround in mind please share.
Thank you!
well i actually wanted to do the same thing.
here is what i've done.
maybe it will help someone in the future.
it would be great if this feature could be integrated in taggd.
function buildTags()
{
// be aware that image.clientWidth and image.clientHeight are available when image is loaded
var croppedWidth = false;
var expectedWidth = 0;
var croppedWidthHalf = 0;
var imageWidth = 0;
var croppedHeight = false;
var expectedHeight = 0;
var croppedHeightHalf = 0;
var imageHeight = 0;
var naturalRatio = image.naturalWidth/image.naturalHeight;
var coverRatio = image.clientWidth/image.clientHeight;
if(Math.abs(naturalRatio - coverRatio) < 0.01)
{
// the image is not cropped, nothing to do
}
else
{
if(naturalRatio > coverRatio)
{
// width is cropped
croppedWidth = true;
expectedWidth = image.clientHeight * naturalRatio;
croppedWidthHalf = (expectedWidth - image.clientWidth)/2;
imageWidth = image.clientWidth;
}
else
{
// height is cropped
croppedHeight = true;
expectedHeight = image.clientWidth / naturalRatio;
croppedHeightHalf = (expectedHeight - image.clientHeight)/2;
imageHeight = image.clientHeight;
}
}
function calcy(y)
{
if(croppedHeight)
{
var positiony = y * expectedHeight;
if(positiony > croppedHeightHalf)
return (positiony - croppedHeightHalf)/imageHeight;
else // tag is outside the picture because cropped
return 0; // TODO : handle that case nicely
}
else
return y;
}
function calcx(x)
{
if(croppedWidth)
{
var positionx = x * expectedWidth;
if(positionx > croppedWidthHalf)
return (positionx - croppedWidthHalf)/imageWidth;
else // tag is outside the picture because cropped
return 0; // TODO : handle that case nicely
}
else
return x;
}
var tags = [
Taggd.Tag.createFromObject({
position: { x: calcx(0.74), y: calcy(0.56) },
text: 'some tag',
}),
Taggd.Tag.createFromObject({
position: { x: calcx(0.9), y: calcy(0.29) },
text: 'some other tag',
}),
....
];
var taggd = new Taggd(image, options, tags);
}
$(window).bind("load", function() {buildTags();});
Is not possible. Think if the user has a tablet with 1024x768 resolution, when the user change view from horizontal to vertical the image can fill the space but you will loose part of the image, loose img quality, etc.
The best way for cross devices is to use big pictures and add in css
img {
height: auto;
width: 100%;
display: block;
}
And fill image background with a color;

jquery plugin, settimeout and div not being append, simple weird case

I'm writing a jquery plugin the code below is not working (I mean the setTimeout is working but nothing is append)
var self = this;
for (var i=0; i<=10; i++) {
setTimeout(function() {
self.append(bubble);
}, 1000);
}
And the code below is working:
for (var i=0; i<=10; i++) {
this.append(bubble);
}
this is a jquery selection. I really don't get what's going on. It can't be scope issue .. can it be ? I don't get it. Thanks in advance for you help
Edit: bubble is a simple div (" ")
Below the whole plugin code:
(function($) {
'use strict';
$.fn.randomBubble = function(options) {
var self = this;
var settings = $.extend({
color: 'blue',
backgroundColor: 'white',
maxBubbleSize: 100
}, options);
var frame = {
height: this.height(),
width: this.width(),
}
var bubble = "<div class='randomBubble'> </div>";
this.getLeft = function(width) {
var left = Math.random() * frame.width;
if (left > (frame.width / 2)) {
left -= width;
} else {
left += width;
}
return left
}
this.getTop = function(height) {
var top = Math.random() * frame.height;
if (top > (frame.height / 2)) {
top -= height;
} else {
top += height;
}
return top
}
this.removeBubbles = function() {
var currentBubbles = this.find('.randomBubble');
if (currentBubbles.length) {
currentBubbles.remove();
}
}
window.oh = this;
for (var i = 0; i <= 10; i++) {
var timer = Math.random() * 1000;
setTimeout(function() {
window.uh = self;
self.append(bubble);
console.log("oh");
}, 1000);
}
this.randomize = function() {
//self.removeBubbles();
var allBubbles = this.find('.randomBubble');
allBubbles.each(function(i, el) {
var height = Math.random() * settings.maxBubbleSize;
var width = height;
$(el).css({
color: settings.color,
backgroundColor: settings.backgroundColor,
zIndex: 1000,
position: 'absolute',
borderRadius: '50%',
top: self.getTop(height),
left: self.getLeft(width),
height: height,
width: width
});
});
}
this.randomize();
//var run = setInterval(self.randomize, 4000);
return this.find('.randomBubble');
}
})(jQuery);
Because the bubbles are appended later due to the setTimeout(), this selector in your randomize() function comes up empty:
var allBubbles = this.find('.randomBubble');
That is why appending them in a simple for loop works fine.
If you really want to use the setTimout() to append your bubbles, one option is to style them when you add them:
setTimeout(function() {
var height = Math.random() * settings.maxBubbleSize;
var width = height;
var b = $(bubble).css({
color: settings.color,
backgroundColor: settings.backgroundColor,
zIndex: 1000,
position: 'absolute',
borderRadius: '50%',
top: self.getTop(height),
left: self.getLeft(width) ,
height: height,
width: width
});
self.append(b);
}, 1000);
Fiddle
Is it because you still call randomize() right away, even when you postpone the creation for one second?
You will also return an empty selection in that case, for the same reason.
Also, you probably want to use the timer variable in setTimeout() instead of hardcoding all to 1000 ms?
this is a javascript selection, the selector in jquery is $(this)
$.fn.randomBubble = function(options) {
var self = $(this);
};

how to insert data into nvd3 barchart

I have a nvd3 chart example, however i have no idea how to enter the data into the graph and make use of it.
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link href="build/nv.d3.css" rel="stylesheet" type="text/css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.2/d3.min.js" charset="utf-8"></script>
<script src="build/nv.d3.js"></script>
<script src="lib/stream_layers.js"></script>
<style>
text {
font: 12px sans-serif;
}
svg {
display: block;
}
html, body, #test1, svg {
margin: 0px;
padding: 0px;
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="test1">
<svg></svg>
</div>
<script>
//var test_data = stream_layers(3,128,.1).map(function(data, i) {
var test_data = stream_layers(3, 128, .1).map(function (data, i) {
return {
key: (i == 1) ? 'Non-stackable Stream' + i : 'Stream' + i,
nonStackable: (i == 1),
values: data
};
});
nv.addGraph({
generate: function () {
var width = nv.utils.windowSize().width,
height = nv.utils.windowSize().height;
var chart = nv.models.multiBarChart()
.width(width)
.height(height)
.stacked(true)
;
chart.dispatch.on('renderEnd', function () {
console.log('Render Complete');
});
var svg = d3.select('#test1 svg').datum(test_data);
console.log('calling chart');
svg.transition().duration(0).call(chart);
return chart;
},
callback: function (graph) {
nv.utils.windowResize(function () {
var width = nv.utils.windowSize().width;
var height = nv.utils.windowSize().height;
graph.width(width).height(height);
d3.select('#test1 svg')
.attr('width', width)
.attr('height', height)
.transition().duration(0)
.call(graph);
});
}
});
</script>
</body>
</html>
Java(Where the data is taken from)
/* Inspired by Lee Byron's test data generator. */
function stream_layers(n, m, o) {
if (arguments.length < 3) o = 0;
function bump(a) {
var x = 1 / (.1 + Math.random()),
y = 2 * Math.random() - .5,
z = 10 / (.1 + Math.random());
for (var i = 0; i < m; i++) {
var w = (i / m - y) * z;
a[i] += x * Math.exp(-w * w);
}
}
return d3.range(n).map(function() {
var a = [], i;
for (i = 0; i < m; i++) a[i] = o + o * Math.random();
for (i = 0; i < 5; i++) bump(a);
return a.map(stream_index);
});
}
/* Another layer generator using gamma distributions. */
function stream_waves(n, m) {
return d3.range(n).map(function(i) {
return d3.range(m).map(function(j) {
var x = 20 * j / m - i / 3;
return 2 * x * Math.exp(-.5 * x);
}).map(stream_index);
});
}
function stream_index(d, i) {
return {x: i, y: Math.max(0, d)};
}
So as seen above the data for the example is randomly generated.
If someone can give me a reference, or an example of how do i enter data into the grouped bar chart. It will really help me a lot.
What im trying to add in my data into is this
http://nvd3.org/livecode/index.html#codemirrorNav
The grouped bar chart example.
I really am new to this javascript coding, so all help is truly appreciated.
An example from the NVD3 website:
nv.addGraph(function() {
var chart = nv.models.multiBarChart()
.transitionDuration(350)
.reduceXTicks(true) //If 'false', every single x-axis tick label will be rendered.
.rotateLabels(0) //Angle to rotate x-axis labels.
.showControls(true) //Allow user to switch between 'Grouped' and 'Stacked' mode.
.groupSpacing(0.1) //Distance between each group of bars.
;
chart.xAxis
.tickFormat(d3.format(',f'));
chart.yAxis
.tickFormat(d3.format(',.1f'));
d3.select('#chart1 svg')
.datum(test_data)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
So, crucial here is to have element with id="chart1" and inside that element to have an empty svg element (I'm explaining the above setup, it can be different id's and directly only svg element)
The important part is that the data format must be in a specific format. So it should be a JSON object, something like this:
test_data = [
{
values: [{x,y},{x,y}],
key: 'some key',
color: 'some color'
},....
{
values: [{x,y},{x,y}],
key: 'some key',
color: 'some color'
}
];
In your case I see strange the generate and callback functions, which I see that it is a little bit mixed up.
Reference for the above example:
http://nvd3.org/examples/multiBar.html
And refer to the latest documentation and examples:
http://nvd3-community.github.io/nvd3/examples/documentation.html

Build dynamic rectangle with Angular directive

Recently I have written a project with D3, so I need a dynamic rectangle. I used Angular to create a dynamic visualization.I have two input type rang, the first one will change the 'width' of rectangle and the second will change the 'height'.However I don't know how to use angular to draw a dynamic rectangle.
This is my code:
<div ng-app="myApp">
<rect-designer/>
<div>
<input type="range" ng-model="rectWidth" min="0" max="400" value="0"/>
<input type="range" ng-model="rectHeight" min="0" max="700" value="0"/>
</div>
</div>
Here is my JavaScript code:
var App = angular.module('myApp', []);
App.directive('rectDesigner', function() {
function link(scope, el, attr) {
var svgwidth=1000, svgheight=600;
var svgContainer = d3.select(el[0]).append('svg').attr('id','svgcontainer')
.attr({ width: svgwidth, height: svgheight });
scope.$watchGroup(['rectWidth','rectHeight'], function () {
svgContainer.append("rect").attr("id", "Rect")
.attr({ width: rectWidth, height: rectHeigh })
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')')
},true);
}return {
link: link,
scope: {
rectHeigh: '=',
rectWidth: '=',
},
restrict: 'E'
};
});
I don't know if there is any way to make svgWidth and svgheight dynamic, I used this code but the result was undefined.
scope.$watch(function(){
svgWidth = el.clientWidth;
svgHeight = el.clientHeight;
});
You are missing some basics here:
You don't have a controller.
The variables you are watching are not part of your directive but they should be part of that missing controller.
Since these variables aren't part of the directive, there's no need to return them into it's scope (again, they will be in the controller).
$scope.watchGroup has a callback with a function of newValues. This is where the changed variables will be.
You want to append the rect to the svg and then manipulate it's width/height. You don't want to re-append it each time the width/height changes.
So putting all this together:
var App = angular.module('myApp', []);
var Ctrl = App.controller('myCtrl', ['$scope', function($scope) {
// I always like to give them defaults in code
$scope.rectWidth = 50;
$scope.rectHeight = 50;
}]);
Ctrl.directive('rectDesigner', function() {
function link(scope, el, attr) {
var svgwidth = 500,
svgheight = 600;
var svgContainer = d3.select(el[0])
.append('svg')
.attr('id', 'svgcontainer')
.attr({
width: svgwidth,
height: svgheight
});
// only append one rect
var rect = svgContainer
.append("rect")
.attr("id", "Rect")
.attr('transform', 'translate(' + svgwidth / 2 + ',' + svgheight / 2 + ')');
scope.$watchGroup(['rectWidth', 'rectHeight'], function(newValues) {
var width = newValues[0];
var height = newValues[1];
// now change it's width and height
rect.attr({
width: width,
height: height
});
}, true);
}
return {
link: link,
};
});
Example here.

Categories