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

Plot 2d-image like data as heatmap #52

Merged
merged 5 commits into from
Apr 13, 2016
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ NixView.pro.user.18
NixView.pro.user.3.2-pre1
NixView.pro.user.5e84e8a
*~
.idea/
149 changes: 149 additions & 0 deletions plotter/imageplotter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#include "imageplotter.h"
#include "ui_imageplotter.h"
#include <QMenu>


ImagePlotter::ImagePlotter(QWidget *parent) :
QWidget(parent), ui(new Ui::ImagePlotter), cmap() {
ui->setupUi(this);

}

ImagePlotter::~ImagePlotter() {
delete ui;
}

QCustomPlot *ImagePlotter::get_plot() {
return ui->plot;
}

struct DimInfo {
bool valid;
QString unit;
QString label;

QVector<double> ticks;
QCPRange range;
};

static DimInfo get_dim_info(const nix::DataArray &array, nix::ndsize_t dim_index) {
nix::Dimension d = array.getDimension(dim_index);
DimInfo info;
info.valid = false;

nix::NDSize shape = array.dataExtent();

if (d.dimensionType() == nix::DimensionType::Sample) {
nix::SampledDimension dim = d.asSampledDimension();
info.valid = true;
info.label = dim.label().get_value_or("").c_str();
info.unit = dim.unit().get_value_or("").c_str();
double si = dim.samplingInterval();
//todo: safe cast?
info.ticks.resize(static_cast<int>(shape[dim_index-1]));

double offset = dim.offset().get_value_or(0);
const int size = info.ticks.size();
for (int i = 0; i < size; i++) {
info.ticks[i] = offset + i * si;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could have asked

info.ticks = QVector<double>::fromStrdVector(dim.axis(shape[dim-1]))

should have given the same result

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Boah, I didn't know about SampledDimension.axis(). Cool, I will change that.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saves you the offset issues


} else if (d.dimensionType() == nix::DimensionType::Range) {
nix::RangeDimension dim = d.asRangeDimension();
info.valid = true;
info.label = dim.label().get_value_or("").c_str();
info.unit = dim.unit().get_value_or("").c_str();

info.ticks = QVector<double>::fromStdVector(dim.ticks());
}

const int size = info.ticks.size();
if (size > 0) {
info.range.lower = info.ticks[0];
info.range.upper = info.ticks[size - 1];
}

return info;
}

void ImagePlotter::draw(const nix::DataArray &array) {
if (array.dimensionCount() != 2) {
std::cerr << "ImagePlotter::draw can only draw 2D!" << std::endl;
return;
}

DimInfo yi = get_dim_info(array, 1); // rows == y
DimInfo xi = get_dim_info(array, 2); // cols == x

if (!(yi.valid && xi.valid)) {
std::cerr << "ImagePlotter::invalid dimensions found in array!" << std::endl;
return;
}

QCustomPlot *plot = get_plot();

plot->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom); // this will also allow rescaling the color scale by dragging/zooming
plot->axisRect()->setupFullAxesBox(true);
plot->xAxis->setLabel(xi.label + " " + xi.unit);
plot->yAxis->setLabel(yi.label + " " + yi.unit);

QCPColorMap *colorMap = new QCPColorMap(plot->xAxis, plot->yAxis);
plot->addPlottable(colorMap);

int nx = xi.ticks.size();
int ny = yi.ticks.size();

colorMap->data()->setSize(nx, ny);
colorMap->data()->setRange(xi.range, yi.range);
colorMap->setInterpolate(false);

for (int xidx = 0; xidx < nx; xidx++) {
std::vector<double> data(static_cast<size_t>(nx));
//lets fetch a whole row (should be fast if data is
// row-major, like hdf5)
//ny, the number of rows in data-array
//xidx, the current column the fetch
// -> fetch all rows at column [xidx]
// count offset
array.getData(data, {ny, 1}, {0, xidx});
for (int yidx = 0; yidx < ny; yidx++) {
// data[ydix], the data point of the row[yidx], col[xidx]
colorMap->data()->setCell(xidx, yidx, data[yidx]);
}
}

QCPColorScale *colorScale = new QCPColorScale(plot);
plot->plotLayout()->addElement(0, 1, colorScale);
colorScale->setType(QCPAxis::atRight);
colorMap->setColorScale(colorScale);
if (array.label()) {
colorScale->axis()->setLabel(array.label().get().c_str());
}
colorMap->setGradient(QCPColorGradient::gpSpectrum);
colorMap->rescaleDataRange();

QCPMarginGroup *marginGroup = new QCPMarginGroup(plot);
plot->axisRect()->setMarginGroup(QCP::msBottom|QCP::msTop, marginGroup);
colorScale->setMarginGroup(QCP::msBottom|QCP::msTop, marginGroup);

plot->rescaleAxes();
}


