QCustomPlot add scatter points on top of existing plot - c++

After creating a plot using qcustomplot, how can I, based on existing X values, retrieve the Y values, and then plot points at these locations?
My attempt is below:
Create plot:
This function creates a plot, adds some data (dates) on the x-axis and some values on the y axis:
void Qt_PlotTest::createPlot(){
QCustomPlot* customPlot = ui.widget;
customPlot->setLocale(QLocale(QLocale::English, QLocale::UnitedKingdom));
//Set some data:
double now = QDateTime::currentDateTime().toTime_t();
QVector<double> yData, xData;
for (int i = 0; i < 100; i++){
xData.push_back(now + i*24.0 * 3600.0);
yData.push_back(pow(double(i), 2) + 550.0*sin(double(i)/4.0));
}
// create graph and assign data to it:
customPlot->addGraph();
customPlot->graph(0)->setData(xData, yData);
//Fix axes:
//Set date axis:
QSharedPointer<QCPAxisTickerDateTime> dateTicker(new QCPAxisTickerDateTime);
dateTicker->setDateTimeFormat("d. MMMM\nyyyy");
customPlot->xAxis->setTicker(dateTicker);
customPlot->xAxis->setLabel("Time");
customPlot->yAxis->setLabel("Value");
customPlot->rescaleAxes();
customPlot->replot();
//Set interations:
customPlot->setInteraction(QCP::iRangeDrag, true);
customPlot->setInteraction(QCP::iRangeZoom, true);
}
Which results in:
Set scatter points:
This functions should plot a few scatter points at correct x/y locations based on x values. (I need to read the corresponding Y value from the existing plot).
void Qt_PlotTest::setScatterPoints(){
QCustomPlot* customPlot = ui.widget;
//The locations where scatter points should be plotted:
QDate qd1(2018, 03, 26), qd2(2018, 03, 30), qd3(2018, 05, 11), qd4(2018, 06, 15);
QVector<double> dates = { (double)QDateTime(qd1).toTime_t(), (double)QDateTime(qd2).toTime_t(), (double)QDateTime(qd3).toTime_t(), (double)QDateTime(qd4).toTime_t() };
//Use tracer to find data at these dates:
QVector<double> values;
QCPItemTracer *tracer = new QCPItemTracer(customPlot);
tracer->setGraph(customPlot->graph(0));
tracer->setInterpolating(true);
for (int i = 0; i < 4; i++){
tracer->setGraphKey(dates[i]);
values.push_back(tracer->position->coords().y());
}
//Plot points at these locations:
QCPGraph* dwPoints = new QCPGraph(customPlot->xAxis, customPlot->yAxis);
dwPoints->setAdaptiveSampling(false);
dwPoints->setLineStyle(QCPGraph::lsNone);
dwPoints->setScatterStyle(QCPScatterStyle::ssCircle);
dwPoints->setPen(QPen(QBrush(Qt::red), 2));
dwPoints->addData(dates, values);
customPlot->replot();
}
Which results in:
So obviously the QCPItemTracer didn't find the correct Y values. (I also got some additional axes which I don't want.)
Is QCPItemTracer what I want to use to achieve what I want? I also saw some example using QCPDataMap to find Y-values based on X-values. But as far as I understand, QCPDataMap, is no longer in qcustomplot.

I solved my own problem:
I had to call tracer->updatePosition() to actually get the coordinates in the coordinate system in which the tracer is placed.
The axes I didn't want were actually the visualization of the tracer at the final tracer location. I just had to call tracer->setVisibile(false) to hide it.
So in void Qt_PlotTest::setScatterPoints I have this:
QCPItemTracer *tracer = new QCPItemTracer(customPlot);
tracer->setGraph(customPlot->graph(0));
tracer->setInterpolating(true);
tracer->setVisible(false);
for (int i = 0; i < 4; i++){
tracer->setGraphKey(dates[i]);
tracer->updatePosition();
values.push_back(tracer->position->coords().y());
}
Result:

Related

How to handle Datachange signal for circle diagram?

