Git Product home page Git Product logo

yew-chart's Introduction

Yew Charting Library

yew-chart is a collection of components that can be assembled to form charts for the Yew framework.

Here is a soil moisture/rainfall chart that has been produced using this library:

Soil moisture/rainfall chart

Here's another chart produced by this library, representing what is known as "Delta-T":

Delta-T chart

By leveraging these SVG-based components many types of charts can be formed with a great deal of flexibility. The library is intended as a toolkit that provides conveniences for rendering chart primitives. These primitives can be regarded at a similar level as SVG's primitives i.e. lines, polygons etc.

Example Plots

Two basic projects are given in the examples folder.

Line Chart

examples/basic is configured to output a basic line chart:

A line chart

...and here's the entire Yew component that was used to generate it:

const WIDTH: f32 = 533.0;
const HEIGHT: f32 = 300.0;
const MARGIN: f32 = 50.0;
const TICK_LENGTH: f32 = 10.0;

#[function_component(App)]
fn app() -> Html {
    let end_date = Utc::now();
    let start_date = end_date.sub(Duration::days(4));
    let timespan = start_date..end_date;

    let circle_text_labeller = Rc::from(series::circle_text_label("Label")) as Rc<dyn Labeller>;

    let data_set = Rc::new(vec![
        (start_date.timestamp_millis(), 1.0, None),
        (
            start_date.add(Duration::days(1)).timestamp_millis(),
            4.0,
            None,
        ),
        (
            start_date.add(Duration::days(2)).timestamp_millis(),
            3.0,
            None,
        ),
        (
            start_date.add(Duration::days(3)).timestamp_millis(),
            2.0,
            None,
        ),
        (
            start_date.add(Duration::days(4)).timestamp_millis(),
            5.0,
            Some(circle_text_labeller),
        ),
    ]);

    let h_scale = Rc::new(TimeScale::new(timespan, Duration::days(1))) as Rc<dyn Scale<Scalar = _>>;
    let v_scale = Rc::new(LinearScale::new(0.0..5.0, 1.0)) as Rc<dyn Scale<Scalar = _>>;

    let tooltip = Rc::from(series::y_tooltip()) as Rc<dyn Tooltipper<_, _>>;

    html! {
            <svg class="chart" viewBox={format!("0 0 {} {}", WIDTH, HEIGHT)} preserveAspectRatio="none">
                <Series<i64, f32>
                    series_type={Type::Line}
                    name="some-series"
                    data={data_set}
                    horizontal_scale={Rc::clone(&h_scale)}
                    horizontal_scale_step={Duration::days(2).num_milliseconds()}
                    tooltipper={Rc::clone(&tooltip)}
                    vertical_scale={Rc::clone(&v_scale)}
                    x={MARGIN} y={MARGIN} width={WIDTH - (MARGIN * 2.0)} height={HEIGHT - (MARGIN * 2.0)} />

                <Axis<f32>
                    name="some-y-axis"
                    orientation={Orientation::Left}
                    scale={Rc::clone(&v_scale)}
                    x1={MARGIN} y1={MARGIN} xy2={HEIGHT - MARGIN}
                    tick_len={TICK_LENGTH}
                    title={"Some Y thing".to_string()} />

                <Axis<i64>
                    name="some-x-axis"
                    orientation={Orientation::Bottom}
                    scale={Rc::clone(&h_scale)}
                    x1={MARGIN} y1={HEIGHT - MARGIN} xy2={WIDTH - MARGIN}
                    tick_len={TICK_LENGTH}
                    title={"Some X thing".to_string()} />

            </svg>
    }
}

If passing SeriesData around via Props, you'll need to provide a PartialEq implementation. Rust won't be able to derive one automatically for you as SeriesData holds a closure that cannot be compared. This doesn't end up being a problem though as should be performing an explicit pointer comparison when comparing Rc based values anyway; Rc contained references are immutable. Rc::ptr_eq is your friend in this scenario.

That all said, you probably won't be passing SeriesData around via props, but some other Rc of a model oriented data type which is to be mapped to it. Using ptr_eq is still the way to go there though as you'll get performance benefits.

Bar Chart

Using the same Yew view method code as above, series_type within the Series tag can be edited to display a bar chart instead by using the Bar keys.

<Series series_type={Type::Bar(BarType::Rise)} ... />

A rising bar chart

The bar chart can also be made to 'drop' instead of 'rise' with the enum variant BarType. This is particularly helpful if using negative axis values, where the bars are dropping from y = 0 or similar.

When the BarType is set to Drop, the same data from above produces the following graph.

As seen, the bars are set to go from the selected side to the datapoint. Since the axis range is from 0.0 to 5.0, and the first point is at (26-Mar, 1.0), the bar drops from 5.0 to 1.0.

