Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save electroheadfx/71827f8b8a997f99ac29e2768dfc3798 to your computer and use it in GitHub Desktop.
Save electroheadfx/71827f8b8a997f99ac29e2768dfc3798 to your computer and use it in GitHub Desktop.
echarts labels overlapping
const dataset = graphData.map((item, index) => {
return {
id: index,
dimensions: ['sscount', 'pk', 'severity', 'value', 'id', 'level'],
source: item.data,
}
})
const dataTransforms = graphData.map((_, index) => {
return {
id: 'transform-' + index,
fromDatasetIndex: index,
transform: [
{
type: 'sort',
config: { dimension: 'id', order: 'asc' },
},
{
type: 'sort',
// here filter by pk if track mode is actived else not its itinary mode (sscount)
config: { dimension: track ? 'pk' : 'sscount', order: 'asc' },
}
],
}
})
dataTransforms.forEach((item, index) => {
dataset.push(item as any)
})
// add path to dataset
// <--- end dataset
// ***************************************************************************
// ***************************************************************************
// Create grid Array
// ---------------------------------------------------------------------------
// grid setup for n measures
const grid = []
for (let i = 0;i < totalGraphs;i++) {
grid.push({
...GridPadX,
show: true,
id: i,
z: 2,
borderColor: bgAxisColor,
top: (heightGrid * i + GridPadY + gapGrid * i) + topPaddingForActionBar,
height: heightGrid + 'px',
width: WindowWidth - gridPadRight + 'px',
})
}
// parcours (path)
const gridPath = {
...GridPadX,
left: GridPadLeft,
show: true,
z: 2,
top: topPaddingForActionBar / 2,
height: 55 + topPaddingForActionBar, // 28
width: WindowWidth - gridPadRight + 'px',
bottom: '80%',
// backgroundColor: '#ddd',
}
// assets
const gridAsset = {
...GridPadX,
bottom: GridPadY - 55,
// bottom: 0,
height: 45,
z: 2,
width: WindowWidth - gridPadRight + 'px',
show: true,
shadowOffsetX: 4,
shadowOffsetY: 4,
shadowColor: 'rgba(0, 0, 0, 0)',
shadowBlur: 0,
borderWidth: 0,
}
// assets
const gridAssetForLabels = {
...GridPadX,
bottom: GridPadY - 55,
height: 45,
z: 9999,
zlevel: 10,
width: WindowWidth - gridPadRight + 'px',
show: true,
shadowOffsetX: 4,
shadowOffsetY: 4,
shadowColor: 'rgba(0, 0, 0, 0)',
shadowBlur: 0,
borderWidth: 0,
}
grid.push(gridPath)
grid.push(gridAsset)
grid.push(gridAssetForLabels)
// ***************************************************************************
// ***************************************************************************
// Create xAxis Array
// ---------------------------------------------------------------------------
// !! We should have only one xAxis for all measures or each axis should have the same min and max range
const xAxis = []
for (let i = 0;i < totalGraphs;i++) {
// const measure = measures[i] as string
xAxis.push({
// name: measure,
type: 'value',
min: range[0],
max: range[1],
gridIndex: i,
splitNumber: 10,
boundaryGap: false,
z: 1,
axisPointer: {
snap: true,
},
axisLabel: {
show: i < totalGraphs - 1 ? false : true,
color: '#ddd',
verticalAlign: 'top',
lineHeight: 18,
fontWeight: 'bold',
showMinLabel: false,
showMaxLabel: false,
// interval: 5,
formatter: (value: number) => {
if (track) {
return value.toLocaleString(router.locale, { minimumFractionDigits: 3 })
}
if (graphData[i]) {
const data = graphData[i]?.data?.sort((a, b) => Math.abs(a[0] - value) - Math.abs(b[0] - value))[0]
if (!data) return null
const newAxisValue = data[1]
return newAxisValue?.toLocaleString(router.locale, { minimumFractionDigits: 3 })
}
return null
},
},
axisLine: {
show: false,
lineStyle: {
color: bgAxisColor,
},
},
axisTick: {
show: false,
},
})
}
// Path (Parcours) xAxis
xAxis.push({
show: false,
type: 'value',
min: range[0],
// min: 'dataMin',
max: range[1],
// max: 'dataMax',
axisPointer: {
snap: false,
},
gridIndex: totalGraphs, // eq to 4 with 4 graphs (0-3)
})
// Assets (Patrimoines) xAxis
xAxis.push({
show: false,
type: 'value',
min: range[0],
max: range[1],
axisPointer: {
snap: false,
},
gridIndex: totalGraphs + 1, // eq to 4 with 4 graphs (0-3)
})
// Assets (Patrimoines) xAxis for labels
xAxis.push({
show: false,
type: 'value',
min: range[0],
max: range[1],
axisPointer: {
snap: false,
},
gridIndex: totalGraphs + 2, // eq to 4 with 4 graphs (0-3)
})
// <--- end xAxis Array
// ***************************************************************************
// ***************************************************************************
// Create yAxis Array
// ---------------------------------------------------------------------------
const yAxis = []
for (let i = 0;i < totalGraphs;i++) {
const measure = measures[i]
if (measure) {
yAxis.push({
type: 'value',
gridIndex: i,
position: 'right',
z: 1,
axisLine: {
show: false,
},
axisTick: {
show: false,
},
axisLabel: {
showMaxLabel: false,
color: '#ddd',
verticalAlign: 'top',
lineHeight: 10,
fontSize: 10,
margin: 4,
formatter: (value: number) => {
const isInteger = (n: number) => n.toString().split('.').length === 1
if (isInteger(value)) return value
else value.toLocaleString(router.locale, measurePrecision)
},
},
// min: 'datamin',
// max: 'datamax',
min: (value: any) => {
// evaluate the ecart min with 0.1 % of max/min difference
return value.min - ((value.max - value.min) * 0.02)
},
max: (value: any) => {
// evaluate the ecart min with 0.1 % of max/min difference
return value.max + ((value.max - value.min) * 0.02)
},
// formatter: (value: number) => {
// return value.toLocaleString(router.locale, measurePrecision)
// },
})
}
}
// Path (Parcours) yAxis
yAxis.push({
// type: 'value',
show: false,
gridIndex: totalGraphs, // eq to 4 with 4 graphs (0-3)
min: 0,
max: 1,
axisPointer: {
type: 'none',
snap: false
}
})
// Assets (patrimoines) yAxis
yAxis.push({
// type: 'value',
show: false,
gridIndex: totalGraphs + 1, // eq to 5 with 4 graphs (0-3)
min: 0,
max: 1,
axisPointer: {
type: 'none',
snap: false
}
})
// Assets (patrimoines) yAxis for labels
yAxis.push({
// type: 'value',
show: false,
gridIndex: totalGraphs + 2, // eq to 5 with 4 graphs (0-3)
min: 0,
max: 1,
axisPointer: {
type: 'none',
snap: false
}
})
// <--- end yAxis Array
// ***************************************************************************
// ---------------------------------------------------------------------------
// ***************************************************************************
// ***************************************************************************
//
// Create series Arrays
//
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
// Create Parcours (path) series
// ---------------------------------------------------------------------------
const SeriesItineraryData = [
{
type: 'line',
name: 'range',
data: [
[assets_start_pk, 0],
[assets_end_pk, 0],
],
showSymbol: false,
tooltip: {
show: false,
},
lineStyle: {
width: 0,
},
emphasis: {
disabled: true
},
xAxisIndex: totalGraphs,
yAxisIndex: totalGraphs,
markArea: {
itemStyle: {
color: 'rgba(0, 0, 0, 0.05)'
},
emphasis: {
disabled: true
},
data: [
[
{
name: 'range',
xAxis: zoomBounds[0]
},
{
xAxis: zoomBounds[1]
}
],
]
}
}
] as any
itineraryTrack.forEach((section, i) => {
const label = section?.label
const asset_start_pk = track ? Number(section?.startLinearReference) : section?.startSSCount
const asset_end_pk = track ? Number(section?.endLinearReference) : section?.endSSCount
const asset_middle_pk = ((asset_end_pk + asset_start_pk) / 2)
// estimate moy px for each asset
const asset_moy_pk = (asset_start_pk + asset_end_pk) / 2
const asset_ratio = (asset_moy_pk - assets_start_pk) / (assets_end_pk - assets_start_pk)
const asset_moy_px = (assets_start_px + asset_ratio) * (assets_end_px - assets_start_px)
const asset_width_px = ((assets_end_px - assets_start_px) * (asset_end_pk - asset_start_pk)) / (assets_end_pk - assets_start_pk)
// getSeverityColor(1) = getSeverityColor('VO)
const trackColor = track ? getSeverityColor(1) : getSeverityColor(1, section.trackNumber - 1, itineraryData?.trackCount)
const newObject = {
name: section?.label,
tooltip: {
show: false,
},
data: [
[track ? Number(section?.startLinearReference) : Number(section?.startSSCount), positionParcours],
[track ? Number(section?.endLinearReference) : Number(section?.endSSCount), positionParcours],
],
type: 'line',
xAxisIndex: totalGraphs,
yAxisIndex: totalGraphs,
showSymbol: false,
symbolSize: 0,
symbol: 'emptyCircle',
lineStyle: {
type: 'solid',
width: 0,
},
itemStyle: {
color: 'DodgerBlue',
},
markLine: {
name: section?.label,
animation: false,
symbolSize: 0,
label: {
show: true,
color: trackColor,
fontWeight: 'bold',
distance: -23,
position: 'middle',
formatter: asset_width_px < 20 ? '.' : section?.label,
width: asset_width_px,
overflow: 'truncate',
},
data: [[
{
name: 'itineraryTrack',
lineStyle: {
type: 'solid',
color: trackColor,
width: 4,
},
symbol: section.startSymbol === 'NULL' ? 'none' : section.startSymbol === 'BACK' ? backIcon : 'square',
symbolSize: section.startSymbol === 'BACK' ? 25 : 12,
symbolRotate: 0,
symbolOffset: section.startSymbol === 'BACK' ? [0, -2] : [0, 0],
coord: [
track ? Number(section?.startLinearReference) : Number(section?.startSSCount), positionParcours,
positionParcours,
]
},
{
symbol: section.endSymbol === 'NULL' ? 'none' : section.endSymbol === 'BACK' ? backIcon : 'circle',
symbolSize: section.endSymbol === 'BACK' ? 25 : 12,
symbolRotate: 0,
symbolOffset: section.endSymbol === 'BACK' ? [0, -2] : [0, 0],
coord: [
track ? Number(section?.endLinearReference) : Number(section?.endSSCount), positionParcours,
positionParcours,
]
}
]],
emphasis: {
// scale: true,
// focus: 'series',
label: {
overflow: 'none',
formatter: section?.label,
}
}
}
}
SeriesItineraryData.push(newObject)
})
itineraryAdv.forEach((adv, i) => {
const advColor = track ? getSeverityColor(1) : getSeverityColor(1, adv.trackNumber - 1, itineraryData?.trackCount)
const newObject = {
name: adv?.label,
data: [
[
track ? Number(adv?.startLinearReference) : Number(adv?.startSSCount) - 20,
positionParcours,
],
[
track ? Number(adv?.endLinearReference) : Number(adv?.endSSCount) + 20,
positionParcours,
],
],
type: 'line',
xAxisIndex: totalGraphs,
yAxisIndex: totalGraphs,
showSymbol: false,
symbolSize: 0,
lineStyle: {
type: 'solid',
width: 0,
},
label: {
show: false,
color: adv.mode === 'DIRECT' ? advColor : 'orange',
fontWeight: 'bold',
fontSize: 10,
position: [4, -23],
align: 'center',
formatter: (obj: any) => (obj.dataIndex % 2 === 0 ? obj.seriesName : ''),
},
emphasis: {
focus: 'series',
scale: true,
label: {
show: true,
},
lineStyle: {
type: 'solid',
width: 0,
},
},
markLine: {
animation: false,
symbolSize: 0,
data: [[
{
lineStyle: {
type: 'solid',
color: adv.mode === 'DIRECT' ? advColor : 'orange',
width: 10,
},
coord: [
track ? Number(adv?.startLinearReference) : Number(adv?.startSSCount),
positionParcours,
]
},
{
coord: [
track ? Number(adv?.endLinearReference) : Number(adv?.endSSCount),
positionParcours,
]
}
]],
emphasis: {
disabled: true,
// scale: true,
// focus: 'series',
}
}
}
SeriesItineraryData.push(newObject)
})
// ***************************************************************************
// Create Assets (patrimoine) series
// ---------------------------------------------------------------------------
// setup series counter for capture the assets ID with React click (selected asset)
const seriesCountId = campaignsId.length * dataLength
const PathSeriesCountId = itineraryTrack.length + itineraryAdv.length
const SeriesAssetData = [
{
type: 'line',
name: 'range',
data: [
[range[0], positionAssetY],
[range[1], positionAssetY],
],
tooltip: {
show: false,
},
z: -1,
lineStyle: {
color: 'gray',
width: 0.5,
type: 'solid'
},
itemStyle: {
color: 'DodgerBlue'
},
symbol: 'square',
symbolSize: 5,
emphasis: {
disabled: true
},
xAxisIndex: totalGraphs + 1,
yAxisIndex: totalGraphs + 1,
labelLayout: {
verticalAlign: 'middle',
},
},
] as any
let seriesCounter = seriesCountId + PathSeriesCountId + 2
nonAdvAssets.forEach((asset, i) => {
const label = t(asset?.type, { ns: 'asset' }) + ' ' + asset?.name
const asset_start_pk = track ? Number(asset?.startLinearReference) : asset?.startSSCount
const asset_end_pk = track ? Number(asset?.endLinearReference) : asset?.endSSCount
const asset_middle_pk = ((asset_end_pk + asset_start_pk) / 2)
const informations = asset.informations.filter((info) => info.type !== '' && info.value !== '').map((info) => ({ type: info.type, value: info.value }))
const nonAdvObj = {
id: `nonAdvAssets-${i}`,
name: 'asset',
type: 'line',
triggerLineEvent: true,
xAxisIndex: totalGraphs + 1,
yAxisIndex: totalGraphs + 1,
showSymbol: true,
symbolSize: 0,
symbol: 'diamond',
data: [
[asset_start_pk, positionAssetY],
[asset_middle_pk, positionAssetY],
[asset_end_pk, positionAssetY]
],
lineStyle: {
type: 'solid',
width: 2,
color: ColorAssets.get(asset.type),
},
itemStyle: {
color: ColorAssets.get(asset.type),
},
label: {
show: false,
distance: -19,
formatter: '{bg|' + label + '}',
rich: {
bg: {
color: ColorAssets.get(asset.type),
fontWeight: 'bold',
fontSize: 11,
}
},
},
emphasis: {
scale: true,
focus: 'series',
lineStyle: {
width: 4,
},
label: {
show: true
},
},
markPoint: {
animation: false,
data: [
{
name: 'asset',
symbol: 'image://' + IconAssets.get(asset.type),
symbolOffset: [0, -16],
symbolSize: 27,
coord: [
asset_middle_pk,
positionAssetY,
{
icon: 'image://' + IconAssets.get(`${asset.type}`),
iconSelected: 'image://' + IconAssets.get(`${asset.type}-SEL`),
}
]
}
],
emphasis: {
scale: true,
focus: 'series',
}
}
}
const nonAdvLabel = {
id: `nonAdvAssets-${i}-label`,
name: 'asset',
type: 'scatter',
z: 999999999,
data: [[asset_middle_pk, positionAssetY + 0.6]],
xAxisIndex: totalGraphs + 2,
yAxisIndex: totalGraphs + 2,
symbolSize: 0,
showSymbol: false,
itemStyle: {
color: 'transparent',
},
emphasis: {
disabled: true
},
label: {
show: false,
offset: [0, -100],
// t(adv?.type, { ns: 'asset' }) + ' ' + adv?.name
formatter: [
`{icon|} {title|${t(asset?.type, { ns: 'asset' })}}{abg|}`,
`{name|${asset?.name}}`,
`{key|${t('ASSET_PK_START')}}{value|${asset.startLinearReference}}`,
`{key|${t('ASSET_PK_END')}}{value|${asset.endLinearReference}}`,
informations.length > 0 ? informations.map((info) => `{key|${info.type}}{value|${info.value}}`).join('\n') : ''
].filter(Boolean).join('\n'),
backgroundColor: 'white',
borderColor: ColorAssets.get(asset.type),
borderWidth: 2,
borderRadius: 4,
padding: [0, 0, 5, 0],
color: '#000',
fontSize: 14,
lineHeight: 18,
rich: {
title: {
fontSize: 14,
lineHeight: 25,
fontWeight: 'bold',
color: 'white',
padding: 10
},
abg: {
backgroundColor: ColorAssets.get(asset.type),
width: '100%',
align: 'right',
height: 25,
borderRadius: [4, 4, 0, 0],
},
icon: {
height: 25,
align: 'center',
shadowColor: 'white',
shadowBlur: 5,
shadowOffsetX: 0,
shadowOffsetY: 0,
backgroundColor: {
image: 'data:image://' + IconAssets.get(`${asset.type}`)
}
},
name: {
fontSize: 12,
fontWeight: 'bold',
padding: 10,
align: 'right',
color: ColorAssets.get(asset.type)
},
key: {
fontWeight: 'bold',
color: '#555',
width: 20,
padding: 10,
align: 'left'
},
value: {
color: '#333',
width: 20,
padding: 10,
align: 'right'
}
}
}
}
SeriesAssetData.push(nonAdvObj)
SeriesAssetData.push(nonAdvLabel)
})
seriesCounter = seriesCounter + nonAdvAssets.length
assetAdvs.forEach((adv, i) => {
const label = t(adv?.type, { ns: 'asset' }) + ' ' + adv?.name
const asset_start_pk = track ? Number(adv?.startLinearReference) : adv?.startSSCount
const asset_end_pk = track ? Number(adv?.endLinearReference) : adv?.endSSCount
const asset_middle_pk = ((asset_end_pk + asset_start_pk) / 2)
const informations = adv.informations.filter((info) => info.type !== '' && info.value !== '').map((info) => ({ type: info.type, value: info.value }))
const assetAdvObj = {
id: `assetAdvs-${i}`,
name: 'asset',
type: 'line',
triggerLineEvent: true,
xAxisIndex: totalGraphs + 1,
yAxisIndex: totalGraphs + 1,
showSymbol: true,
symbolSize: 0,
symbol: 'diamond',
data: [
[asset_start_pk, positionAssetY],
[asset_middle_pk, positionAssetY],
[asset_end_pk, positionAssetY]
],
lineStyle: {
type: 'solid',
width: 2,
color: ColorAssets.get(adv.type),
},
itemStyle: {
color: ColorAssets.get(adv.type),
},
label: {
show: false,
distance: -19,
formatter: '{bg|' + label + '}',
rich: {
bg: {
color: ColorAssets.get(adv.type),
fontWeight: 'bold',
fontSize: 11,
}
},
},
emphasis: {
scale: true,
focus: 'series',
lineStyle: {
width: 4,
z: 0
},
label: {
show: true
},
},
markPoint: {
animation: false,
data: [
{
name: 'asset',
symbol: 'image://' + IconAssets.get(adv.type),
symbolOffset: [0, -16],
symbolSize: 27,
coord: [
asset_middle_pk,
positionAssetY,
{
icon: 'image://' + IconAssets.get(`${adv.type}`),
iconSelected: 'image://' + IconAssets.get(`${adv.type}-SEL`)
}
// adv?.startLinearReference?.toLocaleString(router.locale, { minimumFractionDigits: 3 }),
// adv?.endLinearReference?.toLocaleString(router.locale, { minimumFractionDigits: 3 }),
// {
// label,
// mode: adv.mode,
// position: asset_moy_px,
// informations: adv.informations.filter((info) => info.type !== '' && info.value !== '').map((info) => ({ type: info.type, value: info.value }))
// }
]
}
],
emphasis: {
scale: true,
focus: 'series',
}
}
}
const AdvLabel = {
id: `assetAdvs-${i}-label`,
name: 'asset',
type: 'scatter',
z: 999999999,
data: [[asset_middle_pk, positionAssetY + 0.6]],
xAxisIndex: totalGraphs + 2,
yAxisIndex: totalGraphs + 2,
symbolSize: 0,
showSymbol: false,
itemStyle: {
color: 'transparent',
},
emphasis: {
disabled: true
},
label: {
show: false,
offset: [0, -100],
// t(adv?.type, { ns: 'asset' }) + ' ' + adv?.name
formatter: [
`{icon|} {title|${t(adv.type, { ns: 'asset' })}}{abg|}`,
`{name|${adv.name} | ${t(adv.mode, { ns: 'common' })}}`,
`{key|${t('ASSET_PK_START')}}{value|${adv.startLinearReference}}`,
`{key|${t('ASSET_PK_END')}}{value|${adv.endLinearReference}}`,
informations.length > 0 ? informations.map((info) => `{key|${info.type}}{value|${info.value}}`).join('\n') : ''
].filter(Boolean).join('\n'),
backgroundColor: 'white',
borderColor: ColorAssets.get(adv.type),
borderWidth: 2,
borderRadius: 4,
padding: [0, 0, 5, 0],
color: '#000',
fontSize: 14,
lineHeight: 18,
rich: {
title: {
fontSize: 14,
lineHeight: 25,
fontWeight: 'bold',
color: 'white',
padding: 10
},
abg: {
backgroundColor: ColorAssets.get(adv.type),
width: '100%',
align: 'right',
height: 25,
borderRadius: [4, 4, 0, 0],
},
icon: {
height: 25,
align: 'center',
shadowColor: 'white',
shadowBlur: 5,
shadowOffsetX: 0,
shadowOffsetY: 0,
backgroundColor: {
image: 'data:image://' + IconAssets.get(`${adv.type}`)
}
},
name: {
fontSize: 12,
fontWeight: 'bold',
padding: 10,
align: 'right',
color: ColorAssets.get(adv.type)
},
key: {
fontWeight: 'bold',
color: '#555',
width: 20,
padding: 10,
align: 'left'
},
value: {
color: '#333',
width: 20,
padding: 10,
align: 'right'
}
}
}
}
SeriesAssetData.push(assetAdvObj)
SeriesAssetData.push(AdvLabel)
})
// ---------------------------------------------------------------------------
// ***************************************************************************
// Create series data Array
// ---------------------------------------------------------------------------
const series = [] as any
const datasetIndexStart = dataLength * campaignsId.length
// totalGraphs = liste des graphs (measures non applaties)
// dataLength = liste des measures applaties
// track les measures sont toujours applati :
// [ ['measure1'], ['measure2'] ] -> totalGraphs = dataLength = 2
// itinerary les measures sont composé:
//[ ['measure1', 'measure2'], ['measure3', 'measure3'] ] -> totalGraphs 2, = dataLength = 4
const createMeasureAxisIterrator = (layout: string[][]) => {
const axisMeasures = layout.map((graph, index) => {
return graph.map((_) => {
return index
})
}).flatMap(s => s)
return axisMeasures[Symbol.iterator]()
}
const axisMeasuresIndex = createMeasureAxisIterrator(graphsLayout)
// foreach on campaignsId
campaignsId.forEach((campaignId, index) => {
for (let i = 0;i < dataLength;i++) {
const measure = measures[i]
if (measure) {
// get the layout position for Xaxis and yAxis
// const layoutPosition = track ? i : getMeasureLayoutPosition(measure, graphsLayout)
const layoutPosition = track ? i : axisMeasuresIndex.next().value
series.push({
id: `series-${i}-campaignID${campaignId}`,
name: track ? `${campaignId}` : `${measure}-campaignID${campaignId}`,
// name: `${measure}-campaignID${campaignId}`,
nameId: `${campaignId}`,
type: 'line',
datasetIndex: datasetIndexStart + i + (dataLength * index),
largeThreshold: 2000,
xAxisIndex: layoutPosition,
yAxisIndex: layoutPosition,
showSymbol: true,
hoverAnimation: false,
animation: false,
// symbolSize: 0.1,
endLabel: {
show: false,
},
labelLine: {
show: false,
},
encode: {
tooltip: [1, 3, 2],
x: track ? 'pk' : 'sscount',
y: 'value',
},
lineStyle: {
width: 0.8,
},
itemStyle: {
color: getSeverityColor(1, 0, totalGraphs),
},
emphasis: {
scale: true,
focus: 'series',
},
})
}
}
})
// Path (Parcours) series
series.push(...SeriesItineraryData)
// Assets (Patrimoine) series
series.push(...SeriesAssetData)
// console.log('series', series, 'dataset', dataset)
// return {}
// <--- end series Array
// ***************************************************************************
// ***************************************************************************
// Create visualMap pieces with thresholds
// ---------------------------------------------------------------------------
// Attention : here map through thresholds 2 times : one for each measure and one for each threshold
const baseColor = getSeverityColor(1)
const defaultVisualMapPiece = {
type: 'piecewise',
bottom: 50,
right: 10,
dimension: track ? 1 : 0,
show: false,
color: [baseColor],
symbolSize: 0,
outOfRange: {
color: baseColor,
symbolSize: 0
},
seriesIndex: 'all'
}
let visualMapPieces: any = []
const thresholdsMap = graphData.map((d) => ({
data: d.thresholds,
measureName: d.measureName,
runId: d.runId
}))
if (thresholdsMap.length > 0) {
visualMapPieces = thresholdsMap.map((threshold, index) => {
const tresholdPieces = threshold.data.map((item) => {
return {
label: item.label,
min: item.min,
max: item.max,
color: item.color,
}
})
let positionColor = 0
if (track) {
positionColor = threshold.runId
} else {
positionColor = getMeasurePosition(threshold.measureName, graphsLayout)
}
const measuresCountPerGraph = track ? campaignsId.length + 1 : 2
const color = getSeverityColor(1, positionColor, measuresCountPerGraph)
const baseVisualMapPiece = {
type: 'piecewise',
bottom: 50,
right: 10,
dimension: track ? 1 : 0,
show: false,
color: [color],
symbolSize: 0.1,
outOfRange: {
color: color,
symbolSize: 0.1
},
}
return { ...baseVisualMapPiece, pieces: tresholdPieces, seriesIndex: index }
})
}
const visualMapPoints = {
// setup for point severity color
type: 'piecewise',
orient: 'horizontal',
show: false,
selectedMode: false,
padding: [20, 10 + GridPadLeft],
dimension: 5, // set the column which is used in categories (severity) // 2
seriesIndex: Array.from(Array(thresholdsMap.length).keys()),
// categories: classifications,
categories: [1, 2, 3, 4],
inRange: {
symbolSize: [0.1, 3, 4, 5], // 4 levels defect classification by size
// color: [1, 2, 3, 4].map((d: any) => getSeverityColor(d)),
color: [getSeverityColor(1), DScolorSeverity[1], DScolorSeverity[2], DScolorSeverity[3]], // 'VO' 'VA' 'VI' 'VR'
symbol: {
'': 'circle',
},
},
outRange: {
symbolSize: [0.1],
symbol: {
'': 'circle',
},
}
}
// const FinalvisualMapPieces = [...visualMapPieces]
const FinalvisualMapPieces = [...visualMapPieces, visualMapPoints]
// <--- end visualMap
// ***************************************************************************
// synchronize dataZoom axis graphs
const dataZoomSync = Array.from(Array(totalGraphs).keys())
// const dataZoomSync = measures.map((d, i) => i)
// add dataZoom sync for path (section)
// dataZoomSync.push(dataZoomSync.length)
// add dataZoom sync for asset (patrimoine)
dataZoomSync.push(dataZoomSync.length + 1)
// ***************************************************************************
// DataZooms creation
// ---------------------------------------------------------------------------
const dataZoom = [
{
// dataZoom inside for all measures
type: 'inside',
maxSpan: 100,
minSpan: 0.02,
start: initZoomState[0],
end: initZoomState[1],
xAxisIndex: dataZoomSync,
filterMode: 'none',
zoomOnMouseWheel: 'ctrl',
},
{
// X Top Global DataZoom Slider
type: 'slider',
show: true,
start: initZoomState[0],
end: initZoomState[1],
realtime: true,
showDetail: true,
labelFormatter: (value: any, valueStr: string) => {
if (track) return valueStr
if (dataset[0]?.source === undefined) return ''
const sorted = dataset[0].source.sort(
(a, b) => Math.abs(a[0] - Number(valueStr)) - Math.abs(b[0] - Number(valueStr))
)
if (sorted[0] === undefined) return ''
return sorted[0][1]
},
xAxisIndex: dataZoomSync,
height: 8, // 50
left: GridPadLeft,
right: gridPadRight - 15,
top: 52, // 0
filterMode: 'none',
// inactive the drawing zoom feature
brushSelect: false,
borderRadius: 0,
borderColor: 'transparent',
// Remove graph shadow
showDataShadow: false,
backgroundColor: bgAxisColor,
fillerColor: 'DodgerBlue',
handleIcon: 'path:M2 1C2 1.55229 1.55228 2 1 2C0.447716 2 0 1.55229 0 1C0 0.447708 0.447716 0 1 0C1.55228 0 2 0.447708 2 1Z,M6 1C6 1.55229 5.55228 2 5 2C4.44772 2 4 1.55229 4 1C4 0.447708 4.44772 0 5 0C5.55228 0 6 0.447708 6 1Z,M9 2C9.55228 2 10 1.55229 10 1C10 0.447708 9.55228 0 9 0C8.44772 0 8 0.447708 8 1C8 1.55229 8.44772 2 9 2Z',
handleSize: 20,
handleStyle: { color: 'white', borderColor: '#ddd', borderWidth: 0 },
textStyle: {
color: 'white',
fontWeight: 'bold',
lineHeight: 11,
fontSize: 11,
backgroundColor: 'DodgerBlue',
padding: [1, 3, 1, 3],
borderRadius: 2,
},
emphasis: {
// handleStyle: { borderColor: 'PowderBlue' },
},
},
]
if (offline) {
for (let i = 0;i < totalGraphs;i++) {
dataZoom.push({
// Y dataZoom Slider for measure #i
type: 'slider',
yAxisIndex: [i],
right: gridPadRight - 15,
width: 10, // 20,
brushSelect: false,
show: true,
filterMode: 'none',
handleSize: '150%',
showDataShadow: false,
handleStyle: { color: 'gray', borderColor: '#ccc', borderWidth: 1 },
} as any)
dataZoom.push({
// Y dataZoom inside for measure #i
type: 'inside',
maxSpan: 100,
minSpan: 0.02,
yAxisIndex: [i],
filterMode: 'none',
zoomOnMouseWheel: 'shift',
} as any)
}
}
// <--- end DataZooms
// **************************************************************************
// graphic for Background Axis color with bgAxisColor ---->
const graphic = [
{
type: 'group',
bounding: 'raw',
left: GridPadLeft,
right: gridPadRight + 10,
top: WindowHeight! - 226 - totalGaps + totalGraphs * gapGrid,
z: 0,
children: [
{
type: 'rect',
z: 1,
shape: {
width: WindowWidth + 11,
height: 30,
},
style: {
fill: bgAxisColor,
},
},
],
},
{
type: 'group',
bounding: 'raw',
top: 59 - 7,
right: gridPadRight - 15,
z: 0,
children: [
{
type: 'rect',
z: 1,
shape: {
width: 40,
height: WindowHeight! - 284 + 7 - totalGaps + totalGraphs * gapGrid,
},
style: {
fill: bgAxisColor,
},
},
],
},
]
// <----- end graphic
// **************************************************************************
// *********************
// *********************
// Create echarts option
// *********************
// *********************
const option = {
graphic: graphic,
width: String(WindowWidth) + 'px',
grid: grid,
dataset: dataset,
xAxis: xAxis,
yAxis: yAxis,
series: series,
dataZoom: dataZoom,
legend: {
// show: track ? true : false,
show: false,
top: 10,
left: 25,
z: 99999,
formatter: function (name: string) {
const campaign = campaignsInfo?.docs.find((item) => item.id === name)
return campaign?.date.toLocaleString(router.locale)
},
data: campaignsId.map((campaignId, index) => {
return {
name: campaignId,
icon: 'circle',
itemStyle: {
color: getSeverityColor(1, index, campaignsId.length + 1),
}
}
}),
},
visualMap: pending ? defaultVisualMapPiece : FinalvisualMapPieces,
tooltip: {
trigger: 'axis', // => 'axis' show all measures graphData in tooltip, 'item' show one measure
alwaysShowContent: true, // if you want the tooltip fixed
shadowBlur: 0,
shadowOffsetX: 0,
shadowOffsetY: 0,
backgroundColor: 'transparent',
borderColor: 'transparent',
padding: 0,
position: [String(tooltipPosX) + 'px', topPaddingForActionBar + GridPadY - 6 + 'px'],
formatter: (params: any) => {
// data dimension : ['sscount', 'pk', 'severity', 'value', 'id']
let html = '<div>'
if (track) {
// track mode
measures.forEach((measure) => {
html += `
<section style="height: ${heightGrid}px; margin-bottom: ${gapGrid}px; padding-top: 12px;">
<div style="
font-size: 12px;
color: #666;
font-weight: 400;
text-align: right;
width: 45px;
float: left;
">
${t(measure.toUpperCase(), { ns: 'measure' })}
</div>
`
campaignsId.forEach((campaignId, index) => {
// const measureName = `${measure}-campaignID${campaignId}`
const measureName = `series-${index}-campaignID${campaignId}`
// const data = params.find((p: any) => p.seriesName === measureName)
const data = params.find((p: any) => p.seriesId === measureName)
if (data === undefined) return null
const pk = data.value[1]?.toLocaleString(router.locale, { minimumFractionDigits: 3 })
const severity = data.value[5] as number // point[5] = level
let value = null
if (data.value[3]) value = data.value[3].toLocaleString(router.locale, measurePrecision)
const color = DScolorSeverity[severity] // const color = getSeverityColor(severity)
const colorMeasureBase = getSeverityColor(1, index, campaignsId.length) // 'VO' color
html += `
<span style="
margin-left: 6px;
border-color: ${colorMeasureBase};
background-color: rgba(255,255,255,0.65);
border-width: 2px;
border-radius: 5px;
padding: 2px 4px
">
<span
style="
display: inline-block;
border-radius: 10px;
width: 10px;
height: 10px;
background-color: ${color}; "
></span>
<span
style="
font-size: 12px;
color: #666;
font-weight: 400;"
>${value}
</span>
</span>
`
})
html += `</section><div>`
})
} else {
// itinary mode
graphsLayout.forEach((graph, index) => {
html += `<section style="height: ${heightGrid}px; margin-bottom: ${gapGrid}px; padding-top: 12px;">`
graph.forEach((measure, index) => {
const seriesName = params[0].seriesName
const data = params.find((p: any) => p.seriesName === `${measure}-campaignID${campaignsId[0]}`)
if (data === undefined) return null
const pk = data.value[1]?.toLocaleString(router.locale, { minimumFractionDigits: 3 })
const severity = data.value[5] as number // point[5] = level
let value = null
if (data.value[3]) value = data.value[3].toLocaleString(router.locale, measurePrecision)
const color = getSeverityColor(severity)
const colorMeasureBase = getSeverityColor(1, index, 2) // 'VO' color
html += `
<span style="
margin-left: 10px;
border-color: ${colorMeasureBase};
background-color: rgba(255,255,255,0.65);
border-width: 2px;
border-radius: 5px;
padding: 2px 4px
">
<span style="
font-size: 12px;
color: #666;
font-weight: 400;">${t(measure.toUpperCase(), { ns: 'measure' })}
</span>
<span
style="
display: inline-block;
border-radius: 10px;
width: 10px;
height: 10px;
background-color: ${color};"
></span>
<span
style="
font-size: 12px;
color: #666;
font-weight: 400;"
>${value}
</span
</span>
</span>
`
})
html += `</section>`
})
}
return `${html}`
},
axisPointer: { type: 'cross' },
},
axisPointer: {
animation: false,
snap: true,
z: -10,
link: [
{
xAxisIndex: [0, 1, 2, 3],
},
],
lineStyle: {
width: 1,
color: 'gray',
type: 'dashed',
},
label: {
show: true,
formatter: (params: any) => {
if (params.axisIndex > totalGraphs - 1) {
return null
}
if (params.axisDimension === 'y' && params.axisIndex < totalGraphs) {
return '{box|' + Math.round(params.value) + '}'
}
if (params.axisDimension === 'x' && params.axisIndex === totalGraphs - 1) {
const value = params.value
if (track) return '{box|' + value.toLocaleString(router.locale, { minimumFractionDigits: 3 }) + '}'
let newValue = ''
if (graphData[1]?.data) {
const sortedData = graphData[1].data.sort((a, b) => Math.abs(a[0] - value) - Math.abs(b[0] - value))
if (sortedData[0]) {
newValue = sortedData[0]?.[1]?.toLocaleString(router.locale, { minimumFractionDigits: 3 })
}
} else {
return null
}
return '{box|' + newValue + '}'
}
},
margin: 0,
padding: [10, -36, 4, 7],
verticalAlign: 'middle',
align: 'center',
height: 4,
backgroundColor: 'transparent',
color: 'black',
rich: {
box: {
backgroundColor: '#E5342A',
lineHeight: 26,
align: 'center',
verticalAlign: 'middle',
width: 32,
height: 20,
color: 'white',
padding: 4,
borderRadius: 4,
fontSize: 11,
},
},
},
},
animation: false,
}
return option
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment