529 lines
17 KiB
JavaScript
529 lines
17 KiB
JavaScript
function ZtForceChart(type) {
|
|
ZtTreeChart.apply(this, [type]);
|
|
this.viewBreadcrumb = false;
|
|
this.byCount = (this.type == "FORCESTR");
|
|
this.colorsTree = {};
|
|
}
|
|
ZtForceChart.prototype = Object.create(ZtTreeChart.prototype);
|
|
ZtForceChart.prototype.constructor = ZtForceChart;
|
|
|
|
ZtForceChart.prototype._SetChartSettingsJSON = function () {
|
|
ZtTreeChart.prototype._SetChartSettingsJSON.call(this);
|
|
this.defSettings.shape = this.chartConfig.graphSettings.shape;
|
|
//this.defSettings.shape.figureTypes = ["circle", "square", "triangle", "cross", "diamond", "star", "wye"];
|
|
this.defSettings.shape.figure = "circle"; // default
|
|
if (this.objConfig.mapSeries && !Empty(Object.keys(this.objConfig.mapSeries))) {
|
|
this.defSettings.shape.differSeries = true;
|
|
this.defSettings.shape.figureTypes = [];
|
|
this.defSettings.colorSeries = [];
|
|
var okys = Object.keys(this.objConfig.mapSeries);
|
|
for (var i = 0; i < okys.length; i++) {
|
|
this.defSettings.shape.figureTypes.push(this.objConfig.mapSeries[okys[i]].type);
|
|
this.defSettings.colorSeries.push({ color: this.objConfig.mapSeries[okys[i]].color, gradient: ""});
|
|
}
|
|
}
|
|
this.defSettings.animation.animate = true;
|
|
if (Empty(this.defSettings.animation.duration))
|
|
this.defSettings.animation.duration = 1000;
|
|
}
|
|
|
|
ZtForceChart.prototype._SetFieldsIndexes = function () {
|
|
ZtTreeChart.prototype._SetFieldsIndexes.call(this);
|
|
this.levelsClassIndex = {};
|
|
var lev = this.objConfig.labelSeries.split(",");
|
|
for (var i = 0; i < lev.length; i++) {
|
|
this.levelsClassIndex[lev[i]] = this.propertyName.indexOf(lev[i]);
|
|
}
|
|
}
|
|
|
|
ZtForceChart.prototype._SetLists = function () {
|
|
ZtTreeChart.prototype._SetLists.call(this);
|
|
if (!Empty(this.objConfig.labelSeries))
|
|
this.levelsLabelFields = this.objConfig.labelSeries.split(",");
|
|
if (!Empty(this.objConfig.fieldClass))
|
|
this.levelsClassFields = this.objConfig.fieldClass.split(",");
|
|
if (this.objConfig.mapSeries)
|
|
this.orgSeriesList = this.legendList = Object.keys(this.objConfig.mapSeries);
|
|
}
|
|
|
|
ZtForceChart.prototype._SetCalculationDataset = function () {
|
|
var addFields;
|
|
if (!this.byCount) {
|
|
var lSeries = !Empty(this.objConfig.labelSeries) ? this.objConfig.labelSeries : this.levelsFields[0];
|
|
addFields = [];
|
|
if (this.categoryCodeIndex >= 0)
|
|
addFields.push( { prop: "categoryCode", index: this.categoryCodeIndex } );
|
|
var hie = getHieDataset(this.dataSet, this.levelsFields, this.valueFields, lSeries, this.propertyName, addFields, true);
|
|
this.calcDataset = d3.hierarchy(hie);
|
|
this.categoryList = getListFromObjectArray(this.calcDataset.children, "data.name")
|
|
}
|
|
else {
|
|
addFields = [];
|
|
if (this.categoryCodeIndex >= 0)
|
|
addFields.push( { prop: "categoryCode", index: this.categoryCodeIndex } );
|
|
var robj = getForceDataset(this.dataSet, this.levelsFields, this.valueFields, this.propertyName, this.levelsClassFields, this.levelsLabelFields, addFields);
|
|
this.calcDataset = robj.nodes;
|
|
this.calcLinks = robj.links;
|
|
this.categoryList = robj.array.unique();
|
|
if (!Empty(this.levelsLabelFields)) {
|
|
this.orgCategoryList = robj.array;
|
|
this.categoryList = robj.labels.unique();
|
|
}
|
|
if (!this.objConfig.mapSeries || Empty(Object.keys(this.objConfig.mapSeries)))
|
|
this.orgSeriesList = this.legendList = this.categoryList.slice();
|
|
else
|
|
this.orgSeriesList = this.legendList = Object.keys(this.objConfig.mapSeries);
|
|
}
|
|
this.SetChartLegends();
|
|
}
|
|
|
|
ZtForceChart.prototype._DrawChartElements = function () {
|
|
var group = this.renderer.append("g")
|
|
.attr("id", this.chartId + this.type + "_elementGroup")
|
|
.attr("transform","translate(" + [0,0] + ")"),
|
|
radius = 5,
|
|
minlen = Math.min((this.chartWidth / 2 - this.marginOffsetX), (this.chartHeight / 2 - this.marginOffsetY)),
|
|
_this = this;
|
|
|
|
this.forceSimulationFnc = function () {
|
|
_this.svgDraw.link
|
|
.attr("x1", function (d) { return d.source.x; })
|
|
.attr("y1", function (d) { return d.source.y; })
|
|
.attr("x2", function (d) { return d.target.x; })
|
|
.attr("y2", function (d) { return d.target.y; });
|
|
|
|
_this.svgDraw.node.attr("transform", function(d) { return "translate(" + d.x + ", " + d.y + ")"; });
|
|
}
|
|
|
|
this.forceSimulation = d3.forceSimulation()
|
|
.force("center", d3.forceCenter(this.chartWidth / 2, this.chartHeight / 2))
|
|
.force("charge", d3.forceManyBody().strength(- Math.min(this.chartWidth / 2, this.chartHeight / 2) / 2))
|
|
.force("link", d3.forceLink().distance(minlen / 4))
|
|
.force("collide", d3.forceCollide()
|
|
.radius(function(d) {
|
|
if (_this.scaleZ && d.data.size)
|
|
return _this.scaleZ(d.data.size);
|
|
return radius;
|
|
})
|
|
)
|
|
.force("x", d3.forceX())
|
|
.force("y", d3.forceY())
|
|
.on("tick", this.forceSimulationFnc);
|
|
|
|
if (!this.drawStatic) {
|
|
/* parto dal presupposto che ?log(alphaMin) / log(1 - alphaDecay)?; by default, this is 300 (rif. API d3js)
|
|
e tengo fissata la parte log(alphaMin)
|
|
l'equazione da risolvere e' log(alphaMin) / log(1 - alphaDecay) = prevIterations
|
|
*/
|
|
var seconds = this.defSettings.animation.duration / 1000,
|
|
defaultIterations = 300,
|
|
defaultSeconds = 5,
|
|
prevIterations = (defaultIterations * seconds) / defaultSeconds,
|
|
alphaDecay = 1 - Math.exp(Math.log(this.forceSimulation.alphaMin()) / prevIterations);
|
|
if (alphaDecay < 0)
|
|
alphaDecay = 0;
|
|
if (alphaDecay > 1)
|
|
alphaDecay = 1;
|
|
this.forceSimulation.alphaDecay(alphaDecay);
|
|
}
|
|
|
|
this.DrawForceLayout(group, this.calcDataset);
|
|
}
|
|
|
|
ZtForceChart.prototype._SetElementsColor = function () {
|
|
this.SetColorSet();
|
|
var idx = 0,
|
|
gradID,
|
|
colD,
|
|
styleObj;
|
|
|
|
for (var i = 0; i < this.items.length; i++) {
|
|
idx = this.categoryList.indexOf(this.items[i].desc);
|
|
if (!this.byCount)
|
|
idx = i;
|
|
if (idx >= 0) {
|
|
this.items[i].idx = this.items[i].index;
|
|
if (!this.byCount) {
|
|
if (this.items[i].hasChild) {
|
|
if (!(this.items[i].category in this.colorsTree)) {
|
|
this.colorsTree[this.items[i].category] = this.colorSet[idx];
|
|
idx ++;
|
|
}
|
|
this.items[i].color = this.colorsTree[this.items[i].category];
|
|
}
|
|
else {
|
|
var parent = this.items[i].parent;
|
|
if (parent && parent.data) {
|
|
if (!(parent.data.name in this.colorsTree)) { // tutela
|
|
this.colorsTree[parent.data.name] = this.colorSet[idx];
|
|
idx ++;
|
|
}
|
|
this.items[i].color = this.colorsTree[parent.data.name];
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
this.items[i].color = this.colorSet[idx];
|
|
if (this.objConfig.mapSeries) {
|
|
if (!Empty(this.items[i].class) && (this.items[i].class in this.objConfig.mapSeries)) {
|
|
if (this.objConfig.mapSeries[this.items[i].class].color)
|
|
this.items[i].color = { color: this.objConfig.mapSeries[this.items[i].class].color, gradient: "" };
|
|
}
|
|
}
|
|
}
|
|
styleObj = {};
|
|
gradID = this.chartId + this.type + "_" + idx + "Gradient";
|
|
if (!Empty(this.items[i].color.color) && Empty(this.items[i].color.gradient) || this.items[i].color.color.toLowerCase() == this.items[i].color.gradient.toLowerCase())
|
|
colD = this.items[i].color.color;
|
|
else if (Empty(this.items[i].color.color) && !Empty(this.items[i].color.gradient))
|
|
colD = this.items[i].color.gradient;
|
|
else {
|
|
this.CreateGradient(this.items[i].color.color, this.items[i].color.gradient, gradID, "element");
|
|
colD = "url(" + document.URL + "#" + gradID + ")";
|
|
}
|
|
styleObj.fill = colD;
|
|
styleObj.stroke = "#FAFAFA";
|
|
styleObj.stroke9width = "0.5px";
|
|
d3.select(this.items[i].itm).attr("style", makeStyleHTMLString(styleObj, d3.select(this.items[i].itm).attr("style")));
|
|
}
|
|
}
|
|
|
|
styleObj.fill = "none";
|
|
styleObj.stroke = this.defSettings.line.color || "#D0D0D0";
|
|
styleObj.stroke9width = (this.defSettings.line.stroke || 0.5) + "px";
|
|
|
|
if (this.items.length > 1) {
|
|
this.svg
|
|
.selectAll(".link")
|
|
.attr("style", makeStyleHTMLString(styleObj, this.svg.selectAll(".link").attr("style")));
|
|
}
|
|
}
|
|
|
|
ZtForceChart.prototype._ApplyMouseOver = function (item, itemObj) {
|
|
StdChart.prototype._ApplyMouseOver.call(this, item, itemObj);
|
|
}
|
|
|
|
ZtForceChart.prototype.AppendHoverItem = function (item, itemObj) {
|
|
StdChart.prototype.AppendHoverItem.call(this, item, itemObj);
|
|
}
|
|
|
|
ZtForceChart.prototype._SelectItem = function (itemObject, select) {
|
|
StdChart.prototype._SelectItem.call(this, itemObject, select);
|
|
if (this.byCount)
|
|
return;
|
|
if (!itemObject.hasChild || !this.scaleZ)
|
|
return;
|
|
var fobj = this._FindNode(this.calcDataset.children, itemObject.category, []),
|
|
current;
|
|
|
|
if (fobj.found && fobj.progr.length >= 1) {
|
|
current = this.calcDataset.children[fobj.progr[0]];
|
|
for (var i = 1; i < fobj.progr.length; i++) {
|
|
current = current.children[fobj.progr[i]];
|
|
}
|
|
if (current.children && current.children.length >= 1) {
|
|
current.data._size = current.data.size;
|
|
current.data.size = d3.max(this.allValuesZ) * 2;
|
|
current._children = current.children;
|
|
current.children = null;
|
|
}
|
|
else {
|
|
current.data.size = current.data._size;
|
|
current.data._size = null;
|
|
current.children = current._children;
|
|
current._children = null;
|
|
}
|
|
}
|
|
this._UpdateElements();
|
|
}
|
|
|
|
ZtForceChart.prototype._UpdateElements = function () {
|
|
var group = this.svg.select("#" + this.chartId + this.type + "_elementGroup");
|
|
group.selectAll("g").remove();
|
|
this.RemoveAllHovers();
|
|
this.ResetItems();
|
|
this.DrawForceLayout(group, this.calcDataset);
|
|
this.AfterDraw();
|
|
this.forceSimulation.restart();
|
|
this.forceSimulation.alphaTarget(0);
|
|
}
|
|
|
|
ZtForceChart.prototype._GetItem = function (prop, value) {
|
|
return this._GetItems().filter(function (it) { return it[prop] == value; })[0];
|
|
}
|
|
|
|
ZtForceChart.prototype._FindNode = function (chd, key, progr) {
|
|
var found = false,
|
|
obj = {};
|
|
for (var i = 0; !found && i < chd.length; i++) {
|
|
if (chd[i].data && chd[i].data.name == key) {
|
|
found = true;
|
|
progr.push(i);
|
|
}
|
|
else if (chd[i].children) {
|
|
obj = this._FindNode(chd[i].children, key, progr);
|
|
found = obj.found;
|
|
progr = obj.progr;
|
|
if (found)
|
|
progr.splice(progr.length - 1, 0, i);
|
|
}
|
|
}
|
|
return { found : found, progr : progr};
|
|
}
|
|
|
|
ZtForceChart.prototype._GetNodeSize = function (node, size) {
|
|
for (var i = 0; i < node.children.length; i++) {
|
|
if (node.children[i].data && ("size" in node.children[i].data))
|
|
size += node.children[i].data.size;
|
|
else
|
|
size = this._GetNodeSize(node.children[i], size);
|
|
}
|
|
return size;
|
|
}
|
|
|
|
ZtForceChart.prototype._GetDefaultAnimateFunction = function (/*item, lastValueObj, duration*/) { }
|
|
|
|
ZtForceChart.prototype._GetParametersStringForTooltip = function (item, itemObject) {
|
|
var purl = "";
|
|
if (this.byCount) {
|
|
// dep
|
|
if (!Empty(itemObject.key))
|
|
purl += "&key=" + URLenc(itemObject.key);
|
|
|
|
if (!Empty(itemObject.desc))
|
|
purl += "&category=" + URLenc(itemObject.desc);
|
|
|
|
if (!Empty(itemObject.class))
|
|
purl += "&serie=" + URLenc(itemObject.class);
|
|
}
|
|
else {
|
|
if (!Empty(itemObject.category))
|
|
purl += "&category=" + URLenc(itemObject.category);
|
|
}
|
|
|
|
if (!Empty(itemObject.value)) {
|
|
var val = itemObject.value;
|
|
// apply picture + funzione
|
|
if (this.defSettings.valueLabel.applyPicture && this.defSettings.valuePicture.applyFunction) {
|
|
if (this.functionObj.GetValuePicture) {
|
|
val = this.functionObj.GetValuePicture(this._GetItemForGrid(itemObject));
|
|
}
|
|
}
|
|
purl += "&value=" + URLenc(val);
|
|
}
|
|
|
|
return purl;
|
|
}
|
|
|
|
ZtForceChart.prototype._GetParametersStringForLink = function (item) {
|
|
var purl = "";
|
|
if (this.byCount) {
|
|
// dep
|
|
if (!Empty(item.keyF))
|
|
purl += "&key=" + URLenc(item.keyF);
|
|
|
|
if (!Empty(item.category))
|
|
purl += "&category=" + URLenc(item.category);
|
|
|
|
if (!Empty(item.series))
|
|
purl += "&serie=" + URLenc(item.series);
|
|
}
|
|
else {
|
|
if (!Empty(item.category))
|
|
purl += "&category=" + URLenc(item.category);
|
|
}
|
|
|
|
if (!Empty(item.value)) {
|
|
var val = item.value;
|
|
// apply picture + funzione
|
|
if (this.defSettings.valueLabel.applyPicture && this.defSettings.valuePicture.applyFunction) {
|
|
if (this.functionObj.GetValuePicture) {
|
|
val = this.functionObj.GetValuePicture(this._GetItemForGrid(item));
|
|
}
|
|
}
|
|
purl += "&value=" + URLenc(val);
|
|
}
|
|
|
|
return purl;
|
|
}
|
|
|
|
ZtForceChart.prototype._GetItemObj = function (itemObject) {
|
|
var ret;
|
|
if (this.byCount) {
|
|
// dep
|
|
ret = {
|
|
category: itemObject.desc,
|
|
series: itemObject.class,
|
|
value: itemObject.value,
|
|
categoryCode: itemObject.categoryCode,
|
|
keyF: itemObject.key,
|
|
rowIdx: -1,
|
|
isSelected : itemObject.isSelected,
|
|
multidimCurrentKey : this.appliedKey,
|
|
color: itemObject.color
|
|
}
|
|
}
|
|
else {
|
|
// hie
|
|
var row = -1,
|
|
depth = itemObject.depth,
|
|
fidx = this.levelsIndex[this.levelsFields[depth]],
|
|
vidx = this.yIndex;
|
|
this._ZtChart.orgData.filter(function (rec, index) {
|
|
if (rec[fidx] == itemObject.category &&
|
|
rec[vidx] == itemObject.value ) {
|
|
row = index;
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
|
|
ret = {
|
|
category: itemObject.category,
|
|
parent: itemObject.parentName,
|
|
value: itemObject.value,
|
|
rowIdx: itemObject.hasChild ? -1 : row,
|
|
categoryCode: itemObject.categoryCode,
|
|
depth: itemObject.depth,
|
|
isSelected : itemObject.isSelected,
|
|
multidimCurrentKey : this.appliedKey
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
ZtForceChart.prototype._HasShapes = function () {
|
|
return true;
|
|
}
|
|
|
|
ZtForceChart.prototype._EvaluateAutomaticScales = function () {
|
|
if (this.objConfig.mapSeries && !Empty(Object.keys(this.objConfig.mapSeries)))
|
|
this.objConfig.manualScale = true;
|
|
}
|
|
|
|
ZtForceChart.prototype._GetReferredList = function () {
|
|
return this.legendList;
|
|
}
|
|
|
|
ZtForceChart.prototype._GetDrawingList = function () {
|
|
return this.legendList;
|
|
}
|
|
|
|
ZtForceChart.prototype._CreateGrid = function (thead, tbody1) {
|
|
//table header
|
|
var td, innerdiv,
|
|
i,
|
|
fields = this.levelsFields.slice(),
|
|
coords;
|
|
|
|
if (!Empty(this.valueFields))
|
|
fields.push(this.valueFields);
|
|
coords = fields.slice();
|
|
|
|
if (!Empty(this.objConfig.labelsGrid)) {
|
|
var progrIndex = 0;
|
|
if (!Empty(this.objConfig.labelsGrid.key)) {
|
|
var labval = this.objConfig.labelsGrid.key.split(","),
|
|
end = !Empty(this.valueFields) ? coords.length - 1 : coords.length;
|
|
for (i = 0; i < end; i++) {
|
|
if (!Empty(labval[i]))
|
|
coords[progrIndex] = labval[i];
|
|
progrIndex++;
|
|
}
|
|
}
|
|
|
|
if (!Empty(this.valueFields) && !Empty(this.objConfig.labelsGrid.values))
|
|
coords[progrIndex] = this.objConfig.labelsGrid.values;
|
|
}
|
|
|
|
for (i = 0; i < coords.length; i++){
|
|
td = document.createElement("td");
|
|
td.className = "grid_title grid_cell_title grid_cell_title_table";
|
|
innerdiv = document.createElement("div");
|
|
// testo colonne campi
|
|
innerdiv.textContent = coords[i];
|
|
td.appendChild(innerdiv);
|
|
thead.appendChild(td);
|
|
}
|
|
|
|
//table content
|
|
this.__gfields = fields.slice();
|
|
for (i = 0; i < this.dataSet.length; i++) {
|
|
var vals = this.dataSet[i],
|
|
row = document.createElement("tr");
|
|
row.style.height = "42px";
|
|
if (i == 0 || i % 2 == 0) {
|
|
row.className = "grid_rowodd";
|
|
}
|
|
else {
|
|
row.className = "grid_row";
|
|
}
|
|
for (var b = 0; b < fields.length; b++) {
|
|
var cell = document.createElement("td"),
|
|
div3 = document.createElement("div"),
|
|
idx = this.propertyName.indexOf(fields[b]);
|
|
div3.textContent = this._GetGridCellValue(vals[idx], b, vals);
|
|
cell.appendChild(div3);
|
|
row.appendChild(cell);
|
|
}
|
|
tbody1.appendChild(row);
|
|
}
|
|
}
|
|
|
|
ZtForceChart.prototype._GetGridCellValue = function (value, dataidx, record) {
|
|
return ZtSunburstChart.prototype._GetGridCellValue.call(this, value, dataidx, record);
|
|
}
|
|
|
|
ZtForceChart.prototype._FindItemForGrid = function (record) {
|
|
if (this.byCount) {
|
|
var itm = {},
|
|
idx;
|
|
idx = this.propertyName.indexOf(this.valueFields);
|
|
itm.value = record[idx];
|
|
idx = this.propertyName.indexOf(this.levelsFields[1]);
|
|
itm.desc = record[idx];
|
|
itm.index = this.orgSeriesList.indexOf(itm.desc);
|
|
return this._GetItemForGrid(itm);
|
|
}
|
|
else
|
|
return ZtSunburstChart.prototype._FindItemForGrid.call(this, record);
|
|
}
|
|
|
|
ZtForceChart.prototype._GetParentPath = function (item) {
|
|
return ZtSunburstChart.prototype._GetParentPath.call(this, item);
|
|
}
|
|
|
|
ZtForceChart.prototype._GetItemForGrid = function (item) {
|
|
if (this.byCount) {
|
|
var itm = {},
|
|
i;
|
|
itm.node = item.desc;
|
|
itm.value = item.value;
|
|
itm.field = this.valueFields;
|
|
itm.source = {};
|
|
itm.source.field = this.levelsFields[0];
|
|
itm.source.values = [];
|
|
itm.target = {};
|
|
itm.target.field = this.levelsFields[1];
|
|
itm.target.values = [];
|
|
for (i = 0; i < this.calcLinks.length; i++) {
|
|
var link = this.calcLinks[i];
|
|
// item come target
|
|
if (link.target.index == item.index) {
|
|
if (itm.source.values.indexOf(link.source.name) < 0)
|
|
itm.source.values.push(link.source.name);
|
|
}
|
|
// item come source
|
|
if (link.source.index == item.index) {
|
|
if (itm.target.values.indexOf(link.target.name) < 0)
|
|
itm.target.values.push(link.target.name);
|
|
}
|
|
}
|
|
// class
|
|
// labels
|
|
return itm;
|
|
}
|
|
else
|
|
return ZtSunburstChart.prototype._GetItemForGrid.call(this, item);
|
|
}
|