With static data the word cloud works fine.
When the data change and I update html, the cloud doesn't update.
This is my code in HTML
<div id="chartdiv"></div>
<script type="text/javascript" src="../wordCloud.js"></script>
<script type="text/javascript">wordcloud(myData)</script>
This is How I initialize the chart
function wordcloud(myData) {
am4core.ready(function () {
// Themes begin
am4core.useTheme(am4themes_animated);
am4core.useTheme(am4themes_kelly);
// Themes end
var chart = am4core.create("chartdiv", am4plugins_wordCloud.WordCloud);
chart.fontFamily = "Courier New";
var series = chart.series.push(new am4plugins_wordCloud.WordCloudSeries());
series.randomness = 0.1;
series.rotationThreshold = 0.5;
series.angles = [0];
series.data = myData;
series.dataFields.word = "tag";
series.dataFields.value = "count";
series.heatRules.push({
"target": series.labels.template,
"property": "fill",
"min": am4core.color("#0000CC"),
"max": am4core.color("#CC00CC"),
"dataField": "value"
});
series.labels.template.tooltipText = "{word}: {value}";
var hoverState = series.labels.template.states.create("hover");
hoverState.properties.fill = am4core.color("#FF0000");
var title = chart.titles.create();
title.text = "Most frequent words in corpus";
title.fontSize = 20;
title.fontWeight = "800";
});
}
Users can use a button to have more or less words displayed in the tag cloud.
The new data is then calculated in the back end. But how can I update the cloud?
Thanks for any help.
Edit:
I read the documentation and also read this thread. But this is not helping me because the difference btw. word cloud and chart is that the data are added via series variable and not the chart variable.
The same update rules mentioned in the linked thread apply to series-level data - replacing the array, calling addData or updating in place with invalidateRawData (both of which have series-level methods) will enable you to update the WordCloud. Your code seems to have the same limitation as the previous thread's code where you don't have access to the chart variable outside of your method, so I'm not seeing how you would exactly update that instance without making similar changes.
Basic demo below using a button that sets a new data array on the series:
var myData = [{
'tag': 'Yes',
'count': 50
}, {
'tag': 'No',
'count': 50
}, {
'tag': 'Maybe',
'count': 50
}]
am4core.useTheme(am4themes_animated);
// Themes end
var chart = am4core.create("chartdiv", am4plugins_wordCloud.WordCloud);
chart.fontFamily = "Courier New";
var series = chart.series.push(new am4plugins_wordCloud.WordCloudSeries());
series.randomness = 0.1;
series.rotationThreshold = 0.5;
series.angles = [0];
series.data = myData;
series.dataFields.word = "tag";
series.dataFields.value = "count";
series.heatRules.push({
"target": series.labels.template,
"property": "fill",
"min": am4core.color("#0000CC"),
"max": am4core.color("#CC00CC"),
"dataField": "value"
});
series.labels.template.tooltipText = "{word}: {value}";
var hoverState = series.labels.template.states.create("hover");
hoverState.properties.fill = am4core.color("#FF0000");
var title = chart.titles.create();
title.text = "Most frequent words in corpus";
title.fontSize = 20;
title.fontWeight = "800";
document.getElementById('change').addEventListener('click', function() {
series.data = [{
'tag': 'Yes',
'count': 50
}, {
'tag': 'No',
'count': 50
}, {
'tag': 'Maybe',
'count': 50
}, {
'tag': 'Sorta',
'count': 50
}, {
'tag': 'Kinda',
'count': 50
}]
})
#chartdiv {
width: 100%;
height: 350px;
}
<script src="//www.amcharts.com/lib/4/core.js"></script>
<script src="//www.amcharts.com/lib/4/charts.js"></script>
<script src="//www.amcharts.com/lib/4/plugins/wordCloud.js"></script>
<script src="//www.amcharts.com/lib/4/themes/animated.js"></script>
<div id="chartdiv"></div>
<button id='change'>Change data</button>
Related
I am trying to get a bubble chart to populate using ngx-charts. The problem is the swimlane documentation on it is almost non-existent and I haven't been able to find any good examples.
I have read the open source code that swimlane provides and have created what I believe are the appropriate variables in Typescript and have constructed my data points using an example line graph I found using the same tools. However the chart still appears empty on the page.
HTML:
<ngx-charts-bubble-chart
[view]="view"
[results]="bubbleDemoTempData"
[showGridLines]="showGridLines"
[legend]="showLegend"
[legendTitle]="legendTitle"
[xAxis]="showXAxis"
[yAxis]="showYAxis"
[showXAxisLabel]="showXAxisLabel"
[showYAxisLabel]="showYAxisLabel"
[xAxisLabel]="xAxisLabel"
[yAxisLabel]="yAxisLabel"
[autoScale]="autoScale"
[scheme]="colorScheme"
[minRadius]="minRadius"
[maxRadius]="maxRadius"
(select)="onSelectBubbleInteractivePoint($event)"
*ngIf="dataTypeDisplay == 'GraphForm'"
[#fade]>
</ngx-charts-bubble-chart>
TypeScript:
view: any[] = [700, 400];
// options
showXAxis = true;
showXAxisLabel = true;
showYAxisLabel = true;
showYAxis = true;
gradient = false;
showLegend = true;
legendTitle = "Hi";
xAxisLabel = 'Number';
yAxisLabel = 'Color Value';
showGridLines = true;
autoScale=true;
minRadius = 1;
maxRadius = 1;
colorScheme = {
domain: ['#5AA454', '#A10A28', '#C7B42C', '#AAAAAA']
};
Data Points:
public multi = [
{
"name": "Germany",
"series": [
{
"name": "2010",
"value": 7300000
},
{
"name": "2011",
"value": 8940000
}
]
},
{
"name": "USA",
"series": [
{
"name": "2010",
"value": 7870000
},
{
"name": "2011",
"value": 8270000
}
]
},
{
"name": "France",
"series": [
{
"name": "2010",
"value": 5000002
},
{
"name": "2011",
"value": 5800000
}
]
}
];
I would appreciate if someone could give me some advice on correctly populating the chart.
I was eventually able to get it worked out. I will post the same 3 code snippets from above here in their updated forms.
HTML:
<ngx-charts-bubble-chart
[results]="bubbleDemoTempData"
[view]="view"
[showGridLines]="showGridLines"
[legend]="legend"
[legendTitle]="legendTitle"
[legendPosition]="legendPOsition"
[xAxis]="XAxis"
[yAxis]="YAxis"
[showXAxisLabel]="showXAxisLabel"
[showYAxisLabel]="showYAxisLabel"
[xAxisLabel]="xAxisLabel"
[yAxisLabel]="yAxisLabel"
[trimXAxisTicks]="trimXAxisTicks"
[trimYAxisTicks]="trimYAxisTicks"
[maxXAxisTickLength]="maxXAxisTickLength"
[maxYAxisTickLength]="maxYAxisTickLength"
[roundDomains]="roundDomains"
[minRadius]="minRadius"
[maxRadius]="maxRadius"
[autoScale]="autoScale"
[schemeType]="schemeType"
(select)="onSelectBubbleInteractivePoint($event)"
*ngIf="dataTypeDisplay == 'GraphForm'"
[#fade]>
</ngx-charts-bubble-chart>
TypeScript:
view: any[] = [700, 400];
// options
showGridLines = true;
legend = true;
legendTitle = "Dots Mf'er";
legendPosition = "right";
xAxis = true;
yAxis = true;
showXAxisLabel = true;
showYAxisLabel = true;
xAxisLabel = "LR";
yAxisLabel = "Jobs";
trimXAxisTicks = true;
trimYAxisTicks = true;
rotateXAxisTicks = true;
maxXAxisTickLength = 16;
maxYAxisTickLength = 16;
// xAxisTicks;
// yAxisTicks;
roundDomains = false;
maxRadius = 5;
minRadius = 5;
autoScale = true;
schemeType = "ordinal";
tooltipDisabled = false;
Data Points:
public bubbleDemoTempData = [
{
"name": "Example1",
"series": [
{
"name": "a",
"x": 0,
"y": 0,
"r": 1
},
{
"name": "b",
"x":10,
"y":3,
"r":10
}
]
},
{
"name":"Example2",
"series": [
{
"name":"1",
"x":20,
"y":1,
"r":30
},
{
"name":"2",
"x":3,
"y":3,
"r":500
}
]
}
];
This works and definitely answers my question from above. That being said the grid lines are still not appearing but that's an entirely different issue that I may have to post a new ticket for.
I want to dynamically adjust the radius of circle-based extrusions with Mapbox based on the zoom level.
I have used for a toy dataset the solution provided by #stdob-- here
and for which the JS Fiddle is available here.
The problem with that solution is that it is computationally very expensive and with my real dataset (more than a million point) this is not a viable solution. I therefore thought about using queryRenderedFeatures() as suggested in the comments of the previous SO posts. However even that is not giving me a good enough interactive visualization.
Instead, I therefore wanted to initially load all of my dataset and layers (including the 3D extrusions) and then on map-zoom events only recompute the radius that is going to be used for the 3D extrusions.
Here is the code I used:
Here is simple geojson file to reproduce the error with
{"type": "FeatureCollection", "features": [{"id": 1, "type": "Feature", "properties": {"x": 1.0, "group": 1, "my_property": 217}, "geometry": {"type": "Point", "coordinates": [8.539961, 47.37347]}}, {"id": 2, "type": "Feature", "properties": {"x": 2.0, "group": 1, "my_property": 520}, "geometry": {"type": "Point", "coordinates": [8.517961, 47.37520]}}]}
the following code:
HTML:
<html>
<head>
<meta charset='utf-8' />
<title>Display buildings in 3D</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.48.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.48.0/mapbox-gl.css' rel='stylesheet' />
<script src='https://npmcdn.com/#turf/turf/turf.min.js'></script>
<script src="https://unpkg.com/supercluster#4.1.1/dist/supercluster.min.js"></script>
</head>
<body>
<div id='map'></div>
<script>
</script>
</body>
</html>
CSS:
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
JS:
mapboxgl.accessToken = 'pk.eyJ1IjoibG9ubmliZXNhbmNvbiIsImEiOiJjamxjaWNpOHQwMHV0M3FwaHhneGhvY2l2In0.7GxI8W_dnTKITNF4hEvZeQ';
var map = new mapboxgl.Map({
style: 'mapbox://styles/mapbox/light-v9',
center:[8.538961, 47.37247],
zoom: 10,
pitch: 20,
bearing: 0,
container: 'map'
});
var url = "REPLACE WITH GEOJSON LOCATION"
//
var zoom_level_3D_bars = 14
var radius_zoom_d = 10
var map_zoom = 10
map.on('load', function() {
// Insert the layer beneath any symbol layer.
var layers = map.getStyle().layers;
var labelLayerId;
for (var i = 0; i < layers.length; i++) {
if (layers[i].type === 'symbol' && layers[i].layout['text-field']) {
labelLayerId = layers[i].id;
break;
}
}
map.addSource("data", {
type: "geojson",
data: url,
});
map.addLayer({
'id': 'extrusion',
'type': 'fill-extrusion',
'minzoom': zoom_level_3D_bars,
"source": {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": []
}
},
'source': 'data',
'paint': {
'fill-extrusion-height': ['/', ['number', ['get', 'my_property'],0], 10],
'fill-extrusion-base': 0,
'fill-extrusion-opacity': 0.5
}
});
map.addLayer({
'id': 'population',
'type': 'circle',
'source': 'data',
'paint': {
'circle-color': {
'property': 'group',
'type': 'categorical',
stops: [
[1, 'rgba(252,141,98,1)'],
[2, 'rgba(102,194,165,1)'],
[3, 'rgba(102,194,165,1)'],
[4, 'rgba(102,194,165,1)'],
[5, 'rgba(102,194,165,1)'],
[6, 'rgba(102,194,165,1)'],
//'4', '#3bb2d0',
/* other 'rgba(102,194,165,0.1)'*/
]
},
}
});
map.on('data', function() {
//if (!firstTower) updateTower();
//});
//console.log("Initialize")
//initializeTower();
})
map.on('zoom', function() {
map_zoom = map.getZoom();
if(map.isSourceLoaded('data') == false){
return
}
if(map_zoom < zoom_level_3D_bars){
map.setPaintProperty('population', 'circle-radius', radius_zoom_d);
if(map.getPaintProperty('population','circle-opacity') != 1){
map.setPaintProperty('population', 'circle-opacity', 1)
}
}
radius_zoom_d = 10 - (map_zoom/2)
if(map_zoom >= zoom_level_3D_bars){
opacity_point = 0
console.log("Update tower bc zoom = "+map_zoom)
if(map.getPaintProperty('population','circle-opacity') != 0){
map.setPaintProperty('population', 'circle-opacity', 0)
}
updateTower();
}
})
function updateTower() {
var radiusPX = false;
var layer = map.getLayer('population')
if (layer.paint) radiusPX = map.getLayer('population').paint.get('circle-radius').evaluate();
if (radiusPX === false) return;
var data = {
"type": "FeatureCollection",
"features": []
}
//HERE IS THE PART where I would like to change the radius without having to take
// all the querySourceFeatures or queryRenderedFeatures for performance issues
//But I don't know how to just go through the dataset of the layer extrusion
}
map.on('data', function(e) {
// if (e.sourceId !== 'total') return
if (e.sourceId !== 'data') return
if (e.isSourceLoaded !== true) return
initializeTower()
})
//map.on('sourcedata', sourceCallback);
function initializeTower(){
if (layer.paint) radiusPX = map.getLayer('population').paint.get('circle-radius').evaluate();
if (radiusPX === false) return;
var nb_of_objects = 0
var data = {
"type": "FeatureCollection",
"features": []
}
map.querySourceFeatures('data').forEach(function(f) {
var object = turf.centerOfMass(f)
var center = object.geometry.coordinates
var xy = map.project(center)
xy.x += radiusPX;
var LL = map.unproject(xy)
LL = turf.point([LL.lng, LL.lat])
//var radius = turf.distance(center, LL, {
// units: 'meters'
//}) + 0.00000001
var radius = radius_zoom_d ;
var options = {
steps: 16,
units: 'meters',
properties: object.properties
};
data.features.push(turf.circle(center, radius, options))
nb_of_objects +=1
})
console.log("Finished preparing data for "+nb_of_objects+" objects")
map.getSource('extrusion').setData(data);
}
});
The first issue I have is that it triggers a ReferenceError: layer is not defined on the line if (layer.paint) radiusPX = map.getLayer('my_initial_2D_layer').paint.get('circle-radius').evaluate();. This is probably due to the layer's style not being rendered yet, but it seems from the documentation and few Mapbox Questions on SO and on their GitHub that there is no way to check for that.
If I comment this line, this triggers later on in the code a Cannot read property 'setData' of undefined on the line map.getSource('extrusion').setData(data); and also that it prints it processed 0 objects which is quite problematic. I get the output from my console.log().
Finished preparing data for 0 objects
The second issue that I have is that I don't know how I could later modify the data of this extrusion layer. It seems that there is no function to get the data for my extrusion layer in order to just change its radius (as it seems that this cannot be done dynamically in the layer style).
Would anyone know how to proceed?
I am migrating google charts to amCharts. I am using a data array like this:
[
[CITY, SUM],
[A, 1500],
[B, 1470],
[C, 1920]
]
I can use this in google charts. So this solution is very flexible and dynamic. And I do not set any value field ot category field like amCharts.
But I see that amCharts data should be json object array.
[
{CITY: A, SUM: 1500},
{CITY: B, SUM: 1470},
{CITY: C, SUM: 1920}
]
So I need to know value ad category propery for every dataset.
var chart = AmCharts.makeChart("chartdiv", {
"categoryField": "CITY",
"graphs": [{
"type": "column",
"valueField": "SUM"
}]
}
SO this is not very flexible.
Is there any solution to get;
first item of json object is categoryField
second item of solution is valueField
Or using google datatable data in amCharts.
This functionality is not available out of the box as AmCharts requires this information to be defined upfront.
You can certainly write a pre-processing method or a plugin through AmCharts' addInitHandler method to convert your data and create graphs for you. Here's a basic example which defines a custom dataTable property containing the settings needed to make a custom plugin work:
//mini plugin to handle google datatable array of arrays format
AmCharts.addInitHandler(function(chart) {
if (!chart.dataTable && !chart.dataTable.data && !chart.dataTable.graph) {
return;
}
var dataProvider;
var graphs = [];
var graphTemplate = chart.dataTable.graph;
var fields = chart.dataTable.data[0];
var data = chart.dataTable.data.slice(1);
fields.slice(1).forEach(function(valueField) {
graphs.push({
type: graphTemplate.type || "line",
fillAlphas: graphTemplate.fillAlphas || 0,
lineAlpha: graphTemplate.lineAlpha || 1,
valueField: valueField
});
});
dataProvider = data.map(function(arr) {
var dataObj = {};
arr.forEach(function(value, idx) {
dataObj[fields[idx]] = value;
})
return dataObj;
});
chart.categoryField = fields[0];
chart.graphs = graphs;
chart.dataProvider = dataProvider;
});
var chart = AmCharts.makeChart("chartdiv", {
"type": "serial",
"theme": "light",
//custom dataTable property used by the chart to accept dataTable format
"dataTable": {
"data": dataTable,
"graph": { //graph template for all value fields
"type": "column",
"fillAlphas": .8,
"lineAlpha": 1
}
}
});
You can extend this as much as you need.
Here's a demo using your data and an additional column of dummy data:
var dataTable = [
["CITY", "SUM", "AVG"],
["A", 1500, 500],
["B", 1470, 490],
["C", 1920, 640]
];
//mini plugin to handle google datatable array of arrays format
AmCharts.addInitHandler(function(chart) {
//check if the required properties for the plugin are defined before proceeding
if (!chart.dataTable && !chart.dataTable.data && !chart.dataTable.graph) {
return;
}
var dataProvider;
var graphs = [];
var graphTemplate = chart.dataTable.graph;
var fields = chart.dataTable.data[0];
var data = chart.dataTable.data.slice(1);
//create the graph objects using the graph template from the custom dataTable property
fields.slice(1).forEach(function(valueField) {
graphs.push({
type: graphTemplate.type || "line",
fillAlphas: graphTemplate.fillAlphas || 0,
lineAlpha: graphTemplate.lineAlpha || 1,
valueField: valueField
});
});
//construct the dataProvider array from the datatable data
dataProvider = data.map(function(arr) {
var dataObj = {};
arr.forEach(function(value, idx) {
dataObj[fields[idx]] = value;
})
return dataObj;
});
//update the chart properties
chart.categoryField = fields[0];
chart.graphs = graphs;
chart.dataProvider = dataProvider;
});
var chart = AmCharts.makeChart("chartdiv", {
"type": "serial",
"theme": "light",
//custom dataTable property used by the chart to accept dataTable format
"dataTable": {
"data": dataTable,
"graph": { //graph template for all value fields
"type": "column",
"fillAlphas": .8,
"lineAlpha": 1
}
}
});
html,
body {
width: 100%;
height: 100%;
margin: 0px;
}
#chartdiv {
width: 100%;
height: 100%;
}
<script src="//www.amcharts.com/lib/3/amcharts.js"></script>
<script src="//www.amcharts.com/lib/3/serial.js"></script>
<script src="//www.amcharts.com/lib/3/themes/light.js"></script>
<div id="chartdiv"></div>
I am creating a chart with Chartist.js. I'm getting json data with the Google embed API. I have a problem with this one. The array works with the values I give. But it does not work for data from json.
my code :
var TotalBrowser = [];
var BrowserSeries = [];
var oxyn = {
queryAnalytics: function() {
var id = '164690638';
var expressions = [{
expression: 'ga:hits'
}];
var dimension = [{
name: 'ga:browser'
}];
oxyn.getReportQuery(id, '7daysago', 'today', expressions, dimension).then(function(response) {
var formattedJson = JSON.stringify(response.result, null, 2);
var data = JSON.parse(formattedJson);
var i = 0;
BrowserTotal = data.reports[0].data.totals[0].values[0];
jQuery(data.reports[0].data.rows).each(function() {
if (i <= 3) {
jQuery('#Browsers').append(browsericon[i] + this.dimensions[0]);
var percent = (parseInt(this.metrics[0].values[0]) / parseInt(BrowserTotal)) * 100;
BrowserSeries.push(Math.round(percent));
TotalBrowser.push(Math.round(percent) + '%');
i++;
}
});
demo.initChartist();
});
}
}
var demo = {
initChartist: function() {
var dataPreferences = {
series: [
[BrowserSeries.join()]
]
};
var optionsPreferences = {
donut: true,
donutWidth: 40,
startAngle: 0,
total: 100,
showLabel: false,
axisX: {
showGrid: false
}
};
Chartist.Pie('#chartPreferences', dataPreferences, optionsPreferences);
Chartist.Pie('#chartPreferences', {
labels: [TotalBrowser.join()],
series: [BrowserSeries.join()]
});
console.log(BrowserSeries.join());
}
};
it does not work that way. But if I write the code like this, it works.
Chartist.Pie('#chartPreferences', {
labels: [TotalBrowser.join()],
series: [30, 70]
});
and this is working.
Chartist.Pie('#chartPreferences', {
labels: [TotalBrowser[0], TotalBrowser[1]],
series: [BrowserSeries[0], BrowserSeries[1]]
});
console output
console.log(BrowserSeries.join());
30,70
JSON Source
It's a very silly problem.
yes I finally solved it. I write for those who have the same problem.
Chartist.Pie('#chartPreferences', {
labels: TotalBrowser,
series: BrowserSeries
});
We need to remove [ ] characters. We must also send the data directly to the array.
Also : https://github.com/gionkunz/chartist-js/issues/738
I have created a bubble chart using chart.js,which looks like the below
Is there a way to name each and every bubble in the chart? I am planning to put a data box below this chart. On clicking each bubble data box should display info associated with each bubble. Each bubble will have its own data like maturity_date,bond_type,credit_rating,symbol,etc... How can I name each bubble? These bubbles are created dynamically. This is the code I use to create the chart
$(document).ready(function(){
$.ajax({url: "xxxxxxxx.x.xx", success: function(result){
var dataObj = {};
dataObj.datasets = [];
var object = {};
object.label = 'First Dataset';
object.backgroundColor = [];
object.hoverBackgroundColor = [];
object.data = [];
var resultData = result.data;
var currentYear = new Date().getFullYear();
for (var i=0; i<resultData.length; i++) {
if(resultData[i].hasOwnProperty("maturity_date") && resultData[i].hasOwnProperty("ask_ytm")) {
var maturity_date = resultData[i].maturity_date.split("-");
var matYear = new Date(maturity_date[1]+"-"+maturity_date[0]+"-"+maturity_date[2]).getFullYear();
if (resultData[i].bond_type == "Tax Free" )
{
object.backgroundColor.push("#34A10C");
object.hoverBackgroundColor.push("#34A10C");
}
else
{
object.backgroundColor.push("#1130E8");
object.hoverBackgroundColor.push("#1130E8");
}
object.data.push({x: (matYear - currentYear), y: resultData[i].ask_ytm, r: 4});
}
}
dataObj.datasets.push(object);
var ctx = document.getElementById("myChart");
var myBubbleChart = new Chart(ctx,{
type: 'bubble',
data : dataObj,
legend: {
display: false
},
responsive: true,
maintainAspectRatio: true,
}
});
}});
});
In your data declaration, you can add custom properties if you need to :
data: [{
x: 20,
y: 30,
r: 15,
symbol: "£",
bond_type: "corporate"
}, {
x: 40,
y: 10,
r: 10,
symbol: "$",
bond_type: "james"
} /* ... */]
Since this data is dynamic, you need to do it from your back-end of course.
Afterwards, you can access these new properties in your callback (onClick event for instance) :
options: {
onClick: function(e) {
var element = this.getElementAtEvent(e);
if (element.length > 0) {
var data = this.config.data.datasets[element[0]._datasetIndex].data[element[0]._index];
console.log(data);
// You can have the following for instance :
// data -> { x:40, y:10, r:10, symbol:"$", bond_type:"james" }
}
}
}