@@ -240,7 +240,13 @@ export function baseTableToExtTable(table: any[], derivedFields: FieldItem[], al
240
240
}
241
241
242
242
243
- export const instantiateVegaTemplate = ( chartType : string , encodingMap : { [ key in Channel ] : EncodingItem ; } , allFields : FieldItem [ ] , workingTable : any [ ] ) => {
243
+ export const assembleVegaChart = (
244
+ chartType : string ,
245
+ encodingMap : { [ key in Channel ] : EncodingItem ; } ,
246
+ conceptShelfItems : FieldItem [ ] ,
247
+ workingTable : any [ ] ,
248
+ maxNominalValues : number = 68
249
+ ) => {
244
250
245
251
if ( chartType == "Table" ) {
246
252
return [ "Table" , undefined ] ;
@@ -250,7 +256,6 @@ export const instantiateVegaTemplate = (chartType: string, encodingMap: { [key i
250
256
//console.log(chartTemplate);
251
257
252
258
let vgObj = JSON . parse ( JSON . stringify ( chartTemplate . template ) ) ;
253
- const baseTableSchemaObj : any = { } ;
254
259
255
260
for ( const [ channel , encoding ] of Object . entries ( encodingMap ) ) {
256
261
@@ -260,30 +265,8 @@ export const instantiateVegaTemplate = (chartType: string, encodingMap: { [key i
260
265
encodingObj [ "scale" ] = { "type" : "sqrt" , "zero" : true } ;
261
266
}
262
267
263
- const field = encoding . fieldID ? _ . find ( allFields , ( f ) => f . id === encoding . fieldID ) : undefined ;
268
+ const field = encoding . fieldID ? _ . find ( conceptShelfItems , ( f ) => f . id === encoding . fieldID ) : undefined ;
264
269
if ( field ) {
265
- //console.log(field)
266
- // the synthesizer only need to see base table schema
267
- let baseFields = ( field . source == "derived" ?
268
- ( field . transform as ConceptTransformation ) . parentIDs . map ( ( parentID ) => allFields . find ( ( f ) => f . id == parentID ) as FieldItem )
269
- : [ field ] ) ;
270
-
271
- for ( let baseField of baseFields ) {
272
- if ( Object . keys ( baseTableSchemaObj ) . includes ( baseField . name ) ) {
273
- continue ;
274
- }
275
- baseTableSchemaObj [ baseField . name ] = {
276
- channel,
277
- dtype : getDType ( baseField . type , workingTable . map ( r => r [ baseField . name ] ) ) ,
278
- name : baseField . name ,
279
- original : baseField . source == "original" ,
280
- // domain: {
281
- // values: [...new Set(baseField.domain.values)],
282
- // is_complete: baseField.domain.isComplete
283
- // },
284
- } ;
285
- }
286
-
287
270
// create the encoding
288
271
encodingObj [ "field" ] = field . name ;
289
272
encodingObj [ "type" ] = getDType ( field . type , workingTable . map ( r => r [ field . name ] ) ) ;
@@ -426,16 +409,8 @@ export const instantiateVegaTemplate = (chartType: string, encodingMap: { [key i
426
409
vgObj = chartTemplate . postProcessor ( vgObj , workingTable ) ;
427
410
}
428
411
429
- // console.log(JSON.stringify(vgObj))
430
-
431
- return [ vgObj , baseTableSchemaObj ] ;
432
- }
433
-
434
- export const assembleChart = ( chart : Chart , conceptShelfItems : FieldItem [ ] , dataValues : any [ ] ) => {
435
-
436
- let vgSpec : any = instantiateVegaTemplate ( chart . chartType , chart . encodingMap , conceptShelfItems , dataValues ) [ 0 ] ;
437
-
438
- let values = JSON . parse ( JSON . stringify ( dataValues ) ) ;
412
+ // this is the data that will be assembled into the vega chart
413
+ let values = JSON . parse ( JSON . stringify ( workingTable ) ) ;
439
414
values = values . map ( ( r : any ) => {
440
415
let keys = Object . keys ( r ) ;
441
416
let temporalKeys = keys . filter ( ( k : string ) => conceptShelfItems . some ( concept => concept . name == k && ( concept . type == "date" || concept . semanticType == "Year" ) ) ) ;
@@ -444,9 +419,75 @@ export const assembleChart = (chart: Chart, conceptShelfItems: FieldItem[], data
444
419
}
445
420
return r ;
446
421
} )
447
- return { ...vgSpec , data : { values : values } }
422
+
423
+ // Handle nominal axes with many entries
424
+ for ( const channel of [ 'x' , 'y' ] ) {
425
+ const encoding = vgObj . encoding ?. [ channel ] ;
426
+ if ( encoding ?. type === 'nominal' ) {
427
+ const fieldName = encoding . field ;
428
+ const uniqueValues = [ ...new Set ( workingTable . map ( r => r [ fieldName ] ) ) ] ;
429
+
430
+ if ( uniqueValues . length > maxNominalValues ) {
431
+ const oppositeChannel = channel === 'x' ? 'y' : 'x' ;
432
+ const oppositeEncoding = vgObj . encoding ?. [ oppositeChannel ] ;
433
+
434
+ let valuesToKeep : any [ ] ;
435
+ if ( oppositeEncoding ?. type === 'quantitative' ) {
436
+ // Sort by the quantitative field and take top maxNominalValues
437
+ const quantField = oppositeEncoding . field ;
438
+ valuesToKeep = uniqueValues
439
+ . map ( val => ( {
440
+ value : val ,
441
+ sum : workingTable
442
+ . filter ( r => r [ fieldName ] === val )
443
+ . reduce ( ( sum , r ) => sum + ( r [ quantField ] || 0 ) , 0 )
444
+ } ) )
445
+ . sort ( ( a , b ) => b . sum - a . sum )
446
+ . slice ( 0 , maxNominalValues )
447
+ . map ( v => v . value ) ;
448
+ } else {
449
+ // If no quantitative axis, just take first maxNominalValues
450
+ valuesToKeep = uniqueValues . slice ( 0 , maxNominalValues ) ;
451
+ }
452
+
453
+ // Filter the working table
454
+ const omittedCount = uniqueValues . length - maxNominalValues ;
455
+ const placeholder = `...${ omittedCount } items omitted` ;
456
+ values = values . filter ( ( row : any ) => valuesToKeep . includes ( row [ fieldName ] ) ) ;
457
+
458
+ // Add text formatting configuration
459
+ if ( ! encoding . axis ) {
460
+ encoding . axis = { } ;
461
+ }
462
+ encoding . axis . labelColor = {
463
+ condition : {
464
+ test : `datum.label == '${ placeholder } '` ,
465
+ value : "#999999"
466
+ } ,
467
+ value : "#000000" // default color for other labels
468
+ } ;
469
+ encoding . axis . labelFont = {
470
+ condition : {
471
+ test : `datum.label == '${ placeholder } '` ,
472
+ value : "italic"
473
+ } ,
474
+ value : "normal" // default font style for other labels
475
+ } ;
476
+
477
+ // Add placeholder to domain
478
+ if ( ! encoding . scale ) {
479
+ encoding . scale = { } ;
480
+ }
481
+ encoding . scale . domain = [ ...valuesToKeep , placeholder ]
482
+ }
483
+ }
484
+ }
485
+
486
+ return { ...vgObj , data : { values : values } }
448
487
}
449
488
489
+
490
+
450
491
export const adaptChart = ( chart : Chart , targetTemplate : ChartTemplate ) => {
451
492
452
493
let discardedChannels = Object . entries ( chart . encodingMap ) . filter ( ( [ ch , enc ] ) => {
0 commit comments