I can create a HeatMap using d3.js, dc.js and crossfilter, using data in a CSV file.
code:
var chart = dc.heatMap("#test");
d3.csv("morley.csv", function(error, experiments) {
var ndx = crossfilter(experiments),
runDim = ndx.dimension(function(d) { return [+d.Run, +d.Expt]; }),
runGroup = runDim.group().reduceSum(function(d) { return +d.Speed; });
chart
.width(45 * 20 + 80)
.height(45 * 5 + 40)
.dimension(runDim)
.group(runGroup)
.keyAccessor(function(d) { return +d.key[0]; })
.valueAccessor(function(d) { return +d.key[1]; })
.colorAccessor(function(d) { return +d.value; })
.title(function(d) {
return "Run: " + d.key[0] + "\n" +
"Expt: " + d.key[1] + "\n" +
"Speed: " + (299000 + d.value) + " km/s";})
.colors(["#ffffd9","#edf8b1","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#253494","#081d58"])
.calculateColorDomain();
chart.render();
});
but i want to do this same thing using JSON data, perhaps an array with some json data. This seems like a noob question but I was unable to find any example which uses JSON data for heatmap.
If you're using a JSON file then the code is going to be largely the same just replace d3.csv with d3.json.
If the data is already loaded in the browser then you can remove this function and just run:
var experiments = JSON.parse(json_object) //if json_object is still a string
var ndx = crossfilter(experiments)
and the rest of the code will be the same.
Related
I'm using cross filter, dc.js and leaflet to display data on an interactive dashboard.
This is my code for grouping and showing me the data table which is working fine.
.group(function (d) {
var format = d3.format('02d');
return d.Timestamp.getFullYear() + '/' + format((d.Timestamp.getMonth() + 1));
})
.columns([
"Timestamp",
"KMIST_TEMP_ID",
"ID POP",
"CUSTOMER NAME",
"district",
"ward",
]);
I want to return on a map with leaflet my geographic coordinates which are in the data table.
pharmaciesMarkers.clearLayers();
_.each(format.top(Infinity), function (d) {
var name = d.CUSTOMER NAME;
var marker = L.marker([loc.lat, loc.long]);
pharmaciesMarkers.addLayer(marker);
marker.bindPopup("<p>" + CUSTOMER NAME + " " + loc.KMIST_TEMP_ID + " " + loc.ward + "</p>");
});
map.addLayer(pharmaciesMarkers);
map.fitBounds(pharmaciesMarkers.getBounds())
What am I doing wrong here, why the markers do not display on the map?
I am drawing a scatter chart with NVD3 library in React when a component is first mounted. I hide and show the component depending on the buttons clicked. Each time the component appears, it is mounted, so it is re-drawn but I want to avoid the redrawing, doing some kind of caching of the chart since it takes quite a bit of time to draw the chart with many datapoints. I am calling createScatterChart in componentDidMount:
createScatterChart() {
const node = this.node
nv.utils.symbolMap.set('thin-x', function(size) {
size = Math.sqrt(size);
return 'M' + (-size/2) + ',' + (-size/2) +
'l' + size + ',' + size +
'm0,' + -(size) +
'l' + (-size) + ',' + size;
});
// create the chart
var chart;
nv.addGraph(function() {
chart = nv.models.scatterChart()
.showDistX(true)
.showDistY(true)
.useVoronoi(true)
.color(d3.scale.category10().range())
.duration(300)
;
var data_func = () => this.props.datum;
var data_obj = data_func();
var that = this;
chart.tooltip.contentGenerator(function (d) {
//var html = "<div>";
var html = "";
d.series.forEach(function(elem){
Object.keys(data_obj).forEach(function(key_1) {
var outer_obj = data_obj[key_1];
if (outer_obj["key"] === elem.key) {
that.showBarChart(elem.key);
html += "<p>cluster " + elem.key + "</p>";
/*var expr = outer_obj["values"][0]["expr"];
html += "<p>" + elem.key + "</p>";
html += "<p>x = " + d.value + ", y = " + elem.value + "</p>";*/
}
});
})
//html += "</div>";
return html;
});
chart.dispatch.on('renderEnd', function(){
console.log('render complete');
});
chart.xAxis.tickFormat(d3.format('.02f'));
chart.yAxis.tickFormat(d3.format('.02f'));
d3.select(node)
.datum(data_func)
.call(chart);
nv.utils.windowResize(chart.update);
chart.dispatch.on('stateChange', function(e) { ('New State:', JSON.stringify(e)); });
return chart;
}.bind(this));
}
The function ultimately returns chart, so could I somehow save it in a variable and then draw it much faster? Or what would you recommend for such caching of the chart?
The problem can be resolved by changing visible attribute of the html element instead of rendering it.
<div visibility={this.state.showButton ? "visible": "hidden"} ></div>
So I have a page where multiple containers are dynamically added and filled with html, some of which are populated with data pulled via ajax from a json file. Every 5 minutes the page runs a function that gets every container (marked with class) and for each of them works out its id/index (probably not very efficiently) and does the ajax post.etc.
But the ajax call resulting data is the same essentially for every instance (no limit but on average there would be ~30 ajax calls of the same data for one whole page), it grabs it, decodes it, sorts it, updates html and that is it really.
This feels clunky and im sure will cause issues down the line, is there anything I can do to prevent these 30+ ajax posts without disabling it being 'Asynchronous'/lessen the impact of it?
setInterval(function() {
$('.fill').each(function() {
var selectId = this.id;
var selectIdNum = selectId.replace(/\D/g, '');
selectedId = 'selectedcoin' + selectIdNum;
var index = document.getElementById(selectedId).selectedIndex;
$.ajax({
url: 'array-to-json.php',
type: 'POST',
dataType: 'json',
success: function(data) {
data.sort(function(a, b) {
return (a.id > b.id) ? 1 : ((b.id > a.id) ? -1 : 0);
});
result = data;
var php1 = [result[index].name, result[index].symbol, result[index].price_btc, result[index].percent_change_24h, result[index].price_usd, result[index].id, result[index]['24h_volume_usd']];
var myCoinCost = parseFloat($('#buyprice' + selectIdNum).val());
var myPercCoin = (parseFloat(php1[2]).toPrecision(20) - myCoinCost) / myCoinCost * 100;
var myCoinTotal = parseFloat($('#coins' + selectIdNum).val());
var myUsdCoin = myCoinTotal * parseFloat(php1[4]).toPrecision(20);
$("#price" + selectIdNum).html(php1[2]);
$("#pricePercent" + selectIdNum).html(php1[3]);
$("#priceUsd" + selectIdNum).html(php1[4] + "</span>");
$("#volDay" + selectIdNum).html("$" + php1[6] + "</span>");
$("#myPercent" + selectIdNum).html(myPercCoin.toFixed(2) + "%");
$("#myEarnings" + selectIdNum).html(myUsdCoin.toFixed(2));
},
error: function() {
alert("error");
}
});
});
}, 300 * 1000);
It seems like your call returns all the data for all the containers already. You don't pass any specific ID into it, and you are filtering the results when you get them, so I will make that assumption.
In that case, all you need to do is move your .each loop inside the ajax success function. That way the ajax runs once, and you just loop through the data when it's received to apply it to the HTML.
I think this should do it:
setInterval(function() {
$.ajax({
url: 'array-to-json.php',
type: 'POST',
dataType: 'json',
success: function(data) {
data.sort(function(a, b) {
return (a.id > b.id) ? 1 : ((b.id > a.id) ? -1 : 0);
}); //is this really necessary? The server-side could probably sort it more efficiently, esp if it's the result of the SQL query.
result = data;
$('.fill').each(function() {
var selectId = this.id;
var selectIdNum = selectId.replace(/\D/g, '');
selectedId = 'selectedcoin' + selectIdNum;
var index = document.getElementById(selectedId).selectedIndex;
var php1 = [
result[index].name, result[index].symbol,
result[index].price_btc, result[index].percent_change_24h,
result[index].price_usd, result[index].id,
result[index]['24h_volume_usd']
];
var myCoinCost = parseFloat($('#buyprice' + selectIdNum).val());
var myPercCoin = (parseFloat(php1[2]).toPrecision(20) - myCoinCost) / myCoinCost * 100;
var myCoinTotal = parseFloat($('#coins' + selectIdNum).val());
var myUsdCoin = myCoinTotal * parseFloat(php1[4]).toPrecision(20);
$("#price" + selectIdNum).html(php1[2]);
$("#pricePercent" + selectIdNum).html(php1[3]);
$("#priceUsd" + selectIdNum).html(php1[4] + "</span>");
$("#volDay" + selectIdNum).html("$" + php1[6] + "</span>");
$("#myPercent" + selectIdNum).html(myPercCoin.toFixed(2) + "%");
$("#myEarnings" + selectIdNum).html(myUsdCoin.toFixed(2));
});
},
error: function(jqXHR) {
alert("Error while fetching data");
console.log("Error while fetching data: " + jqXHR.status + " " + jqXHR.statusText + " " + jqXHR.responseText); //improved error logging
}
});
}, 300 * 1000);
I am attempting to load some files asynchronously using d3's queue, defer and await. The problem is trying to do so in a loop, and for each item in the loop, store the fetched data in the dictionary:
var myDictionary = {};
// e.g. hierarchy = ["State", "County"]
hierarchy.forEach(function(geo) {
queue()
.defer(d3.json, 'assets/data/geo/' + geo + '/' + geo + '.json')
.defer(d3.csv, 'assets/data/geo/' + geo + '/' + geo + '_info.csv')
.await(myFunc);
});
function myFunc(error, jsonData, csvData) {
// need access to geo
console.log(geo);
myDictionary[geo].jsonData = jsonData;
myDictionary[geo].csvData = csvData;
}
in myFunc, I would like access to geo to fill in the appropriate keys in the dictionary, however passing them inside .await.myFunc(geo) would print undefined inside myFunc.
I don't know whether this problem pertains to Javascript's callback functions, or D3's await(), or both.
Any recommendations?
Thanks
You can use this:
function myFunc(geo) {
return function(error, jsonData, csvData) {
// geo available here
console.log(geo);
myDictionary[geo].jsonData = jsonData;
myDictionary[geo].csvData = csvData;
};
};
Then:
var myDictionary = {};
// e.g. hierarchy = ["State", "County"]
hierarchy.forEach(function(geo) {
queue()
.defer(d3.json, 'assets/data/geo/' + geo + '/' + geo + '.json')
.defer(d3.csv, 'assets/data/geo/' + geo + '/' + geo + '_info.csv')
.await(myFunc(geo));
});
You could add it as a task so it gets passed into your myFunc.
queue()
.defer(d3.json, 'assets/data/geo/' + geo + '/' + geo + '.json')
.defer(d3.csv, 'assets/data/geo/' + geo + '/' + geo + '_info.csv')
.defer(callback=>callback(geo))
.await(myFunc);
...
function myFunc(error, jsonData, csvData, geo) {
console.log(geo);
}
This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 7 years ago.
I'm new to using JSON and Javascript, but I am trying to sort various values of the JSON file provided by Reddit. In the console, I do see the array and the JSON values from the console.log(posts) print. However, the console.log(posts.length) statement returns 0 and nothing is displayed to the screen, which I suspect is due to how I am storing and/or retrieving the JSON values in the array.
var minVotes = 5;
var subreddit = "askreddit";
var posts = [];
//Retrieve JSON from Reddit using JQuery
$.getJSON("https://www.reddit.com/r/" + subreddit + "/rising.json?limit=50", function foo(result) {
$.each(result.data.children.slice(0, 50), function(i, post) {
if (post.data.ups > minVotes) {
//Push JSON data to array to be sorted later
posts.push(post.data);
}
})
})
//Sort the array
posts.sort(function(a, b) {
return parseInt(a.data.ups - a.data.num_comments) - parseInt(b.data.ups - b.data.num_comments);
});
console.log(posts);
console.log(posts.length); //returns 0 ???
//Display the content, which doesn't work
for (var i = 0; i < posts.length; i++) {
$("#reddit-content").append('<br>' + "Title: " + posts[i].title);
$("#reddit-content").append('<br>' + "Url: " + posts[i].url);
$("#reddit-content").append('<br>' + "Upvotes: " + posts[i].ups);
$("#reddit-content").append('<br>' + "Comments: " + posts[i].num_comments);
$("#reddit-content").append('<hr>');
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="reddit-content"></div>
It's because of the async nature of $.getJSON. If you sort the array inside the response handler it works fine.
You also had another issue in your sort with accessing the property values. I don't believe you need to parseInt either as JSON.parse will return those values as numbers already.
var minVotes = 5;
var subreddit = "askreddit";
//Retrieve JSON from Reddit using JQuery
$.getJSON("https://www.reddit.com/r/" + subreddit + "/rising.json?limit=50", function foo(result) {
var posts = [];
$.each(result.data.children.slice(0, 50), function(i, post) {
if (post.data.ups > minVotes) {
//Push JSON data to array to be sorted later
posts.push(post.data);
}
});
//Sort the array
posts.sort(function(a, b) {
return parseInt(a.ups - a.num_comments) - parseInt(b.ups - b.num_comments);
});
console.log(posts);
console.log(posts.length);
//Display the content, which doesn't work
for (var i = 0; i < posts.length; i++) {
$("#reddit-content").append('<br>' + "Title: " + posts[i].title);
$("#reddit-content").append('<br>' + "Url: " + posts[i].url);
$("#reddit-content").append('<br>' + "Upvotes: " + posts[i].ups);
$("#reddit-content").append('<br>' + "Comments: " + posts[i].num_comments);
$("#reddit-content").append('<hr>');
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="reddit-content"></div>