From 23afbd876ef4cef17237b02aa9349439303c1577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=A2=E4=BB=95=E4=BC=98?= <2279038930@qq.com> Date: Mon, 15 Jul 2024 17:09:47 +0800 Subject: [PATCH 1/6] Based on GPT, covers VChart basic chart types --- .../browser/src/constants/mockData.ts | 465 +++++++++++++++++ .../src/pages/ChartGeneration/DataInput.tsx | 22 +- packages/vmind/package.json | 2 +- .../applications/chartGeneration/constants.ts | 12 +- .../generateTypeAndFieldMap/GPT/index.ts | 12 + .../GPT/patcher/index.ts | 169 ++++++- .../GPT/prompt/index.ts | 3 +- .../GPT/prompt/knowledges.ts | 102 +++- .../GPT/prompt/template.ts | 5 +- .../getChartSpec/VChart/chartPipeline.ts | 86 +++- .../getChartSpec/VChart/constants.ts | 2 + .../getChartSpec/VChart/transformers.ts | 466 +++++++++++++++++- .../src/applications/chartGeneration/types.ts | 3 +- packages/vmind/src/applications/types.ts | 12 +- .../src/base/taskNode/ruleBasedTaskNode.ts | 13 +- packages/vmind/src/common/typings/index.ts | 55 ++- packages/vmind/src/common/utils/utils.ts | 3 + packages/vmind/src/core/VMind.ts | 28 +- 18 files changed, 1409 insertions(+), 51 deletions(-) diff --git a/packages/vmind/__tests__/browser/src/constants/mockData.ts b/packages/vmind/__tests__/browser/src/constants/mockData.ts index dfa24217..1f5fe7e5 100644 --- a/packages/vmind/__tests__/browser/src/constants/mockData.ts +++ b/packages/vmind/__tests__/browser/src/constants/mockData.ts @@ -4751,6 +4751,471 @@ export const gmvData = { input: '帮我绘制按城市的gmv和增长率,使用双轴图' }; +export const bubbleCirclePackingData = { + csv: `industry,gross product +第一产业,88345.1 +第二产业,483164.5 +第三产业,638697.6 +农林牧渔业,92582.4 +工业,401644.3 +建筑业,83383.1 +批发和零售业,114517.7 +交通运输、仓储和邮政业,49673.7 +住宿和餐饮业,17855.3 +金融业,96811.0 +房地产业,73821.3 +其他,279918.4 +`, + input: '请使用气泡图帮我绘制' +}; + +export const mapChartData = { + csv: `name,value +Russia,17234034 +Canada,9984670 +China,9596960 +United States of America,9525067 +Brazil,8515167 +`, + input: '请将数据渲染到地图图表中' +}; + +export const rangeColumnChartData = { + csv: `categories,min,max +Category One,76,100 +Category Two,56,108 +Category Three,38,129 +Category Four,58,155 +Category Five,45,120 +Category Six,23,99 +Category Seven,18,56 +Category Eight,18,34 +`, + input: '请使用区间柱图渲染数据' +}; + +export const sunburstChartData = { + csv: `Country,Region,Category,Value +Country A,Region1,Office Supplies,824 +Country A,Region1,Furniture,920 +Country A,Region1,Electronic equipment,936 +Country A,Region2,Office Supplies,1270 +Country A,Region2,Furniture,1399 +Country A,Region2,Electronic equipment,1466 +Country A,Region3,Office Supplies,1408 +Country A,Region3,Furniture,1676 +Country A,Region3,Electronic equipment,1559 +Country A,Region4,Office Supplies,745 +Country A,Region4,Furniture,919 +Country A,Region4,Electronic equipment,781 +Country A,Region5,Office Supplies,267 +Country A,Region5,Furniture,316 +Country A,Region5,Electronic equipment,230 +Country A,Region6,Office Supplies,347 +Country A,Region6,Furniture,501 +Country A,Region6,Electronic equipment,453 +Country B,Region1,Office Supplies,824 +Country B,Region1,Furniture,920 +Country B,Region1,Electronic equipment,936 +Country B,Region2,Office Supplies,1270 +Country B,Region2,Furniture,1399 +Country B,Region2,Electronic equipment,1466 +Country B,Region3,Office Supplies,1408 +Country B,Region3,Furniture,1676 +Country B,Region3,Electronic equipment,1559 +Country B,Region4,Office Supplies,745 +Country B,Region4,Furniture,919 +Country B,Region4,Electronic equipment,781 +Country B,Region5,Office Supplies,267 +Country B,Region5,Furniture,316 +Country B,Region5,Electronic equipment,230 +Country B,Region6,Office Supplies,347 +Country B,Region6,Furniture,501 +Country B,Region6,Electronic equipment,453 +Country C,Region1,Office Supplies,824 +Country C,Region1,Furniture,920 +Country C,Region1,Electronic equipment,936 +Country C,Region2,Office Supplies,1270 +Country C,Region2,Furniture,1399 +Country C,Region2,Electronic equipment,1466 +Country C,Region3,Office Supplies,1408 +Country C,Region3,Furniture,1676 +Country C,Region3,Electronic equipment,1559 +Country C,Region4,Office Supplies,745 +Country C,Region4,Furniture,919 +Country C,Region4,Electronic equipment,781 +Country C,Region5,Office Supplies,267 +Country C,Region5,Furniture,316 +Country C,Region5,Electronic equipment,230 +Country C,Region6,Office Supplies,347 +Country C,Region6,Furniture,501 +Country C,Region6,Electronic equipment,453 +`, + input: '请使用旭日图来渲染数据' +}; + +export const treemapChartData = { + csv: `Category-0,Category-1,Category-2,Category-3,value +query,methods,add,,593 +query,methods,and,,330 +query,methods,average,,287 +query,methods,count,,277 +query,methods,distinct,,292 +query,methods,div,,595 +query,methods,eq,,594 +query,methods,fn,,460 +query,methods,gt,,603 +query,methods,gte,,625 +query,methods,iff,,748 +query,methods,isa,,461 +query,methods,lt,,597 +query,methods,lte,,619 +query,methods,max,,283 +query,methods,min,,283 +query,methods,mod,,591 +query,methods,mul,,603 +query,methods,neq,,599 +query,methods,not,,386 +query,methods,or,,323 +query,methods,orderby,,307 +query,methods,range,,772 +query,methods,select,,296 +query,methods,stddev,,363 +query,methods,sub,,600 +query,methods,sum,,280 +query,methods,update,,307 +query,methods,variance,,335 +query,methods,where,,299 +query,methods,xor,,354 +query,methods,_,,264 +query,AggregateExpression,,,1616 +query,And,,,1027 +query,Arithmetic,,,3891 +query,Average,,,891 +query,BinaryExpression,,,2893 +query,Comparison,,,5103 +query,CompositeExpression,,,3677 +query,Count,,,781 +query,DateUtil,,,4141 +query,Distinct,,,933 +query,Expression,,,5130 +query,ExpressionIterator,,,3617 +query,Fn,,,3240 +query,If,,,2732 +query,IsA,,,2039 +query,Literal,,,1214 +query,Match,,,3748 +query,Maximum,,,843 +query,Minimum,,,843 +query,Not,,,1554 +query,Or,,,970 +query,Query,,,13896 +query,Range,,,1594 +query,StringUtil,,,4130 +query,Sum,,,791 +query,Variable,,,1124 +query,Variance,,,1876 +query,Xor,,,1101 +util,palette,ColorPalette,,6367 +util,palette,Palette,,1229 +util,palette,ShapePalette,,2059 +util,palette,valuePalette,,2291 +util,math,DenseMatrix,,3165 +util,math,IMatrix,,2815 +util,math,SparseMatrix,,3366 +util,heap,FibonacciHeap,,9354 +util,heap,HeapNode,,1233 +util,Arrays,,,8258 +util,Colors,,,10001 +util,Dates,,,8217 +util,Displays,,,12555 +util,Filter,,,2324 +util,Geometry,,,10993 +util,IEvaluable,,,335 +util,IPredicate,,,383 +util,IValueProxy,,,874 +util,Maths,,,17705 +util,Orientation,,,1486 +util,Property,,,5559 +util,Shapes,,,19118 +util,Sort,,,6887 +util,Stats,,,6557 +util,Strings,,,22026 +animate,interpolate,ArrayInterpolator,,1983 +animate,interpolate,ColorInterpolator,,2047 +animate,interpolate,DateInterpolator,,1375 +animate,interpolate,Interpolator,,8746 +animate,interpolate,MatrixInterpolator,,2202 +animate,interpolate,NumberInterpolator,,1382 +animate,interpolate,ObjectInterpolator,,1629 +animate,interpolate,PointInterpolator,,1675 +animate,interpolate,RectangleInterpolator,,2042 +animate,Easing,,,17010 +animate,FunctionSequence,,,5842 +animate,ISchedulable,,,1041 +animate,Parallel,,,5176 +animate,Pause,,,449 +animate,Scheduler,,,5593 +animate,Sequence,,,5534 +animate,Transition,,,9201 +animate,Transitioner,,,19975 +animate,TransitionEvent,,,1116 +animate,Tween,,,6006 +scale,IScaleMap,,,2105 +scale,LinearScale,,,1316 +scale,LogScale,,,3151 +scale,OrdinalScale,,,3770 +scale,QuantileScale,,,2435 +scale,QuantitativeScale,,,4839 +scale,RootScale,,,1756 +scale,Scale,,,4268 +scale,ScaleType,,,1821 +scale,TimeScale,,,5833 +physics,DragForce,,,1082 +physics,GravityForce,,,1336 +physics,IForce,,,319 +physics,NBodyForce,,,10498 +physics,Particle,,,2822 +physics,Simulation,,,9983 +physics,Spring,,,2213 +physics,SpringForce,,,1681 +data,converters,Converters,,721 +data,converters,DelimitedTextConverter,,4294 +data,converters,GraphMLConverter,,9800 +data,converters,IDataConverter,,1314 +data,converters,JSONConverter,,2220 +data,DataField,,,1759 +data,DataSchema,,,2165 +data,DataSet,,,586 +data,DataSource,,,3331 +data,DataTable,,,772 +data,DataUtil,,,3322 +vis,controls,AnchorControl,,2138 +vis,controls,ClickControl,,3824 +vis,controls,Control,,1353 +vis,controls,ControlList,,4665 +vis,controls,DragControl,,2649 +vis,controls,ExpandControl,,2832 +vis,controls,HoverControl,,4896 +vis,controls,IControl,,763 +vis,controls,PanZoomControl,,5222 +vis,controls,SelectionControl,,7862 +vis,controls,TooltipControl,,8435 +vis,operator,layout,AxisLayout,6725 +vis,operator,layout,BundledEdgeRouter,3727 +vis,operator,layout,CircleLayout,9317 +vis,operator,layout,CirclePackingLayout,12003 +vis,operator,layout,DendrogramLayout,4853 +vis,operator,layout,ForceDirectedLayout,8411 +vis,operator,layout,IcicleTreeLayout,4864 +vis,operator,layout,IndentedTreeLayout,3174 +vis,operator,layout,Layout,7881 +vis,operator,layout,NodeLinkTreeLayout,12870 +vis,operator,layout,PieLayout,2728 +vis,operator,layout,RadialTreeLayout,12348 +vis,operator,layout,RandomLayout,870 +vis,operator,layout,StackedAreaLayout,9121 +vis,operator,layout,TreeMapLayout,9191 +vis,operator,encoder,ColorEncoder,3179 +vis,operator,encoder,Encoder,4060 +vis,operator,encoder,PropertyEncoder,4138 +vis,operator,encoder,ShapeEncoder,1690 +vis,operator,encoder,valueEncoder,1830 +vis,operator,distortion,BifocalDistortion,4461 +vis,operator,distortion,Distortion,6314 +vis,operator,distortion,FisheyeDistortion,3444 +vis,operator,filter,FisheyeTreeFilter,5219 +vis,operator,filter,GraphDistanceFilter,3165 +vis,operator,filter,VisibilityFilter,3509 +vis,operator,label,Labeler,9956 +vis,operator,label,RadialLabeler,3899 +vis,operator,label,StackedAreaLabeler,3202 +vis,operator,IOperator,,1286 +vis,operator,Operator,,2490 +vis,operator,OperatorList,,5248 +vis,operator,OperatorSequence,,4190 +vis,operator,OperatorSwitch,,2581 +vis,operator,SortOperator,,2023 +vis,data,render,ArrowType,698 +vis,data,render,EdgeRenderer,5569 +vis,data,render,IRenderer,353 +vis,data,render,ShapeRenderer,2247 +vis,data,Data,,20544 +vis,data,DataList,,19788 +vis,data,DataSprite,,10349 +vis,data,EdgeSprite,,3301 +vis,data,NodeSprite,,19382 +vis,data,ScaleBinding,,11275 +vis,data,Tree,,7147 +vis,data,TreeBuilder,,9930 +vis,axis,Axes,,1302 +vis,axis,Axis,,24593 +vis,axis,AxisGridLine,,652 +vis,axis,AxisLabel,,636 +vis,axis,CartesianAxes,,6703 +vis,events,DataEvent,,2313 +vis,events,SelectionEvent,,1880 +vis,events,TooltipEvent,,1701 +vis,events,VisualizationEvent,,1117 +vis,legend,Legend,,20859 +vis,legend,LegendItem,,4614 +vis,legend,LegendRange,,10530 +vis,Visualization,,,16540 +display,DirtySprite,,,8833 +display,LineSprite,,,1732 +display,RectSprite,,,3623 +display,TextSprite,,,10066 +analytics,graph,BetweennessCentrality,,3534 +analytics,graph,LinkDistance,,5731 +analytics,graph,MaxFlowMinCut,,7840 +analytics,graph,ShortestPaths,,5914 +analytics,graph,SpanningTree,,3416 +analytics,cluster,AgglomerativeCluster,,3938 +analytics,cluster,CommunityStructure,,3812 +analytics,cluster,HierarchicalCluster,,6714 +analytics,cluster,MergeEdge,,743 +analytics,optimization,AspectRatioBanker,,7074 +flex,FlareVis,,,4116`, + input: '请使用矩形树图渲染数据' +}; + +export const gaugeChartData = { + csv: `name,value,description + 目标A,0.6,目标A的描述`, + input: '请使用仪表盘来完成数据渲染' +}; + +export const linearProgressChartData = { + csv: `type,value,text +Tradition Industries,0.795,79.5% +Business Companies,0.25,25% +Customer-facing Companies,0.065,6.5%`, + input: '请使用线形进度图来完成数据渲染' +}; + +export const basicHeatMapChartData = { + csv: `val1,val2,value +Asset Liability Ratio,Asset Liability Ratio,1 +Asset Liability Ratio,Asset Liability Ratio (Deducting Advance Payments),0.594527 +Asset Liability Ratio,Debt-to-long Capital Ratio,0.492963 +Asset Liability Ratio,Long Term Asset Suitability Ratio,-0.160995 +Asset Liability Ratio,Equity Multiplier,0.723664 +Asset Liability Ratio,Equity Ratio of Current Liability,0.658646 +Asset Liability Ratio,Interest Bearing Debt / Fully Invested Capital,-0.857474 +Asset Liability Ratio,Current Liability / Total Liabilities,0.320706 +Asset Liability Ratio,Capital Fixation Ratio,-0.284634 +Asset Liability Ratio,Expected Default Frequency,-0.091423 +Asset Liability Ratio (Deducting Advance Payments),Asset Liability Ratio,0.594527 +Asset Liability Ratio (Deducting Advance Payments),Asset Liability Ratio (Deducting Advance Payments),1 +Asset Liability Ratio (Deducting Advance Payments),Debt-to-long Capital Ratio,0.724546 +Asset Liability Ratio (Deducting Advance Payments),Long Term Asset Suitability Ratio,-0.099318 +Asset Liability Ratio (Deducting Advance Payments),Equity Multiplier,0.540639 +Asset Liability Ratio (Deducting Advance Payments),Equity Ratio of Current Liability,0.49214 +Asset Liability Ratio (Deducting Advance Payments),Interest Bearing Debt / Fully Invested Capital,-0.554039 +Asset Liability Ratio (Deducting Advance Payments),Current Liability / Total Liabilities,0.17127 +Asset Liability Ratio (Deducting Advance Payments),Capital Fixation Ratio,-0.265259 +Asset Liability Ratio (Deducting Advance Payments),Expected Default Frequency,0.068577 +Debt-to-long Capital Ratio,Asset Liability Ratio,0.492963 +Debt-to-long Capital Ratio,Asset Liability Ratio (Deducting Advance Payments),0.724546 +Debt-to-long Capital Ratio,Debt-to-long Capital Ratio,1 +Debt-to-long Capital Ratio,Long Term Asset Suitability Ratio,-0.091338 +Debt-to-long Capital Ratio,Equity Multiplier,0.450542 +Debt-to-long Capital Ratio,Equity Ratio of Current Liability,0.375839 +Debt-to-long Capital Ratio,Interest Bearing Debt / Fully Invested Capital,-0.524955 +Debt-to-long Capital Ratio,Current Liability / Total Liabilities,0.300627 +Debt-to-long Capital Ratio,Capital Fixation Ratio,-0.198362 +Debt-to-long Capital Ratio,Expected Default Frequency,0.033209 +Long Term Asset Suitability Ratio,Asset Liability Ratio,-0.160995 +Long Term Asset Suitability Ratio,Asset Liability Ratio (Deducting Advance Payments),-0.099318 +Long Term Asset Suitability Ratio,Debt-to-long Capital Ratio,-0.091338 +Long Term Asset Suitability Ratio,Long Term Asset Suitability Ratio,1 +Long Term Asset Suitability Ratio,Equity Multiplier,-0.049872 +Long Term Asset Suitability Ratio,Equity Ratio of Current Liability,-0.028452 +Long Term Asset Suitability Ratio,Interest Bearing Debt / Fully Invested Capital,0.157157 +Long Term Asset Suitability Ratio,Current Liability / Total Liabilities,0.009742 +Long Term Asset Suitability Ratio,Capital Fixation Ratio,-0.162374 +Long Term Asset Suitability Ratio,Expected Default Frequency,0.155095 +Equity Multiplier,Asset Liability Ratio,0.723664 +Equity Multiplier,Asset Liability Ratio (Deducting Advance Payments),0.540639 +Equity Multiplier,Debt-to-long Capital Ratio,0.450542 +Equity Multiplier,Long Term Asset Suitability Ratio,-0.049872 +Equity Multiplier,Equity Multiplier,1 +Equity Multiplier,Equity Ratio of Current Liability,0.951933 +Equity Multiplier,Interest Bearing Debt / Fully Invested Capital,-0.651767 +Equity Multiplier,Current Liability / Total Liabilities,0.079052 +Equity Multiplier,Capital Fixation Ratio,-0.535984 +Equity Multiplier,Expected Default Frequency,0.00798 +Equity Ratio of Current Liability,Asset Liability Ratio,0.658646 +Equity Ratio of Current Liability,Asset Liability Ratio (Deducting Advance Payments),0.49214 +Equity Ratio of Current Liability,Debt-to-long Capital Ratio,0.375839 +Equity Ratio of Current Liability,Long Term Asset Suitability Ratio,-0.028452 +Equity Ratio of Current Liability,Equity Multiplier,0.951933 +Equity Ratio of Current Liability,Equity Ratio of Current Liability,1 +Equity Ratio of Current Liability,Interest Bearing Debt / Fully Invested Capital,-0.543147 +Equity Ratio of Current Liability,Current Liability / Total Liabilities,-0.106139 +Equity Ratio of Current Liability,Capital Fixation Ratio,-0.52232 +Equity Ratio of Current Liability,Expected Default Frequency,0.011466 +Interest Bearing Debt / Fully Invested Capital,Asset Liability Ratio,-0.857474 +Interest Bearing Debt / Fully Invested Capital,Asset Liability Ratio (Deducting Advance Payments),-0.554039 +Interest Bearing Debt / Fully Invested Capital,Debt-to-long Capital Ratio,-0.524955 +Interest Bearing Debt / Fully Invested Capital,Long Term Asset Suitability Ratio,0.157157 +Interest Bearing Debt / Fully Invested Capital,Equity Multiplier,-0.651767 +Interest Bearing Debt / Fully Invested Capital,Equity Ratio of Current Liability,-0.543147 +Interest Bearing Debt / Fully Invested Capital,Interest Bearing Debt / Fully Invested Capital,1 +Interest Bearing Debt / Fully Invested Capital,Current Liability / Total Liabilities,-0.595016 +Interest Bearing Debt / Fully Invested Capital,Capital Fixation Ratio,0.310521 +Interest Bearing Debt / Fully Invested Capital,Expected Default Frequency,0.066397 +Current Liability / Total Liabilities,Asset Liability Ratio,0.320706 +Current Liability / Total Liabilities,Asset Liability Ratio (Deducting Advance Payments),0.17127 +Current Liability / Total Liabilities,Debt-to-long Capital Ratio,0.300627 +Current Liability / Total Liabilities,Long Term Asset Suitability Ratio,0.009742 +Current Liability / Total Liabilities,Equity Multiplier,0.079052 +Current Liability / Total Liabilities,Equity Ratio of Current Liability,-0.106139 +Current Liability / Total Liabilities,Interest Bearing Debt / Fully Invested Capital,-0.595016 +Current Liability / Total Liabilities,Current Liability / Total Liabilities,1 +Current Liability / Total Liabilities,Capital Fixation Ratio,-0.105199 +Current Liability / Total Liabilities,Expected Default Frequency,-0.064886 +Capital Fixation Ratio,Asset Liability Ratio,-0.284634 +Capital Fixation Ratio,Asset Liability Ratio (Deducting Advance Payments),-0.265259 +Capital Fixation Ratio,Debt-to-long Capital Ratio,-0.198362 +Capital Fixation Ratio,Long Term Asset Suitability Ratio,-0.162374 +Capital Fixation Ratio,Equity Multiplier,-0.535984 +Capital Fixation Ratio,Equity Ratio of Current Liability,-0.52232 +Capital Fixation Ratio,Interest Bearing Debt / Fully Invested Capital,0.310521 +Capital Fixation Ratio,Current Liability / Total Liabilities,-0.105199 +Capital Fixation Ratio,Capital Fixation Ratio,1 +Capital Fixation Ratio,Expected Default Frequency,-0.080153 +Expected Default Frequency,Asset Liability Ratio,-0.091423 +Expected Default Frequency,Asset Liability Ratio (Deducting Advance Payments),0.068577 +Expected Default Frequency,Debt-to-long Capital Ratio,0.033209 +Expected Default Frequency,Long Term Asset Suitability Ratio,0.155095 +Expected Default Frequency,Equity Multiplier,0.00798 +Expected Default Frequency,Equity Ratio of Current Liability,0.011466 +Expected Default Frequency,Interest Bearing Debt / Fully Invested Capital,0.066397 +Expected Default Frequency,Current Liability / Total Liabilities,-0.064886 +Expected Default Frequency,Capital Fixation Ratio,-0.080153 +Expected Default Frequency,Expected Default Frequency,1`, + input: '请使用热图完成数据渲染' +}; + +export const vennChartData = { + csv: `sets,name,value +0,A,8 +1,D,8 +2,B,10 +3,C,12 +4,A,4 +4,B,4 +5,A,4 +5,C,4 +6,B,4 +6,C,4 +7,A,2 +7,B,2 +7,C,2`, + input: '请使用韦恩图渲染数据' +}; + export const mockUserTextInput0 = { text: `快手消失了。快手上市后,市值一度超过2000亿美元,现在只剩200多亿美元。去年快手的营收破了千亿,公司也赚钱了,但市场不买账了。 滴滴也不见了。滴滴去年营收接近2000亿元,并首次实现年度盈利,不过滴滴从美股退市后,一直没在港股上市,所以没有市值参考。`, diff --git a/packages/vmind/__tests__/browser/src/pages/ChartGeneration/DataInput.tsx b/packages/vmind/__tests__/browser/src/pages/ChartGeneration/DataInput.tsx index 0ba31f56..c14daebb 100644 --- a/packages/vmind/__tests__/browser/src/pages/ChartGeneration/DataInput.tsx +++ b/packages/vmind/__tests__/browser/src/pages/ChartGeneration/DataInput.tsx @@ -40,7 +40,16 @@ import { SalesRecordsData, gmvData, mockProgressData, - liquidData + liquidData, + bubbleCirclePackingData, + rangeColumnChartData, + sunburstChartData, + treemapChartData, + gaugeChartData, + linearProgressChartData, + basicHeatMapChartData, + vennChartData, + mapChartData } from '../../constants/mockData'; import VMind, { ArcoTheme, builtinThemeMap, BuiltinThemeType } from '../../../../../src/index'; import { Model } from '../../../../../src/index'; @@ -87,7 +96,16 @@ const demoDataList: { [key: string]: any } = { DataQuery: mockUserInput18, salesData: SalesRecordsData, progress: mockProgressData, - liquid: liquidData + liquid: liquidData, + BubbleCirclePacking: bubbleCirclePackingData, + Map: mapChartData, + RangeColumn: rangeColumnChartData, + Sunburst: sunburstChartData, + TreeMap: treemapChartData, + Gauge: gaugeChartData, + LinearProgress: linearProgressChartData, + BasicHeatMap: basicHeatMapChartData, + Venn: vennChartData }; const globalVariables = (import.meta as any).env; diff --git a/packages/vmind/package.json b/packages/vmind/package.json index e773ba67..ff7ef369 100644 --- a/packages/vmind/package.json +++ b/packages/vmind/package.json @@ -77,7 +77,7 @@ "react": "^18.0.0", "react-dom": "^18.0.0", "react-router-dom": "6.9.0", - "@visactor/vchart": "^1.10.4", + "@visactor/vchart": "^1.11.4", "@rollup/plugin-dynamic-import-vars": "~2.1.0", "@types/node": "*", "dotenv": "~16.3.1", diff --git a/packages/vmind/src/applications/chartGeneration/constants.ts b/packages/vmind/src/applications/chartGeneration/constants.ts index 951d4cac..07b0ec82 100644 --- a/packages/vmind/src/applications/chartGeneration/constants.ts +++ b/packages/vmind/src/applications/chartGeneration/constants.ts @@ -1,3 +1,13 @@ -import { ChartType } from '../../common/typings'; +import type { BasemapOption } from '../../common/typings'; +import { ChartType, MapRegionCoordinate, UncommonChartType } from '../../common/typings'; export const SUPPORTED_CHART_LIST = Object.values(ChartType); +export const SUPPORTED_UNCOMMON_CHART_LIST = Object.values(UncommonChartType); + +export const DEFAULT_MAP_OPTION: BasemapOption = { + jsonUrl: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/geojson/world.json', + regionProjectType: null, + regionCoordinate: MapRegionCoordinate.GEO, + zoom: 1, + center: null +}; diff --git a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/index.ts b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/index.ts index ef7168d5..bd2fbd4d 100644 --- a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/index.ts +++ b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/index.ts @@ -6,13 +6,19 @@ import { chartGenerationRequestLLM, parseChartGenerationResponse } from './utils import { GPTChartGenerationPrompt } from './prompt'; import { patchAxisField, + patchBasicHeatMapChart, patchBoxPlot, patchCartesianXField, patchColorField, patchDualAxis, patchDynamicBarChart, patchLabelField, + patchLinearProgressChart, + patchNeedColor, + patchNeedSize, patchPieChart, + patchRangeColumnChart, + patchSunburstAndTreemapChart, patchWordCloud, patchYField } from './patcher'; @@ -34,11 +40,17 @@ const ChartGenerationTaskNodeGPTMeta: LLMBasedTaskNodeMeta< patchColorField, patchLabelField, patchYField, + patchNeedColor, + patchNeedSize, patchBoxPlot, patchDualAxis, patchPieChart, patchWordCloud, patchDynamicBarChart, + patchRangeColumnChart, + patchLinearProgressChart, + patchSunburstAndTreemapChart, + patchBasicHeatMapChart, patchCartesianXField ], requester: chartGenerationRequestLLM, diff --git a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/patcher/index.ts b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/patcher/index.ts index 3ae1eb1a..1b6504a4 100644 --- a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/patcher/index.ts +++ b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/patcher/index.ts @@ -6,12 +6,13 @@ import { foldDatasetByYField, getFieldByDataType, getFieldByRole, + getFieldsByDataType, getRemainedFields } from '../../../../../../common/utils/utils'; -import { ChartType } from '../../../../../../common/typings'; -import { DataType, ROLE } from '../../../../../../common/typings'; +import { ChartType, DataType, ROLE } from '../../../../../../common/typings'; import type { GenerateChartAndFieldMapContext, GenerateChartAndFieldMapOutput } from '../../types'; import { isValidDataset } from '../../../../../../common/dataProcess'; +import { needSizeFieldChartList, needColorFieldChartList } from '../prompt/knowledges'; const CARTESIAN_CHART_LIST = [ 'Dynamic Bar Chart', @@ -21,7 +22,9 @@ const CARTESIAN_CHART_LIST = [ 'Funnel Chart', 'Dual Axis Chart', 'Waterfall Chart', - 'Box Plot' + 'Box Plot', + 'Range Column Chart', + 'linear Progress Chart' ]; export const patchAxisField: Transformer< @@ -96,7 +99,8 @@ export const patchYField: Transformer< if (y && isArray(y) && y.length > 1) { if ( chartTypeNew === ('BOX PLOT' as ChartType) || - (chartTypeNew === ('DUAL AXIS CHART' as ChartType) && y.length === 2) + (chartTypeNew === ('DUAL AXIS CHART' as ChartType) && y.length === 2) || + (chartTypeNew === ('RANGE COLUMN CHART' as ChartType) && y.length === 2) ) { return { ...context @@ -367,3 +371,160 @@ export const patchCartesianXField: Transformer< cell: cellNew }; }; + +export const patchNeedColor: Transformer< + GenerateChartAndFieldMapContext & GenerateChartAndFieldMapOutput, + Partial +> = (context: GenerateChartAndFieldMapContext & GenerateChartAndFieldMapOutput) => { + const { chartType, cell, fieldInfo } = context; + const cellNew: any = { ...cell }; + if (needColorFieldChartList.some(needColorFieldChartType => needColorFieldChartType.toUpperCase() === chartType)) { + const colorField = [cellNew.color, cellNew.x, cellNew.label, (cellNew as any).sets].filter(Boolean).flat(); + if (colorField.length !== 0) { + cellNew.color = colorField[0]; + } else { + const remainedFields = getRemainedFields(cellNew, fieldInfo); + const colorField = getFieldByRole(remainedFields, ROLE.DIMENSION); + if (colorField) { + cellNew.color = colorField.fieldName; + } else { + cellNew.color = remainedFields[0].fieldName; + } + } + } + return { + cell: cellNew + }; +}; + +export const patchNeedSize: Transformer< + GenerateChartAndFieldMapContext & GenerateChartAndFieldMapOutput, + Partial +> = (context: GenerateChartAndFieldMapContext & GenerateChartAndFieldMapOutput) => { + const { chartType, cell, fieldInfo } = context; + const cellNew: any = { ...cell }; + if (needSizeFieldChartList.some(needSizeFieldChartType => needSizeFieldChartType.toUpperCase() === chartType)) { + const sizeField = [cellNew.size, cellNew.value, cellNew.y, cellNew.radius, cellNew.angle].filter(Boolean).flat(); + if (sizeField.length !== 0) { + cellNew.size = sizeField[0]; + } else { + const remainedFields = getRemainedFields(cellNew, fieldInfo); + const sizeField = getFieldByRole(remainedFields, ROLE.MEASURE); + if (sizeField) { + cellNew.size = sizeField.fieldName; + } else { + cellNew.size = remainedFields[0].fieldName; + } + } + } + return { + cell: cellNew + }; +}; + +export const patchRangeColumnChart: Transformer< + GenerateChartAndFieldMapContext & GenerateChartAndFieldMapOutput, + Partial +> = (context: GenerateChartAndFieldMapContext & GenerateChartAndFieldMapOutput) => { + // Range Column Chart's y field must length == 2 + const { chartType, cell, fieldInfo } = context; + const cellNew = { ...cell }; + const remainedFields = getRemainedFields(cellNew, fieldInfo); + const numericFields = getFieldsByDataType(remainedFields, [DataType.FLOAT, DataType.INT]); + if (chartType === ('RANGE COLUMN CHART' as ChartType)) { + if (cellNew.y && cellNew.y instanceof Array && cellNew.y.length === 2) { + return { cell: cellNew }; + } + if (numericFields.length >= 2) { + cellNew.y = [numericFields[0].fieldName, numericFields[1].fieldName]; + } else { + throw Error( + 'The y-axis of the range column chart requires two numeric fields, ' + + 'but the result of data aggregation does not have two numeric fields' + ); + } + } + return { + //...context, + cell: cellNew + }; +}; + +export const patchLinearProgressChart: Transformer< + GenerateChartAndFieldMapContext & GenerateChartAndFieldMapOutput, + Partial +> = (context: GenerateChartAndFieldMapContext & GenerateChartAndFieldMapOutput) => { + const { chartType, cell, fieldInfo } = context; + const cellNew = { ...cell }; + if (chartType === ('Linear Progress Chart' as ChartType)) { + const yField = [cellNew.y, cellNew.size, cellNew.value, cellNew.radius, cellNew.angle].filter(Boolean).flat(); + if (yField.length !== 0) { + cellNew.y = yField[0]; + } else { + const remainedFields = getRemainedFields(cellNew, fieldInfo); + const yField = getFieldByRole(remainedFields, ROLE.MEASURE); + if (yField) { + cellNew.y = yField.fieldName; + } else { + cellNew.y = remainedFields[0].fieldName; + } + } + } + return { + //...context, + cell: cellNew + }; +}; + +export const patchSunburstAndTreemapChart: Transformer< + GenerateChartAndFieldMapContext & GenerateChartAndFieldMapOutput, + Partial +> = (context: GenerateChartAndFieldMapContext & GenerateChartAndFieldMapOutput) => { + const { chartType, cell } = context; + const cellNew: any = { ...cell }; + + if (chartType === ('SUNBURST CHART' as ChartType) || chartType === ('TREEMAP CHART' as ChartType)) { + cellNew.y = [cellNew.y, cellNew.value, cellNew.radius, cellNew.size, cellNew.angle].filter(Boolean).flat(); + if (cellNew.y.length !== 0) { + cellNew.y = cellNew.y[0]; + } else { + throw Error( + 'The y-axis of the sunburst chart and the treemap chart requires a numeric field,' + + 'but the result of data aggregation does not have a numeric field' + ); + } + } + + return { + //...context, + cell: cellNew + }; +}; + +export const patchBasicHeatMapChart: Transformer< + GenerateChartAndFieldMapContext & GenerateChartAndFieldMapOutput, + Partial +> = (context: GenerateChartAndFieldMapContext & GenerateChartAndFieldMapOutput) => { + const { chartType, cell, fieldInfo } = context; + const cellNew: any = { ...cell }; + if (chartType === ('BASIC HEAT MAP' as ChartType)) { + const colorField = [cellNew.x, cellNew.y, cellNew.label, cellNew.color].filter(Boolean).flat(); + if (colorField.length >= 2) { + cellNew.x = colorField[0]; + cellNew.y = colorField[1]; + } else { + const remainedFields = getRemainedFields(cellNew, fieldInfo); + const colorField = getFieldsByDataType(remainedFields, [DataType.STRING]); + if (colorField.length >= 2) { + cellNew.x = colorField[0]; + cellNew.y = colorField[1]; + } else { + cellNew.x = remainedFields[0].fieldName; + cellNew.y = remainedFields[1].fieldName; + } + } + } + return { + cell: cellNew + }; +}; diff --git a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/index.ts b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/index.ts index 47a16d96..8c162331 100644 --- a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/index.ts +++ b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/index.ts @@ -39,7 +39,7 @@ export class GPTChartGenerationPrompt extends Prompt chartKnowledgeDict[a].index - chartKnowledgeDict[b].index); @@ -75,6 +75,7 @@ export class GPTChartGenerationPrompt extends Prompt { - if ( - chartTypeList.includes(ChartType.WordCloud) || - chartTypeList.includes(ChartType.PieChart) || - chartTypeList.includes(ChartType.RoseChart) - ) { - const validChartList = [ChartType.WordCloud, ChartType.PieChart, ChartType.RoseChart]; - const includedCharts = chartTypeList.filter(chart => validChartList.includes(chart)); + const validSet = new Set(chartTypeList); + if (needColorFieldChartList.some(chartType => validSet.has(chartType))) { + const includedCharts = chartTypeList.filter(chart => needColorFieldChartList.includes(chart)); return ", can't be empty in " + includedCharts.join(', ') + '.'; } return '.'; }; const getSizeKnowledge = (chartTypeList: ChartType[]) => { - if (chartTypeList.includes(ChartType.ScatterPlot) || chartTypeList.includes(ChartType.WordCloud)) { - const validChartList = [ChartType.ScatterPlot, ChartType.WordCloud]; - const includedCharts = chartTypeList.filter(chart => validChartList.includes(chart)); - return ' Only used in ' + includedCharts.join(' and ') + '.'; + const validSet = new Set(chartTypeList); + if (needSizeFieldChartList.some(chartType => validSet.has(chartType))) { + const includedCharts = chartTypeList.filter(chart => needSizeFieldChartList.includes(chart)); + return ' Only used in ' + includedCharts.join(' , ') + '.'; } return '.'; }; @@ -32,7 +48,7 @@ export const visualChannelInfoMap = { color: (chartTypeList: ChartType[]) => `the field mapped to the color channel. Must use a string field${getColorKnowledge(chartTypeList)}`, size: (chartTypeList: ChartType[]) => - `the field mapped to the size channel. Must use a number field.${getSizeKnowledge(chartTypeList)}`, + `the field mapped to the size channel. Must use a number field${getSizeKnowledge(chartTypeList)}`, angle: (chartTypeList: ChartType[]) => "the field mapped to the angle channel of the pie chart. Can't be empty in Pie Chart.", radius: (chartTypeList: ChartType[]) => @@ -44,7 +60,9 @@ export const visualChannelInfoMap = { target: (chartTypeList: ChartType[]) => "the field mapped to the target channel. Only used in Sankey Chart. Can't be empty in Sankey Chart.", value: (chartTypeList: ChartType[]) => - "the field mapped to the value channel. Only used in Sankey Chart. Can't be empty in Sankey Chart." + "the field mapped to the value channel. Only used in Sankey Chart. Can't be empty in Sankey Chart.", + group: (chartTypeList: ChartType[]) => + "the field mapped to the value channel. Only used in Venn Chart. Can't be empty in Venn Chart. Digital IDs are often used to distinguish whether data is in the same group." }; export const chartKnowledgeDict: ChartKnowledge = { [ChartType.BarChart]: { @@ -141,6 +159,64 @@ export const chartKnowledgeDict: ChartKnowledge = { knowledge: [ 'Circular progress chart is also used to display progress data, presented in a circular form, with the values on the numerical axis typically ranging from 0 to 1.' ] + }, + [ChartType.BubbleCirclePacking]: { + index: 17, + visualChannels: ['color', 'size'], + examples: [], + knowledge: [] + }, + [ChartType.MapChart]: { + index: 18, + visualChannels: ['color', 'size'], + examples: [], + knowledge: [] + }, + [ChartType.RangeColumnChart]: { + index: 19, + visualChannels: ['y', 'x'], + examples: [], + knowledge: [] + }, + [ChartType.SunburstChart]: { + index: 20, + visualChannels: ['x', 'y'], + examples: [], + knowledge: [ + 'The x field of a sunburst chart can be an array. The order of the elements in the array needs to be sorted from large to small according to the coverage described by the data field.' + ] + }, + [ChartType.TreemapChart]: { + index: 21, + visualChannels: ['x', 'y'], + examples: [], + knowledge: [ + 'The x field of a treemap chart can be an array. The order of the elements in the array needs to be sorted from large to small according to the coverage described by the data field.' + ] + }, + [ChartType.Gauge]: { + index: 22, + visualChannels: ['color', 'size'], + examples: [], + knowledge: ['The gauge chart must contain two fields: size and color.'] + }, + // [ChartType.LinearProgressChart]: { + // index: 23, + // visualChannels: ['y', 'x'], + // examples: [], + // knowledge: [] + // }, + [ChartType.BasicHeatMap]: { + index: 24, + visualChannels: ['y', 'x', 'size'], + examples: [], + knowledge: [] + }, + [ChartType.VennChart]: { + index: 25, + visualChannels: ['size', 'color', 'group'], + examples: [], + knowledge: ['The venn chart must contain three fields: group, size and color.'] } }; diff --git a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/template.ts b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/template.ts index a713eef4..bf51a4af 100644 --- a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/template.ts +++ b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/template.ts @@ -2,6 +2,7 @@ export const ChartAdvisorPromptEnglish = ( showThoughts: boolean, supportedChartList: string[], + supportedUncommonChartList: string[], knowledge: string, visualChannels: string, constraints: string, @@ -10,9 +11,9 @@ export const ChartAdvisorPromptEnglish = ( User want to create an visualization chart for data video using data from a csv file. Ignore the duration in User Input. Your task is: 1. Based on the user's input, infer the user's intention, such as comparison, ranking, trend display, proportion, distribution, etc. If user did not show their intention, just ignore and do the next steps. -2. Select the single chart type that best suites the data from the list of supported charts. Supported chart types: ${JSON.stringify( +2. Select the single chart type that best suites the data from the list of supported charts. If the user's input does not specify a chart type, do not use uncommon chart types. Supported chart types: ${JSON.stringify( supportedChartList -)}. +)}. Uncommon chart types: ${JSON.stringify(supportedUncommonChartList)}. 3. Map all the fields in the data to the visual channels according to user input and the chart type you choose. Don't use non-existent fields. Only use existing fields without further processing. If the existing fields can't meet user's intention, just use the most related fields. ${knowledge.length > 0 ? '\nKnowledge' : ''} ${knowledge} diff --git a/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/chartPipeline.ts b/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/chartPipeline.ts index 8b6b23cf..f03b9876 100644 --- a/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/chartPipeline.ts +++ b/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/chartPipeline.ts @@ -51,7 +51,33 @@ import { circularProgressStyle, linearProgressStyle, linearProgressAxes, - indicator + indicator, + bubbleCirclePackingField, + bubbleCirclePackingDisplayConf, + bubbleCirclePackingData, + rangeColumnField, + rangeColumnDisplayConf, + sunburstData, + sunburstDisplayConf, + treemapData, + sunburstOrTreemapField, + treemapDisplayConf, + gaugeField, + gaugeDisplayConf, + // linearProgressField, + // linearProgressDisplayConf, + arrayData, + vennData, + vennField, + basicHeatMapSeries, + basicHeatMapRegion, + basicHeatMapColor, + basicHeatMapAxes, + basicHeatMapLegend, + basemap, + mapField, + mapDisplayConf, + registerChart } from './transformers'; const pipelineBar = [ @@ -153,24 +179,52 @@ const pipelineBoxPlot = [chartType, data, color, boxPlotField, boxPlotStyle, leg const pipelineLiquid = [chartType, data, color, liquidField, liquidStyle, indicator, theme]; const pipelineLinearProgress = [ + chartType, + data, + color, + linearProgressField, + linearProgressAxes, + linearProgressStyle, + theme +]; + +const pipelineCircularProgress = [ + chartType, + data, + color, + circularProgressField, + circularProgressStyle, + indicator, + theme +]; + +const pipelineBubbleCirclePacking = [ chartType, + bubbleCirclePackingData, data, color, - linearProgressField, - linearProgressAxes, - linearProgressStyle, + bubbleCirclePackingField, + bubbleCirclePackingDisplayConf, theme ]; -const pipelineCircularProgress = [ +const pipelineMapChart = [chartType, basemap, arrayData, mapField, mapDisplayConf, theme]; +const pipelineRangeColumn = [chartType, data, rangeColumnField, rangeColumnDisplayConf, theme]; +const pipelineSunburst = [chartType, sunburstData, sunburstOrTreemapField, sunburstDisplayConf, theme]; +const pipelineTreemap = [chartType, treemapData, sunburstOrTreemapField, treemapDisplayConf, theme]; +const pipelineGauge = [chartType, arrayData, gaugeField, gaugeDisplayConf, theme]; +// const pipelineLinearProgress = [chartType, arrayData, linearProgressField, linearProgressDisplayConf, theme]; +const pipelineBasicHeatMap = [ chartType, - data, - color, - circularProgressField, - circularProgressStyle, - indicator, + arrayData, + basicHeatMapSeries, + basicHeatMapRegion, + basicHeatMapColor, + basicHeatMapAxes, + basicHeatMapLegend, theme ]; +const pipelineVenn = [chartType, registerChart, vennData, vennField, legend, theme]; const pipelineMap: { [chartType: string]: any } = { 'BAR CHART': pipelineBar, @@ -188,7 +242,17 @@ const pipelineMap: { [chartType: string]: any } = { 'BOX PLOT': pipelineBoxPlot, [ChartType.LiquidChart.toUpperCase()]: pipelineLiquid, [ChartType.LinearProgress.toUpperCase()]: pipelineLinearProgress, - [ChartType.CircularProgress.toUpperCase()]: pipelineCircularProgress + [ChartType.CircularProgress.toUpperCase()]: pipelineCircularProgress, + 'BOX PLOT': pipelineBoxPlot, + 'BUBBLE CIRCLE PACKING': pipelineBubbleCirclePacking, + 'MAP CHART': pipelineMapChart, + 'RANGE COLUMN CHART': pipelineRangeColumn, + 'SUNBURST CHART': pipelineSunburst, + 'TREEMAP CHART': pipelineTreemap, + 'GAUGE CHART': pipelineGauge, + // 'LINEAR PROGRESS CHART': pipelineLinearProgress, + 'BASIC HEAT MAP': pipelineBasicHeatMap, + 'VENN CHART': pipelineVenn }; export const beforePipe: Transformer = ( diff --git a/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/constants.ts b/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/constants.ts index 9642dfd2..7653ac80 100644 --- a/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/constants.ts +++ b/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/constants.ts @@ -12,6 +12,8 @@ export const LINEAR_COLOR_THEMES = [ ['#1DD0F3', '#CB2BC6'] ]; +export const BASIC_HEAT_MAP_COLOR_THEMES = ['#feedde', '#fdbe85', '#fd8d3c', '#e6550d', '#a63603']; + export const animationDuration = 500; export const oneByOneGroupSize = 10; export const DEFAULT_VIDEO_LENGTH = 2000; diff --git a/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/transformers.ts b/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/transformers.ts index 3f665797..811c0673 100644 --- a/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/transformers.ts +++ b/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/transformers.ts @@ -1,4 +1,6 @@ import type { Transformer } from '../../../../../base/tools/transformer'; +import { registerVennChart } from '@visactor/vchart'; +import VChart from '@visactor/vchart'; import type { GetChartSpecContext, GetChartSpecOutput } from '../types'; import { COLOR_THEMES, @@ -13,13 +15,15 @@ import { SUB_SERIES_ID, WORDCLOUD_NUM_LIMIT, animationDuration, - oneByOneGroupSize + oneByOneGroupSize, + BASIC_HEAT_MAP_COLOR_THEMES } from './constants'; import { getFieldByDataType } from '../../../../../common/utils/utils'; import { array, isArray } from '@visactor/vutils'; import { isValidDataset } from '../../../../../common/dataProcess'; import { DataType, ChartType } from '../../../../../common/typings'; import { builtinThemeMap } from '../../../../../common/builtinTheme'; +import type { VMindDataset } from '../../../../../common/typings'; import { COLOR_FIELD } from '@visactor/chart-advisor'; type Context = GetChartSpecContext & GetChartSpecOutput; @@ -41,6 +45,15 @@ const chartTypeMap: { [chartName: string]: string } = { [ChartType.LiquidChart.toUpperCase()]: 'liquid', [ChartType.LinearProgress.toUpperCase()]: 'linearProgress', [ChartType.CircularProgress.toUpperCase()]: 'circularProgress' + 'BUBBLE CIRCLE PACKING': 'circlePacking', + 'MAP CHART': 'map', + 'RANGE COLUMN CHART': 'rangeColumn', + 'SUNBURST CHART': 'sunburst', + 'TREEMAP CHART': 'treemap', + 'GAUGE CHART': 'gauge', + // 'LINEAR PROGRESS CHART': 'linearProgress', + 'BASIC HEAT MAP': 'common', + 'VENN CHART': 'venn' }; export const chartType: Transformer = (context: Context) => { @@ -60,6 +73,17 @@ export const data: Transformer = (context: Context) return { spec }; }; +export const arrayData: Transformer = (context: Context) => { + const { dataset, spec } = context; + spec.data = [ + { + id: 'data', + values: isValidDataset(dataset) ? dataset.flat(4) : [] + } + ]; + return { spec }; +}; + export const funnelData: Transformer = (context: Context) => { const { dataset, cell, spec } = context; // spec.data = [dataset] @@ -1385,3 +1409,443 @@ export const indicator: Transformer = (context: Con }; return { spec }; }; + +export const bubbleCirclePackingData: Transformer = (context: Context) => { + const { dataset, spec, cell } = context; + if (cell.size) { + dataset.forEach(data => { + data.value = data[cell.size]; + delete data[cell.size]; + }); + } + return { spec }; +}; + +export const bubbleCirclePackingField: Transformer = (context: Context) => { + //assign field in spec according to cell + const { cell, spec } = context; + spec.categoryField = cell.color || cell.x; + + if (cell.size) { + spec.valueField = cell.size; + } + + return { spec }; +}; + +export const bubbleCirclePackingDisplayConf: Transformer = (context: Context) => { + const { spec } = context; + spec.drill = true; + spec.layoutPadding = 5; + spec.animationEnter = { + easing: 'cubicInOut' + }; + spec.animationExit = { + easing: 'cubicInOut' + }; + spec.animationUpdate = { + easing: 'cubicInOut' + }; + return { spec }; +}; + +export const rangeColumnField: Transformer = (context: Context) => { + //assign field in spec according to cell + const { cell, spec } = context; + spec.yField = cell.x; + + spec.xField = [cell.y[0], cell.y[1]]; + + return { spec }; +}; + +export const rangeColumnDisplayConf: Transformer = (context: Context) => { + const { spec } = context; + spec.direction = 'horizontal'; + spec.label = { + visible: true + }; + return { spec }; +}; + +export const sunburstData: Transformer = (context: Context) => { + const { dataset, cell, spec } = context; + spec.data = { id: 'data', values: getSunburstData(dataset, cell.x, 0, cell.y) }; + return { spec }; +}; + +export const getSunburstData: any = ( + dataset: VMindDataset, + xField: string[] | string, + index: number, + yField: string +) => { + if (xField.length - 1 === index) { + return Array.from( + new Set( + dataset.map(data => { + return { name: data[xField[index]], value: data[yField] }; + }) + ) + ); + } + // Get the value range of this layer + const values = Array.from( + new Set( + dataset.map(data => { + return data[xField[index]]; + }) + ) + ); + return values.map(value => { + const currentDataset = dataset.filter(data => { + return data[xField[index]] === value; + }); + return { name: value, children: getSunburstData(currentDataset, xField, index + 1, yField) }; + }); +}; + +export const sunburstOrTreemapField: Transformer = (context: Context) => { + //assign field in spec according to cell + const { spec } = context; + spec.categoryField = 'name'; + + spec.valueField = 'value'; + + return { spec }; +}; + +export const sunburstDisplayConf: Transformer = (context: Context) => { + const { spec } = context; + spec.offsetX = 0; + spec.offsetY = 0; + spec.outerRadius = 1; + spec.innerRadius = 0; + spec.gap = 5; + spec.drill = true; + spec.sunburst = { + visible: true, + style: { + fillOpacity: (datum: { isLeaf: any }) => { + return datum.isLeaf ? 0.4 : 0.8; + } + } + }; + spec.label = { + visible: true, + style: { + fontSize: 12, + fillOpacity: (datum: { isLeaf: any }) => { + return datum.isLeaf ? 0.4 : 0.8; + } + } + }; + spec.tooltip = { + mark: { + title: { + value: (val: { datum: any[] }) => { + return val?.datum?.map(data => data.name).join(' / '); + } + } + } + }; + spec.animationEnter = { + easing: 'cubicInOut', + duration: 1000 + }; + spec.animationExit = { + easing: 'cubicInOut', + duration: 1000 + }; + spec.animationUpdate = { + easing: 'cubicInOut', + duration: 1000 + }; + return { spec }; +}; + +export const treemapData: Transformer = (context: Context) => { + const { dataset, cell, spec } = context; + spec.data = { id: 'data', values: getTreemapData(dataset, cell.x, 0, cell.y) }; + return { spec }; +}; + +export const getTreemapData: any = ( + dataset: VMindDataset, + xField: string[] | string, + index: number, + yField: string +) => { + if (xField.length - 1 === index) { + return Array.from( + new Set( + dataset.map(data => { + return { name: data[xField[index]], value: data[yField] }; + }) + ) + ); + } + // Get the value range of this layer + const values = Array.from( + new Set( + dataset.map(data => { + return data[xField[index]]; + }) + ) + ); + return values.map(value => { + const currentDataset = dataset.filter(data => { + return data[xField[index]] === value; + }); + if (currentDataset[0] && currentDataset[0][xField[index + 1]] === '') { + return { name: value, value: currentDataset[0][yField] }; + } + return { name: value, children: getTreemapData(currentDataset, xField, index + 1, yField) }; + }); +}; + +export const treemapDisplayConf: Transformer = (context: Context) => { + const { spec } = context; + spec.label = { + visible: true, + style: { + fontSize: 12 + } + }; + return { spec }; +}; + +export const gaugeField: Transformer = (context: Context) => { + const { spec, cell } = context; + spec.valueField = cell.size; + spec.categoryField = cell.color; + return { spec }; +}; + +export const gaugeDisplayConf: Transformer = (context: Context) => { + const { spec } = context; + spec.outerRadius = 0.8; + spec.innerRadius = 0.5; + spec.startAngle = -180; + spec.endAngle = 0; + return { spec }; +}; + +export const linearProgressField: Transformer = (context: Context) => { + const { spec, cell } = context; + spec.yField = cell.x; + spec.xField = cell.y; + + spec.seriesField = cell.x; + return { spec }; +}; + +export const linearProgressDisplayConf: Transformer = (context: Context) => { + const { spec } = context; + spec.direction = 'horizontal'; + + spec.cornerRadius = 20; + spec.bandWidth = 30; + spec.axes = [ + { + orient: 'left', + label: { visible: true }, + type: 'band', + domainLine: { visible: false }, + tick: { visible: false } + }, + { orient: 'bottom', label: { visible: true }, type: 'linear', visible: false } + ]; + return { spec }; +}; + +export const vennData: Transformer = (context: Context) => { + const { dataset, spec, cell } = context; + const id2dataMap = {}; + dataset.forEach(data => { + if (id2dataMap[data[cell.group]]) { + id2dataMap[data[cell.group]].sets.push(data[cell.color]); + } else { + id2dataMap[data[cell.group]] = { sets: [data[cell.color]], value: data[cell.size] }; + } + }); + spec.data = { + values: Object.values(id2dataMap) + }; + + return { spec }; +}; + +export const vennField: Transformer = (context: Context) => { + const { spec } = context; + spec.valueField = 'value'; + spec.categoryField = 'sets'; + spec.seriesField = 'sets'; + return { spec }; +}; +export const registerChart: Transformer = (context: Context) => { + const { spec } = context; + if (spec.type === 'venn') { + registerVennChart(); + } + return { spec }; +}; + +export const basicHeatMapSeries: Transformer = (context: Context) => { + const { spec, cell } = context; + spec.series = [ + { + type: 'heatmap', + regionId: 'region0', + xField: cell.x, + yField: cell.y, + valueField: cell.size, + cell: { + style: { + fill: { + field: cell.size, + scale: 'color' + } + } + } + } + ]; + return { spec }; +}; +export const basicHeatMapRegion: Transformer = (context: Context) => { + const { spec } = context; + spec.region = [ + { + id: 'region0', + width: 200, // limit the width of the region + height: 200, // limit the height of the region + padding: { + top: 40 + } + } + ]; + return { spec }; +}; +export const basicHeatMapColor: Transformer = (context: Context) => { + const { spec, cell } = context; + spec.color = { + type: 'linear', + domain: [ + { + dataId: 'data', + fields: [cell.size] + } + ], + range: BASIC_HEAT_MAP_COLOR_THEMES + }; + return { spec }; +}; +export const basicHeatMapAxes: Transformer = (context: Context) => { + const { spec } = context; + spec.axes = [ + { + orient: 'bottom', + type: 'band', + grid: { + visible: false + }, + domainLine: { + visible: false + }, + label: { + space: 10, + style: { + textAlign: 'left', + textBaseline: 'middle', + angle: 90, + fontSize: 8 + } + }, + bandPadding: 0, + height: (layoutRect: any) => { + // canvas height - region height - paddingTop - paddingBottom + return layoutRect.height - 314; + } + }, + { + orient: 'left', + type: 'band', + grid: { + visible: false + }, + domainLine: { + visible: false + }, + label: { + space: 10, + style: { + fontSize: 8 + } + }, + bandPadding: 0 + } + ]; + return { spec }; +}; + +export const basicHeatMapLegend: Transformer = (context: Context) => { + const { spec } = context; + spec.legends = { + visible: true, + orient: 'right', + position: 'start', + type: 'color', + field: 'value' + }; + return { spec }; +}; + +export const basemap: Transformer> = async (context: Context) => { + const { basemapOption, spec } = context; + const jsonUrl = basemapOption.jsonUrl; + const response = await fetch(jsonUrl); + const geoJson = await response.json(); + VChart.registerMap('map', geoJson); + if (basemapOption.regionProjectType) { + spec.region = [ + { + roam: true, + projection: { type: basemapOption.regionProjectType }, + coordinate: basemapOption.regionCoordinate + } + ]; + } else { + spec.region = [ + { + roam: true, + coordinate: basemapOption.regionCoordinate + } + ]; + } + + spec.map = 'map'; + return { spec }; +}; + +export const mapField: Transformer = (context: Context) => { + const { spec, cell } = context; + + spec.nameField = cell.color; + spec.valueField = cell.size; + spec.nameProperty = cell.color; + return { spec }; +}; + +export const mapDisplayConf: Transformer = (context: Context) => { + const { spec } = context; + spec.legends = [ + { + visible: true, + type: 'color', + field: 'value', + orient: 'bottom', + position: 'start' + } + ]; + return { spec }; +}; diff --git a/packages/vmind/src/applications/chartGeneration/types.ts b/packages/vmind/src/applications/chartGeneration/types.ts index ede8319c..f3656d90 100644 --- a/packages/vmind/src/applications/chartGeneration/types.ts +++ b/packages/vmind/src/applications/chartGeneration/types.ts @@ -1,6 +1,6 @@ export type Cell = { //字段映射,可用的视觉通道:["x","y","color","size","angle","time"] - x?: string; + x?: string | string[]; y?: string | string[]; color?: string; size?: string; @@ -11,4 +11,5 @@ export type Cell = { target?: string; value?: string; category?: string; + group?: string; }; diff --git a/packages/vmind/src/applications/types.ts b/packages/vmind/src/applications/types.ts index a71caba2..3fb577ce 100644 --- a/packages/vmind/src/applications/types.ts +++ b/packages/vmind/src/applications/types.ts @@ -1,6 +1,14 @@ -import type { ChartType, ILLMOptions, SimpleFieldInfo, VMindDataset, ChartTheme } from '../common/typings'; +import type { + BasemapOption, + ChartType, + ILLMOptions, + SimpleFieldInfo, + VMindDataset, + ChartTheme +} from '../common/typings'; import type { Cell } from './chartGeneration/types'; import type { InsightAlgorithm, VMindInsight } from './IngelligentInsight/types'; +import type { UncommonChartType } from '../common/typings'; //context of the DataExtraction Application export type DataExtractionContext = { @@ -38,6 +46,8 @@ export type ChartGenerationContext = { fieldInfo: SimpleFieldInfo[]; dataset?: VMindDataset; chartTypeList: ChartType[]; //supported chart list + uncommonChartTypeList: UncommonChartType[]; //supported uncommon chart list + basemapOption: BasemapOption; // only use in map chart } & { totalTime?: number; colors?: string[]; diff --git a/packages/vmind/src/base/taskNode/ruleBasedTaskNode.ts b/packages/vmind/src/base/taskNode/ruleBasedTaskNode.ts index 92168c9e..3bb46a87 100644 --- a/packages/vmind/src/base/taskNode/ruleBasedTaskNode.ts +++ b/packages/vmind/src/base/taskNode/ruleBasedTaskNode.ts @@ -32,7 +32,7 @@ export class RuleBasedTaskNode extends BaseTaskNode { this.updateContext({ ...this.context, ...context }); let pipelines = this.pipelines; if (isFunction(this.pipelines)) { @@ -40,13 +40,10 @@ export class RuleBasedTaskNode extends BaseTaskNode[]).reduce( - (pre: any, transformer: Transformer) => { - const res = transformer(pre); - return { ...pre, ...res }; - }, - context - ); + let result: any = context; + for (const transformer of pipelines as Transformer[]) { + result = { ...result, ...(await transformer(result)) }; + } return result; } catch (e: any) { console.error(`${this.name} error!`); diff --git a/packages/vmind/src/common/typings/index.ts b/packages/vmind/src/common/typings/index.ts index 73bc22a0..f6bd1027 100644 --- a/packages/vmind/src/common/typings/index.ts +++ b/packages/vmind/src/common/typings/index.ts @@ -75,7 +75,28 @@ export enum ChartType { BoxPlot = 'Box Plot', LinearProgress = 'Linear Progress chart', CircularProgress = 'Circular Progress chart', - LiquidChart = 'Liquid Chart' + LiquidChart = 'Liquid Chart', + BubbleCirclePacking = 'Bubble Circle Packing', + MapChart = 'Map Chart', + RangeColumnChart = 'Range Column Chart', + SunburstChart = 'Sunburst Chart', + TreemapChart = 'Treemap Chart', + Gauge = 'Gauge Chart', + // LinearProgressChart = 'Linear Progress Chart', + BasicHeatMap = 'Basic Heat Map', + VennChart = 'Venn Chart' +} + +export enum UncommonChartType { + BubbleCirclePacking = 'Bubble Circle Packing', + MapChart = 'Map Chart', + RangeColumnChart = 'Range Column Chart', + SunburstChart = 'Sunburst Chart', + TreemapChart = 'Treemap Chart', + Gauge = 'Gauge Chart', + LinearProgressChart = 'Linear Progress Chart', + BasicHeatMap = 'Basic Heat Map', + VennChart = 'Venn Chart' } export type GPTChartAdvisorResult = { @@ -181,3 +202,35 @@ export type VMindTheme = { }; export type { ITheme as ChartTheme } from '@visactor/vchart'; + +export enum mapRegionProjectionType { + ALBERS = 'albers', + ALBERS_USA = 'albersUsa', + AZIMUTHAL_EQUAL_AREA = 'azimuthalEqualArea', + AZIMUTHAL_EQUIDISTANT = 'azimuthalEquidistant', + CONIC_CONFORMAL = 'conicConformal', + CONIC_EQUAL_AREA = 'conicEqualArea', + CONIC_EQUIDISTANT = 'conicEquidistant', + EQUAL_EARTH = 'equalEarth', + EQUIRECTANGULAR = 'equirectangular', + GNOMONIC = 'gnomonic', + MERCATOR = 'mercator', + NATURAL_EARTH1 = 'naturalEarth1', + ORTHOGRAPHIC = 'orthographic', + STEREOGRAPHIC = 'stereographic', + TRANSVERSE_MERCATOR = 'transverseMercator' +} + +export enum MapRegionCoordinate { + CARTESIAN = 'cartesian', + POLAR = 'polar', + GEO = 'geo' +} + +export type BasemapOption = { + jsonUrl: string; + regionProjectType: mapRegionProjectionType; + regionCoordinate: MapRegionCoordinate; + zoom: number; + center: number[]; +}; diff --git a/packages/vmind/src/common/utils/utils.ts b/packages/vmind/src/common/utils/utils.ts index a92981a3..b1411b29 100644 --- a/packages/vmind/src/common/utils/utils.ts +++ b/packages/vmind/src/common/utils/utils.ts @@ -78,6 +78,9 @@ export const getFieldByRole = (fields: SimpleFieldInfo[], role: ROLE) => { export const getFieldByDataType = (fields: SimpleFieldInfo[], dataTypeList: DataType[]) => { return fields.find(f => dataTypeList.includes(f.type)); }; +export const getFieldsByDataType = (fields: SimpleFieldInfo[], dataTypeList: DataType[]) => { + return fields.filter(f => dataTypeList.includes(f.type)); +}; export const foldDatasetByYField = ( dataset: DataItem[], diff --git a/packages/vmind/src/core/VMind.ts b/packages/vmind/src/core/VMind.ts index 89163647..303cd865 100644 --- a/packages/vmind/src/core/VMind.ts +++ b/packages/vmind/src/core/VMind.ts @@ -7,7 +7,9 @@ import type { OuterPackages, VMindDataset, ChartType, - ChartTheme + ChartTheme, + UncommonChartType, + BasemapOption } from '../common/typings'; import { Model, ModelType } from '../common/typings'; import { getFieldInfoFromDataset, parseCSVData as parseCSVDataWithRule } from '../common/dataProcess'; @@ -23,7 +25,11 @@ import type { import applicationMetaList, { ApplicationType } from '../applications'; import { calculateTokenUsage } from '../common/utils/utils'; import { isNil } from '@visactor/vutils'; -import { SUPPORTED_CHART_LIST } from '../applications/chartGeneration/constants'; +import { + DEFAULT_MAP_OPTION, + SUPPORTED_CHART_LIST, + SUPPORTED_UNCOMMON_CHART_LIST +} from '../applications/chartGeneration/constants'; import { BaseApplication } from '../base/application'; import { fillSpecTemplateWithData } from '../common/specUtils'; import type { ApplicationMeta, TaskNode } from '../base/metaTypes'; @@ -170,6 +176,8 @@ class VMind { * @param colorPalette color palette of the chart * @param animationDuration duration of chart animation. * @param chartTypeList supported chart list. VMind will generate a chart among this list. + * @param uncommonChartTypeList uncommon chart types. If the user does not specify a type, uncommon types are not considered. + * @param basemapOption map chart's base map. Only use in map chart. * @returns spec and time duration of the chart. */ async generateChart( @@ -182,6 +190,8 @@ class VMind { animationDuration?: number; enableDataQuery?: boolean; theme?: ChartTheme | string; + uncommonChartTypeList?: UncommonChartType[]; + basemapOption?: BasemapOption; } ): Promise { const modelType = this.getModelType(); @@ -189,7 +199,15 @@ class VMind { let finalFieldInfo = fieldInfo; let queryDatasetUsage; - const { enableDataQuery, colorPalette, animationDuration, theme, chartTypeList } = options ?? {}; + const { + enableDataQuery, + colorPalette, + animationDuration, + theme, + chartTypeList, + uncommonChartTypeList, + basemapOption + } = options ?? {}; try { if (!isNil(dataset) && (isNil(enableDataQuery) || enableDataQuery) && modelType !== ModelType.CHART_ADVISOR) { //run data aggregation first @@ -223,7 +241,9 @@ class VMind { colors: colorPalette, totalTime: animationDuration, chartTheme: theme, - chartTypeList: chartTypeList ?? SUPPORTED_CHART_LIST + chartTypeList: chartTypeList ?? SUPPORTED_CHART_LIST, + uncommonChartTypeList: uncommonChartTypeList ?? SUPPORTED_UNCOMMON_CHART_LIST, + basemapOption: basemapOption ?? DEFAULT_MAP_OPTION }; const chartGenerationResult = await this.runApplication(ApplicationType.ChartGeneration, modelType, context); From c25982523b3e59a41d0359ee18aeac2572f2d826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=A2=E4=BB=95=E4=BC=98?= <2279038930@qq.com> Date: Mon, 22 Jul 2024 15:57:08 +0800 Subject: [PATCH 2/6] Remove uncommon chart types --- .../applications/chartGeneration/constants.ts | 3 +-- .../GPT/prompt/index.ts | 3 +-- .../GPT/prompt/template.ts | 5 ++--- packages/vmind/src/applications/types.ts | 2 -- packages/vmind/src/common/typings/index.ts | 12 ----------- packages/vmind/src/core/VMind.ts | 20 ++----------------- 6 files changed, 6 insertions(+), 39 deletions(-) diff --git a/packages/vmind/src/applications/chartGeneration/constants.ts b/packages/vmind/src/applications/chartGeneration/constants.ts index 07b0ec82..15376fb0 100644 --- a/packages/vmind/src/applications/chartGeneration/constants.ts +++ b/packages/vmind/src/applications/chartGeneration/constants.ts @@ -1,8 +1,7 @@ import type { BasemapOption } from '../../common/typings'; -import { ChartType, MapRegionCoordinate, UncommonChartType } from '../../common/typings'; +import { ChartType, MapRegionCoordinate } from '../../common/typings'; export const SUPPORTED_CHART_LIST = Object.values(ChartType); -export const SUPPORTED_UNCOMMON_CHART_LIST = Object.values(UncommonChartType); export const DEFAULT_MAP_OPTION: BasemapOption = { jsonUrl: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/geojson/world.json', diff --git a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/index.ts b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/index.ts index 8c162331..47a16d96 100644 --- a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/index.ts +++ b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/index.ts @@ -39,7 +39,7 @@ export class GPTChartGenerationPrompt extends Prompt chartKnowledgeDict[a].index - chartKnowledgeDict[b].index); @@ -75,7 +75,6 @@ export class GPTChartGenerationPrompt extends Prompt 0 ? '\nKnowledge' : ''} ${knowledge} diff --git a/packages/vmind/src/applications/types.ts b/packages/vmind/src/applications/types.ts index 3fb577ce..ec7fe39c 100644 --- a/packages/vmind/src/applications/types.ts +++ b/packages/vmind/src/applications/types.ts @@ -8,7 +8,6 @@ import type { } from '../common/typings'; import type { Cell } from './chartGeneration/types'; import type { InsightAlgorithm, VMindInsight } from './IngelligentInsight/types'; -import type { UncommonChartType } from '../common/typings'; //context of the DataExtraction Application export type DataExtractionContext = { @@ -46,7 +45,6 @@ export type ChartGenerationContext = { fieldInfo: SimpleFieldInfo[]; dataset?: VMindDataset; chartTypeList: ChartType[]; //supported chart list - uncommonChartTypeList: UncommonChartType[]; //supported uncommon chart list basemapOption: BasemapOption; // only use in map chart } & { totalTime?: number; diff --git a/packages/vmind/src/common/typings/index.ts b/packages/vmind/src/common/typings/index.ts index f6bd1027..22dedde0 100644 --- a/packages/vmind/src/common/typings/index.ts +++ b/packages/vmind/src/common/typings/index.ts @@ -87,18 +87,6 @@ export enum ChartType { VennChart = 'Venn Chart' } -export enum UncommonChartType { - BubbleCirclePacking = 'Bubble Circle Packing', - MapChart = 'Map Chart', - RangeColumnChart = 'Range Column Chart', - SunburstChart = 'Sunburst Chart', - TreemapChart = 'Treemap Chart', - Gauge = 'Gauge Chart', - LinearProgressChart = 'Linear Progress Chart', - BasicHeatMap = 'Basic Heat Map', - VennChart = 'Venn Chart' -} - export type GPTChartAdvisorResult = { CHART_TYPE: ChartType; DOUBLE_CHECK: string; diff --git a/packages/vmind/src/core/VMind.ts b/packages/vmind/src/core/VMind.ts index 303cd865..5059145c 100644 --- a/packages/vmind/src/core/VMind.ts +++ b/packages/vmind/src/core/VMind.ts @@ -8,7 +8,6 @@ import type { VMindDataset, ChartType, ChartTheme, - UncommonChartType, BasemapOption } from '../common/typings'; import { Model, ModelType } from '../common/typings'; @@ -25,11 +24,7 @@ import type { import applicationMetaList, { ApplicationType } from '../applications'; import { calculateTokenUsage } from '../common/utils/utils'; import { isNil } from '@visactor/vutils'; -import { - DEFAULT_MAP_OPTION, - SUPPORTED_CHART_LIST, - SUPPORTED_UNCOMMON_CHART_LIST -} from '../applications/chartGeneration/constants'; +import { DEFAULT_MAP_OPTION, SUPPORTED_CHART_LIST } from '../applications/chartGeneration/constants'; import { BaseApplication } from '../base/application'; import { fillSpecTemplateWithData } from '../common/specUtils'; import type { ApplicationMeta, TaskNode } from '../base/metaTypes'; @@ -176,7 +171,6 @@ class VMind { * @param colorPalette color palette of the chart * @param animationDuration duration of chart animation. * @param chartTypeList supported chart list. VMind will generate a chart among this list. - * @param uncommonChartTypeList uncommon chart types. If the user does not specify a type, uncommon types are not considered. * @param basemapOption map chart's base map. Only use in map chart. * @returns spec and time duration of the chart. */ @@ -190,7 +184,6 @@ class VMind { animationDuration?: number; enableDataQuery?: boolean; theme?: ChartTheme | string; - uncommonChartTypeList?: UncommonChartType[]; basemapOption?: BasemapOption; } ): Promise { @@ -199,15 +192,7 @@ class VMind { let finalFieldInfo = fieldInfo; let queryDatasetUsage; - const { - enableDataQuery, - colorPalette, - animationDuration, - theme, - chartTypeList, - uncommonChartTypeList, - basemapOption - } = options ?? {}; + const { enableDataQuery, colorPalette, animationDuration, theme, chartTypeList, basemapOption } = options ?? {}; try { if (!isNil(dataset) && (isNil(enableDataQuery) || enableDataQuery) && modelType !== ModelType.CHART_ADVISOR) { //run data aggregation first @@ -242,7 +227,6 @@ class VMind { totalTime: animationDuration, chartTheme: theme, chartTypeList: chartTypeList ?? SUPPORTED_CHART_LIST, - uncommonChartTypeList: uncommonChartTypeList ?? SUPPORTED_UNCOMMON_CHART_LIST, basemapOption: basemapOption ?? DEFAULT_MAP_OPTION }; From 19e47814664cc1b37552732779d6dacb0229c191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=A2=E4=BB=95=E4=BC=98?= <2279038930@qq.com> Date: Mon, 22 Jul 2024 17:04:48 +0800 Subject: [PATCH 3/6] Migrate needSizeFieldChatTypeList, it has nothing to do with prompt. --- .../applications/chartGeneration/constants.ts | 20 +++++++++++++ .../GPT/patcher/index.ts | 8 +++-- .../GPT/prompt/knowledges.ts | 29 ++++--------------- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/packages/vmind/src/applications/chartGeneration/constants.ts b/packages/vmind/src/applications/chartGeneration/constants.ts index 15376fb0..1a1c4041 100644 --- a/packages/vmind/src/applications/chartGeneration/constants.ts +++ b/packages/vmind/src/applications/chartGeneration/constants.ts @@ -3,6 +3,26 @@ import { ChartType, MapRegionCoordinate } from '../../common/typings'; export const SUPPORTED_CHART_LIST = Object.values(ChartType); +export const NEED_COLOR_FIELD_CHART_LIST = [ + ChartType.WordCloud, + ChartType.PieChart, + ChartType.RoseChart, + ChartType.MapChart, + ChartType.BubbleCirclePacking, + ChartType.VennChart, + ChartType.Gauge +]; + +export const NEED_SIZE_FIELD_CHART_LIST = [ + ChartType.ScatterPlot, + ChartType.WordCloud, + ChartType.MapChart, + ChartType.BubbleCirclePacking, + ChartType.VennChart, + ChartType.Gauge, + ChartType.BasicHeatMap +]; + export const DEFAULT_MAP_OPTION: BasemapOption = { jsonUrl: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/geojson/world.json', regionProjectType: null, diff --git a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/patcher/index.ts b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/patcher/index.ts index 1b6504a4..68f543c6 100644 --- a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/patcher/index.ts +++ b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/patcher/index.ts @@ -12,7 +12,7 @@ import { import { ChartType, DataType, ROLE } from '../../../../../../common/typings'; import type { GenerateChartAndFieldMapContext, GenerateChartAndFieldMapOutput } from '../../types'; import { isValidDataset } from '../../../../../../common/dataProcess'; -import { needSizeFieldChartList, needColorFieldChartList } from '../prompt/knowledges'; +import { NEED_SIZE_FIELD_CHART_LIST, NEED_COLOR_FIELD_CHART_LIST } from '../../../../constants'; const CARTESIAN_CHART_LIST = [ 'Dynamic Bar Chart', @@ -378,7 +378,9 @@ export const patchNeedColor: Transformer< > = (context: GenerateChartAndFieldMapContext & GenerateChartAndFieldMapOutput) => { const { chartType, cell, fieldInfo } = context; const cellNew: any = { ...cell }; - if (needColorFieldChartList.some(needColorFieldChartType => needColorFieldChartType.toUpperCase() === chartType)) { + if ( + NEED_COLOR_FIELD_CHART_LIST.some(needColorFieldChartType => needColorFieldChartType.toUpperCase() === chartType) + ) { const colorField = [cellNew.color, cellNew.x, cellNew.label, (cellNew as any).sets].filter(Boolean).flat(); if (colorField.length !== 0) { cellNew.color = colorField[0]; @@ -403,7 +405,7 @@ export const patchNeedSize: Transformer< > = (context: GenerateChartAndFieldMapContext & GenerateChartAndFieldMapOutput) => { const { chartType, cell, fieldInfo } = context; const cellNew: any = { ...cell }; - if (needSizeFieldChartList.some(needSizeFieldChartType => needSizeFieldChartType.toUpperCase() === chartType)) { + if (NEED_SIZE_FIELD_CHART_LIST.some(needSizeFieldChartType => needSizeFieldChartType.toUpperCase() === chartType)) { const sizeField = [cellNew.size, cellNew.value, cellNew.y, cellNew.radius, cellNew.angle].filter(Boolean).flat(); if (sizeField.length !== 0) { cellNew.size = sizeField[0]; diff --git a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/knowledges.ts b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/knowledges.ts index 76db8a3e..e9746388 100644 --- a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/knowledges.ts +++ b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/knowledges.ts @@ -2,31 +2,12 @@ import { ChartType } from '../../../../../../common/typings'; import { barChartExample1, dynamicBarChart1, lineChartExample1, lineChartExample2, pieChartExample1 } from './examples'; import type { ChartKnowledge } from './types'; - -export const needColorFieldChartList = [ - ChartType.WordCloud, - ChartType.PieChart, - ChartType.RoseChart, - ChartType.MapChart, - ChartType.BubbleCirclePacking, - ChartType.VennChart, - ChartType.Gauge -]; - -export const needSizeFieldChartList = [ - ChartType.ScatterPlot, - ChartType.WordCloud, - ChartType.MapChart, - ChartType.BubbleCirclePacking, - ChartType.VennChart, - ChartType.Gauge, - ChartType.BasicHeatMap -]; +import { NEED_SIZE_FIELD_CHART_LIST, NEED_COLOR_FIELD_CHART_LIST } from '../../../../constants'; const getColorKnowledge = (chartTypeList: ChartType[]) => { const validSet = new Set(chartTypeList); - if (needColorFieldChartList.some(chartType => validSet.has(chartType))) { - const includedCharts = chartTypeList.filter(chart => needColorFieldChartList.includes(chart)); + if (NEED_COLOR_FIELD_CHART_LIST.some(chartType => validSet.has(chartType))) { + const includedCharts = chartTypeList.filter(chart => NEED_COLOR_FIELD_CHART_LIST.includes(chart)); return ", can't be empty in " + includedCharts.join(', ') + '.'; } return '.'; @@ -34,8 +15,8 @@ const getColorKnowledge = (chartTypeList: ChartType[]) => { const getSizeKnowledge = (chartTypeList: ChartType[]) => { const validSet = new Set(chartTypeList); - if (needSizeFieldChartList.some(chartType => validSet.has(chartType))) { - const includedCharts = chartTypeList.filter(chart => needSizeFieldChartList.includes(chart)); + if (NEED_SIZE_FIELD_CHART_LIST.some(chartType => validSet.has(chartType))) { + const includedCharts = chartTypeList.filter(chart => NEED_SIZE_FIELD_CHART_LIST.includes(chart)); return ' Only used in ' + includedCharts.join(' , ') + '.'; } return '.'; From fb750799059b2c91b1c65d9fe5995cf0568b422e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=A2=E4=BB=95=E4=BC=98?= <2279038930@qq.com> Date: Tue, 23 Jul 2024 10:30:58 +0800 Subject: [PATCH 4/6] Adjust visual channels for Sunburst, Treemap, and Venn --- .../browser/src/constants/mockData.ts | 2 +- .../applications/chartGeneration/constants.ts | 29 +++++--- .../generateTypeAndFieldMap/GPT/index.ts | 2 - .../GPT/patcher/index.ts | 74 ++++++++----------- .../GPT/prompt/index.ts | 20 +++-- .../GPT/prompt/knowledges.ts | 58 +++++++++++---- .../getChartSpec/VChart/chartPipeline.ts | 15 ++-- .../getChartSpec/VChart/transformers.ts | 48 ++++++------ .../src/applications/chartGeneration/types.ts | 5 +- 9 files changed, 142 insertions(+), 111 deletions(-) diff --git a/packages/vmind/__tests__/browser/src/constants/mockData.ts b/packages/vmind/__tests__/browser/src/constants/mockData.ts index 1f5fe7e5..e60bd7ad 100644 --- a/packages/vmind/__tests__/browser/src/constants/mockData.ts +++ b/packages/vmind/__tests__/browser/src/constants/mockData.ts @@ -4795,7 +4795,7 @@ Category Eight,18,34 }; export const sunburstChartData = { - csv: `Country,Region,Category,Value + csv: `Category0,Category1,Category2,Value Country A,Region1,Office Supplies,824 Country A,Region1,Furniture,920 Country A,Region1,Electronic equipment,936 diff --git a/packages/vmind/src/applications/chartGeneration/constants.ts b/packages/vmind/src/applications/chartGeneration/constants.ts index 1a1c4041..00bb380e 100644 --- a/packages/vmind/src/applications/chartGeneration/constants.ts +++ b/packages/vmind/src/applications/chartGeneration/constants.ts @@ -3,24 +3,31 @@ import { ChartType, MapRegionCoordinate } from '../../common/typings'; export const SUPPORTED_CHART_LIST = Object.values(ChartType); -export const NEED_COLOR_FIELD_CHART_LIST = [ +export const NEED_COLOR_FIELD_CHART_LIST = [ChartType.PieChart, ChartType.RoseChart]; + +export const NEED_SIZE_FIELD_CHART_LIST = [ChartType.ScatterPlot, ChartType.BasicHeatMap]; + +export const NEED_COLOR_AND_SIZE_CHART_LIST = [ ChartType.WordCloud, - ChartType.PieChart, - ChartType.RoseChart, ChartType.MapChart, ChartType.BubbleCirclePacking, ChartType.VennChart, - ChartType.Gauge + ChartType.Gauge, + ChartType.SunburstChart, + ChartType.TreemapChart ]; -export const NEED_SIZE_FIELD_CHART_LIST = [ +export const CARTESIAN_CHART_LIST = [ + ChartType.DynamicBarChart, + ChartType.BarChart, + ChartType.LineChart, ChartType.ScatterPlot, - ChartType.WordCloud, - ChartType.MapChart, - ChartType.BubbleCirclePacking, - ChartType.VennChart, - ChartType.Gauge, - ChartType.BasicHeatMap + ChartType.FunnelChart, + ChartType.DualAxisChart, + ChartType.WaterFallChart, + ChartType.BoxPlot, + ChartType.RangeColumnChart, + ChartType.LinearProgressChart ]; export const DEFAULT_MAP_OPTION: BasemapOption = { diff --git a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/index.ts b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/index.ts index bd2fbd4d..2ba28beb 100644 --- a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/index.ts +++ b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/index.ts @@ -18,7 +18,6 @@ import { patchNeedSize, patchPieChart, patchRangeColumnChart, - patchSunburstAndTreemapChart, patchWordCloud, patchYField } from './patcher'; @@ -49,7 +48,6 @@ const ChartGenerationTaskNodeGPTMeta: LLMBasedTaskNodeMeta< patchDynamicBarChart, patchRangeColumnChart, patchLinearProgressChart, - patchSunburstAndTreemapChart, patchBasicHeatMapChart, patchCartesianXField ], diff --git a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/patcher/index.ts b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/patcher/index.ts index 68f543c6..59f4dd9c 100644 --- a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/patcher/index.ts +++ b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/patcher/index.ts @@ -12,20 +12,12 @@ import { import { ChartType, DataType, ROLE } from '../../../../../../common/typings'; import type { GenerateChartAndFieldMapContext, GenerateChartAndFieldMapOutput } from '../../types'; import { isValidDataset } from '../../../../../../common/dataProcess'; -import { NEED_SIZE_FIELD_CHART_LIST, NEED_COLOR_FIELD_CHART_LIST } from '../../../../constants'; - -const CARTESIAN_CHART_LIST = [ - 'Dynamic Bar Chart', - 'Bar Chart', - 'Line Chart', - 'Scatter Plot', - 'Funnel Chart', - 'Dual Axis Chart', - 'Waterfall Chart', - 'Box Plot', - 'Range Column Chart', - 'linear Progress Chart' -]; +import { + NEED_COLOR_FIELD_CHART_LIST, + NEED_SIZE_FIELD_CHART_LIST, + CARTESIAN_CHART_LIST, + NEED_COLOR_AND_SIZE_CHART_LIST +} from '../../../../constants'; export const patchAxisField: Transformer< GenerateChartAndFieldMapContext & GenerateChartAndFieldMapOutput, @@ -379,9 +371,10 @@ export const patchNeedColor: Transformer< const { chartType, cell, fieldInfo } = context; const cellNew: any = { ...cell }; if ( - NEED_COLOR_FIELD_CHART_LIST.some(needColorFieldChartType => needColorFieldChartType.toUpperCase() === chartType) + NEED_COLOR_FIELD_CHART_LIST.some(needColorFieldChartType => needColorFieldChartType.toUpperCase() === chartType) || + NEED_COLOR_AND_SIZE_CHART_LIST.some(needColorFieldChartType => needColorFieldChartType.toUpperCase() === chartType) ) { - const colorField = [cellNew.color, cellNew.x, cellNew.label, (cellNew as any).sets].filter(Boolean).flat(); + const colorField = [cellNew.color, cellNew.x, cellNew.label, (cellNew as any).sets].filter(Boolean); if (colorField.length !== 0) { cellNew.color = colorField[0]; } else { @@ -405,7 +398,10 @@ export const patchNeedSize: Transformer< > = (context: GenerateChartAndFieldMapContext & GenerateChartAndFieldMapOutput) => { const { chartType, cell, fieldInfo } = context; const cellNew: any = { ...cell }; - if (NEED_SIZE_FIELD_CHART_LIST.some(needSizeFieldChartType => needSizeFieldChartType.toUpperCase() === chartType)) { + if ( + NEED_SIZE_FIELD_CHART_LIST.some(needSizeFieldChartType => needSizeFieldChartType.toUpperCase() === chartType) || + NEED_COLOR_AND_SIZE_CHART_LIST.some(needSizeFieldChartType => needSizeFieldChartType.toUpperCase() === chartType) + ) { const sizeField = [cellNew.size, cellNew.value, cellNew.y, cellNew.radius, cellNew.angle].filter(Boolean).flat(); if (sizeField.length !== 0) { cellNew.size = sizeField[0]; @@ -433,7 +429,7 @@ export const patchRangeColumnChart: Transformer< const cellNew = { ...cell }; const remainedFields = getRemainedFields(cellNew, fieldInfo); const numericFields = getFieldsByDataType(remainedFields, [DataType.FLOAT, DataType.INT]); - if (chartType === ('RANGE COLUMN CHART' as ChartType)) { + if (chartType === ChartType.RangeColumnChart.toUpperCase()) { if (cellNew.y && cellNew.y instanceof Array && cellNew.y.length === 2) { return { cell: cellNew }; } @@ -458,7 +454,20 @@ export const patchLinearProgressChart: Transformer< > = (context: GenerateChartAndFieldMapContext & GenerateChartAndFieldMapOutput) => { const { chartType, cell, fieldInfo } = context; const cellNew = { ...cell }; - if (chartType === ('Linear Progress Chart' as ChartType)) { + if (chartType === ChartType.LinearProgressChart.toUpperCase()) { + const xField = [cellNew.x, cellNew.color].filter(Boolean).flat(); + if (xField.length !== 0) { + cellNew.x = xField[0]; + } else { + const remainedFields = getRemainedFields(cellNew, fieldInfo); + const xField = getFieldByRole(remainedFields, ROLE.DIMENSION); + if (xField) { + cellNew.x = xField.fieldName; + } else { + cellNew.x = remainedFields[0].fieldName; + } + } + const yField = [cellNew.y, cellNew.size, cellNew.value, cellNew.radius, cellNew.angle].filter(Boolean).flat(); if (yField.length !== 0) { cellNew.y = yField[0]; @@ -478,38 +487,13 @@ export const patchLinearProgressChart: Transformer< }; }; -export const patchSunburstAndTreemapChart: Transformer< - GenerateChartAndFieldMapContext & GenerateChartAndFieldMapOutput, - Partial -> = (context: GenerateChartAndFieldMapContext & GenerateChartAndFieldMapOutput) => { - const { chartType, cell } = context; - const cellNew: any = { ...cell }; - - if (chartType === ('SUNBURST CHART' as ChartType) || chartType === ('TREEMAP CHART' as ChartType)) { - cellNew.y = [cellNew.y, cellNew.value, cellNew.radius, cellNew.size, cellNew.angle].filter(Boolean).flat(); - if (cellNew.y.length !== 0) { - cellNew.y = cellNew.y[0]; - } else { - throw Error( - 'The y-axis of the sunburst chart and the treemap chart requires a numeric field,' + - 'but the result of data aggregation does not have a numeric field' - ); - } - } - - return { - //...context, - cell: cellNew - }; -}; - export const patchBasicHeatMapChart: Transformer< GenerateChartAndFieldMapContext & GenerateChartAndFieldMapOutput, Partial > = (context: GenerateChartAndFieldMapContext & GenerateChartAndFieldMapOutput) => { const { chartType, cell, fieldInfo } = context; const cellNew: any = { ...cell }; - if (chartType === ('BASIC HEAT MAP' as ChartType)) { + if (chartType === ChartType.BasicHeatMap.toUpperCase()) { const colorField = [cellNew.x, cellNew.y, cellNew.label, cellNew.color].filter(Boolean).flat(); if (colorField.length >= 2) { cellNew.x = colorField[0]; diff --git a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/index.ts b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/index.ts index 47a16d96..aaf6b2c7 100644 --- a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/index.ts +++ b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/index.ts @@ -3,7 +3,14 @@ import { ChartAdvisorPromptEnglish } from './template'; import type { GenerateChartAndFieldMapContext } from '../../types'; import { pick } from '@visactor/vutils'; import { getStrFromArray } from '../../../../../../common/utils/utils'; -import { chartGenerationConstraints, chartKnowledgeDict, defaultExamples, visualChannelInfoMap } from './knowledges'; +import { + chartGenerationConstraints, + chartKnowledgeDict, + defaultExamples, + getCartesianCoordinateSystemKnowledge, + getNeedColorAndSizeKnowledge, + visualChannelInfoMap +} from './knowledges'; import { uniqArray } from '@visactor/vutils'; const patchUserInput = (userInput: string) => { @@ -44,10 +51,13 @@ export class GPTChartGenerationPrompt extends Prompt chartKnowledgeDict[a].index - chartKnowledgeDict[b].index); - const chartKnowledge = sortedChartTypeList.reduce((res, chartType) => { - const { knowledge } = chartKnowledgeDict[chartType]; - return [...res, ...(knowledge ?? [])]; - }, []); + const chartKnowledge = sortedChartTypeList.reduce( + (res, chartType) => { + const { knowledge } = chartKnowledgeDict[chartType]; + return [...res, ...(knowledge ?? [])]; + }, + [getNeedColorAndSizeKnowledge(chartTypeList), getCartesianCoordinateSystemKnowledge(chartTypeList)] + ); const knowledgeStr = getStrFromArray(chartKnowledge); diff --git a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/knowledges.ts b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/knowledges.ts index e9746388..16e4cfa5 100644 --- a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/knowledges.ts +++ b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/knowledges.ts @@ -2,7 +2,12 @@ import { ChartType } from '../../../../../../common/typings'; import { barChartExample1, dynamicBarChart1, lineChartExample1, lineChartExample2, pieChartExample1 } from './examples'; import type { ChartKnowledge } from './types'; -import { NEED_SIZE_FIELD_CHART_LIST, NEED_COLOR_FIELD_CHART_LIST } from '../../../../constants'; +import { + CARTESIAN_CHART_LIST, + NEED_COLOR_FIELD_CHART_LIST, + NEED_SIZE_FIELD_CHART_LIST, + NEED_COLOR_AND_SIZE_CHART_LIST +} from '../../../../constants'; const getColorKnowledge = (chartTypeList: ChartType[]) => { const validSet = new Set(chartTypeList); @@ -17,7 +22,31 @@ const getSizeKnowledge = (chartTypeList: ChartType[]) => { const validSet = new Set(chartTypeList); if (NEED_SIZE_FIELD_CHART_LIST.some(chartType => validSet.has(chartType))) { const includedCharts = chartTypeList.filter(chart => NEED_SIZE_FIELD_CHART_LIST.includes(chart)); - return ' Only used in ' + includedCharts.join(' , ') + '.'; + return ", can't be empty in " + includedCharts.join(' , ') + '.'; + } + return '.'; +}; + +export const getNeedColorAndSizeKnowledge = (chartTypeList: ChartType[]) => { + const validSet = new Set(chartTypeList); + if (NEED_COLOR_AND_SIZE_CHART_LIST.some(chartType => validSet.has(chartType))) { + const includedCharts = chartTypeList.filter(chart => NEED_COLOR_AND_SIZE_CHART_LIST.includes(chart)); + return ( + includedCharts.join(', ') + + ' all commonly use color to represent different categories and size to represent the magnitude of the values. Map your data fields to the color (representing the category) and size (representing the value) visual channels instead of using the x-axis of the Cartesian coordinate system.' + ); + } + return '.'; +}; + +export const getCartesianCoordinateSystemKnowledge = (chartTypeList: ChartType[]) => { + const validSet = new Set(chartTypeList); + if (CARTESIAN_CHART_LIST.some(chartType => validSet.has(chartType))) { + const includedCharts = chartTypeList.filter(chart => CARTESIAN_CHART_LIST.includes(chart)); + return ( + includedCharts.join(', ') + + ' are visualizations based on the Cartesian coordinate system. The Cartesian coordinate system is usually used to show linear relationships and numerical changes. Please map the data fields to the x-axis (independent variable) and the y-axis (dependent variable).' + ); } return '.'; }; @@ -41,9 +70,7 @@ export const visualChannelInfoMap = { target: (chartTypeList: ChartType[]) => "the field mapped to the target channel. Only used in Sankey Chart. Can't be empty in Sankey Chart.", value: (chartTypeList: ChartType[]) => - "the field mapped to the value channel. Only used in Sankey Chart. Can't be empty in Sankey Chart.", - group: (chartTypeList: ChartType[]) => - "the field mapped to the value channel. Only used in Venn Chart. Can't be empty in Venn Chart. Digital IDs are often used to distinguish whether data is in the same group." + "the field mapped to the value channel. Only used in Sankey Chart. Can't be empty in Sankey Chart." }; export const chartKnowledgeDict: ChartKnowledge = { [ChartType.BarChart]: { @@ -69,7 +96,10 @@ export const chartKnowledgeDict: ChartKnowledge = { [ChartType.WordCloud]: { index: 6, visualChannels: ['color', 'size'], - examples: [] + examples: [], + knowledge: [ + 'Word cloud is an effective visualization tool that can intuitively display the frequency of keywords. The size of a keyword is proportional to its popularity, which is suitable for displaying the importance and trends in text data.' + ] }, [ChartType.RoseChart]: { index: 7, @@ -161,19 +191,17 @@ export const chartKnowledgeDict: ChartKnowledge = { }, [ChartType.SunburstChart]: { index: 20, - visualChannels: ['x', 'y'], + visualChannels: ['color', 'size'], examples: [], knowledge: [ - 'The x field of a sunburst chart can be an array. The order of the elements in the array needs to be sorted from large to small according to the coverage described by the data field.' + 'The colors field for sunburst chart and treemap chart must be an array. The order of the elements in the array needs to be sorted from large to small according to the coverage described by the data field.' ] }, [ChartType.TreemapChart]: { index: 21, - visualChannels: ['x', 'y'], + visualChannels: ['color', 'size'], examples: [], - knowledge: [ - 'The x field of a treemap chart can be an array. The order of the elements in the array needs to be sorted from large to small according to the coverage described by the data field.' - ] + knowledge: [] }, [ChartType.Gauge]: { index: 22, @@ -195,9 +223,11 @@ export const chartKnowledgeDict: ChartKnowledge = { }, [ChartType.VennChart]: { index: 25, - visualChannels: ['size', 'color', 'group'], + visualChannels: ['size', 'color'], examples: [], - knowledge: ['The venn chart must contain three fields: group, size and color.'] + knowledge: [ + 'The color field of the Venn diagram requires an array of length 2. The field with subscript 0 maps to the sets, and the field with subscript 1 maps to the name.' + ] } }; diff --git a/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/chartPipeline.ts b/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/chartPipeline.ts index f03b9876..fe96ceff 100644 --- a/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/chartPipeline.ts +++ b/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/chartPipeline.ts @@ -208,15 +208,16 @@ const pipelineBubbleCirclePacking = [ theme ]; -const pipelineMapChart = [chartType, basemap, arrayData, mapField, mapDisplayConf, theme]; -const pipelineRangeColumn = [chartType, data, rangeColumnField, rangeColumnDisplayConf, theme]; -const pipelineSunburst = [chartType, sunburstData, sunburstOrTreemapField, sunburstDisplayConf, theme]; -const pipelineTreemap = [chartType, treemapData, sunburstOrTreemapField, treemapDisplayConf, theme]; -const pipelineGauge = [chartType, arrayData, gaugeField, gaugeDisplayConf, theme]; -// const pipelineLinearProgress = [chartType, arrayData, linearProgressField, linearProgressDisplayConf, theme]; +const pipelineMapChart = [chartType, basemap, color, arrayData, mapField, mapDisplayConf, theme]; +const pipelineRangeColumn = [chartType, data, color, rangeColumnField, rangeColumnDisplayConf, theme]; +const pipelineSunburst = [chartType, sunburstData, color, sunburstOrTreemapField, sunburstDisplayConf, theme]; +const pipelineTreemap = [chartType, treemapData, color, sunburstOrTreemapField, treemapDisplayConf, theme]; +const pipelineGauge = [chartType, arrayData, color, gaugeField, gaugeDisplayConf, theme]; +// const pipelineLinearProgress = [chartType, arrayData, color, linearProgressField, linearProgressDisplayConf, theme]; const pipelineBasicHeatMap = [ chartType, arrayData, + color, basicHeatMapSeries, basicHeatMapRegion, basicHeatMapColor, @@ -224,7 +225,7 @@ const pipelineBasicHeatMap = [ basicHeatMapLegend, theme ]; -const pipelineVenn = [chartType, registerChart, vennData, vennField, legend, theme]; +const pipelineVenn = [chartType, registerChart, vennData, color, vennField, legend, theme]; const pipelineMap: { [chartType: string]: any } = { 'BAR CHART': pipelineBar, diff --git a/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/transformers.ts b/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/transformers.ts index 811c0673..07f14524 100644 --- a/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/transformers.ts +++ b/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/transformers.ts @@ -753,7 +753,7 @@ export const cartesianBar: Transformer = (context: const cellNew = { ...cell }; const flattenedXField = Array.isArray(cell.x) ? cell.x : [cell.x]; if (cell.color && cell.color.length > 0 && cell.color !== cell.x) { - flattenedXField.push(cell.color); + flattenedXField.push(cell.color as string); } spec.xField = flattenedXField; spec.yField = cell.y; @@ -1155,7 +1155,7 @@ export const animationCartesianPie: Transformer = ( const { spec } = context; const totalTime = context.totalTime ?? DEFAULT_PIE_VIDEO_LENGTH; - const groupKey = context.cell.color; + const groupKey = context.cell.color as string; const dataValues = spec.data.values as any[]; const groupNum = dataValues.map(d => d[groupKey!]).filter(onlyUnique).length; //const delay = totalTime / groupNum - 1000; @@ -1470,21 +1470,21 @@ export const rangeColumnDisplayConf: Transformer = export const sunburstData: Transformer = (context: Context) => { const { dataset, cell, spec } = context; - spec.data = { id: 'data', values: getSunburstData(dataset, cell.x, 0, cell.y) }; + spec.data = { id: 'data', values: getSunburstData(dataset, cell.color, 0, cell.size) }; return { spec }; }; export const getSunburstData: any = ( dataset: VMindDataset, - xField: string[] | string, + colorField: string[] | string, index: number, - yField: string + sizeField: string ) => { - if (xField.length - 1 === index) { + if (colorField.length - 1 === index) { return Array.from( new Set( dataset.map(data => { - return { name: data[xField[index]], value: data[yField] }; + return { name: data[colorField[index]], value: data[sizeField] }; }) ) ); @@ -1493,15 +1493,15 @@ export const getSunburstData: any = ( const values = Array.from( new Set( dataset.map(data => { - return data[xField[index]]; + return data[colorField[index]]; }) ) ); return values.map(value => { const currentDataset = dataset.filter(data => { - return data[xField[index]] === value; + return data[colorField[index]] === value; }); - return { name: value, children: getSunburstData(currentDataset, xField, index + 1, yField) }; + return { name: value, children: getSunburstData(currentDataset, colorField, index + 1, sizeField) }; }); }; @@ -1566,21 +1566,21 @@ export const sunburstDisplayConf: Transformer = (co export const treemapData: Transformer = (context: Context) => { const { dataset, cell, spec } = context; - spec.data = { id: 'data', values: getTreemapData(dataset, cell.x, 0, cell.y) }; + spec.data = { id: 'data', values: getTreemapData(dataset, cell.color, 0, cell.size) }; return { spec }; }; export const getTreemapData: any = ( dataset: VMindDataset, - xField: string[] | string, + colorField: string[] | string, index: number, - yField: string + sizeField: string ) => { - if (xField.length - 1 === index) { + if (colorField.length - 1 === index) { return Array.from( new Set( dataset.map(data => { - return { name: data[xField[index]], value: data[yField] }; + return { name: data[colorField[index]], value: data[sizeField] }; }) ) ); @@ -1589,18 +1589,18 @@ export const getTreemapData: any = ( const values = Array.from( new Set( dataset.map(data => { - return data[xField[index]]; + return data[colorField[index]]; }) ) ); return values.map(value => { const currentDataset = dataset.filter(data => { - return data[xField[index]] === value; + return data[colorField[index]] === value; }); - if (currentDataset[0] && currentDataset[0][xField[index + 1]] === '') { - return { name: value, value: currentDataset[0][yField] }; + if (currentDataset[0] && currentDataset[0][colorField[index + 1]] === '') { + return { name: value, value: currentDataset[0][sizeField] }; } - return { name: value, children: getTreemapData(currentDataset, xField, index + 1, yField) }; + return { name: value, children: getTreemapData(currentDataset, colorField, index + 1, sizeField) }; }); }; @@ -1662,11 +1662,13 @@ export const linearProgressDisplayConf: Transformer export const vennData: Transformer = (context: Context) => { const { dataset, spec, cell } = context; const id2dataMap = {}; + const setsField = cell.color[0]; + const nameField = cell.color[1]; dataset.forEach(data => { - if (id2dataMap[data[cell.group]]) { - id2dataMap[data[cell.group]].sets.push(data[cell.color]); + if (id2dataMap[data[setsField]]) { + id2dataMap[data[setsField]].sets.push(data[nameField]); } else { - id2dataMap[data[cell.group]] = { sets: [data[cell.color]], value: data[cell.size] }; + id2dataMap[data[setsField]] = { sets: [data[nameField]], value: data[cell.size] }; } }); spec.data = { diff --git a/packages/vmind/src/applications/chartGeneration/types.ts b/packages/vmind/src/applications/chartGeneration/types.ts index f3656d90..5103fed2 100644 --- a/packages/vmind/src/applications/chartGeneration/types.ts +++ b/packages/vmind/src/applications/chartGeneration/types.ts @@ -1,8 +1,8 @@ export type Cell = { //字段映射,可用的视觉通道:["x","y","color","size","angle","time"] - x?: string | string[]; + x?: string; y?: string | string[]; - color?: string; + color?: string | string[]; size?: string; angle?: string; radius?: string; @@ -11,5 +11,4 @@ export type Cell = { target?: string; value?: string; category?: string; - group?: string; }; From f8871b3de757f88ab2ab7ba860f57e8db53af0b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=A2=E4=BB=95=E4=BC=98?= <2279038930@qq.com> Date: Tue, 23 Jul 2024 11:23:56 +0800 Subject: [PATCH 5/6] Adjust the registration timing of the map basemap --- .../src/pages/ChartGeneration/DataInput.tsx | 9 +++++++++ .../applications/chartGeneration/constants.ts | 1 - .../getChartSpec/VChart/transformers.ts | 20 +++++++++++-------- .../src/base/taskNode/ruleBasedTaskNode.ts | 13 +++++++----- packages/vmind/src/common/typings/index.ts | 1 - 5 files changed, 29 insertions(+), 15 deletions(-) diff --git a/packages/vmind/__tests__/browser/src/pages/ChartGeneration/DataInput.tsx b/packages/vmind/__tests__/browser/src/pages/ChartGeneration/DataInput.tsx index c14daebb..b4614f9e 100644 --- a/packages/vmind/__tests__/browser/src/pages/ChartGeneration/DataInput.tsx +++ b/packages/vmind/__tests__/browser/src/pages/ChartGeneration/DataInput.tsx @@ -54,6 +54,7 @@ import { import VMind, { ArcoTheme, builtinThemeMap, BuiltinThemeType } from '../../../../../src/index'; import { Model } from '../../../../../src/index'; import { isArray } from '@visactor/vutils'; +import VChart from '@visactor/vchart'; const TextArea = Input.TextArea; const Option = Select.Option; @@ -117,6 +118,7 @@ const ModelConfigMap: any = { }; const OPENAI_API_URL = 'https://api.openai.com/v1/chat/completions'; +const MAP_CHART_BASEMAP = 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/geojson/world.json'; const specTemplateTest = false; export function DataInput(props: IPropsType) { const defaultDataKey = Object.keys(demoDataList)[3]; @@ -168,6 +170,13 @@ export function DataInput(props: IPropsType) { const finalDataset = specTemplateTest && model !== Model.CHART_ADVISOR ? undefined : dataset; const startTime = new Date().getTime(); + + // The map chart requires the user to register a base map named map in advance using VChart.registerMap + if (csv === demoDataList.Map.csv) { + const response = await fetch(MAP_CHART_BASEMAP); + const geoJson = await response.json(); + VChart.registerMap('map', geoJson); + } const chartGenerationRes = await vmind.generateChart(describe, finalFieldInfo, finalDataset, { //enableDataQuery: false, //chartTypeList: [ChartType.BarChart, ChartType.LineChart], diff --git a/packages/vmind/src/applications/chartGeneration/constants.ts b/packages/vmind/src/applications/chartGeneration/constants.ts index 00bb380e..ff6af9f6 100644 --- a/packages/vmind/src/applications/chartGeneration/constants.ts +++ b/packages/vmind/src/applications/chartGeneration/constants.ts @@ -31,7 +31,6 @@ export const CARTESIAN_CHART_LIST = [ ]; export const DEFAULT_MAP_OPTION: BasemapOption = { - jsonUrl: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/geojson/world.json', regionProjectType: null, regionCoordinate: MapRegionCoordinate.GEO, zoom: 1, diff --git a/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/transformers.ts b/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/transformers.ts index 07f14524..27d25c24 100644 --- a/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/transformers.ts +++ b/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/transformers.ts @@ -1,6 +1,5 @@ import type { Transformer } from '../../../../../base/tools/transformer'; import { registerVennChart } from '@visactor/vchart'; -import VChart from '@visactor/vchart'; import type { GetChartSpecContext, GetChartSpecOutput } from '../types'; import { COLOR_THEMES, @@ -1802,12 +1801,8 @@ export const basicHeatMapLegend: Transformer = (con return { spec }; }; -export const basemap: Transformer> = async (context: Context) => { +export const basemap: Transformer = (context: Context) => { const { basemapOption, spec } = context; - const jsonUrl = basemapOption.jsonUrl; - const response = await fetch(jsonUrl); - const geoJson = await response.json(); - VChart.registerMap('map', geoJson); if (basemapOption.regionProjectType) { spec.region = [ { @@ -1839,15 +1834,24 @@ export const mapField: Transformer = (context: Cont }; export const mapDisplayConf: Transformer = (context: Context) => { - const { spec } = context; + const { spec, cell } = context; spec.legends = [ { visible: true, type: 'color', - field: 'value', + field: cell.size, orient: 'bottom', position: 'start' } ]; + spec.area = { + style: { + fill: { + field: cell.size, + scale: 'color', + changeDomain: 'replace' + } + } + }; return { spec }; }; diff --git a/packages/vmind/src/base/taskNode/ruleBasedTaskNode.ts b/packages/vmind/src/base/taskNode/ruleBasedTaskNode.ts index 3bb46a87..92168c9e 100644 --- a/packages/vmind/src/base/taskNode/ruleBasedTaskNode.ts +++ b/packages/vmind/src/base/taskNode/ruleBasedTaskNode.ts @@ -32,7 +32,7 @@ export class RuleBasedTaskNode extends BaseTaskNode { + executeTask(context: Context): Result | TaskError { this.updateContext({ ...this.context, ...context }); let pipelines = this.pipelines; if (isFunction(this.pipelines)) { @@ -40,10 +40,13 @@ export class RuleBasedTaskNode extends BaseTaskNode[]) { - result = { ...result, ...(await transformer(result)) }; - } + const result: Result = (pipelines as Transformer[]).reduce( + (pre: any, transformer: Transformer) => { + const res = transformer(pre); + return { ...pre, ...res }; + }, + context + ); return result; } catch (e: any) { console.error(`${this.name} error!`); diff --git a/packages/vmind/src/common/typings/index.ts b/packages/vmind/src/common/typings/index.ts index 22dedde0..2afca682 100644 --- a/packages/vmind/src/common/typings/index.ts +++ b/packages/vmind/src/common/typings/index.ts @@ -216,7 +216,6 @@ export enum MapRegionCoordinate { } export type BasemapOption = { - jsonUrl: string; regionProjectType: mapRegionProjectionType; regionCoordinate: MapRegionCoordinate; zoom: number; From 7099ae894980d0da6ce7662c21da5d048d46b45a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=A2=E4=BB=95=E4=BC=98?= <2279038930@qq.com> Date: Tue, 23 Jul 2024 14:56:01 +0800 Subject: [PATCH 6/6] Use enumerations instead of chart type strings; merge linear progress charts --- .../browser/src/constants/mockData.ts | 4 +- .../applications/chartGeneration/constants.ts | 8 +- .../GPT/patcher/index.ts | 24 +++--- .../GPT/prompt/knowledges.ts | 14 +--- .../getChartSpec/VChart/chartPipeline.ts | 75 ++++++++---------- .../getChartSpec/VChart/transformers.ts | 79 ++++++------------- 6 files changed, 81 insertions(+), 123 deletions(-) diff --git a/packages/vmind/__tests__/browser/src/constants/mockData.ts b/packages/vmind/__tests__/browser/src/constants/mockData.ts index e60bd7ad..25de468e 100644 --- a/packages/vmind/__tests__/browser/src/constants/mockData.ts +++ b/packages/vmind/__tests__/browser/src/constants/mockData.ts @@ -5324,11 +5324,11 @@ export const mockUserTextInput10 = { export const mockProgressData = { csv: `年份,进度 2024,0.56`, - input: '帮我展示今年的进度数据' + input: '请使用环形进度图帮我展示今年的进度数据' }; export const liquidData = { csv: `进度 0.56`, - input: '展示进度数据' + input: '请使用水波图展示进度数据' }; diff --git a/packages/vmind/src/applications/chartGeneration/constants.ts b/packages/vmind/src/applications/chartGeneration/constants.ts index ff6af9f6..4326f34e 100644 --- a/packages/vmind/src/applications/chartGeneration/constants.ts +++ b/packages/vmind/src/applications/chartGeneration/constants.ts @@ -3,7 +3,7 @@ import { ChartType, MapRegionCoordinate } from '../../common/typings'; export const SUPPORTED_CHART_LIST = Object.values(ChartType); -export const NEED_COLOR_FIELD_CHART_LIST = [ChartType.PieChart, ChartType.RoseChart]; +export const NEED_COLOR_FIELD_CHART_LIST = [ChartType.PieChart, ChartType.RoseChart, ChartType.LinearProgress]; export const NEED_SIZE_FIELD_CHART_LIST = [ChartType.ScatterPlot, ChartType.BasicHeatMap]; @@ -14,7 +14,9 @@ export const NEED_COLOR_AND_SIZE_CHART_LIST = [ ChartType.VennChart, ChartType.Gauge, ChartType.SunburstChart, - ChartType.TreemapChart + ChartType.TreemapChart, + ChartType.CircularProgress, + ChartType.LiquidChart ]; export const CARTESIAN_CHART_LIST = [ @@ -27,7 +29,7 @@ export const CARTESIAN_CHART_LIST = [ ChartType.WaterFallChart, ChartType.BoxPlot, ChartType.RangeColumnChart, - ChartType.LinearProgressChart + ChartType.LinearProgress ]; export const DEFAULT_MAP_OPTION: BasemapOption = { diff --git a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/patcher/index.ts b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/patcher/index.ts index 59f4dd9c..6cab9455 100644 --- a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/patcher/index.ts +++ b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/patcher/index.ts @@ -90,9 +90,9 @@ export const patchYField: Transformer< if (y && isArray(y) && y.length > 1) { if ( - chartTypeNew === ('BOX PLOT' as ChartType) || - (chartTypeNew === ('DUAL AXIS CHART' as ChartType) && y.length === 2) || - (chartTypeNew === ('RANGE COLUMN CHART' as ChartType) && y.length === 2) + chartTypeNew === ChartType.BoxPlot.toUpperCase() || + (chartTypeNew === ChartType.DualAxisChart.toUpperCase() && y.length === 2) || + (chartTypeNew === ChartType.RangeColumnChart.toUpperCase() && y.length === 2) ) { return { ...context @@ -112,7 +112,7 @@ export const patchYField: Transformer< cellNew.color = FOLD_NAME.toString(); } } else { - chartTypeNew = 'SCATTER PLOT' as ChartType; + chartTypeNew = ChartType.ScatterPlot.toUpperCase(); cellNew = { ...cell, x: y[0], @@ -139,7 +139,7 @@ export const patchBoxPlot: Transformer< ...cell }; const { y } = cellNew; - if (chartType === ('BOX PLOT' as ChartType)) { + if (chartType === ChartType.BoxPlot.toUpperCase()) { if (typeof y === 'string' && y.split(',').length > 1) { cellNew.y = y.split(',').map(str => str.trim()); } else if (isNil(y) || y.length === 0) { @@ -205,7 +205,7 @@ export const patchDualAxis: Transformer< const cellNew: any = { ...cell }; //Dual-axis drawing yLeft and yRight - if (chartType === ('DUAL AXIS CHART' as ChartType)) { + if (chartType === ChartType.DualAxisChart.toUpperCase()) { cellNew.y = [cellNew.y, cellNew.yLeft, cellNew.yRight, cellNew.y1, cellNew.y2].filter(Boolean).flat(); } @@ -222,12 +222,12 @@ export const patchPieChart: Transformer< const { chartType, cell, fieldInfo } = context; const cellNew = { ...cell }; - if (chartType === ('ROSE CHART' as ChartType)) { + if (chartType === ChartType.RoseChart.toUpperCase()) { cellNew.angle = cellNew.radius ?? cellNew.size ?? cellNew.angle; } //Pie chart must have color field and the angle field - if (chartType === ('PIE CHART' as ChartType) || chartType === ('ROSE CHART' as ChartType)) { + if (chartType === ChartType.PieChart.toUpperCase() || chartType === ChartType.RoseChart.toUpperCase()) { if (!cellNew.color || !cellNew.angle) { const remainedFields = getRemainedFields(cellNew, fieldInfo); @@ -265,7 +265,7 @@ export const patchWordCloud: Transformer< const { chartType, cell, fieldInfo } = context; const cellNew = { ...cell }; - if (chartType === ('WORD CLOUD' as ChartType)) { + if (chartType === ChartType.WordCloud.toUpperCase()) { if (!cellNew.size || !cellNew.color || cellNew.color === cellNew.size) { const remainedFields = getRemainedFields(cellNew, fieldInfo); @@ -311,7 +311,7 @@ export const patchDynamicBarChart: Transformer< const cellNew = { ...cell }; let chartTypeNew = chartType; - if (chartType === ('DYNAMIC BAR CHART' as ChartType)) { + if (chartType === ChartType.DynamicBarChart.toUpperCase()) { if (!cell.time || cell.time === '' || cell.time.length === 0) { const remainedFields = getRemainedFields(cellNew, fieldInfo); @@ -325,7 +325,7 @@ export const patchDynamicBarChart: Transformer< cellNew.time = stringField.fieldName; } else { //no available field, set chart type to bar chart - chartTypeNew = 'BAR CHART' as ChartType; + chartTypeNew = ChartType.BarChart.toUpperCase(); } } } @@ -454,7 +454,7 @@ export const patchLinearProgressChart: Transformer< > = (context: GenerateChartAndFieldMapContext & GenerateChartAndFieldMapOutput) => { const { chartType, cell, fieldInfo } = context; const cellNew = { ...cell }; - if (chartType === ChartType.LinearProgressChart.toUpperCase()) { + if (chartType === ChartType.LinearProgress.toUpperCase()) { const xField = [cellNew.x, cellNew.color].filter(Boolean).flat(); if (xField.length !== 0) { cellNew.x = xField[0]; diff --git a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/knowledges.ts b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/knowledges.ts index 16e4cfa5..f35c59f4 100644 --- a/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/knowledges.ts +++ b/packages/vmind/src/applications/chartGeneration/taskNodes/generateTypeAndFieldMap/GPT/prompt/knowledges.ts @@ -159,9 +159,7 @@ export const chartKnowledgeDict: ChartKnowledge = { index: 15, visualChannels: ['x', 'y'], examples: [], - knowledge: [ - 'Linear Progress chart is typically used to display progress data, which is usually a value between 0 and 1. Linear progress bars can show single progress values as well as multiple progress values. By default, the left Y-axis of the linear progress bar is the categorical field, and the bottom X-axis is the numerical field.' - ] + knowledge: [] }, [ChartType.CircularProgress]: { index: 16, @@ -209,20 +207,14 @@ export const chartKnowledgeDict: ChartKnowledge = { examples: [], knowledge: ['The gauge chart must contain two fields: size and color.'] }, - // [ChartType.LinearProgressChart]: { - // index: 23, - // visualChannels: ['y', 'x'], - // examples: [], - // knowledge: [] - // }, [ChartType.BasicHeatMap]: { - index: 24, + index: 23, visualChannels: ['y', 'x', 'size'], examples: [], knowledge: [] }, [ChartType.VennChart]: { - index: 25, + index: 24, visualChannels: ['size', 'color'], examples: [], knowledge: [ diff --git a/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/chartPipeline.ts b/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/chartPipeline.ts index fe96ceff..79657aed 100644 --- a/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/chartPipeline.ts +++ b/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/chartPipeline.ts @@ -64,8 +64,6 @@ import { treemapDisplayConf, gaugeField, gaugeDisplayConf, - // linearProgressField, - // linearProgressDisplayConf, arrayData, vennData, vennField, @@ -179,23 +177,23 @@ const pipelineBoxPlot = [chartType, data, color, boxPlotField, boxPlotStyle, leg const pipelineLiquid = [chartType, data, color, liquidField, liquidStyle, indicator, theme]; const pipelineLinearProgress = [ - chartType, - data, - color, - linearProgressField, - linearProgressAxes, - linearProgressStyle, - theme + chartType, + data, + color, + linearProgressField, + linearProgressAxes, + linearProgressStyle, + theme ]; const pipelineCircularProgress = [ - chartType, - data, - color, - circularProgressField, - circularProgressStyle, - indicator, - theme + chartType, + data, + color, + circularProgressField, + circularProgressStyle, + indicator, + theme ]; const pipelineBubbleCirclePacking = [ @@ -213,7 +211,6 @@ const pipelineRangeColumn = [chartType, data, color, rangeColumnField, rangeColu const pipelineSunburst = [chartType, sunburstData, color, sunburstOrTreemapField, sunburstDisplayConf, theme]; const pipelineTreemap = [chartType, treemapData, color, sunburstOrTreemapField, treemapDisplayConf, theme]; const pipelineGauge = [chartType, arrayData, color, gaugeField, gaugeDisplayConf, theme]; -// const pipelineLinearProgress = [chartType, arrayData, color, linearProgressField, linearProgressDisplayConf, theme]; const pipelineBasicHeatMap = [ chartType, arrayData, @@ -228,32 +225,30 @@ const pipelineBasicHeatMap = [ const pipelineVenn = [chartType, registerChart, vennData, color, vennField, legend, theme]; const pipelineMap: { [chartType: string]: any } = { - 'BAR CHART': pipelineBar, - 'LINE CHART': pipelineLine, - 'PIE CHART': pipelinePie, - 'WORD CLOUD': pipelineWordCloud, - 'SCATTER PLOT': pipelineScatterPlot, - 'DYNAMIC BAR CHART': pipelineRankingBar, - 'FUNNEL CHART': pipelineFunnel, - 'DUAL AXIS CHART': pipelineDualAxis, - 'ROSE CHART': pipelineRose, - 'RADAR CHART': pipelineRadar, - 'SANKEY CHART': pipelineSankey, - 'WATERFALL CHART': pipelineWaterfall, - 'BOX PLOT': pipelineBoxPlot, + [ChartType.BarChart.toUpperCase()]: pipelineBar, + [ChartType.LineChart.toUpperCase()]: pipelineLine, + [ChartType.PieChart.toUpperCase()]: pipelinePie, + [ChartType.WordCloud.toUpperCase()]: pipelineWordCloud, + [ChartType.ScatterPlot.toUpperCase()]: pipelineScatterPlot, + [ChartType.DynamicBarChart.toUpperCase()]: pipelineRankingBar, + [ChartType.FunnelChart.toUpperCase()]: pipelineFunnel, + [ChartType.DualAxisChart.toUpperCase()]: pipelineDualAxis, + [ChartType.RoseChart.toUpperCase()]: pipelineRose, + [ChartType.RadarChart.toUpperCase()]: pipelineRadar, + [ChartType.SankeyChart.toUpperCase()]: pipelineSankey, + [ChartType.WaterFallChart.toUpperCase()]: pipelineWaterfall, + [ChartType.BoxPlot.toUpperCase()]: pipelineBoxPlot, [ChartType.LiquidChart.toUpperCase()]: pipelineLiquid, [ChartType.LinearProgress.toUpperCase()]: pipelineLinearProgress, [ChartType.CircularProgress.toUpperCase()]: pipelineCircularProgress, - 'BOX PLOT': pipelineBoxPlot, - 'BUBBLE CIRCLE PACKING': pipelineBubbleCirclePacking, - 'MAP CHART': pipelineMapChart, - 'RANGE COLUMN CHART': pipelineRangeColumn, - 'SUNBURST CHART': pipelineSunburst, - 'TREEMAP CHART': pipelineTreemap, - 'GAUGE CHART': pipelineGauge, - // 'LINEAR PROGRESS CHART': pipelineLinearProgress, - 'BASIC HEAT MAP': pipelineBasicHeatMap, - 'VENN CHART': pipelineVenn + [ChartType.BubbleCirclePacking.toUpperCase()]: pipelineBubbleCirclePacking, + [ChartType.MapChart.toUpperCase()]: pipelineMapChart, + [ChartType.RangeColumnChart.toUpperCase()]: pipelineRangeColumn, + [ChartType.SunburstChart.toUpperCase()]: pipelineSunburst, + [ChartType.TreemapChart.toUpperCase()]: pipelineTreemap, + [ChartType.Gauge.toUpperCase()]: pipelineGauge, + [ChartType.BasicHeatMap.toUpperCase()]: pipelineBasicHeatMap, + [ChartType.VennChart.toUpperCase()]: pipelineVenn }; export const beforePipe: Transformer = ( diff --git a/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/transformers.ts b/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/transformers.ts index 27d25c24..3b1e4bd5 100644 --- a/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/transformers.ts +++ b/packages/vmind/src/applications/chartGeneration/taskNodes/getChartSpec/VChart/transformers.ts @@ -28,31 +28,30 @@ import { COLOR_FIELD } from '@visactor/chart-advisor'; type Context = GetChartSpecContext & GetChartSpecOutput; const chartTypeMap: { [chartName: string]: string } = { - 'BAR CHART': 'bar', - 'LINE CHART': 'line', - 'PIE CHART': 'pie', - 'WORD CLOUD': 'wordCloud', - 'SCATTER PLOT': 'scatter', - 'DYNAMIC BAR CHART': 'bar', - 'FUNNEL CHART': 'funnel', - 'DUAL AXIS CHART': 'common', - 'ROSE CHART': 'rose', - 'RADAR CHART': 'radar', - 'SANKEY CHART': 'sankey', - 'WATERFALL CHART': 'waterfall', - 'BOX PLOT': 'boxPlot', + [ChartType.BarChart.toUpperCase()]: 'bar', + [ChartType.LineChart.toUpperCase()]: 'line', + [ChartType.PieChart.toUpperCase()]: 'pie', + [ChartType.WordCloud.toUpperCase()]: 'wordCloud', + [ChartType.ScatterPlot.toUpperCase()]: 'scatter', + [ChartType.DynamicBarChart.toUpperCase()]: 'bar', + [ChartType.FunnelChart.toUpperCase()]: 'funnel', + [ChartType.DualAxisChart.toUpperCase()]: 'common', + [ChartType.RoseChart.toUpperCase()]: 'rose', + [ChartType.RadarChart.toUpperCase()]: 'radar', + [ChartType.SankeyChart.toUpperCase()]: 'sankey', + [ChartType.WaterFallChart.toUpperCase()]: 'waterfall', + [ChartType.BoxPlot.toUpperCase()]: 'boxPlot', [ChartType.LiquidChart.toUpperCase()]: 'liquid', [ChartType.LinearProgress.toUpperCase()]: 'linearProgress', - [ChartType.CircularProgress.toUpperCase()]: 'circularProgress' - 'BUBBLE CIRCLE PACKING': 'circlePacking', - 'MAP CHART': 'map', - 'RANGE COLUMN CHART': 'rangeColumn', - 'SUNBURST CHART': 'sunburst', - 'TREEMAP CHART': 'treemap', - 'GAUGE CHART': 'gauge', - // 'LINEAR PROGRESS CHART': 'linearProgress', - 'BASIC HEAT MAP': 'common', - 'VENN CHART': 'venn' + [ChartType.CircularProgress.toUpperCase()]: 'circularProgress', + [ChartType.BubbleCirclePacking.toUpperCase()]: 'circlePacking', + [ChartType.MapChart.toUpperCase()]: 'map', + [ChartType.RangeColumnChart.toUpperCase()]: 'rangeColumn', + [ChartType.SunburstChart.toUpperCase()]: 'sunburst', + [ChartType.TreemapChart.toUpperCase()]: 'treemap', + [ChartType.Gauge.toUpperCase()]: 'gauge', + [ChartType.BasicHeatMap.toUpperCase()]: 'common', + [ChartType.VennChart.toUpperCase()]: 'venn' }; export const chartType: Transformer = (context: Context) => { @@ -1348,11 +1347,9 @@ export const circularProgressField: Transformer = ( //assign field in spec according to cell const { cell, spec } = context; - spec.categoryField = cell.radius; + spec.categoryField = cell.color; spec.valueField = cell.value; - if (cell.color) { - spec.seriesField = cell.color; - } + spec.seriesField = cell.color; spec.radius = 0.8; spec.innerRadius = 0.7; @@ -1630,34 +1627,6 @@ export const gaugeDisplayConf: Transformer = (conte return { spec }; }; -export const linearProgressField: Transformer = (context: Context) => { - const { spec, cell } = context; - spec.yField = cell.x; - spec.xField = cell.y; - - spec.seriesField = cell.x; - return { spec }; -}; - -export const linearProgressDisplayConf: Transformer = (context: Context) => { - const { spec } = context; - spec.direction = 'horizontal'; - - spec.cornerRadius = 20; - spec.bandWidth = 30; - spec.axes = [ - { - orient: 'left', - label: { visible: true }, - type: 'band', - domainLine: { visible: false }, - tick: { visible: false } - }, - { orient: 'bottom', label: { visible: true }, type: 'linear', visible: false } - ]; - return { spec }; -}; - export const vennData: Transformer = (context: Context) => { const { dataset, spec, cell } = context; const id2dataMap = {};