Created
June 27, 2024 08:45
-
-
Save electroheadfx/71827f8b8a997f99ac29e2768dfc3798 to your computer and use it in GitHub Desktop.
echarts labels overlapping
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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