Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Line style for varied segments and connectNulls #17138

Open
Ovilia opened this issue May 30, 2022 · 24 comments
Open

[Feature] Line style for varied segments and connectNulls #17138

Ovilia opened this issue May 30, 2022 · 24 comments
Labels
en This issue is in English new-feature topic: line

Comments

@Ovilia
Copy link
Contributor

Ovilia commented May 30, 2022

What problem does this feature solve?

Current API lacks the ability to control line style for varied segments. For example, if we want to make different styles for a line series, we have to mock it using many series, which is not an elegant way to do.

052a24de686d38783a9723dbb

What does the proposed API look like?

The new option should give the developer the control over line styles for different line segments. The API could be:

{
  data: [{
    value: 10,
    lineStyle: {...}, // controls the line style after this data
    areaStyle: {...}
  }]
}

or something similar to visualMap.pieces:

{
  linePieces: [{
    index: [0, 2, 4], // the index of line segments
    lineStyle: {...},
    areaStyle: {...}
  }]
}

or callback rules:

{
  linePieces: [{
    rule: function(params) { return params.dataIndex % 4 === 1; },
    lineStyle: {...},
    areaStyle: {...}
  }]
}

We should also provide a way to set the lineStyle for the lines from connectNulls. The API may be

{
  nullLineStyle: {...},
  nullAreaStyle: {...}
}

or

{
  connectNullStyle: {
    lineStyle: {...},
    areaStyle: {...}
  }
}

or

{
  linePieces: [{
    rule: function(params) { return params.isConnectNulls },
    lineStyle: {...},
    areaStyle: {...}
  }]
}

This issue is a task for OSPP so it will probably be fixed by a student from the program. Discussion about the design and your own requirement is always welcomed.

@Ovilia
Copy link
Contributor Author

Ovilia commented May 30, 2022

See also: #14239 #10233

@pe-2
Copy link

pe-2 commented May 30, 2022

What about api like this? This is my initial idea. @Ovilia 😊

   series: [
       {
          data: [150, 230, 224, 218, 135, 147, 260],
          type: 'line',
           // itemStyle and areastyle is also like this
          lineStyle:{ 
                type: "dotted",  // global style
                styles:{  // part style
                      type:'solid',
                      scope:[0,1],
                },
          }
       }
    ]

@pe-2
Copy link

pe-2 commented May 30, 2022

sorry ,styles should be an array 😅

@Ovilia
Copy link
Contributor Author

Ovilia commented May 31, 2022

@pe-2 Thanks for your suggestion. Although we do not have something like this for other options, theoretically speeking, this could also be a solution. I would suggest comparing each of these solutions and getting to a conclusion which one is the best. The thinking behind the choice of API design is the most important thing for this task.

P.S. We don't have any application on this task yet. Please make sure you submit the application on time.

Thanks!

@pe-2
Copy link

pe-2 commented May 31, 2022

You’re welcome, It’s my pleasure. I will submit the application after I complete it. 😁

@zz5840
Copy link

zz5840 commented Jun 4, 2022

We should also provide a way to set the lineStyle for the lines from connectNulls. The API may be

{
  nullLineStyle: {...},
  nullAreaStyle: {...}
}

or

{
  connectNullStyle: {
    lineStyle: {...},
    areaStyle: {...}
  }
}

If we use these two APIs for setting lineStyle for null item, how could we set lineStyle for each null item individually?

@zz5840
Copy link

zz5840 commented Jul 24, 2022

@Ovilia I'd like to add following properties to option in LineSeries.ts#L72 for implementing the second proposal:

export interface LinePieceData {
    index: number
    leftValue: number
    rightValue: number
    lineStyle: LineStyleOption
    isConnectNulls: boolean
}

export interface LinePiecesOption {
    indexes?: number[]
    rules: (piece: LinePieceData) => boolean
    lineStyle?: LineStyleOption
}

export interface LineSeriesOption {
    // ...
    linePieces?: LinePiecesOption[]
}

The new API can be call by providing the linePieces option as you proposed:

or something similar to visualMap.pieces:

{
  linePieces: [{
    index: [0, 2, 4], // the index of line segments
    lineStyle: {...},
    areaStyle: {...}
  }]
}

or callback rules:

