scatter.ts 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /*
  2. * <<
  3. * Davinci
  4. * ==
  5. * Copyright (C) 2016 - 2017 EDP
  6. * ==
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. * >>
  19. */
  20. import { IChartProps } from '../../components/Chart'
  21. import {
  22. decodeMetricName,
  23. getChartTooltipLabel,
  24. getSizeValue,
  25. getSizeRate
  26. } from '../../components/util'
  27. import {
  28. getMetricAxisOption,
  29. getLabelOption,
  30. getLegendOption,
  31. getGridPositions,
  32. getSymbolSize,
  33. getCartesianChartReferenceOptions
  34. } from './util'
  35. import { PIVOT_DEFAULT_SCATTER_SIZE } from 'app/globalConstants'
  36. import ChartTypes from '../../config/chart/ChartTypes'
  37. export default function (chartProps: IChartProps, drillOptions?: any) {
  38. const {
  39. data,
  40. cols,
  41. metrics,
  42. chartStyles,
  43. color,
  44. tip,
  45. size,
  46. references
  47. } = chartProps
  48. const {
  49. spec,
  50. xAxis,
  51. yAxis,
  52. splitLine,
  53. label: labelStyleConfig,
  54. legend
  55. } = chartStyles
  56. const {
  57. showVerticalLine,
  58. verticalLineColor,
  59. verticalLineSize,
  60. verticalLineStyle,
  61. showHorizontalLine,
  62. horizontalLineColor,
  63. horizontalLineSize,
  64. horizontalLineStyle
  65. } = splitLine
  66. const { selectedItems } = drillOptions
  67. const labelOption = {
  68. label: getLabelOption('scatter', labelStyleConfig, metrics)
  69. }
  70. const referenceOptions = getCartesianChartReferenceOptions(references, ChartTypes.Scatter, metrics, data)
  71. let sizeRate = 0
  72. let sizeItemName = ''
  73. if (size.items.length) {
  74. const sizeItem = size.items[0]
  75. sizeItemName = `${sizeItem.agg}(${decodeMetricName(sizeItem.name)})`
  76. const sizeValues = data.map((d) => d[sizeItemName])
  77. sizeRate = getSizeRate(Math.min(...sizeValues), Math.max(...sizeValues))
  78. }
  79. const series = []
  80. const seriesData = []
  81. if (cols.length || color.items.length) {
  82. const groupColumns = color.items.map((c) => c.name).concat(cols.map((c) => c.name))
  83. .reduce((distinctColumns, col) => {
  84. if (!distinctColumns.includes(col)) {
  85. distinctColumns.push(col)
  86. }
  87. return distinctColumns
  88. }, [])
  89. const grouped = data.reduce((obj, val) => {
  90. const groupingKey = groupColumns
  91. .reduce((keyArr, col) => keyArr.concat(val[col]), [])
  92. .join(String.fromCharCode(0))
  93. if (!obj[groupingKey]) {
  94. obj[groupingKey] = []
  95. }
  96. obj[groupingKey].push(val)
  97. return obj
  98. }, {})
  99. const labelItemName = color.items.length
  100. ? color.items[0].name
  101. : cols[0].name
  102. const groupedEntries = Object.entries(grouped)
  103. groupedEntries.forEach(([key, value], gIndex) => {
  104. series.push({
  105. name: key.replace(String.fromCharCode(0), ' '),
  106. type: 'scatter',
  107. data: value.map((v, index) => {
  108. const [x, y] = metrics
  109. const currentSize = size.items.length ? v[sizeItemName] : PIVOT_DEFAULT_SCATTER_SIZE
  110. const sizeValue = getSizeValue(size.value['all'])
  111. const itemStyleObj = selectedItems && selectedItems.length && selectedItems.some((item) => item === gIndex) ? {itemStyle: {
  112. normal: {
  113. opacity: 1
  114. }
  115. }} : {}
  116. return {
  117. ...itemStyleObj,
  118. value: [
  119. v[`${x.agg}(${decodeMetricName(x.name)})`],
  120. v[`${y.agg}(${decodeMetricName(y.name)})`],
  121. v[labelItemName],
  122. currentSize
  123. ],
  124. symbolSize: size.items.length
  125. ? getSymbolSize(sizeRate, currentSize) * sizeValue
  126. : currentSize * sizeValue
  127. }
  128. }),
  129. itemStyle: {
  130. normal: {
  131. color: color.items.length
  132. ? color.items[0].config.values[key.split(String.fromCharCode(0))[0]]
  133. : color.value['all'],
  134. opacity: selectedItems && selectedItems.length > 0 ? 0.25 : 1
  135. }
  136. },
  137. ...labelOption,
  138. ...(gIndex === groupedEntries.length - 1 && referenceOptions)
  139. })
  140. seriesData.push(value)
  141. })
  142. } else {
  143. series.push({
  144. name: 'single',
  145. type: 'scatter',
  146. data: data.map((d, index) => {
  147. const [x, y] = metrics
  148. const currentSize = size.items.length ? d[sizeItemName] : PIVOT_DEFAULT_SCATTER_SIZE
  149. const sizeValue = getSizeValue(size.value['all'])
  150. const itemStyleObj = selectedItems && selectedItems.length && selectedItems.some((item) => item === index) ? {itemStyle: {
  151. normal: {
  152. opacity: 1
  153. }
  154. }} : {}
  155. return {
  156. ...itemStyleObj,
  157. value: [
  158. d[`${x.agg}(${decodeMetricName(x.name)})`],
  159. d[`${y.agg}(${decodeMetricName(y.name)})`],
  160. '',
  161. currentSize
  162. ],
  163. symbolSize: size.items.length
  164. ? getSymbolSize(sizeRate, currentSize) * sizeValue
  165. : currentSize * sizeValue
  166. }
  167. }),
  168. itemStyle: {
  169. normal: {
  170. color: color.value['all'],
  171. opacity: selectedItems && selectedItems.length > 0 ? 0.25 : 1
  172. }
  173. },
  174. ...labelOption,
  175. ...referenceOptions
  176. })
  177. seriesData.push(data)
  178. }
  179. const seriesNames = series.map((s) => s.name)
  180. // dataZoomOptions = dataZoomThreshold > 0 && dataZoomThreshold < dataSource.length && {
  181. // dataZoom: [{
  182. // type: 'inside',
  183. // start: Math.round((1 - dataZoomThreshold / dataSource.length) * 100),
  184. // end: 100
  185. // }, {
  186. // start: Math.round((1 - dataZoomThreshold / dataSource.length) * 100),
  187. // end: 100,
  188. // handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
  189. // handleSize: '80%',
  190. // handleStyle: {
  191. // color: '#fff',
  192. // shadowBlur: 3,
  193. // shadowColor: 'rgba(0, 0, 0, 0.6)',
  194. // shadowOffsetX: 2,
  195. // shadowOffsetY: 2
  196. // }
  197. // }]
  198. // }
  199. const {isDrilling, getDataDrillDetail, instance } = drillOptions
  200. const brushedOptions = isDrilling === true ? {
  201. brush: {
  202. toolbox: ['rect', 'polygon', 'keep', 'clear'],
  203. throttleType: 'debounce',
  204. throttleDelay: 300,
  205. brushStyle: {
  206. borderWidth: 1,
  207. color: 'rgba(255,255,255,0.2)',
  208. borderColor: 'rgba(120,140,180,0.6)'
  209. }
  210. }
  211. } : null
  212. if (isDrilling) {
  213. // instance.off('brushselected')
  214. // instance.on('brushselected', brushselected)
  215. setTimeout(() => {
  216. instance.dispatchAction({
  217. type: 'takeGlobalCursor',
  218. key: 'brush',
  219. brushOption: {
  220. brushType: 'rect',
  221. brushMode: 'multiple'
  222. }
  223. })
  224. }, 0)
  225. }
  226. function brushselected (params) {
  227. console.log({params})
  228. // console.log({seriesData})
  229. const brushComponent = params.batch[0]
  230. const brushed = []
  231. const sourceData = seriesData[0]
  232. let range: any[] = []
  233. if (brushComponent && brushComponent.areas && brushComponent.areas.length) {
  234. brushComponent.areas.forEach((area) => {
  235. range = range.concat(area.range)
  236. })
  237. }
  238. if (brushComponent && brushComponent.selected && brushComponent.selected.length) {
  239. for (let i = 0; i < brushComponent.selected.length; i++) {
  240. const rawIndices = brushComponent.selected[i].dataIndex
  241. const seriesIndex = brushComponent.selected[i].seriesIndex
  242. brushed.push({[i]: rawIndices})
  243. }
  244. }
  245. // console.log({sourceData})
  246. if (getDataDrillDetail) {
  247. getDataDrillDetail(JSON.stringify({range, brushed, sourceData}))
  248. }
  249. }
  250. const xAxisSplitLineConfig = {
  251. showLine: showVerticalLine,
  252. lineColor: verticalLineColor,
  253. lineSize: verticalLineSize,
  254. lineStyle: verticalLineStyle
  255. }
  256. const yAxisSplitLineConfig = {
  257. showLine: showHorizontalLine,
  258. lineColor: horizontalLineColor,
  259. lineSize: horizontalLineSize,
  260. lineStyle: horizontalLineStyle
  261. }
  262. return {
  263. xAxis: getMetricAxisOption(xAxis, xAxisSplitLineConfig, decodeMetricName(metrics[0].name), 'x'),
  264. yAxis: getMetricAxisOption(yAxis, yAxisSplitLineConfig, decodeMetricName(metrics[1].name)),
  265. series,
  266. tooltip: {
  267. formatter: getChartTooltipLabel('scatter', seriesData, { cols, metrics, color, tip })
  268. },
  269. legend: getLegendOption(legend, seriesNames),
  270. grid: getGridPositions(legend, seriesNames, '', false, yAxis)
  271. // ...brushedOptions
  272. }
  273. }