1const TrendLinesForFacetedCharts = { 2 allLayers: [], // [ { encodingName: { name, value } ... } ] (array of layer objects) 3 newSpecification: {}, 4 5 initSpecification: function() { 6 const self = this; 7 let newSpecification = {}; 8 9 newSpecification.data = {}; 10 newSpecification.data.values = []; 11 newSpecification.facet = {}; 12 newSpecification.spec = {}; 13 newSpecification.spec.layer = []; 14 15 self.initFacetSpecification(newSpecification); 16 self.newSpecification = newSpecification; 17 }, 18 initFacetSpecification: function(newSpecification) { 19 let splittingAttributes = TrendLines.splittingAttributes; 20 let rowIsOccupied = 'row' in splittingAttributes; 21 let columnIsOccupied = 'column' in splittingAttributes; 22 23 if (rowIsOccupied && !splittingAttributes.row.isQuantitativeBin) 24 newSpecification.facet.row = { 25 field: 'row', 26 type: 'nominal', 27 axis: { title: splittingAttributes.row.name } 28 }; 29 30 if (rowIsOccupied && splittingAttributes.row.isQuantitativeBin) 31 newSpecification.facet.row = { 32 field: 'row', 33 type: 'nominal', 34 axis: { title: splittingAttributes.row.name }, 35 sort: TrendLines.getSortedRanges(splittingAttributes.row) 36 }; 37 38 if (columnIsOccupied && !splittingAttributes.column.isQuantitativeBin) 39 newSpecification.facet.column = { 40 field: 'column', 41 type: 'nominal', 42 axis: { title: splittingAttributes.column.name } 43 }; 44 45 if (columnIsOccupied && splittingAttributes.column.isQuantitativeBin) 46 newSpecification.facet.column = { 47 field: 'column', 48 type: 'nominal', 49 axis: { title: splittingAttributes.column.name }, 50 sort: TrendLines.getSortedRanges(splittingAttributes.column) 51 }; 52 }, 53 addData: function() { 54 const self = this; 55 let data = []; 56 57 self.addScatterplotData(data); 58 self.addTrendLineData(data); 59 self.newSpecification.data.values = data; 60 }, 61 addScatterplotData: function(data) { 62 let xAxisAttribute = TrendLines.xAxisAttribute; 63 let yAxisAttribute = TrendLines.yAxisAttribute; 64 let splittingAttributes = TrendLines.splittingAttributes; 65 let nonSplittingAttributes = TrendLines.nonSplittingAttributes; 66 67 let isTooltipShelfEmpty = Shelf.tooltip.isEmpty(); 68 let tooltipShelfData = Shelf.tooltip.getCapsuleData(); 69 70 for (let i = 0; i < TrendLines.allGroups.length; i++) { 71 let currentGroup = TrendLines.allGroups[i]; 72 73 for (let j = 0; j < currentGroup.objects.length; j++) { 74 let currentRow = currentGroup.objects[j]; 75 let currentPointObject = {}; 76 77 // splitting 78 for (let encodingName in currentGroup.splittingAttributes) { 79 let currentSplittingAttribute = splittingAttributes[encodingName];
80 let currentGroupValue = currentGroup.splittingAttributes[encodingName].value; 81 82 if (currentSplittingAttribute.isQuantitativeBin) { 83 let currentBinIndex = currentGroupValue; 84 let currentLowerBound = currentSplittingAttribute.min + currentBinIndex * currentSplittingAttribute.binSize; 85 let currentUpperBound = currentLowerBound + currentSplittingAttribute.binSize; 86 87 if (currentLowerBound.countDecimals() > 2) currentLowerBound = currentLowerBound.toFixed(2); 88 if (currentUpperBound.countDecimals() > 2) currentUpperBound = currentUpperBound.toFixed(2); 89 currentPointObject[encodingName] = currentLowerBound + ' - ' + currentUpperBound; 90 } 91 92 if (!currentSplittingAttribute.isQuantitativeBin) 93 currentPointObject[encodingName] = currentGroupValue; 94 } 95 96 // non splitting 97 for (let encodingName in nonSplittingAttributes) 98 currentPointObject[nonSplittingAttributes[encodingName].name] = currentRow[nonSplittingAttributes[encodingName].name]; 99 100 // others 101 if (!isTooltipShelfEmpty) 102 currentPointObject[tooltipShelfData.attributeName] = currentRow[tooltipShelfData.attributeName]; 103 104 currentPointObject.x = currentRow[xAxisAttribute.name]; 105 currentPointObject.y = currentRow[yAxisAttribute.name]; 106 currentPointObject.objectType = 'point'; 107 data.push(currentPointObject); 108 } 109 } 110 }, 111 addTrendLineData: function(data) { 112 const self = this; 113 let splittingAttributes = TrendLines.splittingAttributes; 114 115 for (let i = 0; i < TrendLines.allGroups.length; i++) { 116 let currentGroup = TrendLines.allGroups[i]; 117 let currentPointsOnTrendLine = currentGroup.pointsOnTrendLine; 118 119 for (let j = 0; j < currentPointsOnTrendLine.length; j++) { 120 for (let encodingName in currentGroup.splittingAttributes) { 121 let currentSplittingAttribute = splittingAttributes[encodingName];
122 let currentGroupValue = currentGroup.splittingAttributes[encodingName].value; 123 124 if (currentSplittingAttribute.isQuantitativeBin) { 125 let currentBinIndex = currentGroupValue; 126 let currentLowerBound = currentSplittingAttribute.min + currentBinIndex * currentSplittingAttribute.binSize; 127 let currentUpperBound = currentLowerBound + currentSplittingAttribute.binSize; 128 129 if (currentLowerBound.countDecimals() > 2) currentLowerBound = currentLowerBound.toFixed(2); 130 if (currentUpperBound.countDecimals() > 2) currentUpperBound = currentUpperBound.toFixed(2); 131 currentPointsOnTrendLine[j][encodingName] = currentLowerBound + ' - ' + currentUpperBound; 132 currentPointsOnTrendLine[j].objectType = 'line'; 133 } 134 135 if (!currentSplittingAttribute.isQuantitativeBin) { 136 currentPointsOnTrendLine[j][encodingName] = currentGroupValue; 137 currentPointsOnTrendLine[j].objectType = 'line'; 138 } 139 } 140 141 data.push(currentPointsOnTrendLine[j]); 142 } 143 } 144 }, 145 findAllLayers: function() { 146 const self = this; 147 let splittingAttributes = TrendLines.splittingAttributes; 148 let splittingAttributeArray = []; 149 let numberOfLayers = 1; 150 let allLayers = []; 151 152 // init 153 for (let encodingName in splittingAttributes) 154 if (encodingName != 'row' && encodingName != 'column') 155 splittingAttributeArray.push(splittingAttributes[encodingName]); 156 157 for (let i = 0; i < splittingAttributeArray.length; i++) { 158 let currentSplittingAttribute = splittingAttributeArray[i]; 159 let currentMagicNumber = 1; 160 161 if (i > 0) 162 for (let j = 0; j < i; j++) 163 currentMagicNumber *= splittingAttributeArray[j].allGroups.length; 164 165 currentSplittingAttribute.magicNumber = currentMagicNumber; 166 numberOfLayers *= currentSplittingAttribute.allGroups.length; 167 } 168 169 for (let i = 0; i < numberOfLayers; i++) 170 allLayers.push({}); 171 172 // create allLayer 173 for (let i = 0; i < splittingAttributeArray.length; i++) { 174 let currentSplittingAttribute = splittingAttributeArray[i]; 175 let currentSplittingAttrGroupIndex = -1; 176 let currentSplittingAttrGroup = null; 177 let currentMagicNumber = currentSplittingAttribute.magicNumber; 178 179 for (let j = 0; j < numberOfLayers; j++) { 180 let attributeObject = {}; 181 182 if (j % currentMagicNumber == 0) { // get currentSplittingAttrGroup 183 currentSplittingAttrGroupIndex++; 184 currentSplittingAttrGroupIndex = currentSplittingAttrGroupIndex % currentSplittingAttribute.allGroups.length; 185 currentSplittingAttrGroup = currentSplittingAttribute.allGroups[currentSplittingAttrGroupIndex]; 186 } 187 188 attributeObject.name = currentSplittingAttribute.name; 189 attributeObject.value = currentSplittingAttrGroup; 190 allLayers[j][currentSplittingAttribute.encodingName] = attributeObject; 191 } 192 } 193 194 self.allLayers = allLayers; 195 }, 196 initScatterplotSpecification: function() { 197 const self = this; 198 let scatterplotSpecification = {}; 199 200 scatterplotSpecification.transform = [ { filter: { field: "objectType", equal: "point" } } ]; 201 scatterplotSpecification.mark = "point"; 202 scatterplotSpecification.encoding = {}; 203 204 self.initScatterplotXYSpec(scatterplotSpecification); 205 self.initScatterplotSplittingAttrSpec(scatterplotSpecification); 206 self.initScatterplotNonSplittingAttrSpec(scatterplotSpecification); 207 self.initScatterplotTooltipSpec(scatterplotSpecification); 208 self.newSpecification.spec.layer.push(scatterplotSpecification); 209 }, 210 initScatterplotXYSpec: function(scatterplotSpecification) { 211 let xAxisAttribute = TrendLines.xAxisAttribute; 212 let yAxisAttribute = TrendLines.yAxisAttribute; 213 214 scatterplotSpecification.encoding.x = { field: 'x', type: 'quantitative', axis: { title: xAxisAttribute.name } }; 215 scatterplotSpecification.encoding.y = { field: 'y', type: 'quantitative', axis: { title: yAxisAttribute.name } }; 216 217 if (xAxisAttribute.isTemporal) { 218 scatterplotSpecification.encoding.x.axis.format = 'd'; 219 scatterplotSpecification.encoding.x.scale = {}; 220 scatterplotSpecification.encoding.x.scale.domain = [ xAxisAttribute.min, xAxisAttribute.max ]; 221 } 222 223 if (yAxisAttribute.isTemporal) { 224 scatterplotSpecification.encoding.y.axis.format = 'd'; 225 scatterplotSpecification.encoding.y.scale = {}; 226 scatterplotSpecification.encoding.y.scale.domain = [ yAxisAttribute.min, yAxisAttribute.max ]; 227 } 228 }, 229 initScatterplotSplittingAttrSpec: function(scatterplotSpecification) { 230 const self = this; 231 let splittingAttributes = TrendLines.splittingAttributes; 232 233 for (let encodingName in splittingAttributes) 234 if (encodingName != 'row' && encodingName != 'column') { 235 let currentSplittingAttr = splittingAttributes[encodingName]; 236 let attributeName = currentSplittingAttr.name; 237 let categoryList = currentSplittingAttr.allGroups; 238 let colorScale = (encodingName == 'color') ? self.generateColourScale(attributeName, categoryList) : null; 239 let title = currentSplittingAttr.name; 240 241 if (!currentSplittingAttr.isQuantitativeBin && colorScale === null) 242 scatterplotSpecification.encoding[encodingName] = { 243 field: encodingName, 244 type: 'nominal', 245 axis: { title: title } 246 }; 247 248 if (!currentSplittingAttr.isQuantitativeBin && colorScale !== null) 249 scatterplotSpecification.encoding[encodingName] = { 250 field: encodingName, 251 type: 'nominal', 252 axis: { title: title }, 253 scale: colorScale 254 }; 255 256 if (currentSplittingAttr.isQuantitativeBin) 257 scatterplotSpecification.encoding[encodingName] = { 258 field: encodingName, 259 type: 'nominal', 260 axis: { title: title }, 261 sort: TrendLines.getSortedRanges(currentSplittingAttr) 262 }; 263 } 264 }, 265 initScatterplotNonSplittingAttrSpec: function(scatterplotSpecification) { 266 let nonSplittingAttributes = TrendLines.nonSplittingAttributes; 267 268 for (let encodingName in nonSplittingAttributes) { 269 let shelfName = (encodingName == 'color') ? 'colour' : encodingName; 270 VegaliteGenerator.setOneEncoding(shelfName, scatterplotSpecification); 271 } 272 }, 273 initScatterplotTooltipSpec: function(scatterplotSpecification) { 274 let isTooltipShelfEmpty = Shelf.tooltip.isEmpty(); 275 276 if (!isTooltipShelfEmpty) { 277 VegaliteGenerator.setOneEncoding('tooltip', scatterplotSpecification); 278 VegaliteGenerator.adjustTooltipEncoding(scatterplotSpecification); 279 } 280 }, 281 initTrendLineSpecifications: function(scatterplotSpecification) { 282 const self = this; 283 let splittingAttributes = TrendLines.splittingAttributes; 284 285 for (let i = 0; i < self.allLayers.length; i++) { 286 let currentLayerConfig = self.allLayers[i]; 287 let currentTrendLineSpecification = {}; 288 289 currentTrendLineSpecification.transform = null; 290 currentTrendLineSpecification.mark = "line"; 291 currentTrendLineSpecification.encoding = {}; 292 293 self.initTrendLineFilterSpec(currentTrendLineSpecification, currentLayerConfig); 294 self.initTrendLineXYSpec(currentTrendLineSpecification); 295 self.initTrendLineMarkSpec(currentTrendLineSpecification, currentLayerConfig); 296 self.initTrendLineTooltipSpec(currentTrendLineSpecification); 297 self.newSpecification.spec.layer.push(currentTrendLineSpecification); 298 } 299 }, 300 initTrendLineFilterSpec: function(trendLineSpecification, layerConfig) { 301 let splittingAttributes = TrendLines.splittingAttributes; 302 303 trendLineSpecification.transform = [ { filter: { field: "objectType", equal: "line" } } ]; 304 305 for (let encodingName in layerConfig) { 306 let currentGroupValue = layerConfig[encodingName].value; 307 let currentFilterObject = { filter: { field: encodingName, equal: currentGroupValue } }; 308 let currentSplittingAttribute = splittingAttributes[encodingName]; 309 310 if (currentSplittingAttribute.isQuantitativeBin) { 311 let currentBinIndex = currentGroupValue; 312 let currentLowerBound = currentSplittingAttribute.min + currentBinIndex * currentSplittingAttribute.binSize; 313 let currentUpperBound = currentLowerBound + currentSplittingAttribute.binSize; 314 315 if (currentLowerBound.countDecimals() > 2) currentLowerBound = currentLowerBound.toFixed(2); 316 if (currentUpperBound.countDecimals() > 2) currentUpperBound = currentUpperBound.toFixed(2); 317 currentFilterObject.filter.equal = currentLowerBound + ' - ' + currentUpperBound; 318 } 319 320 trendLineSpecification.transform.push(currentFilterObject); 321 } 322 }, 323 initTrendLineXYSpec: function(trendLineSpecification) { 324 trendLineSpecification.encoding.x = { field: 'x', type: 'quantitative' }; 325 trendLineSpecification.encoding.y = { field: 'y', type: 'quantitative' }; 326 }, 327 initTrendLineMarkSpec: function(trendLineSpecification, layerConfig) { 328 let splittingAttributes = TrendLines.splittingAttributes; 329 let colorShelfHasSplitAttr = splittingAttributes !== null && 'color' in splittingAttributes; 330 let currentMarkSpecification = { type: 'line' }; 331 332 if (colorShelfHasSplitAttr) { 333 let colourShelfAttribute = layerConfig.color.name; 334 let currentGroupColourValue = layerConfig.color.value; 335 let isAttrOriginallyNominal = colourShelfAttribute in Database.categoryToColourDictForEachNominalAttr; 336 let currentGroupColourCode = null; 337 338 if (isAttrOriginallyNominal) 339 currentGroupColourCode = Database.categoryToColourDictForEachNominalAttr[colourShelfAttribute][currentGroupColourValue]; 340 if (!isAttrOriginallyNominal) 341 currentGroupColourCode = TrendLines.categoryToColourDict[currentGroupColourValue]; 342 343 currentMarkSpecification.stroke = currentGroupColourCode; 344 } 345 346 trendLineSpecification.mark = currentMarkSpecification; 347 }, 348 initTrendLineTooltipSpec: function(trendLineSpecification) { 349 let splittingAttributes = TrendLines.splittingAttributes; 350 351 if (splittingAttributes === null) 352 trendLineSpecification.encoding.tooltip = { value: false }; 353 if (splittingAttributes !== null) 354 trendLineSpecification.encoding.tooltip = []; 355 356 for (let encodingName in splittingAttributes) { 357 let currentSplittingAttribute = splittingAttributes[encodingName]; 358 let newEncodingSpecification = { field: encodingName, type: 'nominal', axis: { title: currentSplittingAttribute.name } }; 359 trendLineSpecification.encoding.tooltip.push(newEncodingSpecification); 360 } 361 }, 362 modifySpecificationWidthAndHeight: function() { 363 const self = this; 364 let width = VegaliteGenerator.specification.width; 365 let height = VegaliteGenerator.specification.height; 366 367 self.newSpecification.spec.layer[0].width = width; 368 self.newSpecification.spec.layer[0].height = height; 369 }, 370 generateColourScale: function(attributeName, categoryList) { // assuming no missing values in categoryList 371 let isOriginallyNominal = (attributeName in Database.categoryToColourDictForEachNominalAttr); 372 let categoryToColourDict = isOriginallyNominal ? Database.categoryToColourDictForEachNominalAttr[attributeName] : null; 373 let colourCodeList = []; 374
375 if (!isOriginallyNominal) 376 return null; 377 378 for (let i = 0; i < categoryList.length; i++) { 379 let currentCategory = categoryList[i]; 380 let currentColourCode = categoryToColourDict[currentCategory]; 381 colourCodeList.push(currentColourCode); 382 } 383 return { 384 domain: categoryList, 385 range: colourCodeList 386 }; 387 } 388}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.