{
  linePieces: [{
    rule: function(params) { return params.dataIndex % 4 === 1; },
    lineStyle: {...},
    areaStyle: {...}
  }]
}

When using property rules to indicate which pieces should this style applied to, the callback function will receive the following arguments as a piece object:

export interface LinePieceData {
    // index of the line piece
    index: number
    // left endpoint value of the line piece
    leftValue: number
    // right endpoint value of the line piece
    rightValue: number
    // line style will be applied to the line piece
    lineStyle: LineStyleOption
    // indicates if the piece is used to connect null data
    isConnectNulls: boolean
}

What do you think of this implement?

@Ovilia
Copy link
Contributor Author

Ovilia commented Jul 25, 2022

@zz5840 Thanks for the update. I want to clearify that if linePieces.index is the index of the data? I'm not sure what does index: [0, 2, 4] mean? Does this mean the lines between (data[0], data[1]), (data[2], data[3]), (data[3], data[4]) appy this style?

We should better use names like startValue and endValue rather than leftValue and rightValue because if the axis is inversed, the order of data is not the same as in the chart.

areaStyle should also be provided in the API.

@zz5840
Copy link

zz5840 commented Jul 26, 2022

@Ovilia Thanks for reply.

I'd rather make linePieces.index be the index of the line segment, which is showed in the image below. The red number is line index and the blue is data index.

msedge_9d7gVn5wp0

Line index will be different from data index only if a null data exist. So if we use line index, all index will be valid, and things like index pointing to a null data will not happen.

But there still exist disadvantage in line index. For example, it's hard to control the style of data segment (eg. to set the style for all data whose x-axis data is small than a certain value) when using dynamic data and number of nulls is unknown.

So which index should we provide, or should we provide both?

@Ovilia
Copy link
Contributor Author

Ovilia commented Jul 29, 2022

If the index means the ordinal number of the lines, then you must be careful because when the data is dense enough with line series, the data item (empty circles) is not always visible. Do you wish to have

image

or

image

?

Also, I feel it strange to exclude null data when making this index. It would mean the nth non-null line. I'm not sure if this is helpful for developers. Can you think of an example when this way is more convenient?

it's hard to control the style of data segment (eg. to set the style for all data whose x-axis data is small than a certain value) when using dynamic data and number of nulls is unknown.

This is a common situation so I think we shouldn't exclude null data in the index unless we have some solid reasons.

@zz5840
Copy link

zz5840 commented Aug 3, 2022

Also, I feel it strange to exclude null data when making this index. It would mean the nth non-null line. I'm not sure if this is helpful for developers. Can you think of an example when this way is more convenient?

I didn't mean to exclude null data, I mean this is what will happen if we use the index of the left endpoint as the index of line segment. But as you say, it's very common to set the style for all data whose x-axis data is small than a certain value, so we must accept a inconsecutive index if we use choose this solution.

Also, if we choose this solution, only the following image will show the correct index.

@Ovilia
Copy link
Contributor Author

Ovilia commented Aug 4, 2022

@zz5840 Thanks for the clarification. Just a few updates on the API design:

export interface LineSeriesOption {
    lineSegments?: LineSegmentsOption[],
    nullItemStyle?: {...}, // item style for null data
    nullLineStyle?: {...},
    nullAreaStyle?: {...},
    // ...
}

export interface LineSegmentsOption {
    dataIndexRange: number[][]
    includeEndItem: boolean = true
    itemStyle?: ItemStyleOption
    lineStyle?: LineStyleOption
    areaStyle?: AreaStyleOption
}
  1. I don't think we should provide isConnectNulls in LinePieceData (or at least you can make an PR without this first and later add this feature when you have time). For now, isConnectNulls is a series-level option and it doesn't seem very necessary to provide a segment-level option to me.
  2. Please note that the type of data values of line series is not number only. It could be numbers like 123, or strings like '123' or arrays like ['2020-10-10 13:00:00', 123]. Use LineDataValue if you are referencing to input data types.
  3. dataIndex is a better name than index because the latter one can also mean series index or others.
  4. dataIndexRange is an array containing an array of length two. It works like Array.slice, where the first number means the starting index and the second number means the length. So [[2, 1], [7, 2]] means data with index 2, 7, and 8.
  5. For itemStyle, if includeEndIndex is set to be true, the data at the end of the segment will be applied the itemStyle. For example, if dataIndexRange is [[2, 1], [7, 2]] and includeEndIndex is false, then data item 2, 7, and 8 will apply the itemStyle. If includeEndIndex is true, then data item 2, 3, 7, 8 and 9 will apply the itemStyle.
  6. I removed the callback form in the API because it seems to complicate the situation. Developers have such situations can loop through the data and generate an array of lineSegments to implement the requirement. So I think we can go without it and see if it's necessary in the future.
  7. I added nullItemStyle, nullLineStyle, and nullAreaStyle in the API. When data contains null, we should automatically create a segment from the null data and use corresponding styles.