<Series series_type={Type::Bar(BarType::Drop)} ... />

A dropping bar chart

One final example of this is in using a negative scale and negative values. In the following graph, the axis ranges from -5.0 to 0.0, and the first point is (01-Apr, -1.0).

A dropping bar chart

Scatter Plot

examples/scatter is configured to output a basic scatter plot. The method by which this is accomplished is slightly different to that of the Line and Bar charts.

Contribution policy

Contributions via GitHub pull requests are gladly accepted from their original author. Along with any pull requests, please state that the contribution is your original work and that you license the work to the project under the project's open source license. Whether or not you state this explicitly, by submitting any copyrighted material via pull request, email, or other means you agree to license the material under the project's open source license and warrant that you have the legal authority to do so.

License

This code is open source software licensed under the Apache-2.0 license.

© Copyright Titan Class P/L, 2021

yew-chart's People

Contributors

avonow avatar huntc avatar rorystokes avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

yew-chart's Issues

Safari SVG rendering issue when rotating text

There is an SVG rendering issue that seems prominent on Safari (Chrome is fine). The resolution is to avoid rotating text given a translation or transformation of origin.

The unfortunate workaround is to not rotate text along the bottom axis and space things out a bit more.

Title conflicts with axis labels

In its current position, the title conflicts with the axis labels. Since the title is currently already rotated, if the user attempts to provide a new transformation to change the title position, the rotation will be overridden.

From line 127 in vertical_axis.rs:
transform={format!("rotate({}, {}, {})", rotation, x, y)}

image

Line bar in title not optional

The (currently green) bar next to the title is currently not optional; it should be optional for the case that the axis is used without needing a bar indicator.
image

Time not displaying correctly

I have a function that creates data like this:

let fake_pulsewidth_data = (0..10)
    .map(|i| {
        let x_min = time_range_displayed.start;
        let x_range = time_range_displayed.end - time_range_displayed.start;
        let x = x_min + x_range / 10 * i;

        let y_min = self.series_scale.vertical.start;
        let y_range = self.series_scale.vertical.end - self.series_scale.vertical.start;
        let y = y_min + y_range / 10.0 * i as f32;
        (x, y)
    })
    .collect();

These are being plotted like so, where the following ranges are:

time_range_displayed: 2019-04-16T23:50:03.046417Z..2019-04-17T23:50:57.860785Z
self.series_scale.vertical: 0.0..10.0

image

As seen, this works correctly when the time range is quite large (>1 day).

However, when a much smaller time range is used (~ 5 mins), the datapoints are not being rendered correctly:

time_range_displayed: 2022-03-11T02:53:04.282941Z..2022-03-11T02:58:02.182941Z
self.series_scale.vertical: 0.0..10.0

image

Yew 0.20

I might be doing something wrong but I tried one of the examples with Yew 0.20 and get this error:

error[E0277]: the trait bound `Series<i64, f32>: yew::Component` is not satisfied
  --> src\chart.rs:52:14
   |
52 |             <Series<i64, f32>
   |              ^^^^^^ the trait `yew::Component` is not implemented for `Series<i64, f32>`
   |
   = help: the trait `yew::Component` is implemented for `ContextProvider<T>`
   = note: required for `Series<i64, f32>` to implement `yew::BaseComponent`
   = note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info)

Any idea what has changed?

Top tick doesn't render

A labeller is created as such:

    let labeller_v_top =
        || -> Box<Labeller> { Box::new(move |v| format!("{:9.1$}", v, vertical_precision)) };  //vertical_precision = 0;

    let y_scale_top = Rc::new(LinearScale::with_labeller(
        props.series_scale.vertical.clone(),     //-112.35..0.0
        props.series_scale.vertical_step,        //11.235
        Some(Rc::from(labeller_v_top())),
    )) as Rc<dyn Scale>;
<Axis
    name="y-axis"
    orientation={Orientation::Left}
    scale={Rc::clone(&y_scale_top)}
    x1={left_margin}          //100
    y1={top_margin}           //25
    xy2={y2_top_graph}        //432.5
    tick_len={TICK_LENGTH}/>  //15

The top tick is not being rendered:
image

Chart layout assumes single plot and uniform margin

The plot components are well suited to a simple layout with a single plot and uniform margin around all sides, however it isn't clear how to manage different margin sizes or a layout where an axis might only belong to a fraction of the plot area.

Dropped bar charts incorrectly representing data

The recent addition to enable a bar to rise or drop from the bottom or top respectively is not capturing the correct data value. If the examples/basic chart is changed to represent a bar chart with a drop, then the first value of the first bar is 1. However, it is rendered with a length of 4.

Values being clipped

Scatter plot values are clipped when they are greater than the vertical axis.

Ideally, they would not be rendered if they exceed the axis bounds.

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.