node-red-contrib-prib-functions 0.18.0 → 0.20.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/codeql-analysis.yml +3 -3
- package/.github/workflows/npmpublish.yml +6 -6
- package/.vs/VSWorkspaceState.json +7 -0
- package/.vs/node-red-contrib-prib-functions/v17/.wsuo +0 -0
- package/README.md +22 -19
- package/arima/index.js +18 -0
- package/dataAnalysis/arrayAllRowsSwap.js +15 -0
- package/dataAnalysis/arrayCompareToPrecision.js +34 -0
- package/dataAnalysis/arrayDifference.js +14 -0
- package/dataAnalysis/arrayDifferenceSeasonal.js +15 -0
- package/dataAnalysis/arrayDifferenceSeasonalSecondOrder.js +20 -0
- package/dataAnalysis/arrayDifferenceSecondOrder.js +14 -0
- package/dataAnalysis/arrayForEachRange.js +38 -0
- package/dataAnalysis/arrayOverlay.js +13 -0
- package/dataAnalysis/arrayProduct.js +11 -0
- package/dataAnalysis/arrayRandom.js +14 -0
- package/dataAnalysis/arrayReduceRange.js +11 -0
- package/dataAnalysis/arrayScale.js +11 -0
- package/dataAnalysis/arraySum.js +11 -0
- package/dataAnalysis/arraySumSquared.js +11 -0
- package/dataAnalysis/arraySwap.js +11 -0
- package/dataAnalysis/dataAnalysis.html +31 -14
- package/dataAnalysis/dataAnalysis.js +10 -1
- package/dataAnalysis/generateMatrixFunction.js +89 -0
- package/dataAnalysis/generateVectorFunction.js +25 -0
- package/dataAnalysis/pca.js +546 -0
- package/dataAnalysis/svd.js +239 -0
- package/documentation/loadInjector.png +0 -0
- package/echart/echart.html +68 -0
- package/echart/echart.js +85 -0
- package/echart/icons/chart-671.png +0 -0
- package/echart/lib/echarts.js +95886 -0
- package/lib/Chart.js +177 -0
- package/lib/Column.js +99 -0
- package/lib/GraphDB.js +14 -0
- package/lib/Table.js +185 -0
- package/lib/objectExtensions.js +361 -0
- package/matrix/matrix.js +50 -50
- package/matrix/matrixNode.html +144 -154
- package/matrix/matrixNode.js +26 -9
- package/monitor/BarGauge.js +8 -0
- package/monitor/Dataset.js +29 -0
- package/monitor/DialGauge.js +109 -0
- package/monitor/DialNeedle.js +36 -0
- package/monitor/Format.js +74 -0
- package/monitor/centerElement.js +14 -0
- package/monitor/compareElements.js +95 -0
- package/monitor/defs.js +23 -0
- package/monitor/extensions.js +906 -0
- package/monitor/functions.js +36 -0
- package/monitor/json2xml.js +103 -0
- package/monitor/monitorSystem.html +198 -0
- package/monitor/monitorSystem.js +322 -0
- package/monitor/svgHTML.js +179 -0
- package/monitor/svgObjects.js +64 -0
- package/package.json +31 -8
- package/test/00-objectExtensions.js +94 -0
- package/test/01-base.js +88 -0
- package/test/04-tables.js +33 -0
- package/test/data/.config.nodes.json +608 -0
- package/test/data/.config.nodes.json.backup +608 -0
- package/test/data/.config.runtime.json +4 -0
- package/test/data/.config.runtime.json.backup +3 -0
- package/test/data/.config.users.json +21 -0
- package/test/data/.config.users.json.backup +21 -0
- package/test/data/.flow.json.backup +3433 -0
- package/test/data/float32vector10.npy +0 -0
- package/test/data/flow.json +3433 -0
- package/test/data/int2matrix2x3.npy +0 -0
- package/test/data/package-lock.json +158 -0
- package/test/data/package.json +11 -0
- package/test/data/settings.js +544 -0
- package/test/dataAnalysisExtensions.js +472 -0
- package/test/dataAnalysisPCA.js +54 -0
- package/test/dataAnalysisSVD.js +31 -0
- package/test/euclideanDistance.js +2 -2
- package/test/matrix/02base.js +36 -0
- package/test/transformNumPy.js +132 -0
- package/testing/data/countries.csv +250 -0
- package/testing/hostAvailable.html +0 -2
- package/testing/load-injector.html +76 -21
- package/testing/load-injector.js +35 -54
- package/testing/test.js +1 -0
- package/transform/NumPy.js +303 -0
- package/transform/transform.html +73 -19
- package/transform/transform.js +144 -8
- package/documentation/LoadInjector.JPG +0 -0
|
@@ -0,0 +1,906 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
require("../lib/objectExtensions")
|
|
3
|
+
const Format=require('./Format')
|
|
4
|
+
const Table=require('../lib/Table')
|
|
5
|
+
const { action, children } = require('./defs')
|
|
6
|
+
const { table } = require('console')
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
const timePrecision=(time,minutes,hours,days)=>{
|
|
10
|
+
if(Math.floor(time/60000)>0) return minutes()
|
|
11
|
+
if(Math.floor(time/60)>0) return hours()
|
|
12
|
+
if(Math.floor(time/24)>0) return days()
|
|
13
|
+
}
|
|
14
|
+
const timePeriodsMS={
|
|
15
|
+
second:1000,
|
|
16
|
+
minute:60*1000,
|
|
17
|
+
hour:60*60*1000,
|
|
18
|
+
day:24*60*60*1000,
|
|
19
|
+
week:7*24*60*60*1000,
|
|
20
|
+
fortnight:2*7*24*60*60*1000,
|
|
21
|
+
month:30*24*60*60*1000,
|
|
22
|
+
year:365*24*60*60*1000,
|
|
23
|
+
}
|
|
24
|
+
const timeUnitRange=(t,second=p=>r,minute=p=>r,hour=p=>r,day=p=>r,week=p=>r,fortnight=p=>r,month=p=>r,year=p=>r)=>{
|
|
25
|
+
let duration=Math.floor(t/1000)
|
|
26
|
+
if(duration==0) return seconds(duration)
|
|
27
|
+
duration=Math.floor(duration/60)
|
|
28
|
+
if(duration==0) return minutes(duration)
|
|
29
|
+
duration=Math.floor(duration/60)
|
|
30
|
+
if(duration==0) return hours(duration)
|
|
31
|
+
duration=Math.floor(duration/60)
|
|
32
|
+
if(duration==0) return days(duration)
|
|
33
|
+
if(Math.floor(duration/7)==0) return weeks(duration)
|
|
34
|
+
if(Math.floor(duration/14)==0) return fortnights(duration)
|
|
35
|
+
if(Math.floor(duration/30)==0) return months(duration)
|
|
36
|
+
return years(Math.floor(duration/365))
|
|
37
|
+
}
|
|
38
|
+
const timeTickPoints=(timeUnit="second",min=0,max=100)=>{
|
|
39
|
+
const period=timePeriodsMS[second]
|
|
40
|
+
const results=[]
|
|
41
|
+
const rangeRatio=Math.floor((max-min)/period);
|
|
42
|
+
for(let j=min; j<max; j++)
|
|
43
|
+
Math.floor(rangeRatio*j)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
const circle={
|
|
48
|
+
arcPath:(v)=>" A "+v.rx+" "+v.ry+","+(v.xAxisRotation??0)+","+(v.largeArcFlag??0)+","+(v.sweep-flag??0)+","+v.x+" "+v.y,
|
|
49
|
+
basePoints:(degrees=6,circumference=2*Math.PI)=>{
|
|
50
|
+
const points=[],increment=circle.toRadians(degrees)
|
|
51
|
+
for(let i=0; i<circumference; i+increment)
|
|
52
|
+
points.push([Math.cos(i),Math.sin(i)])
|
|
53
|
+
},
|
|
54
|
+
getPieSlicePath:(to,from=0,radius=100)=>{
|
|
55
|
+
const fromRadians=circle.toRadians(from),toRadians=circle.toRadians(to)
|
|
56
|
+
const flip180=(to-from)>180?",0,0,0":",0,1,1,"
|
|
57
|
+
return " M "+radius*Math.cos(fromRadians+" "+radians*Math.cos(fromRadians) +
|
|
58
|
+
" A "+radius+" "+radius + flip180 + radius*Math.cos(toRadians)+" "+radius*Math.cos(toRadians) +
|
|
59
|
+
" Z"
|
|
60
|
+
},
|
|
61
|
+
getPieSliceRatioPath:(to,from=0,radius=100)=>{
|
|
62
|
+
const fromRadians=from*circle.pi2,toRadians=to*circle.pi2
|
|
63
|
+
const flip180=(to-from)>.5?",0,0,0":",0,1,1,"
|
|
64
|
+
return " M "+radius*Math.cos(fromRadians+" "+radians*Math.cos(fromRadians) +
|
|
65
|
+
" A "+radius+" "+radius + flip180 + radius*Math.cos(toRadians)+" "+radius*Math.cos(toRadians) +
|
|
66
|
+
" Z"
|
|
67
|
+
},
|
|
68
|
+
pi2:2 * Math.PI,
|
|
69
|
+
pi2DegreeFactor:180 / Math.PI,
|
|
70
|
+
point:(degrees=6,radius=1)=>{
|
|
71
|
+
const radians=circle.toRadians(degrees)
|
|
72
|
+
return [radius*Math.cos(radians),radius*Math.sin(radians)]
|
|
73
|
+
},
|
|
74
|
+
points:(degrees=6,radius=1)=>circle.circumferenceBasePoints(degrees).map(c=>[radius*c[0],radius*c[1]]),
|
|
75
|
+
pieSlicesDegrees:(degrees,radius=100)=>{
|
|
76
|
+
const pieces=degrees.reduce((a,c,i)=>{
|
|
77
|
+
a.push({action:"path",id:"pieslice"+i,d:circle.getPieSlicePath(f,t,radius)})
|
|
78
|
+
},[])
|
|
79
|
+
return {action:g,id:"pie",children=pieces}
|
|
80
|
+
},
|
|
81
|
+
toDegrees:(radians)=> radians * circle.pi2DegreeFactor,
|
|
82
|
+
toRadians:(degrees)=> degrees / circle.pi2DegreeFactor
|
|
83
|
+
}
|
|
84
|
+
const stackBlocks=(data, colors=getColors(data[0].length), barWidth, yStart=0, yEnd=this.data[0].length-1 )=>{
|
|
85
|
+
let xPos=0, yPos=0
|
|
86
|
+
const results=[]
|
|
87
|
+
for(let i= 0; i < data.length; i++) {
|
|
88
|
+
const row=data[i]
|
|
89
|
+
xPos=Math.floor((i+0.5)*barWidth);
|
|
90
|
+
yPos=Math.sum(...row)
|
|
91
|
+
row.map((y,i)=>({action:"rect",fill:colors[j],x:xPos,y:yPos,width:barWidth,height:y})
|
|
92
|
+
for(let j=0; j<row.length; j++) {
|
|
93
|
+
if (row[j]==null) continue
|
|
94
|
+
const y=row[j]
|
|
95
|
+
results.push({action:"rect",fill:colors[j],x:xPos,y:yPos,width:barWidth,height:y})
|
|
96
|
+
yPos+=y;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return results
|
|
100
|
+
}
|
|
101
|
+
const getPushline=(data,j,tickIncrement,barWidth)=>{
|
|
102
|
+
const points = [];
|
|
103
|
+
for(let i= 0; i <data.length; i++) {
|
|
104
|
+
const row=data[i]
|
|
105
|
+
if(row[j]==null) continue;
|
|
106
|
+
xPos=Math.floor(i*tickIncrement)+(j-1)*barWidth;
|
|
107
|
+
points=[xPos,row[j]]
|
|
108
|
+
}
|
|
109
|
+
return {action:"polyline",points:points}
|
|
110
|
+
}
|
|
111
|
+
const getCircleTicks=(radius,degrees=6,strokeWidth=1,size=2)=>{
|
|
112
|
+
const ri=radius-size/2, ro=radius+size/2
|
|
113
|
+
return {action:"path",d:circle.basePoints(degrees).reduce((a,c)=>" M "+ri*c[0]+","+ri*c[1]+" L "+ro*c[0]+","+ro*c[1],"")}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function chart() {
|
|
117
|
+
Object.assign(this,{
|
|
118
|
+
bubbleRatio:0.2,
|
|
119
|
+
type:"line",
|
|
120
|
+
width:100,
|
|
121
|
+
height:100,
|
|
122
|
+
colour:"black",
|
|
123
|
+
disableLeftMenu:false,
|
|
124
|
+
grouping:null,
|
|
125
|
+
highlight:'',
|
|
126
|
+
legendDisplay:'show',
|
|
127
|
+
legendPositionX:60,
|
|
128
|
+
legendPositionY:0,
|
|
129
|
+
lineWidth:1,
|
|
130
|
+
outline:'black',
|
|
131
|
+
showOptions:true,
|
|
132
|
+
showPoints:false,
|
|
133
|
+
slices:'row',
|
|
134
|
+
tickIncrement:5
|
|
135
|
+
})
|
|
136
|
+
this.sliceByRow=this.slices=='row'
|
|
137
|
+
this.setAxis("x").setAxis("y").setAxis("z")
|
|
138
|
+
if(this.table == null) throw Error("Cannot render chart as table not specified")
|
|
139
|
+
this.processProperties();
|
|
140
|
+
}
|
|
141
|
+
chart.prototype.setAxis= function(dimension){
|
|
142
|
+
const axis=this.axis[dimension]
|
|
143
|
+
axis={columns:null,position:0,bound:{upper:null,lower:null}}
|
|
144
|
+
axis.columnArray=axis.columns.toLowerCase().split(',')
|
|
145
|
+
if(axis.columnArray.length==0) throw axis+" axis has no valid values for the database version, "+axis+"Axis: "+axis.columnArray;
|
|
146
|
+
if(axis.bound.upper!=null) axis.bound.upper=parseInt(axis.bound.upper,10);
|
|
147
|
+
if(axis.bound.lower!=null) axis.bound.lower=parseInt(axis.bound.lower,10);
|
|
148
|
+
axis.normaliser=1
|
|
149
|
+
return this
|
|
150
|
+
}
|
|
151
|
+
chart.prototype.errorFunction=function(msg){
|
|
152
|
+
throw typeof msg == "string"? Error(msg) : msg
|
|
153
|
+
}
|
|
154
|
+
chart.prototype.getPies=function (sliceByRow=this.sliceByRow,height=this.height) {
|
|
155
|
+
let xPos=0, yPos=0,pies=[]
|
|
156
|
+
const piesCount=(sliceByRow?this.columnIndexDetails.y.size+1:this.data.length)
|
|
157
|
+
const piesPerRow=Math.ceil(Math.sqrt(piesCount))
|
|
158
|
+
for(let i=(sliceByRow?0:0); i < piesCount; i++) {
|
|
159
|
+
pies.push(this.drawPie(xPos,yPos,this.height,i))
|
|
160
|
+
if(i%piesPerRow) continue
|
|
161
|
+
xPos=0;
|
|
162
|
+
yPos+=this.height;
|
|
163
|
+
}
|
|
164
|
+
return pies
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
chart.prototype.getPie=function(radius,i,dataset,sliceByRow=this.sliceByRow,label=isSliceRow)?this.label[i]:dataset[i][this.columnIndex[0]]) {
|
|
168
|
+
const circumference=radius * Math.PI+2
|
|
169
|
+
const total=sliceByRow?dataset.reduce((a,row)=>a+row[i],0):dataset[i].reduce((a,c)=>a+c,0)
|
|
170
|
+
if (total==0) return []
|
|
171
|
+
let angleStart=0, angleEnd=0, slices=[]
|
|
172
|
+
const unitDegreesRatio = 2 * Math.PI/total;
|
|
173
|
+
for(let row= (sliceByRow?0:1); row < arrayLength; row++) {
|
|
174
|
+
const value=(sliceByRow?this.data[row][i]:this.data[i][row])
|
|
175
|
+
if(value==null) continue;
|
|
176
|
+
angleStart=angleEnd;
|
|
177
|
+
angleEnd+=unitDegreesRatio*value;
|
|
178
|
+
const dash=(circumference*ratio)
|
|
179
|
+
const gap=circumference-dash
|
|
180
|
+
slices.push({action:"circle",fill:"transparent",cx:radius,cy:radius,r:radius,stroke:this.colour[row],"stroke-width":radius,
|
|
181
|
+
"stroke-dasharray": dash+","+gap,
|
|
182
|
+
transform:{rotate:{angle:angleStart,x:this.radius,y:this.radius}}}
|
|
183
|
+
)
|
|
184
|
+
slices.push({action:"text",x:x,y:y+10,children:[label]})
|
|
185
|
+
}
|
|
186
|
+
return {action:"g",children:[slices]}
|
|
187
|
+
}
|
|
188
|
+
chart.prototype.checkExists=function(value,errorMessage="value is null",error=this.errorFunction) {
|
|
189
|
+
if(value==null) error(errorMessage)
|
|
190
|
+
}
|
|
191
|
+
chart.prototype.processProperties=function(error=(msg)=>{throw Error(msg)}) {
|
|
192
|
+
if(this.grouping!=null) this.grouping = this.grouping.toUpperCase();
|
|
193
|
+
else if(this.baseTableData.primaryKeys.length>0)
|
|
194
|
+
this.grouping = this.baseTableData.primaryKeys.toString();
|
|
195
|
+
if(!['row',"column"].includes(this.slices)) error("parameter slices can only equal row or column")
|
|
196
|
+
this.sliceByRow=this.slices=="row"
|
|
197
|
+
if(['bubble','bubbleandline'].includes(this.type))
|
|
198
|
+
this.checkExists(this.axis.z.columns,'Missing z axis columns for '+this.type,error);
|
|
199
|
+
if(['bubble','bubbleandline','line','pushline','setline'].includes(this.type))
|
|
200
|
+
this.checkExists(this.axis.x.columns,'Missing x axis columns for '+this.type,error);
|
|
201
|
+
if(['bubble','bubbleandline','line','pushline','setline','stack','bar','pie','events'].includes(this.type))
|
|
202
|
+
this.checkExists(this.axis.y.columns,'Missing y axis columns for '+this.type,error);
|
|
203
|
+
if(['pie'].includes(this.type)) {
|
|
204
|
+
this.grouping=null;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
chart.prototype.setParameter=function(name,value) {
|
|
208
|
+
this[name]=value
|
|
209
|
+
return this
|
|
210
|
+
}
|
|
211
|
+
chart.prototype.getMenuOption=function(label,value,property){
|
|
212
|
+
return '<tr><td>'+label+'</td><td>'
|
|
213
|
+
+'<input type="button" value="'+value+'" onclick="this.value='+this.callBackText+'.setParameter(\\\''+property+'\\\',this.value)"/>'
|
|
214
|
+
+'</td></tr>';
|
|
215
|
+
}
|
|
216
|
+
chart.prototype.setMenuOptions=function(menuArray) {
|
|
217
|
+
if(!this.showOptions) return null;
|
|
218
|
+
if(menuArray==null) menuArray=[];
|
|
219
|
+
if(this.optionsDialog==null) {
|
|
220
|
+
this.optionsDialog = new floatingPanel(this.elementUniqueID + '_optionsDialog', 'RAW', "", this.elementUniqueID + '_optionsDialog_button', false, false);
|
|
221
|
+
this.parentPanel.registerNestedObject(this.elementUniqueID + '_optionsDialog', this.optionsDialog);
|
|
222
|
+
}
|
|
223
|
+
const thisObject = this;
|
|
224
|
+
if(!this.disableLeftMenu)
|
|
225
|
+
if(this.baseTableData.localLeftMenu!=null)
|
|
226
|
+
menuArray=menuArray.concat(this.baseTableData.localLeftMenu);
|
|
227
|
+
options='';
|
|
228
|
+
switch (this.type) {
|
|
229
|
+
case 'pie':
|
|
230
|
+
options+=this.getMenuOption('Slice',(this.slices=='row'?'column':'row'),'slices')
|
|
231
|
+
break;
|
|
232
|
+
case 'bubble':
|
|
233
|
+
case 'bubbleandline':
|
|
234
|
+
case 'line':
|
|
235
|
+
break;
|
|
236
|
+
case 'pushline':
|
|
237
|
+
case 'setline':
|
|
238
|
+
options+=this.getMenuOption('Chart Type',this.type=='setline'?'pushline':'setline'),'type')
|
|
239
|
+
break;
|
|
240
|
+
case 'bar':
|
|
241
|
+
case 'stack':
|
|
242
|
+
options+=this.getMenuOption('Chart Type',(this.type=='bar'?'stack':'bar'),'type')
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
options+=this.getMenuOption('Legend',(this.legendDisplay=="hide"?"show":"hide"),'legendDisplay')
|
|
246
|
+
+'<tr><td>Max. rows</td><td>'
|
|
247
|
+
+' <input type="text" value="'+this.baseTableData.maxResultsToFetch+'" onchange="'+this.callBackText+'.setParameter('+"'baseTableData.maxResultsToFetch'"+',this.value)"/>'
|
|
248
|
+
+'</td></tr>';
|
|
249
|
+
this.optionsDialog.draw();
|
|
250
|
+
this.optionsDialog.setContent('<table>'+options+this.getMetricColumnsOptions()+'</table>','Options', null, null, null)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
chart.prototype.getBar=function (tickIncrement=this.tickIncrement,barWidth=Math.floor(this.tickIncrement/this.columnIndexDetails.y.size)) {
|
|
254
|
+
const group=[]
|
|
255
|
+
let xPos=0, yPos=0
|
|
256
|
+
for(let i= 0; i < this.data.length; i++) {
|
|
257
|
+
const row=this.data[i]
|
|
258
|
+
for(let j=0; j<this.table.data.length; j++) {
|
|
259
|
+
if (row[j]==null) continue;
|
|
260
|
+
xPos=Math.floor((i+0.5)*tickIncrement)+(j-1)*barWidth;
|
|
261
|
+
yPos=row[j]
|
|
262
|
+
group.push({action:"rect", x:xPos,y:yPos,width:this.barWidth,height:this.height-yPos,fill:this.colour[j],stroke:this.outline})
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return result
|
|
266
|
+
}
|
|
267
|
+
chart.prototype.getBubble=function () {
|
|
268
|
+
const zDetails=this.axis.z;
|
|
269
|
+
const results=[]
|
|
270
|
+
for( let i= 0; i < this.data.length; i++) {
|
|
271
|
+
for( let j=0; j<this.data.length; j++) {
|
|
272
|
+
const row = this.data[i]
|
|
273
|
+
const zData = zDetails.dataStore
|
|
274
|
+
const y=row[j];
|
|
275
|
+
if(y==null || isNaN(y)) continue
|
|
276
|
+
xPos=row[0]
|
|
277
|
+
radius=Math.abs(zData[i][j]/2);
|
|
278
|
+
if(radius==0) continue
|
|
279
|
+
results.push({action:"circle",fill:this.colour[j],cx:xPos,cy:y,r:radius,stroke:this.outline,"stroke-width":1, opacity:0.5})
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return results
|
|
283
|
+
}
|
|
284
|
+
chart.prototype.getBubbleAndLine=function () {
|
|
285
|
+
return [this.getLine(),this.getBubble()]
|
|
286
|
+
}
|
|
287
|
+
chart.prototype.getEvents=function(data) {
|
|
288
|
+
let dataX, plotX, lines=[]
|
|
289
|
+
for(let j=0 ; j<data.length; j++) {
|
|
290
|
+
dataX=data[j][0];
|
|
291
|
+
if (dataX==null || isNaN(dataX) ) continue;
|
|
292
|
+
lines.push({action:"line",x1:dataX,y1:0,x1:dataX,y1:this.height,stroke:"red","stroke-width":this.lineWidth,"stroke-opacity":0.8})
|
|
293
|
+
}
|
|
294
|
+
return lines
|
|
295
|
+
}
|
|
296
|
+
chart.prototype.getLine=function () {
|
|
297
|
+
let errors,points;
|
|
298
|
+
for(let j=0; j<this.data.length; j++)
|
|
299
|
+
try {
|
|
300
|
+
this.plot(j, 1, this.data)
|
|
301
|
+
} catch(e) {
|
|
302
|
+
errors += 'error plotting '+ this.label[i] + ' ' +e +'\n';
|
|
303
|
+
}
|
|
304
|
+
if(errors) this.errorFunction(errors)
|
|
305
|
+
return {action:'polyline',points:"100,100 150,25 150,75 200,0",fill:"none",stroke="red","stroke-width":this.lineWidth,"stroke-opacity":0.8}
|
|
306
|
+
},
|
|
307
|
+
drawChart_pushline: function() {
|
|
308
|
+
const errors=[],result="",yDetails=this.columnIndexDetails.y
|
|
309
|
+
this.barWidth=Math.floor(this.tickIncrement/yDetailssize);
|
|
310
|
+
for(let j=yDetails.start; j<yDetails.end; j++)
|
|
311
|
+
try {
|
|
312
|
+
result+=this.plotSet(j,this.data,this.colour[j], this.barWidth);
|
|
313
|
+
} catch(e) {
|
|
314
|
+
errors += 'error plotting '+ this.label[i] + ' ' +e +'\n';
|
|
315
|
+
}
|
|
316
|
+
if (errors.length) throw errors;
|
|
317
|
+
},
|
|
318
|
+
drawChart_setline: function () {return this.drawChart_pushline();},
|
|
319
|
+
drawChart_stack: function(data,tickIncrement) {
|
|
320
|
+
const y=this.columnIndexDetails.y
|
|
321
|
+
return stackBlock(data, this.colour, tickIncrement)
|
|
322
|
+
},
|
|
323
|
+
chart.prototype.getChart=function () {
|
|
324
|
+
let errors;
|
|
325
|
+
const colourCnt = this.type=='pie'&& this.sliceByRow? ? this.data.length : this.columnIndexDetails.y.size+1
|
|
326
|
+
if(colourCnt>this.colour.length)
|
|
327
|
+
this.color=getColors(colourPallet.length/colourCnt)
|
|
328
|
+
this.ctx.clearRect(0,0,this.width,this.height);
|
|
329
|
+
this.ctx.strokeStyle = "black";
|
|
330
|
+
if(['bar','bubble','bubbleandline','line','pushline','setline','stack'].includes[this.type])
|
|
331
|
+
this.drawLineAxis();
|
|
332
|
+
try { this["drawChart_"+this.type]();} catch(e) {throw "drawing " + this.type + "\n" + e.toString(); };
|
|
333
|
+
|
|
334
|
+
while (this.legend.rows.length> 0) this.legend.deleteRow(0);
|
|
335
|
+
switch (this.type) {
|
|
336
|
+
case 'pie':
|
|
337
|
+
if(this.sliceByRow) {
|
|
338
|
+
for(let i = 1; i < this.data.length; i++)
|
|
339
|
+
this.legend+= "<tr><a style='color:" + this.colour[i] + "' >" + this.data[i][0] + "</a></tr>";
|
|
340
|
+
} else
|
|
341
|
+
for(let i=0; i<this.label.length; i++)
|
|
342
|
+
this.legend+="<tr><a style='color:" + this.colour[i] + "' >" + this.label[i] + "</a></tr>";
|
|
343
|
+
break;
|
|
344
|
+
default:
|
|
345
|
+
this.legend+="x axis: " + this.label[0];
|
|
346
|
+
for(var i=0; i<this.table.data.length; i++) {
|
|
347
|
+
this.legend.+= "<tr><a style='color:" + this.colour[i] + "' >" + this.label[i]
|
|
348
|
+
+ (this.zAxis?" z is " +this.axis.z.label[i]:"")
|
|
349
|
+
+ "</a></tr>";
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
},
|
|
353
|
+
const coords={
|
|
354
|
+
events:(chart,data,xPos,yPos)=>{
|
|
355
|
+
const xPlot=xScaleRange(xPos,chart.xRatio)
|
|
356
|
+
for(let i=0; i<data.length;i++) {
|
|
357
|
+
const v=data[i][0]
|
|
358
|
+
if (v< xPlot.min || v> xPlot.max) continue;
|
|
359
|
+
let details="event:";
|
|
360
|
+
for(let i= 0; i < this.table.data.length; i++)
|
|
361
|
+
details+=" "+this.dataToString(i,v>xPlot.max);
|
|
362
|
+
XYRow.insertCell(-1).innerHTML=details;
|
|
363
|
+
var XYRow=chart.detailXY.insertRow(-1);
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
bubble:(chart,data,xPos,yPos)=>{
|
|
367
|
+
const xPlot=xScaleRange(xPos,chart.xRatio)
|
|
368
|
+
const yPlot=yScaleRange(yPos,chart.yRatio)
|
|
369
|
+
const text=[{action:'text',children:["x: "+this.dataToString(0,xPlot.value) + " "+"y: "+this.dataToString(1,yPlot.value)]}]
|
|
370
|
+
const zText=chart.zAxis?
|
|
371
|
+
(chart,row,colum)=>{
|
|
372
|
+
const zDetails=chart.columnIndexDetails.z
|
|
373
|
+
const zData = zDetails.dataStore
|
|
374
|
+
return "z: "+chart.dataToString(i,zData[row][column]);
|
|
375
|
+
}:
|
|
376
|
+
()=>""
|
|
377
|
+
for(let row= 0; row < this.data.length; row++) {
|
|
378
|
+
const dataRow=data[row]
|
|
379
|
+
const x=dataRow[0]
|
|
380
|
+
const xText=" x: "+this.dataToString(0,x)
|
|
381
|
+
if (v< xPlot.min || v> xPlot.max) continue;
|
|
382
|
+
for(let i= 0; i < this.table.data.length; i++) {
|
|
383
|
+
const y=this.dataToString(i,dataRow[i])
|
|
384
|
+
if(isNaN(y) || y< yPlot.min || y> yPlot.max) continue;
|
|
385
|
+
text.push({action:'text',children:[chart.label[i]+xText+" y: "+y+zText(chart,row,i)]})
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
coords.bubbleandline=coords.bubble
|
|
391
|
+
coords.line=coords.bubble
|
|
392
|
+
function getDisplay(chart,xPos,yPos)
|
|
393
|
+
canvasCoordsDetails: function (chart,xPos,yPos) {
|
|
394
|
+
var XYRow=chart.detailXY.insertRow(-1);
|
|
395
|
+
switch (this.type) {
|
|
396
|
+
case 'bubble':
|
|
397
|
+
case 'bubbleandline':
|
|
398
|
+
case 'line':
|
|
399
|
+
case 'pushline':
|
|
400
|
+
case 'setline':
|
|
401
|
+
break;
|
|
402
|
+
case 'bar':
|
|
403
|
+
case 'stack':
|
|
404
|
+
var x=Math.floor((xPos)/this.tickIncrement-0.5);
|
|
405
|
+
XYRow.insertCell(-1).innerHTML="x:";
|
|
406
|
+
switch (this.dataType[0]) {
|
|
407
|
+
case 'timestamp' :
|
|
408
|
+
case 'date' :
|
|
409
|
+
case 'datetime' :
|
|
410
|
+
XYRow.insertCell(-1).innerHTML=this.dataToString(0,(xPos -this.xOffset)/this.xRatio);
|
|
411
|
+
break;
|
|
412
|
+
default:
|
|
413
|
+
XYRow.insertCell(-1).innerHTML=this.data[x][0];
|
|
414
|
+
}
|
|
415
|
+
XYRow=chart.detailXY.insertRow(-1);
|
|
416
|
+
XYRow.insertCell(-1).innerHTML= "y:";
|
|
417
|
+
XYRow.insertCell(-1).innerHTML=this.dataToString(1,(this.yOffset - yPos)/this.yRatio);
|
|
418
|
+
break;
|
|
419
|
+
default:
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
chart.prototype.plotSet=function (y,data,colour,tickIncrement,barWidth) {
|
|
424
|
+
const points=[]
|
|
425
|
+
const group=[]
|
|
426
|
+
for(let i= 0; i < data.length; i++) {
|
|
427
|
+
const dataiy=data[i][y]
|
|
428
|
+
if(dataiy==null) continue;
|
|
429
|
+
const xPos=Math.floor(i*this.tickIncrement)+(y-1)*barWidth
|
|
430
|
+
points.push([xPos,dataiy])
|
|
431
|
+
}
|
|
432
|
+
group.push({action:"polyline",points:points})
|
|
433
|
+
return {action:"g",stroke:color,children:[group,this.getPoints(points)]}
|
|
434
|
+
},
|
|
435
|
+
chart.prototype.getPoints=function(points,call=(x,y)=>({action:"circle",cx:x,cy:y,r:3})) {
|
|
436
|
+
if(!this.showPoints) return ""
|
|
437
|
+
return points.reduce((a,p)=>{
|
|
438
|
+
a.push(call(p.x,py))
|
|
439
|
+
return a
|
|
440
|
+
},[])
|
|
441
|
+
}
|
|
442
|
+
chart.prototype.getPointsRect=function(points,call=(x,y)=>({action:"rect",x:x-3,y:y-3,height:6,width:6})) {
|
|
443
|
+
this.getPoints(points,call)
|
|
444
|
+
}
|
|
445
|
+
plot: function (y, offset, data) {
|
|
446
|
+
if(data.length==0) return;
|
|
447
|
+
const points=[]
|
|
448
|
+
const row0=this.data[0]
|
|
449
|
+
if(data.length==1 || this.highlight=='first') {
|
|
450
|
+
const dataX=row0[0];
|
|
451
|
+
const dataY=row0[y];
|
|
452
|
+
if (dataX==null || dataY==null) return;
|
|
453
|
+
points=[dataX,dataY]
|
|
454
|
+
if(data.length==1) return;
|
|
455
|
+
}
|
|
456
|
+
result=this.showPoints?this.getPoints(points):this.getPointsRect(points)
|
|
457
|
+
const colour=this.colour[y * offset]
|
|
458
|
+
return {action:"g",stroke:colour,children:result}
|
|
459
|
+
var errors;
|
|
460
|
+
let linePointCnt=0;
|
|
461
|
+
const rowPoints=[]
|
|
462
|
+
for(let row=0; row < data.length; row++) {
|
|
463
|
+
try {
|
|
464
|
+
const rowData=this.data[row]
|
|
465
|
+
const dataX=rowData[0];
|
|
466
|
+
const dataY=rowData[y];
|
|
467
|
+
if (dataX==null || dataY==null || isNaN(dataX) || isNaN(dataY)) {
|
|
468
|
+
if(linePointCnt>0) {
|
|
469
|
+
if(linePointCnt==1 && !this.showPoints)
|
|
470
|
+
rowPoints.push([this.data[row-1][0],data[row-1][y]])
|
|
471
|
+
linePointCnt=0;
|
|
472
|
+
}
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
plotX=dataX
|
|
476
|
+
plotY=dataY
|
|
477
|
+
if(++linePointCnt>1) {
|
|
478
|
+
this.ctx.lineTo(plotX,plotY);
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
this.getPoints(points)
|
|
482
|
+
} catch (e) {
|
|
483
|
+
errors += " x: " + dataX + " ( " + plotX + " ) " + " y: " + dataY + " ( " + plotY + " ) " + "\n" + e + "\n";
|
|
484
|
+
linePointCnt=0;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if(linePointCnt>0) {
|
|
488
|
+
if(( linePointCnt==1 || this.highlight=='last' ) && !this.showPoints)
|
|
489
|
+
rowPoints.push([dataX,dataY])
|
|
490
|
+
}
|
|
491
|
+
this.getPointsRect(rowPoints)
|
|
492
|
+
if(this.showPoints) {
|
|
493
|
+
const points=[]
|
|
494
|
+
for(let row= 0; row < data.length; row++) {
|
|
495
|
+
const dataRow=data[row]
|
|
496
|
+
const dataY=dataRow[y]
|
|
497
|
+
if(dataY==null) continue;
|
|
498
|
+
points.push([dataRow[0],dataY])
|
|
499
|
+
}
|
|
500
|
+
this.getPoints(points)
|
|
501
|
+
}
|
|
502
|
+
if (errors!=null) throw errors;
|
|
503
|
+
},
|
|
504
|
+
dataToString: function(i,value) {
|
|
505
|
+
return Format.toString(this.dataType[i],value)
|
|
506
|
+
}
|
|
507
|
+
drawLineAxis: function() {
|
|
508
|
+
x+-0.5
|
|
509
|
+
const ticks=[[0,this.height]]
|
|
510
|
+
|
|
511
|
+
return {action:"g",id:"axis",stroke:"black","stroke-width":this.axis.X.LineWidth,children:result}
|
|
512
|
+
|
|
513
|
+
{action:"line",x1:0,y1:this.height,x2:0,y2:0}
|
|
514
|
+
switch (this.type) {
|
|
515
|
+
case 'bubble':
|
|
516
|
+
case 'bubbleandline':
|
|
517
|
+
case 'line':
|
|
518
|
+
if (this.xTicks.length>1) {
|
|
519
|
+
this.ctx.textAlign='left';
|
|
520
|
+
this.ctx.fillText(Format.toString(this.dataType[0],parseInt(this.xTicks[0])),0,this.height-this.axisOffset/2);
|
|
521
|
+
}
|
|
522
|
+
for(let i = 0; i< this.xTicks.length; i++) {
|
|
523
|
+
const pos=this.xTicks[i]
|
|
524
|
+
this.drawXAxisTick(pos);
|
|
525
|
+
this.ctx.textAlign='center';
|
|
526
|
+
this.ctx.fillText(this.dataToString(0,this.xTicks[i],true), pos,this.height-this.axisOffset/2);
|
|
527
|
+
}
|
|
528
|
+
break;
|
|
529
|
+
case 'bar':
|
|
530
|
+
case 'pushline':
|
|
531
|
+
case 'setline':
|
|
532
|
+
case 'stack':
|
|
533
|
+
if (this.xTicks.length==0) this.tickIncrement=this.xMax;
|
|
534
|
+
else this.tickIncrement=Math.floor(this.xMax/(this.xTicks.length+1));
|
|
535
|
+
var k = Math.ceil((50/this.tickIncrement));
|
|
536
|
+
for(let i = 0; i< this.xTicks.length; i+= k) {
|
|
537
|
+
|
|
538
|
+
var pos=this.axisOffset + this.tickIncrement*(i+1);
|
|
539
|
+
if(this.type == 'setline')
|
|
540
|
+
pos -= this.tickIncrement;
|
|
541
|
+
|
|
542
|
+
this.drawXAxisTick(pos);
|
|
543
|
+
this.ctx.textAlign='center';
|
|
544
|
+
this.ctx.fillText(Format.toString(this.dataType[0],this.data[i][0]), pos,this.height-this.axisOffset/2);
|
|
545
|
+
}
|
|
546
|
+
break;
|
|
547
|
+
default:
|
|
548
|
+
}
|
|
549
|
+
// y axis
|
|
550
|
+
this.ctx.beginPath();
|
|
551
|
+
this.ctx.moveTo(0,0.5);
|
|
552
|
+
this.ctx.lineTo(this.width-( this.type == 'setline' ? this.tickIncrement : 0 ),0.5);
|
|
553
|
+
this.ctx.stroke();
|
|
554
|
+
// y ticks
|
|
555
|
+
for(let i = 0; i< this.yTicks.length; i++) {
|
|
556
|
+
var pos=this.yTicks[i]
|
|
557
|
+
this.drawYAxisTick(pos);
|
|
558
|
+
this.ctx.textAlign='left';
|
|
559
|
+
this.ctx.fillText(this.dataToString(1,this.yTicks[i]),0 , pos+3);
|
|
560
|
+
}
|
|
561
|
+
},
|
|
562
|
+
|
|
563
|
+
calculateTicks: function(yMax,max,min,type,metric) {
|
|
564
|
+
let tickPosition = [];
|
|
565
|
+
if (max==null || yMax==null ) return tickPosition;
|
|
566
|
+
const tickCount = Math.floor(yMax/20);
|
|
567
|
+
if(tickCount < 1) tickCount = 1;
|
|
568
|
+
let i=0,k=0;
|
|
569
|
+
let duration=max-min;
|
|
570
|
+
if(column.isTime()) {
|
|
571
|
+
this.precision[metric]=1;
|
|
572
|
+
const minTS= new Date();
|
|
573
|
+
minTS.setTime(parseInt(min));
|
|
574
|
+
const maxTS= new Date();
|
|
575
|
+
maxTS.setTime(parseInt(max));
|
|
576
|
+
timePrecision(duration,{
|
|
577
|
+
minutes:()=>{
|
|
578
|
+
this.precision[metric]=60;
|
|
579
|
+
minTS.setMilliseconds(0);
|
|
580
|
+
minTS.setSeconds(0);
|
|
581
|
+
},
|
|
582
|
+
hours:()=>{
|
|
583
|
+
this.precision[metric]=360;
|
|
584
|
+
minTS.setMinutes(0);
|
|
585
|
+
},
|
|
586
|
+
days:()=>{
|
|
587
|
+
this.precision[metric]=1440;
|
|
588
|
+
minTS.setHours(0);
|
|
589
|
+
}
|
|
590
|
+
})
|
|
591
|
+
const getTicks=(max,min,step)
|
|
592
|
+
for(let i = minTS.getTime() ; i < max ; i=i+duration )
|
|
593
|
+
if (i>min && i<=max) tickPosition[k++]=i;
|
|
594
|
+
)
|
|
595
|
+
const hour=60*60*1000
|
|
596
|
+
duration=Math.floor(duration/60000); // minutes?
|
|
597
|
+
if (duration > 0) {
|
|
598
|
+
duration=Math.floor(duration/60); // hours?
|
|
599
|
+
if (duration > 0) {
|
|
600
|
+
duration=Math.floor(duration/24); // days?
|
|
601
|
+
if (duration > 0) {
|
|
602
|
+
for(let j=1; j<this.height ; j++)
|
|
603
|
+
if (duration<j*5+1) break;
|
|
604
|
+
duration=j*24*hour;
|
|
605
|
+
} else {
|
|
606
|
+
duration=Math.floor((max-min)/hour);
|
|
607
|
+
duration=(Math.floor(duration/5)+1)*hour;
|
|
608
|
+
}
|
|
609
|
+
} else {
|
|
610
|
+
duration=Math.floor((max-min)/(60*1000)); // minutes
|
|
611
|
+
if (duration<5)
|
|
612
|
+
duration=60*1000; minute==60000
|
|
613
|
+
else if (duration<25)
|
|
614
|
+
duration=5*60*1000;
|
|
615
|
+
else
|
|
616
|
+
duration=10*60*1000;
|
|
617
|
+
}
|
|
618
|
+
for(let i = minTS.getTime() ; i < max ; i=i+duration )
|
|
619
|
+
if (i>min && i<=max) tickPosition[k++]=i;
|
|
620
|
+
break;
|
|
621
|
+
}
|
|
622
|
+
duration=max-min;
|
|
623
|
+
} else {
|
|
624
|
+
|
|
625
|
+
}
|
|
626
|
+
if(column.isMetric()) {
|
|
627
|
+
const min = parseFloat(min)
|
|
628
|
+
const max = parseFloat(max)
|
|
629
|
+
const tickSpan = Math.floor((max-min)/tickCount);
|
|
630
|
+
var tickTotal = Math.min(tickCount+10, Math.floor(Number.MAX_VALUE / tickSpan));
|
|
631
|
+
for(let j=0;j<=tickTotal;j++) {
|
|
632
|
+
tickPosition[j]= min + j*tickSpan;
|
|
633
|
+
if( tickPosition[j] > max) break;
|
|
634
|
+
}
|
|
635
|
+
} else throw 'Unknown type: "'+type+'" for axis tick creation, check types are numeric, label: '+this.label[metric];
|
|
636
|
+
return tickPosition;
|
|
637
|
+
},
|
|
638
|
+
resize: function() {
|
|
639
|
+
switch (this.dataType[0]) {
|
|
640
|
+
case "int":
|
|
641
|
+
case "real":
|
|
642
|
+
case "number":
|
|
643
|
+
case "date":
|
|
644
|
+
case "time":
|
|
645
|
+
case "datetime":
|
|
646
|
+
case "timestamp":
|
|
647
|
+
if(this.columnIndex[0]==null) {
|
|
648
|
+
this.dataMin[0]=0.5;
|
|
649
|
+
this.dataMax[0]=1.5;
|
|
650
|
+
this.xMax = this.width
|
|
651
|
+
break; // xTicks built whilst building data
|
|
652
|
+
}
|
|
653
|
+
const dataMin0=this.dataMin[0]
|
|
654
|
+
const dataMax0=this.dataMax[0]
|
|
655
|
+
this.xMax = this.width-6;
|
|
656
|
+
if(dataMin0==dataMax0) {
|
|
657
|
+
this.dataMin[0]=dataMax0-1;
|
|
658
|
+
this.dataMax[0]=dataMax0+1;
|
|
659
|
+
}
|
|
660
|
+
this.xRatio = this.xMax/(this.dataMax[0]-this.dataMin[0]);
|
|
661
|
+
this.xOffset= this.dataMin[0]
|
|
662
|
+
this.xTicks = this.calculateTicks(this.dataMax[0],this.dataMin[0],this.dataType[0],0);
|
|
663
|
+
break;
|
|
664
|
+
default:
|
|
665
|
+
}
|
|
666
|
+
this.yDataMin = this.yAxisLowerBound;
|
|
667
|
+
this.yDataMax = this.yAxisUpperBound;
|
|
668
|
+
|
|
669
|
+
switch (this.type) {
|
|
670
|
+
case 'stack':
|
|
671
|
+
this.yDataMin= this.yDataMin ?? 0
|
|
672
|
+
this.yDataMax= this.yDataMax ?? 0
|
|
673
|
+
for( var i=0; i < this.table.data.length; i++) {
|
|
674
|
+
if (this.dataMax[i]==null) continue;
|
|
675
|
+
this.yDataMax+=this.dataMax[i];
|
|
676
|
+
}
|
|
677
|
+
if(this.yAxisUpperBound==null)
|
|
678
|
+
this.yDataMax=this.yDataMax*1.01;
|
|
679
|
+
break;
|
|
680
|
+
case 'bar':
|
|
681
|
+
case 'pushline':
|
|
682
|
+
case 'setline':
|
|
683
|
+
this.yDataMin= this.yDataMin??0
|
|
684
|
+
this.yDataMax= this.yDataMax??0;
|
|
685
|
+
for(var i =0; i < this.table.data.length; i++)
|
|
686
|
+
if (this.yDataMax<this.dataMax[i]) this.yDataMax=this.dataMax[i];
|
|
687
|
+
if(this.yAxisUpperBound==null)
|
|
688
|
+
this.yDataMax=this.yDataMax*1.1;
|
|
689
|
+
break;
|
|
690
|
+
default:
|
|
691
|
+
this.yDataMax= this.yDataMax??this.dataMax[1];
|
|
692
|
+
this.yDataMin= this.yDataMin??this.dataMin[1];
|
|
693
|
+
for(var i = 0; i < this.table.data.length; i++) {
|
|
694
|
+
if (this.yDataMax<this.dataMax[i]) this.yDataMax=this.dataMax[i];
|
|
695
|
+
if (this.yDataMin>this.dataMin[i]) this.yDataMin=this.dataMin[i];
|
|
696
|
+
}
|
|
697
|
+
this.yDataMax= this.yDataMax?? 1
|
|
698
|
+
this.yDataMin= this.yDataMin?? 0;
|
|
699
|
+
if(this.yDataMin==this.yDataMax)
|
|
700
|
+
if(this.yDataMax==0)
|
|
701
|
+
this.yDataMax=1;
|
|
702
|
+
if(this.yAxisLowerBound==null)
|
|
703
|
+
this.yDataMin=this.yDataMin*0.99;
|
|
704
|
+
if(this.yAxisUpperBound==null)
|
|
705
|
+
this.yDataMax=this.yDataMax*1.01;
|
|
706
|
+
break;
|
|
707
|
+
}
|
|
708
|
+
if(this.zAxis) {
|
|
709
|
+
var zDetails=this.axis.z;
|
|
710
|
+
this.zDataMax=null;
|
|
711
|
+
this.zDataMin=null;
|
|
712
|
+
this.zRatioColumns=[];
|
|
713
|
+
for (let i=0;i<zDetails.dataMin.length;i++) {
|
|
714
|
+
if(this.zDataMax<zDetails.dataMax[i]) this.zDataMax=zDetails.dataMax[i];
|
|
715
|
+
if(this.zDataMin>zDetails.dataMin[i]) this.zDataMin=zDetails.dataMin[i];
|
|
716
|
+
this.zRatioColumns[i]={};
|
|
717
|
+
var ratio=this.zRatioColumns[i];
|
|
718
|
+
rangeZ=zDetails.dataMax[i]-zDetails.dataMin[i];
|
|
719
|
+
if(rangeZ==0) {
|
|
720
|
+
rangeZ=zDetails.dataMin[i];
|
|
721
|
+
ratio.zRatioOffset=0;
|
|
722
|
+
} else
|
|
723
|
+
ratio.zRatioOffset=zDetails.dataMin[i];
|
|
724
|
+
ratio.zRatio=this.bubbleRatio*(Math.min(this.yMax,this.xMax)/rangeZ );
|
|
725
|
+
if(ratio.zRatio==Infinity) this.zRatio=this.bubbleRatio;
|
|
726
|
+
if (isNaN(ratio.zRatio)) throw "z ratio calculation error, charting width:"+ this.yMax + " max:"+ zDetails.dataMax[i] + " min:"+ zDetails.dataMin[i];
|
|
727
|
+
}
|
|
728
|
+
rangeZ=this.zDataMax-this.zDataMin;
|
|
729
|
+
if(rangeZ==0) {
|
|
730
|
+
rangeZ=this.zDataMin;
|
|
731
|
+
this.zRatioOffset==0;
|
|
732
|
+
} else
|
|
733
|
+
this.zRatioOffset=this.zDataMin;
|
|
734
|
+
this.zRatio=this.bubbleRatio*(Math.min(this.yMax,this.xMax)/rangeZ);
|
|
735
|
+
if(this.zRatio==Infinity) this.zRatio=this.bubbleRatio;
|
|
736
|
+
if (isNaN(this.zRatio)) throw "z ratio calculation error, charting width:"+ this.yMax + " max:"+ Math.max.apply(Math,this.axis.z.dataMax) + " min:"+ Math.min.apply(Math,this.axis.z.dataMin);
|
|
737
|
+
}
|
|
738
|
+
},
|
|
739
|
+
dataConversion: function(i,value) {
|
|
740
|
+
if (value==null) return null;
|
|
741
|
+
if(dataConversionFunc[this.dataType[i]]==null) return value;
|
|
742
|
+
try{
|
|
743
|
+
return dataConversionFunc[this.dataType[i]](value);
|
|
744
|
+
} catch(e) {
|
|
745
|
+
throw "data conversion error data type: " +dataType[i] + ' value: "'+ value +'"';
|
|
746
|
+
}
|
|
747
|
+
},
|
|
748
|
+
buildDataSet: function(dataset) {
|
|
749
|
+
if (dataset.length <1) this.errorFunction('No data to chart')
|
|
750
|
+
delete this.columnIndexDetails;
|
|
751
|
+
this.columnIndex=[];
|
|
752
|
+
this.data=[];
|
|
753
|
+
this.dataType=[];
|
|
754
|
+
this.dataMax=[];
|
|
755
|
+
this.dataMin=[];
|
|
756
|
+
this.group=[];
|
|
757
|
+
this.groupIndex;
|
|
758
|
+
this.groupingValue="";
|
|
759
|
+
this.groupValue=[];
|
|
760
|
+
this.precision=[];
|
|
761
|
+
this.label=[];
|
|
762
|
+
this.xTicks=[];
|
|
763
|
+
this.yTicks=[];
|
|
764
|
+
// this.table.setDelta([])
|
|
765
|
+
this.setColumnDetails(0,this.axis.x.columns);
|
|
766
|
+
let x = -1
|
|
767
|
+
switch (this.baseTableData.columnsInfo.type[this.columnIndex[0]]) {
|
|
768
|
+
case 'timestamp':
|
|
769
|
+
this.xNormliseFactor=this.deltaNormaliser*1000;
|
|
770
|
+
break;
|
|
771
|
+
default:
|
|
772
|
+
this.xNormliseFactor=this.deltaNormaliser;
|
|
773
|
+
}
|
|
774
|
+
this.axis.x.normaliser=1;
|
|
775
|
+
var xCol=this.columnIndex[0];
|
|
776
|
+
const group=table.list.group.names?
|
|
777
|
+
(row)=>this.setData(++x):
|
|
778
|
+
(row)=>{
|
|
779
|
+
if (x==-1) {
|
|
780
|
+
this.setData(++x);
|
|
781
|
+
} else if (dataset[row][xCol] != dataset[row-1][xCol]) {
|
|
782
|
+
this.setData(++x);
|
|
783
|
+
}
|
|
784
|
+
var newGroupingValue='';
|
|
785
|
+
for(let i = 0; i < this.groupIndex.length; i++)
|
|
786
|
+
newGroupingValue+=' '+ dataset[row][this.groupIndex[i]];
|
|
787
|
+
if (this.groupingValue!=newGroupingValue) {
|
|
788
|
+
this.groupingValue=newGroupingValue;
|
|
789
|
+
this.group[0]=this.groupingValue;
|
|
790
|
+
const i=this.groupValue.find(v=>this.groupingValue==v)
|
|
791
|
+
if (i<0) {
|
|
792
|
+
this.groupValue.push(this.groupingValue)
|
|
793
|
+
this.setColumnIndexDetails("y");
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
}
|
|
798
|
+
table.forEachRow(row=>{
|
|
799
|
+
group(row)
|
|
800
|
+
})
|
|
801
|
+
|
|
802
|
+
for(let row = 0; row < dataset.length; row++) {
|
|
803
|
+
if (this.grouping==null) {
|
|
804
|
+
this.setData(++x);
|
|
805
|
+
} else {
|
|
806
|
+
if (x==-1) {
|
|
807
|
+
this.setData(++x);
|
|
808
|
+
} else if (dataset[row][xCol] != dataset[row-1][xCol]) {
|
|
809
|
+
this.setData(++x);
|
|
810
|
+
}
|
|
811
|
+
let newGroupingValue='';
|
|
812
|
+
for( i = 0; i < this.groupIndex.length; i++)
|
|
813
|
+
newGroupingValue+=' '+ dataset[row][this.groupIndex[i]];
|
|
814
|
+
if (this.groupingValue!=newGroupingValue) {
|
|
815
|
+
this.groupingValue=newGroupingValue;
|
|
816
|
+
this.group[0]=this.groupingValue;
|
|
817
|
+
const i=this.groupValue.find(v=>this.groupingValue==v)
|
|
818
|
+
if (i <0) {
|
|
819
|
+
this.groupValue.push(this.groupingValue)
|
|
820
|
+
this.setColumnIndexDetails("y");
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
this.xTicks[x]=row;
|
|
825
|
+
for(var i = 0; i < this.columnIndex.length; i++) {
|
|
826
|
+
if (this.group[i]!=this.groupingValue) continue;
|
|
827
|
+
var value = (this.columnIndex[i]==null?x+1:this.dataConversion(i,dataset[row][this.columnIndex[i]]));
|
|
828
|
+
this.data[x][i] = value;
|
|
829
|
+
this.setMaxMin(this, i, value);
|
|
830
|
+
if(this.zAxis) this.setDataAxis('z',dataset[row],x,i);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
},
|
|
834
|
+
setMaxMin: (base,i,value)=>{
|
|
835
|
+
if (base.dataMin[i]==null) {
|
|
836
|
+
base.dataMin[i]=value;
|
|
837
|
+
base.dataMax[i]=value;
|
|
838
|
+
} else if (base.dataMin[i]>value)
|
|
839
|
+
base.dataMin[i]=value;
|
|
840
|
+
else if (base.dataMax[i]<value)
|
|
841
|
+
base.dataMax[i]=value;
|
|
842
|
+
},
|
|
843
|
+
setData: function(x) {
|
|
844
|
+
this.data[x]=[];
|
|
845
|
+
this.deltaData[x]=[];
|
|
846
|
+
},
|
|
847
|
+
setDataAxis: function(axis,row,x,i) {
|
|
848
|
+
var colDetails=this.columnIndexDetails[axis];
|
|
849
|
+
var value = (row==null?null:this.dataConversion(i,row[colDetails.columnIndex[i]]));
|
|
850
|
+
if(colDetails.dataStore[x]==null) {
|
|
851
|
+
colDetails.dataStore[x]=[];
|
|
852
|
+
colDetails.deltaData[x]=[];
|
|
853
|
+
}
|
|
854
|
+
colDetails.dataStore[x][i]=value;
|
|
855
|
+
this.setMaxMin(colDetails, i, value);
|
|
856
|
+
},
|
|
857
|
+
setColumnDetails: function(index,columnName) {
|
|
858
|
+
if(columnName==null) {
|
|
859
|
+
this.columnIndex[index]=null;
|
|
860
|
+
this.dataType[index]='int';
|
|
861
|
+
this.label[index]=this.groupingValue;
|
|
862
|
+
this.group[index]=this.groupingValue;
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
const i = this.findColumnIndex(columnName);
|
|
866
|
+
this.columnIndex[index]=i;
|
|
867
|
+
this.dataType[index]=this.baseTableData.columnsInfo.type[i];
|
|
868
|
+
this.label[index]=this.groupingValue + " " + this.table.getColumn(columnName).title
|
|
869
|
+
this.group[index]=this.groupingValue;
|
|
870
|
+
},
|
|
871
|
+
setColumnDetailsAxis: function(columnDetails,columnName) {
|
|
872
|
+
if(columnName==null) throw "Column name not specified"
|
|
873
|
+
const i = this.findColumnIndex(columnName);
|
|
874
|
+
const index=columnDetails.columnIndex.length;
|
|
875
|
+
columnDetails.columnIndex[index]=i;
|
|
876
|
+
columnDetails.dataType[index]=this.baseTableData.columnsInfo.type[i];
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
columnDetails.label[index]=this.groupingValue + " " + this.table.getColumn(columnName).title
|
|
881
|
+
columnDetails.group[index]=this.groupingValue;
|
|
882
|
+
},
|
|
883
|
+
setListProperty: function(select,property,list) {
|
|
884
|
+
this[property]=[];
|
|
885
|
+
const p=this[property]
|
|
886
|
+
for (let i = 0; i < select.options.length; i++)
|
|
887
|
+
if (select.options[i].selected)
|
|
888
|
+
p.push(select.options[i].value)
|
|
889
|
+
this[list]=p.join();
|
|
890
|
+
},
|
|
891
|
+
setMetrics: function(select) {
|
|
892
|
+
this.setListProperty(select,"ySeries","yAxis")
|
|
893
|
+
},
|
|
894
|
+
setGrouping: function(select) {
|
|
895
|
+
this.setListProperty(select,"groupingArray","grouping")
|
|
896
|
+
}
|
|
897
|
+
getMetricColumnsOptions=function() {
|
|
898
|
+
const option='<tr><td>Y Metrics</td><td>'
|
|
899
|
+
+this.table.getSelectHML(this.ySeries,this.callBackText+".setMetrics(this)",(column)=>column.isNumeric())
|
|
900
|
+
+'</td></tr>'
|
|
901
|
+
+'<tr><td>Grouping</td><td>'
|
|
902
|
+
+this.table.getSelectHML(this.grouping,this.callBackText+".setGrouping(this)",(column)=>column.type=="string")
|
|
903
|
+
+'</td></tr>'
|
|
904
|
+
return option
|
|
905
|
+
}
|
|
906
|
+
}
|