2

I want to put logarithmic a scale next to spectrogram. I want the displayed image to be the same as for the linear data. The code for the version with linear scales looks like this:

#include <QApplication>
#include <QMainWindow>

#include <qwt_plot.h>
#include <qwt_plot_spectrogram.h>
#include <qwt_matrix_raster_data.h>
#include <qwt_color_map.h>
#include <qwt_scale_engine.h>

int main( int argc, char* argv[] ) {
  QApplication app( argc, argv );
  QMainWindow wnd;

  QVector<double> heat_values( 100 * 100 );
  for( int n = 0; n < 100 * 100; ++n ) {
    heat_values[n] = ( n % 100 ) + n / 100;
  };

  QwtPlotSpectrogram heat;
  auto heat_data = std::make_unique<QwtMatrixRasterData>();
  heat_data->setValueMatrix( heat_values, 100 );
  heat_data->setResampleMode(
      QwtMatrixRasterData::ResampleMode::NearestNeighbour );
  heat_data->setInterval( Qt::XAxis, QwtInterval( 0, 100.0 ) );
  heat_data->setInterval( Qt::YAxis, QwtInterval( 0, 100.0 ) );
  heat_data->setInterval( Qt::ZAxis, QwtInterval( 0, 200.0 ) );

  heat.setDisplayMode( QwtPlotSpectrogram::DisplayMode::ImageMode, true );
  heat.setColorMap( new QwtLinearColorMap( Qt::white, Qt::black ) );
  heat.setData( heat_data.release() );

  QwtPlot p;
  p.setAutoDelete( false );
  heat.attach( &p );
  p.repaint();

  wnd.setCentralWidget( &p );
  wnd.resize( 400, 300 );
  wnd.show();

  return QApplication::exec();
}

and produces the expected result.

enter image description here

However, I want the same image but with different scales, for example logarithmic scales from 1 to 101. But after I change the scales like this:

  p.setAxisScaleEngine( QwtPlot::yLeft, new QwtLogScaleEngine() );
  p.setAxisScale( QwtPlot::yLeft, 1.0, 101.0 );
  p.setAxisScaleEngine( QwtPlot::xBottom, new QwtLogScaleEngine() );
  p.setAxisScale( QwtPlot::xBottom, 1.0, 101.0 );

then the spectrogram is all messed up.

enter image description here

Does anyone know how to just change the displayed scale?

enter image description here

msvc 2017, x64, qwt 6.1.4, qt 5.12.2

Edit:

I can get half way there by defining my own RasterData and mapping the coordinates back into bins, but it's still missing the inverse transformation, so the displayed data is a 'log' version of the original.

class RasterData : public QwtRasterData
{
public:
  double value( double const x, double const y ) const override {  
    int const ix = std::min<int>( std::max<int>( 0, x ), m_cols-1 );
    int const iy = std::min<int>( std::max<int>( 0, y ), m_cols-1 );
    return m_values[iy * m_cols + ix];
  }

  void setValueMatrix( QVector<double> const& values, int const cols ) {
    m_values = values;
    m_cols = cols;
  }

private: 
  QVector<double> m_values;
  int m_cols;
};

then result then looks like this:

enter image description here

But essentially I want to avoid all of these tranformations. I want it to just transform the image data passed in via setValueMatrix into an image using the set color map and stretch that image to fit the plot.