I'm drawing circle diagram using my own inbuilt libraries.
I'm able to draw circles using table data (x1,y1 & r) ,sharing code
I'm using datachange signal with table, whenever enter any table item data then its creating no. of graph with circles. Is there other signal I can use or what change can make in code ? I want single graph with no. of circles(based on no. of entries in table).
Also when circles are drawn lines coming like we draw without removing our pen to draw another circle how to overcome this ?
CHPlotGraph2D - this class to create graph
CHPlotCurveData- this Class hold the data points for the curves
CHPlotCurve-Class to draw the data as a line curve
CHPlotCurveData* curvedata1 = new CHPlotCurveData();
QAbstractItemModel* table1 = ui.tableView->model();
for (int irows = 0, maxI = table1->rowCount(); irows < maxI; ++irows)
{
double x1 = table1->data(table1->index(irows, 1)).toDouble();
double y1 = table1->data(table1->index(irows, 2)).toDouble();
double r = table1->data(table1->index(irows, 6)).toDouble();
for (double angle = 0; angle <= 360; angle++)
{
double theta = (angle * 180) / 3.14;
double zx = x1 + r * cos(theta);
double zy = y1 + r * sin(theta);
QPointF pt(zx, zy);
curvedata1->append((pt));
}
}
CHPlotCurve* curve1 = (CHPlotCurve*)pGr->insertCurve("circle",
CHPlotGraph2D::Line, false );
curve1->setSamples(curvedata1);
connect(ui.tableView->model(), &QAbstractItemModel::dataChanged,
this,
&tablemodel::drawCircle);
Thank you ..I have solved the problem .When anything changes in the table, I need to remove the existing curve containing all the circles and build/add a new one, or replace the data

Point cloud conversion to 2D range

I am trying to convert a point cloud (x, y, z) data acquired from a Kinect V2 using libfreenect2, into a virtual 2D laser scan (e.g., a horizontal angle/distance vector).
I am currently assigning per pixel column, the PCL distance value, as shown below:
std::vector<float> scan(512, 0);
for (unsigned int row = 0; row < 424; ++row) {
for (unsigned int col = 0; col < 512; ++col) {
float x, y, z;
registration->getPointXYZ(depth, row, col, x, y, z);
if (std::isnan(x) || std::isnan(y) || std::isnan(z)) {
continue;
}
Eigen::Vector3f values = rotate_translate((-1 * x), y - 1.186, z);
if (scan[col] == 0) {
scan[col] = values[1];
}
if (values[1] < scan[col]) {
scan[col] = values[1];
}
}
}
You may ignore the rotate_translate method, it simply changes the local to global coordinates using the sensor pose.
The problem is best shown using the pictures below:
Whereas the LIDAR range sensor produces the following pointsmap:
the kinect 2D range scan is curved, and of course narrower, since the horizontal FOV is 70.6 degrees compared to the 270 degree range of the LIDAR.
It is this curvature that I am trying to fix; the SLAM/ICP library I'm using is mrpt and the actual data scan is inserted into an mrpt::obs::CObservation2DRangeScan observation:
auto obs = mrpt::obs::CObservation2DRangeScan();
obs.loadFromVectors(scan.size(), scan.data(), (char*)scan.data());
obs.aperture = mrpt::utils::DEG2RAD(70.6f);
obs.maxRange = 6.0;
obs.rightToLeft = true;
obs.timestamp = mrpt::system::now();
obs.setSensorPose(sensor);
I've searched around google and SO, and the only answers which seem to address this question, are this one and that one. So whereas I understand that the curvature is the result of me assigning each pixel column the PCL value, I am uncertain as to how I can use that to remove the curvature.
Each reply seems to take a different approach, and from what I understand the task is a linear interpolation of the angle per pixel ratio, and the current pixel coordinates?

QCustomPlot draw residuum between two functions