void ImagePlotter::add_events(const QVector<double> &x_data, const QVector<double> &y_data, const QString &name,
bool y_scale) {

}

void ImagePlotter::add_segments(const QVector<double> &positions, const QVector<double> &extents, const QString &name) {

}

void ImagePlotter::set_label(const std::string &label) {

}

PlotterType ImagePlotter::plotter_type() const {
return PlotterType::Image;
}

41 changes: 41 additions & 0 deletions plotter/imageplotter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#ifndef IMAGEPLOTTER_H
#define IMAGEPLOTTER_H

#include <QWidget>
#include "plotter.h"
#include <nix.hpp>
#include "colormap.hpp"

namespace Ui {
class ImagePlotter;
}

class ImagePlotter : public QWidget, public Plotter {
Q_OBJECT
public:

virtual void add_events(const QVector<double> &x_data, const QVector<double> &y_data, const QString &name,
bool y_scale) override;

virtual void add_segments(const QVector<double> &positions, const QVector<double> &extents,
const QString &name) override;

virtual void set_label(const std::string &label) override;

virtual PlotterType plotter_type() const override;


public:
explicit ImagePlotter(QWidget *parent = 0);
~ImagePlotter();

void draw(const nix::DataArray &array);

private:
Ui::ImagePlotter *ui;
ColorMap cmap;

QCustomPlot* get_plot();
};

#endif //IMAGEPLOTTER_H
66 changes: 66 additions & 0 deletions plotter/imageplotter.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ImagePlotter</class>
<widget class="QWidget" name="ImagePlotter">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Ignored">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QCustomPlot" name="plot" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QCustomPlot</class>
<extends>QWidget</extends>
<header>qcustomplot.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
3 changes: 1 addition & 2 deletions plotter/plotter.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,7 @@ class Plotter {
if (array.getDimension(2).dimensionType() == nix::DimensionType::Set) {
return PlotterType::Line;
} else {
// handle 2D image/heatmap plotting not supported, yet TODO
return PlotterType::Unsupported;
return PlotterType::Image;
}
} else {
if (array.getDimension(2).dimensionType() == nix::DimensionType::Sample ||
Expand Down
14 changes: 12 additions & 2 deletions plotwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "ui_plotwidget.h"
#include "plotter/lineplotter.h"
#include "plotter/categoryplotter.h"
#include "plotter/imageplotter.h"
#include "common/Common.hpp"

PlotWidget::PlotWidget(QWidget *parent) :
Expand Down Expand Up @@ -59,19 +60,28 @@ void PlotWidget::delete_widgets_from_layout() {


Plotter* PlotWidget::process(const nix::DataArray &array) {
if (Plotter::suggested_plotter(array) == PlotterType::Line) {
PlotterType suggestion = Plotter::suggested_plotter(array);

if (suggestion == PlotterType::Line) {
delete_widgets_from_layout();
LinePlotter *lp = new LinePlotter();
ui->scrollAreaWidgetContents->layout()->addWidget(lp);
lp->draw(array);
return lp;
} else if (Plotter::suggested_plotter(array) == PlotterType::Category) {
} else if (suggestion == PlotterType::Category) {
delete_widgets_from_layout();
CategoryPlotter *cp = new CategoryPlotter();
ui->scrollAreaWidgetContents->layout()->addWidget(cp);
cp->draw(array);
return cp;
} else if (suggestion == PlotterType::Image) {
delete_widgets_from_layout();
ImagePlotter *ip = new ImagePlotter();
ui->scrollAreaWidgetContents->layout()->addWidget(ip);
ip->draw(array);
return ip;
}

return nullptr;
}

Expand Down