Thomas
  • 4,980
  • 2
  • 15
  • 30
  • I didn't understand what you want, you could explain in more detail, maybe an image of what you want to get would help – eyllanesc Aug 03 '19 at 23:28
  • @eyllanesc I faked the result by combining the two images. I hope this explains it better. Essentially I want to put an arbitrary scale next to a precalculated image. – Thomas Aug 04 '19 at 07:41
  • Do you expect the same "visible behavior" to be the same on the linear and logarithmic scale? – eyllanesc Aug 04 '19 at 07:50
  • Yes, I just wan't to change the scales. I don't want the data to be rescaled. The image should remain unchanged (linear). – Thomas Aug 04 '19 at 08:09
  • You are not wrong. What makes a logarithmic scale is to smooth the slope, we go with the numbers so that you understand: your numbers range from 1 to 101. What is the logarithm in that range? then from 0 to 2, so on that scale a line with any slope will be converted to a line with a slope of approximately 0 which implies that the values are not distinguished. Different thing if your data had an exponential tendency then the logarithmic scale would change it to linear – eyllanesc Aug 04 '19 at 08:14
  • That change does not modify the data as you point out but it does change how we see that data. The idea of the logarithmic scale is that it allows us to analyze data that has an exponential behavior since the behavior of the venality of a point is minimized, but that is not your case – eyllanesc Aug 04 '19 at 08:16
  • Yes, maybe my wording was not quite right. It changes how we see that data. I don't want that to change. I have a hundred bins of 2d histogram data. I want to add the 'right' scale to that. – Thomas Aug 04 '19 at 10:23

2 Answers2

1

The best way I found to make this work is by deriving from QwtPlotSpectrogram and changing the transformation to linear for the call to draw.

class PlotSpectrogram : public QwtPlotSpectrogram {
public:
  void draw(
      QPainter* painter,
      QwtScaleMap const& xMap,
      QwtScaleMap const & yMap,
      QRectF const& canvasRect ) const override {

    QwtScaleMap xMapLin( xMap );
    QwtScaleMap yMapLin( yMap );

    auto const xi = data()->interval( Qt::XAxis );
    auto const yi = data()->interval( Qt::YAxis );

    auto const dx = xMapLin.transform( xMap.s1() );
    xMapLin.setScaleInterval( xi.minValue(), xi.maxValue() );
    auto const dy = yMapLin.transform( yMap.s2() );
    yMapLin.setScaleInterval( yi.minValue(), yi.maxValue() );

    xMapLin.setTransformation( new QwtNullTransform() );
    yMapLin.setTransformation( new QwtNullTransform() );

    QwtPlotSpectrogram::draw(
        painter, xMapLin, yMapLin, canvasRect.translated( dx, -dy ) );
  }
};

With main altered for a scale log scale from 20..50 and using PlotSpectrogram

  PlotSpectrogram heat;
  auto heat_data = std::make_unique<QwtMatrixRasterData>();
  heat_data->setValueMatrix( heat_values, 100 );
  heat_data->setInterval( Qt::XAxis, QwtInterval( 0, 100.0 ) );
  heat_data->setInterval( Qt::YAxis, QwtInterval( 0, 100.0 ) );
  heat_data->setInterval( Qt::ZAxis, QwtInterval( 0, 200.0 ) );

  heat.setDisplayMode( QwtPlotSpectrogram::DisplayMode::ImageMode, true );
  heat.setColorMap( new QwtLinearColorMap( Qt::white, Qt::black ) );
  heat.setData( heat_data.release() );

  QwtPlot p;

  p.setAxisScaleEngine( QwtPlot::yLeft, new QwtLogScaleEngine() );
  p.setAxisScale( QwtPlot::yLeft, 20.0, 50.0 );
  p.setAxisScaleEngine( QwtPlot::xBottom, new QwtLogScaleEngine() );
  p.setAxisScale( QwtPlot::xBottom, 20.0, 50.0 );

  p.setAutoDelete( false );
  heat.attach( &p );

I then get the desired output

enter image description here

Thomas
  • 4,980
  • 2
  • 15
  • 30
0

QwtPlotMatrixRasterData is not working with non linear scales !

When using QwtRasterData instead everything will work out of the box with any type of scales.

Uwe
  • 71
  • 1
  • `QwtRasterData` is an interface. I had already tried my own implementation of that (see edit above) and it does not lead to the desired result. – Thomas Aug 05 '19 at 15:54
  • Please continue on one of the official Qwt support channels, maybe with posting a short and compilable demo. I will have a look at it then. – Uwe Aug 05 '19 at 17:09