I want to draw the residuum between two functions at discrete points with qcustomplot.
I know the position (x), the starting value y.at(x) and the height.at(x).
what I have so far is an error bar with y+-error:
QCPErrorBars *errorBars = new QCPErrorBars(customPlot->xAxis, customPlot->yAxis);
errorBars->setDataPlottable(customPlot->graph(0));
QVector<double> y1err(x.size());
for (int i = 0; i<x.size(); ++i)
{
y1err[i] = y.at(i) * error;
}
customPlot->graph(0)->setData(QVector<double>::fromStdVector(x), QVector<double>::fromStdVector(y));
errorBars->setData(y1err);
or a bar starting from zero:
QCPBars *newBars = new QCPBars(customPlot->xAxis, customPlot->yAxis);
std::vector<double> xData, yData;
for (auto i = 0; i < x.size(); ++i)
{
xData.push_back(i+1);
yData.push_back(y.at(i));
}
newBars->setData(QVector<double>::fromStdVector(x), QVector<double>::fromStdVector(y));
but what I really want is some kind of a plot starting at the value y.at(x) with the height of the residuum at the point x in addition to the two x-y plots.
How can I plot a bar or error bar starting at the y.at(x) with height.at(x)?
Thank you
For other people facing this problem I found some kind of a solution.
void QLinePlot::AddResiduumData(std::vector<double> x, std::vector<double> y_mid, std::vector<double> y_res)
{
customPlot->addGraph();
++graphCountI;
QCPErrorBars *errorBars = new QCPErrorBars(customPlot->xAxis, customPlot->yAxis);
errorBars->setDataPlottable(customPlot->graph(graphCountI - 1));
customPlot->graph(graphCountI - 1)->setData(QVector<double>::fromStdVector(x), QVector<double>::fromStdVector(y_mid));
customPlot->graph(graphCountI - 1)->setVisible(false);
errorBars->setData(QVector<double>::fromStdVector(y_res));
customPlot->replot();
}
The idea behind this is adding a new invisible graph between both plots and give the error as half the distance between both.

Access array values in a vector<Point2f> and separate x- and y coordinates