@gwak
Copy link

gwak commented Aug 31, 2022

Maybe how PlottableJs handles property updates can be of help for you to decide which API is best. For each property that you can set in PlottableJs, you have the choice of either give the value statically or give an accessor function:

https://github.com/palantir/plottable/blob/develop/src/core/interfaces.ts#L13-L19

/**
 * Accesses a specific datum property. Users supply Accessors to their
 * plots' .x, .y, .attr, etc. functions.
 */
export interface IAccessor<T> {
    (datum: any, index: number, dataset: Dataset): T; // Inputs should be replaced by charts specific dataset data structures
}

Then when creating a Plot in Plottable:

const stackedBarPlot = new Plottable.Plots.StackedBar()
      .datasets(datasets)
      .attr('fill', (d: any, I: number, dataset: Dataset) => dataset.metadata().color);

How it would look like in echarts:

export interface LineSeriesOption .... {
  lineStyle: LineStyleOption | IAccessor<LineStyleOption>;
...
}

Then in user-land:

const baseLineStyle = {...};
...

lineStyle: (datum: any, index: number, dataset: Dataset) => {
  return {... baseLineStyle, dashOffset: index > 10 ? 0 : 4 };
}
...

That way a user of the library can very easily create its own logic, that even reusable because it's a simple function.

@ArslanGhaffar21
Copy link

What about api like this? This is my initial idea. @Ovilia 😊

   series: [
       {
          data: [150, 230, 224, 218, 135, 147, 260],
          type: 'line',
           // itemStyle and areastyle is also like this
          lineStyle:{ 
                type: "dotted",  // global style
                styles:{  // part style
                      type:'solid',
                      scope:[0,1],
                },
          }
       }
    ]

there is no any styles in lineStyle , and did you submit application ?

@DoubleCorner
Copy link

Will this feature be implemented in the next version?

@helgasoft
Copy link

A workaround with renderItem - Demo Code
image

@xiziliang
Copy link

I really need lineStyle!!!
{ data: [{ lineStyle: {...}, }] }

@sgtsaughter
Copy link

Is there a way to simply make lineStyle.color and lineStyle.type accept a function. itemStyle.color already accepts a function and can give you the ability to change color based on dataPoint and index.

@Ovilia Ovilia moved this to Pending in ECharts 6.0 Jan 31, 2024
@Ovilia Ovilia moved this from Pending to Backlog in ECharts 6.0 Jan 31, 2024
@user-et
Copy link

user-et commented Mar 21, 2024

Image

您好,最终能实现到可以根据第三维信息,通过visualmap获取映射对应的颜色;然后两点之间根据首尾节点的颜色做渐变的效果吗?
或者是输入和数据等长的信息,描述每个节点的颜色,然后折线段根据2个节点的颜色做渐变呢?

@helgasoft
Copy link

@user-et, gradient possible thru custom line - Demo

image

@user-et
Copy link

user-et commented Mar 22, 2024

thank you, i'll give it a try

@agraddy
Copy link

agraddy commented Jul 22, 2024

I agree with @sgtsaughter. If lineStyle.color and lineStyle.type could accept a function that would solve the issue.

When I was looking through the documentation, I saw that symbolSize accepts a function so I assumed lineStyle would too. When it didn't accept a function, I searched the issue list and ended up here.

PS Really impressive project! Thanks for all of the consistent updates and documentation, I know that takes a lot of work!

@mountpoint
Copy link

@Ovilia any updates? what the current state of this issue?

@Ovilia
Copy link
Contributor Author

Ovilia commented Nov 6, 2024

@mountpoint I plan to make a PR for v6.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
en This issue is in English new-feature topic: line
Projects
Status: Pending
Development

No branches or pull requests