PMG
PMG

Reputation: 35

Adding PointSeries to Plotters plot in Rust

I am trying to generate a plot with both line and point series using plotters. I have got the chart successfully displaying a line series, however the point series throws a bunch of errors. An abbreviated version of the code I am using for the point series is as follows. I think the problem is how I am specifying the point size but I am not sure how to do it correctly. Any assistance would be great!

fn main() {
    let points: Vec<(f64, f64)> = vec![(2.0, 2.0), (3.0, 3.0 ), (4.0, 4.0), (8.0, 8.0)];
    generate_plot(&points);
}


fn generate_plot(points: &Vec<(f64, f64)>) -> Result<(), Box<dyn std::error::Error>> {
    let root = BitMapBackend::new("C:\\Users\\ppp\\Documents\\Rust\\tpy\\Data\\test.png", (800, 800)).into_drawing_area();
    root.fill(&WHITE)?;
    let mut chart = ChartBuilder::on(&root)
       .build_cartesian_2d(0f64..10f64, 0f64..10f64)?;
    chart.configure_mesh().draw()?;
    chart.draw_series(PointSeries::new(points.clone(), 5, &BLUE))?;  //This does not work. I can get a line series to plot without issue.
    root.present()?;
    Ok(())
}

Errors:

error[E0282]: type annotations needed --> src\main.rs:29:11 | 29 | chart.draw_series(PointSeries::new(points.clone(), 5, &BLUE))?; //This does not work. I can get a line series to plot without issue.
| ^^^^^^^^^^^ cannot infer type of the type parameter B declared on the associated function draw_series | help: consider specifying the generic arguments | 29 | chart.draw_series::<B, E, R, plotters::series::PointSeries<'_, (f64, f64), Vec<(f64, f64)>, E, i32>>(PointSeries::new(points.clone(), 5, &BLUE))?; //This does not work. I can get a line series to plot without issue. |
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

error[E0283]: type annotations needed --> src\main.rs:29:23 | 29 | chart.draw_series(PointSeries::new(points.clone(), 5, &BLUE))?; //This does not work. I can get a line series to plot without issue.
| ^^^^^^^^^^^^^^^^ cannot infer type of the type parameter E declared on the struct PointSeries | = note: multiple impls satisfying _: PointElement<(f64, f64), i32> found in the plotters crate: - impl<Coord, Size> PointElement<Coord, Size> for Circle<Coord, Size> where Size: SizeDesc; - impl<Coord, Size> PointElement<Coord, Size> for Pixel where Size: SizeDesc; - impl<Coord, Size> PointElement<Coord, Size> for TriangleMarker<Coord, Size> where Size: SizeDesc; - impl<Coord, Size> PointElement<Coord, Size> for plotters::element::Cross<Coord, Size> where Size: SizeDesc; note: required by a bound in plotters::series::PointSeries::<'a, Coord, I, E, Size>::new --> C:\Users\ppp.cargo\registry\src\github.com-1ecc6299db9ec823\plotters-0.3.4\src\series\point_series.rs:27:8 | 27 | E: PointElement<Coord, Size>, |
^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in plotters::series::PointSeries::<'a, Coord, I, E, Size>::new help: consider specifying the type argument in the function call | 29 |
chart.draw_series(PointSeries::new::(points.clone(), 5, &BLUE))?; //This does not work. I can get a line series to plot without issue.
| +++++

Upvotes: 2

Views: 481

Answers (1)

cpprust
cpprust

Reputation: 421

Tldr:

Giving a series of points, the compiler doesn't know which type of PointElement you want to create. (Cross or TriangleMarker or Circle or Pixel)

You need to choose the struct for E, e.g.

change

chart.draw_series(PointSeries::new(
    points.clone(),
    5,
    &BLUE,
))?;

to

chart.draw_series(PointSeries::<_, _, Circle<_, _>, _>::new(
    points.clone(),
    5,
    &BLUE,
))?;

Details:

cannot infer type of the type parameter E

multiple impls satisfying _: PointElement<(f64, f64), i32> found in the plotters crate

The type E (a struct which implement PointElement trait) cannot be inferred, because there are multiple candidates, we have to specify which struct we want to create.

In the PointSeries::new, it use E::make_point:

impl<'a, Coord, I: IntoIterator<Item = Coord>, E, Size: SizeDesc + Clone>
    PointSeries<'a, Coord, I, E, Size>
where
    E: PointElement<Coord, Size>,
{
    /// Create a new point series with the element that implements point trait.
    /// You may also use a more general way to create a point series with `of_element`
    /// function which allows a customized element construction function
    pub fn new<S: Into<ShapeStyle>>(iter: I, size: Size, style: S) -> Self {
        Self {
            data_iter: iter.into_iter(),
            size,
            style: style.into(),
            make_point: &|a, b, c| E::make_point(a, b, c),
                                // ^
                                // E is a struct which implement PointElement<Coord, Size>
        }
    }
}

But E have many possible choices:

impl<Coord, Size: SizeDesc> PointElement<Coord, Size> for Cross<Coord, Size> {
    fn make_point(pos: Coord, size: Size, style: ShapeStyle) -> Self {
        Self::new(pos, size, style)
    }
}

impl<Coord, Size: SizeDesc> PointElement<Coord, Size> for TriangleMarker<Coord, Size> {
    fn make_point(pos: Coord, size: Size, style: ShapeStyle) -> Self {
        Self::new(pos, size, style)
    }
}

impl<Coord, Size: SizeDesc> PointElement<Coord, Size> for Circle<Coord, Size> {
    fn make_point(pos: Coord, size: Size, style: ShapeStyle) -> Self {
        Self::new(pos, size, style)
    }
}

impl<Coord, Size: SizeDesc> PointElement<Coord, Size> for Pixel<Coord> {
    fn make_point(pos: Coord, _: Size, style: ShapeStyle) -> Self {
        Self::new(pos, style)
    }
}

You have to specify the struct you want to create in generic argument. (it's ok to fill other arguments with underscore to auto infer)

PointSeries<'a, Coord, I, E, Size>
                          ^

Note:

Upvotes: 1

Related Questions