I'm writing a code which calculates the optical flow with the iterative Lucas-Kanade method: calcOpticalFlowPyrLK().
I have a vector of an array that can hold two elements, see example below:
vector <Point2f> points[2];
The x and y coordinates are stored in the array and the array is stored in the vector. When outputting the array, for instance cout << points[0], the coordinates are currently displayed on the screen as follows:
Output example: [261.837, 65.093]
Now I want to extract the x- and y coordinate, separate them and store them in different variables. Already tried several ways with an iterator with no result. I would appreciate it if someone could help me with this, thanks.
The following example applies the PLK to a regular grid and shows how to read the x and y coordinates. The points are stored in a Point2f class using the vector class to store them in an array. The class has public x and y members you can use directly. This examples uses no iterator.
std::vector<cv::Point2f> prevPoints, currPoints;
std::vector<float> error; // stores the SSD error.
std::vector<uchar> status;// stores a flag of successful tracking / I recomend to ignore it.
cv::Mat prevGrayImg,currGrayImg;
// <- insert code for read the images
// initalize grid or the features you want to track
for( int r = 0; r < prevGrayImg.rows;r+=5){
for( int c = 0; c < prevGrayImg.cols;c+=5){
prevPoints.push_back(cv::Point2f(c,r));
}}
// apply pyramidal lucas kanade
cv::calcOpticalFlowPyrLK(prevGrayImg, currGrayImg, prevPoints, currPoints, status, error);
for( unsigned int i = 0; i < prevPoints.size(); i++){
float x0 = prevPoints[i].x;
float y0 = prevPoints[i].y;
float x1 = currPoints[i].x;
float y1 = currPoints[i].y;
}
With iterator it would be:
for( auto i = prevPoints.begin(); i != prevPoints.end(); ++i){
float x0 = i->x; ... a.s.o

vtk 6.x, Qt: 3D (line, surface, scatter) plotting

I am working on a Qt (4.7.4) project and need to plot data in 2D and 3D coordinate systems. I've been looking into vtk 6.1 because it seems very powerful overall and I will also need to visualize image data at a later point. I basically got 2D plots working but am stuck plotting data in 3D.
Here's what I tried: I'm using the following piece of code that I took from one of vtk's tests ( Charts / Core / Testing / Cxx / TestSurfacePlot.cxx ). The only thing I added is the QVTKWidget that I use in my GUI and its interactor:
QVTKWidget vtkWidget;
vtkNew<vtkChartXYZ> chart;
vtkNew<vtkPlotSurface> plot;
vtkNew<vtkContextView> view;
view->GetRenderWindow()->SetSize(400, 300);
vtkWidget.SetRenderWindow(view->GetRenderWindow());
view->GetScene()->AddItem(chart.GetPointer());
chart->SetGeometry(vtkRectf(75.0, 20.0, 250, 260));
// Create a surface
vtkNew<vtkTable> table;
float numPoints = 70;
float inc = 9.424778 / (numPoints - 1);
for (float i = 0; i < numPoints; ++i)
{
vtkNew<vtkFloatArray> arr;
table->AddColumn(arr.GetPointer());
}
table->SetNumberOfRows(numPoints);
for (float i = 0; i < numPoints; ++i)
{
float x = i * inc;
for (float j = 0; j < numPoints; ++j)
{
float y = j * inc;
table->SetValue(i, j, sin(sqrt(x*x + y*y)));
}
}
// Set up the surface plot we wish to visualize and add it to the chart.
plot->SetXRange(0, 9.424778);
plot->SetYRange(0, 9.424778);
plot->SetInputData(table.GetPointer());
chart->AddPlot(plot.GetPointer());
view->GetRenderWindow()->SetMultiSamples(0);
view->SetInteractor(vtkWidget.GetInteractor());
view->GetInteractor()->Initialize();
view->GetRenderWindow()->Render();
Now, this produces a plot but I can neither interact with it not does it look 3D. I would like to do some basic stuff like zoom, pan, or rotate about a pivot. A few questions that come to my mind about this are:
Is it correct to assign the QVTKWidget interactor to the view in the third line from the bottom?
In the test, a vtkChartXYZ is added to the vtkContextView. According to the documentation, the vtkContextView is used to display a 2D scene but here is used with a 3D chart (XYZ). How does this fit together?
The following piece of code worked for me. No need to explicitly assign an interactor because that's already been taken care of by QVTKWidget.
QVTKWidget vtkWidget;
vtkSmartPointer<vtkContextView> view = vtkSmartPointer<vtkContextView>::New();
vtkSmartPointer<vtkChartXYZ> chart = vtkSmartPointer<vtkChartXYZ>::New();
// Create a surface
vtkSmartPointer<vtkTable> table = vtkSmartPointer<vtkTable>::New();
float numPoints = 70;
float inc = 9.424778 / (numPoints - 1);
for (float i = 0; i < numPoints; ++i)
{
vtkSmartPointer<vtkFloatArray> arr = vtkSmartPointer<vtkFloatArray>::New();
table->AddColumn(arr.GetPointer());
}
table->SetNumberOfRows(numPoints);
for (float i = 0; i < numPoints; ++i)
{
float x = i * inc;
for (float j = 0; j < numPoints; ++j)
{
float y = j * inc;
table->SetValue(i, j, sin(sqrt(x*x + y*y)));
}
}
view->SetRenderWindow(vtkWidget.GetRenderWindow());
chart->SetGeometry(vtkRectf(200.0, 200.0, 300, 300));
view->GetScene()->AddItem(chart.GetPointer());
vtkSmartPointer<vtkPlotSurface> plot = vtkSmartPointer<vtkPlotSurface>::New();
// Set up the surface plot we wish to visualize and add it to the chart.
plot->SetXRange(0, 10.0);
plot->SetYRange(0, 10.0);
plot->SetInputData(table.GetPointer());
chart->AddPlot(plot.GetPointer());
view->GetRenderWindow()->SetMultiSamples(0);
view->GetRenderWindow()->Render();
You might want read the detailed description in vtkRenderViewBase
QVTKWidget *widget = new QVTKWidget;
vtkContextView *view = vtkContextView::New();
view->SetInteractor(widget->GetInteractor());
widget->SetRenderWindow(view->GetRenderWindow());